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.