Direct answer
Outbound bidirectional Twilio Media Streams media.payload is not a WAV file. It is base64-encoded raw audio/x-mulaw at 8,000 Hz and one channel. When the source is already a μ-law WAV, its RIFF/WAVE container bytes must still be removed; only the audio bytes inside the inspected data chunk become the payload.
Source WAV versus delivered payload
Source RIFF/WAVE
├─ RIFF size + WAVE form
├─ fmt chunk
├─ optional chunks and padding
└─ data header
└─ [ μ-law audio bytes ]
Twilio media.payload
= base64([ μ-law audio bytes ])The headerless .ulaw artifact and the JSON template are developer assets. IVRReady does not open a WebSocket or send them to Twilio.
Why slice(44) is unsafe
Forty-four bytes is only one common minimal WAV layout. Real RIFF files can place extra chunks before audio, use odd-sized chunks with padding, or carry larger format structures. IVRReady first validates the RIFF chunk graph, then uses the parser’s proven dataOffset and dataSize bounds. It copies exactly that region and never scans arbitrary bytes for a convenient data string.
Two source paths, one proven payload
Canonical source: zero FFmpeg
8 kHz mono μ-law WAV → structural verification → exact data-chunk extraction → raw .ulaw → base64 → media template. No audio transcoding occurs.
Noncanonical verified source
For example, 48 kHz stereo PCM WAV → browser-local FFmpeg correction → generated WAV reinspection → 0x0007 / 8 kHz / mono proof → exact extraction → payload.
Base64 overhead and the browser envelope
Base64 length is exactly 4 × ceil(raw bytes / 3), so it expands raw media by roughly one third before the JSON envelope is counted. Twilio’s documentation says a payload can be any size; that is a protocol statement, not unlimited browser memory. IVRReady keeps encoding chunks bounded, yields cooperatively during large payloads so cancellation remains responsive, and has exercised raw payloads through 30 MiB on its documented release test host. That measurement is not a Twilio maximum.
One prepared payload versus runtime chunking
IVRReady creates one payload per source. The cited outbound contract does not impose an invented 20 ms or 160-byte message requirement. A runtime application may still choose smaller messages for its own buffering, interruption, or scheduling design; that is an application optimization, not an IVRReady compatibility rule.
The runtime Stream SID
{
"event": "media",
"streamSid": "MZ_REPLACE_AT_RUNTIME",
"media": {
"payload": "<base64 μ-law payload>"
}
}The developer’s application replaces the placeholder with the active Stream SID it received at runtime. Twilio also documents mark messages for completion feedback and clear for buffered media control; those are runtime WebSocket responsibilities and IVRReady does not implement them.
Prepare WAV source locally
Use the Twilio Media Streams audio preparer, review G.711 μ-law WAV fundamentals, or compare the six target contracts.