Close codes
In addition to application
ping/pong, the server emits standard WebSocket control ping frames roughly every 30 seconds.
Replay with last_seq
Persist the highest seq you have fully processed (not just received). On reconnect, supply it as a query parameter:
last_seq + 1 if that sequence is still inside the JetStream window (~24h). If the cursor is older than retention, the server starts from DeliverNew — you must then hydrate via HTTP.
When reconnecting:
- Open WebSocket with
?last_seq=. - Wait for
connected. - Re-send the exact same
scope.setyou had before — server state does not persist across sockets. - Process events; advance your stored
last_seqonly after each event is durable in your system.
Hydration after long gaps
If you’ve been offline longer than retention (~24h), or it is your first connect, run HTTP reads in parallel before going live. Recommended order:Backpressure
If your consumer cannot keep up with delivery rate, the server will close the socket with4002 rather than buffer unbounded memory. To avoid 4002:
- Use the narrowest scope and
entityfilter that satisfies your use case. - Process events on a non-blocking queue — do not do synchronous HTTP/DB work in the WebSocket read loop.
- Persist
last_seqafter each batch so 4002 reconnects are cheap.
Suggested backoff
Never retry
4001 without first re-validating credentials — a tight loop will rate-limit your firm key.
