API Reference

Control Sapphire from Stream Decks, scripts, and other automation over its local REST and WebSocket API.

Base URL

The API is served by the Sapphire backend running on the same machine as the app. In development it listens on http://localhost:8000; the packaged desktop app assigns a dynamic port on launch — read it from Settings → Integrations. The examples below use the dev port.

Authentication

Every /api/control/* endpoint requires an X-API-Key header. Each session has its own key — copy or rotate it in Settings → Integrations, or fetch it programmatically. Rotate the key if it ever leaks.

# Fetch the session key (unauthenticated bootstrap)
curl http://localhost:8000/api/control/$SID/key

# Push a verse live to every display
curl -X POST http://localhost:8000/api/control/$SID/push \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"ref":"John 3:16"}'

Authentication & keys

Every /api/control/* endpoint requires an X-API-Key header matching the session's key. Fetch it once, then reuse it. Non-control endpoints (/api/scripture/*) are unauthenticated on the local-trusted deployment.

GET/api/control/{session_id}/key

Fetch (or lazily create) the API key for a session. Unauthenticated bootstrap — anyone who knows the session ID can read it.

POST/api/control/{session_id}/key/rotateAPI key

Rotate the key. The previous key is invalidated immediately.

Verse control

Put a verse on the audience display, or read back what is currently live.

POST/api/control/{session_id}/pushAPI key

Resolve a reference and broadcast it live to every display client. Accepts a canonical code (JHN:3:16) or free text (John 3:16).

{ "ref": "John 3:16" }
POST/api/scripture/override/{session_id}

Broadcast a manually chosen verse, honouring the session's active translation. Unauthenticated (local-trusted).

{ "reference": "John 3:16" }
POST/api/scripture/clear/{session_id}

Clear the current verse from all screens without ending the session.

GET/api/control/{session_id}/stateAPI key

Snapshot of the session: current verse, staging queue, NDI / OSC status, and the LAN host + port for OSC controllers.

Staging queue (On Deck)

Stage verses for the operator to push, rather than going live immediately.

POST/api/control/{session_id}/queueAPI key

Resolve a reference and add it to the On Deck queue.

{ "ref": "Romans 8:28" }
POST/api/control/{session_id}/dismissAPI key

Remove a queued item by its ID.

{ "item_id": "..." }
POST/api/control/{session_id}/reorderAPI key

Reorder the queue. ordered_ids must list every current item ID.

{ "ordered_ids": ["...", "..."] }

Banners & alerts

Congregation-facing scrolling overlays, independent of scripture (e.g. nursery calls).

POST/api/scripture/banner/{session_id}

Show a scrolling banner on all displays.

{ "text": "Parents, please collect children from the nursery." }
POST/api/scripture/banner/{session_id}/clear

Hide the operator banner.

NDI video output

Drive the per-session NDI sender — the reserved default output OBS / vMix discovers as “Sapphire”.

POST/api/control/{session_id}/ndi/enableAPI key

Start the default NDI sender. Idempotent. Returns available + reason so a missing NDI runtime is explained, not silent.

POST/api/control/{session_id}/ndi/disableAPI key

Stop the default NDI sender. Idempotent.

POST/api/control/{session_id}/ndi/themeAPI key

Apply a theme to the NDI renderer and re-render the current verse. Pass null to clear.

Multi-output channels

Run several independent outputs per session, each with its own name, type (external browser window or ndi source), and theme.

GET/api/control/{session_id}/channelsAPI key

List output channels and the max-channel cap.

POST/api/control/{session_id}/channelsAPI key

Create a channel. Enforces the channel cap and rejects NDI source-name collisions.

{ "name": "Foyer", "type": "ndi", "enabled": true }
PATCH/api/control/{session_id}/channels/{channel_id}API key

Update a channel. Partial — only provided fields change.

DELETE/api/control/{session_id}/channels/{channel_id}API key

Delete a channel (the reserved default channel cannot be deleted).

OSC control surface

Toggle the global OSC server so TouchOSC / Stream Deck (via an OSC bridge) can drive Sapphire. Session ID is passed as a query parameter, not in the path.

POST/api/control/osc/enable?session_id={session_id}API key

Start the global OSC server (UDP, default port 8010). Idempotent.

POST/api/control/osc/disable?session_id={session_id}API key

Stop the global OSC server. Idempotent.

Network sharing

Share the audience display to other devices on the local network.

GET/api/control/{session_id}/shareAPI key

LAN sharing state plus the base URL for building display links.

POST/api/control/{session_id}/share/{action}API key

Enable or disable LAN display sharing (action is enable or disable).

WebSocket protocol

One session ID ties a host to its display clients. Message shapes are JSON; audio frames are raw binary.

WS/ws/stream/{session_id}Host

Send raw 16 kHz PCM audio; receive transcript, scripture, suggestions, status, and tts_audio messages.

WS/ws/display/{session_id}Display

Read-only: receive scripture, status, and display_clear messages.

WS/ws/textText

Stateless — send { "text": "Turn to John 3:16" }, receive a scripture message. Useful for testing extraction without audio.

Full schema

The running backend also serves the complete machine-readable OpenAPI schema at http://localhost:8000/openapi.json — point your own tooling or a client generator at it for the exhaustive surface.