Skip to main content
Version: 9.10 (latest)

API Endpoints

FiestaBoard drives one or more split-flap displays from templated pages.

Hello world​

Put text on the board right now:

curl -X POST http://fiestaboard.local:4420/api/v1/boards/primary/message \
-H 'Content-Type: application/json' \
-d '{"text": "HELLO WORLD"}'

primary works as a board id on every /v1/boards/... path, so a single-board install never has to look one up — and a multi-board install puts the id there instead.

How the pieces fit​

Four nouns, and one way to do each thing:

  • A board is a physical display. POST /v1/boards/{board}/message writes to it; GET /v1/boards/{board} says what is on it and why.

  • A page is a template: literal text plus {{plugin_id.variable}} placeholders that plugins fill with live data.

  • A schedule (or a collection) decides which page a board shows at a given moment. A message write bypasses both for a one-off; DELETE /v1/boards/{board}/message hands the board back to the schedule.

Base URL​

nginx fronts the API under /api, so every path below is reached as /api/<path> — GET /v1/status is http://fiestaboard.local:4420/api/v1/status. These docs live at /api/docs, the schema at /api/openapi.json.

What is not here​

This document is the API to build against: 33 operations. The app serves ~200 more, but they are the web UI's private RPC channel — undocumented on purpose, with no compatibility promise, and liable to change in any release. They are published separately at /api/internal/openapi.json for the UI's own contract checks. Two flat legacy operations, POST /send-message and POST /refresh, remain here because earlier documentation named them; both are deprecated and both name their /v1 replacement in a Link header.

Authentication​

Off by default: a fresh install answers every request. With FIESTABOARD_AUTH_ENABLED=true, a script sends Authorization: Bearer <token> (create one with POST /auth/mcp-token), which is accepted on every /v1 path and on /api/mcp. The web UI instead carries the session cookie that POST /auth/login sets.

Credentials​

Either credential satisfies any request; which one you hold depends on whether you are a script or the web UI. Both are declared in the OpenAPI document, so a client generator picks them up.

SchemeHow it is sentNotes
apiTokenAuthorization: Bearer <token>A FiestaBoard API token, sent as Authorization: Bearer <token>. Create one with POST /auth/mcp-token or pin one out of band with the FIESTABOARD_MCP_TOKEN environment variable. Accepted on every /v1 route and on the MCP endpoint. This is the credential to use from a script: unlike the session cookie it needs no browser login flow. When no token is configured and authentication is disabled, the API is open and no credential is required — the default for a local-only install.
sessionfiestaboard_session (cookie)The browser session cookie issued by POST /auth/login. What the web UI holds. A script should use apiToken instead.

Quick start​

POST /v1/boards/{board}/message is the front door. primary works as a board id on every /v1/boards/... path, so a single-board install never has to look one up.

curl -X POST http://fiestaboard.local:4420/api/v1/boards/primary/message \
-H "Authorization: Bearer $FIESTABOARD_TOKEN" \
-H "Content-Type: application/json" \
-d '{"text": "HELLO WORLD"}'

Drop the Authorization header on an install that has not turned authentication on — it is off by default.

v1​

The FiestaBoard API. Four nouns — board, page, schedule, plugin — and one way to do each thing.

Start here: POST /v1/boards/primary/message with {"text": "HELLO"} puts text on your board. primary works as a board id on every /v1/boards/... path, so a single-board install never needs to look one up.

Every write reports what actually happened rather than merely acknowledging the request: sent is true only when flaps moved, and a reason says why when they did not.

GET /v1/boards​

List the boards this install drives

Every configured board, with the id you use in the rest of this API, its grid size, and whether it is currently paused or following its schedule. The board marked is_primary is the one the literal path segment primary resolves to, so a single-board install never has to look an id up at all.

Responses

StatusBodyDescription
200BoardListResponseSuccessful Response

Errors

StatusBodyMeaning
503ErrorResponseA required dependency is unavailable.

GET /v1/boards/{board}​

Read a board and everything it is doing

One answer to 'what is this board showing, and why'. It merges what the internal API splits across three endpoints: the flaps currently on the board, the page pinned to it by hand, and the page its schedule selects for right now. source says which of the two won. board may be a board id or the literal primary.

Parameters

NameInTypeRequiredDescription
boardpathstringyes—

Responses

StatusBodyDescription
200BoardDetailSuccessful Response

Errors

StatusBodyMeaning
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error
503ErrorResponseA required dependency is unavailable.

PATCH /v1/boards/{board}​

Change how a board behaves

Rename a board, pause or resume it, turn its schedule on or off, or set the page it falls back to when the schedule has a gap. Only the fields you send are applied; omit the rest. Pausing stops every write to the board from every code path — the display loop, schedules, plugin triggers, MQTT and this API alike.

Parameters

NameInTypeRequiredDescription
boardpathstringyes—

Request body (required) — BoardUpdate

FieldTypeRequiredDescription
namestring | nullnoRename the board. (min length 1; max length 100)
pausedboolean | nullnoPause or resume the board. While paused nothing is written to it from any code path.
schedule_enabledboolean | nullnoTurn this board's schedule on or off.
default_page_idstring | nullnoPage shown when the schedule has a gap. Send null to clear it.

Responses

StatusBodyDescription
200BoardDetailSuccessful Response

Errors

StatusBodyMeaning
400ErrorResponseInvalid request — the payload references something that does not exist or violates a rule.
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error
503ErrorResponseA required dependency is unavailable.

POST /v1/boards/{board}/message​

Put something on a board

The one way to write to a board. Send exactly one of text (word-wrapped for you), lines (one string per row), characters (a raw flap grid), page_id (render a saved page) or fill (one flap code everywhere; 0 blanks the board).

Add duration_minutes to make it temporary — it reverts to your schedule, a chosen page, or a blank board when the time is up. board may be a board id or the literal primary.

sent in the response tells you whether flaps actually moved. It is false, with a reason, when the install's output target is UI-only and when the board already showed this exact content. A board that is paused or inside its silence window refuses the write with 409 rather than lying about it.

Parameters

NameInTypeRequiredDescription
boardpathstringyes—

Request body (required) — MessageRequest (v1)

