Brix

Run your digital signage screens from Claude, ChatGPT or any MCP client. Check which screens are offline and why, put content on screens, schedule playlists, capture screenshots, reboot players and pull proof-of-play reports. Hosted remote server, OAuth sign-in or API key.

Hosted MCP Server

npx add-mcp 'https://api.brixsignage.com/v1/mcp'

Installs into Claude Code, Codex, Cursor and more

Documentation

Connect an AI agent (MCP)

Control Brix digital signage from Claude or ChatGPT over MCP. Sign in, no key. Endpoint, setup, compatible clients and all 15 tools.

For AI agents: the full docs index is at https://brixsignage.com/llms.txt, and every page has a markdown twin (add .md to the path).

Source: https://brixsignage.com/developers/mcp/

Yes: you can control Brix digital signage from Claude or ChatGPT. Brix runs a hosted Model Context Protocol (MCP) server at https://api.brixsignage.com/v1/mcp. You connect by signing in, with no key to copy, and the MCP server is on every plan ($6 per screen per month billed annually, or $8 billed monthly).

  • Works in: claude.ai, Claude Desktop and the Claude mobile apps (Brix is in Claude's connector directory), ChatGPT (Developer mode), Claude Code, Cursor, the Claude API and the OpenAI Responses API. Any client that takes a remote MCP URL works.
  • Sign-in: OAuth 2.1 with PKCE. You pick the workspace, an optional location, and View only or View and change for each area. Or use an API key in developer tools.
  • 15 tools, the same for every client (an API key adds 2 for uploading large files):
    • Every kind of item (screens, media, playlists, schedules, layouts, creatives, apps, data sources, screen groups, locations, folders, emergency templates): list_items, get_items, create_items, update_items, delete_items, restore_items
    • People: see who is in the workspace and their roles (list_items, get_items), and invite a person (create_items)
    • Put content on screens: set_screen_content (lasting), start_takeover and end_takeover (existing content for a while, or an emergency)
    • Find and fix problems: get_screen_status, capture_screenshot, control_screens (refresh, reboot, clear cache, TV on or off)
    • Approvals, data and reports: review_approval (approve, reject or withdraw), refresh_data, get_fleet_report (uptime, proof of play, audit log)
  • Deletes go to the recycle bin for 30 days and come back with restore_items. A permanent delete takes two calls: the first only shows what would be erased and gives a code, and the second must carry that code after you say yes. No access to billing or API keys, and no changes to a person's access.

No Brix account yet? Start a 7-day free trial. No card needed.

The full list with arguments and permissions is in the tool catalog. Which other signage platforms have MCP, and how they compare: Control digital signage from Claude or ChatGPT.

Connection details

SettingValue
Endpointhttps://api.brixsignage.com/v1/mcp
TransportStreamable HTTP. POST one JSON-RPC 2.0 message; the reply is a JSON response. No SSE stream.
AuthSign in with OAuth 2.1 (claude.ai, ChatGPT and other connector apps), or Authorization: Bearer <api key> with the same keys as the REST API.
Protocol versions2024-11-05, 2025-03-26, 2025-06-18
Rate limit30 requests per minute per caller, inside the workspace's 600 per minute. Over it: HTTP 429 with Retry-After.
Methodsinitialize, ping, tools/list, tools/call, resources/list, resources/templates/list, resources/read, prompts/list, prompts/get

There are two ways to connect:

  • Sign in (Claude, ChatGPT). In Claude, find Brix in the connector directory. In ChatGPT, add the endpoint as a plugin. In Claude you can also add it as a custom connector. The app opens a Brix page where you sign in and choose what it may do. No key to copy.
  • API key (Claude Code, Cursor, Claude Desktop config file, the Claude and OpenAI APIs). Create a key and put it in the client's config.

Either way, the permissions you pick decide what the agent can do. See Authentication and scopes.

Find Brix in Claude's connector directory

Brix is listed in Claude's connector directory: claude.ai/directory/connectors/brix. Example requests and what Claude can do: Brix connector for Claude.

  1. In Claude, go to Customize > Connectors, click + and search for Brix. Or open the listing link above.
  2. Click Connect.
  3. Claude opens a Brix page. Sign in, then pick the workspace, a location if you want to limit Claude to one, and the permission areas. Click Allow.

This works in claude.ai, Claude Desktop and the Claude mobile apps, which share the same connectors. On a Team or Enterprise plan, an owner first adds Brix under Organization settings > Connectors, then each member connects it from Customize > Connectors.

Add Brix by URL (ChatGPT plugin, or Claude custom connector)

Use these steps for ChatGPT, or in Claude if you prefer to add the endpoint yourself.

  1. Copy the endpoint: https://api.brixsignage.com/v1/mcp.
  2. Add it to the app.
    • Claude: Customize > Connectors, click +, then Add custom connector. Enter a name and the endpoint, then click Add.
    • ChatGPT: first turn on Developer mode under Settings > Security and login (your workspace policy can hide it). Then go to chatgpt.com/plugins and select +. Enter a name and description, paste the endpoint under Connection, and create it.
  3. The app opens a Brix page. Sign in, then pick the workspace, a location if you want to limit the app to one, and the permission areas. Click Allow.

The app acts as you, with only the areas you picked. Viewing screens is always included, so if you pick no areas, the app can only view screens. You cannot give the app a permission you do not hold.

Whatever areas you pick, an app connected by signing in cannot use billing, payment, invoice, API key or single sign-on routes. It can see people and roles, and it can invite a person, but only with a role that does not manage people, roles, API keys, settings or billing. It cannot change, remove or re-role a person, or edit a role. A person does these in the CMS. API keys are not affected.

Every connection is listed in the CMS under Settings > AI, API & MCP > Connected apps, with who connected it and when it was last used. Revoke ends it at once: the app's next call is refused and it has to be connected again.

For client developers: the server follows the MCP authorization spec. A call without a token gets 401 with a WWW-Authenticate header that points to the protected-resource metadata at /.well-known/oauth-protected-resource/v1/mcp. The authorization server is https://api.brixsignage.com (metadata at /.well-known/oauth-authorization-server). It supports authorization code with PKCE (S256), dynamic client registration and client ID metadata documents. Access tokens last an hour. Refresh tokens rotate on every use. An access token from this flow works only at /v1/mcp.

API key clients

1. Create a key for the agent

In the CMS: Settings > AI, API & MCP > API keys > New key. Give it only the areas the agent needs. For a first try, a view-only key (Screens, Playlists, Schedules, Files, Proof of Play) is safe: the agent can answer questions but cannot change what plays.

The same page has a Model Context Protocol (MCP) section with the endpoint and Copy config and Copy command buttons.

Claude Code

claude mcp add --transport http brix https://api.brixsignage.com/v1/mcp \
  --header "Authorization: Bearer $BRIX_API_KEY"

Then run claude and ask: "Which of my Brix screens are offline, and why?" Check the connection with /mcp.

Claude Desktop

To connect by signing in, connect Brix from Claude's connector directory (see above). To use a key instead: Claude Desktop's config file starts local (stdio) servers, so connect through the mcp-remote bridge. It needs Node.js. Open Settings > Developer > Edit Config and add:

{
  "mcpServers": {
    "brix": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote",
        "https://api.brixsignage.com/v1/mcp",
        "--header", "Authorization:${BRIX_AUTH}"
      ],
      "env": { "BRIX_AUTH": "Bearer ak_us_live_..." }
    }
  }
}

