Skip to main content
A call is a set of legs, one per person. You dial the first leg with create, then add the second with add_participant and Unpod connects the two. Both people see your Unpod number. Haven’t made a call yet? Start with the Quick Start.
A call holds at most two participants today. A third returns 409 participant_limit. Conference calls are coming.

Add a participant after the first answers

Export your credentials, then set FROM to your Unpod number and FIRST and SECOND to two phones you can answer, all in E.164.
add_participant returns at once with the new leg in state initiated. SECOND’s phone rings; when they answer, Unpod connects the two and they can talk. Both see FROM as the caller. Keep leg.leg_id if you want to remove that person later.

Play a ringing sound while the second person rings

What FIRST hears while SECOND’s phone rings: dial_music does not play a sound yet: today it accepts only "none", and an audio URL is rejected as not supported. To play your own file, start it right after add_participant and stop it when SECOND’s leg is answered (or hung up). Without a leg_id, play_audio plays to everyone on the call, which until SECOND answers is only FIRST. The file must be a public https MP3 or WAV.
With the webhook server below, start the sound after add_participant and stop it on the Answered event for the customer leg. When FIRST hangs up, Unpod hangs up SECOND too and the call ends. The other way round it doesn’t: if SECOND doesn’t pick up, hangs up, or is removed with remove_participant, FIRST stays on the line alone. End the call yourself:

React to events instead of polling

Polling suits a script. A server should react to events: create the call with a status_callback_url, add the second person when the first one answers, and end the call as soon as either person hangs up (or the second never answers). Add this to your Quick Start app.py from Receive your first call, keeping its /answer route. Paste it above the if __name__ == "__main__": block: below it, uvicorn.run starts before /call-status is registered and every event gets a 404. The client is created at import, so export all four variables (UNPOD_BASE_URL, UNPOD_PLATFORM_TOKEN, UNPOD_ORG_HANDLE, UNPOD_WEBHOOK_SECRET) before you start the server.
app.py
Restart the server, then start a call from another terminal (ngrok already holds the second one), in the same directory and virtualenv as the server, with UNPOD_BASE_URL, UNPOD_PLATFORM_TOKEN, UNPOD_ORG_HANDLE and UNPOD_WEBHOOK_SECRET exported:
ev.role is the role you set on the leg. Unpod retries an event that doesn’t get a 2xx a few times over about a minute (Delivery and retries), so the handler always returns one after a valid signature, and the idempotency_key keeps a retried Answered from dialing second twice. See Webhooks for signatures and every event, and Number Masking for the full production flow.

Remove a participant

Remove a participant and the call stays up with the other person. If you also run the webhook server above, it ends the call on every Hangup event, which may include the leg you removed (it does: the removed leg gets a Hangup with Normal Hangup). Track the removed leg_id and skip it in the handler to keep the call up.

Options

The add_participant options most people need: create takes the same options. Every URL must be public https, or you get 400 invalid_url. Full list: Call Control.