Skip to main content
Orbit’s voice plane is built on Jambonz, the open-source voice engine. Every call Orbit handles is ultimately described as a sequence of Jambonz verbs. What Jambonz documentation will not tell you is how much Orbit layers on top of the stock verb catalog: a feature-code resolver for desk phones, guard verbs that enforce tenant isolation and SIP-credential policy, billing verbs that float money mid-call, and coaching verbs that whisper context into a transfer. This page is the catalog of that extension layer, written for voice developers evaluating or building on Orbit. One framing rule before the catalog: every extension described here runs inside Orbit’s voice engine and depends on Orbit services (tenant schemas, the billing wallet, the Devotel wholesale softswitch for egress). They are not portable to a stock Jambonz install, and this page does not promise a public verb-authoring SDK. It documents what ships today.

The Jambonz baseline

The upstream verb set is the substrate everything below sits on. In one sentence each:
  • dial connects the call to another endpoint: a SIP extension, a queue, or the PSTN through the softswitch.
  • say renders text to speech to the caller.
  • play streams an audio file to the caller.
  • gather collects DTMF digits or speech from the caller.
  • pause holds the call silent for a set interval; Orbit also uses it as the parking primitive while devices ring.
  • record captures the call, including SIPREC hand-off to an external recording server.
  • conference bridges the caller into a named room.
  • redirect sends the call to a new verb sequence, which is how Orbit re-hooks a call when its disposition changes.
  • hangup terminates the call.
If a behaviour you need maps cleanly onto one of those verbs, Orbit passes it through unchanged. The rest of this page is what Orbit adds around them.

The Orbit extension layer

Each row is a verb-shaping concern in the voice engine, named by the behaviour it produces rather than by internal module names. Two adjacent concerns shape verbs without being verbs themselves: the voice DSL executor sanitizes tenant-supplied verb sequences fetched from your webhook answer URLs (Jambonz validates verb shape, not policy, so Orbit enforces the policy side), and the DSL signing layer HMAC-signs those webhook fetches so your verb endpoint can verify requests genuinely come from Orbit.

Billing-integrated verbs

Three extensions exist purely to move money correctly, and each maps to a wallet ledger entry:
  1. Pre-call hold. Every outbound origination (dial-confirm, click-to-call, conference participant adds, warm-transfer consult legs) places a small advance hold with a deterministic idempotency key before the call leaves the platform. An insufficient wallet balance rejects the dial with a 402 rather than placing an unpaid call. Ledger entry: a hold against the org wallet at dispatch.
  2. Final charge. When a call reaches a terminal state with a known duration, the finalizer debits actual usage against the hold and releases the difference. Zero-duration and never-connected legs are refunded by the stuck-call sweep, so the finalizer only ever settles answered calls. Ledger entry: the final per-minute charge reconciling the hold.
  3. Hold pause. A hold-billing mode that pauses accrual while a call is deliberately held, so customers are not charged active rates for their own hold time. Ledger entry: a pause marker that bounds the billable window.
The dialer side has its own gate: per-org dialer billing resolution decides which org wallet a dialer attempt is charged to before the feedback branch records the attempt, so multi-org dialer campaigns bill the right entity per attempt.

Feature-code resolver walk-through

A desk phone has no dashboard. The resolver is what makes the handset a first-class citizen: it turns whatever Jambonz forwards as the dialed destination into a typed feature-code match, and the verb layer renders that match into verbs. The lookup chain, in order:
  1. Normalize. The dialed To: value arrives as bare digits, a full SIP URI, a tel: URI, or with a trailing # send key. Normalization strips scheme, host, URI parameters, and the send key, leaving the bare code. Malformed input never throws; it resolves to “not a feature code.”
  2. Exact table match. *78, *79, *97, and *57 match against the fixed code table first.
  3. Pattern match. Single-digit *1 through *9 match the park-retrieve pattern. Two-digit codes never collide with it because the table runs first.
  4. Deliberate non-match. Call forward (*72 / *73) returns null today by design: unconditional forward is stored per user, and resolving a credential to a user is a link the platform does not yet maintain, and *72 carries a follow-on destination that the say-and-hangup feature-code shape cannot model. Shipping a half-built handler is worse than saying “not a valid feature code.”
  5. Verb render. A match renders into its verb chain: confirmation plus hangup for toggles, mailbox playback for *97, a conference join into the park lobby for retrieval.
