Connect a SIP trunk
A SIP trunk connects your own phone system to Orbit. This guide walks the full lifecycle: decide the direction, create the trunk, validate and test it, wire it to a DID, then keep an eye on registration and route quality. Everything here also exists in the dashboard under Settings → SIP trunks.Step 1 — Pick the direction
Orbit supports two trunk directions:
Incoming trunks authenticate your PBX on every call, by source IP, digest
credentials, or both. Outgoing trunks carry your carrier’s SIP host and the
register credentials the carrier issued you.
Prerequisites
- For outgoing trunks: the SIP host, port, and (if your carrier registered your account) username/password from your carrier. For US outbound traffic, Orbit applies STIR/SHAKEN attestation to the caller ID — see STIR/SHAKEN.
- For incoming trunks: your PBX must be able to send SIP INVITEs to a public domain over UDP 5060, TCP 5060, or TLS 5061, and either present a stable source IP or answer digest challenges.
- To place calls, own at least one DID — buy one under Numbers → Buy numbers or via the Numbers API.
Step 2 — Create an outgoing trunk
Outgoing trunks are the ones you use for dialing out through your own carrier.Validate before you save
Probe first — the probe runs a real SIP REGISTER against the carrier host and returns the exact step that failed (DNS resolution, TCP/TLS connect, or the REGISTER itself) so you fix it before persisting anything:reachable: false response names the phase that failed and gives a
sanitized operator message — the dashboard’s Add trunk dialog runs this
same probe before it lets you save.
Create the trunk
codecs,
maxConcurrent (per-trunk concurrent-call cap, 1–2000),
failoverTrunkId (another trunk Orbit falls over to when this one drops
registration), and enabled (default true). If you ran the probe first,
pass its verdict as probe_status (registered | unregistered |
unknown) so the row starts with an accurate badge instead of a guessed
one — the dashboard does this automatically.
Step 3 — Create an incoming trunk and capture the secret
Incoming trunks let your PBX originate calls into Orbit.authMode is one of:
digest— the PBX answers digest challenges with the issued username/password (handles SBCs with unstable egress IPs).ip_allowlist— only INVITEs fromallowedIpsare accepted (no password exists; nothing can be rotated).both— the PBX must match the allowlist and answer digest.
- Each entry must be a valid IPv4/IPv6 address or CIDR, and at most 50 entries per trunk.
0.0.0.0/0and::/0are rejected — a catch-all would disable the allowlist. UseauthMode: "digest"if you don’t want IP filtering.authMode: "ip_allowlist"or"both"requires at least one entry.
Capture the digest secret — it is shown exactly once
The create response contains the plaintextdigestPassword once, with
credentials_revealed_once: true. Every read of the trunk afterwards shows
the password masked as ***. Save the username and password into your PBX
immediately, or rotate later:
410 GONE, and rotation only applies to incoming trunks with
digest auth. Calls already established under the old password keep
running; the new password applies from the next INVITE — update your PBX
before then to avoid a 401 burst.
Verify go-live readiness before routing traffic
Before you point calls at the trunk, run the readiness check. It is a configuration verdict, not a network probe — incoming trunks never send a REGISTER (your PBX initiates INVITEs toward Orbit, so “registered” does not apply the way it does to outgoing trunks). The check confirms every auth gate yourauthMode requires is satisfiable:
acl_configured— everyallowedIpsentry parses as a valid IP or CIDR (required when the auth mode isip_allowlistorboth).credentials_present— digest username and password exist (required when the auth mode isdigestorboth).auth_path_ready— all required gates pass together.
onboarded_at — the
go-live marker the dashboard onboarding wizard closes on. A blocked run
returns HTTP 200 with status: "not_ready" and the exact gates still
failing in issues, and that blocker is written onto the trunk so the
dashboard health surfaces show it until you re-verify.
Two rules keep the stamp honest:
- Every later trunk edit clears
onboarded_at— changed the allowlist or credentials after onboarding? Re-verify; the stamp can never describe a stale configuration. - Reset restarts the wizard —
POST /api/v1/voice/sip-trunks/{id}/onboarding/resetclears the stamp and checklist without touching your auth config, for when you want to re-run the flow after changing the ACL outside the wizard.
422. Readiness past go-live (auth failures, rejected INVITEs)
is covered by the registration polling and health snapshot in step 8 —
verify only gates whether the configuration is complete enough to carry
traffic.
Step 4 — TLS and SRTP posture
Your carrier-facing config and your softphone-facing config have two different security baselines:
Prefer
tls on trunks when your carrier offers it — the create handler’s
pre-save probe runs against the transport you pick, so a TLS path that
cannot complete the handshake fails the probe and never becomes a broken
saved row. On the credential side, every issued credential reports
sip_srtp: "Mandatory" in its connection details; a softphone that
advertises RTP-only media will not establish audio.
Step 5 — Point your PBX at Orbit
Configure your PBX with the digestdigestUsername and password from the
step above and the SIP edge host shown in Settings → SIP trunks in
the dashboard. For authMode: "ip_allowlist" or "both", list every IP
your SBC can originate from — a burst of unauthorized attempts shows up in
the auth-failures ledger (step 8).
Step 5 — Wire routing to a DID
Calls arriving on an incoming trunk enter Orbit with a dialed number; the DID’s inbound route decides where the call lands. Set it per number:webhook (your HTTPS URL returns call
instructions), agent (an AI voice agent), sip_forward (send the call to
a SIP URI or back through a trunk), queue, ivr, voicemail,
softphone_user, or decline. See the
inbound routing reference for the full set and
config shapes.
Two of those route types deserve a second look in a trunk setup:
sip_forwardto an external AI voice agent — point the DID at a third-party AI provider’s SIP endpoint (callers.ai, Retell, Vapi, or a self-hosted PBX). Orbit terminates the carrier media and re-INVITEs your provider over TLS, so an inbound-trunk call can land in an AI agent without you running any SIP infrastructure. Full walkthrough: AI voice agents via sip_forward.- Inbound to registered softphones — when the trunk carries calls you
want to ring desk phones or softphones directly, bind a SIP credential
per device (
/api/v1/sip-credentials): each issued credential registers against the SIP edge (sip_serverin the response) and is routed on inbound DIDs of typesoftphone_register. Options on the credential include a per-device source-IP list (allowedIpCidrs), the outbound caller-id mode, a daily spend cap, and busy/no-answer/offline forward targets. That is the full cycle for a device: claim the credential, restrict its source IPs, and feed it inbound calls via routing.
routingRules on the trunk itself apply
before the DID route — a countryBlocklist or destinationPrefixBlocklist
blocks matching destinations, and callerIdRewrites swap the presented
caller ID per destination country or prefix. Simulate the fixed evaluation
order against any destination before going live:
executionOrder lists the routing controls in the order dispatch applies
them and which ones you have configured; simulation (when you pass a
destination) shows which control wins and which caller-ID rewrite
applies. The simulation is read-only — it mirrors dispatch exactly.
Step 6 — Test the trunk
Validate configuration or re-check registration —POST /:id/test. On
outgoing trunks this runs the same REGISTER probe as step 2 and records
the verdict on the trunk (it also stamps lastRegistrationCheckAt, which
the dispatch gate consults). On incoming trunks it validates the saved
config (allowlist entries, digest presence) and returns
status: "ok" | "misconfigured" with an issues list:
registered— the last check completed a REGISTER successfully.unregistered— the check ran and the registrar rejected or timed out; readlastRegistrationErroron the trunk for the recorded reason.unknown— no check has run yet, or the verdict was acknowledged as uncertain at creation.
POST /:id/test-call dials a
destination over the trunk and records the billable result. The destination
must be an E.164 number the org owns or an Orbit platform test number;
emergency-shaped dials are rejected with EMERGENCY_CALLING_NOT_SUPPORTED:
Fax over the trunk (T.38)
Inbound fax shares the same inbound edge as voice: a fax sent to a DID arrives as afax.received event and follows the DID’s fax routing to a
webhook, email recipients, or the inbox. If you carry fax on the same
trunk, keep the codec list on the trunk compatible — T.38 negotiation is
driven by the carrier side, and a mismatched codec set can force
unreliable audio fax. See
Inbound fax routing for the receive-to-delivery
lifecycle and the per-DID routing contract.
Step 7 — Emergency dialing
Orbit voice does not support emergency dialing (911/112/999/…), and every voice surface rejects emergency short codes. For US numbers, still register a dispatchable E911 address per DID — it records the location a regulator expects to find even though Orbit’s posture is block-and-disclaim:Step 8 — Monitor
Health snapshot
Ranked attention-first, with severity counts — the same numbers the SIP trunks page badges:Route quality
ASR/SDR-style scoring plus capacity bands per outgoing trunk over a24h or 7d window — read this before blaming a carrier: a saturated
concurrent-call cap shows up as blocked, not failed:
Usage per trunk
Seven days of daily totals by default, or a 24-hour attempt/accepted/ rejected breakdown with the top rejection reason on incoming trunks:What the platform does on its own
Every enabled outgoing trunk is re-registered on a cadence of roughly five minutes: a real REGISTER with the stored credentials, with the verdict persisted back onto the trunk. If a check streak of transient register failures accumulates, the trunk is flagged rather than silently retried forever. Meanwhile a dispatch gate runs on every outbound call: a trunk whose cached registration verdict is anything other thanregistered (or whose verdict is stale) stops being offered new calls,
and the call walks the trunk’s failover chain — the backup trunk you set
via failoverTrunkId — landing on the Devotel default outbound path if
no healthy failover exists. Your calls keep completing; the trunk row
keeps the recorded error until you fix it.
Production checklist
- Outgoing trunk probed before save; verdict carried onto the row via
probe_status - Incoming trunk
authModechosen; allowlist entries are specific IPs/CIDRs, never0.0.0.0/0 - Digest password captured at create (or rotated + revealed once) and stored in the PBX — the plaintext is never shown again
- PBX pointed at the SIP edge host from Settings → SIP trunks with the issued digest credentials
- DID inbound route set (
PUT /numbers/+E164/routing) to a webhook, agent, queue, or SIP target -
routing-analysissimulation run on a few representative destinations before sending traffic -
testreturnsregistered(outgoing) /ok(incoming);test-callcompleted media end to end - Trunk
transportmatches your carrier’s TLS offer, and softphone credentials showsip_srtp: "Mandatory"in every UA - Any fax DIDs confirmed codec-compatible on the trunk; inbound fax routing validated with a test fax
- E911 dispatchable address registered per US DID
-
failoverTrunkIdset on trunks that carry traffic, so the dispatch gate hands calls to a backup when registration drops - Health + route-quality surfaced in your monitoring (poll
.../healthand.../route-quality?window=24h)