Build an IVR
Create programmable IVR menus with TTS audio generation and DID assignment.
Build interactive voice response menus that qualify callers before routing them to an AI agent. Define the menu tree, generate audio with TTS, assign to a DID, and start taking calls.
Define your menu tree
An IVR is a tree of nodes. Each node either gathers a keypress (and routes to the next node) or performs a terminal action.
{
"name": "Plumber Qualifier",
"nodes": {
"welcome": {
"audio": "welcome.wav",
"gather": { "1": "has_license", "9": "dnc" },
"timeout_node": "welcome"
},
"has_license": {
"audio": "license_check.wav",
"gather": { "1": "service_area", "2": "unqualified" }
},
"service_area": {
"audio": "area_check.wav",
"gather": { "1": "transfer_ai", "2": "out_of_area" }
},
"transfer_ai": { "action": "transfer" },
"unqualified": { "action": "hangup", "audio": "not_qualified.wav" },
"out_of_area": { "action": "hangup", "audio": "wrong_area.wav" },
"dnc": { "action": "suppress" }
}
}Node types:
| Type | Fields | Behavior |
|---|---|---|
| Gather | audio, gather, timeout_node | Play audio, collect keypress, route to next node |
| Transfer | action: "transfer" | Hand off to AI agent |
| Hangup | action: "hangup", optional audio | Play goodbye audio and disconnect |
| Suppress | action: "suppress" | Add caller to DNC list and hang up |
Create the IVR
curl -X POST https://api.trunx.io/ivr \
-H "Authorization: Bearer $TRUNX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Plumber Qualifier",
"nodes": {
"welcome": {
"audio": "welcome.wav",
"gather": { "1": "has_license", "9": "dnc" },
"timeout_node": "welcome"
},
"has_license": {
"audio": "license_check.wav",
"gather": { "1": "service_area", "2": "unqualified" }
},
"service_area": {
"audio": "area_check.wav",
"gather": { "1": "transfer_ai", "2": "out_of_area" }
},
"transfer_ai": { "action": "transfer" },
"unqualified": { "action": "hangup", "audio": "not_qualified.wav" },
"out_of_area": { "action": "hangup", "audio": "wrong_area.wav" },
"dnc": { "action": "suppress" }
}
}'Response:
{
"id": "ivr_abc123",
"name": "Plumber Qualifier",
"status": "draft",
"created_at": "2026-03-09T12:00:00.000Z"
}Generate audio for each node
Use TTS to generate audio for every node that has an audio field. Repeat for each node.
curl -X POST https://api.trunx.io/ivr/ivr_abc123/audio \
-H "Authorization: Bearer $TRUNX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"node": "welcome",
"text": "Thanks for calling. Press 1 if you are a licensed plumber, or press 9 to be removed from our list.",
"voice": "rachel"
}'curl -X POST https://api.trunx.io/ivr/ivr_abc123/audio \
-H "Authorization: Bearer $TRUNX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"node": "has_license",
"text": "Great. Press 1 if you serve the Phoenix metro area, or press 2 if you do not.",
"voice": "rachel"
}'Audio is generated via TTS and stored as WAV files. Each generation costs $0.01/second of audio.
Assign to a DID
Bind the IVR to a phone number so inbound calls trigger the menu.
curl -X POST https://api.trunx.io/ivr/ivr_abc123/assign \
-H "Authorization: Bearer $TRUNX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"did": "+14155559876"
}'The IVR is now live. Calls to +14155559876 will hear the welcome prompt.
Test by calling the DID
Call the assigned number from your phone. Walk through the menu tree and verify:
- Audio plays clearly at each node
- Keypresses route to the correct next node
- Transfer hands off to the AI agent
- Pressing 9 adds you to the DNC list
- Timeout replays the current node
IVR definitions are cached on write. The call handler reads from cache at call time — your IVR works even if the hub is mid-deploy.
Use IVRs as cheap pre-qualifiers before routing to AI agents. An IVR minute costs $0.008 vs. $0.10 for AI voice. Deflect unqualified callers before incurring per-minute AI costs.
Updating an IVR
To modify nodes or regenerate audio, update the IVR definition and re-generate audio for changed nodes.
curl -X PATCH https://api.trunx.io/ivr/ivr_abc123 \
-H "Authorization: Bearer $TRUNX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"nodes": {
"welcome": {
"audio": "welcome.wav",
"gather": { "1": "has_license", "2": "hours", "9": "dnc" },
"timeout_node": "welcome"
}
}
}'The Redis cache updates immediately. The next inbound call uses the new definition.