A concrete scenario, *3 (retrieve the call parked in slot 3):
  1. The handset dials *3; Jambonz posts the lookup with To: sip:*3@….
  2. Normalization yields *3; the table misses; the park pattern matches with slot 3.
  3. The handler claims the parked call in slot 3 for this org and receives the immutable parked-call id.
  4. The verb layer renders a conference join into the park lobby holding that parked caller, bridging the retrieving handset to the waiting party. The lobby is keyed on the parked-call id, not the slot, so a second park reusing slot 3 can never strand the first caller.
  5. If slot 3 is empty, the caller hears “no call parked on 3” and the call ends cleanly.
The same normalization and table drive *78 and *79, which flip the DND boolean on the credential the desk phone registered as, and *97, which resolves the mailbox through the credential’s registered inbound routes and plays unheard messages back over the keypad call. See the call park lobby concept for the park half of the loop.

Abandoned-safe-harbor: the tenant-owned abandoned-call guard

When a predictive dialer abandons a call (no agent available after the called party answers), the dialer’s operator is on the hook for what the caller hears. The safe-harbor guard plays the identification message the abandoned-call rules require: the business name on whose behalf the call was placed, that the call was placed on behalf of a telemarketer, and the do-not-call contact information, delivered within the required window after greeting. Two things to be precise about:
  • It is a tenant-owned guardrail. The guard runs for tenants who run predictive dialing and configure the abandoned-call message from their own business identity. It is not a platform-wide compliance claim: Orbit provides the mechanism and the timing, and the content and the legal sufficiency of what plays belong to the tenant running the dialer.
  • It cites, it does not generalize. The message shape tracks the abandoned-call safe-harbor provisions for predictive dialers. If your campaign is not predictive dialing, this guard is not your compliance story; your quiet-hours, suppression, and consent obligations live in the federal voice guard documentation.

Building on the extension layer

There is no public verb-authoring SDK, and this page does not promise one. What exists today is a clear internal landing pattern, documented so platform contributors extend the layer in one place:
  • A new verb concern lands as its own module beside the jambonz route surface, owning its handler and verb rendering, with the route registered by the bridge plugin so the public route surface stays stable.
  • Tenant-supplied verb sequences flow through the DSL executor, which is where policy sanitization happens. Anything that accepts verbs from outside the platform must pass through it, not around it.
  • Webhook fetches of tenant verb sequences are signed with the DSL signing layer; a new fetch path must carry the signature or your endpoint has no way to authenticate Orbit.
  • SIP header reads use the shared case-insensitive header helpers, because upstream proxies rewrite header casing arbitrarily. One matching semantic, one place.
  • State that must survive a replica restart (whisper stashes, park claims, concurrency counters) lives in Redis with a TTL and an idempotency key, never in process memory.
  • Software that mutates voice-edge state as a self-heal writes to an attempt ledger first. A heal without a trail is indistinguishable from drift.

Where these verbs fire in the lifecycle

One inbound call, in order:
  1. Ingress. The INVITE arrives at the voice edge; header helpers extract org and tenant identity from SIP headers regardless of casing.
  2. Guard. The inbound route org guard verifies the routed organization still owns the dialed number; a deleted or hijacked route drops here.
  3. Credential policy. For registered extensions, the concurrency cap and DND filters run before anything rings.
  4. Resolution. The lookup decides the route type: a feature code resolves through the resolver; a webhook route fetches tenant verbs through the signed DSL path; an outbound origination consumes its stashed call flow.
  5. Verb chain. The IVR or queue runs, with per-node analytics emitting on every visit, exit, and abandonment, wait music and personalized announces rendering in queues, and mid-call recording startable from the dashboard at any point.
  6. Billing. The hold lands at dispatch, the hold-pause mode bounds held time, and the finalizer settles the charge at terminal state.
  7. Disposition. The disposition lock records exactly one outcome for the call, even when several producers race to answer.
  8. Aftermath. Quality metrics (MOS, jitter, packet loss) ingest per call, and transcripts and analytics land where the dashboards read them.
Outbound calls run the same spine in reverse order of concern: billing hold and org gate first, then egress exclusively through the Devotel wholesale softswitch, then the same disposition and quality tail.