Worked SIP hook chains
Jambonz calls these hook URLs over HTTP; every body below is the actual POST payload the Devotel softswitch sends to Orbit, not a payload you send to Orbit. The samples exist so a SIP-trunking operator can see which hook fires at which stage and what shape to expect on the wire when chaining to an external softswitch. Use raw cURL (or any HTTP client) to script these — the Node/Python/Go SDKs do not ship a typed helper for the Jambonz hook surface.Chain 1 — SIP trunk auth: Register auth → Customer trunk auth → Lookup
The softswitch challenges every new REGISTER, then every inbound INVITE, then routes the call.1a. REGISTER auth (softphone or BYO-PBX binding)
POST /api/v1/jambonz/auth/registerok/fail.
cURL
200 { "status": "ok", "a1_hash": "1ad1c4…", "algorithm": "MD5", "expires": 60 } — the expires clause only appears when the PBX asked for an interval below the healthy floor; otherwise it is omitted and the PBX’s own value stands. Wrong digest → 401 { "status": "fail" }.
1b. Customer trunk auth (INVITE/REGISTER digest on the shared carrier)
POST /api/v1/jambonz/customer-trunk-authcustomer-trunks carrier — it sees BOTH the INVITE (incoming call) and REGISTER (binding) digest claims of every BYO-PBX trunk.
cURL
{ "status": "ok", "password": "<trunk-digest-secret>" } so the SBC can verify the PBX’s digest locally; when the PBX asked for an interval below the healthy floor the same expires floor clause rides along. Unknown digest username → 401 { "status": "fail" } and the source IP is probe-blacklisted at the edge. Trunk with no digest password configured → 403 { "status": "fail", "message": "no_password_configured" }. Non-INVITE/non-REGISTER methods (OPTIONS keepalives) short-circuit to a bare { "status": "ok" }.
1c. Lookup — resolve the dialed party to a route
POST /api/v1/jambonz/lookupto is the dialed party (E.164, SIP URI, or carrier-prefixed digit run); originating_sip_trunk_name discriminates inbound-PSTN vs browser-softphone outbound; sip.headers carries any custom SIP headers the originating UA sent (e.g. X-Tenant-ID, X-Devotel-Call-Id set at softphone-initiation).
cURL
dial verb fanned out across the matched inbound-route’s SIP usernames, or the business-hours fallback. Lookup-next (POST /api/v1/jambonz/lookup-next) is the sequential-ring continuation: it advances to the next username in the route’s fan-out after a no-answer timeout, carrying the same body shape plus the attempt state.
Chain 2 — inbound routing: Dial-confirm → Callback-answer → Feature-code → Queue-wait
Once the route resolves, the softswitch drives the call through these verb hooks.2a. Dial-confirm — screen the outbound arm before connecting
POST /api/v1/jambonz/dial-confirmdial arm is about to answer so Orbit can last-moment veto or confirm destination eligibility (TCPA/time-window guard) before the caller hears the arm ring.
cURL
2b. Callback-answer — outbound callback leg answered
POST /api/v1/jambonz/callback-answercURL
Pause-elapsed hook (chain 4) is what fires instead when the caller held in-queue and the pause timer expired without an agent.
2c. Feature-code — star-code interception mid-call
POST /api/v1/jambonz/feature-code*97 mailbox, *72 DND toggle) — the softswitch sends the code plus the in-call username binding it to a SIP credential.
cURL
2d. Queue-wait — caller holding in queue
POST /api/v1/jambonz/queue-waitQueue-action and Queue-overflow fire on the caller’s menu choice and on wait-timeout overflow respectively; Conference-wait-music feeds the hold stream.
cURL
say position in queue, play hold music, gather for the callback opt-in).
Chain 3 — AI voice agent: Bridge-to-lk / Supervisor-bridge → Quality-ingest
The AI-agent pillars bridge the live PSTN call into a LiveKit room; supervisors join the same room in listen/whisper/barge/conference modes; the media layer pushes call quality telemetry into the ingest.3a. Bridge-to-lk — drop the PSTN call into the LiveKit room
POST /api/v1/jambonz/bridge-to-lkcURL
dial verb toward the LiveKit SIP URI (X-LK-Room header) — the AI agent joins the room as a conference participant and converses with the caller in real time.
3b. Supervisor-bridge — join a supervisor to the live call
POST /api/v1/jambonz/supervisor-bridge?callId=…&mode=listen|whisper|barge|conferenceconf_<callId> room. The mode drives the audio path: listen (muted supervisor), whisper (audible to the agent only), barge and conference (audible to all — conference is the consult-and-conference 3-way mode).
cURL
conference verb — { "verb": "conference", "name": "conf_call_01HWKJF…", "muted": false, "coachCallSid": "call_01HWKAG…" } for whisper. Invalid query → hangup.
3c. Quality-ingest — RTCP-XR telemetry for the call legs
POST /api/v1/jambonz/quality-ingestcURL
200 with the stored sample ack; a missing platform HMAC key fail-closes every POST with a 503 (the quality surface then reports collection as not enabled, not as empty).
Chain 4 — Mailbox fallback: Voicemail pin/store, Pause-elapsed fallback
When the route ends in a mailbox rather than an agent, the TUI challenges for the PIN and stores the message; the pause/callback fallback covers callers who held past the timer.4a. Voicemail-pin — mailbox TUI PIN challenge
POST /api/v1/jambonz/voicemail-pincURL
say “enter your PIN again” / “you have N messages”) or the polite-decline verbs after the attempt ceiling. The PIN itself is a tenant-owned mailbox control stored as a salted hash; it never leaves the tenant boundary.
4b. Voicemail-store — persist the recorded message
POST /api/v1/jambonz/voicemail-storecURL
200 ack; the message appears in the mailbox for the matched username/DID.
4c. Pause-elapsed — held caller’s pause timer fired
POST /api/v1/jambonz/pause-elapsedCallback-answer in chain 2 and Mid-queue-voicemail for an in-queue mailbox offer).
cURL
Webhook authentication. Every hook above is HMAC-signed: the softswitch sends
jambonz-signature: t=<unix>,v1=<hex> (quality-ingest uses X-Orbit-Signature with the same scheme) over the raw body. Rejects return a clean 4xx envelope ({ "status": "fail", "message": "…" }), never a 5xx — that keeps the SIP transaction clean at the edge. See the Jambonz softswitch concept page for the account/registration model.
Idempotency. Every hook accepts the standard Idempotency-Key header and replays identically on retry — use the response’s envelope exactly as computed (a 409 means the same key arrived twice mid-flight).