Áudio na API Oficial do WhatsApp: por que o áudio chega como arquivo (e os 3 bugs que você precisa corrigir juntos)
Enviar OGG na Cloud API não basta para virar push-to-talk com ondinha. São três bugs empilhados: voice:true no payload, Content-Type audio/ogg sem codecs=opus, metadata MP4 dentro do OGG (erro 131053), start_time negativo e Opus estéreo que quebra no iOS. Receita ffmpeg e checklist de envio.
Áudio parece o caso mais simples de mídia na Cloud API. Um OGG, um upload, um send.
É também onde integração que já manda imagem e PDF quebra sem erro claro.
O chamado chega sempre do mesmo jeito: três prints e a conclusão de que “é bug da Meta”. Quase nunca é. Na maioria das vezes falta voice: true, o Content-Type subiu sujo, ou o arquivo foi em estéreo. Detalhe pequeno, caro de descobrir em produção.
O fluxo típico é este: grava no browser, converte para OGG, sobe na /media, manda com "type": "audio".
Aí acontece uma de três coisas.
No celular do cliente aparece arquivo genérico, não a ondinha de push-to-talk. Ou a Meta devolve 131053, o upload vira application/octet-stream e a mensagem nem sai. Ou entrega, toca no WhatsApp Web, e no iPhone aparece “áudio não disponível”.
Cada sintoma aponta para uma camada diferente: payload, upload, container.
Não é um bug. São três bugs empilhados. Corrigir um e pular os outros só troca o erro que você vê amanhã.
Este guia mapeia os três, mostra a receita ffmpeg validada em produção, e o detalhe de arquitetura que impede um caso quebrado de escapar na próxima gravação.
Escopo: envio de áudio na API oficial (push-to-talk) via Cloud API. Áudio como arquivo anexo (sem
voice: true) é outro produto. Erros gerais de mídia → guia de erros da API (código 131053 na tabela mestra).
Bug 1: chegar com ondinha (push-to-talk), não como arquivo
Não basta mandar OGG/Opus. A Meta só renderiza waveform se duas condições forem verdade ao mesmo tempo:
| Condição | Valor |
|---|---|
| Container / codec | OGG com Opus |
| Payload da mensagem | "voice": true dentro do objeto audio |
OGG sem voice: true chega como arquivo: ícone de documento, sem ondinha. Esse é o parâmetro de push-to-talk que os fóruns chamam de detalhe obscuro da documentação.
Upload (POST /{phone-number-id}/media):
{
"type": "audio",
"audio": {
"id": "<media_id>",
"voice": true
}
}
Sem "voice": true, você entregou um MP3 disfarçado: formato certo, experiência errada.
Bug 2: erro 131053 (vira application/octet-stream e não envia)
O 131053 na Cloud API aparece como falha de upload ou download de mídia. No áudio convertido no browser, ele tem duas causas diferentes. As duas precisam ser tratadas.
Causa A: Content-Type com parâmetro de codec
No upload para /media, mande Content-Type: audio/ogg puro.
A Meta aceita audio/ogg; codecs=opus no upload, mas rejeita na entrega. O parâmetro ; codecs=... precisa sair antes do envio final.
| Header | Resultado |
|---|---|
audio/ogg; codecs=opus |
Upload pode passar; entrega falha ou classifica errado |
audio/ogg |
Caminho validado em produção |
Causa B: metadata de MP4/WebM dentro do OGG
O OGG gerado pelo browser (conversão webm/mp4 → ogg) carrega vestígio do container original. Strings como major_brand=isom e ...vp09... ficam embutidas no arquivo.
A Meta acha essas strings dentro do OGG, conclui que o tipo real é desconhecido, e trata como application/octet-stream. Daí o 131053.
Fix: limpar metadata e re-paginar o container:
ffmpeg -i in.ogg -map_metadata -1 -c copy \
-avoid_negative_ts make_zero -f ogg out.ogg
(-map_metadata -1 zera tags; -f ogg força re-page do container.)
Bug 3: play no iOS (“áudio não disponível”)
O mais traiçoeiro. Toca no WhatsApp Web e quebra só no iPhone.
start_time negativo
Gravação nativa de iOS (e alguns encoders) reporta encoder-delay com start_time negativo, tipicamente perto de −0,000625 s. Player tolerante ignora. O cliente WhatsApp no iOS, não.
Fix: -avoid_negative_ts make_zero (zera o início do stream).
Opus estéreo
A Meta aceita e entrega Opus estéreo. Só que o WhatsApp do destinatário não toca (a bolha muda de estado, principalmente no iOS).
Áudio push-to-talk tem que ser mono.
Fix: -ac 1.
Receita ffmpeg (normalização em background)
Detalhe de arquitetura que fez diferença: não decida “converter ou não”.
Todo áudio de saída passa pela normalização. Arquivo já compatível vai de remux lossless (-c copy, custo baixíssimo, zero perda). Só o que está quebrado de verdade vai para transcode.
Assim nunca escapa um start_time negativo ou um estéreo escondido.
Caso 1: já é OGG/Opus mono
Remux lossless, sem perder qualidade:
ffmpeg -i in.ogg -map_metadata -1 -c copy \
-avoid_negative_ts make_zero -f ogg out.ogg
Caso 2: estéreo, webm, mp4 ou qualquer outra origem
Transcode para mono Opus:
ffmpeg -i in.* -map_metadata -1 -c:a libopus \
-b:a 128k -ar 48000 -ac 1 \
-avoid_negative_ts make_zero -f ogg out.ogg
Parâmetros que importam:
| Flag | Por quê |
|---|---|
-map_metadata -1 |
Remove metadata MP4/WebM que dispara 131053 |
-avoid_negative_ts make_zero |
Corrige play no iOS |
-ac 1 |
Áudio mono (push-to-talk; estéreo quebra no destinatário) |
-ar 48000 |
Taxa padrão Opus/WhatsApp |
-b:a 128k |
Qualidade suficiente para voz |
-f ogg |
Re-page do container após limpeza |
Checklist de envio para a Meta
A armadilha aqui é corrigir um item só e liberar.
voice: true acerta o ícone no Android e deixa o iPhone mudo, porque o problema de lá é o container. Os três precisam andar juntos. E a normalização no ffmpeg vale mesmo quando o arquivo já é OGG.
| Etapa | O que fazer |
|---|---|
| 1. Normalizar | Rodar ffmpeg (sempre, mesmo se “já é OGG”) |
| 2. Upload | POST /media com body OGG e header Content-Type: audio/ogg (sem codecs=) |
| 3. Mensagem | "type": "audio", "audio": { "id": "<media_id>", "voice": true } |
| 4. Validar | Enviar teste para iPhone e Android antes de liberar em produção |
Sintomas → causa → ação
| Sintoma | Causa provável | Ação |
|---|---|---|
| Ícone de arquivo, sem ondinha | OGG ok, falta voice: true |
Adicionar "voice": true no objeto audio |
| 131053 no webhook / upload rejeitado | Content-Type com codecs= ou metadata MP4 no OGG |
Header audio/ogg puro + -map_metadata -1 |
| Toca no Web, “áudio não disponível” no iPhone | start_time negativo |
-avoid_negative_ts make_zero |
| Bolha muda, sem som (iOS) | Opus estéreo | -ac 1 na normalização |
| Funcionava ontem, quebrou hoje | Nova origem (ex.: gravador iOS nativo) | Pipeline universal: sempre normalizar |
FAQ
Dá para mandar MP3 ou M4A com voice: true? Não conte com isso. O caminho validado é OGG/Opus mono normalizado + voice: true.
Preciso transcodificar se o arquivo “já é OGG”? Sim, pelo menos remux com -map_metadata -1 e -avoid_negative_ts make_zero. O custo de -c copy é desprezível e pega metadata fantasma e timestamp negativo que passam despercebidos.
O 131053 é só de áudio? Não. O código cobre falha geral de mídia (tipo, tamanho, codec). Para áudio de voz, as causas acima são as que mais aparecem quando a origem é browser ou conversão client-side. Ver também a tabela 131xxx.
WhatsApp Web aceita estéreo; por que forçar mono? Porque o destino real é o app mobile do cliente. A Meta entrega; o player do WhatsApp no celular (especialmente iOS) não reproduz Opus estéreo como push-to-talk.
O parâmetro voice aparece na documentação? Sim, no objeto audio do Messages API. É fácil omitir porque exemplos genéricos de áudio não incluem o flag.
Créditos
Investigação completa deste encadeamento (browser → OGG → 131053 → iOS) conduzida na comunidade WhatsApp Founders por Lucas Rafagnin (Dev ITA Jr / Bola8), com apoio do time ChatJur, do sintoma “chega como arquivo” até a receita ffmpeg que roda em background hoje.
Referências: WhatsApp Cloud API: Audio messages, Media API. Comportamento de player pode variar por versão do app. Valide sempre em dispositivo real.
Conteúdo da comunidade WhatsApp Founders 🇧🇷. Independente, sem vínculo oficial com o WhatsApp ou a Meta.