Realtime

Open a WebSocket to a collection or one of its fields and receive an event the instant a write commits.

Docs menu

Connect and subscribe

Connect to GET /v1/realtime and upgrade to a WebSocket, with the same auth as the REST API (Authorization: Bearer <key>). The server sends {"type":"connected","conn_id":...} on open, then takes JSON text messages:

const ws = new WebSocket("wss://my-app.substrio.com/v1/realtime", [], {
  headers: { Authorization: `Bearer ${process.env.SUBSTRIO_KEY}` },
});

ws.onmessage = (event) => console.log(JSON.parse(event.data));

ws.onopen = () => {
  ws.send(JSON.stringify({ type: "subscribe", collection: "orders", field: "status", value: "pending" }));
};

A field subscription (type: "subscribe") takes collection, field, and value. It filters by exact match and does not support ranges. The response is {"type":"subscribed","sub_id":...,"collection":...,"field":...,"topic":...}. An error response, {"type":"error","code":...,"error":...}, is returned when the caller cannot read the collection or the connection has reached realtime_subscriptions_limit.

A whole-collection stream (type: "subscribe_collection", {"collection":"orders"}) carries every insert, update, and delete of that collection. Each event includes the record's oid; an insert event also includes the document. The response is {"type":"subscribed_collection","collection":...,"topic":...}.

type: "subscribe_batch" takes a subscriptions array of field entries and returns one result per entry in order. Each result contains a topic or error, and count gives the number accepted. Use type: "unsubscribe" with topic or type: "unsubscribe_collection" with collection to remove a subscription. type: "ping" returns {"type":"pong"}.

Resuming a collection stream

subscribe_collection accepts an optional resumeAfter, using a _seq from an earlier subscribed_collection or change event. With it, changes since that position are replayed before the stream goes live. Without it, the stream starts from the current point. If history for resumeAfter was never kept or has been pruned, the response is {"type":"error","collection":...,"code":"HISTORY_NOT_KEPT",...}. The server does not silently return an incomplete replay.

An app with no sync peer and no retained change history records changes temporarily while its data is copied. This includes backups. The copy uses one scoped drain thread, which ends before the copy returns. It does not start permanent replication threads or retain history for a completed backup. Writes outside that copy retain their existing cost, with no capture publication. Retaining change history for resumable streams requires an explicit changes.history setting or a configured peer. The setting is persisted and reloaded at startup.

Limits

Setting Meaning
realtime_connections_limit this app's open realtime connections; the next upgrade past it is refused 429
realtime_subscriptions_limit subscriptions one connection may hold; the next subscribe past it answers a LIMIT or TENANT_LIMIT error

Stats

curl -s $SUBSTRIO_URL/v1/realtime/stats -H "Authorization: Bearer $SUBSTRIO_KEY"
{"connections": {"total": 4021, "active": 37},
 "subscriptions": {"total": 9102, "active": 112},
 "notifications": {"total": 583012},
 "dropped": {"slow_client": 3},
 "history": {"entry_builds": 44210}}

dropped.slow_client counts connections closed after falling behind their backlog; everything else is a running total since the server last started.