> ## 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.

# API Keys — Create, Scope, and Revoke Access Tokens

> Create, list, and revoke scoped API keys for your Dynamo CSMS organization. Control access to charge points, billing, analytics, and webhook endpoints.

Dynamo CSMS authenticates requests using API keys tied to your organization. You can create multiple keys with distinct permission scopes and revoke them independently. All requests must include your key in either the `Authorization` header or the `X-API-Key` header.

```bash theme={null}
# Option A — Bearer token
Authorization: Bearer YOUR_API_KEY

# Option B — direct header
X-API-Key: YOUR_API_KEY
```

## Available scopes

| Scope                 | Access                              |
| --------------------- | ----------------------------------- |
| `read:charge_points`  | Read charge point data and status   |
| `write:charge_points` | Register and update charge points   |
| `read:billing`        | View billing records and invoices   |
| `write:billing`       | Modify billing configuration        |
| `read:analytics`      | Access usage analytics and reports  |
| `write:webhooks`      | Create and manage webhook endpoints |
| `read:sessions`       | View charging session history       |

***

## List API keys

`GET /api/v1/org/api-keys`

Returns all API keys created for your organization, including their scopes and last-used timestamps. Secret key values are never returned after initial creation.

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

**Response `200 OK`**

```json theme={null}
{
  "keys": [
    {
      "id": "key_abc123",
      "name": "Production Dashboard",
      "scopes": ["read:charge_points", "read:billing", "read:sessions"],
      "created_at": "2024-01-15T10:00:00Z",
      "last_used_at": "2024-01-20T14:30:00Z",
      "expires_at": null
    },
    {
      "id": "key_def456",
      "name": "Analytics Exporter",
      "scopes": ["read:analytics"],
      "created_at": "2024-02-01T08:00:00Z",
      "last_used_at": null,
      "expires_at": "2025-02-01T08:00:00Z"
    }
  ],
  "total": 2
}
```

<ResponseField name="keys" type="array">
  Array of API key objects belonging to your organization.
</ResponseField>

<ResponseField name="keys[].id" type="string">
  Unique identifier for the key. Use this ID when revoking the key.
</ResponseField>

<ResponseField name="keys[].name" type="string">
  Human-readable label you assigned when creating the key.
</ResponseField>

<ResponseField name="keys[].scopes" type="string[]">
  List of permission scopes granted to this key.
</ResponseField>

<ResponseField name="keys[].created_at" type="string">
  ISO 8601 timestamp of when the key was created.
</ResponseField>

<ResponseField name="keys[].last_used_at" type="string | null">
  ISO 8601 timestamp of the most recent authenticated request. `null` if the key has never been used.
</ResponseField>

<ResponseField name="keys[].expires_at" type="string | null">
  ISO 8601 expiry timestamp. `null` for keys with no expiration.
</ResponseField>

<ResponseField name="total" type="integer">
  Total number of keys in your organization.
</ResponseField>

***

## Create API key

`POST /api/v1/org/api-keys`

Creates a new API key. The secret `key` value is returned **only once** in the creation response — store it securely immediately. Subsequent calls to list keys will not return the secret value.

<ParamField body="name" type="string" required>
  A human-readable label for this key (e.g. `"Production Dashboard"`, `"CI Pipeline"`). Must be unique within your organization. Maximum 128 characters.
</ParamField>

<ParamField body="scopes" type="string[]" required>
  List of permission scopes to grant. Must contain at least one scope. See the scopes table above for valid values.
</ParamField>

<ParamField body="expires_in_days" type="integer">
  Optional. Number of days until the key expires. Omit for a non-expiring key. Must be between 1 and 3650.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.dynamo-csms.com/api/v1/org/api-keys \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Fleet Monitor",
      "scopes": ["read:charge_points", "read:sessions", "read:analytics"],
      "expires_in_days": 365
    }'
  ```

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

  response = requests.post(
      "https://api.dynamo-csms.com/api/v1/org/api-keys",
      headers={"Authorization": "Bearer YOUR_API_KEY"},
      json={
          "name": "Fleet Monitor",
          "scopes": ["read:charge_points", "read:sessions", "read:analytics"],
          "expires_in_days": 365,
      },
  )
  data = response.json()
  print(data["key"])  # store this immediately
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    "https://api.dynamo-csms.com/api/v1/org/api-keys",
    {
      method: "POST",
      headers: {
        Authorization: "Bearer YOUR_API_KEY",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        name: "Fleet Monitor",
        scopes: ["read:charge_points", "read:sessions", "read:analytics"],
        expires_in_days: 365,
      }),
    }
  );
  const data = await response.json();
  console.log(data.key); // store this immediately
  ```
</CodeGroup>

**Response `201 Created`**

```json theme={null}
{
  "id": "key_xyz789",
  "name": "Fleet Monitor",
  "key": "dynamo_live_sk_4f8a2b1c9d3e7f0a5b6c8d2e1f4a7b3c",
  "scopes": ["read:charge_points", "read:sessions", "read:analytics"],
  "created_at": "2024-03-10T12:00:00Z",
  "expires_at": "2025-03-10T12:00:00Z"
}
```

<ResponseField name="id" type="string">
  Unique key identifier. Use this to revoke the key.
</ResponseField>

<ResponseField name="name" type="string">
  The label you provided.
</ResponseField>

<ResponseField name="key" type="string">
  The secret API key value. This is the only time this value is returned — save it immediately.
</ResponseField>

<ResponseField name="scopes" type="string[]">
  Confirmed list of scopes granted to the key.
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 creation timestamp.
</ResponseField>

<ResponseField name="expires_at" type="string | null">
  ISO 8601 expiry timestamp, or `null` if the key does not expire.
</ResponseField>

***

## Revoke API key

`DELETE /api/v1/org/api-keys/{key_id}`

Permanently revokes an API key. Any in-flight requests using this key will fail immediately. This action cannot be undone.

<ParamField path="key_id" type="string" required>
  The `id` of the key to revoke, as returned by the list or create endpoints.
</ParamField>

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

**Response `204 No Content`**

An empty body is returned on success.

***

## Error responses

All API key endpoints return standard error objects on failure.

```json theme={null}
{
  "error": {
    "code": "invalid_scope",
    "message": "Scope 'write:unknown' is not a valid permission scope.",
    "request_id": "req_1a2b3c4d"
  }
}
```

| Status | Code            | Meaning                                                  |
| ------ | --------------- | -------------------------------------------------------- |
| `400`  | `invalid_scope` | One or more scopes are not recognized                    |
| `400`  | `name_taken`    | A key with this name already exists in your organization |
| `401`  | `unauthorized`  | Missing or invalid API key on the request itself         |
| `403`  | `forbidden`     | Your key lacks the required scope for this operation     |
| `404`  | `not_found`     | The specified `key_id` does not exist                    |
| `429`  | `rate_limited`  | Too many requests — back off and retry                   |
