waiaconnect

Documentación / Completo

Enviar una plantilla con un archivo en el encabezado

POST /v1/messages

Markdown

Es el caso de uso más común de una plantilla de utilidad: mandar la factura. Una plantilla de Meta puede llevar un encabezado con un documento, una imagen o un video, y ese archivo se elige en cada envío — la plantilla aprobada define el TIPO, vos ponés el archivo.

Cómo se manda. Igual que cualquier plantilla (POST /v1/messages con type: "template"), pero el array components lleva primero un componente header con el archivo:

{ "type": "header", "parameters": [ { "type": "document", "document": { "link": "https://…/A-1042.pdf", "filename": "Factura A-1042.pdf" } } ] }

De dónde sale el archivo: de un enlace tuyo. Meta acepta link (una URL https pública que descarga ella) o id (un identificador de media). Connect no almacena archivos y no tiene endpoint de subida, así que los únicos id que existen son los de archivos que RECIBISTE — para mandar una factura tuya, hospedala y mandá el enlace. Es el mismo criterio que en «Enviar media», y por la misma razón: no guardamos contenido.

El tipo del parámetro lo manda la PLANTILLA, no el archivo. Si Meta aprobó el encabezado como DOCUMENT, el parámetro va como document aunque el archivo sea un JPG. Mandar image sobre una plantilla DOCUMENT lo rechaza Meta.

filename es sólo para documentos (es el nombre que ve quien lo recibe). En imagen y video, Meta lo rechaza.

🔴 Lo que NO se puede: CREAR una plantilla con encabezado de archivo desde Connect. Sólo se crean encabezados de texto. Si mandás un header.format de imagen/documento/video a POST /v1/templates, te contestamos 400 TEMPLATE_PRECHECK_FAILED con el código TPL_HEADER_FORMAT_UNSUPPORTEDno la creamos a medias ni te decimos que sí. El camino es: creála en el Administrador comercial de Meta (ahí sí acepta encabezados de archivo) y después POST /v1/templates/sync — Connect la lee bien, la lista, te avisa cuando cambia de estado, y la envía como dice arriba.

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","components":[{"type":"header","parameters":[{"type":"document","document":{"link":"https://your-server.example/invoices/A-1042.pdf","filename":"Factura A-1042.pdf"}}]},{"type":"body","parameters":[{"type":"text","text":"María"}]}]}}'
# The header component goes FIRST and its type is the one the APPROVED TEMPLATE
# declares (document | image | video) — NOT the file's mime type.
# image: {"type":"image","image":{"link":"https://…/photo.jpg"}}   (no filename)
# by media id instead of a link: {"document":{"id":"1234567890"}}
# ⚠ You cannot CREATE a media-header template from Connect (only TEXT headers).
#   Create it in Meta Business Manager, then POST /v1/templates/sync.
// The header component goes FIRST; its type is the one the APPROVED TEMPLATE declares
// (document | image | video), not the file's mime. `filename` applies to documents only.
const res = await fetch("https://api.waiaconnect.com/v1/messages", {
  method: "POST",
  headers: {
    "Authorization": "Bearer wc_live_YOUR_API_KEY",
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
  "connectionId": "conn_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "to": "5493511234567",
  "type": "template",
  "template": {
    "name": "envio_factura",
    "language": "es",
    "components": [
      {
        "type": "header",
        "parameters": [
          {
            "type": "document",
            "document": {
              "link": "https://your-server.example/invoices/A-1042.pdf",
              "filename": "Factura A-1042.pdf"
            }
          }
        ]
      },
      {
        "type": "body",
        "parameters": [
          {
            "type": "text",
            "text": "María"
          }
        ]
      }
    ]
  }
})
});
console.log(await res.json()); // { id: "msg_…", status: "queued" }
<?php
// The header component goes FIRST; its type is the one the APPROVED TEMPLATE declares.
$ch = curl_init("https://api.waiaconnect.com/v1/messages");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer wc_live_YOUR_API_KEY",
    "Idempotency-Key: " . bin2hex(random_bytes(16)),
    "Content-Type: application/json",
  ],
  CURLOPT_POSTFIELDS => json_encode(["connectionId" => "conn_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","to" => "5493511234567","type" => "template","template" => ["name" => "envio_factura","language" => "es","components" => [["type" => "header","parameters" => [["type" => "document","document" => ["link" => "https://your-server.example/invoices/A-1042.pdf","filename" => "Factura A-1042.pdf"]]]],["type" => "body","parameters" => [["type" => "text","text" => "María"]]]]]]),
]);
echo curl_exec($ch);
import requests, uuid
# The header component goes FIRST; its type is the one the APPROVED TEMPLATE declares
# (document | image | video), not the file's mime. "filename" applies to documents only.
res = requests.post("https://api.waiaconnect.com/v1/messages",
  headers={
    "Authorization": "Bearer wc_live_YOUR_API_KEY",
    "Idempotency-Key": str(uuid.uuid4()),
  },
  json={"connectionId":"conn_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","to":"5493511234567","type":"template","template":{"name":"envio_factura","language":"es","components":[{"type":"header","parameters":[{"type":"document","document":{"link":"https://your-server.example/invoices/A-1042.pdf","filename":"Factura A-1042.pdf"}}]},{"type":"body","parameters":[{"type":"text","text":"María"}]}]}})
print(res.json())

Cómo saber si una plantilla lleva archivo: GET /v1/templates devuelve header: { format: "DOCUMENT" } (o IMAGE/VIDEO). En el panel están marcadas con 📎.

Si te olvidás del archivo te lo decimos ANTES de Meta: 422 TEMPLATE_HEADER_MEDIA_REQUIRED, con details.example = el componente exacto que falta. Meta, en cambio, contesta un 132000 opaco.

Y si llega igual (una plantilla que todavía no sincronizamos), el mensaje falla en el primer intento con lastErrorCode = TEMPLATE_PARAM_MISMATCH — no se reintenta cinco veces un payload que no puede funcionar.

Qué necesita el enlace: ser https, apuntar directo al archivo (sin login ni página intermedia) y traer el Content-Type correcto. ⚠ Un enlace de "compartir" de Google Drive o Dropbox devuelve una página HTML: Meta la descarga y rechaza el envío. Límites de Meta: documento 100 MB, imagen 5 MB, video 16 MB.

Meta cachea tu enlace 10 minutos: si cambiás el archivo detrás de la misma URL, agregale un ?v=… distinto.