Skip to main content
Send the same message to many people: appointment reminders, outage alerts, payment notices. You dial each number with create and give every call the same answer URL. When someone picks up, Unpod fetches it and plays your message. Host the message as a static XML file and you need no server to send the calls. Haven’t made a call yet? Start with the Quick Start.

1. Write the message

Say it in a platform voice with <Speak>, or play a recording with <Play>:
To pick a platform voice, add voice and language to <Speak> with real values. A made-up value can make the speech fail, and the call then ends right after it’s answered. Save either one as blast.xml. The audio file is a public https MP3 or WAV, given as the text of <Play>. <Hangup/> is optional: Unpod hangs up when the last verb finishes. For every verb, see Answer URL & XML. Host blast.xml anywhere public over https that serves it without a redirect, such as object storage or a CDN: Unpod doesn’t follow redirects. See Serve a static file.
If Unpod can’t fetch the file within 3 seconds, or it’s empty, nothing plays and the call stays connected in silence until the person hangs up or you end it. After 5 failed fetches in a row, Unpod skips the fetch for 30 s, so calls answered then are silent even after the file is back. Calls have no maximum length: end a stuck one with calls.end (see Call Control). The script below checks the file once before dialing; also dial your own number first and listen.

2. Dial the list

Put one E.164 number per line in numbers.txt, then run:
create returns as soon as Unpod accepts the call; it doesn’t wait for the phone to ring. The script dials one number a second, because Unpod accepts at most 60 create and add_participant requests a minute per org (3,600 numbers take about an hour). On a 429 (that limit, or your org’s call cap) or a network error it backs off and tries again; any other error is listed at the end and the rest are still dialed. gave up after retries means the 429s (or network errors) didn’t stop: run the script again later to pick those numbers up. Fix the list and run the script again: thanks to the idempotency key reminder-nov:<number>, a re-run within 24 h doesn’t dial the same number twice, unless Unpod restarts (keys live in memory). Use a new campaign name for a new blast. Leave out status_callback_url if you don’t need step 3.

3. Track results

Unpod posts each call’s events to status_callback_url, with your metadata on every one as ev.metadata. Outbound events don’t carry the number in ev.to, so it comes back from the "to" you put in metadata. Record the outcome on Hangup. Create your webhook secret first: status callbacks are signed with it.
app.py
Run it with python app.py and expose it as in the Quick Start. Unpod retries an event that doesn’t get a 2xx a few times over about a minute (Delivery and retries), so the same Hangup can arrive twice; writing the outcome by number makes that harmless. If your server is down for the whole retry window (roughly 27-75 s), the event is dropped and that number never gets an outcome. A number missing from results is either still on a call or lost its Hangup: list the live calls with calls.list() (every page, until one comes back empty) and check again later. Normal Hangup only means the call was answered. A duration shorter than your message means they didn’t hear all of it. A voicemail that picks up, or a <Speak> or <Play> that Unpod skipped because the speech or the audio failed, also shows Normal Hangup. A burst of calls can pass the check in create (202) and still hit your org’s limit while dialing. Those calls never ring and end with Service Unavailable (503). To retry the busy, unanswered and not-dialed numbers, put them in a new numbers.txt and run step 2 with a new campaign name, such as reminder-nov-retry.

Limits

  • The answer URL and status_callback_url must be public https, or create returns 400 invalid_url. A non-https audio URL inside blast.xml makes the reply fail, and the call stays connected in silence. See Answer URL & XML.
  • create and add_participant together are limited to 60 requests a minute per org. Over it, Unpod returns 429 with detail Request was throttled. Expected available in N seconds. and dials nothing. PACE keeps the script under it. Each create returns right away, so up to 60 new calls can be ringing each minute.
  • Unpod can also cap your org’s concurrent calls. At the cap, create returns 429 and dials nothing, and the script backs off and retries. A call accepted right at the cap can still end with Service Unavailable.
  • A reply can be at most 64 KB, with up to 32 verbs and 4096 characters in each <Speak>.

Next

Number Masking

Connect two people without showing their numbers.

Answer URL & XML

Every verb you can put in the message.

Webhooks

Verify events and read every field.