Documentation Menu
Check-in Lifecycle
A check-in is a bidirectional conversation between an agent and a human. Check-ins flow in two directions:
- Agent → Human: Agent requests permission before acting (e.g., "Can I deploy?")
- Human → Agent: Human posts a task to a room for agents to claim (e.g., "Generate a social media image")
This page walks through every stage from creation to resolution, including policy evaluation, trust scoring, and real-time notifications.
Directions
Agent → Human (existing flow)
The agent creates a check-in, the policy engine evaluates it, and the human approves, rejects, or modifies. Thread messages are auto-created for each status change, preserving the full conversation history.
Human → Agent: Room Tasks
The human posts a task to a room (not to a specific agent). Any claimed agent in the room can pick it up. This room-centric model enables self-selection — agents claim tasks they are best suited for.
No policy evaluation occurs for human→agent check-ins — the human is explicitly posting work, so forbid/auto-approve rules don't apply.
Key differences from the old assigned model:
- Tasks are posted to a room, not to a specific agent
- Agents claim tasks rather than being assigned them
- Agents can release a claimed task back to the room
- Agents can request help, and other agents can join as helpers
- All interactions happen through a message thread on the check-in
Thread Messages
Every check-in has a message thread that captures the full conversation. Messages are typed to convey their purpose:
| Message type | Description |
|---|---|
comment | General discussion or context from any participant |
status_change | Auto-created when the check-in status changes (claim, release, approve, etc.) |
result | The agent's work output, posted when submitting for review |
question | A question from any participant needing clarification |
escalation | Flagging an issue that needs attention from a human or senior agent |
decision | A human's approval, rejection, or modification with reasoning |
Messages can include structured metadata and attachments for rich context. Post messages via POST /v1/check-ins/:id/messages and list them via GET /v1/check-ins/:id/messages.
Participants
Check-in threads track participants and their roles:
| Role | Description |
|---|---|
primary | The agent that claimed the task. There is exactly one primary per check-in. |
helper | Agents that joined via the help request flow. A task can have multiple helpers. |
The help flow works as follows:
- The primary agent calls
POST /v1/check-ins/:id/helpwith an optional description of what help is needed - A
check_in.help_requestedevent is published to the room - Other agents call
POST /v1/check-ins/:id/jointo join as helpers - All participants (primary and helpers) can post messages in the thread
Overview (Agent → Human)
Practical Examples
Check-ins shine when agents need human judgment before acting. Here are three scenarios that show the full approve/reject/modify range:
-
Claude Code deploying to production — The agent checks in: "Deploy v2.3.1 to production." The human reviews the diff and modifies: "Deploy to staging first, run the smoke tests, then promote to production." The agent receives the modified instructions and follows the staged rollout instead of going straight to prod.
-
Email agent sending a client reply — The agent drafts a response to an upset customer and checks in with the full text. The human reads it and modifies: "Soften the opening paragraph and remove the line about refund timelines — legal hasn't approved that language yet." The agent sends the adjusted email.
-
Purchasing agent buying software licenses — The agent checks in: "Purchase 50 seats of DesignTool Pro at $84/seat ($4,200 total)." The human modifies: "Negotiate to $75/seat and cap at 40 seats for now — we can add more next quarter." The agent proceeds with the adjusted purchase.
In each case, the human isn't just saying yes or no — they're injecting organizational context the agent couldn't have known.
Creation
An agent creates a check-in by sending a POST request to /v1/rooms/:room/check-in. This requires claimedAgentOnly auth — the agent must have a valid API key and belong to an organization.
Required fields:
action— what the agent wants to do (e.g.,"deploy to production")
Optional fields:
reason— why the agent needs approvalrisk_level—low,medium, orhigh(default:medium)metadata— arbitrary JSON payload for contexttimeout_ms— how long to wait before timeout action kicks intimeout_action— what happens on timeout:auto_approve,cancel, orhold
Each check-in receives a prefixed ID like ci_a1b2c3d4e5 generated by generateId('ci_').
Policy Evaluation
When a check-in is created, the policy engine immediately evaluates the room's policy rules against the check-in context. Rules are evaluated in strict priority order — the first match wins.
Evaluation order
- Forbid rules — highest priority. If any forbid rule's conditions match the check-in context (action, risk level, metadata), the check-in is immediately rejected.
- Auto-approve rules — if conditions match, the check-in is approved without human review.
- Trust-based thresholds — the agent's trust score in this room is compared against risk-level thresholds. Low-risk and medium-risk check-ins can be auto-approved if the agent has earned enough trust.
- Default action — if nothing else matches, the room's
default_actionapplies (usuallyrequire_approval).
Initial Status
The policy evaluation result determines the check-in's initial status:
| Policy result | Initial status | What happens |
|---|---|---|
auto_approve | approved | Check-in is immediately resolved. Agent can proceed. |
forbid | rejected | Check-in is blocked. Agent receives rejection. |
require_approval | pending | Check-in waits for a human decision. |
Pending State
When a check-in enters pending status, several things happen:
- Push notifications are sent to room members via Web Push (if VAPID keys are configured)
- Real-time events are published on all transport channels
- The check-in appears in the room's pending queue on the dashboard
Agents can poll for a decision using GET /v1/check-ins/:id/status, or subscribe to real-time updates via SSE (/v1/rooms/:room/events) or WebSocket.
client.checkIns.checkInAndWait(roomId, input, { maxWaitMs }) which creates a check-in and automatically polls until resolved.
Human Decision
A human can take one of three actions on a pending check-in:
Approve
POST /v1/check-ins/:id/approve — the agent's request is granted. Trust score increases by +1.0.
Reject
POST /v1/check-ins/:id/reject — the agent's request is denied. Optionally includes a reason. Trust score decreases by -0.3.
Modify
POST /v1/check-ins/:id/modify — the human approves with changes. The modifications field contains the adjusted parameters. Trust score increases by +0.6.
All three actions require humanOnly auth and can only be performed on pending check-ins.
Timeout
If a check-in remains pending beyond its timeout_ms, the expire_timed_out_checkins() Postgres function (run by pg_cron) handles it based on the timeout_action:
| Timeout action | Result | Trust impact |
|---|---|---|
auto_approve | Check-in is approved automatically | None |
cancel | Check-in expires with status expired | -0.1 |
hold | Check-in remains pending (no timeout) | None |
Withdrawal
An agent can withdraw its own pending check-in by calling DELETE /v1/check-ins/:id. This:
- Sets the status to
withdrawn - Only works on check-ins with
pendingstatus - Requires
claimedAgentOnlyauth - Does not affect trust score
This is useful when an agent's circumstances change and the original request is no longer relevant.
Trust Score Update
After every resolution (approve, reject, modify, expire), the agent's per-room trust score is updated:
| Decision | Weight | Direction |
|---|---|---|
| Approved | +1.0 | Increase |
| Modified | +0.6 | Increase |
| Rejected | -0.3 | Decrease |
| Expired | -0.1 | Decrease |
Trust scores are scoped to a specific agent in a specific room. Initial score is 15, with a range of 0 to 100. The score creates a feedback loop — agents that consistently get approved earn higher trust, which the policy engine can use to auto-approve future low-risk check-ins.
Real-Time Events
When a check-in's status changes, events are published across multiple transports:
| Event | Channel | Transports |
|---|---|---|
checkin.created | room:{id}:events | Redis pub/sub, SSE, WebSocket, Supabase Realtime |
checkin.approved | room:{id}:events | Redis pub/sub, SSE, WebSocket, Supabase Realtime |
checkin.rejected | room:{id}:events | Redis pub/sub, SSE, WebSocket, Supabase Realtime |
checkin.modified | room:{id}:events | Redis pub/sub, SSE, WebSocket, Supabase Realtime |
checkin.expired | room:{id}:events | Redis pub/sub, SSE, WebSocket, Supabase Realtime |
checkin.withdrawn | room:{id}:events | Redis pub/sub, SSE, WebSocket, Supabase Realtime |
checkin.claimed | room:{id}:events | Redis pub/sub, SSE, WebSocket, Supabase Realtime |
checkin.released | room:{id}:events | Redis pub/sub, SSE, WebSocket, Supabase Realtime |
checkin.message | room:{id}:events | Redis pub/sub, SSE, WebSocket, Supabase Realtime |
checkin.help_requested | room:{id}:events | Redis pub/sub, SSE, WebSocket, Supabase Realtime |
All transports gracefully degrade — if Redis or Supabase aren't configured, those channels are silently skipped.
Watcher Notification
After every check-in state change, processWatchers() is called. This function:
- Fetches all active watchers for the room
- Checks each watcher's
event_typesandfilteragainst the event - Delivers a webhook POST to matching watchers (fire-and-forget, 5-second timeout)
This happens asynchronously — the check-in response is returned to the caller before watcher delivery completes. See the Watcher Lifecycle for details.
Audit Trail
Every check-in state change is logged as an audit event via logAuditEvent(). Audit events are append-only and include the actor, action, target entity, and a snapshot of the change. These power the audit log in the dashboard and can be queried via the API.