API

IVR API

Create programmable IVR menus with TTS audio generation and DID assignment.

IVR (Interactive Voice Response) trees define the menu structure for inbound calls. Each IVR is a set of nodes — gather nodes present a menu and wait for DTMF input, and action nodes perform a terminal operation like transfer or hangup.

IVR definitions are cached on write. At call time, the handler reads the cached definition directly — no runtime fetch to the hub.


Create IVR

POST /ivr

curl -X POST https://api.trunx.io/ivr \
  -H "Authorization: Bearer tk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "id": "qualify-business",
    "nodes": {
      "welcome": {
        "audio": "welcome.wav",
        "gather": { "1": "has_business", "9": "dnc" },
        "timeout_node": "welcome"
      },
      "has_business": {
        "audio": "business_check.wav",
        "gather": { "1": "transfer_ai", "2": "unqualified" }
      },
      "transfer_ai": { "action": "transfer" },
      "unqualified": { "action": "hangup", "audio": "not_qualified.wav" },
      "dnc": { "action": "suppress" }
    }
  }'
const res = await fetch("https://api.trunx.io/ivr", {
  method: "POST",
  headers: {
    "Authorization": "Bearer tk_live_...",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    id: "qualify-business",
    nodes: {
      welcome: {
        audio: "welcome.wav",
        gather: { "1": "has_business", "9": "dnc" },
        timeout_node: "welcome",
      },
      has_business: {
        audio: "business_check.wav",
        gather: { "1": "transfer_ai", "2": "unqualified" },
      },
      transfer_ai: { action: "transfer" },
      unqualified: { action: "hangup", audio: "not_qualified.wav" },
      dnc: { action: "suppress" },
    },
  }),
});
const data = await res.json();
import requests

res = requests.post(
    "https://api.trunx.io/ivr",
    headers={"Authorization": "Bearer tk_live_..."},
    json={
        "id": "qualify-business",
        "nodes": {
            "welcome": {
                "audio": "welcome.wav",
                "gather": {"1": "has_business", "9": "dnc"},
                "timeout_node": "welcome",
            },
            "has_business": {
                "audio": "business_check.wav",
                "gather": {"1": "transfer_ai", "2": "unqualified"},
            },
            "transfer_ai": {"action": "transfer"},
            "unqualified": {"action": "hangup", "audio": "not_qualified.wav"},
            "dnc": {"action": "suppress"},
        },
    },
)
data = res.json()

Node types

Gather node

Plays audio and waits for DTMF input. Routes to the next node based on the digit pressed.

FieldTypeRequiredDescription
audiostringYesAudio file to play (WAV/MP3 filename or TTS-generated ID)
gatherobjectYesMap of DTMF digits to target node names
timeout_nodestringNoNode to jump to if no input received (defaults to replaying current node)

Action node

Performs a terminal operation and ends the call flow.

FieldTypeRequiredDescription
actionstringYesOne of "transfer", "hangup", "suppress"
audiostringNoOptional audio to play before executing the action

Actions

ActionDescription
transferConnect the caller to an AI voice agent
hangupEnd the call (optionally play goodbye audio first)
suppressAdd the caller's number to the DNC suppression list and hang up

List IVRs

GET /ivr

Returns all IVR definitions for the authenticated account.


Get IVR

GET /ivr/:id

Returns a specific IVR definition with all nodes.


Update IVR

PATCH /ivr/:id

Update an existing IVR definition. The updated definition is immediately written to the Redis cache.

IVR updates take effect immediately for new calls. Calls already in progress continue using the IVR definition that was active when the call started.


Delete IVR

DELETE /ivr/:id

Delete an IVR definition. Fails if the IVR is currently assigned to a DID.


Generate audio

POST /ivr/:id/audio

Generate TTS audio for a specific IVR node. The generated audio file is stored and associated with the node.

curl -X POST "https://api.trunx.io/ivr/qualify-business/audio" \
  -H "Authorization: Bearer tk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "node": "welcome",
    "text": "Thank you for calling. Press 1 if you are a business owner. Press 9 to be added to our do not call list.",
    "voice": "rachel"
  }'
const res = await fetch("https://api.trunx.io/ivr/qualify-business/audio", {
  method: "POST",
  headers: {
    "Authorization": "Bearer tk_live_...",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    node: "welcome",
    text: "Thank you for calling. Press 1 if you are a business owner. Press 9 to be added to our do not call list.",
    voice: "rachel",
  }),
});
const data = await res.json();
import requests

res = requests.post(
    "https://api.trunx.io/ivr/qualify-business/audio",
    headers={"Authorization": "Bearer tk_live_..."},
    json={
        "node": "welcome",
        "text": "Thank you for calling. Press 1 if you are a business owner. Press 9 to be added to our do not call list.",
        "voice": "rachel",
    },
)
data = res.json()

Assign IVR to DID

POST /ivr/:id/assign

Bind an IVR definition to a phone number. Inbound calls to that DID will be handled by this IVR tree.

curl -X POST "https://api.trunx.io/ivr/qualify-business/assign" \
  -H "Authorization: Bearer tk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "did": "+19495551234" }'
await fetch("https://api.trunx.io/ivr/qualify-business/assign", {
  method: "POST",
  headers: {
    "Authorization": "Bearer tk_live_...",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ did: "+19495551234" }),
});
import requests

requests.post(
    "https://api.trunx.io/ivr/qualify-business/assign",
    headers={"Authorization": "Bearer tk_live_..."},
    json={"did": "+19495551234"},
)

Each DID can only have one IVR assigned at a time. Assigning a new IVR to a DID replaces the previous assignment.

On this page