HTTP API

Local apps/clankie service on http://127.0.0.1:4310. There is no HTML index: / is 404 and GET /health is the public probe.

Most other routes take an Authorization: Bearer <token> header. Pairing redeem/complete use their one-time offer secrets instead. Persistent tokens live in the credential broker (Keychain service bot.clankie.credentials):

Principal Keychain account Typical use
Operator clankie_operator TUI operator reads/writes, pairing, devices, play stop
Captain clankie_captain (also CLANKIE_CAPTAIN_TOKEN) /operator/v1/dispatch, /captain/v1/lanes
Discord text clankie_discord_bridge Discord channel turns, presence, person memory
Discord voice clankie_discord_voice_bridge Voice briefing; voice-lane channel turns
Discord user text clankie_discord_user_bridge Lab-user channel turns and presence
Discord user voice clankie_discord_user_voice_bridge Lab-user voice-lane turns
Device minted at pairing complete /v1/devices/self*

Discord-lane routes 403 if the bearer is the console captain token (steer source api). Import this file in Yaak (Settings → Import/Export) or run apps/clankie/scripts/setup-yaak.py.

Base URL http://127.0.0.1:4310 · version 0.2.0 · the raw document is openapi.yaml.

This is the local contract on your machine. From the internet only the public network surface is reachable, through api.clankie.bot, and your machine still decides every grant.

Bearers

Bearer Where it lives
operator Keychain clankie_operator
captain Keychain clankie_captain / CLANKIE_CAPTAIN_TOKEN
discord text Keychain clankie_discord_bridge or clankie_discord_user_bridge
discord voice Keychain clankie_discord_voice_bridge or clankie_discord_user_voice_bridge
device Device session token from pairing complete

Routes

Method Route Summary Bearer
GET /health Public liveness none
GET /v1/herdr Running service's worker-runtime binding operator
GET /v1/discord/presence-status Operator presence snapshot operator
GET /v1/discord/user-session/opt-in Current user-session opt-in captain or operator
POST /v1/discord/user-session/opt-in Record user-session ToS acceptance operator
DELETE /v1/discord/user-session/opt-in Revoke the active user-session opt-in operator
GET /v1/discord/stream-watch Latest observed Discord screen shares captain or operator
POST /v1/discord/stream-watch Bridge report of shares and optional stills captain
GET /v1/discord/readiness Discord captain readiness discord text or discord voice
POST /v1/discord/voice-briefing Compose the realtime voice briefing discord voice
POST /v1/discord/presence-session-events Apply a Discord presence phase event discord text or discord voice
GET /v1/discord/presence-sessions Full presence session records captain
GET /v1/discord/voice-history Completed voice stays captain
GET /v1/discord/voice-transcripts Page through retained Discord voice transcripts captain
POST /v1/discord/presence-actions Execute a policy-gated Discord action discord text or discord voice
POST /v1/captain/channel-turns One Discord message becomes one captain turn discord text or discord voice
POST /v1/captain/presence Report captain lease heartbeat captain
POST /operator/v1/dispatch Operator conversation contract captain
GET /operator/v1/voice-chat Open a private local voice chat captain
GET /captain/v1/lanes Observable captain lanes captain
GET /v1/captain/turn-metrics Recent settled captain turns operator
GET /v1/captain/prompt The system prompt a lane's session starts from operator or captain
GET /v1/captain/memory-card The memory card that lane's next run injects operator or captain
GET /v1/mcp Server-to-client notification stream for an open session operator or captain or discord text or discord voice
POST /v1/mcp A lane's tool bank over streamable-HTTP MCP operator or captain or discord text or discord voice
DELETE /v1/mcp End an MCP session operator or captain or discord text or discord voice
GET /v1/seat/events Long-poll the seat's outbox operator or captain
POST /v1/seat/events/{id}/reply Answer an escalation from the seat operator or captain
GET /v1/fleet/seats/{paneId}/events Long-poll a fleet seat's mailbox operator or captain
GET /v1/memory Browse every captain episode and Discord person fact operator
POST /v1/memory/discord-people/proposals Upsert a Discord person fact discord text or discord voice
GET /v1/memory/discord-people/{guildId}/{userId} Recall Discord person facts discord text or discord voice
DELETE /v1/memory/discord-people/{guildId}/{userId} Delete every fact for a Discord person operator
GET /v1/memory/discord-people/{guildId}/{userId}/export Operator export of a Discord person operator
PATCH /v1/memory/discord-people/{guildId}/{userId}/{factId} Edit one Discord person fact without changing its identity or provenance operator
DELETE /v1/memory/discord-people/{guildId}/{userId}/{factId} Forget one Discord person fact operator
GET /v1/memory/captain-episodes Recall episodes for a lane captain
POST /v1/memory/captain-episodes Record a self-authored episode captain
PATCH /v1/memory/captain-episodes/{lane}/{episodeId} Edit one episode's note, visibility, or retention operator
DELETE /v1/memory/captain-episodes/{lane}/{episodeId} Forget one captain episode operator
POST /v1/embodiment/intents Ask to start or stop play captain
GET /v1/embodiment/sessions/live Live play session operator or captain
POST /v1/embodiment/sessions/live/stop Operator kill-switch (ordinary stop intent) operator
GET /v1/embodiment/sessions/live/activity Present-tense self-observation operator or captain
GET /v1/embodiment/sessions/live/still One still of the live play screen operator or captain
GET /v1/embodiment/sessions/live/story Bounded story of this playthrough operator or captain
GET /v1/embodiment/sessions/{id} One embodiment session captain
GET /v1/browser/tools Live browser tool catalog operator or captain
POST /v1/browser/call Call a browser tool operator or captain
POST /v1/media/images Generate or edit an image operator or captain
POST /v1/media/videos Start or resume a video render operator or captain
POST /v1/pairing/offer Mint a one-time pairing offer operator
POST /v1/pairing/redeem Redeem an offer into a pending device none
POST /v1/pairing/complete Activate a pending device none
GET /v1/devices List paired devices operator
POST /v1/devices/{id}/revoke Revoke a device operator
GET /v1/devices/self Device reads its own registration device
POST /v1/devices/self/push Device records or clears its push delivery reference device
POST /v1/devices/self/session/refresh Renew a device session token device

