Skip to main content
POST
Ingest a session event
The credential decides the org and user, so identity fields in the body are provenance only. Re-sending an event is safe: per-event deduplication reports it as deduped instead of storing it twice, which is why this route takes no Idempotency-Key. Use POST /v1/ingest/session-batch for anything larger than a live tail.

Authorization

This route requires the write scope. A missing or invalid credential returns 401 authentication_error. A valid credential without the scope returns 403 permission_denied, and the problem body names the exact scope required.

Headers

These are request conventions the contract does not declare as parameters, so they do not appear in the schema tables below.

Success responses

Errors

Every error is an RFC 9457 application/problem+json body carrying a stable code, a requestId, and a suggestedAction where Exo has one. See Errors.

Notes

  • Use the batch route for anything larger than a live tail. This one is for a daemon following an active transcript.
  • The credential decides the organization and the user. Identity fields in the body are provenance only.

Authorizations

X-Exo-API-Key
string
header
required

An Exo API key (exo_...) sent as the X-Exo-API-Key header.

Body

application/json

POST /v1/ingest/session-event: single event from Stop/SubagentStop hooks.

event
SessionEvent · object
required

A single Claude Code session-log line.

Maps to one parsed line from a .jsonl session transcript (~/.claude/projects//.jsonl) or a sub-agent transcript (...//subagents/agent-*.jsonl).

Sub-agent events have parent_session_id + agent_id populated. Dedup key on the server side: (user_id, line_uuid).

Wire contract uses clean user_id / org_id field names (camelCase aliases on the wire). DB column mapping is handled at the SQL boundary in the ingest worker, not here.

Response

Whether the event was queued or recognised as a duplicate.

ingested
boolean
required
deduped
boolean
default:false
nodeId
string | null