Stream Events
/v2/eventsOpen the realtime event stream of your workspace (text/event-stream): notifications, credit balance changes, subscription and integration changes. Authenticate with your API key in the x-api-key header; a credential in the query string (api_key, apiKey, jwtToken) is refused with 401 credential_in_query. Every message is one data: line holding a JSON EventFrame; there is no event: line, so dispatch on data.type, and ignore a type you do not know (the catalog only grows). Lines starting with : are heartbeat comments, every 25 seconds. retry comes in the first message only; id is for logs, and nothing is replayed: on every (re)connect the first event is stream.ready, after which re-read the state you show. The server ends the stream after 30 minutes; reconnect. Session events never reach an API key. At most 20 open streams per user (429 too_many_streams). Free: no credits.
The first bytes of a stream:
retry: 5000
id: r7-1
data: {"type":"stream.ready","created_at":"2026-10-05T09:00:00.512Z","data":{"connection_id":"r7-1","heartbeat_seconds":25,"expires_at":"2026-10-05T09:30:00.512Z","scope":{"workspace_id":830159,"user_id":"64f1a2b3c4d5e6f7a8b9c0d1"}}}
: ping
id: r7-2
data: {"type":"credits.updated","created_at":"2026-10-05T09:00:00.512Z","data":{"credit_type":"email_search","available":48}}
id: r7-3
data: {"type":"notification.created","created_at":"2026-10-05T09:00:00.512Z","data":{"id":"6ac35b5ffe3fe40ea209b07e","workspace_id":830159,"type":"export","status":"done","title":"Export completed","body":"Your export of 1,250 people is ready.","body_format":"text","link":{"url":"https://files.example.com/exports/6ac35b5ffe3fe40ea209b07d/people-2026-10-05.csv","label":"Click here to download.","kind":"file","file_name":null},"subject_id":"6ac35b5ffe3fe40ea209b07d","read":false,"read_at":null,"created_at":"2026-10-05T08:10:07.385Z"}}
How are credits spent?
The stream is free and consumes no credits. It only tells you that something changed; read the details with the matching endpoint.
Event types
Every event is { "type", "created_at", "data" }. Dispatch on type and
ignore any type you do not recognize: new types are added over time, and
existing types and fields are never removed.
stream.ready- the first event of every connection. Re-read the state you display (credit balances, notifications) after it: events sent while you were disconnected are not replayed.notification.created- a new in-app notification of your workspace, for example a finished export or CSV enrichment.datais the notification itself.credits.updated- a credit balance changed.data.credit_typenames the balance anddata.availableis its new value. Call Get Credit Balances for the full picture.subscription.updated- the workspace subscription changed;data.statusis its new status.integration.updated- the connection status of a CRM integration of the workspace changed;data.integrationnames it anddata.statusis its new status.workspace.member_removedandworkspace.members_removed- one or all other members were removed from the workspace.
session.refresh_required and session.revoked are sent to signed-in app
sessions only. A stream opened with an API key never receives them; if the
key's owner leaves the workspace, the stream closes.
Staying connected
The server ends every stream after 30 minutes, and network hops can drop it
sooner. Reconnect whenever it ends - the same request again - and treat the
stream.ready that follows as your cue to refresh. A comment line (: ping)
arrives every 25 seconds, so a connection silent for much longer than that is
dead. One user can hold up to 20 open streams at a time.
For server-to-server integrations that must not miss an event, poll the
relevant endpoint after each stream.ready rather than relying on the stream
alone.
Responses
For error handling see Errors.