Health

GET /health — Public liveness

Bearer: none

Status Meaning
200 Service is up (application/json)

Operator

GET /v1/herdr — Running service's worker-runtime binding

Pending settings do not change this binding until service restart.

Bearer: operator

Status Meaning
200 Selected local worker runtime (application/json)
401 Operator authentication required
503 Operator authentication or runtime binding unavailable

GET /v1/discord/presence-status — Operator presence snapshot

Phase and counts only. What clankie status reads.

Bearer: operator

Status Meaning
200 Present-tense presence (application/json)

POST /v1/discord/user-session/opt-in — Record user-session ToS acceptance

Bearer: operator

Request body (required JSON)

{
  "schemaVersion": 1,
  "characterId": "clankie",
  "acknowledgement": "I accept Discord user-session transport risk on my account.",
  "guildIds": [
    "123456789012345678"
  ],
  "channelIds": [
    "123456789012345678"
  ],
  "dmPolicy": "deny"
}
Status Meaning
201 Recorded

DELETE /v1/discord/user-session/opt-in — Revoke the active user-session opt-in

Bearer: operator

Status Meaning
200 Revoked
409 Nothing active to revoke

GET /v1/discord/stream-watch — Latest observed Discord screen shares

Metadata plus up to four chronological stills. Raw video is never stored.

Bearer: captain or operator

Status Meaning
200 Current share projection

GET /v1/discord/voice-transcripts — Page through retained Discord voice transcripts

Captain bearer only. Exact speech is returned only while discord.voiceTranscriptLoggingEnabled is enabled; otherwise the route returns an empty page with enabled: false.

Bearer: captain

In Name Required Type Description
query cursor no string, matching ^[0-9]{12}$ Exclusive cursor from a previous page.
query limit no integer, 1–200, default 100
Status Meaning
200 Bounded transcript page (application/json)

GET /operator/v1/voice-chat — Open a private local voice chat

Upgrades to a WebSocket for captain bearers whose source lane is api. Binary client frames are 24 kHz mono signed 16-bit little-endian PCM; {"schemaVersion":1,"type":"commit"} ends one utterance. Binary server frames use the same PCM format. JSON server frames report status, attributed transcript, response_done, or error events. Raw audio is not retained. The realtime room can call the captain through the shared operator-conversation service when work is required.

