The Sync real-time shared-state model
Sync is Orbit’s real-time shared-state primitive — the answer to “several clients need to see the same changing thing.” A browser session, a mobile app, and a backend worker can all read the same object and observe each other’s writes within milliseconds, without you running any pub/sub infrastructure. The object model mirrors Twilio Sync, so an existing Twilio Sync integration migrates as a path swap. This page explains the model at concept level. For the endpoint-by-endpoint walkthrough, see the Sync real-time state guide; for the raw request/response shapes, the Sync API reference.1. The four object kinds
Every Sync object is addressed by aunique_name you choose (letters,
digits, ., _, :, and -, up to 256 characters) and comes in one of
four kinds:
The durable kinds persist until you delete them or their
ttl expires. A
stream persists nothing: only clients subscribed at publish time receive a
message. That durability split is the first modeling decision — Sync is
built for coordination state, not the system of record. Keep authoritative
data in your own database; use Sync for the shared, changing view of it.
2. The convergence loop: REST write → WebSocket broadcast → rehydrate
Sync’s architecture is a write path and a push path that meet at the client:- REST is the only write path. Every mutation — create, update,
delete, publish — is a REST call against
/api/v1/sync. There is no write-over-socket frame, so a write’s authentication, authorization, and validation always go through the same API pipeline. - A write publishes a change event. The committed write fans an event out to the per-object channel.
- The WebSocket gateway relays the event to subscribers. Clients
connected to
/api/v1/ws/syncand subscribed to that object receive an event envelope (type,kind,unique_name,revision,data, pluskeyfor map events andindexfor list events). Update events carry the full newdata; removal events carry none. - Clients rehydrate over REST after a (re)connect. The gateway is a
notification channel, not a state source. After connecting or
reconnecting, a client
GETs the object’s current state over REST, then applies streamed events on top. The same rehydration repairs anything the client missed while disconnected — the REST read is the convergence point for the durable kinds.
revision numbers (per object on
documents, per item on maps and lists) let a client detect a stale base: if
the reply revision moved past the one you held, another writer won, and you
converge on a fresh GET instead of retrying with a stale base. Second, a
stream never converges — by design. If late-joining clients must see a
value, put it in a map or document, not a stream.
One presence map update, end to end
The subscriber converges on the event; the late joiner converges on the REST read. Both end at the same state.3. Tenant scoping
Tenant scoping is absolute. Every object lives inside exactly one tenant, bound at authentication: the API key or session token you present resolves to a tenant, and every path you address is read inside that tenant’s scope. The WebSocket gateway applies the same binding — subscriptions can only attach to objects in the authenticated tenant. One tenant can never read another tenant’s objects, and there is no addressing form that crosses the boundary. Sync imposes no platform-level gate on what you share inside your tenant. Compliance controls (quiet hours, consent, suppression) are tenant-owned configuration elsewhere in the platform; shape your own usage policy on the object.4. TTL and expiration semantics
ttlis a seconds value (integer, maximum one year) attached to a document or map write.ttl: 0is the default and means no expiry — the object persists until youDELETEit.- Setting a new
ttlon a write resets the expiry window from that write. Once the window elapses the object vanishes: reads returnSYNC_OBJECT_NOT_FOUND, and subscriptions on it stop receiving updates. - Use a short
ttlon anything whose absence should clean up automatically — presence entries are the canonical case, because a crashed client then drops off the roster without a cleanup pass. Usettl: 0for state you manage explicitly, and delete it when the workflow ends. - A stream has no retention at all: a client that was not subscribed at publish time never sees the message, no matter when it connects.
5. The read/write permission split
Sync splits access by operation, tied to organization roles:- Reads —
GETon any object family, and WebSocket subscriptions — are open to any authenticated member of the organization. - Writes — create, update, delete, and stream publish — require an
operator role:
owner,admin,developer, oragent. The dashboard’smemberrole can read but not write.
6. When to use Sync — and when not to
Orbit has several real-time surfaces. Pick Sync when the shape is shared state, not event delivery:- Inbox — a durable alert feed where the item itself is the artifact and items accumulate. Sync objects get overwritten or expire; inbox items accumulate. If the history matters, it is an inbox.
- Notifications — point-in-time alerts to an operator. If the event is the message, it is a notification, not shared state.
- Webhooks — server-to-server push to your endpoint. Prefer Sync when the consumers are client applications (browser, mobile, desktop) that you do not want polling; prefer webhooks when the consumer is your backend.
- Async work — Sync is live coordination and carries no durability guarantee for work. A job that must survive a process restart belongs on the queue backbone, not in a Sync object — see how Orbit processes work asynchronously.
See also
- Sync real-time state guide — the full REST + WebSocket walkthrough with a runnable end-to-end example
- Sync API reference — endpoint-by-endpoint shapes
- Endpoint reference (auto-generated)
- How Orbit processes work asynchronously — the queue backbone for work that must survive restarts
- Tenant isolation — the scoping guarantee Sync’s object model builds on