/api/v1/voice
Authentication: Clerk session (Authorization: Bearer <token>) or API
key (X-API-Key).
Scope: every create and participant-control mutation requires a role
with voice write access (owner, admin, or developer). Listing rooms and
reading rosters requires voice read.
The dashboard’s Voice → Conferences page is built on the API below, so
everything on this page maps to a console control or its underlying
POST/GET — pick whichever surface fits your operating model.
1. Conference vs queued transfer vs shared line
Choose the right group-calling pattern before you build:
A conference is a meeting: set the room, hand around the join PIN, and the
roster is two-way participants. A queue is inbound dispatch — callers line
up, an agent takes one at a time. A shared line is a ringing identity —
members race to answer, and grab the call between devices. A page is
fire-and-forget — one announcement, no return path.
For ad-hoc “conference in a third party” while a 1:1 call is live, the
warm-transfer flow is the right tool; this page covers the named, reusable
bridge room.
The decision stays tenant-owned: whichever pattern you pick, the dial-out
legs the room places and the compliance gates they pass (quiet-hours, DNC,
recording consent) remain under your tenant’s configured controls.
2. Create a conference
Open Voice → Conferences and choose New conference, or callPOST /api/v1/voice/conferences.
cURL
201 Created
pin echoes what you sent (or the auto-generated 6-digit
one when you omit it), and each seeded leg starts as ringing
until the far end answers; the roster read in section 4 shows the
settled statuses. With record: true the room opens in
auto_with_consent — section 5 covers what that adds at join
time and where the recording surfaces.
Conferences sit below the typed SDK surface today, so the SDKs
reach them through the same generic escape hatch the
programmable voice guide uses
for its less-typed calls:
Node SDK — @devotel/orbit (generic request)
Python SDK — orbit-sdk (generic request)
The
201 response returns the room’s conf_<32 hex> id and the active
pin — the dashboard shows the PIN on the room detail so the operator can
distribute it.
Pass an Idempotency-Key on create and a retry within five minutes returns
the original room rather than a duplicate — useful when the create is fired
from an automation that retries on timeouts.
Seeded dial-out is fan-out, not a fuse: if one dialed participant misses
the call, that leg lands on the roster as failed with its SIP response
code and the room still exists. If every seeded leg fails, the room is
closed failed — rotate to the
troubleshooting page for the
per-leg reason.
3. Join from SIP trunk, PBX registration, or softphone
A conference accepts legs over whichever inbound connectivity your tenant already runs — the dial-out legs you seed at create are only the proactive side; participants who call into the room come over one of these:From a SIP trunk
Point the trunk’s inbound route at the conference’s bridge number and carry the join PIN in thepin field on the room (or pass it per
invitation). The trunk leg terminates on Orbit over the Devotel softswitch
and lands in the room; the participant enters the roster once the leg
answers. The
SIP trunk connection guide covers trunk
registration.
From PBX registration
A PBX registered on Orbit sees the bridge like any other extension — the PBX leg dials the room’s bridge number, keys in the PIN (4–8 digits) at the IVR prompt, and joins. See Registering your PBX on Orbit. Registration keeps the leg alive across the join prompt, which matters for the rejoin workflow in section 6.From a softphone or browser client
The Orbit softphone and the browser voice client join the room directly — no SIP trunk needed — using the in-app Join with PIN action on Voice → Conferences. This is the path for teammates on the web or mobile app. See Softphone browser calling. Both inbound paths land on the same participant roster the dialed (seeded) legs use; each row carries its own status (ringing, connected,
no_answer, failed) and join/leave timestamps. Invite more participants
one by one with
POST /api/v1/voice/conferences/:id/participants — pass
target.kind: "pstn" with an E.164 number or target.kind: "sip" with a
sip: URI, plus the room’s pin when the room has one.
4. Manage live participants
The dashboard’s room detail page and these endpoints share the same participant controls; every one of them verifies the conference and participant pair belongs to your tenant before the action lands. Read the live roster.GET /conferences/:id/participants
returns one row per leg, dialed or dialed-in, each with its own
status and join/leave timestamps:
cURL
200 OK
no_answer (a failed leg
carries its sip_response_code too — the per-leg diagnostic the
troubleshooting page
walks through), and the invite below re-dials it.
Add a participant mid-meeting. The same dial-out the seeded
create runs, one leg at a time:
cURL
201 Created
target.kind: "sip" with a sip: URI instead of a number.
- Mute a participant —
POST /conferences/:id/participants/:pid/mute(unmute with/unmute). The muted leg still hears the room but cannot speak — typical for a noisy line. - Hold / unhold a participant —
POST /conferences/:id/participants/:pid/hold(release with/unhold). A held leg loses media until you release it; useful when an invitee needs to step off without leaving. - Listen-only, then on-air again. The two sample mutations a moderator reaches for every meeting:
cURL — mute, then unmute
200 OK
muted flag reflects the state after the call — true on the
/mute response, false again after /unmute (and /hold /
/unhold answer the same way). A normal call to the roster after
your moderation call is what the operator loop should look like.
cURL — drop a participant
200 OK
- Elevate to host — a participant who knows the room’s
host_pinexercises host-only controls (host PIN checks a separate credential from the join PIN, rotating either one does not affect the other). - Rotate the join PIN mid-call —
POST /conferences/:id/regenerate-pin. When a PIN leaks mid-meeting, rotate it without ending the room; existing legs stay, new joiners use the new PIN.
5. Recording and analytics — how a conference differs
Recording and QA treat a conference differently from a 1:1 call because the room is the unit of consent and billing, not the individual leg. Recording modes. The room’srecord_mode setting accepts manual,
auto_with_consent, or disabled; pass record: true at create to start
in auto_with_consent, or patch it with PATCH /conferences/:id. In
auto_with_consent the platform collects each join-time consent decision
per leg — POST /conferences/:id/participants/:pid/consent with { "state": "granted" }
or { "state": "declined" } records the outcome, and the recording
captures the room only while the consent ledger allows. Every consent row
is tenant-scoped, and the room’s behavior stays inside your tenant’s
compliance posture.
Creating with record: true returns the same 201 as
section 2 with one difference: record_mode reads
auto_with_consent instead of disabled, and each participant
row grows a consent field that starts pending until the joiner
grants or declines:
201 Created — record: true, first leg
cURL
200 OK
download_url expires (the expires epoch in the
query), so request it when you are ready to hand the asset to
your QA tooling rather than caching it. Legs whose consent stays
declined are excluded from the captured media while they are
in the room — the consent ledger above is the platform’s record
of why.
Billing. The platform bills the dial-out legs that place the room —
per-leg, per-minute metering over the Devotel softswitch — rather than a
flat “room” charge. A conference of four joined participants bills four
leg-minutes per minute of meeting; inbound legs that dial in are billed
as inbound to their trunk or registration path.
QA scoring. A room’s recording lands in the
Recording library as a single multi-party
asset. QA scoring for conferences runs against the room as a whole — the
post-call score is computed once for the room, not per leg — so a
weekly all-hands earns one scoring row per meeting, while per-agent QA
coverage stays with 1:1 conversations. This matches how the aggregate
rollup isolates conference KPIs (GET /conferences/aggregate) from the
per-call index.
Analytics. GET /conferences/aggregate returns the KPI strip for the
window you ask (total, active now, average duration, success rate, peak
concurrent participants, participants in window), and
GET /conferences/failure-analytics returns failure rates with the top
failure reasons and affected destinations. Both accept window_days up
to 365.
Compliance inside the tenant. Room PIN rotation, recording consent,
per-leg hold/mute/drop, and end reasons are all per-tenant controls —
keeping the room’s behavior inside your tenant’s configured compliance
posture. Platform hard limits that apply regardless: at most 32
participants per room, a four-hour expiry backstop that ends any
lingering room, and PIN values restricted to 4–8 digits.
6. Worked example — weekly all-hands that survives rejoins
The pattern where the conference earns its keep: a named weekly meeting team members rejoin as they drop.- On Monday, create the room once: name
Weekly all-hands,maxParticipants: 24,record: true, an auto-generated join PIN that the operator distributes in the invite. - Team members join from their PBX registration or softphone (section 3), or the organizer dials external guests one by one (section 3, invite endpoint).
- If a participant’s line drops, they dial back and enter the same PIN; the room is still open because the last-leg-close and four-hour backstop only matter when nobody is left. The join-time consent prompt fires again on the rejoined leg, and the room’s recording stays continuous across rejoins.
- A noisy line joins: a host mutes it (section 4). If the PIN goes
around the wrong group, the host rotates it mid-meeting with
regenerate-pinand distributes the new PIN in the chat thread. - After the meeting,
conference.endedfires once — withend_reason: "all_legs_terminated"if the last participant hung up, ormanual_endif the host closed it — the room’s per-leg ledger and the recording are still fully retrievable from the Recording library and the participants endpoint after the room closes.
cURL — the operator loop, Monday 09:00
Final roster — after the meeting
disconnected history, the re-added leg answers under a
fresh id — which is exactly the ledger the room detail page draws.
The end-to-end flow the room runs:
Related
- Conferences (API) — endpoint-by-endpoint reference the dashboard page is built on.
- Program an outbound voice call — the 1:1 surface the conference dial-out legs share, with the per-call recording and transcript flow.
- Troubleshoot conference lifecycle failures — per-leg failure codes when a join fails.
- Voice queues — the right shape when callers should line up for an agent.
- Shared Line Appearance — the right shape when any device on a roster should answer the same line.
- Paging groups — the one-way announcement pattern a conference is not.
- Jambonz verb extensions in Orbit — the wait-music and supervisor-bridge verbs that feed these rooms.