Brix Approvals API

The approvals API from Brix — 4 operation(s) for approvals.

Operations 5

GET /v1/approvals List approval requests #
POST /v1/approvals Request approval for content #
GET /v1/approvals/{id} Get an approval request #
POST /v1/approvals/{id}/decide Approve or reject content #
POST /v1/approvals/{id}/withdraw Withdraw an approval request #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/brixsignage-com:brixsignage-com-approvals-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

brixsignage-com-approvals-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Brix Approvals 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: Approvals
paths:
  /v1/approvals:
    get:
      operationId: listApprovals
      tags:
      - Approvals
      summary: List approval requests
      description: 'List approval requests the caller is allowed to see, newest first. Use `?state=` to narrow the list to requests still awaiting a decision. The response is capped because approval history only grows over time.


        Newest first. The route needs `screen.view`, but each request is listed only if the caller can also view (or approve) that content kind at its location — e.g. `playlist.view` for a playlist. A `screen.view`-only key gets an empty list.'
      x-brix-permission: screen.view
      x-brix-schema: verified
      parameters:
      - name: state
        in: query
        required: false
        schema:
          type: string
          enum:
          - pending
          - approved
          - rejected
          - withdrawn
        description: Only requests in this state (`pending` = the inbox).
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 500
        description: At most this many (default and max 500).
      responses:
        '200':
          description: Success.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ApprovalRequest'
                required:
                - data
        '401':
          description: Missing, expired or revoked bearer token.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: The token lacks the permission this operation needs (see `x-brix-permission`).
          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
    post:
      operationId: createApproval
      tags:
      - Approvals
      summary: Request approval for content
      description: 'Open an approval request for a piece of content. The authenticated caller becomes the requester. Decide the request with POST /v1/approvals/:id/decide, or cancel it with POST /v1/approvals/:id/withdraw.


        **Notes.**

        - The request goes to the content''s own location; a `nodeId` in the body is ignored. The content''s review state becomes `pending`.

        - The requester is the caller (`apikey:` for a key).'
      x-brix-permission: screen.view
      x-brix-schema: verified
      x-brix-notes:
      - The request goes to the content's own location; a `nodeId` in the body is ignored. The content's review state becomes `pending`.
      - The requester is the caller (`apikey:<id>` for a key).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                contentKind:
                  type: string
                  description: '`playlist`, `schedule`, `creative`, `layout`, `media`, `app`, …'
                contentId:
                  type: string
                contentName:
                  type: string
                note:
                  type: string
                thumbnailUrl:
                  type: string
                changes:
                  description: Used only for content kinds the server does not compare itself.
                  type: array
                  items: {}
              required:
              - contentKind
              - contentId
              - contentName
            example:
              contentKind: playlist
              contentId: pl_2b3c4d5e6f7a8b9c
              contentName: Lobby Loop
              note: New spring menu
      responses:
        '200':
          description: 'Success: A request for this content is already pending: that request is returned and nothing is created.'
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/ApprovalRequest'
                required:
                - data
        '201':
          description: Success.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/ApprovalRequest'
                required:
                - data
        '401':
          description: Missing, expired or revoked bearer token.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: The content is approved, and you do not hold its approve permission (`playlist.approve`, …) at its location.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such content, or it is at a location where you do not hold `screen.view`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: '`contentKind`, `contentId` or `contentName` is missing.'
          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/approvals/{id}:
    get:
      operationId: getApproval
      tags:
      - Approvals
      summary: Get an approval request
      description: 'Retrieve one approval request. If the request falls outside what you are allowed to see, this returns 404 rather than 403, so its existence is not revealed.


        **Notes.**

        - Besides `screen.view`, the caller must be able to view or approve the content kind at its location (e.g. `playlist.view`); otherwise 404.'
      x-brix-permission: screen.view
      x-brix-schema: verified
      x-brix-notes:
      - Besides `screen.view`, the caller must be able to view or approve the content kind at its location (e.g. `playlist.view`); otherwise 404.
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
        description: Approval request id.
        example: ar_3c4d5e6f7a8b9c0d
      responses:
        '200':
          description: Success.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/ApprovalRequest'
                required:
                - data
        '401':
          description: Missing, expired or revoked bearer token.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: The token lacks the permission this operation needs (see `x-brix-permission`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such request, or you cannot view (or approve) that kind of content at its location.
          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/approvals/{id}/decide:
    post:
      operationId: decideApproval
      tags:
      - Approvals
      summary: Approve or reject content
      description: 'Approve or reject a pending approval request. A reason is required when rejecting. The decision is applied to the underlying content''s approval status.


        Decides the CURRENT level of the chain. Approving the last level lets the content air; approving an earlier level passes it to the next. Besides `screen.view`, the caller must be a named approver for the level or hold the content kind''s approve permission (`playlist.approve`, `schedule.approve`, …) at the level''s location.'
      x-brix-permission: screen.view
      x-brix-schema: verified
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
        description: Approval request id.
        example: ar_3c4d5e6f7a8b9c0d
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                decision:
                  type: string
                  enum:
                  - approved
                  - rejected
                decisionNote:
                  description: Required to reject.
                  type: string
              required:
              - decision
      responses:
        '200':
          description: Success.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/ApprovalRequest'
                required:
                - data
        '401':
          description: Missing, expired or revoked bearer token.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Not eligible to decide this level, or `self_approval` (another approver exists).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: '`already_decided`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Bad `decision`, or a rejection without `decisionNote`.
          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/approvals/{id}/withdraw:
    post:
      operationId: withdrawApproval
      tags:
      - Approvals
      summary: Withdraw an approval request
      description: 'Cancel your own pending approval request. The content it was attached to returns to draft status.


        **Notes.**

        - The content''s review state goes back to `draft`.'
      x-brix-permission: screen.view
      x-brix-schema: verified
      x-brix-notes:
      - The content's review state goes back to `draft`.
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
        description: Approval request id.
        example: ar_3c4d5e6f7a8b9c0d
      responses:
        '200':
          description: Success.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/ApprovalRequest'
                required:
                - data
        '401':
          description: Missing, expired or revoked bearer token.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: You are not the requester, and not an approver for the current level.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: '`already_decided`: the request is no longer pending.'
          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.
    ApprovalRequest:
      type: object
      properties:
        id:
          type: string
          description: Approval request id.
          examples:
          - ar_3c4d5e6f7a8b9c0d
        contentKind:
          type: string
          description: '`playlist`, `schedule`, `creative`, `layout`, `creative-override`, `media`, `app`, …'
        contentId:
          type: string
        contentName:
          type: string
        thumbnailUrl:
          anyOf:
          - type: string
          - type: 'null'
        nodeId:
          type: string
          description: The content's home location; empty string for the workspace root.
        nodeName:
          type: string
        requestedByName:
          type: string
        requestedAt:
          type: string
          description: ISO-8601 timestamp (UTC).
        note:
          anyOf:
          - type: string
          - type: 'null'
        changes:
          type: array
          items: {}
          description: 'What changed since the last approved version: `{kind, label, detail, thumbnailUrl?}` items for kinds the server diffs; for other kinds, whatever the requester sent.'
        state:
          type: string
          enum:
          - pending
          - approved
          - rejected
          - withdrawn
        requestedById:
          type: string
          description: User id, or `apikey:<id>` for a key.
        levels:
          type: array
          items:
            type: object
            properties:
              nodeId:
                type: string
              nodeName:
                type: string
              approverIds:
                type: array
                items:
                  type: string
              approverNames:
                type: array
                items:
                  type: string
            required:
            - nodeId
            - nodeName
            - approverIds
            - approverNames
          description: The approval chain, one entry per tier.
        currentLevel:
          type: integer
          description: Index into `levels` awaiting a decision while pending.
        decisions:
          type: array
          items:
            type: object
            properties:
              level:
                type: integer
              decidedById:
                type: string
              decidedByName:
                anyOf:
                - type: string
                - type: 'null'
              decidedAt:
                type: string
                description: ISO-8601 timestamp (UTC).
              note:
                anyOf:
                - type: string
                - type: 'null'
            required:
            - level
            - decidedById
            - decidedByName
            - decidedAt
            - note
        decidedByName:
          anyOf:
          - type: string
          - type: 'null'
        decidedAt:
          anyOf:
          - type: string
            description: ISO-8601 timestamp (UTC).
          - type: 'null'
        decisionNote:
          anyOf:
          - type: string
          - type: 'null'
        canDecide:
          description: 'On the list and single reads: whether the caller may approve or reject the request at its current level (a named approver, or the kind''s approve permission there). False once it is decided.'
          type: boolean
      required:
      - id
      - contentKind
      - contentId
      - contentName
      - thumbnailUrl
      - nodeId
      - nodeName
      - requestedByName
      - requestedAt
      - note
      - changes
      - state
      - requestedById
      - levels
      - currentLevel
      - decisions
      - decidedByName
      - decidedAt
      - decisionNote
      description: A request to approve content before it can air.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: An API key (`ak_…`) or a user session token. API keys carry `resource.verb` permissions.