¿Programás con IA? Conectala a tu cuenta por MCP: revisa tu configuración, crea webhooks y plantillas con tu confirmación y te escribe el código. Ver cómo →
Armá la plantilla desde tu sistema, mostrale la vista previa a tu cliente y mandala a aprobación. Cuatro llamadas, y ninguna manda un mensaje:
1. POST /v1/templates/media — el archivo de muestra que Meta revisa (PDF, JPG/PNG o MP4; multipart con file, o JSON con file en base64). Devuelve { handle, type, format, mimeType, bytes }. No es el archivo que se envía: ése va en cada envío, con link o mediaId. No guardamos el archivo. 2. POST /v1/templates/validate — el mismo chequeo que crear, sin mandar nada a Meta: ok, y en errors[] todos los problemas juntos, cada uno con su field (header.handle, buttons[1].url…) y el mensaje en tu idioma. 3. POST /v1/templates — header: { "format": "IMAGE", "handle": "…" } (también VIDEO, DOCUMENT y LOCATION) y buttons: QUICK_REPLY (text), URL (text, url, y si termina en {{1}}, example con el final de ejemplo), PHONE_NUMBER (text, phoneNumber con código de país) y COPY_CODE (example). Nace PENDING; te avisa template.status_changed (con previousStatus). 4. POST /v1/templates/preview?format=html — una página autocontenida con la burbuja de WhatsApp (sin scripts ni recursos de afuera) para mostrarle a tu cliente en un <iframe sandbox>. Sin format, la misma vista en JSON.
Los límites de Meta, chequeados antes de Meta: hasta 10 botones, 2 de URL, 1 de teléfono, 1 de copiar código; el texto de un botón hasta 25 caracteres; las respuestas rápidas juntas (al principio o al final). Si la cuenta de Meta tiene el 141006 (medio de pago) o el 141010 (verificación del negocio), la plantilla se crea igual y la respuesta trae un warning: Meta puede aprobarla, pero no vas a poder enviarla hasta arreglarlo.
# 1) The sample file Meta reviews (PDF, JPG/PNG or MP4) → its handle
HANDLE=$(curl -s -X POST https://api.waiaconnect.com/v1/templates/media -H "Authorization: Bearer wc_live_YOUR_API_KEY" -F file=@foto.png | jq -r .handle)
# 2) Validate: every error at once, nothing sent to Meta
BODY='{"connectionId":"conn_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","name":"envio_factura","language":"es","category":"UTILITY","header":{"format":"IMAGE","handle":"HANDLE"},"body":{"text":"Hola {{1}}, tu factura *{{2}}* está lista.","examples":["María","A-1042"]},"footer":{"text":"Tu negocio"},"buttons":[{"type":"QUICK_REPLY","text":"Recibido"},{"type":"URL","text":"Ver factura","url":"https://your-site.example/f/{{1}}","example":"A-1042"},{"type":"PHONE_NUMBER","text":"Llamar","phoneNumber":"+5493511234567"}]}'
BODY=${BODY/HANDLE/$HANDLE}
curl -s -X POST https://api.waiaconnect.com/v1/templates/validate -H "Authorization: Bearer wc_live_YOUR_API_KEY" -H "Content-Type: application/json" -d "$BODY"
# → { "ok": true, "errors": [], "warnings": [], "components": [...] }
# 3) Create it (born PENDING; template.status_changed tells you when Meta decides)
curl -s -X POST https://api.waiaconnect.com/v1/templates -H "Authorization: Bearer wc_live_YOUR_API_KEY" -H "Content-Type: application/json" -d "$BODY"
# 4) The WhatsApp-style preview for your customer (self-contained HTML)
curl -s -X POST "https://api.waiaconnect.com/v1/templates/preview?format=html" -H "Authorization: Bearer wc_live_YOUR_API_KEY" -H "Content-Type: application/json" -d "$BODY" > preview.html
import { readFile } from "node:fs/promises";
const H = { Authorization: "Bearer wc_live_YOUR_API_KEY" };
const J = { ...H, "Content-Type": "application/json" };
// 1) The sample file Meta reviews (PDF, JPG/PNG or MP4) → its handle
const form = new FormData();
form.append("file", new Blob([await readFile("foto.png")]), "foto.png");
const { handle } = await (await fetch("https://api.waiaconnect.com/v1/templates/media", { method: "POST", headers: H, body: form })).json();
const def = {
"connectionId": "conn_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"name": "envio_factura",
"language": "es",
"category": "UTILITY",
"header": {
"format": "IMAGE",
"handle": handle
},
"body": {
"text": "Hola {{1}}, tu factura *{{2}}* está lista.",
"examples": [
"María",
"A-1042"
]
},
"footer": {
"text": "Tu negocio"
},
"buttons": [
{
"type": "QUICK_REPLY",
"text": "Recibido"
},
{
"type": "URL",
"text": "Ver factura",
"url": "https://your-site.example/f/{{1}}",
"example": "A-1042"
},
{
"type": "PHONE_NUMBER",
"text": "Llamar",
"phoneNumber": "+5493511234567"
}
]
};
// 2) Validate: every error at once, nothing sent to Meta
const v = await (await fetch("https://api.waiaconnect.com/v1/templates/validate", { method: "POST", headers: J, body: JSON.stringify(def) })).json();
if (!v.ok) throw new Error(v.errors.map(e => `${e.field}: ${e.message}`).join("\n"));
// 3) Create it (PENDING until Meta decides → template.status_changed)
const created = await (await fetch("https://api.waiaconnect.com/v1/templates", { method: "POST", headers: J, body: JSON.stringify(def) })).json();
// 4) The WhatsApp-style preview (self-contained HTML, show it in an <iframe sandbox>)
const html = await (await fetch("https://api.waiaconnect.com/v1/templates/preview?format=html", { method: "POST", headers: J, body: JSON.stringify(def) })).text();
console.log(created.template.status, created.warning?.message ?? "");
import requests
H = {"Authorization": "Bearer wc_live_YOUR_API_KEY"}
# 1) The sample file Meta reviews (PDF, JPG/PNG or MP4) -> its handle
handle = requests.post("https://api.waiaconnect.com/v1/templates/media", headers=H, files={"file": open("foto.png", "rb")}).json()["handle"]
definition = {"connectionId":"conn_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","name":"envio_factura","language":"es","category":"UTILITY","header":{"format":"IMAGE","handle":handle},"body":{"text":"Hola {{1}}, tu factura *{{2}}* está lista.","examples":["María","A-1042"]},"footer":{"text":"Tu negocio"},"buttons":[{"type":"QUICK_REPLY","text":"Recibido"},{"type":"URL","text":"Ver factura","url":"https://your-site.example/f/{{1}}","example":"A-1042"},{"type":"PHONE_NUMBER","text":"Llamar","phoneNumber":"+5493511234567"}]}
# 2) Validate: every error at once, nothing sent to Meta
v = requests.post("https://api.waiaconnect.com/v1/templates/validate", headers=H, json=definition).json()
if not v["ok"]:
raise SystemExit("\n".join(f"{e['field']}: {e['message']}" for e in v["errors"]))
# 3) Create it (PENDING until Meta decides -> template.status_changed)
created = requests.post("https://api.waiaconnect.com/v1/templates", headers=H, json=definition).json()
# 4) The WhatsApp-style preview (self-contained HTML)
open("preview.html", "w").write(requests.post("https://api.waiaconnect.com/v1/templates/preview", params={"format": "html"}, headers=H, json=definition).text)
Para cambiarla después: PATCH /v1/templates/:id (aprobada, rechazada o pausada; la aprobada, 1 vez cada 24 h y con la misma categoría).
Con 4 botones o más, WhatsApp muestra 2 y «Ver todas las opciones»: la vista previa lo dibuja igual.