FieldTypeRequiredDescription
textstring | nullnoPlain text, word-wrapped to the board. Colour and character markers ({red}, {63}) work here.
linesarray of string | nullnoOne string per board row, laid out without re-wrapping across rows.
charactersarray of array of integer | nullnoA raw flap grid, sized exactly to the board. Codes are 0-71.
page_idstring | nullnoRender a saved page (or collection) and send the result.
fillinteger | nullnoFill the whole board with one flap code (0-71). 0 blanks it. (0–71)
duration_minutesinteger | nullnoShow this for N minutes, then revert. Omit for a message that stays until something else replaces it. (1–480)
revert_mode"schedule" | "page" | "blank" | nullnoWhat to show when a timed message expires. Requires duration_minutes. Defaults to "schedule".
revert_page_idstring | nullnoThe page to revert to. Required when revert_mode is "page".
transitionTransitionOverride | nullnoOverride the install's transition animation for this one send.
forcebooleannoSend even when the board already shows this exact content, which is normally skipped. (default false)

Responses

StatusBodyDescription
200MessageResponseSuccessful Response

Errors

StatusBodyMeaning
400ErrorResponseInvalid request — the payload references something that does not exist or violates a rule.
404ErrorResponseResource not found.
409ErrorResponseConflict — duplicate id, or a resource pinned by the environment.
422HTTPValidationErrorValidation Error
429ErrorResponseRate-limited — the request arrived inside a minimum interval; see Retry-After.
500ErrorResponseThe server could not complete the operation. Deliberately raised, not an unhandled error.
503ErrorResponseA required dependency is unavailable.

DELETE /v1/boards/{board}/message​

Clear a board and let its schedule take over

Undoes a POST to this path. Any timed message is cancelled, the board's content cache is dropped, and the display loop re-renders whatever the schedule or the pinned page says should be there — which is a blank board when nothing is scheduled. sent reports whether that re-render reached the board.

Parameters

NameInTypeRequiredDescription
boardpathstringyes—

Responses

StatusBodyDescription
200MessageResponseSuccessful Response

Errors

StatusBodyMeaning
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error
500ErrorResponseThe server could not complete the operation. Deliberately raised, not an unhandled error.
503ErrorResponseA required dependency is unavailable.

PUT /v1/boards/{board}/active-page​

Pin a page to a board

Sets the page (or collection) this board shows until something changes it — the sticky selection a schedule falls back from. The page is rendered and sent immediately. Send null to unpin, after which the board follows its schedule again. Pinning a page whose size does not match the board is rejected.

The selection is stored whether or not the send succeeded, so check sent — and error, which names the render or send failure when it is false.

Parameters

NameInTypeRequiredDescription
boardpathstringyes—

Request body (required) — ActivePageRequest

FieldTypeRequiredDescription
page_idstring | nullyesPage or collection to pin to this board. Send null to unpin and fall back to the schedule.

Responses

StatusBodyDescription
200ActivePageResponseSuccessful Response

Errors

StatusBodyMeaning
400ErrorResponseInvalid request — the payload references something that does not exist or violates a rule.
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error
502ErrorResponseAn upstream the server called on your behalf failed.

GET /v1/pages​

List saved pages

Every page saved on this install. A page is a reusable board layout — literal text, plugin variables, or rows composed from other sources — that a schedule, a collection or POST /v1/boards/{board}/message can refer to by id.

Responses

StatusBodyDescription
200PageListResponseSuccessful Response

Errors

StatusBodyMeaning
503ErrorResponseA required dependency is unavailable.

POST /v1/pages​

Create a page

Saves a new page and answers with it, including the generated id you refer to it by. type picks how it is built: template for text with {{variables}}, single for one plugin's output, composite for rows drawn from several sources. device_type must match the boards you intend to show it on.

Request body (required) — PageCreate

FieldTypeRequiredDescription
namestringyesmin length 1; max length 100
type"single" | "composite" | "template"yes—
device_type"flagship" | "note" | "note_array" | "panel"nodefault "flagship"
display_typestring | nullno—
rowsarray of RowConfig | nullno—
templatearray of string | nullno—
line_metadataarray of LineMetadata | nullno—
duration_secondsintegerno10–3600; default 300
transition_strategystring | nullno—
transition_interval_msinteger | nullno0–5000
transition_step_sizeinteger | nullnomin 1
demo_plugin_idstring | nullno—
notes_wideinteger | nullno1–8
notes_tallinteger | nullno1–8
grid_rowsinteger | nullno3–96
grid_colsinteger | nullno15–128

Responses

StatusBodyDescription
201PageSuccessful Response

Errors

StatusBodyMeaning
400ErrorResponseInvalid request — the payload references something that does not exist or violates a rule.
422HTTPValidationErrorValidation Error

GET /v1/pages/{page_id}​

Read one page

The saved page with this id, exactly as stored — its type, its device size, its template or row configuration, and its per-page transition overrides. 404 when no page has this id.

Parameters

NameInTypeRequiredDescription
page_idpathstringyes—

Responses

StatusBodyDescription
200PageSuccessful Response

Errors

StatusBodyMeaning
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error

PUT /v1/pages/{page_id}​

Update a page

Applies the fields you send and leaves the rest alone. The response carries the updated page plus incompatible_references: places that still point at this page — a board's schedule, a pinned selection — where your change has made the size no longer fit.

Parameters

NameInTypeRequiredDescription
page_idpathstringyes—

Request body (required) — PageUpdate

FieldTypeRequiredDescription
namestring | nullnomin length 1; max length 100
device_type"flagship" | "note" | "note_array" | "panel" | nullno—
display_typestring | nullno—
rowsarray of RowConfig | nullno—
templatearray of string | nullno—
line_metadataarray of LineMetadata | nullno—
duration_secondsinteger | nullno10–3600
transition_strategystring | nullno—
transition_interval_msinteger | nullno0–5000
transition_step_sizeinteger | nullnomin 1
notes_wideinteger | nullno1–8
notes_tallinteger | nullno1–8
grid_rowsinteger | nullno3–96
grid_colsinteger | nullno15–128

Responses

StatusBodyDescription
200PageUpdateResponseSuccessful Response

Errors

StatusBodyMeaning
400ErrorResponseInvalid request — the payload references something that does not exist or violates a rule.
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error

DELETE /v1/pages/{page_id}​

Delete a page

Removes the page. If it was the last page, or was the one a board was showing, the response says what was put in its place so nothing is left pointing at a page that no longer exists.

Parameters

NameInTypeRequiredDescription
page_idpathstringyes—

Responses

StatusBodyDescription
200PageDeleteResponseSuccessful Response

Errors

StatusBodyMeaning
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error

GET /v1/schedules​

List schedule entries

Every schedule entry, newest rules included. Pass board_id to narrow it to one board, or * for all boards at once. Each entry says which page shows between which times, on which days; the response also carries the fallback page and whether scheduling is switched on.

Parameters

NameInTypeRequiredDescription
board_idquerystring | nullno—

Responses

StatusBodyDescription
200ScheduleListResponseSuccessful Response

Errors