Restart Claude Desktop. The header value goes through env because some platforms split arguments that contain spaces.

Cursor

Add to ~/.cursor/mcp.json (all projects) or .cursor/mcp.json (one project):

{
  "mcpServers": {
    "brix": {
      "url": "https://api.brixsignage.com/v1/mcp",
      "headers": { "Authorization": "Bearer ${env:BRIX_API_KEY}" }
    }
  }
}

${env:BRIX_API_KEY} reads the key from your environment, so it stays out of the file. You can also paste the key in place of it. Open Cursor Settings > MCP to check that brix is connected.

Other clients that accept a remote URL with custom headers use the same shape. This is the config that Copy config in the CMS gives you:

{
  "mcpServers": {
    "brix": {
      "url": "https://api.brixsignage.com/v1/mcp",
      "headers": { "Authorization": "Bearer <YOUR_API_KEY>" }
    }
  }
}

OpenAI API

The OpenAI Responses API sends your key in a header to a remote MCP server:

curl https://api.openai.com/v1/responses \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5",
    "tools": [{
      "type": "mcp",
      "server_label": "brix",
      "server_url": "https://api.brixsignage.com/v1/mcp",
      "headers": { "Authorization": "Bearer '"$BRIX_API_KEY"'" },
      "require_approval": "never"
    }],
    "input": "Which of my screens are offline?"
  }'

"require_approval": "never" lets the model call tools without a pause. Use it with a view-only key, or set it to "always" and approve each call.

Claude API

The Messages API connects to remote MCP servers with the MCP connector:

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: mcp-client-2025-11-20" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5",
    "max_tokens": 4096,
    "mcp_servers": [{
      "type": "url",
      "name": "brix",
      "url": "https://api.brixsignage.com/v1/mcp",
      "authorization_token": "'"$BRIX_API_KEY"'"
    }],
    "tools": [{ "type": "mcp_toolset", "mcp_server_name": "brix" }],
    "messages": [{ "role": "user", "content": "Which of my screens are offline?" }]
  }'

Test the endpoint with curl

curl https://api.brixsignage.com/v1/mcp \
  -H "Authorization: Bearer $BRIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_items","arguments":{"kind":"account"}}}'

A tool result comes back as a text block plus structuredContent. A failed tool call returns isError: true with the error text, so the agent can correct itself.

