wapp-api — envio de mídia
Complementa docs/api.md. Autenticação, códigos de erro e consulta de
status são os mesmos de lá.
Como mandar
Mesma rota do texto, POST /api/mensagens, mas em multipart/form-data
em vez de JSON. O arquivo vai na parte arquivo; os demais campos vão
como campos de formulário.
Multipart e não base64 no JSON de propósito: o upload é derramado para arquivo temporário e a memória do servidor não acompanha o tamanho do anexo. O base64 acontece só no último salto, do worker para a Evolution — o arquivo nunca é exposto por URL.
| Campo | Obrigatório | Observação |
|---|---|---|
arquivo |
sim | a parte com o arquivo em si |
instancia |
sim | nome da instância |
numero |
sim | mesma normalização do texto |
tipo |
sim | imagem, documento, video ou audio |
conteudo |
não | legenda (imagem/vídeo/documento) |
nome_arquivo |
não | nome exibido; sem ele usa o nome do upload |
nota_de_voz |
não | só para tipo=audio; padrão true |
agendado_para |
não | ISO 8601 |
O tipo precisa bater com o mimetype real do arquivo. Se mandar um PDF
dizendo tipo=imagem, a resposta é 400 e nada é gravado.
Tipos aceitos
| mimetype | tipo |
Extensão |
|---|---|---|
application/pdf |
documento |
.pdf |
image/jpeg |
imagem |
.jpg |
image/png |
imagem |
.png |
video/mp4 |
video |
.mp4 |
audio/ogg, audio/mpeg, audio/mp4 |
audio |
.ogg, .mp3, .m4a |
Qualquer outro mimetype: 400.
Áudio: nota de voz ou anexo
nota_de_voz=true(padrão) → bolha de nota de voz, com onda sonora. A conversão para ogg/opus é feita pela Evolution.nota_de_voz=false→ anexo de áudio comum, com player de arquivo.
Tamanho
Teto de 16 MB por arquivo, que é o limite do WhatsApp para mídia tocada dentro da conversa. Acima disso: 400.
O corpo inteiro da requisição é limitado a 20 MB (a folga é o overhead
do multipart). Passar disso devolve 413, com limite_bytes na resposta.
Vídeo acima de 16 MB no WhatsApp só existe enviado como documento, e aí perde o player — vira anexo para baixar. Se precisar disso, fale comigo antes: muda o desenho.
Exemplo em Python
import os
import requests
API = 'http://wapp-api.local'
CABECALHOS = {'X-API-Key': os.environ['WAPP_API_KEY']}
with open('boleto.pdf', 'rb') as arquivo:
resposta = requests.post(
f'{API}/api/mensagens',
headers=CABECALHOS,
data={
'instancia': 'principal',
'numero': '(11) 99999-8888',
'tipo': 'documento',
'conteudo': 'Segue seu boleto.',
'nome_arquivo': 'boleto-agosto.pdf',
},
files={'arquivo': ('boleto.pdf', arquivo, 'application/pdf')},
timeout=60,
)
resposta.raise_for_status()
print(resposta.json()['id'])
Nota de voz é a mesma chamada com 'tipo': 'audio' e o arquivo de áudio.
Exemplo em curl
curl -X POST http://wapp-api.local/api/mensagens \
-H "X-API-Key: $WAPP_API_KEY" \
-F 'instancia=principal' \
-F 'numero=(11) 99999-8888' \
-F 'tipo=documento' \
-F 'conteudo=Segue seu boleto.' \
-F 'arquivo=@boleto.pdf;type=application/pdf'
Onde o arquivo fica
Em disco no servidor, sob MIDIA_DIR/AAAA/MM/<uuid>.<ext>, modo 0640 e
nunca servido pelo Apache. O banco guarda só os metadados
(arquivo_caminho, mimetype, tamanho_bytes).
Ainda não existe rotina de expurgo: os arquivos ficam até serem
apagados à mão. A coluna arquivo_expurgado_em já está no schema para
quando ela for escrita — é o que vai distinguir "a limpeza apagou" de "o
arquivo se perdeu". Se o worker tentar enviar um anexo que sumiu, a
mensagem vai para falhou com o motivo explícito.