# Roles

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

## GET /v1/roles

List roles

Returns the workspace's roles, both built-in and custom, with the permissions each one carries.

**Notes.**
- Not paginated.

Auth: Bearer token. Permission: `permission-group.view`.

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

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of Role |  |
| `data[].id` | string |  |
| `data[].name` | string |  |
| `data[].color` | string \| null | Display style for the role chip. |
| `data[].description` | string | Empty string when not set. |
| `data[].builtIn` | boolean | A role that ships with the product. It cannot be deleted, and its permissions, features and scope cannot be changed. |
| `data[].permissions` | "all" \| array of string | `resource.action` permissions, or `"all"` (every permission, now and later). |
| `data[].features` | "all" \| array of string | Feature keys the role unlocks, or `"all"`. |
| `data[].scope` | object \| object | Where the role applies: the whole workspace, or only the listed locations. |
| `data[].character` | string \| null | A decorative label. Not used for access. |
| `data[].memberCount` | integer | Role assignments that use this role (one per person per location). |
| `data[].createdAt` | string | ISO-8601 timestamp (UTC). |
| `data[].updatedAt` | 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/roles

Create role

Creates a custom role. Provide `name` and `permissions`, a list of `resource.action` strings or `"all"`. You cannot create a role that holds permissions you do not hold yourself.

**Notes.**
- `permissions`, `features` and `scope` are stored as sent: the handler does not check their shape, so a malformed value is stored and read back as it was written.
- `builtIn` in the body is ignored.

Auth: Bearer token. Permission: `permission-group.create`.

