# Realtime overview

Realtime pushes events to connected clients over WebSockets. Your server publishes an event to a named channel, and every client subscribed to that channel receives it within milliseconds. Nothing polls, and no client asks for updates.

Use it when the browser has to learn something it did not ask for: an order changing status, a chat message arriving, a dashboard tile updating, a background job finishing.

## Channels, members, and connections

Three words describe the model. They are not interchangeable.

A **channel** is a named room. It exists while at least one connection is subscribed to it and disappears when the last one leaves, so there is nothing to create, register, or clean up. Names allow letters, digits, and `_ - = @ , . ;` up to 164 characters.

A **connection** is one open WebSocket. It gets an id (`26896.319537`) when it connects, and that id is what authorization signs and what publishing can exclude.

A **member** is an authenticated identity present in a channel, and only presence channels have them. One member can hold several connections at once. Someone with your app open in three tabs is one member with three connections, and presence events fire once per member rather than once per connection.

## The three channel types

The channel name decides how much protection it gets. There is nothing to configure per channel.

| Name             | Who can subscribe                   | Has members |
| ---------------- | ----------------------------------- | ----------- |
| `orders`         | anyone holding the app key          | no          |
| `private-orders` | only clients your backend signs for | no          |
| `presence-lobby` | only clients your backend signs for | yes         |

A **public** channel is readable by anyone who can load your page, because the app key ships in client code. Do not publish anything to a public channel that every visitor should not see. See [Public channels](/docs/guides/realtime/public-channels).

A **private** channel asks your backend to approve each subscription. The client posts the connection id and channel name to an endpoint you own, your endpoint decides whether that user may subscribe, and it returns a signature computed with the app secret. See [Private channels](/docs/guides/realtime/private-channels).

A **presence** channel does the same and additionally attaches an identity (`member_id`, plus optional `member_info`) to the subscriber, so every member can see who else is there and be notified as people come and go. See [Presence channels](/docs/guides/realtime/presence-channels).

## Events the client receives

Application events are yours: you pick the name at publish time (`order-updated`, `message.created`) and bind a handler to it. Alongside those, the client re-emits lifecycle events under the `bird:` prefix, which you bind exactly like your own:

- `bird:subscription_succeeded` fires once per channel when the subscription is live. On a presence channel it carries the current member list, so you can render the room before anyone moves.
- `bird:member_added` and `bird:member_removed` fire on presence channels as members arrive and leave. `member_added` fires when a person's first connection subscribes; `member_removed` only when their last one goes. A second tab opening and closing produces neither.
- `bird:connection_count` reports how many connections are subscribed to the channel, if the app has connection counting enabled. It counts connections, so the three-tab member counts three.
- `bird:subscription_error` fires when a subscription is refused, most often because authorization failed.

Names beginning `client-` are reserved for events clients send directly to each other, which is a separate app setting and only allowed on private and presence channels.

Your server can also receive events, as webhooks, when a channel becomes occupied or vacated and when members join or leave. Those arrive as `realtime.*` events through the same webhook endpoints as the rest of Bird.

## Apps, keys, and regions

An app is an isolated environment with its own credentials and its own channel namespace. Two apps never see each other's channels, which is what makes an app the right boundary between your staging and production environments.

Each app is pinned to a region, `us1` or `eu1`, chosen at creation and fixed afterwards. Pick the one closest to your users: the region decides which edge their WebSockets terminate on.

Every app carries three values, and it is worth keeping them straight because they are used in different places:

- The **app id** (`rap_…`) identifies the app in Bird API calls, and is the value in every `/v1/realtime/apps/…` path.
- The **key** is public. Browsers connect with it, and it is safe to ship in client code.
- The **secret** pairs with the key to authenticate server-side calls and to sign channel authorization. It is shown once, at creation. Anyone holding it can publish to your app and forge presence identities.

Manage all of this on the [**Realtime → Apps**](https://bird.com/dashboard/w/realtime/apps) page, which is also where you rotate keys: add a second key, roll it out, then revoke the old one.

## Visibility

The [**Realtime → Metrics**](https://bird.com/dashboard/w/realtime/metrics) page reports three numbers per app, or across the workspace, over a window you choose. The definitions matter, because two of them are not what a first reading suggests:

- **Max connections** is the highest number of connections open at the same moment inside the window. It is a peak, not a total, and it is the number the connection limit applies to.
- **Average connections** is the mean of the daily peaks, not the mean of every sample. A workspace that spikes each afternoon and idles overnight shows an average well above its quiet hours.
- **Messages** counts event deliveries, one per channel: a publish naming 50 channels counts as 50. It also includes the events the protocol sends on your behalf, so presence joins and connection-count updates land in the same number, which is why it can run ahead of the publishes your code made.

Usage is aggregated in one-minute buckets, so numbers appear a few minutes behind live traffic. There is no public usage API yet. If you need usage in your own systems, count publishes as you make them, or derive activity from `realtime.*` webhooks.

## Limits during the preview

Realtime is free to use during the preview. Each workspace is limited to 100 concurrent connections and 200,000 messages per day, across all of its apps. Creating more apps does not raise the ceiling, because it applies to the workspace.

Per-request caps apply regardless of the preview: one publish names at most 100 channels, a batch carries at most 10 events, and an event payload is capped at 10 KB serialized.

## Next steps

- [Send your first realtime event](/docs/get-started/send-your-first-realtime-event) walks the whole loop end to end.
- [Publishing events](/docs/guides/realtime/publishing-events) covers publishing from your server, batching, and excluding the originating client.
- [Authorizing channels](/docs/guides/realtime/authorizing-channels) is the contract your backend implements for private and presence channels.