Wire reference
The compact, payload-by-payload spec. All keys snake_case. Every timestamp is ms since epoch unless noted.
Envelope
Every JSON response uses the same envelope:
{
"ok": true,
"result": { /* per-route */ }
}On error:
{
"ok": false,
"error": {
"code": "session_not_found",
"message": "optional human copy",
"errors": [
{
"path": "attachments.0.size",
"code": "too_big",
"message": "Number must be less than or equal to 26214400"
}
],
"retry_after_ms": 1500
}
}errors[] is present on validation failures (400 invalid_request)
and absent or empty otherwise. See Errors & rate limits
for the full code table and rate-limit headers.
REST — bridge namespace
/v1/bridge/sendMessage
{
"session_id": "ses_…",
"interaction_id": "int_…?",
"text": "agent reply text",
"attachments": [{ "key": "u/…", "mime": "image/png", "size": 1024, "name": null }],
"reply_to": "msg_…?",
"idempotency_key": "uuid",
"usage": {
"input_tokens": 12,
"output_tokens": 34,
"estimated_cost_usd": 0.0012,
"model": "claude-sonnet-4-5",
"provider": "anthropic"
}
}/v1/bridge/sendMessageDelta
{ "message_id": "msg_…", "delta": "next chunk", "idempotency_key": "uuid" }/v1/bridge/sendMessageEnd
{
"message_id": "msg_…",
"text": "final canonical text (optional — server keeps the streamed text otherwise)",
"usage": { "...": "..." },
"finish_reason": "stop | length | content_filter | tool_call",
"idempotency_key": "uuid"
}/v1/bridge/createTask / updateTask / finishTask — see
Tool calls & approvals.
/v1/bridge/requestApproval — same.
REST — user namespace
POST /v1/me/sessions/:id/send
{
"text": "user message",
"attachments": [{ "key": "u/…", "mime": "image/png", "size": 1024, "name": null }],
"reply_to": "msg_…?",
"thought_level": "default | extended | max"
}POST /v1/me/sessions/:id/archive / unarchive — empty body.
PATCH /v1/me/sessions/:id — { title: string | null }.
DELETE /v1/me/sessions/:id — soft-delete with 24 h grace.
PATCH /v1/me/installations/:id
{
"custom_display_name": "Work Mac" | null,
"custom_emoji": "🦞" | null
}POST /v1/me/approvals/:id
{ "decision": "approve | deny | approve_always", "scope": "tool", "scope_value": "Bash" }SSE — /v1/me/stream
Each event line follows the standard EventSource format:
id: 42
event: message_delta
data: {"session_id":"ses_…","message_id":"msg_…","delta":"hello","interaction_id":"int_…","ts":1730000000000}
Last-Event-ID on reconnect replays the 5-minute / 256-event ring
buffer. iOS sends it automatically.
WebSocket — /v1/bridge/ws
server → bridge:
{ "type": "ready", "installation_id": "inst_…" }
{ "type": "update", "update": { "update_id": "1", "type": "session.message", "payload": {...}, "session_id": "ses_…", "interaction_id": "int_…", "installation_id": "inst_…", "created_at": "iso" } }
{ "type": "ping" }
bridge → server:
{ "type": "ack", "up_to_update_id": "12" }
{ "type": "pong" }
Authentication: Authorization: Bearer inst_…:s_… header during
upgrade.