Bearer: captain

Status Meaning
101 WebSocket opened
403 Bearer is not an operator API captain
503 Local realtime voice is unavailable

Captain

POST /v1/captain/channel-turns — One Discord message becomes one captain turn

Bearer: discord text or discord voice

Request body (required JSON)

{
  "schemaVersion": 1,
  "deliveryId": "yaak-delivery-1",
  "identity": {
    "presenceSessionId": "replace-me",
    "correlationId": "yaak-1",
    "profileHash": "unversioned",
    "characterId": "clankie",
    "credentialRef": "discord_bot",
    "transportKind": "bot"
  },
  "trigger": {
    "kind": "message",
    "id": "1",
    "channelId": "123456789012345679",
    "actorId": "123456789012345680",
    "body": "hello from Yaak"
  }
}
Status Meaning
200 Settled turn

POST /v1/captain/presence — Report captain lease heartbeat

Bearer: captain

Request body (required JSON)

{
  "schemaVersion": 1,
  "type": "captain.heartbeat",
  "eventId": "yaak-hb-1",
  "leaseId": "replace-me",
  "generationId": "replace-me",
  "occurredAt": "2026-08-15T20:00:00.000Z"
}
Status Meaning
202 Accepted

POST /operator/v1/dispatch — Operator conversation contract

One route, discriminated op: list, roster, get, create, fork, close, send, cancel, replay, tail, autonomy, terminal_tail. The TUI and relay both speak this. Seat scopes identify Herdr direct-send/projection threads. cancel interrupts one accepted run: the captain aborts the live model turn and the durable log settles that run as cancelled. fork clones the current Pi leaf into an ephemeral child conversation. Pane-scoped send and state_stance requests qualify their pane ID with the source socket. A mismatched send loses its pane association; a mismatched stance returns 409 without changing a worker.

Bearer: captain

In Name Required Type Description
header x-clankie-herdr-socket no string Caller Herdr session's exact socket path, required for pane identity.

Request body (required JSON)

List conversations

{
  "op": "list",
  "schemaVersion": 1
}

List live Herdr seats

{
  "op": "roster",
  "schemaVersion": 1
}

Create a global conversation

{
  "op": "create",
  "schemaVersion": 1,
  "scope": {
    "kind": "global"
  },
  "title": "Yaak probe"
}

Get one conversation

{
  "op": "get",
  "schemaVersion": 1,
  "conversationId": "replace-me"
}

Fork an ephemeral side conversation

{
  "op": "fork",
  "schemaVersion": 1,
  "parentConversationId": "replace-me"
}

Close a non-default conversation or discard a side fork

{
  "op": "close",
  "schemaVersion": 1,
  "conversationId": "replace-me"
}

Send a message turn

{
  "op": "send",
  "schemaVersion": 1,
  "turn": {
    "schemaVersion": 1,
    "kind": "message",
    "conversationId": "replace-me",
    "surfaceClientId": "yaak",
    "expectedRevision": 0,
    "message": "ping from Yaak"
  }
}

Replay a page

{
  "op": "replay",
  "schemaVersion": 1,
  "replay": {
    "schemaVersion": 1,
    "conversationId": "replace-me",
    "surfaceClientId": "yaak",
    "limit": 50
  }
}

Read autonomy state for one conversation

{
  "op": "autonomy",
  "schemaVersion": 1,
  "conversationId": "replace-me",
  "command": {
    "action": "status"
  }
}
Status Meaning
200 Discriminated result

GET /captain/v1/lanes — Observable captain lanes

Bearer: captain

Status Meaning
200 Lane listing

GET /v1/captain/turn-metrics — Recent settled captain turns

Counters, execution identity and reported usage for recent settled operator and Discord turns, newest first, from the durable turn-settled.jsonl the captain appends. Never a transcript, tool argument, tool output or credential.

execution is the model, provider and effort that actually ran the turn, captured as it executed rather than read back from configuration. usage is totalTokens summed over the assistant messages the provider reported plus reports, the number that contributed. Both are null when unknown — a turn settled before the capture existed, or a provider that reported nothing. Null never means zero, and contextTokensStart / contextTokensEnd are context occupancy, never usage or a charge.

