IVR prompt batch TTS: render phone trees as files — Audra

An IVR tree is the most static audio workload in existence: 'Press 1 for sales' was recorded in 2019 and has played a million times since. Yet every reorganization means a re-record of a human reading forty 5-second prompts into a mic. Batch TTS makes prompt refresh a config change, not a studio session.

This post is the IVR prompt pipeline: batch-rendering a prompt catalog, file format choices for telephony platforms, numbering and versioning, and what a full prompt tree costs on Essential at $25/1M prepaid.

The persona: a 40-prompt phone tree

A mid-size IVR deployment is 30–60 prompts: greetings, menus, hold messages, error messages, after-hours notices, voicemail instructions. Each is 3–10 seconds of speech, 100–300 characters. The entire catalog is maybe 8,000 characters — one afternoon of API calls, forever.

Render the catalog as a manifest

Keep prompts in a manifest (YAML or JSON) keyed by prompt ID, not filenames in a folder. Render each entry, write output as the prompt ID plus version, and upload to your telephony platform in one pass.

curl
curl https://audratalks.com/v2/speech \
  -H "Authorization: Bearer sk-audra-..." \
  -H "Content-Type: application/json" \
  -d '{"model":"audra-essential","voice":"ellis","text":"Thank you for calling Northwind Support. For billing, press one. For technical help, press two.","format":"wav"}' \
  --output prompt-menu-main-v4.wav
Python
import requests

PROMPTS = {
    "menu-main": "Thank you for calling Northwind Support. For billing, press one. For technical help, press two.",
    "hold": "Your call is important to us. Please stay on the line.",
    # ... 38 more, from the manifest
}

for pid, text in PROMPTS.items():
    resp = requests.post(
        "https://audratalks.com/v2/speech",
        headers={"Authorization": "Bearer sk-audra-..."},
        json={"model": "audra-essential", "voice": "ellis", "text": text, "format": "wav"},
        timeout=600,
    )
    resp.raise_for_status()
    open(f"{pid}-v4.wav", "wb").write(resp.content)
JavaScript
const prompts = {
  "menu-main": "Thank you for calling Northwind Support. For billing, press one. For technical help, press two.",
  "hold": "Your call is important to us. Please stay on the line.",
  // ...38 more, from the manifest
};

for (const [pid, text] of Object.entries(prompts)) {
  const res = await fetch("https://audratalks.com/v2/speech", {
    method: "POST",
    headers: { Authorization: "Bearer sk-audra-...", "Content-Type": "application/json" },
    body: JSON.stringify({ model: "audra-essential", voice: "ellis", text, format: "wav" }),
  });
  if (!res.ok) throw new Error(`${pid}: ${await res.text()}`);
  await writeFile(`${pid}-v4.wav`, Buffer.from(await res.arrayBuffer()));
}

WAV, not MP3, for telephony

Prompt files are small and telephony platforms transcode to their own telephony codec (often 8 kHz mono) anyway. Request wav — the format post covers the full tradeoff, but the short version: MP3's lossy compression plus a second lossy transcode compounds artifacts, and at 5 seconds per file the size penalty of WAV is irrelevant.

The one thing IVR builders get wrong: reading style

Voice: clarity over personality

IVR voices are chosen for intelligibility at 8 kHz through a phone speaker. ellis (en-gb) and grant are the usual picks. Audra outputs −16 LUFS, so all prompts land at consistent loudness — no more 'the hold message is quieter than the menu'.

Cost: effectively zero

A 40-prompt catalog is ~8,000 characters: $0.20 to render the entire tree on Essential, once. Re-rendering the full catalog after every reorganization still costs twenty cents. The 25k trial characters cover initial evaluation; production deployment needs a paid pack (commercial rights on, send X-Audra-Commercial-Use: true).

What IVR is not: realtime speech synthesis during a live call — Audra is a batch file factory, not a telephony stack. Prompt files are generated ahead of time; dynamic content (names, balances) is spoken by your existing telephony TTS or pre-rendered in batches. SSML pacing detail: the SSML breaks post.