When Unpod calls your answer URL
An inbound call is still ringing when Unpod fetches the URL, so a reply that starts with
<Hangup/> rejects the call. On an outbound leg, Unpod runs your instructions once that
person connects, instead of just bridging them:
400 invalid_url.
What Unpod sends
On an inbound call, your answer URL receives:
Each
StartApp also carries:
MPCUUID(ev.call_id): the id you use with the call APIsCallUUID(ev.leg_id),From(ev.from_),ToandDirection=inboundSessionStart(ev.raw["SessionStart"])
StartApp, sent once the person answers. It
carries the same fields, with the fixed values Direction=inbound and
CallStatus=ringing, so don’t branch on Direction. Its CallUUID ends in -caller, so it
doesn’t match the leg_id that create returned: match outbound requests on MPCUUID
(ev.call_id). When the call ends, the same URL also gets a Hangup with Duration,
StartTime, EndTime, AnswerTime and HangupCause; reply 200 to it.
Inbound POST requests are signed once your org has a webhook secret (create it as in the
Quick Start). Verify and parse them
with parse_call_event - see Webhooks. Outbound requests
are not signed yet, and GET requests are not signed (there’s no body to sign).
Reply with XML or JSON
Reply with Plivo-style XML or JSON. Greet the caller, then connect them to an agent:
On
<Hangup>, reason applies only when rejecting a ringing inbound call (busy sends
486, anything else 603). code is a SIP status 400-699 and works before and after answer.
JSON field names differ from XML attributes: speak.text, play.url, wait.seconds,
record.max_duration_secs.
Only these verbs are supported. Any other element, such as <GetDigits>, <Conference>
or <Redirect>, makes the whole reply fail and the
fallback applies.
Verbs run in order. When the last one finishes, Unpod hangs up; verbs after <Dial>
don’t run. A reply can be at most 64 KB with 32 verbs, and 4096 characters in each
<Speak>. Every audio URL must be public https.
Set the voice and language on <Speak> itself:
record=True on create doesn’t override it today (see
Recording). Don’t put <Record/> before <Dial>: it waits
for the recording to finish (300 s by default) before dialing, and the caller hears
silence.
A minimal answer server
app.py
python app.py and expose it as in the
Quick Start. /answer rejects
unsigned requests, greets every new inbound call and returns 200 for every other event.
/outbound greets each outbound leg once it’s answered and acknowledges its Hangup. To route callers to free agents, see
Incoming Calls.
Timeouts and fallback
Inbound. Set these on the number’s config (Incoming Calls):
The fallback applies on a timeout, a connection error, any non-2xx status (including your
own 401), a reply that doesn’t parse, or a reply containing an action URL that isn’t
public https. An empty 2xx body,
<Response/> or {"actions": []} is not a failure:
Unpod continues with static routing.
Outbound. The timeout is fixed at 3000 ms. If your app fails or sends an empty reply,
nothing runs and the leg stays connected until you end the call. A reply that starts with
<Hangup/> ends it.
Serve a static file
No server needed: host a fixed XML file on any public https host, then passanswer_url="https://cdn.example.com/greeting.xml" and answer_method="GET" to create.
Every call runs the same instructions, which suits
Call Blasting.
greeting.xml