Loading documentation…
Loading documentation…
Read one Project stream using headers and recover without losing the handover gap.
Events must be enabled. Connect to the configured Events stream origin, which may differ from the public API origin. Do not guess a production stream hostname.
A workload Credential requires explicit customer.events.read.v1 permission on its own active Project, in addition to applicable Service Account network policy. The broad legacy wildcard alone does not grant Events. There is no anonymous or All-project stream.
curl --no-buffer --fail-with-body "$EVENTS_STREAM_ORIGIN/v2/projects/$PROJECT_ID/events/stream" \
--header 'Accept: text/event-stream' \
--header "X-API-Key: $PROJECT_CREDENTIAL"Use this request from your backend. Never put a workload Credential into browser JavaScript, browser storage, logs, analytics or query parameters.
A connection begins with a checkpoint, followed by events in ascending Project append position:
event: checkpoint
id: OPAQUE_CURSOR
data: {"as_of":"2026-09-27T12:00:00+00:00"}
event: inventory.refresh.completed
id: NEXT_OPAQUE_CURSOR
data: {"id":"evt_11111111111141118111111111111111","version":1,"type":"inventory.refresh.completed","occurred_at":"2026-09-27T12:00:00Z","project_id":"prj_22222222222242228222222222222222","data":{"refresh_id":"33333333-3333-4333-8333-333333333333","state":"completed","outcome":"private"},"links":{"refresh":"/v2/steam/inventory-refreshes/33333333-3333-4333-8333-333333333333"}}
The frame id is an opaque cursor, not the JSON event ID. Store progress only after validating and applying the complete event. Reconnect with Last-Event-ID: OPAQUE_CURSOR. A cursor is not authentication. Every reconnect repeats authority checks.
Use one stream per active Project context. Decode UTF-8 incrementally; a network chunk is not a frame. Handle split code points, CR/LF/CRLF, comments, multiline data, blank-line dispatch and unfinished EOF. Bound line/frame size and queued work. Deduplicate event IDs, and apply before advancing progress.
Cancel the old reader and all recovery requests before switching Project, signing out or going offline. Fence late responses using a context generation. Keep payloads and cursors in memory for a dashboard session; clear them on logout. Back off with jitter and bounded server retry delays. Stop on authority refusal rather than endlessly retrying an expired token.
| Control frame | Action |
|---|---|
stream.closing | Reconnect after bounded retry_after_ms; retain last applied cursor |
stream.recovery_required | Run checkpoint/current-state recovery; disclose gaps |
stream.authorization_revoked | Close and request valid authority; no automatic retry with the same token |
Control frames do not carry a new progress id. EOF may happen without a final control frame. Authorization is checked before admission and periodically during streaming; token expiry, account/Project changes or applicable policy revocation can close an existing connection.
The stream updates outcome availability, not continuously changing webhook attempts. Refresh visible delivery summaries separately with bounded batch reads. See recovery, limits and troubleshooting.
Keep the stream in your backend, with its Credential in your secret store. Forward only the application updates your frontend needs. Use your HTTP library's incremental streaming interface and an SSE parser that supports the frame rules above.
connect with X-API-Key and the last applied cursor, if any
for each complete frame:
checkpoint: retain the initial cursor when no events have been applied
event: validate, deduplicate, apply durably, then save its cursor
recovery required: stop and reconcile before reconnecting
authorization revoked: stop until valid access is restored
on disconnect: reconnect with bounded backoff and the saved cursorThis pseudocode describes ordering, not a complete parser or SDK. Set connection and idle deadlines, bound buffered work, and cancel the old reader before replacing the Project context. Never advance the cursor for an event your application failed to apply. If recovery cannot cover the gap, expose that limitation rather than silently treating missing outcomes as success.