Realtime protocol
The WebSocket wire format: subscribe, snapshot, events, resume.
Connecting
wss://cloud.flaresinc.com/realtime
One connection can carry several subscriptions. Frames are JSON objects with a type.
Subscribe (client → server)
{
"type": "subscribe",
"project": "prj_…",
"key": "basket_public_…",
"collection": "messages",
"userToken": "eyJ…", // optional: makes auth.user.* available to rules
"where": [["room_id", "==", "r_42"]], // optional
"snapshot": true, // optional, default true
"limit": 50 // optional snapshot size
}
The gateway asks the API — with these credentials — what this caller may see, and receives a resolved predicate. It decides nothing itself; the same rules that govern reads govern the stream.
Snapshot (server → client)
{ "type": "snapshot", "collection": "messages",
"records": [ { "id": "rec_…", "version": 2, … } ],
"cursor": "eyJ…" }
The state at subscription time. Set snapshot: false for changes only.
Events (server → client)
{ "type": "event", "collection": "messages", "event": "created",
"record": { "id": "rec_…", "version": 1, … }, "cursor": "eyJ…" }
event | Carries |
|---|---|
created | The new record. |
updated | The record after the change, with its new version. |
deleted | The record's id. |
Only records matching your predicate and visible to you arrive.
Cursors and reconnecting
Keep the last cursor. On reconnect, send it back:
{ "type": "subscribe", …, "cursor": "eyJ…", "snapshot": false }
The stream resumes from that point — no replay, no gap. Without a cursor you get a fresh snapshot instead.
Unsubscribe
{ "type": "unsubscribe", "collection": "messages" }
Errors
{ "type": "error", "error": "forbidden", "message": "That key does not belong to this project." }
An error frame does not always close the socket — an unauthorised subscription fails while other subscriptions on the same connection continue.
Metering
Delivered events count as realtime_messages; concurrent connections are sampled as
realtime_peak_connections. Usage →