A channel exists as soon as something subscribes to it and disappears when the last connection leaves. Its name picks the type: public for anything a visitor may read, private for anything scoped to a customer, presence for a room with a roster, and a cache prefix for state a late joiner needs immediately.
const bird = new BirdRealtime({ appKey: APP_KEY, region: "us1" });
// Public: anyone holding the app key can subscribe.
const scores = bird.subscribe("match-42");
// Private: your backend signs every subscription.
const order = bird.subscribe("private-order-ord_123");
// Presence: private, plus an identity the room can see.
const room = bird.subscribe("presence-room-42");
// Cache: the latest event replays to whoever joins next.
const build = bird.subscribe("cache-build-8821");
build.bind("bird:cache_miss", () => showSkeleton());
The prefix is the configuration.
There is no channel registry to keep in step.
Channels are the addressing model of the Bird Realtime API. You never create one: you subscribe to a name, and the first three characters of that name tell the edge how to treat it. A name with no prefix is public. private- asks your backend to approve each subscription. presence- does the same and attaches an identity. private-encrypted- seals the payload with a key Bird never holds. Names take up to 164 characters, are case-sensitive, and are the one part of a channel you should think about carefully, because a public name is visible to anyone holding the app key.
Five kinds of room.
Same protocol, same client, same publish call. The name is what differs.
- 01
Public channels.
No authorization endpoint, no registration. Anyone holding the app key can subscribe, which makes them right for build results, live scores, flight information, or a status page, and wrong for anything scoped to one customer. Keep identifiers out of the name: orders reveals nothing, orders-user-4821 reveals that user 4821 exists.
- 02
Private channels.
A private- name routes the subscription through your own endpoint, which checks the session and signs the connection id and channel name with the app secret. Your rules, your session, your 403. The edge verifies the signature and nothing else reaches the channel.
- 03
Presence channels.
Private-channel authorization plus an identity, so every subscriber gets the member list and hears about arrivals and departures. This is the one channel type with a roster.
- 04
Encrypted channels.
A private-encrypted- channel carries payloads your server seals with a 32-byte master key that never appears in a Realtime request. The edge and everything between it and the browser see ciphertext. Channel and event names stay in the clear, so pick names that do not leak what you are protecting.
- 05
Cache channels.
Lead the name with cache-, after any type prefix, and the channel remembers its latest API-published event and replays it to each new subscriber. The subscription doubles as the initial state fetch. Two constraints worth designing around: only the most recent event is kept, and it may expire before the 30-minute ceiling, so put the whole state in every payload and repopulate from the cache-miss webhook rather than assuming the cache is warm.
One publish, up to a hundred channels.
Publishing is an ordinary REST call from your server. Name up to 100 channels in one request and the edge fans the event out to all of them. A batch carries up to 10 unrelated events, each to its own channel. Pass the acting client's connection id as exclude_connection_id and the tab that already applied the change locally is skipped. Ask for connection or member counts with include and the response tells you the state of each channel at publish time. Retry with the same idempotency key and you will not deliver twice.
// One event, up to 100 channels, one request.
const result = await bird.realtime.publish(APP_ID, {
event: "score-updated",
channels: ["match-42", "cache-match-42"],
data: { home: 2, away: 1 },
// The tab that scored already rendered it locally.
exclude_connection_id: "26896.319537",
include: ["connection_count"],
});
for (const channel of result.data ?? []) {
console.log(channel.name, channel.connection_count);
}
Clients can talk to each other directly.
A typing indicator or a cursor position does not need to visit your API. Enable client events on the app and a subscribed client can trigger an event named client-something straight to the others in the channel, capped at 10 per second per connection. They only work on private and presence channels, which is deliberate: the app key ships in your page, so authorization is what makes a client trustworthy enough to broadcast. Treat what arrives as a signal, never as authoritative state, because the edge does not validate the payload.
What a channel will and will not remember.
A publish returns once the edge has accepted the event. Delivery is asynchronous, there is no per-client receipt, and a client that drops mid-delivery will not be sent the event again on reconnect. That is the honest contract, and it is why durable state belongs in your database and events announce that it changed. Caps are the same on every plan: 100 channels per publish, 10 events per batch, 10 KB per payload, 164-character channel names.
Go deeper in the docs.
The Realtime overview defines channels, members, and connections in one page. Publishing events covers broadcast, batch, and exclusion, cache channels explains the replay, and querying channel state is the server-side read for occupancy and counts.
The rest of Realtime
One app, one key pair. Explore the other capabilities.
Subscribe to a name and start publishing.
Create an app, ship the public key in your client, and keep the secret on your server. The free plan covers 100 concurrent connections.