Glowbox API

The same account-scoped API the web app itself is built on — frames, framesets, alerts, ambient effects, data sources, and media. Anything you can do in the web app, you can do here: build your own integration, script a custom trigger, or pull your device list into another tool.

(Looking for the simpler device-key API a device's own script can push directly to, no account needed? See /docs/device-content instead.)

Auth

Every endpoint below takes Authorization: Bearer <token>. The token can be either your logged-in session token, or a personal API key — create one from the API Keys page in the web app. Both work identically everywhere; an API key just doesn't expire when you log out, which is what you want for a script or webhook.

curl https://api.glowboxlabs.com/v1/devices \
  -H "Authorization: Bearer <your api key>"

Devices

EndpointNotes
GET /v1/devicesList devices claimed by your account. Each has device_id, name, device_type, claimed_at.
POST /v1/devices/{deviceID}/claimClaim a device you've connected to over its own WiFi setup portal.
PUT /v1/devices/{deviceID}/nameBody: {"name": "Living Room"}.
POST /v1/devices/{deviceID}/resetClears any assigned frameset, active alert, and ambient override in one call — back to the built-in clock/announcement/score-reel rotation.

Frames & framesets

A frame is one screen — a set of positioned boxes (text, image, data, or solid color). A frameset is an ordered list of frames a device rotates through. Assigning a frameset to a device is what actually puts it on the panel.

EndpointNotes
POST /v1/framesCreate a frame. See body shape below.
GET /v1/frames / /v1/frames/{id}List / fetch.
PUT /v1/frames/{id} / DELETE /v1/frames/{id}Update / delete.
POST /v1/frames/previewSame body as create, returns the server-compiled bitmap preview without saving — what the web app's live editor uses.
POST /v1/framesetsBody: {"name": "...", "items": [{"frame_id": 1, "duration_ms": 4000}]}.
GET /v1/framesets / /v1/framesets/{id}List / fetch.
PUT /v1/framesets/{id} / DELETE /v1/framesets/{id}Update (same body as create) / delete.
PUT /v1/devices/{deviceID}/framesetBody: {"frameset_id": 1}. Assigns a frameset as the device's normal rotation.

Frame body

{
  "name": "Game night",
  "kind": "custom",
  "duration_ms": 4000,
  "boxes": [
    {
      "x": 0, "y": 0, "width": 64, "height": 16,
      "box_type": "text",
      "background_color": "000000",
      "effect": "static",
      "text": { "content": "GAME NIGHT <color=FF0000>7PM</color>", "align": "center" }
    },
    {
      "x": 20, "y": 16, "width": 24, "height": 16,
      "box_type": "image",
      "media_id": 12
    }
  ]
}
FieldNotes
box_typetext | image | color_fill (data also still accepted, deprecated -- see below)
text.contentMay contain <color=RRGGBB>...</color> markup to color part of the string. Omit entirely (or leave empty) when data_source_id is set -- see below.
media_idAn uploaded image/GIF's ID (see Media below) — only valid on an image box with no data_source_id, and must be media you own.
data_source_idA data source's ID (see Data sources below), valid on a text or image box: on text, its field value becomes the box's content instead of authored spans; on image, the field's value is fetched as an image URL instead of using media_id. Must be a source you own.
data_field_pathWhich field of data_source_id's payload to use (e.g. artist vs track off the same Spotify source) — omit to use that source's own default field.
text_colorHex, no # — a data-source-driven text box's color (an authored box colors itself via <color=...> markup instead).
effectstatic | scroll — per-box marquee scroll instead of a held card.

The standalone data box_type is deprecated but still accepted for existing integrations: it behaves exactly like a text box with data_source_id set. New code should just use text (or image) with data_source_id directly -- a data source is a content source for text/image, not its own kind of box.

Alerts

An alert temporarily interrupts a device's normal rotation — either an existing frameset, or a quick one-off message synthesized on the spot. This is the endpoint a webhook (Zapier, IFTTT, a game-day script) typically fires.

EndpointNotes
POST /v1/alertsSee body shape below.
DELETE /v1/alerts/{deviceID}Clears an active alert early, resuming normal rotation.
curl -X POST https://api.glowboxlabs.com/v1/alerts \
  -H "Authorization: Bearer <your api key>" \
  -H "Content-Type: application/json" \
  -d '{
    "device_id": "AA:BB:CC:DD:EE:FF",
    "text": "GOAL!",
    "color": "FF0000",
    "background_color": "000000",
    "duration_ms": 10000
  }'
FieldNotes
text / color / background_colorThe quick-message path — mutually exclusive with frameset_id.
frameset_idShow an existing frameset as the alert instead of a synthesized message.
duration_msOmit for a 10s default. 0 means indefinite — stays up until cleared via the DELETE above.

Ambient effects

A remote override of a device's ambient lighting effect (plasma/rainbow/sparkle/fire), independent of frame/frameset content.

EndpointNotes
PUT /v1/devices/{deviceID}/ambientBody: {"effect": "fire", "palette": "heat"}. palette is optional.
GET /v1/devices/{deviceID}/ambientCurrent override, if any.
DELETE /v1/devices/{deviceID}/ambientClears the override — falls back to the device's own local setting.

Data sources

Map a JSON API, RSS feed, or your connected Spotify now-playing status to one value you can drop into a data box on any frame.

EndpointNotes
POST /v1/data-sourcesBody: {"name": "...", "kind": "json", "url": "https://...", "field_path": "data.items.0.title"}. For kind: "spotify", omit url.
GET /v1/data-sources / /v1/data-sources/{id}List / fetch.
PUT /v1/data-sources/{id} / DELETE /v1/data-sources/{id}Update / delete.
POST /v1/data-sources/inspectBody: {"kind": "json", "url": "..."} (or just {"kind": "spotify"}). Returns discoverable {path, sample_value} pairs — what the web app's field picker uses instead of hand-typing a path.
POST /v1/spotify/authorizeReturns {"url": "..."} — send the browser there to connect a Spotify account.
GET /v1/spotify/status{"connected": bool, "spotify_user_id": "..."}.
DELETE /v1/spotify/connectionDisconnects. Doesn't delete any data source built on top of it.

Media

EndpointNotes
POST /v1/mediamultipart/form-data: file (PNG/JPEG/GIF), width, height. An animated GIF is stored frame-by-frame automatically.
GET /v1/media / /v1/media/{id}List / fetch metadata.
DELETE /v1/media/{id}Refuses if a frame still references it.

Rate limits & errors

No hard rate limit today beyond generous request-size caps per endpoint. Every error is a plain 4xx/5xx with a short plain-text body — no envelope to parse.