StatusBodyMeaning
422HTTPValidationErrorValidation Error
503ErrorResponseA required dependency is unavailable.

POST /v1/schedules​

Create a schedule entry

Adds a rule: show page_id from start_time to end_time on the days day_pattern selects. Times may also be relative to sunrise or sunset (start_type, start_sun_offset), and a rule may recur weekly, on a calendar date every year, or once. Omit board_id for the default board.

Request body (required) — ScheduleCreate

FieldTypeRequiredDescription
board_idstringnomin length 0; default ""
page_idstringyesmin length 1
start_timestringyes—
end_timestring | nullno—
day_pattern"all" | "weekdays" | "weekends" | "custom"nodefault "all"
custom_daysarray of string | nullno—
enabledbooleannodefault true
recurrence_type"weekly" | "annual_date" | "one_off_date"nodefault "weekly"
annual_datestring | nullno—
annual_end_datestring | nullno—
one_off_datestring | nullno—
one_off_end_datestring | nullno—
start_type"fixed" | "sunrise" | "sunset"nodefault "fixed"
start_sun_offsetintegernodefault 0
end_type"fixed" | "sunrise" | "sunset"nodefault "fixed"
end_sun_offsetintegernodefault 0

Responses

StatusBodyDescription
201ScheduleWriteResponseSuccessful Response

Errors

StatusBodyMeaning
400ErrorResponseInvalid request — the payload references something that does not exist or violates a rule.
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error

GET /v1/schedules/{schedule_id}​

Read one schedule entry

The schedule entry with this id, plus resolved_start_time and resolved_end_time — the wall-clock times a sunrise- or sunset-relative rule works out to today.

Parameters

NameInTypeRequiredDescription
schedule_idpathstringyes—

Responses

StatusBodyDescription
200ScheduleResponseSuccessful Response

Errors

StatusBodyMeaning
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error

PUT /v1/schedules/{schedule_id}​

Update a schedule entry

Applies only the fields you send, so a partial update cannot silently clear the ones you left out. warnings reports rules that now overlap or leave a gap without failing the write.

Parameters

NameInTypeRequiredDescription
schedule_idpathstringyes—

Request body (required) — ScheduleUpdate

FieldTypeRequiredDescription
board_idstring | nullnomin length 0
page_idstring | nullnomin length 1
start_timestring | nullno—
end_timestring | nullno—
day_pattern"all" | "weekdays" | "weekends" | "custom" | nullno—
custom_daysarray of string | nullno—
enabledboolean | nullno—
recurrence_type"weekly" | "annual_date" | "one_off_date" | nullno—
annual_datestring | nullno—
annual_end_datestring | nullno—
one_off_datestring | nullno—
one_off_end_datestring | nullno—
start_type"fixed" | "sunrise" | "sunset" | nullno—
start_sun_offsetinteger | nullno—
end_type"fixed" | "sunrise" | "sunset" | nullno—
end_sun_offsetinteger | nullno—

Responses

StatusBodyDescription
200ScheduleWriteResponseSuccessful Response

Errors

StatusBodyMeaning
400ErrorResponseInvalid request — the payload references something that does not exist or violates a rule.
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error

DELETE /v1/schedules/{schedule_id}​

Delete a schedule entry

Removes this schedule rule. The page it pointed at is untouched, and the board falls back to its remaining rules — or to its gap page when none of them match.

Parameters

NameInTypeRequiredDescription
schedule_idpathstringyes—

Responses

StatusBodyDescription
200ScheduleDeleteResponseSuccessful Response

Errors

StatusBodyMeaning
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error

GET /v1/collections​

List the saved collections

Every collection. A collection is a set of pages plus a rule for choosing between them — rotate on a timer, pick at random, or switch on the value of a template expression — and it can be used anywhere a page id can.

Responses

StatusBodyDescription
200CollectionListResponseSuccessful Response

Errors

StatusBodyMeaning
503ErrorResponseA required dependency is unavailable.

POST /v1/collections​

Create a collection

Saves a set of pages and how to choose between them. selection_mode is time (rotate every time.interval_seconds), random, or variable (evaluate variable.rules in order and show the first match). The generated id is prefixed collection: and is usable wherever a page id is.

Request body (required) — CollectionCreate

FieldTypeRequiredDescription
namestringyesmin length 1; max length 100
page_idsarray of stringyes—
selection_mode"time" | "variable" | "random"nodefault "time"
timeTimeModeConfigno—
variableVariableModeConfig | nullno—
randomRandomModeConfig | nullno—

Responses

StatusBodyDescription
201CollectionSuccessful Response

Errors

StatusBodyMeaning
400ErrorResponseInvalid request — the payload references something that does not exist or violates a rule.
422HTTPValidationErrorValidation Error

GET /v1/collections/{collection_id}​

Read one collection

The collection with this id: its member page ids in order, its selection mode, and the config block for that mode — the rotation interval, or the expression rules that pick between the members.

Parameters

NameInTypeRequiredDescription
collection_idpathstringyes—

Responses

StatusBodyDescription
200CollectionSuccessful Response

Errors

StatusBodyMeaning
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error

PUT /v1/collections/{collection_id}​

Update a collection

Applies only the fields you send. Every page id in page_ids must exist, and a variable rule may only point at a page the collection contains.

Parameters

NameInTypeRequiredDescription
collection_idpathstringyes—

Request body (required) — CollectionUpdate

FieldTypeRequiredDescription
namestring | nullnomin length 1; max length 100
page_idsarray of string | nullno—
selection_mode"time" | "variable" | "random" | nullno—
timeTimeModeConfig | nullno—
variableVariableModeConfig | nullno—
randomRandomModeConfig | nullno—

Responses

StatusBodyDescription
200CollectionSuccessful Response

Errors

StatusBodyMeaning
400ErrorResponseInvalid request — the payload references something that does not exist or violates a rule.
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error

DELETE /v1/collections/{collection_id}​

Delete a collection

Removes the collection. Its member pages are untouched, but anything still referring to the collection id — a schedule rule, a board's pinned selection — will stop resolving.

Parameters

NameInTypeRequiredDescription
collection_idpathstringyes—

Responses

StatusBodyDescription
200CollectionDeleteResponseSuccessful Response

Errors

StatusBodyMeaning
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error

GET /v1/variables​

List every name a template can use

The whole template vocabulary in one answer: the built-in variables, everything the installed plugins contribute, the colour and symbol names, and the filters you can apply. max_lengths gives the longest value each variable can render to, which is what you size a row against.

Responses

StatusBodyDescription
200VariableCatalogSuccessful Response

Errors

StatusBodyMeaning
503ErrorResponseA required dependency is unavailable.

GET /v1/functions​