Bearer: operator

In Name Required Type Description
query limit no integer, 1–100, default 20 1–100; clamped, never refused. Defaults to 20.
query runId no string Narrow to one run.
Status Meaning
200 Bounded settled-turn metrics, newest first
401 Operator authentication required

GET /v1/captain/prompt — The system prompt a lane's session starts from

Plain text, for a seat launcher in another harness. sections is a comma-separated subset of identity, persona, reach, address, model; omitted, it is the four a pi session starts with (model is the per-run card, asked for by name). The lane must be the bearer's own — the operator credential and the console captain token are operator, a Discord bridge bearer is the lane it serves.

Bearer: operator or captain

In Name Required Type Description
query lane no string, one of operator, discord_voice, discord_presence, gameplay Defaults to the bearer's own lane.
query sections no string, e.g. persona,model
Status Meaning
200 The assembled prompt (text/plain)
400 Unknown lane or section name
403 Lane is not the bearer's own

GET /v1/captain/memory-card — The memory card that lane's next run injects

Plain text, for a per-turn hook. Filtered by lane the same way the pi session's own injection is: operator-private episodes reach only the operator lane. Empty when the lane has recalled nothing yet. The lane must be the bearer's own.

Bearer: operator or captain

In Name Required Type Description
query lane no string, one of operator, discord_voice, discord_presence, gameplay Defaults to the bearer's own lane.
Status Meaning
200 The lane's recall card (text/plain)
400 Unknown lane
403 Lane is not the bearer's own

GET /v1/mcp — Server-to-client notification stream for an open session

Requires mcp-session-id; the bearer must resolve to the session's lane.

Bearer: operator or captain or discord text or discord voice

Status Meaning
200 SSE stream
400 No session id
403 The session belongs to another lane
404 Unknown or expired session

POST /v1/mcp — A lane's tool bank over streamable-HTTP MCP

Streamable-HTTP MCP over Clankie's authored tool registry. The bearer selects the lane and the lane selects the tools: the operator credential and the console captain token are operator, a Discord bridge bearer is the lane it serves, and a connection lists exactly that lane's authority plan (authored tools, the browser catalog, and the connected-service tools that lane may reach). An initialize POST opens a session and returns its id in mcp-session-id; every later request must carry that header and resolve to the same lane. DELETE ends the session. clankie mcp is the stdio bridge to this route (ADR 0152).

Bearer: operator or captain or discord text or discord voice

Status Meaning
200 JSON-RPC response for this request
400 No session id on a non-initialize request
403 The session belongs to another lane
404 Unknown or expired session

DELETE /v1/mcp — End an MCP session

Bearer: operator or captain or discord text or discord voice

Status Meaning
200 Session closed
403 The session belongs to another lane
404 Unknown or expired session

GET /v1/seat/events — Long-poll the seat's outbox

Wakes, herdr completion watches, and room escalations queued for a bound head (ADR 0152). Polling is what binds the seat: clankie mcp asks again the moment a poll returns and pushes each event into the session as a channel notification. Operator bearer only; a Discord bridge bearer is 403. wait is capped at 30000 ms.

Bearer: operator or captain

In Name Required Type Description
query wait no integer, 0–30000, default 0
Status Meaning
200 Events taken from the outbox, possibly none (application/json)
403 Not the operator lane

POST /v1/seat/events/{id}/reply — Answer an escalation from the seat

The reply lands in the escalating conversation as his own message and settles that run.

Bearer: operator or captain

In Name Required Type Description
path id yes string

Request body (required JSON)

{
  "schemaVersion": 1,
  "text": "On it — the build is green."
}
Status Meaning
200 Replied
404 Nothing is waiting on that event

GET /v1/fleet/seats/{paneId}/events — Long-poll a fleet seat's mailbox

A DM or room turn queued for a bound fleet seat (ADR 0161). Polling is what binds the mailbox: clankie mcp --seat asks again the moment a poll returns and pushes each event into the session as a channel notification. Operator bearer only; a Discord bridge bearer is 403. wait is capped at 30000 ms. unknown_seat (404) is the pane before herdr has classified the harness; the bridge retries.

Bearer: operator or captain

