01What is speech-to-text (STT)?
Speech-to-text (STT), also called automatic speech recognition (ASR), converts spoken audio into written text. For a WhatsApp business it is what lets a bot or an agent deal with a voice note as easily as a typed message: the customer records "mera order abhi tak nahi aaya", the system produces the transcript, and the rest of the pipeline — search, routing, replies — works on text.
WhatsApp does not transcribe for you on the API. A voice note arrives at your webhook as a message with type set to audio, whose audio object holds a media id, the mime_type (typically audio/ogg; codecs=opus) and a voice flag marking a recording made in WhatsApp. Your server exchanges the ID for a short-lived download URL, fetches the file with its access token and sends the audio to an STT engine. Calls through the WhatsApp Business Calling API can be transcribed too, but as streaming audio during the call rather than as a file afterwards.
STT is only the first step. It turns sound into words; natural language understanding (NLU) or a large language model then works out what those words mean. That split matters for accuracy: Indian customers switch between Hindi, English and regional languages mid-sentence, use local names and say numbers in different languages, so a transcript can read fluently and still be wrong in exactly the words that matter — an order number, a PIN code, a product name.
Key takeaways
- Speech-to-text (STT) converts spoken audio into text, so a bot or agent can handle a voice note like a typed message.
- On the Cloud API a voice note arrives as an
audiomessage with a media ID; you download the file and transcribe it yourself. - Webhook media IDs expire after 7 days and download URLs after about 5 minutes, so fetch audio promptly.
- Accuracy is measured as word error rate (WER) and drops with noise, strong accents and code-mixed Hindi-English speech.
- STT only produces words; working out what they mean is the job of NLU or an LLM afterwards.
02 · How it works, step by step
How does speech-to-text (STT) work?
- Step 1
Receive the voice note
The
messageswebhook delivers a media message withtypeaudio. Store thewamid, the sender'swa_idand the audioid, then acknowledge the webhook immediately — transcription runs in a background job. - Step 2
Download the audio
Media retrieval takes two calls:
GET /{MEDIA_ID}returns aurlvalid for about five minutes, and a GET on that URL with your bearer token returns the bytes. Media IDs received in webhooks expire after 7 days. - Step 3
Transcribe
Send the OGG/Opus file, or a converted WAV, to your STT engine with language hints — or let it run language detection — and request word timestamps and confidence scores where the engine offers them.
- Step 4
Normalise and mask the transcript
Apply inverse text normalisation so spoken digits become numbers, then PII masking for phone numbers, emails and long account numbers before the text reaches logs, analytics or an LLM prompt.
- Step 5
Hand the text on
Pass the transcript to the bot as if the customer had typed it, show it to agents beside the audio player, and route low-confidence transcripts to a person or ask the customer to confirm key details.
03Speech-to-text (STT) in code
// Runs in a queue worker, after the webhook has been acknowledged.async function transcribeVoiceNote(message) {const { id: mediaId, mime_type } = message.audio; // e.g. "audio/ogg; codecs=opus"const auth = { Authorization: `Bearer ${process.env.ACCESS_TOKEN}` };// 1. Exchange the media ID for a short-lived download URL (~5 minutes).const meta = await fetch(`https://graph.facebook.com/v26.0/${mediaId}`, { headers: auth }).then((r) => r.json());// 2. Download the bytes — the media URL needs the bearer token too.const audio = await fetch(meta.url, { headers: auth }).then((r) => r.arrayBuffer());// 3. Send to your speech-to-text engine (a generic HTTP API is shown).const form = new FormData();form.append("file", new Blob([audio], { type: mime_type }), "voice.ogg");form.append("language_hints", "hi,en");form.append("timestamps", "word");const stt = await fetch(process.env.STT_URL, { method: "POST", body: form }).then((r) => r.json()); // → { text, language, confidence, words: [...] }// 4. Mask personal data before the transcript is stored or sent to an LLM.return { wamid: message.id, from: message.from, ...stt, text: maskPii(stt.text) };}
Two Graph API calls fetch the audio; the STT request and its fields depend on the engine you use.
04Example: Voice-note orders at a Chennai grocery
A neighbourhood grocery in Chennai takes orders on WhatsApp, and about half arrive as voice notes in a Tamil-English mix. Each note is downloaded within seconds of the webhook, transcribed with Tamil and English language hints, and masked before storage. The bot reads "two kilo tomato, one packet milk, address same as last time", builds a draft cart and replies with reply buttons to confirm. When the confidence on a product name is low, it asks "Did you mean *toor dal*?" instead of guessing, and voice notes it cannot parse at all go to the shop's agent with the audio attached.
05 · Common mistakes, and the fix
Common mistakes with speech-to-text (STT)
Transcribing inside the webhook request
STT on a long voice note can take seconds. Doing it before returning 200 causes timeouts and retried, duplicated webhooks. Acknowledge first and transcribe from a queue.
Waiting too long to download
The download URL lasts about five minutes and webhook media IDs expire after 7 days. Fetch audio as soon as the job runs, and request a fresh URL rather than reusing an old one.
Forcing a single language
An English-only setting mangles Hinglish and regional speech; a Hindi-only setting mangles brand names. Use multi-language hints or automatic detection, and test on real customer audio.
Trusting numbers in transcripts
Order IDs, amounts and phone numbers are where STT errors hurt most. Confirm critical values back to the customer, or validate them against your order system, before acting.
Storing raw transcripts with personal data
Transcripts contain names, addresses and numbers spoken aloud. Mask them before logging, and apply the same retention rules to transcripts as to the audio under the DPDP Act.
06Speech-to-text (STT): key facts and limits
- Inbound format
- audio message, usually OGG with the Opus codec
- Webhook media ID lifetime
- 7 days
- Download URL lifetime
- About 5 minutes
- Cloud API audio limit
- 16 MB (AAC, AMR, MP3, M4A, OGG/Opus)
- Accuracy metric
- WER = (substitutions + deletions + insertions) ÷ words spoken
- Voice notes vs calls
- Batch file transcription vs streaming during the call
07Why does speech-to-text (STT) matter?
Voice notes are how many Indian customers prefer to talk — quicker than typing, easier in their own language, and natural for older or less text-comfortable users. A bot that replies "Sorry, I can only read text" loses them. With STT, voice notes enter the same queue, intent routing and search as text; agents can skim a transcript instead of replaying audio in a noisy office; and conversations become searchable for quality review and compliance.
08STT FAQs
Does the WhatsApp Cloud API transcribe voice notes automatically?+
How accurate is speech-to-text for Indian languages?+
What is the difference between speech-to-text and NLU?+
Can I transcribe WhatsApp calls as well as voice notes?+
09 · Tips from the bench
Best practices for speech-to-text (STT)
- Keep the audio for a short, defined period so agents can check an unclear transcript.
- Measure word error rate on a sample of your own voice notes per language, not on published benchmarks.
- Show agents the transcript and the audio player together, with low-confidence words highlighted.
- Reply in text even to voice notes unless you have tested text-to-speech in your customers' languages.