Skip to content

Dashboard API·

The routes behind reachy.cagatay.my, generated from dashboard/server.py (and the passkey router in dashboard/auth.py) by scripts/routedoc.py at every docs build. Body fields are the keys the handler actually reads; anonymous? is what the _gate middleware decides.

Keys·

key how who uses it
owner bearer Authorization: Bearer $REACHY_TOKEN — or ?token= for <img>/WebSocket URLs the iOS app, curl, the personas off-robot
passkey WebAuthn session cookie (12 h, REACHY_SESSION_TTL) after /api/auth/login/*; the first enrolment is TOFU, later ones need REACHY_REG_TOKEN the owner's browser
loopback no key from 127.0.0.1 with a loopback Host and no cf-connecting-ipreads everywhere (REACHY_LOOPBACK_READS=1) and writes on /api/tracking* only the personas' tools/reachy_camera.py, tools/head_tracking.py

Control routes additionally require a same-origin Origin (403 otherwise) and are rate-limited to REACHY_RATE_LIMIT (5/s) per client (429).

# health is public
curl -s https://reachy.cagatay.my/api/health | jq .ok
# everything else wants a key
curl -s -H "Authorization: Bearer $REACHY_TOKEN" https://reachy.cagatay.my/api/state | jq .head
curl -s -H "Authorization: Bearer $REACHY_TOKEN" -H 'Content-Type: application/json' \
     -d '{"name":"cheerful1"}' https://reachy.cagatay.my/api/control/express

Routes·

Health & state·

method path params anonymous? what
GET /api/ask/last 🔒 key Whether an Ask turn is running and the last answer. · src
GET /api/emotions 🔒 key The recorded-move library the daemon exposes (81 moves on 1.10). · src
GET /api/health ✅ public Liveness + daemon reachability, camera status, last error, daemon pressure (fds/limit, CLOSE-WAIT on :8000) and the state-stream status. The only public /api/* route. · src
GET /api/log n: int, after: int 🔒 key Tail of the cross-persona agent log (n ≤ 300 rows, after = row id) + recent dashboard events. · src
GET /api/state 🔒 key Full robot state: head/body/antenna pose, motor mode, daemon loop Hz, CM4 system stats, service states, tracking/doa/imu when present. · src
GET /api/telemetry 🔒 key State + camera status — the tiny.technology endpoint-device telemetry action. · src

Camera·

method path params anonymous? what
GET /api/camera/snapshot 🔒 key Alias of /api/snapshot.jpg — the endpoint-device snapshot action. · src
GET /api/snapshot.jpg 🔒 key Latest JPEG frame (503 while the camera has none). · src
GET /api/stream 🔒 key MJPEG stream from the dashboard camera (one daemon camera client shared by every viewer). · src

Control·

method path params anonymous? what
POST /api/control/antennas JSON 🔒 key Antenna angles in degrees. · src
POST /api/control/ask JSON 🔒 key Run a dashboard Ask turn (persona dashboard, full tools); streams to /ws as agent_log rows. · src
POST /api/control/demo JSON 🔒 key Demo mode: pause (on: true) / resume the tiny-thinker persona so manual moves are not overlapped. · src
POST /api/control/express JSON 🔒 key Play a recorded move by name. · src
POST /api/control/home 🔒 key Neutral pose. · src
POST /api/control/look JSON 🔒 key Head pose in degrees/mm (clamped in robot.py), optional antennas: [right, left], body_yaw, duration. · src
POST /api/control/motors JSON 🔒 key mode: enabled · disabled · gravity_compensation. · src
POST /api/control/reel JSON 🔒 key Start the showcase reel (action: start) or abort it. · src
POST /api/control/say JSON 🔒 key Piper TTS on the CM4 → daemon play_sound with head wobble; returns {engine, seconds}. · src
POST /api/control/sleep 🔒 key Sleep pose, motors relaxed. · src
POST /api/control/stop 🔒 key E-stop: cancel the running move. · src
POST /api/control/volume JSON 🔒 key Speaker volume level 0–100 (→ daemon /api/volume/set). · src
POST /api/control/wake 🔒 key Wake the daemon (motors on, wake emote). · src

Ask / chat·

method path params anonymous? what
POST /api/chat JSON 🔒 key One agent turn as a fleet turn (depth-capped, no use_device) — the endpoint-device chat action; waits up to 75 s. · src

Perception·

method path params anonymous? what
GET /api/tracking 🔒 key Face-tracking controller status: enabled, detected, x/y, holds, paused, engine daemon-yunet. · src
POST /api/tracking JSON 🔒 key Turn the daemon face tracker on/off (enabled). Also allowed from 127.0.0.1 without a key — the personas' head_tracking tool. · src
POST /api/tracking/hold JSON 🔒 key Named hold (name: speaking · emotion: · look …, on, ttl s) that pauses tracking while TINY speaks or emotes. · src

Auth·

method path params anonymous? what
GET /api/auth/credentials ✅ public List enrolled passkeys (owner only). · src
DELETE /api/auth/credentials/{cid} ✅ public Remove a passkey (owner only). · src
POST /api/auth/login/begin ✅ public WebAuthn assertion options. · src
POST /api/auth/login/complete ✅ public Verify the assertion, set the session cookie (12 h). · src
POST /api/auth/logout ✅ public Drop the passkey session cookie. · src
POST /api/auth/register/begin ✅ public WebAuthn registration options (first passkey = TOFU; afterwards needs REACHY_REG_TOKEN). · src
POST /api/auth/register/complete ✅ public Store the new passkey. · src
GET /api/auth/status ✅ public Who am I: passkey session, bearer, or anonymous; whether TOFU enrolment is open. · src

Realtime·

method path params anonymous? what
WS /ws sock: WebSocket 🔒 key State frames at REACHY_WS_HZ (15/s) + agent_log rows + events; anonymous sockets are closed with 4401. · src

Shell·

method path params anonymous? what
GET / ✅ public The SPA shell (Cache-Control no-cache — Cloudflare edge-caches hashed assets, not index.html). · src
GET /{path:path} path: str ✅ public Static assets / SPA fallback. · src

Other·

method path params anonymous? what
GET /api/doa 🔒 Turn-toward-speaker controller status: enabled, last bearing (deg), why it is not turning, turns/windups, sign cross-check counters.
POST /api/doa JSON 🔒 {enabled: bool} — turn toward speech when face tracking has no lock (default on; K in the cockpit). Personas may call it from 127.0.0.1 without a key.

39 routes; 11 answer without a key (health, the auth handshake and the SPA shell), the rest 401 anonymous callers and close a WebSocket with 4401.

The WebSocket carries three frame kinds: state (the same object as GET /api/state), agent_log (rows from the shared brain — user/assistant/tool/reasoning, tagged by persona) and event (dashboard receipts such as look ok yaw=9 by token). See Dashboard for what the cockpit does with them.