Canonical ingest contract for POST /api/events and POST /api/events/batch.
session_id(string, non-empty)agent_type(string, non-empty)event_type(enum):tool_usesession_startsession_enderrorllm_requestllm_responseresponsefile_changegit_commitplan_stepuser_promptinstruction_load
event_id(string)tool_name(string)status(enum):success,error,timeout- default:
success - default for
event_type=error:error
- default:
tokens_in(non-negative integer, default0)tokens_out(non-negative integer, default0)branch(string)project(string)duration_ms(non-negative integer)metadata(any JSON value or string)client_timestamp(ISO timestamp string)
created_at: server receive timestamp (set by API at ingest time)client_timestamp: optional client-provided timestamp (normalized to ISO-8601 UTC)
Both are persisted on events so ingestion latency and client-vs-server ordering can be analyzed later.
POST /api/events/batch returns:
received: number of inserted eventsids: inserted DB idsduplicates: count of dropped duplicateevent_iditemsrejected: validation failures with source index and error list
event_idis optional.- If provided, it is unique.
- Duplicate
event_idrecords are acknowledged and skipped (idempotent ingest). - Claude
InstructionsLoadedhooks intentionally omitevent_id, so repeated loads of the same file remain separate observations.
- Metadata is capped by
AGENTMONITOR_MAX_PAYLOAD_KB(default 10KB). - Truncation is UTF-8 byte-safe.
payload_truncatedis stored on events (0or1).- For large object metadata, key fields (for example
command,file_path) are preserved in a compact summary.
{
"event_id": "e0d43a5f-2c9a-4e2a-b145-334fa6f0b51f",
"session_id": "claude-session-001",
"agent_type": "claude_code",
"event_type": "tool_use",
"tool_name": "Bash",
"status": "success",
"tokens_in": 118,
"tokens_out": 460,
"project": "myapp",
"branch": "feature/auth",
"duration_ms": 840,
"client_timestamp": "2026-02-18T18:06:41.231Z",
"metadata": {
"command": "pnpm test"
}
}{
"event_id": "c0618b2c-6a5d-4de5-a69a-98f90f1b1550",
"session_id": "codex-session-008",
"agent_type": "codex",
"event_type": "response",
"status": "success",
"tokens_in": 640,
"tokens_out": 2104,
"project": "frontend",
"branch": "redesign-nav",
"client_timestamp": "2026-02-18T18:06:45.019Z",
"metadata": {
"type": "turn_complete"
}
}{
"session_id": "claude-session-001",
"agent_type": "claude_code",
"event_type": "instruction_load",
"status": "success",
"project": "myapp",
"source": "hook",
"metadata": {
"file_path": "/Users/me/Dev/myapp/CLAUDE.md",
"memory_type": "Project",
"load_reason": "session_start"
}
}Instruction-load metadata may also contain globs, trigger_file_path, and
parent_file_path when Claude supplies them. The hook never reads or emits the
instruction file's contents. Delivery is asynchronous and best-effort; a
configured SessionStart marker without any received load event does not prove
an observed-empty instruction set.