Skip to main content

Connect a SIP trunk (BYOC)

Orbit supports BYOC — bring your own carrier, PBX, or session border controller — through SIP trunks. A trunk connects your own voice equipment or carrier relationship to Orbit while Orbit handles flows, IVRs, queues, dialers, agents, and billing. You keep the provider you already have (or the PBX you already run) and route its calls through Orbit’s platform. Trunks come in two directions, and you pick one when you create the trunk:
  • Outgoing — Orbit dials out to your PBX/SBC over SIP to deliver calls into your equipment (for example, an inbound flow that ends in a SIP transfer to your PBX).
  • Incoming — Your PBX or carrier originates calls into Orbit over SIP, where they enter flows, IVRs, queues, or an AI voice agent. This is the classic BYOC pattern: your carrier terminates to you, Orbit gives it the platform.

Prerequisites

Before you create a trunk you need:
  1. SIP credentials or endpoint details from your carrier/PBX. For an outgoing trunk: the hostname (or IP), port, transport (UDP/TCP/TLS), and optional digest username and password Orbit should use when registering or sending an INVITE to your side. For an incoming trunk: whether your carrier authenticates by source IP, by SIP digest username/password, or by both.
  2. Static source IPs you control. For an incoming trunk with IP allowlisting, you must know the exact static egress IPs of your SBC/carrier — a NAT whose egress rotates will drop calls the moment the address changes. The same IP-hygiene discipline in API key IP allowlisting applies here: allowlist exact IPs or tight CIDRs, never 0.0.0.0/0.
  3. An admin-role API key if you prefer the API over the dashboard (writes to trunk endpoints are admin-guarded; reads work with a standard read key).
  4. A routing target for inbound calls — a flow, IVR, or queue to send incoming-trunk calls into. Ringing a trunk without a target just ends calls.

Create a trunk in the dashboard

Open Voice → SIP trunks in the dashboard. The page has tabs for Outgoing, Incoming, Routing, and Usage. For an outgoing trunk, click Add trunk, enter the host, port, transport, and (optionally) your digest credentials, then run the pre-save probe the dialog offers — the probe issues a real REGISTER against your endpoint and carries the verdict onto the new trunk row, so you know at creation time whether Orbit can reach your PBX, not after traffic is already failing. For an incoming trunk, choose the Incoming tab and pick an auth mode — ip_allowlist, digest, or both (digest required ON TOP of the IP check). IP-only is convenient but weaker; for a shared SBC that other customers of your carrier also share, prefer both.

Create a trunk via the API

Both directions are also creatable programmatically. All endpoints sit under /api/v1/voice/sip-trunks. Outgoing (Orbit → your PBX):
Incoming (your PBX/carrier → Orbit):
Both responses return the full trunk object with its generated id (sip_...). For a digest-authenticated incoming trunk, the response also carries the digest credentials — save the password immediately: it is only shown at creation and is not recoverable afterwards (rotation issues a new one via POST /api/v1/voice/sip-trunks/{id}/credentials/rotate, followed by a single-use reveal on GET /api/v1/voice/sip-trunks/{id}/credentials/reveal). Update either direction with PUT /api/v1/voice/sip-trunks/{id} — partial bodies only change the fields you send.
Outbound (MT) calls to the PSTN always egress through Orbit / Devotel’s wholesale softswitch — never through your trunk to a third-party provider. Trunks connect your equipment to Orbit; they never take over the platform’s outbound PSTN exit.

Inbound routing: attach the trunk to a flow

An incoming trunk on its own answers nothing — its traffic enters Orbit but has no target until something routes it. The routing target is your normal inbound surface:
  • Flows / IVRs — point the inbound route for a caller-id or DID on this trunk into a flow. The Build an IVR flow guide covers the IVR itself; the trunk simply feeds calls into it.
  • Queues — route calls into a voice queue so they land on agents (Voice queues).
  • AI voice agent — route the call into your agent with the sip_forward action (AI voice agents via sip_forward).
The association lives on the inbound route (DID/caller-id → flow, queue, or agent), not on the trunk itself — the trunk controls authentication and guardrails; the route controls the destination.

Outbound routing: caller IDs and the dialer

Publish the numbers you want to show as caller IDs, then use them from surfaces that place outbound voice:
  • Dialer campaigns reference the caller ID you assign to the campaign — see Launch an outbound dialer campaign.
  • Manual/agent calls use the caller ID configured on the Voice side of the dashboard.
