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

# SIP trunk first call: the agreement-to-confirmation walkthrough

> One thread through the SIP trunk lifecycle — validate before save, configure TLS/ws transport and auth, wire inbound routes, verify with the probing and registration surface, then scale with failover and troubleshoot the four recorded causes.

# SIP trunk first call: agreement to confirmation

Three guides cover SIP trunks on Orbit and each assumes the others. This
walkthrough is the thread that joins them — it takes a first-time operator
from the signed carrier/PBX agreement all the way to a completed,
media-confirmed test call, and it names the exact gate at each step so you
know the trunk is verified rather than assumed.

Read this first, then keep the depth guides as reference:
[Connect a SIP trunk end to end](/guides/sip-trunk-setup) for the full
lifecycle surface,
[Connect a SIP trunk (BYOC)](/guides/sip-trunk-connection) for the RTP,
codec, and inbound-auth drills, and
[Register your PBX on Orbit](/guides/registering-your-pbx-on-orbit) for the
outgoing-register deep dive and vendor config templates.

## Step 1 — Validate: the pre-save checklist

Before a trunk row exists, run the agency checklist across the three
surfaces that touch:

* **Carrier/PBX agreement.** You hold the SIP host, port, transport, and
  (for a registering endpoint) the digest username/password the carrier or
  PBX issued. For an incoming trunk, you hold the exact static egress IPs
  you'll originate from.
* **Ownership.** Own at least one DID — buy it under **Numbers → Buy
  numbers** or via the Numbers API. A trunk without a DID can register but
  never carries inbound traffic.
* **Transport match (the SSL/ws decision).** Decide now which transport the
  row saves: `udp` or `tcp` on port 5060, or `tls` on 5061. If your
  softphone/device side will use an Orbit SIP credential rather than a
  trunk, the returned connection details name the recommended transport
  (`TLS`, with SRTP mandatory) — browser- and app-based UAs can still carry
  the media on a secure WebSocket (`wss`) while the digest exchange runs
  over TLS. Pick before you save: the pre-save probe runs against the
  transport you chose, so a mismatch is caught in the dialog, not on the
  first live call.

Run the probe against your endpoint with the payload you intend to save —
POST `/api/v1/voice/sip-trunks/probe` returns the phase that failed (DNS
resolution, TCP/TLS connect, or the REGISTER itself) instead of persisting
a broken row. The dashboard's **Add trunk** dialog runs the same probe
before it lets you save.

## Step 2 — Configure the SSL/ws endpoint

For an outgoing (Orbit → your PBX/carrier) or an incoming (your PBX →
Orbit) trunk, the transport you choose lands on the row as `transport`:
`udp`, `tcp`, or `tls`. Prefer `tls` end to end when your carrier offers it
— it encrypts the signalling path and sidesteps the NAT classes of failure
that plague UDP. Ports follow convention: 5060 for UDP/TCP, 5061 for TLS.
When the SSL surface is a softphone-facing SIP credential instead of a
trunk, the returned `sip_transport_recommended` and `sip_srtp` fields name
the exact posture — TLS on port 5061 with SRTP mandatory, and secure
WebSocket available to browser UAs. Whichever way you go, the row on Orbit
carries the posture that was probed at creation.

## Step 3 — Authorize the SIP username/IP

An outgoing trunk carries the digest pair your PBX issues; an incoming
trunk chooses one of three auth modes:

* `digest` — the PBX answers challenges with the issued username and
  password (handles SBCs with unstable egress IPs).
* `ip_allowlist` — only INVITEs from the listed IPs or CIDRs are accepted.
* `both` — the call must match the allowlist and answer digest.

Authorize with specificity: up to 50 IPs or tight CIDRs per trunk, and
never `0.0.0.0/0` — the catch-all would disable the allowlist, so the API
rejects it. If you chose a digest mode, capture the issued
`digestUsername`/`digestPassword` at create time — the password shows
`credentials_revealed_once: true`, and every later read masks it as `***`.
Rotate later with the rotate-then-single-use-reveal pair if you missed the
capture window.

## Step 4 — Register and assign PBX routes

**Register (outgoing trunks).** The row must read `status: "registered"` on
a real REGISTER exchange — Orbit re-polls every enabled outgoing trunk on a
roughly five-minute cadence, so the row catches up to the fix you made at
the far end. Use the `POST /api/v1/voice/sip-trunks/{id}/test` probe to
re-check on demand; it stamps `lastRegistrationCheckAt` and the dashboard's
dispatch gate consults that verdict on every outbound attempt.

**Assign routes (all trunks).** A trunk without an inbound route answers
nothing: set the DID's inbound route per number with
`PUT /api/v1/numbers/+E164/routing` to one of `webhook`, `agent`,
`sip_forward`, `queue`, `ivr`, `voicemail`, `softphone_user`, or `decline`.
The route lives on the DID, not the trunk — the trunk governs auth and
guardrails; the route chooses the destination. Option the trunk-side
`routingRules` (country/destination-prefix blocklists, caller-ID rewrites,
time windows) or the trunk-side auth path, and simulate the fixed
evaluation order with the `routing-analysis` endpoint before sending
traffic. If this is a softphone-bound route instead, bind a SIP credential
per device (`/api/v1/sip-credentials`) and point the DID's `softphone_user`
route at the credential's username.

## Step 5 — Verify: TRACE/OPTIONS responses

