> ## Documentation Index
> Fetch the complete documentation index at: https://dynamo-csms.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication: API Keys, Scopes, and Rate Limits

> Authenticate with Dynamo CSMS using Bearer tokens or X-API-Key headers. Manage permission scopes, rate limits, and auth error responses.

Every request to the Dynamo CSMS API must include a valid credential. This page explains the two supported authentication methods, how to obtain and manage API keys, what permission scopes control, and how to handle authentication and rate-limit errors.

## Authentication methods

Dynamo CSMS accepts credentials in two header formats. Both are equally valid — choose whichever fits your HTTP client or framework better.

### Bearer token (recommended)

Pass your API key as a Bearer token in the `Authorization` header:

```bash theme={null}
curl -X GET https://api.dynamo-csms.com/api/v1/cpo/fleet/summary \
  -H "Authorization: Bearer dyn_live_abc123xyz789..."
```

This is the standard OAuth 2.0 Bearer format and is supported by virtually every HTTP client library.

### X-API-Key header

Alternatively, pass your API key in the `X-API-Key` header:

```bash theme={null}
curl -X GET https://api.dynamo-csms.com/api/v1/cpo/fleet/summary \
  -H "X-API-Key: dyn_live_abc123xyz789..."
```

<Note>
  Do not pass credentials in the URL query string (for example, `?api_key=...`). Query parameters appear in server logs and browser history. Use headers instead.
</Note>

### Code examples

<CodeGroup>
  ```python Python theme={null}
  import requests

  headers = {"Authorization": "Bearer dyn_live_abc123xyz789..."}

  response = requests.get(
      "https://api.dynamo-csms.com/api/v1/cpo/fleet/summary",
      headers=headers
  )
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    "https://api.dynamo-csms.com/api/v1/cpo/fleet/summary",
    {
      headers: {
        Authorization: "Bearer dyn_live_abc123xyz789..."
      }
    }
  );
  ```

  ```go Go theme={null}
  req, _ := http.NewRequest("GET",
      "https://api.dynamo-csms.com/api/v1/cpo/fleet/summary", nil)
  req.Header.Set("Authorization", "Bearer dyn_live_abc123xyz789...")

  client := &http.Client{}
  resp, err := client.Do(req)
  ```

  ```java Java theme={null}
  HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://api.dynamo-csms.com/api/v1/cpo/fleet/summary"))
      .header("Authorization", "Bearer dyn_live_abc123xyz789...")
      .GET()
      .build();

  HttpResponse<String> response = client.send(request,
      HttpResponse.BodyHandlers.ofString());
  ```
</CodeGroup>

## Obtaining an API key

### Via the Developer Portal

