Skip to content

Stream protocol reference

Generated from the AsyncAPI document (Status302 Stream0.1.0, AsyncAPI 3.0.0). For how to use the protocol, see the stream guide.

Realtime protocol shared by the Status302 web app and client integrations. One WebSocket connection carries many topic subscriptions. Replayable topics carry a per-topic monotonically increasing seq; clients resume with since after reconnecting. When the requested since is older than the replay window the server answers resync with the REST snapshot to reload from. A topic's seq can start over (the server lost its counters); the server then sends resync (reason gap) on the affected subscriptions and continues from the new seq, so clients forget the last seq they kept for a topic on every resync.

ping is answered with heartbeat echoing its id. Subscribing to a topic again replaces its filter. Close codes are listed in x-close-codes; payloads and filters per topic: https://status302.com/docs/stream/.

Servers

ServerURLAuthentication
productionwss://api.status302.com/v1/streamapiKey or ticket
developmentws://localhost:8080/v1/streamAs production

Authentication

apiKey: Authorization header

Bearer tx_live_<keyId>_<secret> on the upgrade request (server-side clients). The key needs the stream scope and a plan with API access (Pro+; the sales/onsales firehoses need Elite+). Each topic also needs one scope of its REST equivalent, otherwise subscribe is answered forbidden_topic naming the missing scope: event.{id}.kpis and catalog.rows need market:read; sales and event.{id}.sales need sales:read; event.{id}.primary needs maps:read or market:read; onsales needs catalog:read; alerts needs alerts:write. Each connection holds one of the org's stream connection slots (plan quota; refused with 4429) and at most the plan's subscriptions per connection. Revoking the key, or the end of its rotation grace or expiry, closes its connections with 4401; changing its scopes closes them with 4409.

ticket: ticket query parameter

Single-use ticket (60 s) from POST /v1/stream/ticket, for browsers; issued to a session or to an API key with the stream scope (the connection then acts as that key, topic scopes included). A user holds at most 20 open connections and a session at most 10 (across every server; 4429 beyond); API keys count against their org's quota instead.

Client messages

JSON text frames you send. Each request carries an id that the reply echoes.

subscribe

{"op":"subscribe","id":"r1","topic":"event.4102.sales"}
FieldTypeRequiredDescription
op"subscribe"yes
idstringyesClient request id echoed in the reply.
topicTopicyes
sinceintegernoReplay everything after this seq (replayable topics only).
filterobjectnoTopic-specific filter (per topic: https://status302.com/docs/stream/).

unsubscribe

{"op":"unsubscribe","id":"r1","topic":"event.4102.sales"}
FieldTypeRequiredDescription
op"unsubscribe"yes
idstringyes
topicTopicyes

ping

{"op":"ping","id":"r1"}
FieldTypeRequiredDescription
op"ping"yes
idstringyes

Server messages

JSON text frames the server sends, distinguished by type.

subscribed

{"type":"subscribed","id":"r1","topic":"event.4102.sales","seq":48231}
FieldTypeRequiredDescription
type"subscribed"yes
idstringyes
topicTopicyes
seqintegeryesCurrent head seq of the topic.
snapshotstringnoREST path returning the full current state.

data

{"type":"data","topic":"event.4102.sales","seq":48231,"ts":"2026-10-28T18:04:11.204Z","data":{}}
FieldTypeRequiredDescription
type"data"yes
topicTopicyes
seqintegeryes
tsstring, date-timeyes
dataobjectyes

resync

{"type":"resync","topic":"event.4102.sales","reason":"gap","snapshot":"<snapshot>"}
FieldTypeRequiredDescription
type"resync"yes
topicTopicyes
reason"gap" | "backpressure"yes
snapshotstringyes

error

{"type":"error","code":"bad_request","message":"<message>"}
FieldTypeRequiredDescription
type"error"yes
idstringno
code"bad_request" | "unknown_topic" | "forbidden_topic" | "subscription_limit" | "rate_limited" | "unavailable"yesunknown_topic: not a topic, or an event topic whose event id does not exist. forbidden_topic: the plan, or the API key's scopes, do not allow the topic. unavailable: the topic could not be read right now (Redis); retry the subscribe.
messagestringyes

heartbeat

{"type":"heartbeat","ts":"2026-10-28T18:04:11.204Z"}
FieldTypeRequiredDescription
type"heartbeat"yes
idstringnoThe ping this answers.
tsstring, date-timeyes

Error codes

The code of an error message. The connection stays open; the request it answers had no effect.

  • bad_request
  • unknown_topic
  • forbidden_topic
  • subscription_limit
  • rate_limited
  • unavailable

unknown_topic: not a topic, or an event topic whose event id does not exist. forbidden_topic: the plan, or the API key's scopes, do not allow the topic. unavailable: the topic could not be read right now (Redis); retry the subscribe.

Topics

One of: event.{id}.kpis, event.{id}.sales, event.{id}.primary, sales, onsales, alerts, catalog.rows (app only). Reserved, without a producer yet (answered unknown_topic): event.{id}.listings, event.{id}.map, catalog.movers.

Valid topic names match ^(event\.[1-9][0-9]*\.(kpis|sales|primary)|sales|onsales|catalog\.rows|alerts)$.

Close codes

CodeNameMeaningReconnect?
1001going_awayIdle timeout (no client message or pong for 60 s; the server sends heartbeat and pings every 20 s), or a failed write.Yes, with backoff; resume with since
1009message_too_bigA client message exceeded 64 KiB.Yes, with backoff; resume with since
1012service_restartThe server is restarting; reconnect and resume with since.Yes, with backoff; resume with since
4008slow_consumerThe connection's send buffer overflowed; reconnect and resume with since.Yes, with backoff; resume with since
4401unauthorizedNo credentials, ticket invalid or already used, session or API key revoked, key expired or past its rotation grace. Get new credentials before reconnecting.No, not with the same credentials
4403forbiddenThe plan does not allow streaming, or the API key lacks the stream scope.No, not with the same credentials
4409reconnectRole, active org, plan or API key scopes changed; fetch a new ticket if you use one, reconnect and resume with since.Yes, with backoff; resume with since
4429too_many_connectionsThe org's stream connection quota is used up (API keys), or the user or session already holds its maximum of open connections (signed-in clients; 20 per user and 10 per session by default); close another connection or retry later.Yes, with backoff; resume with since