# SSO connections

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

## GET /v1/sso-connections

List SSO connections

Returns the workspace's enterprise single sign-on connections, with client secrets masked. The key or person must have `settings.view` for the whole workspace. A caller limited to a location or to a franchise workspace gets 403. `lastClaims` holds the claims from the last sign-in: a person's email, name and groups. It is `null` unless the caller also has `user.view` for the whole workspace.

Auth: Bearer token. Permission: `settings.view`.

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

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of SsoConnection |  |
| `data[].id` | string |  |
| `data[].displayName` | string |  |
| `data[].vendor` | string | Free text; `generic-oidc` when not set. |
| `data[].issuer` | string | The OpenID Connect issuer URL (`https://`, no trailing slash). |
| `data[].clientId` | string |  |
| `data[].clientSecretPreview` | "••••••••" | Always this mask: the client secret is stored encrypted and is never returned. |
| `data[].emailDomains` | string | Comma-separated, lower-case (a leading `@` and a trailing dot are removed). |
| `data[].domainsVerifiedAt` | string \| null | When DNS proved the domains; null until verified. An unverified connection is not offered at sign-in. |
| `data[].provenAt` | string \| null | When a sign-in first completed with the current issuer, client and domains; null until then. |
| `data[].lastSignInAt` | string \| null | The latest sign-in through this connection. Only the list fills it; create and update answer null. |
| `data[].jitProvisioning` | boolean | Create a person on their first sign-in. |
| `data[].defaultRoleId` | string \| null | The role a new person gets at the workspace root when no rule applies. |
| `data[].claimMapping` | SsoClaimMapping \| null |  |
| `data[].extraScopes` | string \| null | Scopes requested on top of `openid email profile`, space-separated. |
| `data[].lastClaims` | object \| null | The claims of the last sign-in (a person's email, name and groups). Null unless the caller also holds `user.view` for the whole workspace. |
| `data[].lastClaimsAt` | string \| null |  |
| `data[].enabled` | boolean |  |
| `data[].redirectUri` | string | The redirect URI to register in the identity provider. |
| `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 do not hold the permission for the whole workspace (a grant at one location, or in a franchise workspace, is not enough).

| 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/sso-connections

Create SSO connection

Creates an enterprise single sign-on connection. The client secret is encrypted before it is stored.

**Notes.**
- A new connection is unverified: prove its domains (`domain-challenge`, then `verify-domains`) before it is offered at sign-in.

Auth: Bearer token. Permission: `settings.edit`.

Request body (`application/json`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `displayName` | string | yes |  |
| `vendor` | string | no | Free text, e.g. `okta`, `entra`. |
| `issuer` | string | yes | Must start with `https://`. A trailing slash is removed. |
| `clientId` | string | yes |  |
| `clientSecret` | string | yes | Encrypted before it is stored; never returned. |
| `emailDomains` | string | yes | Comma-separated email domains, e.g. `acme.com,acme.org`. |
| `jitProvisioning` | boolean | no | Default true. |
| `enabled` | boolean | no | Default true. |
| `defaultRoleId` | string \| null | no | You must be able to grant this role for the whole workspace. |
| `claimMapping` | SsoClaimMapping \| null | no | You must be able to grant each rule's role where the rule grants it. Null clears it. |
| `extraScopes` | string \| null | no | Space- or comma-separated scope tokens (at most 20). Null or empty clears them. |

```bash
curl -X POST "https://api.brixsignage.com/v1/sso-connections" \
  -H "Authorization: Bearer $BRIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"displayName":"Acme Okta","issuer":"https://acme.okta.com","clientId":"0oa1b2c3d4","clientSecret":"example-client-secret","emailDomains":"acme.com"}'
```

Response 201: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | SsoConnection | An enterprise single sign-on (OpenID Connect) connection. |
| `data.id` | string |  |
| `data.displayName` | string |  |
| `data.vendor` | string | Free text; `generic-oidc` when not set. |
| `data.issuer` | string | The OpenID Connect issuer URL (`https://`, no trailing slash). |
| `data.clientId` | string |  |
| `data.clientSecretPreview` | "••••••••" | Always this mask: the client secret is stored encrypted and is never returned. |
| `data.emailDomains` | string | Comma-separated, lower-case (a leading `@` and a trailing dot are removed). |
| `data.domainsVerifiedAt` | string \| null | When DNS proved the domains; null until verified. An unverified connection is not offered at sign-in. |
| `data.provenAt` | string \| null | When a sign-in first completed with the current issuer, client and domains; null until then. |
| `data.lastSignInAt` | string \| null | The latest sign-in through this connection. Only the list fills it; create and update answer null. |
| `data.jitProvisioning` | boolean | Create a person on their first sign-in. |
| `data.defaultRoleId` | string \| null | The role a new person gets at the workspace root when no rule applies. |
| `data.claimMapping` | SsoClaimMapping \| null |  |
| `data.extraScopes` | string \| null | Scopes requested on top of `openid email profile`, space-separated. |
| `data.lastClaims` | object \| null | The claims of the last sign-in (a person's email, name and groups). Null unless the caller also holds `user.view` for the whole workspace. |
| `data.lastClaimsAt` | string \| null |  |
| `data.enabled` | boolean |  |
| `data.redirectUri` | string | The redirect URI to register in the identity provider. |
| `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 do not hold the permission for the whole workspace (a grant at one location, or in a franchise workspace, is not enough). Also: `defaultRoleId` or a rule's role holds permissions you cannot grant there.

| 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 connection, role or location 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 422: A required field is missing, `issuer` is not `https://`, an invalid `claimMapping`, or `extraScopes` is not a valid scope list.

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

Update SSO connection

Updates an enterprise single sign-on connection. `clientSecret` is optional: when provided, it replaces the stored secret; when omitted, the existing secret is left unchanged.

**Notes.**
- Changing `emailDomains` clears `domainsVerifiedAt`. Changing `issuer`, `clientId`, `clientSecret` or `emailDomains` clears `provenAt`.

Auth: Bearer token. Permission: `settings.edit`.

Parameters:

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

Request body (`application/json`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `displayName` | string | no |  |
| `vendor` | string | no | Free text, e.g. `okta`, `entra`. |
| `issuer` | string | no | Must start with `https://`. A trailing slash is removed. |
| `clientId` | string | no |  |
| `clientSecret` | string | no | Replaces the stored secret. Omitted or empty keeps it. |
| `emailDomains` | string | no | Comma-separated email domains, e.g. `acme.com,acme.org`. |
| `jitProvisioning` | boolean | no | Default true. |
| `enabled` | boolean | no | Default true. |
| `defaultRoleId` | string \| null | no | You must be able to grant this role for the whole workspace. |
| `claimMapping` | SsoClaimMapping \| null | no | You must be able to grant each rule's role where the rule grants it. Null clears it. |
| `extraScopes` | string \| null | no | Space- or comma-separated scope tokens (at most 20). Null or empty clears them. |

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

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | SsoConnection | An enterprise single sign-on (OpenID Connect) connection. |
| `data.id` | string |  |
| `data.displayName` | string |  |
| `data.vendor` | string | Free text; `generic-oidc` when not set. |
| `data.issuer` | string | The OpenID Connect issuer URL (`https://`, no trailing slash). |
| `data.clientId` | string |  |
| `data.clientSecretPreview` | "••••••••" | Always this mask: the client secret is stored encrypted and is never returned. |
| `data.emailDomains` | string | Comma-separated, lower-case (a leading `@` and a trailing dot are removed). |
| `data.domainsVerifiedAt` | string \| null | When DNS proved the domains; null until verified. An unverified connection is not offered at sign-in. |
| `data.provenAt` | string \| null | When a sign-in first completed with the current issuer, client and domains; null until then. |
| `data.lastSignInAt` | string \| null | The latest sign-in through this connection. Only the list fills it; create and update answer null. |
| `data.jitProvisioning` | boolean | Create a person on their first sign-in. |
| `data.defaultRoleId` | string \| null | The role a new person gets at the workspace root when no rule applies. |
| `data.claimMapping` | SsoClaimMapping \| null |  |
| `data.extraScopes` | string \| null | Scopes requested on top of `openid email profile`, space-separated. |
| `data.lastClaims` | object \| null | The claims of the last sign-in (a person's email, name and groups). Null unless the caller also holds `user.view` for the whole workspace. |
| `data.lastClaimsAt` | string \| null |  |
| `data.enabled` | boolean |  |
| `data.redirectUri` | string | The redirect URI to register in the identity provider. |
| `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 do not hold the permission for the whole workspace (a grant at one location, or in a franchise workspace, is not enough). Also: `defaultRoleId` or a rule's role holds permissions you cannot grant there.

| 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 connection, role or location 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 422: A required field is missing, `issuer` is not `https://`, an invalid `claimMapping`, or `extraScopes` is not a valid scope list.

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

Delete SSO connection

Soft-deletes an enterprise single sign-on connection.

**Notes.**
- Answers `{ ok: true }`, not the `{ id, deleted }` shape of other deletes. The connection is also disabled.

Auth: Bearer token. Permission: `settings.edit`.

Parameters:

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

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

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.ok` | 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 do not hold the permission for the whole workspace (a grant at one location, or in a franchise workspace, is not enough).

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

## GET /v1/sso-connections/{id}/domain-challenge

Get domain verification challenge

Returns the DNS TXT records you must publish to prove control of the email domains used by this SSO connection. The key or person must have `settings.view` for the whole workspace.

Auth: Bearer token. Permission: `settings.view`.

Parameters:

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

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

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.records` | array of object |  |
| `data.records[].domain` | string |  |
| `data.records[].name` | string | The record name: `_brix-verify.<domain>`. |
| `data.records[].type` | "TXT" |  |
| `data.records[].value` | string | The record value to publish: `brix-domain-verify=<token>`. |
| `data.verifiedAt` | string \| null |  |

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 the permission for the whole workspace (a grant at one location, or in a franchise workspace, is not enough).

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

## GET /v1/sso-connections/{id}/scim

Get SCIM configuration

Returns the SCIM base URL for this SSO connection and a preview of its currently live provisioning tokens. Token secrets themselves are never returned.

Auth: Bearer token. Permission: `settings.view`.

Parameters:

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

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

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.baseUrl` | string | The SCIM 2.0 base URL to give the identity provider. |
| `data.tokens` | array of object | Live tokens only. |
| `data.tokens[].id` | string |  |
| `data.tokens[].preview` | string | `••••` and the last four characters. |
| `data.tokens[].createdAt` | string | ISO-8601 timestamp (UTC). |
| `data.tokens[].lastUsedAt` | string \| null |  |

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 the permission for the whole workspace (a grant at one location, or in a franchise workspace, is not enough).

| 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 connection 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/sso-connections/{id}/scim/tokens

Create SCIM token

Creates a new SCIM provisioning token for this SSO connection. The token value is shown exactly once, in this response. A connection can have at most 5 live tokens at a time. This action cannot be taken while impersonating another user.

Auth: Bearer token. Permission: `settings.edit`.

Parameters:

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

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

Response 201: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.id` | string |  |
| `data.token` | string | The SCIM bearer token. Shown only here; store it now. |
| `data.baseUrl` | string |  |

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 the permission for the whole workspace (a grant at one location, or in a franchise workspace, is not enough).

| 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 connection 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: `too_many_tokens`: the connection already has 5 live tokens.

| 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/sso-connections/{id}/scim/tokens/{tokenId}

Revoke SCIM token

Revokes one SCIM provisioning token belonging to this SSO connection.

Auth: Bearer token. Permission: `settings.edit`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | SSO connection id. |
| `tokenId` | path | string | yes | SCIM token id. |

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

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.ok` | 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 do not hold the permission for the whole workspace (a grant at one location, or in a franchise workspace, is not enough).

| 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 connection, or no live token with this id.

| 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/sso-connections/{id}/test-mapping

Test SSO claim mapping

Previews the role and location a person would be granted, given a set of example identity-provider claims, by running the same mapping logic used at sign-in. This is read-only and does not sign anyone in or change anything.

Auth: Bearer token. Permission: `settings.view`.

Parameters:

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

Request body (`application/json`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `claims` | object | yes | Example identity-provider claims. |
| `claimMapping` | SsoClaimMapping \| null | no | An unsaved mapping to test; omitted = the saved one. |

```bash
curl -X POST "https://api.brixsignage.com/v1/sso-connections/{id}/test-mapping" \
  -H "Authorization: Bearer $BRIX_API_KEY" \
  -H "Content-Type: application/json"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.mapped` | boolean | False when there is no mapping: `grants` is then the default role at the root, without names. |
| `data.grants` | array of object |  |
| `data.grants[].nodeId` | string | Location id (the workspace id for the root). |
| `data.grants[].roleId` | string |  |
| `data.grants[].roleName` | string \| null | Absent when `mapped` is false. |
| `data.grants[].nodeName` | string \| null | Absent when `mapped` is false. |
| `data.matchedRules` | array of integer | Indexes of the rules that matched. |
| `data.unmatchedLocations` | array of string | Location values that matched no location. |
| `data.ambiguousLocations` | array of string | Location values that matched more than one location. |
| `data.usedDefault` | boolean |  |
| `data.denied` | boolean | True when the sign-in would be refused (`noMatch: deny`). |

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 the permission for the whole workspace (a grant at one location, or in a franchise workspace, is not enough).

| 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 connection 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 422: `claims` is not an object, or `claimMapping` is invalid.

| 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/sso-connections/{id}/verify-domains

Verify SSO domains

Verifies an SSO connection's email domains using a DNS TXT record lookup. Verified domains control whether the connection is offered at sign-in and whether cross-workspace sign-in is allowed for that domain.

**Notes.**
- Answers 200 with `verified: false` when a domain fails; the connection stays unverified.

Auth: Bearer token. Permission: `settings.edit`.

Parameters:

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

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

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.verified` | boolean | True only when every domain proved out. |
| `data.results` | array of object |  |
| `data.results[].domain` | string |  |
| `data.results[].ok` | boolean |  |
| `data.results[].reason` | string | Why a domain failed: `claimed_by_another_workspace`, `dns_<status>`, or a lookup error. Absent when the lookup answered. |
| `data.verifiedAt` | string \| null |  |

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 the permission for the whole workspace (a grant at one location, or in a franchise workspace, is not enough).

| 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 connection 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 422: `no_domains`: the connection has no email domains.

| 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/sso-connections/test

Test SSO discovery

Fetches the identity provider's OpenID Connect discovery document and returns its parsed authorization, token, and key endpoints, so the connection can be confirmed as reachable before the setup form is submitted.

**Notes.**
- Needs `settings.view` held anywhere, unlike the other SSO routes, which need it for the whole workspace.

Auth: Bearer token. Permission: `settings.view`.

Request body (`application/json`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `issuer` | string | yes | Must start with `https://`. |

```bash
curl -X POST "https://api.brixsignage.com/v1/sso-connections/test" \
  -H "Authorization: Bearer $BRIX_API_KEY" \
  -H "Content-Type: application/json"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.ok` | true |  |
| `data.issuer` | string | The issuer the discovery document names; absent when it names none. |
| `data.authorizationEndpoint` | string |  |
| `data.tokenEndpoint` | string |  |
| `data.jwksUri` | string |  |

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 422: `issuer` is not `https://`.

| 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 502: `discovery_failed` (the provider answered an error), `discovery_incomplete` (an endpoint is missing), or `discovery_threw` (not reachable).

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