---
title: "SMS callback URLs"
description: "Send an SMS message's events, or a messaging profile's replies, to your own HTTPS URL. Covers routing, signature checks, retries, and replaying failures."
canonical: "https://bird.com/docs/guides/sms/callbacks"
---

# SMS callback URLs

A callback URL receives the events of one SMS message, or of every message on a messaging profile, in place of your webhook endpoints. Use it when a message or a program needs its own receiver.

Manage the signing secret, the attempt history and replay in the dashboard, or through the [API](/docs/api/reference/rotate-webhook-callback-signing-secret) and the `bird webhooks callbacks` commands. Set a profile's URL when you [create a messaging profile](/docs/api/reference/create-sms-messaging-profile) or [update one](/docs/api/reference/update-sms-messaging-profile).

## Choose where events go

The URL is resolved once, when the message is accepted, and kept for the message's life. A later change to the profile does not reroute a message in flight.

| Message                                        | URL used                                                              |
| ---------------------------------------------- | --------------------------------------------------------------------- |
| Sent with `options.callback_url`               | That URL                                                              |
| Sent with `options.callback_url` set to `null` | None: events go to your webhook endpoints                             |
| Sent without it, on a messaging profile        | The profile's `options.callback_url`                                  |
| Received on a number of a messaging profile    | The profile's `inbound.callback_url`, then its `options.callback_url` |
| No URL from any of the above                   | None: events go to your webhook endpoints                             |

The URL must be HTTPS, at most 2048 characters, and a public address. A private, loopback, or link-local host returns `422`.

The callback attempts list shows where a received message's event was delivered.

## What the URL replaces

While a URL applies, that message's events skip your webhook endpoints. Other messages in the workspace still reach them. A failed callback does not fall back to your endpoints: use [replay](#find-and-replay-failed-callbacks) to recover it.

MCP Events subscriptions and connector endpoints still receive the message's events.

A messaging profile's `callback_events` choose which outbound events go to the URL, from `sms.accepted`, `sms.sent`, `sms.delivered`, `sms.undelivered`, `sms.failed`, `sms.rejected`, and `sms.expired`. An event the list leaves out reaches no webhook endpoint for that message either. A profile without a list, and a send without a profile, deliver every outbound event. `sms.received` is never filtered. The payloads are the [SMS events](/docs/guides/sms/events).

## Verify signatures

Callbacks use the same [Standard Webhooks](/docs/guides/webhooks#verify-signatures) headers and HMAC-SHA256 signature as webhook endpoints, so the verification code you have works unchanged. The key is your workspace's callback signing secret, one secret shared by every callback URL.

The first secret is never shown. Rotate it to get one you can use, under **Webhooks → Direct callbacks** in the dashboard or with [Rotate callback signing secret](/docs/api/reference/rotate-webhook-callback-signing-secret). The new secret appears once, so store it before you leave the page. For the next 24 hours each callback is signed with both the old and the new secret, so you can deploy the new one without dropping deliveries. A workspace holds at most 5 secrets valid at once, so rotating again inside the overlap can be refused. A rotation takes effect within about a minute.

Rotate before you rely on verification: until you do, callbacks carry a signature made with a secret you cannot read.

## Retries and duplicates

Callbacks follow the [retry schedule of webhook endpoints](/docs/guides/webhooks#delivery-semantics): eight attempts over about 27.5 hours, and a `2xx` within 15 seconds counts as success. Unlike an endpoint, a callback URL is never paused.

Delivery is at least once and not ordered. Deduplicate on the `webhook-id` header, and sort by the `timestamp` inside the payload.

## Find and replay failed callbacks

The **Direct callbacks** tab under **Webhooks** in the dashboard, and [List callback delivery attempts](/docs/api/reference/list-webhook-callback-attempts), show the workspace's callback attempts, newest first, with the URL, the status your receiver returned, and the latency. Attempts are kept for three days.

[Replay failed callback deliveries](/docs/api/reference/create-webhook-callback-replay) queues one more attempt for each event whose callbacks failed in a time window you choose, as [replaying endpoint deliveries](/docs/guides/webhooks#replaying-failed-deliveries) does for endpoints. An event that was also delivered successfully within that window is skipped. A success outside the window does not count, so that event is sent again. One replay sends at most the oldest 10,000 failed events in the window; after a longer outage, replay again with a later start time for the rest.

A replay sends to the URL recorded on the failed attempts, not to your webhook endpoints and not to the profile's current URL. It recovers an outage at your receiver. It cannot recover a message sent to a wrong URL, so send that message again. Replays count toward the same daily limit as endpoint replays.

## Next steps

- [SMS events](/docs/guides/sms/events): the payload of each event
- [Webhooks & events](/docs/guides/webhooks): signatures, retries, and replay in full
- [Sending SMS](/docs/guides/sms/sending-sms): the send request

## Related resources

- [Sending your first SMS](/learn/sms/sending-your-first-sms) (video)
- [What does SMS mean?](/explained/sms/what-does-sms-mean) (answer)
- [SMS](/sms-api) (product)
- [Build your first integration](/learn/paths/integration) (course)

[Get an implementation brief](/learn/workspace?topic=sms)
