Skip to main content
An event stream delivers a project’s events to your server over one HTTP connection as they happen. It carries the same events as your webhooks and event history, so you can use it instead of a public webhook endpoint or alongside one.
Event streams are a beta. A team owner or admin enrolls the team on the project’s API Keys page in the Console by accepting the beta terms. Without enrollment, opening a stream returns 403 with the feature_unavailable code.

Before you start

You need:
  • a team key (pmfa_…) or a project credential (pmfa_pt_…) with the events:listen permission. A project credential can stream only its own project. Client tokens cannot open a stream.
  • the project ID.
Keep these credentials on your server. Do not use them in a browser.

Open a stream

The response is a server-sent event stream. Each message has an event name, a JSON data field, and, for events, an id:
The first message is always ready. sequence counts messages on this connection and starts again at 1 on every connection. Use cursor, not sequence, to track your position.

Filter event types

Pass types as a comma-separated list, or repeat the parameter. A trailing * matches a prefix:
Omit types to receive every event type. You can send up to 32 filters. The ready message lists the filters the stream applied.

Read an event

The event object in each event message has the same fields as a project event from the event history API: id, type, source, environment, createdAt, payloadAvailability, replayableUntil, metadataExpiresAt, and payload. When payloadAvailability is available, payload.data is the base64 of the exact webhook body for that event. Decode it to read the same JSON your webhook endpoint receives. The body is present only when all of these are true:
  • hosted message storage is enabled for the number that produced the event;
  • the body is within the storage size limit; and
  • that storage is still enabled, and the number is connected, when the stream sends the event.
Otherwise payload is null and payloadAvailability explains why: Use the event id to deduplicate. The same event has the same id on the stream, in webhooks, and in event history.

Resume after a disconnect

Every event message carries a cursor, also sent as the message id. To continue where you stopped, reconnect with the last cursor you processed, either as the Last-Event-ID header or as the cursor query parameter:
Standard server-sent event clients send Last-Event-ID automatically when they reconnect. The stream then delivers the events after that cursor. A cursor:
  • is valid for 24 hours after its event;
  • works only with the same team, project, and types filter; and
  • must not be modified.
A cursor for another project or filter returns 400 stream_cursor_invalid. An older cursor returns 410 stream_cursor_expired: reconnect without a cursor and read what you missed from event history. If events after your cursor are no longer kept, the stream sends one gap message with reason: "retention_exceeded", then continues with the next available event. The message includes missedEvents when Polymorfa can count the missing events; without missedEvents, some events may be missing and their number is unknown. Read the missing events from event history.

Acknowledge events

By default (ack=auto) the stream releases each event as soon as it is written to the connection. Save the cursor after you process each event, and resume from it after a disconnect. To have the stream wait for your processing, open it with ack=manual. The stream then keeps at most maxInFlight unacknowledged events outstanding. Acknowledge everything up to a message with its cursor and sequence, using the same credential that opened the stream:
Acknowledgements are cumulative. After each acknowledgement the stream sends a checkpoint message with the acknowledged cursor. If an event stays unacknowledged for 30 seconds, the stream sends backpressure; if it is still unacknowledged 30 seconds later, the stream sends dropped and closes. Reconnect from the last acknowledged cursor.

Keep the connection healthy

The stream checks your credential, project, and beta enrollment at every heartbeat, so revocation takes effect within one heartbeat interval. In ack=auto mode, a client that stops reading for 30 seconds is disconnected. Reconnect with exponential backoff and jitter, starting at one second and capping at 30 seconds.

Limits

  • A team can have 10 open event streams at once. Another connection returns 429 stream_connection_limit_reached with Retry-After.
  • Opening streams counts toward a per-credential rate limit. Exceeding it returns 429 rate_limit_exceeded with Retry-After.
See Errors for recovery steps for each code, and the Stream project events reference for the full message schema.