List the functions a template expression can call

Every function usable inside {{ }} — conditionals, arithmetic, text and date helpers — with its signature and a one-line summary. This is the reference for writing a collection's variable rules as well as for page templates.

Responses

StatusBodyDescription
200FormulaFunctionsResponseSuccessful Response

Errors

StatusBodyMeaning
503ErrorResponseA required dependency is unavailable.

POST /v1/render​

Render a template without saving or sending it

Runs a template against the current data and gives you back the text, so you can see what a page would look like before you save it. Pass board — a board id or primary — to lay it out for that board's size, or device_type to lay it out for a hardware shape you have not configured a board for; without either, the template is rendered at the default flagship geometry. Nothing is written to any board.

Parameters

NameInTypeRequiredDescription
boardquerystring | nullnoBoard id, or 'primary', whose geometry the template should be laid out for.
device_typequery"flagship" | "note" | "note_array" | nullnoBoard shape to lay the template out for, as an alternative to naming a board. Use this to preview a page whose device type no configured board has.

Request body (required) — RenderRequest

FieldTypeRequiredDescription
templatestring | array of stringyesA template string, or one string per board row. A list is padded to the board's height.
line_metadataarray of object | nullnoPer-line alignment and wrap options, one entry per line.

Responses

StatusBodyDescription
200TemplateRenderResponseSuccessful Response

Errors

StatusBodyMeaning
400ErrorResponseInvalid request — the payload references something that does not exist or violates a rule.
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error

GET /v1/health​

Check that the instance is up

Answers 200 whenever the process is serving. service_running reports whether the display loop — the thing that actually drives the boards — is running, which is separate from the API being reachable.

Responses

StatusBodyDescription
200HealthResponseSuccessful Response

Errors

StatusBodyMeaning
503ErrorResponseA required dependency is unavailable.

GET /v1/status​

Read the display loop's state, board by board

What the instance is doing: whether the display loop is running, a summary of the resolved configuration, and per board whether it has a working connection, whether it is paused, which page it is showing, and why it failed to start if it did.

Responses

StatusBodyDescription
200StatusResponseSuccessful Response

Errors

StatusBodyMeaning
503ErrorResponseA required dependency is unavailable.

GET /v1/plugins​

List installed plugins

Every plugin installed on this instance, whether or not it is switched on. A plugin is a data source — weather, transit, a stock ticker — that contributes template variables you can put on a page. enabled says whether it runs; configured says whether its required settings have been filled in.

Responses

StatusBodyDescription
200PluginListResponseSuccessful Response

Errors

StatusBodyMeaning
503ErrorResponseA required dependency is unavailable.

GET /v1/plugins/{plugin_id}​

Read one plugin

Everything about one plugin: its manifest, the template variables it contributes and their maximum lengths, its settings schema, and its current configuration. Stored secrets come back masked as ***; sending that value back in a PATCH leaves the stored secret untouched.

Parameters

NameInTypeRequiredDescription
plugin_idpathstringyes—

Responses

StatusBodyDescription
200PluginDetailSuccessful Response

Errors

StatusBodyMeaning
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error
503ErrorResponseA required dependency is unavailable.

PATCH /v1/plugins/{plugin_id}​

Enable, disable or configure a plugin

One call for the three things you can do to a plugin. Send enabled to switch it on or off, config to replace its settings, or both — the settings are written first so a plugin is never enabled with a configuration you meant to replace. Answers with the plugin's full detail, secrets masked.

Parameters

NameInTypeRequiredDescription
plugin_idpathstringyes—

Request body (required) — PluginUpdate

FieldTypeRequiredDescription
enabledboolean | nullnoEnable or disable the plugin.
configobject | nullnoReplace the plugin's settings. Send "***" for a secret you do not want to change.

Responses

StatusBodyDescription
200PluginDetailSuccessful Response

Errors

StatusBodyMeaning
400ErrorResponseInvalid request — the payload references something that does not exist or violates a rule.
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error
503ErrorResponseA required dependency is unavailable.

GET /v1/plugins/{plugin_id}/data​

Read what a plugin is currently reporting

The plugin's latest fetch, in both forms the internal API served separately: data is the raw variable payload a template reads from, and lines/text are the board-ready rendering of it. available is false, with error set, when the plugin is disabled, unconfigured, or its source could not be reached — a 200, because that is the answer to the question asked. 404 means no such plugin is installed.

Parameters

NameInTypeRequiredDescription
plugin_idpathstringyes—

Responses

StatusBodyDescription
200PluginDataSuccessful Response

Errors

StatusBodyMeaning
400ErrorResponseInvalid request — the payload references something that does not exist or violates a rule.
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error
503ErrorResponseA required dependency is unavailable.

POST /v1/plugins/{plugin_id}/receive​

Push data into a plugin from outside

The webhook target. A plugin that declares a receive handler accepts a JSON body here and updates what it reports without polling anything — the way to drive a board from a system FiestaBoard cannot reach out to. 405 means this plugin does not accept pushes.

Parameters

NameInTypeRequiredDescription
plugin_idpathstringyes—

Responses

StatusBodyDescription
200PluginReceiveResponseSuccessful Response

Errors

StatusBodyMeaning
400ErrorResponseInvalid request — the payload references something that does not exist or violates a rule.
403ErrorResponseForbidden.
404ErrorResponseResource not found.
405ErrorResponseThe resource exists but does not implement this operation.
422HTTPValidationErrorValidation Error
503ErrorResponseA required dependency is unavailable.

service​

The display loop itself: health, status, start/stop/refresh.

POST /refresh​

Refresh Display

Manually trigger a display refresh.

