Bird
Bird

A world of possibilities.
One place to start.

All guides
BIRD FIELD GUIDERealtime

Recover a realtime application connection

In this guide

Bring a stale booking or job view back to the current saved state. Reconnect, authorize, subscribe and reconcile without replaying the business action.

One change. Everyone on the same page

Keep customers and staff in the moment as the state of your application changes.

Worked scenario: Friday · Studio visits. The following is an illustrative application workflow, not a live result.

  1. Request. Alex requests Friday at 14:00. The customer and studio are looking at the same pending visit. Observable state: Two views, one visit.
  2. Confirm. The booking system confirms the visit and updates its authoritative record. Observable state: Server state updated.
  3. Publish. Your server publishes the update. A connected subscriber receives the event for the booking. Observable state: Event delivered to subscriber.
  4. Reflect. The customer view reflects the confirmed booking. After a connection gap, your app refreshes from its source of truth. Observable state: Both views agree.

What this changes

  • For your customer: See a useful update without repeatedly refreshing.
  • For your team: Work from current application context.
  • For your developers: Publish/subscribe primitives for live experiences and progress.

Explore the illustrative example ↗

Before you call it complete

Confirm each observable state in your own integration. The application must establish the business outcome separately from a successful API request or delivery event. Exercise the relevant unsuccessful case in the product’s working example and follow its technical references.

Establish the first observed result

Fieldnotes owns booking FN–1042 in its database. The studio saves changes; a permitted customer sees updates through a private channel. Bird carries notifications. The booking application owns permission, mutations, revisions and the authoritative read. A publish response is not the customer's receipt.

  1. Follow the maintained quickstart for an access-confirmed app, region and client/server configuration. Keep the app secret on the server. Give the application a session-authenticated authorization endpoint that checks the actual channel and resource.
  2. Bind the event handler, request the exact private channel and wait for bird:subscription_succeeded. A connected socket alone is insufficient. Fetch the authorized booking through the application's read API to establish the initial view.
  3. Save one permitted change through the booking write API, then publish its application ID/revision using the server publish operation. Observe the receiving handler and rendered revision in the client. HTTP 200 proves edge acceptance, not this observed subscriber result.
  4. Repeat with a caller who lacks booking access. The authorization endpoint returns 403; no signature or private subscription is granted. The application read API must refuse that caller independently.

The retained implementation excerpts below show the transport handoff. Your application supplies the authorization and record APIs; they are not new Bird endpoints.

Recover the view in a deliberate order

In the fictional worked trace, both views show revision 0, Requested. The customer loses its connection. The studio saves revision 1, Confirmed, while the customer still holds revision 0. Display that last known value as stale; do not report a booking failure or create another booking.

  1. Observe loss. Bind connection states and errors. Unexpected interruption may enter unavailable and retry with backoff/jitter. Deliberate disconnected and terminal failed need different handling.
  2. Restore authority. On a new connection ID, private/presence channels require fresh authorization. The client resubscribes, but your endpoint rechecks session and current resource access before signing. Never reuse the old connection signature.
  3. Wait for the subscription. Only after bird:subscription_succeeded, begin the authorized current-state read. Keep the stale label during that read and collect incoming change notifications according to the application’s reconciliation rule.
  4. Fetch the record. Read FN–1042 from the booking service. Revision 1, Confirmed, replaces the stale revision 0. A channel occupancy query does not return this booking. No missed event was replayed.
  5. Reconcile concurrent changes. Use the record owner's revision policy, not client arrival time. Apply only changes newer than the snapshot. If the application cannot safely interpret a payload, or a bounded buffer overflows, mark the view stale and fetch again.
  6. Resume the current view. Remove stale only after successful reconciliation in the same authorized connection generation. Continue with later updates. A reconnect repairs a view; it does not retry the booking mutation.

A delayed read must not move the screen backward

Working example
Displayed before loss: revision 7
Saved while absent: revision 9
New connection: authorize; subscription succeeds
Start application read; buffer notifications
Read returns revision 9
Buffered revision 10: apply after 9
Late revision 8 / duplicate 10: ignore
Final displayed revision: 10, current

This is an application design rule, not a transport ordering guarantee. Bound the buffer by the application's memory/time budget. A new disconnect or logout invalidates the in-flight read; ignore its old-generation response. If the read fails or returns an unfamiliar state, retain the last permitted view as stale and retry the read under bounded application policy. For 403 or removed access, clear protected content and resolve the session/permission decision rather than looping.

Refusal codes 4000–4099 enter failed without automatic retry. For 4009, inspect whether member authentication ended or connection authorization timed out; restore eligible authority before manually reconnecting. Unknown errors remain diagnostic evidence, not successful recovery. A client trigger while unsubscribed returns false; no stored edit should depend on that transient signal.

Reproduce the local booking and progress cases

Open the illustrative two-view example and select Private studio booking to reset it. Its application model and emitted controls are checked locally; it makes no real connection or API call. Use this trace to understand the procedure, then use your own authorized service test for integration evidence.

  1. Choose Save & publish: both views show Confirmed at revision 1. This is the local observed subscriber result.
  2. Choose Disconnect subscriber, then Save & publish: the source advances to Requested at revision 2; the customer retains Confirmed with stale shown.
  3. Choose Reauthorize & subscribe: the view is still stale. Choose Fail state read: it remains stale. Choose Fetch application state: it reaches revision 2, Requested.
  4. Choose Repeat older notification: the displayed value does not change. This control illustrates the revision policy; it is not a provider reordering test.
  5. Choose Refuse private access, then subscribe and fetch: Access denied remains. Choose Restore eligible session, subscribe, then fetch: the current permitted record returns.

