Skip to content

Hook System

The hook system enables agents to report lifecycle events back to the command center. Claude Code hooks, OpenClaw callbacks, and custom scripts POST structured events that update the session state machine and broadcast changes over WebSocket in real time.

Endpoint

POST /api/hooks/ingest
Content-Type: application/json

This endpoint is unauthenticated but restricted to localhost. Hook events should only come from processes running on the same host as the OptimalOS server (e.g., Claude Code hook scripts curling localhost:3000). External requests through the Cloudflare Tunnel do not reach this endpoint.

In fabric mode, the cockpit on Hetzner mounts the same /api/hooks/ingest (route registered at src/server.ts:115, handler at src/orchestration/hooks.ts) — the endpoint, payload contract, and event map are unchanged. Sessions running on paired devices report status to the cockpit through the /ws/device multiplex and device-router.ts, not via this hook endpoint; /api/hooks/ingest remains the ingest path for locally-launched Claude Code, OpenClaw, and shell agents on whichever host runs the OptimalOS server.

Payload Format

json
{
  "tmux_session": "agent-claude-code-abc123",
  "event": "tool_use",
  "metadata": "tool_use",
  "agent_type": "claude-code"
}
FieldTypeRequiredDescription
tmux_sessionstringYes*The tmux session this event belongs to
session_idstringYes*Direct session ID (alternative to tmux_session)
eventstringYesEvent type (see table below)
metadatastringNoFree-form description of the event
agent_typestringNoclaude-code, openclaw, or shell (defaults to claude-code)

*At least one of tmux_session or session_id must be provided. The endpoint looks up sessions by session_id first, then falls back to tmux_session.

Event Types

Events are mapped to state machine transitions via HOOK_EVENT_MAP:

EventMaps toEffect
startstartidle -> running
runninghook.to_runningawaiting_review -> running
to_in_progresshook.to_runningawaiting_review -> running
tool_useagent.tool_useawaiting_review -> running
prompt_readyagent.prompt_readyrunning -> awaiting_review
to_reviewhook.to_reviewrunning -> awaiting_review
awaiting_reviewhook.to_reviewrunning -> awaiting_review
exitprocess.exitrunning/awaiting_review -> completed
completedprocess.exitrunning/awaiting_review -> completed
exit_errorprocess.exit_errorrunning/awaiting_review -> failed
failedprocess.exit_errorrunning/awaiting_review -> failed
requeueuser.requeuecompleted/failed -> idle

Auto-Registration

If a start event arrives for an unknown tmux_session, the system automatically creates a new session entry in the StateHub. Similarly, any event from a tmux session with an agent- prefix auto-registers the session and transitions it to running.

Additional Hook Endpoints

List Sessions

GET /api/hooks/sessions

Returns all tracked sessions. Unauthenticated (localhost only).

Register Session

POST /api/hooks/sessions
Content-Type: application/json
json
{
  "agent_type": "claude-code",
  "tmux_session": "agent-my-task",
  "label": "My Task",
  "prompt": "Fix the bug",
  "task_id": "task-uuid",
  "depends_on": ["other-task-uuid"]
}

Manually registers a session. Returns the existing session if one already exists for that tmux name.

Update Session State

PATCH /api/hooks/sessions/:id
Content-Type: application/json
json
{
  "state": "awaiting_review"
}

Used by the board's drag-and-drop to change session state. Accepts either a state (mapped to the corresponding event) or a direct event name.

Delete Session

DELETE /api/hooks/sessions/:id

Removes a session and cleans up any session-specific hook artifacts.

Claude Code Hook Installation

When the agent launcher starts a Claude Code session, it installs hook scripts in ~/.claude/settings.json. These hooks fire curl commands to /api/hooks/ingest on the following lifecycle events:

Claude Code HookEvent sent
PostToolUsetool_use
Notificationto_review
UserPromptSubmitprompt_ready
Stopexit

The hooks read $OPTIMALOS_SESSION_TMUX from the tmux session environment to identify which session the event belongs to. Sessions without this variable are silently skipped (exit 0).

OpenClaw Integration

The OptimalOS server maintains a WebSocket client connection to the OpenClaw gateway on port 18789 (configurable via OPENCLAW_GATEWAY_PORT). OpenClaw agent events are streamed over this connection and routed to the correct session in the StateHub.

Hook Queue

Failed hook events are written to a file-backed retry queue at ~/.optimalos/hook-queue.jsonl. The queue drains every 5 seconds. Events that fail 5 times are dead-lettered.

Error Handling

The endpoint returns standard HTTP status codes:

CodeMeaning
200Event accepted and processed
400Invalid JSON, missing event field, or unknown event type
404Session not found (and not auto-registerable)

Built by Carlos Lenis in Miami