Verify the path before live traffic with two surfaces:

* **The configuration verdict.** POST
  `/api/v1/voice/sip-trunks/{id}/onboarding/verify` walks the auth gates
  your `authMode` requires and stamps `onboarded_at` on the first clean
  pass. A blocked run returns the exact failing gates in `issues`, and
  every later trunk edit clears the stamp, so re-verify after you change
  the allowlist or credentials.
* **The live media confirmation.** POST
  `/api/v1/voice/sip-trunks/{id}/test-call` dials your destination over the
  trunk and records the billable result. For a SIP credential device, the
  verification is the UA's reachability itself: issue an OPTIONS ping from
  your SBC (a `qualify` cadence) and confirm the Contact stays fresh in
  `GET /api/v1/sip-credentials/{id}` — the one TRACE-style response that
  proves the digest, transport, and binding are all holding.

After go-live, watch `GET /api/v1/voice/sip-trunks/health` — it ranks
attention-first (`down` before `degraded` before `active` before `unknown`)
and the dashboard Health tab groups on the same severities.

## Step 6 — Advanced: failover, rate limits, inbound failover

* **Failover chains.** Set `failoverTrunkId` on any trunk carrying traffic.
  When a dispatch finds the primary unregistered, Orbit walks the chain
  first and lands on the platform's default outbound path if no healthy
  failover exists — chains are cycle-checked, and the fallback keeps the
  call down instead of dropping it.
* **Per-trunk limits.** `maxConcurrent` (1–2000 per trunk),
  `dailySpendCapCents`, and `hourlyCallCap` on the incoming side are the
  guardrails the billing-hold verdict cites when a call is rejected on
  capacity.
* **Inbound failover.** An incoming trunk's first allowed IP is your own
  already-validated SBC address, so the same `failoverTrunkId` mechanism
  works for the inbound direction: the chain can hand a call to your own
  next hop rather than fail outright.
* **Routing rules.** `countryBlocklist`, `destinationPrefixBlocklist`, and
  `callerIdRewrites` per trunk apply before the DID route; the simulation
  endpoint shows which control wins and which rewrite applies for any
  destination.

## Step 7 — Troubleshooting: 401, 423, connection refused

Work through these in order — they account for nearly every recorded
failure.

**401 Unauthorized.** The first 401 on REGISTER is the challenge step, not
an error. A wall of 401s means the username/password on the trunk does not
match what the PBX accepts, or the realm the PBX hashes against is wrong —
and the domain must be a single unsplit token (the SIP server where packets
go, versus the account's digest identity domain, are different values and
the PBX must hash against the correct one). Capture the altered password at
create or rotate, and update both sides — a PBX-side rotation without a
matching trunk update bursts 401s on the next call.

**423 / rapidly-flapping registration.** A softphone that re-registers on a
pathologically short interval (roughly 8 seconds) can be granted a binding
that lapses between refreshes — jambonz computes the granted expiry from
the request with no minimum, so the AOR flaps offline on ordinary jitter.
The register-auth webhook floors a too-brief requested expiry up to a
healthy minimum and emits a `register_expires_too_brief` counter; on your
side, set a sane interval (30 seconds or longer) and confirm the Contact
stays fresh on the credential's `GET` response rather than watching 423
responses.

**Connection refused / timeouts.** Almost always one of three things: DNS
failed to resolve the host (the probe names the phase), the firewall
dropped the SIP port (allow UDP/TCP/TLS 5060/5061 from Orbit's edge), or
the port forward points to the wrong internal address. Symptom check: if
the PBX logs show no arrival, the failure is reachability; if logs show
arrival but no `200 OK`, the failure is auth or transport.

**Still stuck.** Walk
[SIP trunk troubleshooting](/troubleshooting/sip-trunk) for the
symptom-first checklist, and for incoming-side rejections read
`lastCallVerdictReason` on the trunk — auth failure, caller-id mismatch,
routing-rule violation, or billing hold — and follow the incoming
diagnostics in [Connect a SIP trunk end to end](/guides/sip-trunk-setup).

## Production checklist

* [ ] Carrier/PBX SIP details captured and a DID owned on the account
* [ ] Probe run before save, with `probe_status` carried onto the row
* [ ] Transport chosen (`tls` preferred) and captured in the row
* [ ] Incoming `authMode` chosen; allowlist entries are specific IPs, never `0.0.0.0/0`
* [ ] Digest password captured at create (or rotated + revealed once)
* [ ] DID inbound route set (`PUT /numbers/+E164/routing`)
* [ ] `routing-analysis` simulated on a few representative destinations
* [ ] `test` returns `registered` (outgoing) / `ok` (incoming); onboarding
  `verify` returns `verified` and stamps `onboarded_at`
* [ ] `test-call` completed media end to end
* [ ] Softphone credentials show `sip_srtp: "Mandatory"` in every UA
* [ ] `failoverTrunkId` set on trunks carrying traffic
* [ ] Health polled into your monitoring, alerting on `counts.down > 0` or
  `counts.degraded > 0`

## See also

* [Connect a SIP trunk end to end](/guides/sip-trunk-setup)
* [Connect a SIP trunk (BYOC)](/guides/sip-trunk-connection)
* [Register your PBX on Orbit](/guides/registering-your-pbx-on-orbit)
* [SIP trunk troubleshooting](/troubleshooting/sip-trunk)
* [Number health](/numbers/health)
