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 for the full lifecycle surface, Connect a SIP trunk (BYOC) for the RTP, codec, and inbound-auth drills, and Register 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:
udportcpon port 5060, ortlson 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.
/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 astransport:
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.
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 readstatus: "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/verifywalks the auth gates yourauthModerequires and stampsonboarded_aton the first clean pass. A blocked run returns the exact failing gates inissues, 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-calldials 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 (aqualifycadence) and confirm the Contact stays fresh inGET /api/v1/sip-credentials/{id}— the one TRACE-style response that proves the digest, transport, and binding are all holding.
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
failoverTrunkIdon 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, andhourlyCallCapon 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
failoverTrunkIdmechanism works for the inbound direction: the chain can hand a call to your own next hop rather than fail outright. - Routing rules.
countryBlocklist,destinationPrefixBlocklist, andcallerIdRewritesper 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 aregister_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 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.
Production checklist
- Carrier/PBX SIP details captured and a DID owned on the account
- Probe run before save, with
probe_statuscarried onto the row - Transport chosen (
tlspreferred) and captured in the row - Incoming
authModechosen; allowlist entries are specific IPs, never0.0.0.0/0 - Digest password captured at create (or rotated + revealed once)
- DID inbound route set (
PUT /numbers/+E164/routing) -
routing-analysissimulated on a few representative destinations -
testreturnsregistered(outgoing) /ok(incoming); onboardingverifyreturnsverifiedand stampsonboarded_at -
test-callcompleted media end to end - Softphone credentials show
sip_srtp: "Mandatory"in every UA -
failoverTrunkIdset on trunks carrying traffic - Health polled into your monitoring, alerting on
counts.down > 0orcounts.degraded > 0