Remember the trunk’s role here: the dialer’s PSTN exit stays on the platform (per the note above). An outgoing trunk is about delivering calls into YOUR equipment — a flow that SIP-transfers to your PBX, for example — not about moving the dialer off Orbit’s wholesale path.

Health checks and failover

Orbit keeps a live picture of every trunk.
  • Registration status (outgoing). A reconciler re-polls REGISTER continuously and the dashboard’s Test trunk button runs the same probe on demand (POST /api/v1/voice/sip-trunks/{id}/test). The trunk row and the API expose status, lastRegisteredAt, lastRegistrationLatencyMs, and lastRegistrationError so a failing registration surfaces with a reason, not a bare “unregistered”.
  • Accepted-call freshness (incoming). An incoming trunk has no REGISTER (your side initiates), so it tracks accepted traffic: accepted within 5 minutes → “registered”, within an hour → “trying”, otherwise “idle”. Rejections surface on lastCallVerdict with the exact reason (auth failure, caller-id mismatch, routing rule violation, billing hold).
  • Fleet snapshot. GET /api/v1/voice/sip-trunks/health returns an attention-ranked snapshot across all trunks — use it for an ops dashboard or an alerting hook. GET /api/v1/voice/sip-trunks/route-quality gives per-trunk ASR/SDR scoring and capacity bands for the outbound side. See also Number health for per-number warming and deliverability; the trunk health snapshot sits next to it in overall fleet observability.
Failover. Set failoverTrunkId on an outgoing trunk to point at another trunk (outgoing OR incoming — an incoming trunk’s first allowed IP is your own already-validated SBC address, so it makes a valid fallback). When a dispatch finds the primary trunk unregistered, Orbit walks the failover chain first, then falls back to the platform default, keeping the call down instead of dropping it. Chains are cycle-checked — an update that would create a loop is rejected with a 422.

Troubleshooting

Registration failures (outgoing trunks)

  1. Read the reason on the trunk row: GET the trunk and look at lastRegistrationError — the probe and reconciler write the sanitized registrar response (“Registrar rejected the credentials…”, DNS resolution failure, timeout) there, no log dive needed.
  2. Verify reachability: your firewall must allow Orbit’s UDP/TCP/TLS connect on the port you set, and your SBC must answer REGISTER with the credentials you configured. A wrong port or a blocked transport looks identical to “not responding” — confirm both ends.
  3. If the dashboard shows “unregistered” right after creation, run Test trunk — the probe also refreshes the row’s status if you’ve already fixed the far end.

RTP / media issues (one-way audio, no audio)

  • Confirm both directions of media are allowed through your SBC: signalling alone succeeding means the call sets up but stays silent. Whitelist Orbit’s media IPs, not just the signalling edge.
  • Check NAT: your PBX must advertise a routable IP in SDP; an internal/private SDP address gives one-way or no audio. Enable your SBC’s NAT-traversal or host the PBX behind a proper SBC.
  • Timeouts on unanswered calls with no media are usually a firewall that accepts SIP but drops RTP — allow the media port range, not only 5060/5061.

Codec mismatches

Symptoms: calls connect and immediately hang up, or one leg hears garbled audio.
  • Set an explicit codecs list on the trunk in preference order — PCMU/PCMA for PSTN-compatible endpoints, OPUS only where both sides genuinely support it.
  • If your PBX negotiates OPUS but the other side (a PSTN carrier) doesn’t, the call fails — narrow the list to codecs both endpoints offer.

Incoming trunk rejections

Read lastCallVerdictReason on the trunk:
  • Auth failure — the source IP isn’t in allowedIps (or the digest failed when authMode: both or digest). Confirm your actual egress IP — a NAT or carrier that rotates will reject intermittently.
  • Caller-id mismatch — you set allowedCallerIds and the caller ID on the INVITE wasn’t in it; widen the list or remove it.
  • Routing rule violation — a per-trunk routingRules blocklist or time-window rejected the call; inspect the rules you set.
  • Billing hold — the per-trunk cap (maxConcurrent, dailySpendCapCents, hourlyCallCap) tripped; raise the limit or wait for the period to roll.

Endpoints

See also

  • Connect via SMPP — the SMS analog of a BYO carrier: bind your own SMPP client to Orbit the same way a SIP trunk brings your own voice carrier to Orbit
  • AI voice agents via sip_forward — SIP-forwarding inbound calls into a third-party AI agent
  • Build an IVR flow — the inbound routing target you’re attaching the trunk to
  • Voice queues — queue-based inbound routing
  • Number health — per-number deliverability and warming, alongside trunk-level health
  • Voice — API reference for the voice surface