Skip to main content
GET /v1/events is a live view, not a log. It does not replay. A client that must not miss a transition should use webhooks, which retry. Both transports carry the same event catalogue and apply the same reach rules, so a receiver sees the same set either way. The difference is durability.

Open a stream

The stream requires the read scope. X-Exo-Subject is accepted and has no effect: the stream is organization-scoped rather than per-subject.
The browser EventSource API cannot set request headers, so it cannot carry an Exo API key. Use fetch with a stream reader, or proxy the stream from your own server.

Frame format

Each event is a named SSE frame. The data: line carries a JSON envelope with the type repeated inside it, so a consumer can dispatch on either.
Every frame ends with a blank line. The id: line is present when the event has one, and is for your own bookkeeping.

Heartbeats

After 15 seconds of idle, Exo sends a comment frame:
It exists to stop proxies closing an idle connection. A line beginning with : is a comment in the SSE format and carries no data. Skip these lines rather than trying to parse them, which is the most common integration bug on this route.

The catalogue

The same eight event types flow here as over webhooks. See the webhook catalogue for the data keys on each. graph.updated and the import.* and identity.* families are published inside the API process. The other six, job.completed, insight.proposed, contradiction.detected, belief.superseded, basin.shifted and brain.trained, originate in a background worker and reach this stream over a database bridge.
That bridge is fail-soft. If it cannot start, worker-origin events stop reaching the stream while webhooks keep delivering every one of them. A bridge that dies later reconnects with backoff, so the gap is bounded rather than lasting until the next deploy. This is another reason a receiver that must not miss a transition should use a webhook.

Which events reach whom

Reach differs by origin and by event, and this is the one thing worth knowing about the stream.
  • An event published in the API process reaches the acting user’s own stream. You see your own graph.updated, and not another member’s.
  • An event arriving over the bridge is fanned out by what it is about. insight.proposed and brain.trained describe the organization and reach every human member. job.completed, basin.shifted, contradiction.detected and belief.superseded name a subject and reach that subject only, because the routes you read them back from are per-user: another member’s job is a 404, identity reads are self or admin, and contradiction and graph reads filter by user.
  • One exemption matches the REST surface exactly: when the subject is a provisioned subject rather than a human member, the whole team receives the event, because any member key can already read that partition with X-Exo-Subject.
The result deliberately under-delivers in one case: an organization admin gets no live view of a peer’s events even where a REST read would be allowed.

Limits

The sixth concurrent stream for an organization returns 429 too_many_streams with Retry-After. Slots are released when a connection drops. If you need delivery to many independent consumers, fan out from one stream inside your own system, or use webhooks. Last-Event-ID is accepted on the request so that resuming clients do not error, and it is not acted on. v1 does not replay: a client disconnected across an event does not receive it on reconnect, with or without the header.

Reconnecting

Treat a dropped stream as a gap, not a pause. When you reconnect:
  1. Reopen the stream.
  2. Re-read the state you care about through the API rather than assuming nothing changed. GET /v1/jobs and GET /v1/contradictions are the usual two.
Because there is no replay, a stream is the right transport for a live interface and the wrong one for a system of record.

Errors

Webhooks

Signed deliveries with retries, for anything that must not be missed.

Events and webhooks

Every route in the family, with request and response schemas.