Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Brix Shares API
version: '1'
summary: 'Control plane for Brix digital signage: screens, content, playlists, schedules, casts and emergencies.'
description: 'Generated from the route table the API actually mounts
(`apps/api/routes.snapshot.json`), the description each route is required to
carry (`tests/contracts/route-registry.json`), and request/response schemas
(`apps/api/src/openapi/`). CI enforces all three, so this document cannot list a
route that does not exist or omit one that does.
**Schemas.** 447 operations carry request and response schemas
(`x-brix-schema: verified`). Each is checked in CI against the response the
real handler returns — a missing, mistyped or undeclared field fails the build.
The remaining operations are route-level only (`x-brix-schema: none`): their
path, method, permission and purpose are exact, but the shape of `data` is not
yet modelled, and we would rather say so than publish a schema that validates
against nothing. Objects are open: a new field is not a breaking change.
**Envelope.** Success bodies are `{ "data": … }`. Failures are
`{ "error": "", "message": "…" }`, plus `requestId` on a 5xx.
Many list routes page opt-in (see each operation): pass `?limit=` and
follow `nextCursor` via `?cursor=` until it is null.
**Authentication.** `Authorization: Bearer ` with an API key
(`ak_…`, created in the console under Settings → API & MCP) or a user session. An
API key carries a creator-chosen set of `resource.verb` permissions and may be
pinned to one location''s subtree; a key can never be granted a permission its
creator does not hold. The permission an operation needs is in
`x-brix-permission` and in its security requirement.
**Tenancy.** Every request resolves to one workspace. A resource belonging to
another tenant returns 404, never 403, so a response never confirms that an
account exists.
**Rate limits.** A 429 carries `Retry-After`; a 503 `database_busy` means retry
after the `Retry-After` interval.'
servers:
- url: https://api.brixsignage.com
description: Production
security:
- bearerAuth: []
tags:
- name: Shares
paths:
/v1/shares/{kind}/{id}:
get:
operationId: getShare
tags:
- Shares
summary: Get a content item's shares
description: 'List the share entries for one content item, showing which nodes, users, and roles it is shared with, and at what level. Requires view, edit, or share permission on the item at its home node.
**Notes.**
- Besides the `screen.view` gate, the caller needs the item''s view, edit or share permission (`playlist.view`, …) at its location; media is checked at the workspace root.
- A row targets one location (and everything under it), one person or one role; a row with no target shares with every location.'
x-brix-permission: screen.view
x-brix-schema: verified
x-brix-notes:
- Besides the `screen.view` gate, the caller needs the item's view, edit or share permission (`playlist.view`, …) at its location; media is checked at the workspace root.
- A row targets one location (and everything under it), one person or one role; a row with no target shares with every location.
parameters:
- name: kind
in: path
required: true
schema:
type: string
description: '`playlist`, `schedule`, `layout`, `creative`, `signage`, `app` or `media`.'
example: playlist
- name: id
in: path
required: true
schema:
type: string
description: The content item's id.
example: pl_2b3c4d5e6f7a8b9c
responses:
'200':
description: Success.
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
type: object
properties:
id:
type: string
targetNodeId:
anyOf:
- type: string
- type: 'null'
targetUserId:
anyOf:
- type: string
- type: 'null'
targetRoleId:
anyOf:
- type: string
- type: 'null'
level:
type: string
enum:
- disabled
- play
- play_local
required:
- id
- targetNodeId
- targetUserId
- targetRoleId
- level
required:
- data
'401':
description: Missing, expired or revoked bearer token.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: You hold none of the item's view, edit or share permission at its location.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: No such item in this workspace.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'422':
description: Unknown `kind`.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
5XX:
description: Server error. The body carries a `requestId` to quote to support.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- bearerAuth:
- screen.view
put:
operationId: replaceShare
tags:
- Shares
summary: Replace a content item's shares
description: 'Replace the entire share set for one content item. Each entry targets exactly one of a node, a user, or a role. Requires the item''s own share permission at its home node.
**Notes.**
- Replaces every share of the item in one step. Besides the `screen.view` gate, the caller needs the kind''s share permission at the item''s location (media: at the workspace root).
- A row targets one location (and everything under it), one person or one role; a row with no target shares with every location.
- The response echoes the request''s rows rather than the stored ones.'
x-brix-permission: screen.view
x-brix-schema: verified
x-brix-notes:
- 'Replaces every share of the item in one step. Besides the `screen.view` gate, the caller needs the kind''s share permission at the item''s location (media: at the workspace root).'
- A row targets one location (and everything under it), one person or one role; a row with no target shares with every location.
- The response echoes the request's rows rather than the stored ones.
parameters:
- name: kind
in: path
required: true
schema:
type: string
description: '`playlist`, `schedule`, `layout`, `creative`, `signage`, `app` or `media`.'
example: playlist
- name: id
in: path
required: true
schema:
type: string
description: The content item's id.
example: pl_2b3c4d5e6f7a8b9c
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
shares:
maxItems: 200
type: array
items:
type: object
properties:
targetNodeId:
anyOf:
- type: string
- type: 'null'
targetUserId:
anyOf:
- type: string
- type: 'null'
targetRoleId:
anyOf:
- type: string
- type: 'null'
level:
type: string
description: '`disabled`, `play` or `play_local`; any other value is stored as `play`.'
required:
- level
description: The whole new share set (at most 200). An empty array removes every share.
required:
- shares
responses:
'200':
description: Success.
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
contentKind:
type: string
enum:
- playlist
- schedule
- layout
- creative
- app
- media
- signage
contentId:
type: string
shares:
type: array
items:
type: object
properties:
targetNodeId:
anyOf:
- type: string
- type: 'null'
targetUserId:
anyOf:
- type: string
- type: 'null'
targetRoleId:
anyOf:
- type: string
- type: 'null'
level:
type: string
required:
- level
additionalProperties: {}
description: 'The share set as SENT, with duplicate targets collapsed (last one wins): `level` is echoed as sent, not as stored, and there are no row ids (read them with GET).'
required:
- contentKind
- contentId
- shares
required:
- data
'401':
description: Missing, expired or revoked bearer token.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: You do not hold the kind's share permission (`playlist.share`, …) at the item's location.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: No such item in this workspace.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'422':
description: Unknown `kind`, more than 200 rows, a row with more than one target, or a person or role that is not in this workspace (a person must be one the share directory offers you).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
5XX:
description: Server error. The body carries a `requestId` to quote to support.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- bearerAuth:
- screen.view
/v1/shares/directory:
get:
operationId: getShareDirectory
tags:
- Shares
summary: List shareable people and roles
description: 'List the people and roles a share can target, including id, display name, and, for roles, colour and member count. Available to anyone holding a share permission on any content, rather than requiring full user directory access. People are listed only from the caller''s own workspace: a franchise child workspace sees its own people, never the parent''s or another child workspace''s. Email is returned only for people the caller can see with the user view permission; otherwise it is null. A role''s member count counts only the listed people.
**Notes.**
- Besides the `screen.view` gate, the caller needs at least one share permission somewhere.
- People stop at the child-workspace line: a franchise child workspace sees only its own people, and the parent does not see into a child. `email` needs `user.view` reaching that person, else it is null.'
x-brix-permission: screen.view
x-brix-schema: verified
x-brix-notes:
- Besides the `screen.view` gate, the caller needs at least one share permission somewhere.
- 'People stop at the child-workspace line: a franchise child workspace sees only its own people, and the parent does not see into a child. `email` needs `user.view` reaching that person, else it is null.'
responses:
'200':
description: Success.
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
users:
type: array
items:
type: object
properties:
id:
type: string
name:
type: string
email:
anyOf:
- type: string
- type: 'null'
description: Only for people the caller could read with `user.view`; null otherwise.
required:
- id
- name
- email
description: People in the workspace the caller shares from (active and invited; never across a franchise child-workspace line).
roles:
type: array
items:
type: object
properties:
id:
type: string
name:
type: string
color:
anyOf:
- type: string
- type: 'null'
memberCount:
type: integer
description: Members among the people listed above.
required:
- id
- name
- color
- memberCount
description: Every role in the workspace.
required:
- users
- roles
required:
- data
'401':
description: Missing, expired or revoked bearer token.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: You hold no share permission (`playlist.share`, `media.share`, …) anywhere.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
5XX:
description: Server error. The body carries a `requestId` to quote to support.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- bearerAuth:
- screen.view
components:
schemas:
Error:
type: object
properties:
error:
type: string
description: 'Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, …'
message:
description: Human-readable explanation. Safe to show an operator.
type: string
requestId:
description: 'Present on 5xx: quote it to support.'
type: string
required:
- error
additionalProperties: {}
description: Error envelope returned by every failing request.
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: An API key (`ak_…`) or a user session token. API keys carry `resource.verb` permissions.