In Name Required Type Description
path paneId yes string
query wait no integer, 0–30000, default 0
Status Meaning
200 Events taken from the mailbox, possibly none (application/json)
403 Not the operator lane
404 No messageable agent in that pane

Discord

GET /v1/discord/user-session/opt-in — Current user-session opt-in

Bearer: captain or operator

Status Meaning
200 Opt-in or null

POST /v1/discord/stream-watch — Bridge report of shares and optional stills

Bearer: captain

Status Meaning
204 Report accepted

GET /v1/discord/readiness — Discord captain readiness

Requires a Discord text or voice bridge bearer.

Bearer: discord text or discord voice

Status Meaning
200 Ready
503 A required check is down

POST /v1/discord/voice-briefing — Compose the realtime voice briefing

Voice-bridge bearer only. Request carries ids; persona is owner-authored.

Bearer: discord voice

Request body (required JSON)

{
  "schemaVersion": 1,
  "guildId": "123456789012345678",
  "channelId": "123456789012345679",
  "consentedUserIds": [
    "123456789012345680"
  ]
}
Status Meaning
200 Bounded instructions + briefing

POST /v1/discord/presence-session-events — Apply a Discord presence phase event

Bearer: discord text or discord voice

Request body (required JSON)

A JSON object.

Status Meaning
200 Accepted or idempotent replay

GET /v1/discord/presence-sessions — Full presence session records

Bearer: captain

Status Meaning
200 Session list

GET /v1/discord/voice-history — Completed voice stays

Bearer: captain

In Name Required Type Description
query limit no integer, 1–32, default 5
Status Meaning
200 Recent stays

POST /v1/discord/presence-actions — Execute a policy-gated Discord action

Needs a live-session claim in headers: x-clankie-discord-presence-session, x-clankie-discord-presence-phase, x-clankie-discord-presence-revision.

Bearer: discord text or discord voice

In Name Required Type Description
header x-clankie-discord-presence-session yes string
header x-clankie-discord-presence-phase yes string
header x-clankie-discord-presence-revision yes integer

Request body (required JSON)

{
  "schemaVersion": 1,
  "idempotencyKey": "yaak-typing-1",
  "action": "discord.presence.typing_start",
  "identity": {
    "presenceSessionId": "replace-me",
    "correlationId": "yaak-1",
    "profileHash": "unversioned",
    "characterId": "clankie",
    "credentialRef": "discord_bot",
    "transportKind": "bot"
  },
  "payload": {
    "kind": "typing_start",
    "channelId": "123456789012345679"
  }
}
Status Meaning
200 Action result
409 Stale live claim or action unavailable

Memory

GET /v1/memory — Browse every captain episode and Discord person fact

Bearer: operator

Status Meaning
200 Complete operator-only memory catalog

POST /v1/memory/discord-people/proposals — Upsert a Discord person fact

Bearer: discord text or discord voice

Request body (required JSON)

{
  "schemaVersion": 1,
  "proposalId": "yaak-proposal-1",
  "fact": {
    "schemaVersion": 1,
    "factId": "yaak-fact-1",
    "subject": {
      "guildId": "123456789012345678",
      "userId": "123456789012345680"
    },
    "kind": "person-fact",
    "body": "Prefers short replies.",
    "visibility": {
      "scope": "guild"
    },
    "provenance": {
      "correlationId": "yaak-1",
      "sourceEventId": "yaak-src-1",
      "sourceSurface": "discord_text",
      "rawTranscript": false
    },
    "confidence": 0.8,
    "createdAt": "2026-08-15T20:00:00.000Z",
    "updatedAt": "2026-08-15T20:00:00.000Z"
  }
}
Status Meaning
201 Stored

GET /v1/memory/discord-people/{guildId}/{userId} — Recall Discord person facts

Bearer: discord text or discord voice

In Name Required Type Description
path guildId yes string
path userId yes string
query channelId no string
query query no string
Status Meaning
200 Facts and optional recall card

DELETE /v1/memory/discord-people/{guildId}/{userId} — Delete every fact for a Discord person

Bearer: operator

In Name Required Type Description
path guildId yes string
path userId yes string
Status Meaning
200 Deleted ids

GET /v1/memory/discord-people/{guildId}/{userId}/export — Operator export of a Discord person

Bearer: operator

