Sync API
Sync is a general-purpose, real-time shared-state primitive for building presence indicators, collaborative widgets, live counters, or any app state multiple clients need to see change in real time — without standing up your own pub/sub infrastructure. It mirrors the Documents / Maps / Lists / Streams model of Twilio Sync: Documents, Maps, and Lists are durable (they persist until you delete them), Streams are ephemeral pub/sub with no persistence. Base path:/api/v1/sync
WebSocket gateway: /api/v1/ws/sync — connect here to receive change events (document.updated, map.item.set, list.item.added, stream.message, etc.) as they happen.
Authentication: API key (X-API-Key) or session JWT. Reads are open to any authenticated member; writes require an operator role (owner, admin, developer, or agent).
Documents
A Document is a single JSON object addressed by a unique name, with a monotonic revision that bumps on every update.Maps
A Map is an unordered, key-addressed collection of JSON values.Lists
A List is an ordered, append-indexed collection of JSON values.Streams
A Stream is ephemeral pub/sub — nothing is stored. Only clients connected to the WebSocket gateway at publish time receive the message.WebSocket consumer
The gateway exposes a small JSON protocol over the socket. Sendsubscribe / unsubscribe frames naming a kind (document, map, list, or stream) and a unique name; the server answers subscribed / unsubscribed to acknowledge, then relays change events.
WebSocket constructor cannot set headers, so encode the credential as a subprotocol token: new WebSocket(url, ["orbit-sync-v1", "Bearer.<jwt>"]) for a session JWT, or new WebSocket(url, ["ApiKey.<key>"]) for an API key. The server scans the Sec-WebSocket-Protocol list for the Bearer. / ApiKey. token and promotes it to the normal auth header shape during the upgrade handshake. Never embed a secret API key in front-end code shipped to end users — mint keys on your backend.
type (the discriminator), kind, unique_name, revision (absent for stream.message), key (map events), index (list events), data (absent on removal events), and a ts publish timestamp. To unsubscribe, send { "type": "unsubscribe", "kind": "...", "name": "..." }. Client ping frames get a pong answer; malformed frames get an error frame with a reason.
Ops debugging with wscat
Recipe — a collaborative presence indicator
A classic presence pattern: each client announces itself in a Map keyed by user id, and every client renders from those change events. REST writes, WebSocket reads.kind: "map", name: "room_42_presence", then each announce() call fans out to every connected viewer. To compute a live viewer counter instead of a per-user roster, have your backend publish to a Stream (POST /sync/streams/room_42_viewers/messages) and render stream.message events — no Map cleanup needed.
See also
- Webhooks — for server-to-server event delivery instead of a client WebSocket
- Video rooms — a common Sync use case is live participant/viewer counts alongside a video room