Skip to main content
When an inbound call arrives on a SIP trunk, Daily creates a room and POSTs a sip_trunk.incoming notification to that trunk’s notification.webhook_url. This is a per-trunk notification (not a domain webhook), signed with the trunk’s hmac — and it is on the call path: Daily holds the caller in ringback until your endpoint returns a 2xx, then bridges the caller into the room. Your 2xx is a commitment to join.
Respond 2xx immediately, then start your bot. Daily waits up to about 12 seconds in total for a 2xx (retrying a failed request while that budget allows). If none arrives, the call is rejected with 480 Notification Failed — the caller hears ringback and then a normal failure, never dead air. Don’t do slow work (starting the bot, database lookups) before responding: the config-time sip_trunk.test probe cannot catch a slow handler, but every real call will.
It fires as soon as the room exists — before the SIP worker is up and before the caller is bridged — so your bot is in the room first. Join room.url, then wait for the dial-in caller to appear as a participant before greeting. See the guide.

Headers

integer
Unix epoch milliseconds when the notification was signed.
string
HMAC-SHA256(base64-decoded hmac) over `${X-Siptrunk-Timestamp}.${rawBody}` — verify against the request body exactly as received, and against your trunk’s hmac, to authenticate the request. See verifying the signature.

The sip_trunk.test probe

Your endpoint also receives a one-shot {"event": "sip_trunk.test", "timestamp": <ms>} POST when a trunk is created and on any update that touches notification — signed with the same headers (using the trunk’s final hmac, including a freshly generated or rotated one). Respond 2xx, or the create/update API call fails with 400. Use it to confirm reachability and validate your signature check.

Your response

Return any 2xx as fast as you can — the body is ignored.
  • 2xx — accepted: the caller is bridged into room.url. Return 2xx for every notification you have already handled as well: a redelivered retry, or a call.duplicate: true branch. Dedupe internally on (call.sipCallIdHeader, room.name), never with a 4xx — a 4xx rejects that branch, and with some carriers the later branch is the one carrying the caller.
  • Anything else — non-2xx, timeout, connection failure, redirect — is a failure. Failures are retried while the budget allows; a request that times out has used the budget and is not retried. When the budget (about 12 s from the INVITE) is exhausted, the call is rejected with 480 Notification Failed.
Because retries can deliver the same notification more than once, treat (call.sipCallIdHeader, room.name) as the idempotency key.

Payload

string
Always sip_trunk.incoming.
object
{ id, description, trunk_name }. description is your free-text field — a handy place to key which bot or prompt to launch.
object
{ name, url, exp } — the provisioned dial-in room. Join url; exp is the room’s unix-seconds expiry.
object
The call and caller context (fields below).
string
Your Daily domain name.
integer
Unix epoch milliseconds when the notification was sent.

call

string
The SIP Call-ID header from the carrier’s INVITE, verbatim — use it to correlate with your carrier’s own logs. Its format varies by carrier, so treat it as an opaque string. It is not the carrier’s own call-record id (Twilio sends that separately as X-Twilio-CallSid in sipHeaders).
string
The caller’s SIP/phone address (the From header).
string
The dialed address (the To header).
string
The Request-URI the call arrived on.
string
The source IP of the INVITE (already admission-checked by Daily).
string
The domain part of the caller’s From address.
string
The caller’s display name, if present. Delivered unquoted, so it can be shown to a user as-is.
string
The Diversion header, if the call was forwarded — the raw header value, so it keeps the carrier’s angle brackets and any parameters (for example <sip:+15550001111@twilio.com>;reason=unconditional).
string
The P-Asserted-Identity header (network-asserted caller identity), if present. Also the raw header value, so a quoted display name inside it keeps its quote characters — unlike fromDisplayName.
object
X-* custom headers your carrier attached to the INVITE (up to 32), passed through with their original header names and values — use them to map the call to your business context.
boolean
Present (true) only when this notification duplicates an earlier one for the same sipCallIdHeader — your carrier failed the call over between Daily’s SIP nodes, so a second room was provisioned. Don’t key state by sipCallIdHeader alone and don’t drop flagged notifications: respond 2xx and join this room too — exactly one room receives the caller, and it is often the flagged one (a 4xx here would reject it). See duplicate notifications.
string
On a duplicate notification: the room name from the first notification for this sipCallIdHeader, when known.