What callers get
-
A place in line
<Enqueue>puts each caller in a named queue, first come, first served. -
Announcements on hold
A hold document plays a message, pauses, and repeats until a caseworker is free.
-
Agents take the next caller
A caseworker who calls in is connected to whoever has waited longest, with
<Dial><Queue>. -
A word before connecting
The caller hears “your number has been called” just before the two are connected.
How the pieces fit
| Piece | Where it lives | What it does |
|---|---|---|
| Main document | voiceml.org, /r/ URL | The number’s voice URL. Puts callers in the queue and connects caseworkers to them. |
| Hold document | voiceml.org, /s/ URL | The waitUrl of the <Enqueue>. Plays while a caller waits, over and over. |
| Connect document | voiceml.org, /s/ URL | The url of the <Queue>. Plays to the caller just before they are connected. |
| Caseworkers’ phones | Any phone numbers | Call the same number to take the next caller. |
Before you start
- A VoiceML account with a phone number. See VoiceML or start a free trial.
- The numbers your agents call from, in E.164 format.
The caseworker numbers in this document are fictional. They use the 555-0100 to 555-0199 range, which is set aside for fiction. Replace them with your agents’ numbers before you publish.
Step 1: Publish the hold and connect documents
Publish both at voiceml.org. Neither contains Liquid, so each is served from an /s/ URL. Keep the URLs exactly as returned, key included, and don’t post them publicly.
wait.xml
<Response>
<Say>Please continue to hold. A caseworker will see you shortly.</Say>
<Pause length="2"/>
<Say>While you wait, this is a good time to read the Handbook for the Recently Deceased.</Say>
<Pause length="20"/>
</Response>
connect.xml
<Response>
<Say>Your number has been called. Your caseworker will see you now.</Say>
</Response>
Step 2: Publish the main document and point the number at it
Replace WAIT_URL and CONNECT_URL with the URLs from step 1, and the caseworker numbers with your agents’. Publish it, and set the /r/ URL it returns as the phone number’s voice URL.
main.xml
<Response>
{% assign caseworkers = "+18605550101,+18605550102" | split: "," %}
{% if caseworkers contains From %}
<!-- Caseworkers take the caller who has waited longest -->
<Say>Caseworker line. Connecting you to the next soul in the waiting room.</Say>
<Dial timeout="10">
<Queue url="CONNECT_URL">recently-deceased</Queue>
</Dial>
<Say>Call again to see the next soul. Goodbye.</Say>
<Hangup/>
{% else %}
<!-- Everyone else takes a number and waits -->
<Say>Welcome to the afterlife waiting room. Please take a number. A caseworker will see you in turn.</Say>
<Enqueue waitUrl="WAIT_URL">recently-deceased</Enqueue>
<Say>Thank you for waiting. Goodbye.</Say>
<Hangup/>
{% endif %}
</Response>
Step 3: Test it
- Call from a phone that isn’t on the caseworker list. You are asked to take a number and hear the hold announcements.
- From a caseworker phone, call the same number. The waiting caller hears “Your number has been called,” and the two of you are connected.
- Call from a caseworker phone again with nobody waiting. After ten seconds, you hear the goodbye.
How the queue works
Both sides use the same queue name, recently-deceased. Queues are private to your account, so another account using the same name gets a separate queue.
| Side | Markup | What happens |
|---|---|---|
| Caller | <Enqueue waitUrl="…"> | The caller joins the back of the queue. The hold document is fetched and played, then fetched and played again, until a caseworker takes the call or the caller hangs up. |
| Caseworker | <Dial timeout="10"><Queue url="…"> | The caller who has waited longest leaves the queue, hears the connect document, and is connected to the caseworker. With nobody waiting, the dial gives up after ten seconds. |
Caseworkers are recognized by From, using the same Liquid list check as the call screening example. Neither side has an action, so when a call ends, each document carries on to its goodbye.
To play music instead of announcements, leave waitUrl out: callers then hear the platform’s default hold playlist.
Where the movie turns up
| Where | Reference |
|---|---|
| The greeting | The afterlife’s waiting room, where the dead take a numbered ticket and wait for a caseworker. |
| The agents | Caseworkers, like Juno, the Maitlands’ caseworker. |
| The hold message | The Handbook for the Recently Deceased. |
Limits of this design
- No position announcements. The hold document is fetched with the caller’s place in line and the queue’s wait times, but a voiceml.org document can’t read those values. Announcing “you are caller number 3” needs a hold document served from your own server.
- Callers leave by hanging up. This hold document doesn’t offer a way to leave the line or leave a message instead.
- Agents pull calls; calls aren’t pushed to them. A caller waits until a caseworker phones in. To ring agents as soon as someone joins, use the on-call escalation example instead.
- Waiting callers don’t survive a platform restart. Queues are kept in memory, so a restart drops the callers waiting in them.
Credits
- The waiting room, its caseworkers, and the handbook come from Beetlejuice (1988), directed by Tim Burton and distributed by Warner Bros. Michael Keaton plays Betelgeuse; Alec Baldwin and Geena Davis play Adam and Barbara Maitland; Winona Ryder plays Lydia Deetz; and Sylvia Sidney plays Juno, the Maitlands’ caseworker.
- The line, its queue, and its phone numbers are fictional in this example. VoiceTel is not affiliated with the film or its rights holders.
- References: the
<Enqueue>reference, the<Queue>reference, and the voiceml.org publishing and integration guide.