How the tools behave

  • Start with get_items kind account in an unfamiliar workspace. It returns the key, the workspace and its permissions.
  • Every tool uses the same kind names. list_items finds ids by name; get_items reads items in full. Never guess an id.
  • start_takeover shows existing content for a limited time (default one hour) or fires a prepared emergency. To show a new announcement, make it first with create_items creatives, then pass its id to start_takeover. set_screen_content changes what a screen plays by default.
  • create_items adds items of one kind. It also uploads files you hold (up to 5 files, 6 MB each), copies files from public links (500 MB each) and pairs a device by its 6-digit code.
  • update_items changes one item or many with the same changes: playlist items, schedule blocks, screen group members, screen settings, a web link's crop or refresh. It also moves a screen to a new device (the new device's pairing code) and closes an alert by hand. delete_items moves items to the recycle bin; restore_items brings them back.
  • Tools never take sign-in values. A data feed that needs a sign-in or key (a POS, Stripe, Shopify, HubSpot, a private sheet) is connected by a person in the CMS under Content > Data sources, and tools then use it by its id. create_items makes the feeds that need no sign-in: a CSV URL, a Google Sheet published to anyone with the link, an iCal feed, a public transit stop or a public JSON URL. It refuses the rest and stores nothing. A stored sign-in value reads as ••••••••; sending that back, or leaving it out, keeps what is stored.
  • Repeat for each row (beta, off until we turn it on for your workspace): one design and one data source play as a slideshow, one slide per row. create_items creatives and update_items take dataSourceId and repeat: { over: "rows", secondsPerRow } (3 to 3600 seconds, default 12); repeat: null turns it off. Without the feature, the call is refused with 403 feature_disabled and nothing is saved.
    • Bind to the current row with {{row.column}} in a text box's text, row.column in a field box's field, and {{row.photo}} as an image box's url. A picture cell must be a plain https address. {{rows.0.column}} stays on the first row; update_items useEachRow: true moves those bindings to the current row, like Use each row in the CMS.
    • The column name is the sheet's header in lower case, with accents removed, every other character changed to _, repeats collapsed and none at the ends: Date de début is row.date_de_debut, Région (€) is row.region.
    • At most 200 rows and 3 MB of slides play, and each slide is a whole copy of the design, so a large design fits fewer rows. get_items returns repeatStatus on such a design: rowCount, rowsThatFit, limitedBy, columns, missingColumns and warnings.
    • refresh_data syncs the sheet. A sheet published to the web changes on Google's side up to about 5 minutes after an edit, so a sync straight after an edit can still read the old rows.
    • A Google Sheet published to anyone with the link works by tool. A sheet that needs a sign-in is connected by a person in the CMS, and tools then use it by its id.
  • Every client gets the same tools. An API key also gets mint_upload_url and confirm_media_upload for files over 6 MB. For anything without a tool, use the REST API with the same key.
  • Every tool declares MCP annotations: read-only, destructive, idempotent, and whether it reaches outside Brix. Clients use them to decide when to ask you first.
  • Content created over MCP lands as a draft where the location requires approval.
  • Changes made over MCP are recorded in the workspace audit log. Reads are not.

Things to ask

  • "Which of my screens are offline, and why?"
  • "Why is the lobby screen blank? Show me what it's displaying right now."
  • "Put the Summer Menu playlist on every screen at the Downtown location for the next two hours."
  • "How many times did the Burger Promo play last week, and on which screens?"
  • "Give me this month's uptime report, worst screens first."

Your AI app asks before it runs a tool that changes what a screen shows.

Privacy, disconnecting and support

  • The app sees only what the tools return for the workspace, location and permission areas you approved. Brix does not receive your conversation with the app, only the tool calls it makes.
  • Disconnect any time in Brix under Settings > AI, API & MCP > Connected apps. Revoke ends the connection at once: the app's next call is refused. Removing the connector or plugin in the app stops the app using it.
  • A connection record is deleted 30 days after the connection ends (revoked, refresh token expired, or sign-in never completed). The audit log entries stay for the audit log's normal retention period.
  • Brix does not use data accessed through connected apps to train AI models. The app's provider handles what the app receives under its own terms.
  • How Brix handles data is in the Privacy Policy. For help, email hello@brixsignage.com.
  • To report a security problem, see Reporting a vulnerability.

Tool catalog

17 tools. Each needs the permission shown on the API key. Read-only tools never change anything; the others say what they change.

list_items

List items of one kind with their ids, so other tools can act on them.

When to use:

  • A request names a screen, file, playlist or location and you need its id.
  • Seeing what runs now (casts, emergencies), what is wrong now (alerts), approval requests, deleted items (recycle_bin), or who is in the workspace and what they may do (people, roles).

When NOT to use:

  • Reading one item's contents: use get_items.
  • History over time: use get_fleet_report.

What to know:

  • Rows are short (id, name, nodeId, counts), never configs or members; only locations this connection can see. people rows add each person's role at each location (never sign-in details); roles rows add their permissions.
  • A kind this connection cannot read is an error, not an empty list. people and roles are read-only here (invite with create_items); changing a person's access, roles, API keys and billing stays with a signed-in person in the Brix console, so a connected app can never widen access. To see what this connection itself may do, use get_items kind account.
  • media, alerts and recycle_bin come newest first. Every call counts toward the connection's 30 tool calls a minute, so page with offset instead of re-listing.

Parameters:

  • kind: the list to read; app_types and connectors are what create_items can make. A connectors row's setup is here (create_items makes it) or console (a person connects it in the Brix console, and it is then used by its id).
  • Filters (all optional, combined with AND, ignored on kinds they do not name): q (name contains, any kind but people and roles); status (screens online/offline/pairing; approvals pending by default, approved, rejected, withdrawn); nodeId (screens at that location and below; alerts at that location only); tag (screens, media); mediaKind and folderId (media); severity and screenId (alerts); binKind (recycle_bin).
  • limit: page size for media and approvals (default 50); other kinds give 100 a page. offset: pass nextOffset while hasMore.
  • Example: {kind:"screens", status:"offline", nodeId:"on_west"}.

Permission: the kind's view permission. Behavior: read-only.

