> ## Documentation Index
> Fetch the complete documentation index at: https://docs.daily.co/llms.txt
> Use this file to discover all available pages before exploring further.

# sip_trunk.incoming notification

> The signed webhook Daily POSTs to your notification.webhook_url when a SIP-trunk call arrives.

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.

<Warning>
  **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.
</Warning>

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](/docs/guides/features/dial-in-dial-out/sip-trunk#connect-your-bot).

## Headers

<ParamField header="X-Siptrunk-Timestamp" type="integer">
  Unix epoch milliseconds when the notification was signed.
</ParamField>

<ParamField header="X-Siptrunk-Signature" type="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](/docs/guides/features/dial-in-dial-out/sip-trunk#verify-the-signature).
</ParamField>

## 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

<ParamField body="event" type="string">
  Always `sip_trunk.incoming`.
</ParamField>

<ParamField body="trunk" type="object">
  `{ id, description, trunk_name }`. `description` is your free-text field — a handy place to key which bot or
  prompt to launch.
</ParamField>

<ParamField body="room" type="object">
  `{ name, url, exp }` — the provisioned dial-in room. Join `url`; `exp` is the room's unix-seconds expiry.
</ParamField>

<ParamField body="call" type="object">
  The call and caller context (fields below).
</ParamField>

<ParamField body="domain" type="string">
  Your Daily domain name.
</ParamField>

<ParamField body="timestamp" type="integer">
  Unix epoch milliseconds when the notification was sent.
</ParamField>

### call

<ParamField body="sipCallIdHeader" type="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`).
</ParamField>

<ParamField body="From" type="string">
  The caller's SIP/phone address (the `From` header).
</ParamField>

<ParamField body="To" type="string">
  The dialed address (the `To` header).
</ParamField>

<ParamField body="requestUri" type="string">
  The Request-URI the call arrived on.
</ParamField>

<ParamField body="sourceIp" type="string">
  The source IP of the INVITE (already admission-checked by Daily).
</ParamField>

<ParamField body="fromDomain" type="string">
  The domain part of the caller's `From` address.
</ParamField>

<ParamField body="fromDisplayName" type="string">
  The caller's display name, if present. Delivered unquoted, so it can be shown to a user as-is.
</ParamField>

<ParamField body="diversion" type="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`).
</ParamField>

<ParamField body="pAssertedIdentity" type="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`.
</ParamField>

<ParamField body="sipHeaders" type="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.
</ParamField>

<ParamField body="duplicate" type="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](/docs/guides/features/dial-in-dial-out/sip-trunk#duplicate-notifications-carrier-failover).
</ParamField>

<ParamField body="duplicateOfRoom" type="string">
  On a `duplicate` notification: the room name from the first notification for this `sipCallIdHeader`, when
  known.
</ParamField>
