Conference Calls
A conference (conf_…) is a room. Its participants are ordinary calls: each has its own call_id, and everything that works on a call (events, recording, streams, metadata) works on a participant.
You can refer to a participant by its call_id or by a label you choose when adding it, like "specialist" or "supervisor".
Two-person handoffs don’t need a conference: call.transfer still connects two calls directly.
Start a conference
Create one with the people you want, or start from a call you’re already on. Existing calls are added first, so the conference is live before dialed people answer.
The response is the conference with its participants. A dialed participant starts as dialing and becomes joined when they answer.
From a call’s WebSocket, you don’t need to create anything first: conference.add_participant puts this call and the new person into a conference, and the ack tells you its ID.
Over the WebSocket, from defaults to your number on the current call.
Add people
Either dial someone or pull in a call you already have:
Options for both: label, muted, on_hold, whisper_to, end_conference_on_leave. Dialing also takes announce, ring_timeout_secs, record, and metadata.
announce plays to the new person when they answer, before they join; nobody else hears it:
Use { "url": "https://…/intro.mp3" } to play a file instead of text-to-speech.
If a dialed person doesn’t answer, is busy, or rejects the call, you get participant.failed with the reason and everyone else is unaffected.
Control who hears whom
Each participant has three settings. Change them with PATCH /conferences/{id}/participants/{participant} or the conference.update_participant command. Only the fields you send change.
A participant can’t be muted and whispering at the same time.
In commands, "participant": "self" means the call the WebSocket is on.
Audio your app sends
Audio your app sends over a call’s WebSocket is heard by everyone in that call’s conference, and your app hears the whole room on the stream. If the call is whispering, your app’s audio is whispered too.
When your app’s call is on hold, your app’s audio reaches only that call’s own person, not the rest of the conference.
Events
Every call in the conference gets these on its WebSocket. Each carries conference_id and participant_count (people currently joined).
participant.speaking can fire several times a second, so it’s off by default and isn’t saved to the call’s event history. Turn it on with speaking_events: true when creating the conference (or with PATCH /conferences/{id}).
Each participant’s own call still gets its normal events (call.answered, call.ended, …).
When a conference ends
- When the last participant leaves.
- With
DELETE /conferences/{id}or theconference.endcommand. - When a participant added with
end_conference_on_leave: trueleaves. - After 4 hours.
It does not end just because one person is left; they might be waiting for someone to be added back. Ending a conference hangs up everyone still in it, including anyone still being dialed.
To take one person out while everyone else stays, remove them with DELETE /conferences/{id}/participants/{participant} or conference.remove_participant. To leave from your own call, send conference.leave.
Closing your app’s WebSocket doesn’t end its call: the people in the conference keep talking.
Recording
Recording works per participant call, the same as for any call: each recording is that person plus what they heard, so it captures the whole conversation from their side. As always, recording needs a stream with recording turned on. record: true when dialing a participant turns it on in that call’s stream token.
Pricing
Each participant is charged $0.003 per minute they’re in the conference, rounded up to the whole minute and deducted from your credits when they leave. Someone who never joins (busy, no answer, or still being dialed when the conference ends) isn’t charged. This is on top of each participant’s own call, which is billed like any call.
Example: bring in a human and hand off
- Your app, on the caller’s call, adds a specialist:
conference.add_participant { "to": "+14155551234", "label": "specialist" } - On
participant.joined, your app introduces the call. The caller hears this too. - Your app closes its WebSocket. The caller and the specialist keep talking, and the conference ends when they both hang up.
To let a supervisor listen silently, add them with muted: true. To let them coach the specialist, set whisper_to: ["specialist"]. To let them take over, set muted: false and whisper_to: null.
Limits and errors
max_participantsis 10 by default, up to 50.- Labels are 1–64 characters of
A–Z a–z 0–9 _ -, unique within the conference, and can’t start withcall_orleg_. 409when the conference has ended or is full, a label is taken, or the call isn’t answered or is already in a conference.Idempotency-KeyonPOST /conferencesandPOST /conferences/{id}/participantsmakes dialing safe to retry. Over the WebSocket, retrying a command with the samecommand_idnever dials twice.