1. Log in to the [Dynamo CSMS Developer Portal](https://csms-staging-155982676532.europe-west1.run.app/developer).
2. Navigate to **Settings → API Keys**.
3. Click **Create API key**, choose a name, and select the scopes you need.
4. Copy the key immediately — it is only displayed once.

### Via the API

If you already have a valid key with the `write:api_keys` scope, you can create additional keys programmatically:

```bash theme={null}
curl -X POST https://api.dynamo-csms.com/api/v1/org/api-keys \
  -H "Authorization: Bearer YOUR_EXISTING_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "webhook-service",
    "scopes": ["read:charge_points", "read:sessions"]
  }'
```

```json theme={null}
{
  "id": "key_01hx2b3c4d5e6f7g8h9j",
  "name": "webhook-service",
  "key": "dyn_live_abc123xyz789...",
  "scopes": ["read:charge_points", "read:sessions"],
  "created_at": "2026-05-01T09:00:00Z"
}
```

<Warning>
  The `key` field is only returned on creation. If you lose the key value, you must revoke it and create a new one. The API never returns the full key value again after the creation response.
</Warning>

## Permission scopes

Scopes restrict what an API key can do. Assign only the scopes an application actually needs — this limits the blast radius if a key is ever leaked.

| Scope                 | Access                                                          |
| --------------------- | --------------------------------------------------------------- |
| `read:charge_points`  | List and query charge point status and details                  |
| `write:charge_points` | Register, update, and delete charge points                      |
| `read:sessions`       | Read active and historical charging sessions                    |
| `write:sessions`      | Start and stop charging sessions remotely                       |
| `read:billing`        | Read tariffs, billing rules, and revenue reports                |
| `write:billing`       | Create and update tariffs, promotional codes, and billing rules |
| `read:webhooks`       | List configured webhook endpoints                               |
| `write:webhooks`      | Create, update, and delete webhook endpoints                    |
| `read:analytics`      | Access fleet analytics and usage reports                        |
| `read:organisations`  | Read organisation and team member data                          |
| `write:organisations` | Update organisation settings and manage team members            |
| `write:api_keys`      | Create and revoke API keys for your organisation                |
| `read:ocpi`           | Read OCPI roaming partner data                                  |
| `write:ocpi`          | Configure OCPI connections with partner networks                |
| `write:commands`      | Send remote commands to charge points (reboot, unlock, etc.)    |

<Tip>
  For read-only integrations such as monitoring dashboards or analytics pipelines, use only `read:*` scopes. Reserve `write:*` scopes for services that actively control hardware.
</Tip>

## Installer authentication

Installers in the field use a separate token-based flow rather than long-lived API keys. This is because installer credentials are personal and time-limited — they are not associated with an organisation's API key quota.

To authenticate as an installer:

```bash theme={null}
curl -X POST https://api.dynamo-csms.com/api/v1/installer/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "installer@example.com",
    "password": "your-password"
  }'
```

```json theme={null}
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "installer_id": "ins_01hx9k2m3n4p"
}
```

Use the `access_token` value as a Bearer token in the same way as an API key. Installer tokens expire after 60 minutes. See the [Installer quickstart](/quickstart-installer) for the full workflow.

## Rate limits

Dynamo CSMS enforces rate limits per API key to ensure fair use and platform stability.

| Tier       | Limit                                                                      |
| ---------- | -------------------------------------------------------------------------- |
| Free       | 1,000 requests per hour                                                    |
| Pro        | 10,000 requests per hour                                                   |
| Enterprise | Custom — contact [support@dynamo-csms.com](mailto:support@dynamo-csms.com) |

Every response includes rate-limit headers so you can track your consumption:

```
X-RateLimit-Limit: 10000
X-RateLimit-Remaining: 9847
X-RateLimit-Reset: 1746090000
```

The `X-RateLimit-Reset` value is a Unix timestamp indicating when your hourly quota resets.

## Error responses

### 401 Unauthorized

A `401` response means the request was not authenticated or the credential is invalid.

```json theme={null}
{
  "error": "unauthorized",
  "message": "Invalid or missing API key.",
  "request_id": "req_01hxzz9aa1b2"
}
```

Common causes:

* The `Authorization` or `X-API-Key` header is missing entirely.
* The API key has been revoked or expired.
* The key belongs to a different organisation than the resource being accessed.
* The `Bearer ` prefix is missing from the `Authorization` header value.

### 403 Forbidden

A `403` response means the credential is valid but the key does not have the required scope for the requested operation.

```json theme={null}
{
  "error": "forbidden",
  "message": "The API key does not have the required scope: write:charge_points",
  "required_scope": "write:charge_points",
  "request_id": "req_01hxzz9aa1b3"
}
```

Add the missing scope to the API key in the Developer Portal, or create a new key with the correct scopes.

### 429 Too Many Requests

A `429` response means you have exceeded your hourly rate limit.

```json theme={null}
{
  "error": "rate_limit_exceeded",
  "message": "You have exceeded the rate limit of 1000 requests per hour.",
  "retry_after": 847,
  "request_id": "req_01hxzz9aa1b4"
}
```

The `retry_after` field tells you how many seconds to wait before retrying. Implement exponential backoff in your application rather than retrying immediately.

```python theme={null}
import time
import requests

def call_with_backoff(url, headers, max_retries=3):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)
        if response.status_code == 429:
            retry_after = response.json().get("retry_after", 60)
            time.sleep(retry_after)
            continue
        return response
    raise Exception("Rate limit retries exhausted")
```

## Revoking an API key

To revoke a key that is no longer needed or may have been compromised, delete it via the API or from **Settings → API Keys** in the Developer Portal.

```bash theme={null}
curl -X DELETE https://api.dynamo-csms.com/api/v1/org/api-keys/key_01hx2b3c4d5e6f7g8h9j \
  -H "Authorization: Bearer YOUR_ADMIN_KEY"
```

Revocation takes effect immediately. Any in-flight requests using the revoked key that have already been authenticated will complete, but new requests will return `401`.

## Security best practices

* **Rotate keys regularly.** Create a new key, update your application, then revoke the old one.
* **Use environment variables.** Never hard-code API keys in source files.
* **Apply least-privilege scopes.** Give each key only the scopes it needs.
* **Monitor usage.** Review `X-RateLimit-Remaining` in responses and alert on unexpected spikes.
* **Separate keys per environment.** Use distinct keys for development, staging, and production.
