Atualizado em 28/10/2026
Quando o cliente manda uma foto, um PDF, um áudio ou um vídeo, o webhook da API do WhatsApp não traz o arquivo: traz um media id. Para baixar, você faz GET /{media-id} para obter uma URL temporária, que expira em 5 minutos, e depois faz um GET nessa URL com o seu token de acesso no cabeçalho Authorization. Como o media id recebido por webhook vale só 7 dias, o certo é baixar logo e guardar o arquivo no seu próprio storage.
Este guia mostra o fluxo completo, os campos de cada tipo de mídia, os limites de tamanho, como conferir a integridade do arquivo e exemplos em cURL, Node.js e Python, incluindo o envio para o Amazon S3.
O fluxo em 4 passos
- Webhook: chega uma mensagem com
typeigual aimage,document,audio,videoousticker, com oidda mídia. - URL temporária:
GET https://graph.facebook.com/v26.0/{media-id}devolveurl,mime_type,sha256efile_size. - Download:
GETnaurlcomAuthorization: Bearer <token>. Sem o token, a requisição falha. - Armazenamento: salve no seu storage (S3, GCS, disco) e grave no banco o caminho do arquivo, nunca a URL da Meta.
Os prazos vêm da documentação de mídia da Meta: URLs de mídia expiram em 5 minutos (depois disso, basta consultar o media id de novo para gerar outra), media ids vindos de webhooks expiram em 7 dias e media ids gerados por upload expiram em 30 dias.
O que chega no webhook
Este é o formato de uma imagem recebida, segundo a referência de webhooks de imagem (valores de exemplo):
{
"object": "whatsapp_business_account",
"entry": [{
"id": "WABA_ID",
"changes": [{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "5511999999999",
"phone_number_id": "PHONE_NUMBER_ID"
},
"contacts": [{ "profile": { "name": "Maria" }, "wa_id": "5511988887777" }],
"messages": [{
"from": "5511988887777",
"id": "wamid.HBgLNTUx...",
"timestamp": "1761650000",
"type": "image",
"image": {
"caption": "Comprovante",
"mime_type": "image/jpeg",
"sha256": "SfInY0gGKTsJlUWbwxC1k+FAD0FZHvzwfpvO0zX0GUI=",
"id": "1003383421387256",
"url": "https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=..."
}
}]
}
}]
}]
}
O objeto muda de nome conforme o tipo (image, document, audio, video, sticker), mas a estrutura é parecida. Documentos trazem também filename, útil para salvar com o nome original.
Repare no campo url. A Meta começou a liberar esse campo no próprio webhook de forma gradual a partir de 12/11/2025, permitindo pular o passo 2 e baixar direto com o token. Como a liberação é gradual, seu código deve funcionar com e sem ele: use a url se vier e, se não vier ou o download der erro, consulte o media id.
| Campo | Para que serve |
|---|---|
id | Media id; use para gerar a URL de download |
mime_type | Tipo do arquivo (image/jpeg, application/pdf, audio/ogg…) |
sha256 | Hash do arquivo, para conferir integridade |
caption | Legenda enviada pelo cliente, quando houver |
filename | Nome original (documentos) |
url | URL de download direto, em liberação gradual |
Quer testar enquanto lê? Crie sua conta na Vectalk e envie a primeira mensagem pela API oficial. Criar conta grátis →
Tipos e limites de tamanho
A tabela abaixo resume os tipos suportados e os tamanhos máximos da página de mídia da Meta. Ela ajuda a dimensionar o storage e o tempo limite do download:
| Tipo | Formatos | Tamanho máximo |
|---|---|---|
| Áudio | AAC, AMR, MP3, M4A, OGG (só codec OPUS, mono) | 16 MB |
| Documento | TXT, XLS, XLSX, DOC, DOCX, PPT, PPTX, PDF | 100 MB |
| Imagem | JPEG, PNG (8 bits, RGB ou RGBA) | 5 MB |
| Figurinha | WebP | 500 KB animada, 100 KB estática |
| Vídeo | 3GP, MP4 (H.264 e AAC) | 16 MB |
Notas de voz chegam como mensagens do tipo audio. Se você vai passar o áudio para um serviço de transcrição, confira o mime_type recebido e se o serviço aceita aquele formato; se não aceitar, converta antes.
Exemplo com cURL
Passo 2, obter a URL:
curl "https://graph.facebook.com/v26.0/1003383421387256" \
-H "Authorization: Bearer $TOKEN"
Resposta:
{
"messaging_product": "whatsapp",
"url": "https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=...",
"mime_type": "image/jpeg",
"sha256": "SfInY0gGKTsJlUWbwxC1k+FAD0FZHvzwfpvO0zX0GUI=",
"file_size": 303833,
"id": "1003383421387256"
}
Passo 3, baixar:
curl -L "URL_RETORNADA" \
-H "Authorization: Bearer $TOKEN" \
-o comprovante.jpg
Opcionalmente, envie phone_number_id como parâmetro de consulta no passo 2: assim a Meta só processa a requisição se a mídia pertencer àquele número, uma proteção útil quando a mesma aplicação atende vários números.
Exemplo em Node.js
import crypto from 'node:crypto';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
const GRAPH = 'https://graph.facebook.com/v26.0';
const TOKEN = process.env.WHATSAPP_TOKEN;
const s3 = new S3Client({ region: process.env.AWS_REGION });
async function obterUrl(mediaId) {
const res = await fetch(`${GRAPH}/${mediaId}`, {
headers: { Authorization: `Bearer ${TOKEN}` },
});
if (!res.ok) throw new Error(`media ${mediaId}: HTTP ${res.status}`);
return res.json(); // { url, mime_type, sha256, file_size, id }
}
async function baixar(url) {
const res = await fetch(url, { headers: { Authorization: `Bearer ${TOKEN}` } });
if (!res.ok) throw Object.assign(new Error('download falhou'), { status: res.status });
return Buffer.from(await res.arrayBuffer());
}
export async function salvarMidia(midia, chave) {
let info = { url: midia.url, sha256: midia.sha256, mime_type: midia.mime_type };
let arquivo;
try {
if (!info.url) throw new Error('sem url no webhook');
arquivo = await baixar(info.url);
} catch {
info = await obterUrl(midia.id); // gera uma URL nova
arquivo = await baixar(info.url);
}
const hash = crypto.createHash('sha256').update(arquivo).digest('base64');
if (info.sha256 && hash !== info.sha256) throw new Error('hash não confere');
await s3.send(new PutObjectCommand({
Bucket: process.env.BUCKET,
Key: chave,
Body: arquivo,
ContentType: info.mime_type,
}));
return chave;
}
O hash sha256 dos exemplos da Meta está em base64, por isso a comparação usa digest('base64'). Se no seu caso o formato vier diferente, registre os dois valores antes de ativar a checagem.
Exemplo em Python
import base64
import hashlib
import os
import boto3
import httpx
GRAPH = "https://graph.facebook.com/v26.0"
TOKEN = os.environ["WHATSAPP_TOKEN"]
HEADERS = {"Authorization": f"Bearer {TOKEN}"}
s3 = boto3.client("s3")
def obter_url(client: httpx.Client, media_id: str) -> dict:
r = client.get(f"{GRAPH}/{media_id}", headers=HEADERS)
r.raise_for_status()
return r.json()
def salvar_midia(midia: dict, chave: str) -> str:
with httpx.Client(timeout=60, follow_redirects=True) as client:
info = obter_url(client, midia["id"])
r = client.get(info["url"], headers=HEADERS)
if r.status_code == 404: # URL vencida: gere outra e tente de novo
info = obter_url(client, midia["id"])
r = client.get(info["url"], headers=HEADERS)
r.raise_for_status()
calculado = base64.b64encode(hashlib.sha256(r.content).digest()).decode()
if info.get("sha256") and calculado != info["sha256"]:
raise ValueError("hash não confere")
s3.put_object(
Bucket=os.environ["BUCKET"],
Key=chave,
Body=r.content,
ContentType=info["mime_type"],
)
return chave
Para documentos de até 100 MB, carregar tudo em memória pode pesar. Nesse caso, baixe em stream para um arquivo temporário e use upload_fileobj do boto3 ou o Upload do pacote @aws-sdk/lib-storage no Node, que fazem upload em partes.
Onde colocar isso na arquitetura
Não baixe a mídia dentro da requisição do webhook. O endpoint deve validar a assinatura, enfileirar o evento e responder 200 rápido; um worker separado faz o download e o upload. Assim, um vídeo grande não segura o webhook nem gera reenvios da Meta. Sobre a validação, veja como validar a assinatura do webhook.
Algumas boas práticas:
- Chave de armazenamento previsível, por exemplo
whatsapp/{phone_number_id}/{wa_id}/{wamid}.{ext}, para achar o arquivo pela mensagem. - Idempotência: antes de baixar, verifique se aquela mensagem já foi processada; o webhook pode chegar repetido.
- Bucket privado e links assinados para quem precisa ver o arquivo, já que comprovantes e documentos têm dados pessoais. A LGPD (Lei 13.709/2018) exige cuidado com segurança e retenção desses dados; defina por quanto tempo guarda cada tipo. Isso não substitui a orientação de um advogado.
- Retry com prazo: se o download falhar, você tem até 7 dias de validade do media id para tentar de novo.
Erros comuns
404 ao baixar. A página de mídia orienta gerar uma nova URL de mídia e tentar de novo; se continuar, renove o token de acesso. Na prática, a causa mais comum é usar uma URL guardada há mais de 5 minutos.
401 ou 403 no download. O GET na URL foi feito sem o cabeçalho Authorization, ou com um token que não tem acesso à conta. Use um token permanente de usuário do sistema; veja token permanente da API do WhatsApp.
Erro 131052 no webhook. A tabela de erros da Meta define como “não foi possível baixar a mídia enviada pelo usuário”. Leia o campo error_data.details do webhook e peça ao cliente para reenviar o arquivo por outro meio.
Media id expirado. Depois de 7 dias, não há como recuperar a mídia pela API. Por isso o download deve acontecer logo após o webhook.
Como fazer isso com a Vectalk
Na Vectalk, os webhooks são os mesmos da Meta, então o media id chega no mesmo campo. Para a mídia, a API da Vectalk oferece GET https://apis.vectalk.com.br/api/media/{mediaId}, que devolve a URL temporária (válida por cerca de 5 minutos), e GET https://apis.vectalk.com.br/api/media/{mediaId}/download, que já devolve o arquivo binário com o Content-Type correto, eliminando o segundo passo. As duas rotas usam o token de instância. Crie sua conta em apps.vectalk.com.br/register.
Para o caminho inverso, enviar arquivos ao cliente, veja como enviar imagem, PDF, áudio e vídeo pela API. Se o seu bot recebe muitos arquivos, os exemplos completos estão nos tutoriais de bot em Python e bot em Node.js. O guia geral é o da API oficial do WhatsApp.
Perguntas frequentes
Quanto tempo a URL da mídia fica válida?
5 minutos. Depois disso, consulte o media id de novo para gerar uma URL nova. O media id recebido por webhook vale 7 dias.
Posso guardar a URL da Meta no banco e abrir depois?
Não. A URL expira em 5 minutos e exige o token para download. Guarde o arquivo no seu storage e salve no banco o caminho dele.
Preciso do token para baixar a URL?
Sim. A documentação da Meta diz que a requisição falha se você omitir o token. Envie Authorization: Bearer <token> tanto na consulta do media id quanto no download.
Qual o tamanho máximo de um arquivo recebido?
Pela tabela de tipos suportados da Meta, imagens vão até 5 MB, áudio e vídeo até 16 MB e documentos até 100 MB. Figurinhas têm limites menores.
Como saber se o arquivo baixado está íntegro?
Calcule o SHA-256 do conteúdo e compare com o sha256 do webhook ou da resposta do media id. Nos exemplos da Meta, o valor vem em base64.
Fontes oficiais consultadas em 28/10/2026: Media (Meta), Webhook de mensagens de imagem (Meta), Webhook de mensagens de documento (Meta), Códigos de erro (Meta), Mídia na API da Vectalk, PutObjectCommand (AWS SDK for JavaScript v3), upload_fileobj (boto3), LGPD (Lei 13.709/2018).
Teste a API oficial do WhatsApp hoje
Crie sua conta, gere o token e envie a primeira mensagem seguindo o guia da documentação. Mesmos payloads e webhooks da Cloud API da Meta.