In Name Required Type Description
path guildId yes string
path userId yes string
Status Meaning
200 Export bundle

PATCH /v1/memory/discord-people/{guildId}/{userId}/{factId} — Edit one Discord person fact without changing its identity or provenance

Bearer: operator

In Name Required Type Description
path guildId yes string
path userId yes string
path factId yes string

Request body (required JSON)

{
  "body": "Prefers concise replies.",
  "confidence": 0.9
}
Status Meaning
200 Updated fact
404 Fact not found

DELETE /v1/memory/discord-people/{guildId}/{userId}/{factId} — Forget one Discord person fact

Bearer: operator

In Name Required Type Description
path guildId yes string
path userId yes string
path factId yes string
Status Meaning
204 Forgotten

GET /v1/memory/captain-episodes — Recall episodes for a lane

Without query, the bounded recent card. With one, on-demand search over everything retained, filtered by what that lane may see. Both branches answer with a rendered card and never with episode records, so no caller reads another memory's provenance ids.

Bearer: captain

In Name Required Type Description
query lane yes string, one of operator, discord_voice, discord_presence, gameplay
query query no string Search terms; every term must appear in the note or the room it happened in.
query limit no integer, 1–… Matches to return (default 8, capped at 32).
Status Meaning
200 Recall card — the recent one, or the search's when a query was given

POST /v1/memory/captain-episodes — Record a self-authored episode

Recording only ever adds. An id the store already holds is a conflict, not an update; a byte-identical retry returns the original. Editing an existing note goes through PATCH. A Discord bearer may only write the lane it serves (discord_text writes discord_presence, discord_voice writes its own).

Bearer: captain

Request body (required JSON)

{
  "schemaVersion": 1,
  "episodeId": "yaak-ep-1",
  "lane": "operator",
  "targetId": "self",
  "summary": "Tried the HTTP API from Yaak.",
  "visibility": "operator_private",
  "retained": false,
  "provenance": {
    "characterId": "clankie",
    "sessionId": "captain",
    "selfAuthored": true,
    "rawTranscript": false
  },
  "occurredAt": "2026-08-15T20:00:00.000Z"
}
Status Meaning
200 Recorded id
403 The bearer's lane may not author an episode in that lane
409 Retained memory is full, or the episode id already exists. Nothing was changed in either case.

PATCH /v1/memory/captain-episodes/{lane}/{episodeId} — Edit one episode's note, visibility, or retention

Bearer: operator

In Name Required Type Description
path lane yes string, one of operator, discord_voice, discord_presence, gameplay
path episodeId yes string

Request body (required JSON)

{
  "summary": "Corrected note about what happened.",
  "visibility": "operator_private",
  "retained": true
}
Status Meaning
200 Updated episode
404 Episode not found
409 Retained memory is full; nothing was changed

DELETE /v1/memory/captain-episodes/{lane}/{episodeId} — Forget one captain episode

Bearer: operator

In Name Required Type Description
path lane yes string, one of operator, discord_voice, discord_presence, gameplay
path episodeId yes string
Status Meaning
204 Forgotten

Embodiment

POST /v1/embodiment/intents — Ask to start or stop play

Bearer: captain

Request body (required JSON)

start

{
  "kind": "start",
  "schemaVersion": 1,
  "intentId": "yaak-start-1",
  "originLane": "operator",
  "requestedBy": "local-operator",
  "requestedAt": "2026-08-15T20:00:00.000Z",
  "environmentId": "pokemon-firered",
  "budget": {}
}

stop

{
  "kind": "stop",
  "schemaVersion": 1,
  "intentId": "yaak-stop-1",
  "originLane": "operator",
  "requestedBy": "local-operator",
  "requestedAt": "2026-08-15T20:00:00.000Z",
  "sessionId": "replace-me"
}
Status Meaning
200 Accepted, refused (play_session_active when another local or hosted session is live), or stop requested

GET /v1/embodiment/sessions/live — Live play session

Captain or operator.

Bearer: operator or captain

Status Meaning
200 { session } or { session: null }

POST /v1/embodiment/sessions/live/stop — Operator kill-switch (ordinary stop intent)

Bearer: operator

Status Meaning
200 Stop submitted
404 Not playing