A missed completion must not start another job

Select Private rendering progress to reset the same local example. Save once: Rendering · 50%. Disconnect, then save again: the job owner is Complete · 100%, while the client retains stale 50%. Reauthorize, subscribe and fetch: the client becomes Complete · 100%. Retrying the read does not advance the source revision. These percentages describe this fictional job, not model token progress.

In the application, persist the job identity and accepted request before work. The worker owns execution, approval, cancellation, retries, failure and terminal result. Save approved customer-facing phases before publishing; never put secrets, internal reasoning or raw tool output in progress. Store large results behind an authorized result read and publish a safe reference. A viewer reconnect or refresh must not invoke a tool, repeat a purchase or restart completed work. An unfamiliar phase stays unknown until the job service explains it; failed work stays failed until an authorized retry creates the intended application transition.

An approval-required job stays pending in the job owner until an authorized approval action succeeds. A peer message cannot grant tool authority. Member-addressed delivery requires member sign-in and has no offline queue; recover durable personal notifications from the application. App/key/region management, server SDK or HTTP publication, and connected client subscriptions are different integration surfaces; do not invent an MCP subscribe/publish tool. Use member delivery and server publication for their exact operations.

Keep shared activity separate from saved work

For a shared task board, Alex and Sam read revision 7. Alex's authorized write commits revision 8 and publishes it. Sam submits an edit expecting revision 7: the application rejects the conflict, reads revision 8 and lets Sam revise the proposal. Realtime does not supply a CRDT, conflict resolver or document history.

A private channel protects the board audience; presence adds the roster. Replace that roster on subscription success. One member with three tabs is one identity and three connections; the last connection leaving removes the member. Signed member_info is visible to all members, so keep sensitive attributes out. Join names from application records when a server channel query returns only member IDs.

Typing/cursor hints may use enabled client- events on an already subscribed private/presence channel. Validate and expire these untrusted, transient hints locally; they never write the board. Access removal must affect subscription authorization and application reads/writes. Where member sign-in is used, terminate current member connections as appropriate, and refuse later unauthorized joins. Disconnect alone is not a permanent ban.

A cache channel holds only the latest API-published event for up to 30 minutes, possibly less. Client events do not populate it. Handle bird:cache_miss by recovering current state; a cached progress update or occupancy snapshot does not prove the job's current terminal state. Read durable history from your application if every intermediate edit is required.

Implement with presence, client events, channel snapshots and member termination.

Build the complete path

Copy this acceptance record for the application's own service test. The local trace demonstrates view behavior; real authorization, delivery and reconnection still require this end-to-end test.

Working example
Record / authorized read / mutation owner:
Application revision and conflict rule:
App / region / exact channel / allowed and refused callers:
First event: saved revision / edge response / observed client:
Loss: prior displayed revision / stale label / missed changes:
New connection / fresh authorization / subscription success:
Current read result / buffered changes / final displayed revision:
Failures: refused subscriber; failed read; cold cache; unknown phase
Races: delayed read; newer event; duplicate; old generation; overflow
Progress: missed completion recovered; refresh creates no new job
Shared work: conflicting edit; invalid peer hint; access removal
Actual SDK/service evidence / unresolved owner / launch decision:

Run the maintained quickstart with controlled recipients and synthetic records only after access is confirmed. Verify a real client result, disconnect it while the application commits changes, then test reauthorization, failed reads and a delayed snapshot with concurrent updates. Confirm region, limits and current terms; a local check does not establish availability or delivery. Prepare that test or start workspace setup.

Server publication excerpt

Code
import { BirdClient } from "@messagebird/sdk";

const bird = new BirdClient({
  apiKey: process.env.BIRD_API_KEY,
  realtime: {
    key: process.env.BIRD_REALTIME_KEY,
    secret: process.env.BIRD_REALTIME_SECRET,
  },
});

await bird.realtime.publish(process.env.BIRD_REALTIME_APP_ID, {
  event: "studio-visits.updated",
  channels: ["studio-visits"],
  data: { visit: "example", status: "confirmed" },
});

Subscribe from the browser

Browser subscription excerpt

Code
import { BirdRealtime } from "@messagebird/realtime";

const realtime = new BirdRealtime({
  appKey: "your-public-app-key",
  region: "us1",
});

// Public channel containing synthetic demonstration data only.
const visits = realtime.subscribe("studio-visits");
visits.bind("studio-visits.updated", (booking) => {
  console.log("Booking changed", booking);
});
// Use an authorized private channel for real customer records.

Use the same app, region, exact channel and event name. Adapt the maintained authorization/lifecycle procedure above before using private customer data; the excerpts alone do not implement reconciliation.

Connect the next step

Your application owns the customer relationship and business actions. Preserve the appropriate identity, consent, and outcome when you move between products.

Questions to resolve

Does resubscription replay missed updates?

No. Fetch current application state after subscription succeeds and reconcile newer changes.

Can channel occupancy recover a booking?

No. Occupancy and members describe the channel; the booking or job service owns its saved record.

Go deeper in the documentation

Explore Realtime and its interactive example ↗

Put it into practice.

Continue with the documentation, guides and examples for this topic. Resources are in English.

Try the practice and get an implementation brief

Start with one channel.
Add the others when you're ready.

A test API key is yours immediately. Production unlocks when you add a payment method and verify a sender.

Read docs
Using Claude Code, Cursor, or Codex? Copy a setup prompt and your agent installs the Bird CLI and skills for you. Pick yours:
Cursor