ArgumentTypeRequiredDescription
kindscreens | media | playlists | schedules | layouts | creatives | apps | data_sources | screen_groups | locations | media_folders | emergency_templates | casts | emergencies | alerts | approvals | recycle_bin | app_types | connectors | people | rolesyesWhich list.
qstringnoName contains.
statusonline | offline | pairing | pending | approved | rejected | withdrawnnoScreen or approval state.
nodeIdstringnoLocation id.
tagstringnoTag.
mediaKindstringnoMedia type.
folderIdstringnoMedia folder id.
severityinfo | warning | criticalnoAlert severity.
screenIdstringnoScreen id.
binKindstringnoRecycle bin kind.
limitintegernoPage size.
offsetintegernoRows to skip.

get_items

Read items of one kind in full.

When to use:

  • Before update_items, to see current values and part ids (playlist items pli_…, schedule blocks sb_…).
  • Checking how many rows of a sheet a repeating creative plays, and how many fit.
  • Checking an approval request, a fired emergency, a device test run, a person's locations and roles, a role's permissions, or who this connection is (kind account).

When NOT to use:

  • Searching by name: use list_items first.
  • Why a screen is blank, its outages or its TV state: use get_screen_status (get_items returns a screen's settings and assigned content).

What to know:

  • Returns a playlist's items in order, a schedule's blocks, a layout's zones, an app's config, a screen's status and content.
  • A creative that repeats for each row also returns repeatStatus: rowCount and rowsThatFit (what the last sync holds and how many play), limitedBy (rows, bytes or null), loopSeconds, columns (each header and its row tag), missingColumns, firstRowBindings, lastSyncedAt and warnings to pass on. rowCount is null when this connection cannot read that data source.
  • Sign-in details of feeds and web links stay in the Brix console: a stored one reads as ••••••••, never as its value. Items outside this connection's locations read as not found.

Parameters:

  • kind: the kind of every id. account takes no ids and returns this connection's actor, workspace and permissions.
  • ids: prefix-typed ids from list_items (pl_… playlist, scr_… screen, sch_… schedule); required for every kind but account. Each id returns its item or not found; the rest still return.
  • device_test_runs: call again until status is finished.
  • Example: {kind:"playlists", ids:["pl_8f2c"]}.

Permission: the kind's view permission. Behavior: read-only.

ArgumentTypeRequiredDescription
kindscreens | media | playlists | schedules | layouts | creatives | apps | data_sources | screen_groups | locations | media_folders | emergency_templates | approvals | emergencies | device_test_runs | people | roles | accountyesKind of the ids.
idsarraynoItem ids.

create_items

Create new items of one kind.

When to use:

  • Adding anything new, uploading files (media_files for bytes you hold, media_from_urls for public links), pairing a device by its code or inviting a person.

When NOT to use:

  • Changing an existing item: use update_items.
  • Showing it on screens: create it, then use set_screen_content or start_takeover.

What to know:

  • playlists, schedules, locations and creatives are all-or-nothing: one bad row rejects the call and names it (a location whose externalId exists is skipped, so imports re-run safely). Other kinds are created one by one; each reports ok or its error and the rest continue.
  • New content at a location under review starts as a draft. Needs the kind's create permission there.
  • Tools never take sign-in values: a data_sources or apps item holding one, or a feed that cannot work without one, is refused and nothing is created. A person connects those in the Brix console; the feed is then used by its id.
  • Repeat for each row: one design and one data source play as a slideshow, one slide per row of the sheet. Set dataSourceId and repeat {over:"rows", secondsPerRow:12}; seconds run from 3 to 3600, default 12. A workspace without this feature refuses it with 403 feature_disabled and saves nothing, so tell the person instead of retrying.
  • Row tags follow the slide: {{row.column}} in a text box's text, row.column in a field box's field, {{row.photo}} as an image box's url or the backgroundUrl. {{rows.0.column}} stays on the sheet's first row on every slide.
  • The column name in a row tag is the sheet's header in lower case with accents removed, every other character changed to _, repeats collapsed and none at the ends: Date de début is row.date_de_debut, Région (€) is row.region. A missing column or an empty cell reads blank.
  • A picture cell must be a plain https address; anything else leaves that box empty on that slide.
  • At most 200 rows and 3 MB of slides play, and each slide is a whole copy of the design, so a large design fits fewer rows. Afterwards read it with get_items: repeatStatus gives rowCount, rowsThatFit, limitedBy, columns, missingColumns and warnings. Tell the person when rowsThatFit is below rowCount.
  • It reaches screens like any design: set_screen_content or start_takeover with contentKind creative, a schedule block or a layout zone. A playlist item has its own switch: appendItems with refKind creative may answer 403 feature_disabled, and then set_screen_content still works.

Parameters:

  • Pass exactly one array; its name is the kind and two arrays is an error.
  • Ids inside items (refId, contentId, nodeId, screenId, appKey, connector) must already exist; get them from list_items.
  • Example: {playlists:[{name:"Lobby loop", items:[{refKind:"media", refId:"med_logo", durationSec:10}]}]}.

Permission: the kind's create permission. Behavior: creates, reaches outside Brix.

ArgumentTypeRequiredDescription
playlistsarraynoPlaylists to create, max 500 per call.
schedulesarraynoSchedules to create, max 200 per call.
locationsarraynoLocations (org-tree nodes) to create, max 1000 per call. List a parent before its children.
celebrationsarraynoCelebration entries to import, max 100 per call.
creativesarraynoCreatives to create, max 100 per call.
screensarraynoPair new devices, max 20. Each device shows a 6-digit pairing code on its screen; the code pairs it into this workspace.
screen_groupsarraynoScreen groups, max 50. A group is a flat label across locations; it grants no permissions.
web_linksarraynoLive web pages or dashboards for the media library, max 50. Set crop, scroll and refresh after with update_items kind media.
media_from_urlsarraynoFiles (image, video, PDF, audio) copied from public URLs into the media library, max 20 per call and 500 MB each. Copies land at the workspace root, so a connection limited to one location is refused.
media_filesarraynoFiles whose bytes you hold (an image you made, a file the person pasted), uploaded straight into the media library, 6 MB each.
layoutsarraynoMulti-zone layouts, max 50. A new layout starts with one full-screen zone; set zones with update_items (read a layout with get_items to copy its zone shape).
appsarraynoApp instances (weather, clock, menu board, …), max 50. Find valid appKey values with list_items kind app_types.
data_sourcesarraynoData feeds that need no sign-in, max 20: a CSV URL, a Google Sheet published to anyone with the link, an iCal feed, a public transit stop, or a public JSON URL. A feed that needs a sign-in or key (POS, Stripe, Shopify, HubSpot, a private sheet) is connected by a person in the Brix console and refused here; use it by its id from list_items kind data_sources. list_items kind connectors shows each type's setup.
media_foldersarraynoMedia library folders, max 50.
emergency_templatesarraynoPre-armed emergency takeovers (Lockdown, Fire drill, …), max 50. Fire one with start_takeover emergencyTemplateId.
peoplearraynoInvite people, max 10. Each gets an email invitation; inviting someone already here adds the missing locations. Roles that manage people, roles, API keys, settings or billing are refused: a person gives those in the Brix console.
device_test_runsarraynoDevice Lab test runs, max 10. Each queues at once; read results with get_items kind device_test_runs. Suites content, soak, chaos and full change what the screen plays while they run; chaos and full run only on internal or test-tagged screens.

update_items

Change existing items of one kind, the same changes on every id.

When to use:

  • Renaming, moving or retagging items; editing what is inside a playlist, schedule, layout or screen group; screen, web link and data feed settings; swapping a screen's device; closing an alert.
  • Turning Repeat for each row on, changing its seconds or turning it off on a creative (repeat, useEachRow).
  • Franchise menus: share a creative live with child workspaces (shareWithWorkspaces), and set one location's own prices on a creative shared to it (localText) while the owner's design keeps updating.

When NOT to use:

  • Showing content on screens: use set_screen_content.
  • New items: use create_items. Removing: use delete_items.

What to know:

  • Per id, changes run as steps in a fixed order and stop at that id's first failure; earlier steps stay and are named; other ids go on. Each id returns the item as it is now.
  • Content that airs where review is required goes back to draft. Needs the kind's edit permission (screen moves: at both locations).
  • Changes go live at once: every screen playing an edited playlist, schedule or layout shows the new version at its next sync. There is no undo; overwritten values and removed playlist items are gone, so note the old values with get_items first.
  • Repeat for each row: one design and one data source play as a slideshow, one slide per row of the sheet. Set dataSourceId and repeat {over:"rows", secondsPerRow:12}; seconds run from 3 to 3600, default 12. A workspace without this feature refuses it with 403 feature_disabled and saves nothing, so tell the person instead of retrying.
  • Row tags follow the slide: {{row.column}} in a text box's text, row.column in a field box's field, {{row.photo}} as an image box's url or the backgroundUrl. {{rows.0.column}} stays on the sheet's first row on every slide.
  • The column name in a row tag is the sheet's header in lower case with accents removed, every other character changed to _, repeats collapsed and none at the ends: Date de début is row.date_de_debut, Région (€) is row.region. A missing column or an empty cell reads blank.
  • A picture cell must be a plain https address; anything else leaves that box empty on that slide.
  • At most 200 rows and 3 MB of slides play, and each slide is a whole copy of the design, so a large design fits fewer rows. Afterwards read it with get_items: repeatStatus gives rowCount, rowsThatFit, limitedBy, columns, missingColumns and warnings. Tell the person when rowsThatFit is below rowCount.
  • It reaches screens like any design: set_screen_content or start_takeover with contentKind creative, a schedule block or a layout zone. A playlist item has its own switch: appendItems with refKind creative may answer 403 feature_disabled, and then set_screen_content still works.
  • repeat:null turns it off and the design plays once; its row tags then have no row to read, so change them to {{rows.0.column}} in the same call's boxes.

Parameters:

  • kind and ids: every id must be of that kind; get them from list_items. Every id gets identical changes; for different values per item, call once per value.
  • changes: only the fields to change. Each field's description names the kinds that take it; a field the kind does not take is refused before anything changes.
  • zones, boxes, config and tags replace the whole value: read it with get_items first. In config, a value that reads •••••••• (sent back or left out) keeps what is stored; a new sign-in value, or clearing one, is refused because a person changes those in the Brix console.
  • Example: {kind:"screens", ids:["scr_lobby7", "scr_lobby8"], changes:{nodeId:"on_store12", tags:["lobby"]}}.

Permission: the kind's edit permission. Behavior: changes existing data, reaches outside Brix.

ArgumentTypeRequiredDescription
kindscreens | media | playlists | schedules | layouts | creatives | apps | data_sources | screen_groups | locations | media_folders | emergency_templates | alertsyesKind of the ids.
idsarrayyesItem ids.
changesobjectyesFields to change.

delete_items

Delete items of one kind into the 30-day recycle bin.

When to use:

  • The person has confirmed what should go (show it with list_items or get_items first).

When NOT to use:

  • Taking a screen out of a group: use update_items removeScreenIds.
  • Stopping a takeover: use end_takeover.

What to know:

  • restore_items undoes a delete within 30 days. A deleted playlist or schedule stops on every screen playing it; a deleted screen's device returns to pairing; a deleted location hands its content to its parent.
  • Refused: the root, a location with child locations, items shared to another workspace. Each id reports ok or its error. Needs the kind's delete permission.

Parameters:

  • kind and ids: every id must be of that kind.
  • permanent: true erases for good instead (recycle-bin kinds only). The first call deletes nothing and returns wouldErase and a confirmCode.
  • confirmCode: only with permanent:true; send the code from the first call, after the person says yes, with the same kind and ids. It lasts 10 minutes and fits only those ids; without permanent it is an error.
  • Example: {kind:"playlists", ids:["pl_8f2c"]}.

Permission: the kind's delete permission. Behavior: changes existing data, idempotent, reaches outside Brix.

ArgumentTypeRequiredDescription
kindscreens | media | playlists | schedules | layouts | creatives | apps | data_sources | screen_groups | locations | media_folders | emergency_templatesyesKind of the ids.
idsarrayyesItem ids.
permanentbooleannoErase for good.
confirmCodestringnoCode from the first call.

restore_items

Bring back deleted items of one kind from the 30-day recycle bin.

When to use:

  • Undoing delete_items.

When NOT to use:

  • Items deleted more than 30 days ago: they are gone and report not found.

What to know:

  • Whatever used a restored item (playlists, screens) uses it again; a restored screen re-pairs by itself if its device still holds its pairing. Each id reports ok or not found. Needs the kind's delete permission.

Parameters:

  • kind: the list_items kind name, so a recycle_bin row of kind playlist is restored with kind playlists.
  • ids: up to 50, from list_items kind recycle_bin. An id that is not in the bin reports "not in the recycle bin"; one deleted over 30 days ago reports not found.
  • Example: {kind:"playlists", ids:["pl_8f2c"]}.

Permission: the kind's delete permission. Behavior: creates, idempotent, reaches outside Brix.

ArgumentTypeRequiredDescription
kindscreens | media | playlists | schedules | layouts | creatives | apps | data_sources | media_folders | screen_groupsyesKind of the ids.
idsarrayyesDeleted item ids.

set_screen_content

Set what screens play by default until it is changed again.

When to use:

  • Putting a playlist, schedule, app, media file or creative on one screen, a list, or every screen under a location.

When NOT to use:

  • A takeover that ends by itself: use start_takeover.
  • Changing what a playlist contains: use update_items (every screen playing it follows).

What to know:

  • Screens update within seconds. In a bulk call, screens the content is not shared to or whose location needs approval for it are skipped and counted (notShared, needsApproval); the rest change. Needs screen.cast.

Parameters:

  • screenId, screenIds and nodeId (every screen at and under it, up to 1000): give at least one; they combine into one set.
  • contentKind: playlist, schedule, app, media or creative. null clears what plays and needs exactly one screen and no nodeId.
  • contentId: the playlist, schedule, app, media or creative id from list_items; omit only with contentKind null.
  • Example: {nodeId:"on_ca", contentKind:"playlist", contentId:"pl_spring"}.

Permission: none. Behavior: changes existing data, idempotent, reaches outside Brix.

ArgumentTypeRequiredDescription
screenIdstringnoScreen id.
screenIdsarraynoScreen ids.
nodeIdstringnoLocation id.
contentKindplaylist | schedule | app | media | creativeyesContent kind.
contentIdstringnoContent id.

start_takeover

Show something on screens over their normal content, now.

When to use:

  • Existing content for a while (the lunch menu until 2 pm), or a pre-armed emergency.

When NOT to use:

  • A lasting change: use set_screen_content.

What to know:

  • Content ends by itself; an emergency stays above everything until end_takeover, so confirm its scope with the person first. Returns castId for end_takeover. Needs screen.cast.

Parameters:

  • Pass exactly one of: contentId with contentKind (from list_items), or emergencyTemplateId (from list_items kind emergency_templates).
  • scopeKind: all (default, every screen this connection reaches), node with scopeNodeId, or screens with screenIds. An emergency ignores scope and uses its template's.
  • expiresAt: ISO 8601 with offset, default one hour from now.
  • Example: {contentKind:"playlist", contentId:"pl_lunch", scopeKind:"node", scopeNodeId:"on_store12", expiresAt:"2026-10-20T14:00:00-07:00"}.

Permission: screen.cast. Behavior: changes existing data, reaches outside Brix.

ArgumentTypeRequiredDescription
contentKindmedia | playlist | app | creative | schedulenoContent kind.
contentIdstringnoContent id.
emergencyTemplateIdstringnoEmergency template id.
scopeKindall | node | screensnoWhich screens.
scopeNodeIdstringnoLocation id.
screenIdsarraynoScreen ids.
expiresAtstringnoEnd time.
headlinestringnoListing name, with contentId only.

end_takeover

End a running takeover or emergency on every screen it covers, in one call.

When to use:

  • Taking down what start_takeover started, early.

When NOT to use:

  • Changing default content: use set_screen_content.
  • Calling once per screen: one call ends it everywhere.

What to know:

  • Screens return to their normal content at once. Ending one that already ended succeeds with alreadyEnded:true; an unknown id fails and lists what is running. Needs screen.cast (emergency-override.create for an emergency).

Parameters:

  • castId: the castId start_takeover returned, an emergency id (emg_…), the id of the content on screen, or the takeover's exact headline. The last two end every running takeover of it. Find running ones with list_items kind casts or emergencies.

Permission: screen.cast. Behavior: changes existing data, idempotent, reaches outside Brix.

ArgumentTypeRequiredDescription
castIdstringyesTakeover id.

get_screen_status

Explain the state of one screen.

When to use:

  • Why a screen shows what it shows, why it is blank or wrong, when it was down, or what its TV did.

When NOT to use:

  • A screen's settings, tags or assigned content: use get_items kind screens.
  • Many screens: use get_fleet_report. A picture: use capture_screenshot. A fix: use control_screens.

What to know:

  • Reads what the screen already reported and never contacts it, so it answers for offline screens. Needs screen.view.

Parameters:

  • screenId: one scr_… id from list_items kind screens.
  • view: why (the source on screen: deactivated, emergency, takeover, schedule or default); diagnose (likely cause plus a checklist; guided:true marks a TV/input/cable guess, not a reading); outages (each outage with length and cause); tv (TV off, other input, cable lost; Signage Sticks only).
  • days: outages default 30; tv default and max 14. limit: default 50; max 200 for outages, 500 for tv. Both are ignored for why and diagnose.
  • Example: {screenId:"scr_lobby7", view:"outages", days:7}.

Permission: screen.view. Behavior: read-only.

ArgumentTypeRequiredDescription
screenIdstringyesScreen id.
viewwhy | diagnose | outages | tvyesWhat to explain.
daysnumbernoDays to look back.
limitnumbernoMost rows.

capture_screenshot

Take a new screenshot of one screen and return it as an image; nothing on the screen changes.

When to use:

  • Checking what a screen shows, e.g. after set_screen_content or control_screens.

When NOT to use:

  • Learning why: use get_screen_status. Polling in a loop.

What to know:

  • Waits up to about 5 seconds; a slow or offline screen returns its last frame with fresh:false. A frame over 2 MB returns latestFrameUrl only. Needs screen.edit (it sends a device command).

Parameters:

  • screenId: one scr_… id from list_items kind screens; for several screens, call once each. An id outside this connection's reach reads as not found and nothing is sent.

Permission: screen.edit. Behavior: creates, reaches outside Brix.

ArgumentTypeRequiredDescription
screenIdstringyesScreen id.

control_screens

Send a device action to one or many screens.

When to use:

  • A change has not appeared, a screen is frozen, an old or broken file keeps playing, or the TV must go on or off.

When NOT to use:

  • Changing content: use set_screen_content. New data from a feed or web page: use refresh_data.

What to know:

  • Devices act within seconds; offline ones act when they come back. Check the result with capture_screenshot. Needs screen.edit.

Parameters:

  • action: refresh (reload content, no restart; try first), reboot (restart; about a minute blank), clear_cache (delete stored files so they download again; media shows late meanwhile), tv_on or tv_off (HDMI-CEC TVs only; the player keeps running).
  • screenId and screenIds: give either or both; they combine into one set, each screen gets the action once, and screens out of reach are skipped.
  • Example: {action:"refresh", screenIds:["scr_7", "scr_8"]}.

Permission: screen.edit. Behavior: changes existing data, reaches outside Brix.

ArgumentTypeRequiredDescription
actionrefresh | reboot | clear_cache | tv_on | tv_offyesDevice action.
screenIdstringnoScreen id.
screenIdsarraynoScreen ids.

review_approval

Approve, reject or withdraw one pending approval request.

When to use:

  • This connection's person is an approver and a request is waiting (list_items kind approvals; read it with get_items kind approvals).
  • Cancelling a request that is no longer wanted, or one left behind by a person who has gone: withdraw.

When NOT to use:

  • Editing the content: use update_items (an edit opens a new request).

What to know:

  • approve airs the content where it is assigned, at once; reject keeps it off screens; withdraw closes the request with no decision and puts the content back to draft. Each is final.
  • approve and reject need this connection's person to be an approver in the request's chain; withdraw needs the requester or such an approver. Otherwise the call is refused.

Parameters:

  • approvalId: an ar_… id from list_items kind approvals.
  • reason: required with reject and shown to the requester word for word; with approve, an optional note; ignored with withdraw.
  • Example: {approvalId:"ar_12", decision:"reject", reason:"Price on slide 2 is wrong."}.

Permission: an approver for the request. Behavior: changes existing data, reaches outside Brix.

ArgumentTypeRequiredDescription
approvalIdstringyesApproval request id.
decisionapprove | reject | withdrawyesApprove, reject or withdraw.
reasonstringnoWhy.

refresh_data

Pull fresh content now instead of at the next scheduled refresh.

When to use:

  • Before an announcement, so menus and prices are current; after someone edits a sheet that a creative repeats over; after changing a web link's crop with update_items, to see the result.

When NOT to use:

  • Changing a feed's or link's settings: use update_items. A screen that has not picked up a change, or is frozen: use control_screens.

What to know:

  • Settings never change. A failing feed is reported in the result, not thrown. Screens showing the data update when a sync succeeds. Needs integration.edit for feeds, media.edit for a web link.
  • A sheet published to the web changes on Google's side up to about 5 minutes after an edit, so a sync straight after an edit can still read the old rows: synced true says the sync ran, not that the edit arrived. Check rowCount with get_items and sync again after a few minutes if it is old.

Parameters:

  • Pass exactly one; none or two is an error.
  • dataSourceId: one ds_… feed from list_items kind data_sources.
  • allDataSources: true syncs every feed you can edit, one after another; one failure does not stop the rest; limited to 5 calls a minute.
  • webLinkId: one med_… web link (list_items kind media, mediaKind web); renders a new snapshot.

Permission: integration.edit (media.edit for a web link). Behavior: creates, idempotent, reaches outside Brix.

ArgumentTypeRequiredDescription
dataSourceIdstringnoData source id.
allDataSourcesbooleannoEvery data source.
webLinkIdstringnoWeb link id.

get_fleet_report

Read a report across screens for a time window.

When to use:

  • Uptime and outage causes, what actually played, or who changed what.

When NOT to use:

  • One screen's detail: use get_screen_status. What is wrong now: use list_items kind alerts.

What to know:

  • Reads stored history only; only screens this connection can see count. Needs screen.view (uptime), playback-log.view (proof_of_play) or audit-log.view (audit_log).

Parameters:

  • report: uptime (online % per screen, outages with causes, worst first, one-line insights), proof_of_play (plays, watch time, skips and failures per content, and where) or audit_log (who changed what, newest first, by name).
  • from and to: uptime and proof_of_play only; ISO 8601; default the whole stored window (30 days for uptime) up to now; a non-ISO value is refused. screenId: uptime and proof_of_play only, one screen.
  • since, limit (default 50) and cursor (nextCursor of the previous page): audit_log only.
  • Parameters for another report are ignored. Example: {report:"uptime", from:"2026-10-13T00:00:00Z"}.

Permission: per report. Behavior: read-only.

ArgumentTypeRequiredDescription
reportuptime | proof_of_play | audit_logyesWhich report.
fromstringnoWindow start.
tostringnoWindow end.
screenIdstringnoScreen id.
sincestringnoEvents since.
limitintegernoPage size.
cursorstringnoNext page.

mint_upload_url

Get a one-shot upload URL for a large file. The model's host PUTs bytes to it then calls confirm_media_upload with the same uploadId.

Permission: media.create. Behavior: creates.

ArgumentTypeRequiredDescription
contentTypestringyesMIME type of the file, e.g. video/mp4 or image/png.
namestringyesDisplay name for the asset.

confirm_media_upload

Finalize an upload after PUTting bytes to a mint_upload_url. Creates the media_assets row.

Permission: media.create. Behavior: creates.

ArgumentTypeRequiredDescription
uploadIdstringyesThe upl_… id that mint_upload_url returned.
folderIdstringnoLibrary folder to file it in; omit for the library root.
tagsarraynoTags to set on the new asset.

Resources

Read with resources/read. Each one checks its own view permission.

  • brix://screens: Every screen in the workspace, with status + location + assigned content.
  • brix://playlists: Playlists with their items (refKind/refId/duration), approval state and org node.
  • brix://schedules: Time-of-day schedules and which screens they target.
  • brix://alerts/open: Currently open alert events — anything offline / storage-critical / playback-failing.
  • brix://org-tree: The hierarchical org-node structure (Spaces / districts / schools / rooms).
  • brix://media: Images, videos, PDFs, web links and fonts — id, name, kind, folder, tags, state.
  • brix://creatives: Canvas creatives (data-bound designs) with stage + box count.
  • brix://apps: Installed app instances (weather, RSS, menus…) with their app key + node.
  • brix://layouts: Multi-zone layouts a screen can be pointed at, each with its theme (the Brand Kit look — { source: "brand", mode?, radius? } — or null for a bare canvas). Zone geometry, bindings and per-zone frame / radius / role come from GET /v1/layouts/:id.
  • brix://data-sources: Connected data feeds (CSV, Sheets, POS…) with connection + last-sync status.
  • brix://screen-groups: Named saved cohorts of screens — the unit for bulk commands and casts.
  • brix://emergency-templates: Pre-armed emergency takeover recipes; pass one's id to start_takeover as emergencyTemplateId.
  • brix://approvals/pending: Content awaiting an approve/reject decision before it can air.
  • brix://screens/{screenId}/why: The resolution chain (deactivated / emergency / cast / scheduled / default) for one screen.
  • brix://playlists/{playlistId}: One playlist WITH its ordered items.
  • brix://media/{mediaId}/usage: Where a media asset is used (playlists/creatives/layouts) and when it last played.

Prompts

  • fleet_health_report: A daily ops digest: fleet status, open alerts, worst uptime offenders and what to do about each.
  • diagnose_offline_screen: Walk the likely-cause diagnosis for one screen and hand back a guided next-step checklist. Arguments: screen_id (optional).
  • onboard_new_location: Set up a new org location end-to-end: create the node, home its screens there and assign starter content. Arguments: location_name.
  • weekly_content_review: Proof-of-play rollup for the last 7 days: top content, skipped/failed plays, unused media worth pruning.