Four mechanisms can tell your code that something happened, and the choice is usually decided by one question before any of the trade-offs matter: who needs to know, your server or a person looking at a screen?
What are the four options?
| Mechanism | Direction | Authenticates with | Survives your downtime |
|---|---|---|---|
| Webhooks | Bird posts to your server | An API key on your endpoint's signing secret | Yes, retried for about a day |
| Realtime | Bird pushes to a connected client | An app key in client code, with your backend signing private channels | No |
| SSE stream | Bird streams to a browser session | A dashboard session cookie | No |
| Polling | You ask Bird | An API key | Not applicable |
When are webhooks the right answer?
Whenever your own server is the thing that needs to react, which is most integrations.
Webhooks are the only option here with delivery guarantees. A failed delivery is retried eight times over roughly 27.5 hours, every attempt carries the same webhook-id so you can deduplicate, and anything that fails past the schedule can be replayed. So a deploy, a database failover or an outage on your side costs you nothing permanent, which is not true of any of the other three.
The costs are real and worth stating. You need a publicly reachable HTTPS endpoint, you have five seconds to respond, and delivery is at-least-once and unordered, so your handler has work to do. How failed webhooks are retried covers the schedule and the ordering rule, and handling duplicate webhooks covers the handler.
When should I use Realtime instead?
When the thing that needs to know is a browser or an app, and a request would be the wrong shape.
Realtime pushes over WebSockets: your server publishes to a named channel and every subscribed client receives it without asking. That is the mechanism for order status on a page somebody is watching, a chat message, a dashboard tile, or a background job finishing.
It is not a substitute for webhooks, for one structural reason: there is no retry. Both guides say so plainly, that Realtime does not replay events published while a client was disconnected and that a client which drops during delivery can miss the event.
The one exception proves the rule rather than softening it. A cache channel remembers its latest event and replays that to each new subscriber, which is how a view renders current state on reconnect. It stores only the most recent event and keeps no history, so a client that missed two updates gets the latest state and never sees the one in between. The platform's remedy for a gap is always the current state, never the events you missed. So Realtime is for what a person sees now, and webhooks are for what your system must not lose.
Two details shape the design. The app key ships inside client code, so a public channel is readable by anyone who has your page; private- and presence- channels ask your backend to sign each subscription instead. And Realtime has webhooks of its own, telling your server when a channel becomes occupied or vacated and when members come and go, which are configured in the dashboard rather than through the public webhooks API.
Does Bird have an SSE endpoint?
Yes, and it is almost certainly not what you want.
GET /v1/events/stream opens a Server-Sent Events stream that notifies you when API resources change, carrying the resource type, id and timestamp so you can then read the resource for its current state. The catch is the credential: it accepts a dashboard session rather than an API key, and its own API reference says to use webhooks with API-key authentication. It exists to keep a dashboard fresh, not to back an integration.
So if you were hoping to avoid running an HTTP endpoint by holding a stream open instead, that is not a path Bird offers today. The answer is webhooks, or polling.
When is polling actually correct?
Three cases, and the first is not a compromise.
When no public event exists for the thing you care about. Toll-free verification is the clear example: there is no public webhook event for its state, so reading the verification on a schedule is the intended way to learn a decision landed, not a workaround. Read it with bird sms tfn verifications get on the command line or the equivalent agent tool rather than looking for it in the API reference, because that operation is not in the public API bundle.
When you cannot accept an inbound request. Local development, a network that will not expose an endpoint, or a compliance boundary that forbids one. Polling needs nothing but outbound access.
When you only need state, not every transition. If what you want is "what is true now", one read answers it. Events tell you about changes, and reconstructing current state from a stream of changes is more work than asking.
What to watch is the rate limit. Reads and lists bucket per acting credential within your organization, so a tight polling loop spends the budget of the key running it rather than the whole organization's, and a 429 tells you to back off. Poll on an interval matched to how quickly you actually need the answer.
Webhooks and Realtime have the full detail on the first two, and rate limits covers what a polling loop is spending.