Skip to Content
APIPlantillas

Plantillas

GET /v1/channels/{channel_id}/templates te devuelve las plantillas de WhatsApp de un canal con todo lo que el envío necesita: el nombre, el idioma, cómo se llaman sus variables, la categoría y en qué estado de aprobación está cada una.

Sirve para no tener que adivinar. Enviar una plantilla exige que content.name, content.language y las claves de content.variables coincidan exactamente con lo que Meta aprobó; si no coinciden, el envío se rechaza con 422 template_variables_mismatch. Con este listado, tu código puede construir la request a partir de lo que la plantilla declara, en vez de tener esos valores escritos a mano en algún lado.

La llamada

Necesitas una API key con el permiso channels:read (el mismo de GET /v1/channels).

curl https://api.alttos.ai/api/public/v1/channels/1b1a6d68-6e78-4717-aa04-5ace8614e907/templates \ -H "Authorization: Bearer $ALTTOS_API_KEY"
{ "data": [ { "name": "recordatorio_cita", "language": "es", "status": "APPROVED", "category": "UTILITY", "parameter_format": "positional", "body": "Hola {{1}}, te recordamos tu cita del {{2}}.", "variables": [ { "key": "1", "example": "Ana" }, { "key": "2", "example": "12 de marzo a las 9:00" } ], "header": { "format": "TEXT", "text": "Tu cita", "variables": [] }, "footer": "Responde CANCELAR para anularla", "buttons": [ { "type": "URL", "text": "Ver mi cita", "url": "https://tu-sitio.com/citas", "phone_number": null } ], "rejected_reason": null } ] }

La lista no se pagina: viene completa, en el orden en que la devuelve Meta. A diferencia de /v1/channels y /v1/contacts, que sí usan cursor, esta no lo va a necesitar: no sale de una tabla nuestra sino del catálogo de plantillas de tu cuenta de WhatsApp, que Meta ya acota. Puedes recorrer data entero sin preocuparte por páginas.

Los campos

CampoQué es
nameLo que va en content.name al enviar.
languageLo que va en content.language. La misma plantilla puede estar aprobada en varios idiomas: cada idioma es una entrada propia de esta lista, con su propio estado.
statusEstado de aprobación en Meta. Solo APPROVED se puede enviar.
categoryUTILITY, MARKETING o AUTHENTICATION. Decide el costo y si aplica el opt-out de promociones del destinatario.
parameter_formatpositional o named: decide cómo se llaman las claves de content.variables.
bodyEl cuerpo de la plantilla con sus marcadores sin sustituir.
variablesLas variables del cuerpo, en orden, como { key, example }. key es la clave exacta para content.variables; example es el valor de muestra que cargaste en Meta (o null).
headerEl encabezado: format (TEXT, IMAGE, VIDEO, DOCUMENT, LOCATION), su text si es de texto, y sus propias variables. Es null si la plantilla no tiene encabezado.
footerEl pie de la plantilla, o null.
buttonsLos botones, con su type, su text y, según el tipo, url o phone_number.
rejected_reasonPor qué Meta la rechazó, cuando status es REJECTED.

Como en toda esta API, ignora los campos que no conozcas: dentro de /v1 solo agregamos campos, nunca cambiamos el significado de los que ya existen.

Los estados

El listado trae todas tus plantillas, no solo las que se pueden enviar. Esa es la mitad de su utilidad: saber que una plantilla está en revisión es lo que te evita programar un envío que va a fallar.

statusQué significaQué hacer
APPROVEDLista para enviar.Nada.
PENDINGMeta todavía la está revisando.Esperar. Suele tardar minutos, a veces horas.
REJECTEDMeta la rechazó. rejected_reason dice por qué.Corregirla en Meta Business.
PAUSEDPausada por baja calidad (muchos bloqueos o reportes).Revisar a quién le estás escribiendo; se reactiva sola con el tiempo.
DISABLEDDeshabilitada por Meta tras pausas repetidas.Crear una plantilla nueva.

Intentar enviar cualquiera que no sea APPROVED responde 422 template_not_approved, con el estado que reportó Meta en detail.template_status.

De la plantilla al envío

El punto de todo esto es que variables te da las claves exactas. Una plantilla posicional:

{ "name": "recordatorio_cita", "language": "es", "parameter_format": "positional", "variables": [{ "key": "1", "example": "Ana" }, { "key": "2", "example": "12 de marzo" }] }

se envía así:

{ "channel_id": "1b1a6d68-6e78-4717-aa04-5ace8614e907", "to": { "phone": "+573001234567" }, "content": { "type": "template", "name": "recordatorio_cita", "language": "es", "variables": { "1": "Ana", "2": "12 de marzo a las 9:00" } } }

Y una named, con variables igual a [{ "key": "nombre" }, { "key": "fecha" }], se envía con { "nombre": "Ana", "fecha": "12 de marzo a las 9:00" }. Los valores son siempre texto: ver las variables de la plantilla.

Las variables del encabezado todavía no se pueden enviar por la API. El envío sustituye las del cuerpo únicamente. Si una plantilla trae header.variables con contenido, aparece en este listado (para que veas cómo es), pero enviarla por la API termina en un error de Meta. Por ahora esas se envían desde el panel.

Cada cuánto consultarla

No hace falta llamarla antes de cada envío. Tus plantillas cambian cuando tú las tocas en Meta, no solas: lo razonable es consultarla cuando construyes o revisas la integración, y cachear el resultado de tu lado.

Del nuestro ya hay un cache de unos minutos que se invalida en cuanto Meta nos avisa que una plantilla cambió de estado, así que una plantilla recién aprobada aparece acá en segundos. Aun así, este endpoint consume tu cuota de lecturas por minuto como cualquier otro: ver Errores y límites.

Errores

codeHTTPCuándo
channel_not_found404El canal no existe, fue eliminado, o es de otra cuenta.
channel_not_supported422El canal existe pero no es de WhatsApp. Las plantillas son de WhatsApp: un webchat o un Instagram no tienen.
channel_not_connected422El canal no está conectado a WhatsApp, o su conexión expiró. Es el mismo código que devuelve el envío ante lo mismo. Se reconecta desde el panel.
provider_rate_limited429WhatsApp nos está limitando. Listar plantillas es la llamada más cara de la API, así que es el 429 más probable acá. No trae Retry-After: usa tu propio backoff.
provider_unavailable502No pudimos consultarle a WhatsApp (error o timeout). No hay nada que corregir en tu request: reintenta con backoff.

El resto (401, 403, y el 429 por tu propia cuota de lecturas) es el de toda la API: ver Errores y límites.

Last updated on