# Stream Events `GET https://api.getprospect.com/v2/events` Open 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. `data` is the notification itself. - **`credits.updated`** - a credit balance changed. `data.credit_type` names the balance and `data.available` is its new value. Call [Get Credit Balances](https://getprospect.com/api-docs/credits.md) for the full picture. - **`subscription.updated`** - the workspace subscription changed; `data.status` is its new status. - **`integration.updated`** - the connection status of a CRM integration of the workspace changed; `data.integration` names it and `data.status` is its new status. - **`workspace.member_removed`** and **`workspace.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. ## Authentication Pass your API key in the `x-api-key` header. ## Example request ```bash curl --no-buffer \ --url https://api.getprospect.com/v2/events \ --header 'Accept: text/event-stream' \ --header 'x-api-key: YOUR_API_KEY' ``` ## Response 200 The stream (`text/event-stream`). Each message is an `EventStreamMessage` whose `data:` line is a JSON-encoded `EventFrame`; lines starting with `:` are heartbeat comments. ```text 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"}}} ``` ## Response 401 Missing, unknown or inactive credentials (`UNAUTHORIZED`), before the stream opens; `payload.code` `credential_in_query` (send the credential in a header) or `token_expired` (refresh the token). ```json { "success": false, "data": null, "metadata": { "timestamp": "2026-10-05T09:00:00.512Z" }, "errors": [ { "name": "UNAUTHORIZED", "message": "Credentials are not accepted in the query string: send the x-api-key header", "payload": { "field": "api_key", "code": "credential_in_query" } } ] } ``` ## Response 403 A job token (`FORBIDDEN`, `payload.code: session_required`). ## Response 429 Too many open streams for this user (`RATE_LIMIT`, `payload.code: too_many_streams`). ```json { "success": false, "data": null, "metadata": { "timestamp": "2026-10-05T09:00:00.512Z" }, "errors": [ { "name": "RATE_LIMIT", "message": "At most 20 open event streams per user: close one first", "payload": { "code": "too_many_streams" } } ] } ``` ## Response 500 Unexpected server error.