Args: board_id: Optional board to refresh (query param, or {"board_id": ...} in the JSON body). Omitted → legacy behavior: refresh every board, primary first (issue #1244).

Parameters

NameInTypeRequiredDescription
board_idquerystring | nullno—

Request body (optional) — RefreshRequest | null

No fields.

Responses

StatusBodyDescription
200RefreshResponseSuccessful Response

Errors

StatusBodyMeaning
404ErrorResponseResource not found.
422HTTPValidationErrorValidation Error
500ErrorResponseThe server could not complete the operation. Deliberately raised, not an unhandled error.
503ErrorResponseA required dependency is unavailable.

board​

Write to a board out of band, and read back what is physically on it.

POST /send-message​

Send Message

Send a custom message to a board.

board_id (optional) targets one board; omitted → the primary board, which is what every caller got before this endpoint could address a second one (issue #1247). Gate for gate this is the same policy the MCP executor applies — see src/ops/executors.py.

The v1 successor takes the board in the path, so it cannot be forgotten, and reports whether flaps actually moved rather than only that the request was accepted.

Request body (required) — MessageRequest (board_api)

FieldTypeRequiredDescription
textstringyes—
board_idstring | nullno—

Responses

StatusBodyDescription
200SendResponseSuccessful Response

Errors

StatusBodyMeaning
404ErrorResponseResource not found.
409ErrorResponseConflict — duplicate id, or a resource pinned by the environment.
422HTTPValidationErrorValidation Error
429ErrorResponseRate-limited — the request arrived inside a minimum interval; see Retry-After.
500ErrorResponseThe server could not complete the operation. Deliberately raised, not an unhandled error.
503ErrorResponseA required dependency is unavailable.

Schemas​

Every model the operations above name, expanded once.

ActivePageRequest​

PUT /v1/boards/{board}/active-page.

FieldTypeRequiredDescription
page_idstring | nullyesPage or collection to pin to this board. Send null to unpin and fall back to the schedule.

ActivePageResponse​

The result of pinning a page to a board.

FieldTypeRequiredDescription
board_idstringyes—
page_idstring | nullyesWhat is now pinned. Null means nothing is.
sentbooleanyesWhether the new page reached the board immediately.
errorstring | nullnoWhy the page did not reach the board, when sent is false. The selection is stored either way, so a 200 here is not on its own proof that anything was displayed (#1791).
warningsarray of stringnoNon-fatal problems, e.g. collection members that do not fit this board.

BoardDetail​

One board plus what is on it right now.

The merge named in the design: GET /board/current-message (what the flaps show), GET /settings/active-page (the sticky manual selection) and GET /schedules/active/page (what the schedule says) answered as one document, because "what is this board doing" is one question.

FieldTypeRequiredDescription
idstringyesThe board's stable id. Usable anywhere {board} appears in a v1 path.
namestringyesHuman-readable board name, as set in Settings.
device_typestringyesBoard hardware: "flagship", "note", or "note_array".
rowsintegeryesGrid height in flaps.
colsintegeryesGrid width in flaps.
is_primarybooleanyesTrue for the board that the alias "primary" resolves to.
pausedbooleanyesWhile true, FiestaBoard writes nothing to this board from any code path.
schedule_enabledbooleanyesWhether this board follows its schedule rather than a fixed page.
charactersarray of array of integer | nullnoThe flap codes currently on the board, or null if nothing has been sent to it yet.
textstring | nullnocharacters decoded back to text, for reading. Null when characters is null.
expected_charactersarray of array of integer | nullnoThe flap grid FiestaBoard last sent to this board. When it differs from characters the board has drifted from what was sent — a flap that did not turn, or something else writing to the board. Null until this instance has sent anything.
read_atstring | nullnoWhen characters was read from the board (ISO 8601). Null for a live read.
active_page_idstring | nullnoThe page or collection pinned to this board by hand, if any.
scheduled_page_idstring | nullnoThe page the schedule selects for right now. Null when scheduling is off or nothing matches.
resolved_page_idstring | nullnoThe page actually being displayed, with any collection resolved to a member page.
resolved_next_check_secondsinteger | nullnoSeconds until resolved_page_id may change on its own, when it came from a collection that rotates. Poll again after this long rather than on a guessed timer. Null for a plain page and for a collection that cannot rotate.
source"manual" | "schedule" | "none"yesWhere resolved_page_id came from.
default_page_idstring | nullnoThe page shown when the schedule has a gap.
override_expires_atstring | nullnoWhen the active timed message expires (ISO 8601), or null if none is running.

BoardListResponse​

GET /v1/boards.

FieldTypeRequiredDescription
boardsarray of BoardSummaryyes—
totalintegeryes—

BoardStatus​

Per-board runtime state (issue #1244).

FieldTypeRequiredDescription
configuredbooleanyes—
pausedbooleanyes—
active_page_idstring | nullno—
errorstring | nullno—

BoardSummary​

One board, as a consumer needs to see it.

A deliberate projection, not the stored entry: GET /settings/board serves connection credentials (masked) and per-tile wiring, none of which a consumer writing to a board has any use for.

FieldTypeRequiredDescription
idstringyesThe board's stable id. Usable anywhere {board} appears in a v1 path.
namestringyesHuman-readable board name, as set in Settings.
device_typestringyesBoard hardware: "flagship", "note", or "note_array".
rowsintegeryesGrid height in flaps.
colsintegeryesGrid width in flaps.
is_primarybooleanyesTrue for the board that the alias "primary" resolves to.
pausedbooleanyesWhile true, FiestaBoard writes nothing to this board from any code path.
schedule_enabledbooleanyesWhether this board follows its schedule rather than a fixed page.

BoardUpdate​

PATCH /v1/boards/{board} — every field optional, only what you send is applied.

FieldTypeRequiredDescription
namestring | nullnoRename the board. (min length 1; max length 100)
pausedboolean | nullnoPause or resume the board. While paused nothing is written to it from any code path.
schedule_enabledboolean | nullnoTurn this board's schedule on or off.
default_page_idstring | nullnoPage shown when the schedule has a gap. Send null to clear it.

Collection​

A collection – an ordered set of pages plus a selection mode.

FieldTypeRequiredDescription
idstringno—
namestringyesmin length 1; max length 100
page_idsarray of stringyes—
selection_mode"time" | "variable" | "random"nodefault "time"
timeTimeModeConfigno—
variableVariableModeConfig | nullno—
randomRandomModeConfig | nullno—
created_atdate-timeno—
updated_atdate-time | nullno—

CollectionCreate​

Request model for creating a new collection.

FieldTypeRequiredDescription
namestringyesmin length 1; max length 100
page_idsarray of stringyes—
selection_mode"time" | "variable" | "random"nodefault "time"
timeTimeModeConfigno—
variableVariableModeConfig | nullno—
randomRandomModeConfig | nullno—

CollectionDeleteResponse​

Body of DELETE /collections/{collection_id}.

Per docs/internal/reference/API_CONVENTIONS.md a delete answers 200 with the deleted resource id (this domain's choice) — not a {"status": "success"} envelope.

FieldTypeRequiredDescription
idstringyes—

CollectionListResponse​

Body of GET /collections.

FieldTypeRequiredDescription
collectionsarray of Collectionyes—
totalintegeryes—

CollectionUpdate​

Request model for updating an existing collection.

FieldTypeRequiredDescription
namestring | nullnomin length 1; max length 100
page_idsarray of string | nullno—
selection_mode"time" | "variable" | "random" | nullno—
timeTimeModeConfig | nullno—
variableVariableModeConfig | nullno—
randomRandomModeConfig | nullno—

ErrorResponse​

The single error body the API serves.

FieldTypeRequiredDescription
detailstringyes—

FormulaFunctionEntry​

One built-in formula function, as the function picker lists it.

FieldTypeRequiredDescription
categorystringyes—
signaturestringyes—
summarystringyes—

FormulaFunctionsResponse​

GET /templates/formula-functions.

FieldTypeRequiredDescription
functionsobject of FormulaFunctionEntryyes—

HealthResponse​

GET|HEAD /health — the liveness probe nginx, Docker and the boot gate all use.

FieldTypeRequiredDescription
statusstringyes—
service_runningbooleanyes—
versionstringyes—

HTTPValidationError​

FieldTypeRequiredDescription
detailarray of ValidationErrorno—

IncompatibleReference​

A reference left pointing this page at a board it no longer fits.

Produced by a device/size retarget (issue #1250, extended by #1788). Warn-only: the backend never mutates or removes the reference.

FieldTypeRequiredDescription
board_idstringyes—
board_namestringyes—
surface"schedule" | "active_page" | "silence"yes—
schedule_idstring | nullno—

LineMetadata​

Per-line formatting metadata for template pages.

Stores alignment and wrap settings that were previously encoded as inline prefixes ({center}, {wrap}, etc.) in the template strings.

FieldTypeRequiredDescription
alignment"left" | "center" | "right"nodefault "left"
wrapbooleannodefault false

MessageRequest (board_api)​

Body of POST /send-message.

board_id closes the gap that made board 2 unreachable over HTTP: the MCP executor (src.ops.executors.send_message) has taken one since issue #1765, and this endpoint — the one the published docs recommend — had no spelling for it. Omitted → the primary board, which is exactly what every existing caller gets today.

FieldTypeRequiredDescription
textstringyes—
board_idstring | nullno—

MessageRequest (v1)​

POST /v1/boards/{board}/message — the front door.

Exactly one of text, lines, characters, page_id or fill says what to show. Everything else says how long and how.

FieldTypeRequiredDescription
textstring | nullnoPlain text, word-wrapped to the board. Colour and character markers ({red}, {63}) work here.
linesarray of string | nullnoOne string per board row, laid out without re-wrapping across rows.
charactersarray of array of integer | nullnoA raw flap grid, sized exactly to the board. Codes are 0-71.
page_idstring | nullnoRender a saved page (or collection) and send the result.
fillinteger | nullnoFill the whole board with one flap code (0-71). 0 blanks it. (0–71)
duration_minutesinteger | nullnoShow this for N minutes, then revert. Omit for a message that stays until something else replaces it. (1–480)
revert_mode"schedule" | "page" | "blank" | nullnoWhat to show when a timed message expires. Requires duration_minutes. Defaults to "schedule".
revert_page_idstring | nullnoThe page to revert to. Required when revert_mode is "page".
transitionTransitionOverride | nullnoOverride the install's transition animation for this one send.
forcebooleannoSend even when the board already shows this exact content, which is normally skipped. (default false)

MessageResponse​

What a v1 board write actually did.

sent is the honest answer, not an acknowledgement: it is false when the install's output target is UI-only, when the board already showed this exact content, and when a timed message was queued for the display loop rather than written on the spot. reason names which.

FieldTypeRequiredDescription
sentbooleanyesTrue only when flaps were written to the physical board by this request.
board_idstringyesThe board this went to, with the 'primary' alias already resolved.
charactersarray of array of integeryesThe flap grid this request produced, sent or not.
textstringyescharacters decoded back to text, for logs and confirmations.
expires_atstring | nullnoWhen a timed message reverts (ISO 8601). Null for a message with no duration.
reasonstring | nullnoWhy nothing was written, when sent is false. Null when sent is true.

Page​

A saved page configuration.

Pages can be one of three types:

  • single: Displays a single source type
  • composite: Combines specific rows from multiple sources
  • template: Custom content with templated variables

Each page targets a specific device type (flagship: 22x6, note: 15x3).

FieldTypeRequiredDescription
idstringno—
namestringyesmin length 1; max length 100
type"single" | "composite" | "template"yes—
device_type"flagship" | "note" | "note_array" | "panel"nodefault "flagship"
display_typestring | nullno—
rowsarray of RowConfig | nullno—
templatearray of string | nullno—
line_metadataarray of LineMetadata | nullno—
duration_secondsintegerno10–3600; default 300
transition_strategystring | nullno—
transition_interval_msinteger | nullno0–5000
transition_step_sizeinteger | nullnomin 1
demo_plugin_idstring | nullno—
notes_wideintegerno1–8; default 1
notes_tallintegerno1–8; default 1
grid_rowsinteger | nullno3–96
grid_colsinteger | nullno15–128
created_atdate-timeno—
updated_atdate-time | nullno—

PageCreate​

Request model for creating a new page.

FieldTypeRequiredDescription
namestringyesmin length 1; max length 100
type"single" | "composite" | "template"yes—
device_type"flagship" | "note" | "note_array" | "panel"nodefault "flagship"
display_typestring | nullno—
rowsarray of RowConfig | nullno—
templatearray of string | nullno—
line_metadataarray of LineMetadata | nullno—
duration_secondsintegerno10–3600; default 300
transition_strategystring | nullno—
transition_interval_msinteger | nullno0–5000
transition_step_sizeinteger | nullnomin 1
demo_plugin_idstring | nullno—
notes_wideinteger | nullno1–8
notes_tallinteger | nullno1–8
grid_rowsinteger | nullno3–96
grid_colsinteger | nullno15–128

PageDeleteResponse​

DELETE /pages/{page_id} — the deleted id plus what else moved.

Deleting the last page auto-creates a default welcome page, and deleting the active page re-points the active reference; both are reported here so the client can follow without re-fetching everything.

FieldTypeRequiredDescription
idstringyes—
messagestringyes—
default_page_createdbooleannodefault false
new_page_idstring | nullno—
active_page_updatedbooleannodefault false
new_active_page_idstring | nullno—

PageListResponse​

GET /pages — every saved page, plus the count.

FieldTypeRequiredDescription
pagesarray of Pageyes—
totalintegeryes—

PageUpdate​

Request model for updating an existing page.

FieldTypeRequiredDescription
namestring | nullnomin length 1; max length 100
device_type"flagship" | "note" | "note_array" | "panel" | nullno—
display_typestring | nullno—
rowsarray of RowConfig | nullno—
templatearray of string | nullno—
line_metadataarray of LineMetadata | nullno—
duration_secondsinteger | nullno10–3600
transition_strategystring | nullno—
transition_interval_msinteger | nullno0–5000
transition_step_sizeinteger | nullnomin 1
notes_wideinteger | nullno1–8
notes_tallinteger | nullno1–8
grid_rowsinteger | nullno3–96
grid_colsinteger | nullno15–128

PageUpdateResponse​

PUT /pages/{page_id} — the updated page and its stale references.

Not a bare Page because the retarget warning is genuine payload, not an envelope: the editor shows it to the user after a size change. incompatible_references is always present and empty when the update did not change the page's size.

FieldTypeRequiredDescription
pagePageyes—
incompatible_referencesarray of IncompatibleReferenceno—

PluginData​

GET /v1/plugins/{plugin}/data — the merge of the raw and formatted reads.

FieldTypeRequiredDescription
plugin_idstringyes—
availablebooleanyesFalse when the plugin is disabled, unconfigured, or its fetch failed.
dataobject | nullnoThe plugin's own variable payload.
linesarray of stringnoThe plugin's board-ready lines, as GET /displays/{type} served them.
textstringnolines joined with newlines. (default "")
errorstring | nullnoWhy the data is unavailable, when it is.

PluginDetail​

GET /plugins/{plugin_id} — derived, masked, and its own model.

config is the stored configuration with sensitive values masked, deliberately without the env-var overlay: this response feeds the settings form, and any value baked in here comes straight back in the next save, so serving the overlay would freeze env values into config.json (#1864 review). Which keys the environment currently controls is reported separately in env_overridden_keys, values excluded.

FieldTypeRequiredDescription
idstringyes—
namestringyes—
versionstringyes—
descriptionstringnodefault ""
authorstringnodefault ""
iconstring | nullno—
categorystring | nullno—
plugin_typestringnodefault "data"
enabledbooleanyes—
configobjectyes—
env_overridden_keysarray of stringyes—
settings_schemaobjectyes—
variablesobjectyes—
max_lengthsobjectyes—
env_varsarray of anyyes—
documentationstring | nullno—
has_demobooleanyes—
demo_page_idstring | nullno—
instance_labelstring | nullno—
base_plugin_idstring | nullno—
instancesarray of PluginInstanceInfoyes—

PluginEntry​

One plugin in the merged catalogue.

GET /plugins and GET /displays are the same registry listing seen twice — /displays is a four-field projection of it. v1 publishes one catalogue, with the display projection's available folded in.

FieldTypeRequiredDescription
idstringyesPlugin id. Usable anywhere {plugin} appears in a v1 path.
namestringyes—
versionstringyes—
descriptionstringnodefault ""
authorstringnodefault ""
categorystring | nullno—
plugin_typestringno"data" for a content source, "transition" for an animation. (default "data")
iconstring | nullno—
enabledbooleanyesWhether this plugin runs and contributes template variables.
configuredbooleanyesWhether its required settings have been filled in.

PluginInstanceInfo​

One named instance of a plugin.

FieldTypeRequiredDescription
labelstringyes—
keystring | nullno—
enabledbooleannodefault false
has_configbooleannodefault false

PluginListResponse​

GET /v1/plugins.

FieldTypeRequiredDescription
pluginsarray of PluginEntryyes—
totalintegeryes—
enabled_countintegeryes—

PluginReceiveResponse​

POST /plugins/{plugin_id}/receive.

The status key survives the conventions pass on purpose: this endpoint is a webhook target for third-party systems (CI pipelines, home automations) that this repo does not control and cannot update in lockstep. "Deprecation, never deletion" applies to it more literally than to any browser-facing route. plugin_id is added so the ack names what it acked.

FieldTypeRequiredDescription
statusstringnodefault "ok"
plugin_idstringyes—

PluginUpdate​

PATCH /v1/plugins/{plugin}.

Collapses POST /plugins/{plugin_id}/enable, POST /plugins/{plugin_id}/disable and PUT /plugins/{plugin_id}/config into one call. config stays a free-form map on purpose: a typed body would reject the "***" sentinel the API serves in place of a stored secret, which is what a client round-trips when it edits one field of a form.

FieldTypeRequiredDescription
enabledboolean | nullnoEnable or disable the plugin.
configobject | nullnoReplace the plugin's settings. Send "***" for a secret you do not want to change.

RandomModeConfig​

Settings for random page selection.

interval_seconds is the page duration — how long each randomly chosen page is shown before a new one is selected.

FieldTypeRequiredDescription
interval_secondsintegerno5–86400; default 30

RefreshRequest​

Optional body of POST /refresh.

board_id may also arrive as a query parameter; the query wins when both are present, which is what the pre-conversion handler did.

FieldTypeRequiredDescription
board_idstring | nullno—

RefreshResponse​

POST /refresh — what the refresh pass did.

FieldTypeRequiredDescription
messagestringyes—
board_idstring | nullno—
sentbooleanyes—

RenderRequest​

POST /v1/render — render a template without saving or sending it.

FieldTypeRequiredDescription
templatestring | array of stringyesA template string, or one string per board row. A list is padded to the board's height.
line_metadataarray of object | nullnoPer-line alignment and wrap options, one entry per line.

RowConfig​

Configuration for a single row in a composite page.

Specifies which row from which source should be placed at which position. Row limits depend on the device type of the parent page.

FieldTypeRequiredDescription
sourcestringyes—
row_indexintegeryesmin 0
target_rowintegeryesmin 0

ScheduleCreate​

Request model for creating a new schedule entry.

FieldTypeRequiredDescription
board_idstringnomin length 0; default ""
page_idstringyesmin length 1
start_timestringyes—
end_timestring | nullno—
day_pattern"all" | "weekdays" | "weekends" | "custom"nodefault "all"
custom_daysarray of string | nullno—
enabledbooleannodefault true
recurrence_type"weekly" | "annual_date" | "one_off_date"nodefault "weekly"
annual_datestring | nullno—
annual_end_datestring | nullno—
one_off_datestring | nullno—
one_off_end_datestring | nullno—
start_type"fixed" | "sunrise" | "sunset"nodefault "fixed"
start_sun_offsetintegernodefault 0
end_type"fixed" | "sunrise" | "sunset"nodefault "fixed"
end_sun_offsetintegernodefault 0

ScheduleDeleteResponse​

The id of the schedule that was deleted.

FieldTypeRequiredDescription
idstringyes—

ScheduleListResponse​

What GET /schedules answers with.

default_page_id and enabled are per-board, so both are null on the cross-board listing (board_id=*) — that listing has no single board to answer for.

FieldTypeRequiredDescription
schedulesarray of ScheduleResponseyes—
totalintegeryes—
default_page_idstring | nullno—
enabledboolean | nullno—

ScheduleResponse​

A schedule as the API serves it: the stored entry plus today's times.

resolved_* equals the stored time for fixed schedules and the computed sunrise/sunset time for sun-based ones, so a client never has to know the install's location to render the window.

FieldTypeRequiredDescription
idstringno—
board_idstringnomin length 0; default ""
page_idstringyesmin length 1
start_timestringyes—
end_timestring | nullno—
day_pattern"all" | "weekdays" | "weekends" | "custom"nodefault "all"
custom_daysarray of string | nullno—
enabledbooleannodefault true
recurrence_type"weekly" | "annual_date" | "one_off_date"nodefault "weekly"
annual_datestring | nullno—
annual_end_datestring | nullno—
one_off_datestring | nullno—
one_off_end_datestring | nullno—
start_type"fixed" | "sunrise" | "sunset"nodefault "fixed"
start_sun_offsetintegernodefault 0
end_type"fixed" | "sunrise" | "sunset"nodefault "fixed"
end_sun_offsetintegernodefault 0
created_atdate-timeno—
updated_atdate-time | nullno—
resolved_start_timestringyes—
resolved_end_timestring | nullno—

ScheduleUpdate​

Request model for updating an existing schedule entry.

FieldTypeRequiredDescription
board_idstring | nullnomin length 0
page_idstring | nullnomin length 1
start_timestring | nullno—
end_timestring | nullno—
day_pattern"all" | "weekdays" | "weekends" | "custom" | nullno—
custom_daysarray of string | nullno—
enabledboolean | nullno—
recurrence_type"weekly" | "annual_date" | "one_off_date" | nullno—
annual_datestring | nullno—
annual_end_datestring | nullno—
one_off_datestring | nullno—
one_off_end_datestring | nullno—
start_type"fixed" | "sunrise" | "sunset" | nullno—
start_sun_offsetinteger | nullno—
end_type"fixed" | "sunrise" | "sunset" | nullno—
end_sun_offsetinteger | nullno—

ScheduleWriteResponse​

What create and update answer with.

warnings carries the non-fatal page<->board size mismatches of issue #1245 — a collection may mix page sizes, and the write is allowed as long as one member fits. The key was previously omitted when there was nothing to warn about, so a client could not tell "no warnings" from "this server doesn't report warnings"; it is now always present and empty when clean.

FieldTypeRequiredDescription
idstringno—
board_idstringnomin length 0; default ""
page_idstringyesmin length 1
start_timestringyes—
end_timestring | nullno—
day_pattern"all" | "weekdays" | "weekends" | "custom"nodefault "all"
custom_daysarray of string | nullno—
enabledbooleannodefault true
recurrence_type"weekly" | "annual_date" | "one_off_date"nodefault "weekly"
annual_datestring | nullno—
annual_end_datestring | nullno—
one_off_datestring | nullno—
one_off_end_datestring | nullno—
start_type"fixed" | "sunrise" | "sunset"nodefault "fixed"
start_sun_offsetintegernodefault 0
end_type"fixed" | "sunrise" | "sunset"nodefault "fixed"
end_sun_offsetintegernodefault 0
created_atdate-timeno—
updated_atdate-time | nullno—
resolved_start_timestringyes—
resolved_end_timestring | nullno—
warningsarray of stringno—

SendResponse​

The outcome of an out-of-band write.

sent is False when the content was identical to what the board already shows — the write was correctly skipped, not refused. Every other non-delivery (paused board, silence window, send floor, board failure) is a status code, not a flag.

FieldTypeRequiredDescription
messagestringyes—
sentbooleanyes—

StatusResponse​

GET /status — the display loop's state plus a per-board breakdown.

FieldTypeRequiredDescription
runningbooleanyes—
initializedbooleanyes—
config_summaryobjectyes—
boardsobject of BoardStatusnodefault {}

TemplateRenderResponse​

POST /templates/render.

FieldTypeRequiredDescription
renderedstringyes—
linesarray of stringyes—
line_countintegeryes—

TimeModeConfig​

Settings for time-based rotation (classic carousel).

FieldTypeRequiredDescription
interval_secondsintegerno5–86400; default 30

TransitionOverride​

Per-send transition animation, overriding the install's settings.

FieldTypeRequiredDescription
strategystring | nullnoTransition strategy name, e.g. "instant" or "wipe".
interval_msinteger | nullnoMilliseconds between animation steps. (0–5000)
step_sizeinteger | nullnoFlaps advanced per animation step. (min 1)

ValidationError​

FieldTypeRequiredDescription
locarray of string | integeryes—
msgstringyes—
typestringyes—
inputanyno—
ctxobjectno—

VariableCatalog​

GET /v1/variables — every name a template may use, from both sources.

Merges GET /templates/variables (the engine's vocabulary plus the static colour, symbol and filter tables) with GET /plugins/variables/all (what installed plugins contribute). They were two endpoints answering one question.

FieldTypeRequiredDescription
variablesobject of array of stringyesVariable names grouped by their source namespace.
max_lengthsobject of integeryesLongest value each variable can render to, for layout.
variable_metadataobjectnoPer-variable descriptions and types.
variable_groupsobjectnoDisplay grouping for editors.
colorsobject of integeryesColour names to flap codes, e.g. {"red": 63}.
symbolsarray of stringyesSymbol names usable as {sun}, {star} and so on.
filtersarray of stringyesFilters usable after a variable, e.g. {{x|pad:5}}.
formattingobjectyesLayout helpers such as fill_space.
syntax_examplesobject of stringyesWorked examples of the template syntax.
plugin_system_enabledbooleanyesFalse when the plugin subsystem is unavailable on this install.

VariableModeConfig​

Settings for variable-driven page selection.

rules are evaluated in order; the first one whose expression returns a truthy non-error value wins. If no rule matches (or all error), the collection falls back to default_page_id.

poll_seconds controls how often the active-page loop re-evaluates the rules.

FieldTypeRequiredDescription
rulesarray of VariableRuleno—
default_page_idstringyesmin length 1
poll_secondsintegerno2–600; default 10

VariableRule​

A single (expression, page_id) selection rule.

The expression is evaluated by the template expression engine. A truthy non-error result selects page_id as the active page.

FieldTypeRequiredDescription
expressionstringyesmin length 1
page_idstringyesmin length 1

Where the rest of the API went​

This page documents the 33 operations the app publishes. The other ~200 paths the app serves are the web UI's private RPC channel: they still answer exactly as they always have, but they are undocumented on purpose, carry no compatibility promise, and may change in any release. They are published separately at /api/internal/openapi.json so the UI keeps a contract to check itself against.

Next steps​

  • Docker Setup — how the API is served and proxied
  • Plugin Configuration — configuring plugins over the API
  • /api/docs — the same operations as a Swagger explorer you can send requests from