O Connect não armazena arquivos. Para mandar um arquivo seu, hospede-o e mande o link dele (link) — quem baixa é a Meta, não nós. Esse é o caminho principal, não a alternativa.
Os identificadores (id) são os de arquivos que você RECEBEU: chegam dentro do evento de uma mensagem recebida (data.message.image.id, .document.id, …). Não há endpoint de upload — GET /v1/media/:mediaId é só de download —, então não existe forma de obter um id para um arquivo seu.
⚠ E a parte incômoda, dita como é: a Meta costuma permitir reutilizar um identificador recebido para responder do mesmo número, mas não garante e nós ainda não verificamos contra a Meta real. Não afirmamos nem negamos. Se importa para você que saia sempre, use o link.
Quer mandar SEMPRE o mesmo arquivo — uma lista de preços, um manual, um catálogo? Hospede-o e mande o link. É a resposta completa: não há um id próprio para guardar e reutilizar. E não é uma limitação disfarçada — não guardamos arquivos porque não guardamos conteúdo (nem o texto das suas mensagens, salvo o histórico de Coexistence que você escolhe receber: criptografado e por 7 dias no máximo). É exatamente o que a nossa política de privacidade promete, aplicado aqui.
Como se manda. POST /v1/messages com type image/document/audio/video e, dentro do objeto desse tipo, link ou id — exatamente um dos dois. caption para image/document/video; audio não aceita caption; document pode levar filename (o nome que quem recebe vê).
O que o link precisa para a Meta aceitá-lo: ser https e apontar direto para o arquivo (que a resposta sejam os bytes, sem login nem página intermediária); trazer o Content-Type correto — se não coincidir com o arquivo, a Meta recusa; e respeitar os limites da Meta: imagem JPEG/PNG 5 MB, áudio (aac · amr · mp3 · m4a · ogg só OPUS) 16 MB, vídeo mp4/3gpp 16 MB, documento (pdf · doc/docx · xls/xlsx · ppt/pptx · txt) 100 MB.
⚠ A armadilha mais comum: Google Drive, Dropbox e similares NÃO servem. Um link de "compartilhar" devolve uma página HTML intermediária, não os bytes do arquivo: a Meta baixa essa página e recusa o envio. Sirva-o a partir de uma URL que responda com o arquivo direto.
curl -X POST https://api.waiaconnect.com/v1/messages \
-H "Authorization: Bearer wc_live_YOUR_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"connectionId":"conn_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","to":"5493511234567","type":"image","image":{"link":"https://example.com/product.jpg","caption":"New product 📦"}}'
# document: {"type":"document","document":{"link":"https://…/invoice.pdf","filename":"invoice.pdf","caption":"Your invoice"}}
# audio (no caption): {"type":"audio","audio":{"link":"https://…/note.ogg"}}
# video: {"type":"video","video":{"link":"https://…/demo.mp4","caption":"Demo"}}
# by media id instead of a link: {"image":{"id":"1234567890"}}
# ...but the only ids that exist are the ones you RECEIVED (from a message.received
# event); there is no upload endpoint. To send a file of your own, host it and link it.⚠ Um identificador que chega num webhook vence na Meta em poucos dias (o prazo é definido pela Meta). Vencido, não serve nem para baixá-lo nem para reenviá-lo.
A Meta guarda o seu link em cache por 10 minutos. Se você trocar o arquivo por trás da MESMA URL, acrescente uma query string aleatória (?v=…) ou vai mandar o antigo.
Se o link não servir, você fica sabendo em dois momentos. (1) Ao aceitar, com um 400 e o seu code: MEDIA_LINK_OR_ID_REQUIRED (não mandou nenhum), MEDIA_LINK_AND_ID (mandou os dois), MEDIA_LINK_INVALID (não é uma URL https bem formada), AUDIO_CAPTION_NOT_ALLOWED.
(2) Se passou pela validação mas a Meta não conseguiu baixá-lo, a recusa chega DEPOIS do 202: consulte GET /v1/messages/:id → lastErrorCode = MEDIA_DOWNLOAD_FAILED (link inacessível ou página intermediária), MEDIA_FILE_REJECTED (formato ou tamanho) ou MEDIA_TYPE_UNSUPPORTED. O que fazer: servir o arquivo direto, com o seu Content-Type, dentro dos limites acima.
Para baixar um arquivo que mandaram para você, veja «Baixar mídia recebida».