DEVELOPERS — CHECKPOINTS & THE MESH
Two contracts on the same key: checkpoints hold a running workflow until someone — or something — decides, and the mesh turns agent moments into supervision with receipts that name a real check.
CHECKPOINTS — THE RUN WAITS FOR YOU
Steering is not a dial you watch; it's a moment the run stops and waits for a call. When an agent SeaOtter manages hits a question, real uncertainty, or a change big enough to matter, it publishes a checkpoint — a typed object your code, your dashboard, and your phone-sized attention span can all act on. Nothing moves until someone (or something you wrote) answers: let it continue, send it back, or stop it.
List what's waiting on you
Any signed-in surface can read the queue. web_decide_enabled tells older clients the decision buttons are live here too.
curl -s 'https://api.seaotter.ai/api/v1/checkpoints?state=pending&limit=50' \
-H "Authorization: Bearer $OTTER_KEY"
# -> { "checkpoints": [ {
# "schema": "seaotter.checkpoint.v1",
# "checkpoint_id": "ckpt-...",
# # kinds: managed_agent_question |
# # seaotter_uncertainty | important_change
# "kind": "managed_agent_question",
# "importance": "hold", # or hard_stop
# "headline": "Send the refund email to 214 customers?",
# "agent": { "display_name": "support-drafter" },
# "forecast": [ { "label", "before", "after" } ],
# "actions": [ { "action": "continue" },
# { "action": "send_back" }, { "action": "stop" } ]
# } ],
# "count": 1, "web_decide_enabled": true }Decide — from anywhere you're signed in
One POST settles it: the run continues, goes back to fix first, or stops. Every decision stamps who decided from which surface and lands a signed audit row.
curl -s -X POST \
https://api.seaotter.ai/api/v1/checkpoints/$CKPT_ID/decide \
-H "Authorization: Bearer $OTTER_KEY" \
-H 'Content-Type: application/json' \
-d '{ "action": "send_back",
"note": "Tighten the refund wording first." }'
# -> { ..., "state": "decided",
# "decision": { "action": "send_back",
# "decided_by": { "surface": "web",
# "actor": "you@acme.com" } } }
# First terminal decision wins; a later decide
# on the same checkpoint changes nothing.pendingdecidedresolvedexpired
A checkpoint is born pending — the run is holding. A decision (from the app, the web dashboard, or your own code) moves it to decided and the run acts on your call. When the agent finishes acting on it, the Mac app marks it resolved with an outcome line. A pending checkpoint nobody answers expires — it never silently self-approves.
The Mac app is the surface that publishes checkpoints (it is the one watching the agent work) and marks them resolved afterwards. Deciding is deliberately wider: the app, the web dashboard's "Waiting on you" panel, or any code holding your key can answer — so the person with context can make the call from wherever they are.
Every decision is recorded: who, from where, what changed, in a tamper-evident audit trail — the same record the rest of the platform writes.
AGENTS CHECKING AGENTS — SUPERVISION ON HOOKS
Your agents already announce the moments that matter — a piece of work finished, a change applied, a session stuck. The mesh turns those moments into supervision: link a supervisor agent to a supervised one, tell it which moments to care about, and land its findings as receipts that reference a real check — not a vibe. This is how a fleet reviews itself while you sleep.
1 — Link supervisor to supervised
One POST declares the relationship and the moments it cares about. Closed vocabulary: work_produced · work_applied · grade_completed · session_started · session_idle · agent_blocked · agent_error · peer_note. Mesh calls ride your signed-in SeaOtter session ($SEAOTTER_SESSION below) — the Mac app wires this for embedded agents automatically; your sk-otter key is for the checking surface, not the mesh.
curl -s -X POST https://api.seaotter.ai/api/v1/mesh/links \
-H "Authorization: Bearer $SEAOTTER_SESSION" \
-H 'Content-Type: application/json' \
-d '{ "supervisor": { "agent_id": "reviewer-1",
"kind": "embedded_app", "display_name": "Reviewer",
"surface": "claude_code" },
"supervised": { "agent_id": "drafter-7",
"kind": "embedded_app", "display_name": "Drafter",
"surface": "cursor" },
"hook_events": ["work_produced", "agent_error"],
"mode": "observe", "visibility": "structural" }'
# 201 -> { "schema": "seaotter.agent_link.v1",
# "link_id": "link-...", "status": "active",
# "summary": "Reviewer checks Drafter's work when it
# finishes a piece of work or hits an error" }2 — A moment fires
The supervised side (the Mac app's embedded adapters, or your own harness) posts the moment as an event. Work-shaped moments carry a structural artifact ref — never the raw content.
curl -s -X POST https://api.seaotter.ai/api/v1/mesh/events \
-H "Authorization: Bearer $SEAOTTER_SESSION" \
-H 'Content-Type: application/json' \
-d '{ "event_id": "evt-01", "kind": "work_produced",
"ts": "2026-07-21T14:31:00Z", "seq": 1,
"source": { "agent_id": "drafter-7",
"kind": "embedded_app", "surface": "cursor" },
"artifact_ref": { "label": "src/refund_email.md",
"sha256": "..." } }'
# 202 -> { "accepted": true, "deduplicated": false, ... }
# supervisor side: poll
# GET /api/v1/mesh/events?cursor=...&kind=work_produced3 — Land the verdict receipt
The supervisor runs the artifact through a real check first (POST /api/v1/eval/feedback -> run_id), then posts the receipt naming that run. A receipt with no run behind it is refused.
curl -s -X POST https://api.seaotter.ai/api/v1/mesh/verdicts \
-H "Authorization: Bearer $SEAOTTER_SESSION" \
-H 'Content-Type: application/json' \
-d '{ "verdict_id": "v-01", "link_id": "link-...",
"event_id": "evt-01",
"supervisor": { "agent_id": "reviewer-1",
"kind": "embedded_app", "surface": "claude_code" },
"supervised": { "agent_id": "drafter-7",
"kind": "embedded_app", "surface": "cursor" },
"artifact_ref": { "label": "src/refund_email.md",
"sha256": "..." },
"eval_run_id": "$RUN_ID",
"score": 0.58, "band": "route_to_fix",
"ts": "2026-07-21T14:32:10Z" }'
# 201 -> { "schema": "seaotter.supervision_verdict.v1",
# ..., "summary": "Reviewer checked Drafter's work:
# needs a fix before it ships" }linkmoment firessupervisor checksreceipt lands
One link says who watches whom, and on which moments (in the API: hook_events — when the supervised agent finishes a piece of work, applies it, gets stuck, or errors). When one of those moments fires, an event lands on the mesh. The supervisor picks it up, runs the work through a real check, and posts a verdict receipt that names the check it came from. The /team page draws who-checks-whom from these same objects.