Internal training TTS: convert SOPs to audio for teams — Audra

Every company has a folder of SOPs, onboarding docs, and compliance training that employees are supposed to read and mostly skim. Audio versions get listened to on commutes and walks. The blocker has always been re-recording: SOPs change monthly and nobody re-records audio. Batch rendering makes audio a build artifact, not a project.

This post is the L&D pipeline: converting an SOP library to audio, chunking long procedure documents, handling internal jargon, and what a training library costs per quarter on Essential at $25/1M prepaid.

The persona: an L&D team with a 120-SOP library

Picture 120 SOPs and onboarding modules averaging 4,000 characters each — about 480,000 characters total, roughly 10 hours of finished audio. SOPs differ from courses: they are procedural, dense with numbered steps, and full of internal system names. The render pipeline is easy; the script cleanup is where the quality lives.

Render per SOP, keyed to version

Each SOP is one sync request (all under 50k chars). Tag the output filename with the document version so your LMS always plays the current one — when the wiki page bumps to v7, the pipeline re-renders and the old audio is never served again.

curl
curl https://audratalks.com/v2/speech \
  -H "Authorization: Bearer sk-audra-..." \
  -H "Content-Type: application/json" \
  -d '{"model":"audra-essential","voice":"grant","text":"Procedure PR-014: Expense reimbursement...","format":"mp3"}' \
  --output pr-014-v7.mp3
Python
import requests

resp = requests.post(
    "https://audratalks.com/v2/speech",
    headers={"Authorization": "Bearer sk-audra-..."},
    json={"model": "audra-essential", "voice": "grant", "text": sop_text, "format": "mp3"},
    timeout=3600,
)
resp.raise_for_status()
if resp.headers.get("X-QA-Passed") != "true":
    raise RuntimeError("SOP render failed QA; credits auto-refunded")
open("pr-014-v7.mp3", "wb").write(resp.content)
JavaScript
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: "grant", text: sopText, format: "mp3" }),
});
if (!res.ok) throw new Error(await res.text());
await writeFile("pr-014-v7.mp3", Buffer.from(await res.arrayBuffer()));

grant's newsread delivery suits procedural content; the numbered steps read as clearly as they look. Keep speed at the default 1.0 — speeding up training audio to save listening time hurts comprehension more than it saves.

Chunking: the rare case it matters

Most SOPs render in one request. The exception is compiled compliance bundles (multiple SOPs concatenated into one 'module'), which can exceed 50k characters. Two options: render each constituent SOP separately and concatenate the files, or send the bundle to POST /v2/speech/jobs (up to 500k chars) as a single background job.

Internal jargon and system names

SOPs reference internal systems whose names TTS engines mangle. Two tools fix this without rewriting every document: check pronunciation first with POST /v2/phonemize (free, no credits consumed), then lock corrections in your per-account lexicon (up to 100 entries like 'JIRA' → 'JEE-ra') so every future render says it right automatically.

Cost per quarter, at L&D scale

Full library first render: 480,000 chars × $25/1M = $12. Quarterly, maybe 15% of SOPs change: ~72,000 chars ≈ $1.80. Annual cost of keeping the entire audio library current sits under $20 on Essential. A $5 credit pack covers the first two quarters. Model your library size on /calc.

Watch-outs for training content

Per-course-hour economics for external courseware: the course narration post. Retention and training-stance details: the data-retention post.