# Private channels

A channel whose name starts with `private-` cannot be subscribed to on the app key alone. Before the edge admits a client, your own backend has to approve that specific client for that specific channel and return a signature computed with the app secret.

Use one whenever the events belong to somebody: a customer's order updates, a tenant's dashboard, one user's notifications.

```typescript
const bird = new BirdRealtime({
  appKey: "your-app-key",
  region: "us1",
  authEndpoint: "/bird/auth",
});

const orders = bird.subscribe("private-orders-4821");
orders.bind("order-updated", (data) => {
  console.log(data);
});
```

The client posts the connection id and channel name to `authEndpoint`, your endpoint decides whether this caller may subscribe, and the subscription completes only if the signature it returns is valid. [Authorizing channels](/docs/guides/realtime/authorizing-channels) documents that request, the response, and the exact string to sign.

## Naming channels per user or per tenant

Put the scope in the channel name and let your endpoint check it. A client can ask to subscribe to any name it likes, so the check in your backend is the only thing that stops one customer from reading another's channel:

```typescript
if (channel_name !== `private-orders-${user.accountId}`) {
  return res.sendStatus(403);
}
```

That single comparison is the authorization. Without it, a private channel is only as private as your channel names are unguessable, which is not private at all.

## What private channels support

- **Client events.** When the app has client events enabled, a subscribed client can send `client-` events to the other clients on the channel without a round trip through your server. The edge rejects these on public channels.
- **Connection counting.** With the app setting enabled, the client receives `bird:connection_count` for the channel.
- **Server publishing.** Publishing from your server works exactly as it does for a public channel: name the channel in the `channels` array. No authorization applies to your server, which already holds the app secret.

Private channels have no member identity and no member events. If you need to know who is present, use a [presence channel](/docs/guides/realtime/presence-channels).

## When a subscription is refused

A refused subscription arrives on the client as `bird:subscription_error`, and the two common causes are your endpoint returning a non-2xx status, which is the correct outcome for a caller who may not subscribe, or a signature that does not match what the edge computed. The failure list in [Authorizing channels](/docs/guides/realtime/authorizing-channels) covers how to tell them apart.

## Next steps

- [Authorizing channels](/docs/guides/realtime/authorizing-channels) is the contract your backend implements.
- [Presence channels](/docs/guides/realtime/presence-channels) add member identity on top of the same authorization.
- [Excluding event recipients](/docs/guides/realtime/excluding-event-recipients) stops a client from receiving the event it caused.