Documentation / Complete
Templates with an image, a document and buttons
POST /v1/messages
Building with AI? Connect it to your account over MCP: it checks your setup, creates webhooks and templates with your confirmation and writes the code for you. See how →
Send a rich template without building Meta's components. On POST /v1/messages with type: "template", instead of components you can send three fields and Connect builds the rest, exactly:
- header — { "type": "document", "link": "https://…/A-1042.pdf", "filename": "Invoice A-1042.pdf" }. Also image and video (with mediaId or link), text ({ "type": "text", "text": "…" }) and location (latitude, longitude, name, address). filename is for documents only: it's the name the contact sees.
- body — a list for {{1}}, {{2}}… (["María", "A-1042"]) or, if the template uses named variables, an object ({ "name": "María" }).
- buttons — the buttons that take a value: url → text (the end of the dynamic URL), copy_code → code, quick_reply → payload (it comes back in the message.received when the contact taps it). We infer the position (index) from the approved template; if we haven't synced it yet, send it yourself (from 0).
Every mistake comes back at once, before Meta. If we know the template, we check how many variables it has, their names, its buttons and the header type: 400 TEMPLATE_SEND_INVALID with details.errors[], each with its field (the path in your JSON) and the message in your language. Nothing is sent to Meta.
The journey: create the template (with a file or buttons: POST /v1/templates, see “Create templates with an image, document and buttons”) → POST /v1/templates/sync → wait for the template.status_changed webhook with APPROVED → send it as above → follow it with GET /v1/messages/{id} or the message.sent / delivered / read / failed webhooks.
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":"template","template":{"name":"envio_factura","language":"es","header":{"type":"document","link":"https://your-server.example/invoices/A-1042.pdf","filename":"Factura A-1042.pdf"},"body":["María","A-1042"],"buttons":[{"type":"url","text":"A-1042"}]}}'
# header: image | video | document (mediaId OR link; filename = documents only) | text | location
# body: ["v1","v2"] for {{1}},{{2}} — or {"name":"v1"} for named {{variables}}
# buttons: url → text (the URL suffix) · copy_code → code · quick_reply → payload
# Every mistake comes back at once: 400 TEMPLATE_SEND_INVALID, details.errors[].fieldcomponents keeps working as before. Send one or the other, not both.
Standalone media (within 24 h): type image, document, video, audio or sticker (WEBP, no caption), with link or id (also mediaId).
If Meta pauses or disables the template for its quality, the message fails with TEMPLATE_PAUSED or TEMPLATE_DISABLED and is not retried.
The link must be https and point straight at the file (a Drive "share" link won't work). Meta's limits: document 100 MB, image 5 MB, video 16 MB.