GET /v1/embodiment/sessions/live/activity — Present-tense self-observation

Bearer: operator or captain

Status Meaning
200 not_playing, pending, or snapshot

GET /v1/embodiment/sessions/live/still — One still of the live play screen

Read-only glance. PNG as base64. No controller.

Bearer: operator or captain

Status Meaning
200 not_playing, pending, or still

GET /v1/embodiment/sessions/live/story — Bounded story of this playthrough

Journal projection — objective, maps, last notable moments. Not the raw log.

Bearer: operator or captain

Status Meaning
200 not_playing, pending, or card

GET /v1/embodiment/sessions/{id} — One embodiment session

Bearer: captain

In Name Required Type Description
path id yes string
Status Meaning
200 Session
404 Unknown

Browser

GET /v1/browser/tools — Live browser tool catalog

Bearer: operator or captain

Status Meaning
200 Catalog
503 Browser host unavailable

POST /v1/browser/call — Call a browser tool

Calls one raw agent-browser MCP tool from the live catalog.

Bearer: operator or captain

Request body (required JSON)

{
  "schemaVersion": 1,
  "tool": "agent_browser_snapshot",
  "arguments": {}
}
Status Meaning
200 Result or typed refusal

Media

POST /v1/media/images — Generate or edit an image

Bearer: operator or captain

Request body (required JSON)

{
  "schemaVersion": 1,
  "prompt": "a small robot sitting at a terminal",
  "aspectRatio": "1:1"
}
Status Meaning
200 ok or refused
503 Media plane unconfigured

POST /v1/media/videos — Start or resume a video render

Bearer: operator or captain

Request body (required JSON)

{
  "schemaVersion": 1,
  "prompt": "a small robot waving",
  "durationSeconds": 6
}
Status Meaning
200 ok, pending, or refused

Pairing

POST /v1/pairing/offer — Mint a one-time pairing offer

The offer secret appears once in the response. Do not log it.

Bearer: operator

Status Meaning
200 Offer wire (deep link + code)

POST /v1/pairing/redeem — Redeem an offer into a pending device

Unauthenticated. The offer secret or typed code is the capability.

Bearer: none

Request body (required JSON)

{
  "code": "123-456",
  "device": {
    "name": "Yaak",
    "platform": "macos"
  }
}
Status Meaning
200 Pending device + completion token; a configured gateway also returns the host-scoped base URL

POST /v1/pairing/complete — Activate a pending device

Bearer: none

Request body (required JSON)

{
  "completionToken": "replace-me",
  "acceptedGrants": {
    "chat": true,
    "steer": true,
    "terminalObserve": true,
    "terminalControl": false
  }
}
Status Meaning
200 Device token issued; relayUrl is the host-scoped gateway base when configured

Devices

GET /v1/devices — List paired devices

Secret-free rows. push carries the device's last delivery state — { registrationId, sequence, enabled } — so the listing is truthful about a device that turned notifications off or whose registration the gateway invalidated. The APNs token and delivery key are never here (ADR 0159).

Bearer: operator

Status Meaning
200 Secret-free device rows

POST /v1/devices/{id}/revoke — Revoke a device

Bearer: operator

In Name Required Type Description
path id yes string
Status Meaning
200 Revoked row
404 Unknown

GET /v1/devices/self — Device reads its own registration

Bearer: device

Status Meaning
200 Self row

POST /v1/devices/self/push — Device records or clears its push delivery reference

Body is { registrationId, sequence, enabled }. The host stores only the reference, its version and whether delivery is on — never the APNs token or the delivery key, which the app registers with the gateway directly (ADR 0159). Disabling records that state at its version rather than forgetting it, so ordering survives it.

An older sequence is 409 stale_push_registration. The same sequence is accepted only when it restates what was already recorded: a different registrationId, or the same one with enabled flipped, is 409 conflicting_push_registration. That is also what refuses a re-enable at the version a gateway invalidation was recorded against.

Bearer: device

Status Meaning
200 Accepted reference, echoed with its enabled state
400 Malformed body
401 Device session is unknown, expired, or revoked
409 Stale or conflicting registration version

POST /v1/devices/self/session/refresh — Renew a device session token

Bearer: device

Status Meaning
200 New token; relayUrl republishes the host-scoped gateway base when configured