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
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. Thedata: line carries a JSON envelope with the type repeated inside it, so a consumer can dispatch on either.
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:: 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 thedata 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.
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.proposedandbrain.traineddescribe the organization and reach every human member.job.completed,basin.shifted,contradiction.detectedandbelief.supersededname 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.
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:- Reopen the stream.
- Re-read the state you care about through the API rather than assuming nothing changed.
GET /v1/jobsandGET /v1/contradictionsare the usual two.
Errors
Related
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.