Request body (`application/json`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes | Required on create. |
| `color` | string | no |  |
| `description` | string | no |  |
| `permissions` | "all" \| array of string | no | You must hold every permission you grant, workspace-wide. Default `[]`. |
| `features` | "all" \| array of string | no | Default `[]`. |
| `scope` | object \| object | no | Default `{ "kind": "workspace" }`. |
| `character` | string \| null | no |  |

```bash
curl -X POST "https://api.brixsignage.com/v1/roles" \
  -H "Authorization: Bearer $BRIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Store manager","permissions":["screen.view","screen.cast","media.view","media.create"]}'
```

Response 201: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | Role | A named set of permissions assigned to people at locations. |
| `data.id` | string |  |
| `data.name` | string |  |
| `data.color` | string \| null | Display style for the role chip. |
| `data.description` | string | Empty string when not set. |
| `data.builtIn` | boolean | A role that ships with the product. It cannot be deleted, and its permissions, features and scope cannot be changed. |
| `data.permissions` | "all" \| array of string | `resource.action` permissions, or `"all"` (every permission, now and later). |
| `data.features` | "all" \| array of string | Feature keys the role unlocks, or `"all"`. |
| `data.scope` | object \| object | Where the role applies: the whole workspace, or only the listed locations. |
| `data.character` | string \| null | A decorative label. Not used for access. |
| `data.memberCount` | integer | Role assignments that use this role (one per person per location). |
| `data.createdAt` | string | ISO-8601 timestamp (UTC). |
| `data.updatedAt` | 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: `permissions` holds a permission you do not hold workspace-wide.

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

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

## GET /v1/roles/{id}

Get role

Returns one role with its full list of permissions.

Auth: Bearer token. Permission: `permission-group.view`.

Parameters:

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

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

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | Role | A named set of permissions assigned to people at locations. |
| `data.id` | string |  |
| `data.name` | string |  |
| `data.color` | string \| null | Display style for the role chip. |
| `data.description` | string | Empty string when not set. |
| `data.builtIn` | boolean | A role that ships with the product. It cannot be deleted, and its permissions, features and scope cannot be changed. |
| `data.permissions` | "all" \| array of string | `resource.action` permissions, or `"all"` (every permission, now and later). |
| `data.features` | "all" \| array of string | Feature keys the role unlocks, or `"all"`. |
| `data.scope` | object \| object | Where the role applies: the whole workspace, or only the listed locations. |
| `data.character` | string \| null | A decorative label. Not used for access. |
| `data.memberCount` | integer | Role assignments that use this role (one per person per location). |
| `data.createdAt` | string | ISO-8601 timestamp (UTC). |
| `data.updatedAt` | 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 404: No such role in this workspace (or it is deleted).

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

## PATCH /v1/roles/{id}

Update role

Updates a role. The same rule as creating a role applies: you cannot add or remove a permission you do not hold yourself. Only a signed-in account owner can take full ownership away from a role that people hold; an API key cannot. On a built-in role, only the name, color and description change; its permissions, features and scope stay fixed.

**Notes.**
- `permissions`, `features` and `scope` are stored as sent: the handler does not check their shape, so a malformed value is stored and read back as it was written.
- `builtIn` in the body is ignored.
- On a built-in role, `permissions`, `features` and `scope` are ignored without an error; only `name`, `color`, `description` and `character` change.

Auth: Bearer token. Permission: `permission-group.edit`.

Parameters:

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

Request body (`application/json`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | no | Required on create. |
| `color` | string | no |  |
| `description` | string | no |  |
| `permissions` | "all" \| array of string | no | You must hold every permission you grant, workspace-wide. Default `[]`. |
| `features` | "all" \| array of string | no | Default `[]`. |
| `scope` | object \| object | no | Default `{ "kind": "workspace" }`. |
| `character` | string \| null | no |  |

```bash
curl -X PATCH "https://api.brixsignage.com/v1/roles/{id}" \
  -H "Authorization: Bearer $BRIX_API_KEY" \
  -H "Content-Type: application/json"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | Role | A named set of permissions assigned to people at locations. |
| `data.id` | string |  |
| `data.name` | string |  |
| `data.color` | string \| null | Display style for the role chip. |
| `data.description` | string | Empty string when not set. |
| `data.builtIn` | boolean | A role that ships with the product. It cannot be deleted, and its permissions, features and scope cannot be changed. |
| `data.permissions` | "all" \| array of string | `resource.action` permissions, or `"all"` (every permission, now and later). |
| `data.features` | "all" \| array of string | Feature keys the role unlocks, or `"all"`. |
| `data.scope` | object \| object | Where the role applies: the whole workspace, or only the listed locations. |
| `data.character` | string \| null | A decorative label. Not used for access. |
| `data.memberCount` | integer | Role assignments that use this role (one per person per location). |
| `data.createdAt` | string | ISO-8601 timestamp (UTC). |
| `data.updatedAt` | 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: You would add or remove a permission you do not hold workspace-wide, or change `scope` of a role whose permissions you do not hold workspace-wide; `owner_required`: the change takes full ownership away from a role people hold, and the caller is not a signed-in owner (an API key never is).

| 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 role 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: `last_owner_role`: this is the only role with full ownership.

| 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/roles/{id}

Delete role

Soft-deletes a custom role. The role can later be restored. Refuses the request while anyone is still assigned to the role.

Auth: Bearer token. Permission: `permission-group.delete`.

Parameters:

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

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

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.id` | string |  |
| `data.deleted` | 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: 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 404: No such role 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: `protected` (a built-in role), `last_owner_role`, or `in_use` (people still hold the role).

| 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/roles/{id}/restore

Restore deleted role

Restores a previously deleted custom role. Requires the same permission needed to delete a role.

**Notes.**
- Answers the whole restored role, not the `{ id, restored }` acknowledgement other restores use.

Auth: Bearer token. Permission: `permission-group.delete`.

Parameters:

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

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

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | Role | A named set of permissions assigned to people at locations. |
| `data.id` | string |  |
| `data.name` | string |  |
| `data.color` | string \| null | Display style for the role chip. |
| `data.description` | string | Empty string when not set. |
| `data.builtIn` | boolean | A role that ships with the product. It cannot be deleted, and its permissions, features and scope cannot be changed. |
| `data.permissions` | "all" \| array of string | `resource.action` permissions, or `"all"` (every permission, now and later). |
| `data.features` | "all" \| array of string | Feature keys the role unlocks, or `"all"`. |
| `data.scope` | object \| object | Where the role applies: the whole workspace, or only the listed locations. |
| `data.character` | string \| null | A decorative label. Not used for access. |
| `data.memberCount` | integer | Role assignments that use this role (one per person per location). |
| `data.createdAt` | string | ISO-8601 timestamp (UTC). |
| `data.updatedAt` | 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 404: No such role 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: `not_deleted`: the role is not deleted.

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