Actualizado el 29/10/2026
Para crear una plantilla con la API de WhatsApp, haces POST /<WABA_ID>/message_templates con name, language, category y el arreglo components, incluyendo un valor de ejemplo para cada variable. Meta responde con el id de la plantilla y el estado PENDING; cuando termina la revisión, llega el webhook message_template_status_update. Para editar, usa POST /<TEMPLATE_ID>; para eliminar, DELETE /<WABA_ID>/message_templates?name=..., con hsm_id si quieres borrar un solo idioma.
Esta guía muestra los payloads de cada operación, los límites de edición y eliminación y los errores que más traban a quien automatiza plantillas. El concepto de plantilla y sus reglas de uso están en la guía de plantillas de la API de WhatsApp; aquí el foco es el ciclo de vida por API.
Dónde viven las plantillas después del 23/09/2026
Desde el 23 de septiembre de 2026, Meta separa la antigua cuenta de WhatsApp Business (WABA) en cuenta de WhatsApp (el número y el perfil) y cuenta de mensajes (plantillas, facturación y webhooks). La documentación de plantillas ya habla de «Messaging account»: las plantillas son activos de la cuenta de mensajes, y cada cuenta de mensajes tiene las suyas.
En la práctica, el código no cambia. Las cuentas de mensajes heredaron el ID de la WABA, y los ejemplos de la documentación siguen usando /<WABA_ID>/message_templates. Es decir: el mismo ID que ya usabas sirve para crear, listar y eliminar. La consecuencia que importa es otra: las plantillas no se comparten entre cuentas de mensajes. Si dos proveedores usan el mismo número, cada uno tiene su cuenta de mensajes y necesita las plantillas creadas en ella. La estructura completa está en cuenta de WhatsApp vs. cuenta de mensajes.
Antes de empezar
- ID de la cuenta: el WABA ID (o ID de la cuenta de mensajes, que es el mismo). Si no sabes dónde está, revisa dónde encontrar el Phone Number ID y el WABA ID.
- Token: necesita el permiso
whatsapp_business_management. Según la documentación, con solowhatsapp_business_messagingel borrado devuelve un error con código 200. Para producción, usa un token permanente de usuario del sistema. - Versión de la Graph API: los ejemplos usan
v26.0. Cámbiala por la que uses. - Límites de la cuenta: hasta 100 plantillas creadas por hora por cuenta de mensajes. El total es de 250 plantillas si el portafolio comercial no está verificado y de hasta 6.000 si está verificado y tiene nombre visible aprobado en al menos una cuenta de WhatsApp.
¿Quieres probar mientras lees? Crea tu cuenta en Vectalk y envía el primer mensaje por la API oficial. Crear cuenta gratis →
Los campos de la creación
| Campo | Obligatorio | Qué es |
|---|---|---|
name | Sí | Minúsculas, números y guion bajo; hasta 512 caracteres |
language | Sí | Código de idioma, como es_MX, es_AR o pt_BR |
category | Sí | MARKETING, UTILITY o AUTHENTICATION |
components | En la práctica, sí | HEADER, BODY, FOOTER y BUTTONS |
parameter_format | No | POSITIONAL ({{1}}) o NAMED ({{nombre}}) |
message_send_ttl_seconds | No | Tiempo de vida del mensaje enviado |
library_template_name | No | Crea a partir de un modelo de la biblioteca de Meta |
El nombre no es único: puedes tener pedido_enviado en es_MX y en pt_BR, y cada combinación nombre + idioma es una plantilla con ID propio. La respuesta de la creación trae tres campos:
{
"id": "1252715608684590",
"status": "PENDING",
"category": "UTILITY"
}
Guarda el id. Es la clave para consultar, editar y eliminar solo ese idioma.
Ejemplo 1: utilidad con variables posicionales
Cada variable necesita un valor de ejemplo; sin eso, la creación falla. En el cuerpo, los ejemplos van en example.body_text, un arreglo dentro de otro arreglo, en el orden de las variables.
curl -X POST "https://graph.facebook.com/v26.0/<WABA_ID>/message_templates" \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "pedido_enviado",
"language": "es_MX",
"category": "UTILITY",
"components": [
{
"type": "HEADER",
"format": "TEXT",
"text": "Pedido {{1}} enviado",
"example": { "header_text": ["#4821"] }
},
{
"type": "BODY",
"text": "Hola, {{1}}. Tu pedido va en camino y debería llegar a más tardar el {{2}}. Número de guía: {{3}}.",
"example": { "body_text": [["Ana", "05/11", "MX123456789"]] }
},
{
"type": "FOOTER",
"text": "Responde este mensaje si necesitas ayuda"
}
]
}'
El encabezado de texto acepta una sola variable. Su ejemplo va en header_text (arreglo simple) y el del cuerpo en body_text (arreglo de arreglos). Confundir uno con otro es un error frecuente.
Ejemplo 2: variables con nombre
Con parameter_format: "NAMED", las variables tienen nombre en lugar de número, y los ejemplos pasan a body_text_named_params (en el encabezado, header_text_named_params), cada uno con param_name y example:
{
"name": "factura_disponible",
"language": "es_MX",
"category": "UTILITY",
"parameter_format": "NAMED",
"components": [
{
"type": "BODY",
"text": "Hola, {{primer_nombre}}. Tu factura {{numero_factura}} vence el {{vencimiento}}.",
"example": {
"body_text_named_params": [
{ "param_name": "primer_nombre", "example": "Carlos" },
{ "param_name": "numero_factura", "example": "2026-10-0193" },
{ "param_name": "vencimiento", "example": "10/11/2026" }
]
}
}
]
}
Las variables con nombre hacen el envío más legible y evitan cambiar {{2}} por {{3}} cuando alguien reescribe el texto. En los ejemplos de Meta, los nombres usan minúsculas y guion bajo; sigue el mismo patrón. El formato también vale para el envío: una plantilla NAMED se envía con parameter_name en cada parámetro, como mostramos en cómo enviar una plantilla por la API.
Un recordatorio de factura de tu propio cliente, como el del ejemplo, es utilidad. Cobrar deudas en nombre de terceros está prohibido por la Política de Mensajería Comercial, en cualquier categoría.
Ejemplo 3: encabezado con imagen, video o documento
Para un encabezado multimedia, el ejemplo no es una URL: es un handle generado por la Resumable Upload API de la Graph API. Son dos llamadas, y la primera usa el App ID de tu app de Meta, no el WABA ID.
Paso 1, abrir la sesión de carga:
curl -X POST "https://graph.facebook.com/v26.0/<APP_ID>/uploads?file_name=banner.jpg&file_length=184320&file_type=image/jpeg" \
-H "Authorization: Bearer <TOKEN>"
La respuesta trae {"id": "upload:<UPLOAD_SESSION_ID>"}. Paso 2, enviar el archivo binario a esa sesión:
curl -X POST "https://graph.facebook.com/v26.0/upload:<UPLOAD_SESSION_ID>" \
-H "Authorization: OAuth <TOKEN>" \
-H "file_offset: 0" \
--data-binary @banner.jpg
Fíjate en el encabezado Authorization: OAuth, tal como aparece en la documentación. La respuesta trae {"h": "4::aW..."}, y ese valor va en example.header_handle:
{
"type": "HEADER",
"format": "IMAGE",
"example": { "header_handle": ["4::aW..."] }
}
La Resumable Upload API acepta application/pdf, image/jpeg, image/jpg, image/png y video/mp4. En format usa IMAGE, VIDEO o DOCUMENT (el formato GIF existe solo para la Marketing Messages API). El archivo de ejemplo es solo para la revisión: en el envío mandas el archivo real de cada destinatario.
Ejemplo 4: marketing con botones
Los botones van en un único componente BUTTONS. Esta plantilla de campaña combina URL con sufijo dinámico, código de cupón y respuesta rápida:
{
"name": "cupon_buen_fin",
"language": "es_MX",
"category": "MARKETING",
"components": [
{
"type": "BODY",
"text": "{{1}}, llegó el Buen Fin: {{2}} de descuento en toda la tienda hasta el domingo.",
"example": { "body_text": [["Marina", "20%"]] }
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "URL",
"text": "Ver ofertas",
"url": "https://tienda.ejemplo.com.mx/ofertas?c={{1}}",
"example": ["bf2026"]
},
{ "type": "COPY_CODE", "example": "BUENFIN20" },
{ "type": "QUICK_REPLY", "text": "No más promociones" }
]
}
]
}
La URL acepta una variable, siempre al final. El ejemplo de la URL es un arreglo; el de COPY_CODE es un string. Si mezclas respuestas rápidas con otros tipos de botón, deben ir agrupadas (todas antes o todas después de las demás); si no, la API devuelve error. Las tarjetas con imagen y botón usan otro formato, la plantilla carrusel.
Límites de los componentes
| Elemento | Límite |
|---|---|
| Texto del encabezado | 60 caracteres, 1 variable |
| Cuerpo | 1.024 caracteres |
| Pie de página | 60 caracteres |
| Texto de botón (URL, teléfono, respuesta rápida) | 25 caracteres |
| Ejemplo de código de cupón | 20 caracteres |
| URL del botón | 2.000 caracteres, 1 variable |
| Botones en total | 10 |
| Botones de URL | 2 |
| Botones de teléfono | 1 |
| Botones de copiar código | 1 |
La documentación advierte además que las plantillas con 4 o más botones, o con respuesta rápida junto a otro tipo de botón, no se ven en WhatsApp para escritorio. No uses Markdown en el encabezado de texto.
Categoría: qué pasa si te equivocas
La categoría que declaras no es la última palabra. Desde el 9 de abril de 2025, si envías una plantilla como UTILITY y Meta concluye que es de marketing, se aprueba como MARKETING, sin necesidad del antiguo allow_category_change: true (ese comportamiento pasó a ser el predeterminado). En otros casos, la plantilla vuelve REJECTED con el motivo INCORRECT_CATEGORY.
Meta también revisa plantillas ya aprobadas. Antes de cambiar la categoría de una, dispara el webhook template_category_update con el campo correct_category; después del cambio, el evento trae previous_category y new_category. Para cambios de utilidad a marketing, la documentación habla de un aviso con 1 día de anticipación, que no se da a empresas ya advertidas por uso indebido. Tienes 60 días para apelar en Business Support Home.
La diferencia de precio hace que valga la pena acertar. En Brasil, desde el 1/10/2026, un mensaje de marketing cuesta R$ 0,3217 (reales brasileños) contra R$ 0,0350 uno de utilidad; las tarifas de tu país están en la lista de precios de Meta. Lee cómo Meta clasifica las plantillas antes de automatizar la creación.
Consultar el estado
Para una plantilla específica, pide los campos que te interesan:
curl "https://graph.facebook.com/v26.0/<TEMPLATE_ID>?fields=name,language,status,category,rejected_reason,quality_score" \
-H "Authorization: Bearer <TOKEN>"
Para listar, usa GET /<WABA_ID>/message_templates. Sin fields, la respuesta trae name, parameter_format, components, language, status, category, sub_category e id. Puedes filtrar y paginar:
curl "https://graph.facebook.com/v26.0/<WABA_ID>/message_templates?fields=name,status,category&status=rejected&limit=50" \
-H "Authorization: Bearer <TOKEN>"
La respuesta trae data y paging, con los cursores before y after y la URL next. Sigue next hasta que ya no exista.
Recibir cambios de estado por webhook
Consultar en bucle funciona, pero lo correcto es suscribir el webhook de tu app al campo message_template_status_update. El evento llega con field: "message_template_status_update" y, en value, los campos event, message_template_id, message_template_name, message_template_language, message_template_category y reason. Un ejemplo con valores ilustrativos:
{
"object": "whatsapp_business_account",
"entry": [{
"id": "<WABA_ID>",
"time": 1761730000,
"changes": [{
"field": "message_template_status_update",
"value": {
"event": "REJECTED",
"message_template_id": 1252715608684590,
"message_template_name": "cupon_buen_fin",
"message_template_language": "es_MX",
"message_template_category": "MARKETING",
"reason": "INVALID_FORMAT",
"rejection_info": {
"reason": "...",
"recommendation": "..."
}
}
}]
}]
}
Los valores de event más útiles para automatizar:
event | Qué hacer |
|---|---|
APPROVED | Habilitar la plantilla en tu sistema |
REJECTED | Leer reason (y rejection_info en casos de INVALID_FORMAT), corregir y editar |
PAUSED / FLAGGED | Avisar al equipo; la plantilla tiene calidad baja |
DISABLED | Dejar de usarla; disable_info trae la fecha |
REINSTATED / UNARCHIVED | Volver a habilitarla |
ARCHIVED | Archivada por inactividad; se elimina en 28 días si no se desarchiva |
LIMIT_EXCEEDED | La cuenta llegó al límite de plantillas |
Los motivos de reason incluyen INCORRECT_CATEGORY, TAG_CONTENT_MISMATCH, INVALID_FORMAT, PROMOTIONAL, ABUSIVE_CONTENT y SCAM. Las correcciones más comunes están en plantilla rechazada: motivos y cómo corregir, y las pausas en plantilla pausada o desactivada.
Editar una plantilla
La edición es un POST al ID de la plantilla, y el arreglo components que envías reemplaza todos los componentes. No se puede editar un componente aislado: manda la plantilla completa.
curl -X POST "https://graph.facebook.com/v26.0/<TEMPLATE_ID>" \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"components": [
{
"type": "BODY",
"text": "Hola, {{1}}. Tu pedido va en camino y llega a más tardar el {{2}}. Guía: {{3}}.",
"example": { "body_text": [["Ana", "05/11", "MX123456789"]] }
}
]
}'
La respuesta es {"success": true}. Las reglas de la página de gestión de plantillas:
- Se pueden editar plantillas
APPROVED,REJECTEDoPAUSED(la referencia de la API menciona aprobadas y rechazadas). - Los campos editables son categoría, componentes y tiempo de vida, pero la categoría de una plantilla aprobada no se puede editar.
- Una plantilla aprobada se puede editar como máximo una vez cada 24 horas y hasta 10 veces en 30 días. Las rechazadas y pausadas no tienen ese límite.
- Después de editarse, una plantilla aprobada o pausada vuelve a revisión; si la pasa, sigue aprobada.
Si necesitas cambiar el nombre, el idioma o la categoría de una plantilla aprobada, crea una plantilla nueva.
Eliminar plantillas
El borrado usa el endpoint de listado con DELETE. Hay tres formas:
# Borra TODOS los idiomas con ese nombre
curl -X DELETE "https://graph.facebook.com/v26.0/<WABA_ID>/message_templates?name=pedido_enviado" \
-H "Authorization: Bearer <TOKEN>"
# Borra solo la plantilla con ese ID (un idioma)
curl -X DELETE "https://graph.facebook.com/v26.0/<WABA_ID>/message_templates?hsm_id=1252715608684590&name=pedido_enviado" \
-H "Authorization: Bearer <TOKEN>"
La tercera es hsm_ids, con un arreglo JSON de hasta 100 IDs. No se puede combinar con name ni con hsm_id, y un solo ID inválido hace fallar toda la solicitud.
Qué pasa después:
- Los mensajes ya enviados y aún no entregados dejan la plantilla en
PENDING_DELETION, y WhatsApp intenta entregarlos durante 30 días. - El nombre de una plantilla aprobada eliminada no se puede reutilizar durante 30 días.
- Las plantillas desactivadas (
DISABLED) no se pueden eliminar. - Eliminar un nombre que no existe devuelve error.
Antes de correr una limpieza, recuerda que Meta archiva las plantillas sin uso durante 12 meses y programa su eliminación 28 días después. A veces la limpieza manual ni siquiera hace falta.
Errores comunes al automatizar
- Falta un ejemplo: cada variable necesita uno, incluida la de la URL del botón.
header_textcomo arreglo de arreglos, obody_textcomo arreglo simple: los formatos son distintos.- Una URL en
header_handle: el ejemplo multimedia es el handle de la Resumable Upload API. - WABA ID en la carga: la sesión de carga se abre con el App ID.
- Token sin
whatsapp_business_management: puede listar o crear, pero el borrado falla con código 200. - Más de 100 creaciones por hora en una cuenta de mensajes.
- Recrear con el mismo nombre justo después de borrar una aprobada: espera 30 días o usa otro nombre.
- Editar una aprobada dos veces el mismo día: la segunda edición choca con el límite de 24 horas.
Cómo hacerlo con Vectalk
Vectalk es un proxy de la API oficial de WhatsApp, y las mismas operaciones están en su API, con token de instancia (el WABA ID se resuelve con el token, así que no va en la URL):
| Operación | Ruta en Vectalk |
|---|---|
| Listar o filtrar por nombre | GET https://apis.vectalk.com.br/api/templates?name=pedido_enviado |
| Crear | POST https://apis.vectalk.com.br/api/templates |
Editar (solo APPROVED o REJECTED) | POST https://apis.vectalk.com.br/api/templates/{templateId} |
| Eliminar por nombre | DELETE https://apis.vectalk.com.br/api/templates?name=pedido_enviado |
El cuerpo de la creación usa los mismos campos de Meta (name, language, category, components), y la respuesta trae id, status: "PENDING" y category:
curl -X POST https://apis.vectalk.com.br/api/templates \
-H "Authorization: Bearer <TOKEN_DE_INSTANCIA>" \
-H "Content-Type: application/json" \
-d '{
"name": "pedido_enviado",
"language": "es_MX",
"category": "UTILITY",
"components": [
{
"type": "BODY",
"text": "Hola, {{1}}. Tu pedido va en camino y debería llegar a más tardar el {{2}}.",
"example": { "body_text": [["Ana", "05/11"]] }
}
]
}'
Los tokens de organización no acceden a plantillas; crea un token vinculado a la instancia en Configuración → API Tokens. Si prefieres no escribir JSON, el panel tiene la sección de plantillas con la Biblioteca de Modelos. Los detalles de cada ruta están en la documentación de plantillas de Vectalk (en portugués), y la cuenta se crea en apps.vectalk.com.br/register. Para ideas de texto por sector, mira los 60 ejemplos de plantillas.
Preguntas frecuentes
¿Cuánto tarda Meta en aprobar una plantilla creada por API?
La página de gestión de plantillas no fija un plazo único. La plantilla nace PENDING, y te enteras del resultado por el webhook message_template_status_update o consultando GET /<TEMPLATE_ID>?fields=status. No armes un flujo que dependa de una aprobación inmediata.
¿Uso el WABA ID o el ID de la cuenta de mensajes?
Son el mismo número. En la separación del 23/09/2026, las cuentas de mensajes heredaron el ID de la WABA, y la documentación de plantillas sigue usando /<WABA_ID>/message_templates.
¿Puedo cambiar la categoría de una plantilla aprobada?
No con la edición: la documentación dice que la categoría de una plantilla aprobada no se puede editar. Crea otra plantilla con la categoría correcta. Quien sí puede recategorizar una aprobada es Meta, y lo avisa con el webhook template_category_update.
¿Cómo creo la misma plantilla en varios idiomas?
Haz un POST por idioma con el mismo name y otro language (por ejemplo, es_MX, es_AR y es_CO). Cada una recibe su id. Para borrar un solo idioma, usa name + hsm_id.
¿El cliente ve la imagen de ejemplo?
No. El handle de ejemplo sirve solo para la revisión de Meta. En el envío pasas el archivo real en el componente header, por link o media ID.
Fuentes oficiales consultadas el 29/10/2026: Templates overview (Meta), Template management (Meta), Message Templates API (Meta), Template components (Meta), Template categorization (Meta), Webhook message_template_status_update (Meta), Resumable Upload API (Meta), Actualizaciones de las cuentas de WhatsApp Business (Meta), Plantillas en la API de Vectalk.
Pon a funcionar el WhatsApp oficial de tu empresa
Crea tu cuenta, conecta tu número a la API oficial (puedes mantener la app con coexistencia) y empieza a atender y enviar mensajes.
