# API keys

Base URL: `https://api.brixsignage.com`. Send `Authorization: Bearer $BRIX_API_KEY` unless an operation says Auth: none.

## GET /v1/api-keys

List API keys

Returns the workspace's API keys: name, assigned permissions, the location the key is pinned to (if any), and when each was created and last used. Key secrets are never returned. A key is listed only where the caller holds the API key view permission at the key's location; a workspace-wide key needs that permission for the whole workspace.

**Notes.**
- Not paginated. Revoked keys are listed too (`revokedAt` set).

Auth: Bearer token. Permission: `api-key.view`.

```bash
curl "https://api.brixsignage.com/v1/api-keys" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of ApiKey |  |
| `data[].id` | string |  |
| `data[].name` | string |  |
| `data[].preview` | string | The first characters of the key (`ak_us_live_Ab3x`), to recognise it. Not usable as a credential. |
| `data[].permissions` | "all" \| array of string | `resource.action` permissions, or `"all"` (every permission, now and later). |
| `data[].nodeId` | string \| null | The location the key is pinned to (it can act only there and below). Null = workspace-wide. |
| `data[].lastUsedAt` | string \| null |  |
| `data[].expiresAt` | string \| null | Null = never expires. |
| `data[].expired` | boolean |  |
| `data[].revokedAt` | string \| null | Set when the key was revoked; a revoked key no longer authenticates. |
| `data[].createdAt` | string | ISO-8601 timestamp (UTC). |

Response 401: Missing, expired or revoked bearer token.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 5XX: Server error. The body carries a `requestId` to quote to support.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

## POST /v1/api-keys

Create API key

Creates a new API key. Provide `name`, `permissions` (a list of `resource.action` strings, or `"all"`), and an optional `nodeId` to restrict the key to one location and everything beneath it. The raw key secret is returned exactly once, in this response; only its hash is stored afterward, so it cannot be retrieved again.

Auth: Bearer token. Permission: `api-key.create`.

Request body (`application/json`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes |  |
| `permissions` | "all" \| array of string | no | You must hold each one where the key applies. Default `[]` (a key that can do nothing). |
| `nodeId` | string \| null | no | Pin the key to this location. Omit for a workspace-wide key, which needs its permissions workspace-wide. |
| `expiresInDays` | number \| null | no | Days until the key stops working (at most 3650). Omit, null or 0 = never expires. |

```bash
curl -X POST "https://api.brixsignage.com/v1/api-keys" \
  -H "Authorization: Bearer $BRIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Menu board sync","permissions":["screen.view","media.create"],"expiresInDays":365}'
```

Response 201: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.id` | string |  |
| `data.name` | string |  |
| `data.preview` | string | The first characters of the key (`ak_us_live_Ab3x`), to recognise it. Not usable as a credential. |
| `data.permissions` | "all" \| array of string | `resource.action` permissions, or `"all"` (every permission, now and later). |
| `data.nodeId` | string \| null | The location the key is pinned to (it can act only there and below). Null = workspace-wide. |
| `data.lastUsedAt` | string \| null |  |
| `data.expiresAt` | string \| null | Null = never expires. |
| `data.expired` | boolean |  |
| `data.revokedAt` | string \| null | Set when the key was revoked; a revoked key no longer authenticates. |
| `data.createdAt` | string | ISO-8601 timestamp (UTC). |
| `data.secret` | string | The full key. Shown ONLY in this response; store it now. Send it as `Authorization: Bearer <secret>`. |

Response 401: Missing, expired or revoked bearer token.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 403: You do not hold every permission you grant, or not at the key's location (a workspace-wide key needs them workspace-wide). Also refused for a Brix support session.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 422: `name` missing, `expiresInDays` over 3650 or not a number, or `nodeId` not a location of this workspace.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 5XX: Server error. The body carries a `requestId` to quote to support.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

## DELETE /v1/api-keys/{id}

Revoke API key

Revokes an API key immediately, so it can no longer be used. You must hold the permission to delete keys at or above the location the key is pinned to, not merely somewhere else in the workspace.

**Notes.**
- The key stays in the list with `revokedAt` set. Revoking a key again answers 200 and moves `revokedAt` to now.

Auth: Bearer token. Permission: `api-key.delete`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | API key id. |

```bash
curl -X DELETE "https://api.brixsignage.com/v1/api-keys/{id}" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.id` | string |  |
| `data.revoked` | true |  |

Response 401: Missing, expired or revoked bearer token.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 403: You lack `api-key.delete` at the key's location.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 404: No such key in this workspace.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 5XX: Server error. The body carries a `requestId` to quote to support.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

## POST /v1/api-keys/{id}/rotate

Rotate API key

Replaces an API key's secret with a new one, invalidating the old secret immediately. The new secret is returned exactly once, in this response. You must hold the permission to create keys at or above the location the key is pinned to.

**Notes.**
- The key keeps its id, name, permissions, location and expiry; `lastUsedAt` is reset to null. An expired key can be rotated and stays expired.

Auth: Bearer token. Permission: `api-key.create`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | API key id. |

```bash
curl -X POST "https://api.brixsignage.com/v1/api-keys/{id}/rotate" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.id` | string |  |
| `data.name` | string |  |
| `data.preview` | string | The first characters of the key (`ak_us_live_Ab3x`), to recognise it. Not usable as a credential. |
| `data.permissions` | "all" \| array of string | `resource.action` permissions, or `"all"` (every permission, now and later). |
| `data.nodeId` | string \| null | The location the key is pinned to (it can act only there and below). Null = workspace-wide. |
| `data.lastUsedAt` | string \| null |  |
| `data.expiresAt` | string \| null | Null = never expires. |
| `data.expired` | boolean |  |
| `data.revokedAt` | string \| null | Set when the key was revoked; a revoked key no longer authenticates. |
| `data.createdAt` | string | ISO-8601 timestamp (UTC). |
| `data.secret` | string | The full key. Shown ONLY in this response; store it now. Send it as `Authorization: Bearer <secret>`. |

Response 401: Missing, expired or revoked bearer token.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 403: The key holds permissions you cannot grant at its location, you lack `api-key.create` there, or a Brix support session.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 404: No such key in this workspace.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 409: `revoked`: a revoked key cannot be rotated; create a new one.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 5XX: Server error. The body carries a `requestId` to quote to support.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |
