Actualizado el 29/10/2026
Para enviar una plantilla (template) por la API de WhatsApp, haz un POST a /{phone-number-id}/messages con "type": "template", el name exacto de la plantilla aprobada, el language.code con el que se creó y un arreglo components con los valores de las variables del encabezado, del cuerpo y de los botones. El texto fijo no se envía: solo mandas los parámetros, y deben coincidir en cantidad, formato y orden con lo aprobado.
Esta guía trae un payload listo para cada caso: solo texto con variables posicionales y nombradas, encabezado con imagen, video o documento (por link y por id), botón de URL dinámica, respuesta rápida con payload, código de cupón y un ejemplo combinado. Al final, la tabla de errores de parámetros más comunes y cómo hacer el mismo envío con Vectalk.
Anatomía de un envío de plantilla
Las plantillas usan el mismo endpoint de la Cloud API que los demás mensajes:
POST https://graph.facebook.com/v26.0/{PHONE_NUMBER_ID}/messages
Authorization: Bearer {ACCESS_TOKEN}
Content-Type: application/json
El cuerpo siempre tiene la misma estructura base:
{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "573001234567",
"type": "template",
"template": {
"name": "nombre_de_la_plantilla",
"language": { "code": "es_MX" },
"components": []
}
}
Dentro de components, cada objeto tiene un type que apunta a una parte de la plantilla:
type del componente | Cuándo incluirlo | Qué va en parameters |
|---|---|---|
header | Encabezado con variable de texto o multimedia | text, image, video, document o location |
body | Cuerpo con variables | text (y también currency y date_time) |
button | Botón con valor dinámico | Depende del sub_type: url, quick_reply, copy_code |
Si la plantilla no tiene ninguna variable, puedes omitir components. El pie (footer) nunca aparece en el envío, porque no admite variables. Si todavía no enviaste ningún mensaje por la API, empieza por cómo enviar un mensaje por la API de WhatsApp con cURL, Node.js y Python.
1. Plantilla de texto con variables posicionales
Es el formato predeterminado: si nadie eligió otro formato al crearla, la plantilla usa variables posicionales, {{1}}, {{2}} y así sucesivamente. Según la documentación de plantillas de Meta, los valores deben enviarse en el mismo orden en que aparecen los marcadores en el texto.
Plantilla aprobada (cuerpo):
¡Hola {{1}}! Tu pedido {{2}} está confirmado y llega en {{3}} días hábiles.
Envío:
curl -X POST "https://graph.facebook.com/v26.0/$PHONE_NUMBER_ID/messages" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "573001234567",
"type": "template",
"template": {
"name": "pedido_confirmado",
"language": { "code": "es_MX" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Mariana" },
{ "type": "text", "text": "#48213" },
{ "type": "text", "text": "3" }
]
}
]
}
}'
Tres marcadores, tres objetos text, en orden. Si falta uno, sobra uno o cambia el tipo, el envío falla (mira la tabla de errores más abajo).
¿Quieres probar mientras lees? Crea tu cuenta en Vectalk y envía el primer mensaje por la API oficial. Crear cuenta gratis →
2. Plantilla con variables nombradas (parameter_name)
Al crearla, la plantilla puede declarar "parameter_format": "named". Las variables pasan a ser nombres como {{first_name}}, que deben ser únicos y usar solo minúsculas y guion bajo. En el envío, cada parámetro lleva un parameter_name y, según Meta, el orden deja de importar.
Plantilla aprobada (cuerpo):
¡Gracias, {{first_name}}! El número de tu pedido es {{order_number}}.
Envío:
{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "573001234567",
"type": "template",
"template": {
"name": "confirmacion_pedido",
"language": { "code": "es_MX" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "parameter_name": "order_number", "text": "SKBUP2-4CPIG9" },
{ "type": "text", "parameter_name": "first_name", "text": "Jéssica" }
]
}
]
}
}
Las variables nombradas hacen el código más legible y evitan el error clásico de invertir dos valores. El formato se define al crear la plantilla, no al enviarla: mandar parameter_name a una plantilla posicional (o al revés) es un error de parámetro. Cómo definir el formato al crearla está en la guía de crear, editar y eliminar plantillas por la API.
Variables de moneda y fecha
Además de text, el cuerpo acepta parámetros currency (con fallback_value, code y amount_1000, el monto multiplicado por 1.000) y date_time (con fallback_value), como muestra la página de multimedia en plantillas. Enviar el valor ya formateado como text también funciona.
3. Encabezado con imagen, video o documento
Si la plantilla se aprobó con encabezado multimedia, el envío debe incluir el archivo en un componente header. La imagen de ejemplo usada al crearla solo sirve para la revisión; no se le envía al cliente. El type del parámetro es image, video o document, y el objeto con el mismo nombre acepta link o id:
| Opción | Cómo funciona | Cuándo usarla |
|---|---|---|
link | URL HTTPS pública del archivo en tu servidor | Archivos que cambian en cada envío (facturas, comprobantes) |
id | Media ID obtenido al subir el archivo a los servidores de Meta | El mismo archivo para muchos destinatarios (campañas) |
Meta recomienda id, para reducir errores y solicitudes a tu servidor, y advierte que el link debe estar en un servidor público o el mensaje fallará. Cuando usas link, la Cloud API guarda el archivo en caché por 10 minutos y lo reutiliza si la misma URL vuelve a aparecer en ese lapso; para forzar otra descarga, agrega una query string aleatoria a la URL, según la documentación de envío.
Imagen por link
{
"messaging_product": "whatsapp",
"to": "573001234567",
"type": "template",
"template": {
"name": "promo_imagen",
"language": { "code": "es_MX" },
"components": [
{
"type": "header",
"parameters": [
{ "type": "image", "image": { "link": "https://cdn.mitienda.com/promo/octubre.jpg" } }
]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "20%" }
]
}
]
}
}
Documento (PDF) por media ID
{
"messaging_product": "whatsapp",
"to": "573001234567",
"type": "template",
"template": {
"name": "factura_disponible",
"language": { "code": "es_MX" },
"components": [
{
"type": "header",
"parameters": [
{
"type": "document",
"document": { "id": "1013859600285441", "filename": "factura-octubre.pdf" }
}
]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Carlos" },
{ "type": "text", "text": "10/11/2026" }
]
}
]
}
}
filename es opcional y viene del objeto de documento de los mensajes de documento: la app usa la extensión para mostrar el ícono correcto. En Brasil, este patrón es común para enviar el boleto (un comprobante bancario de pago) como PDF en el encabezado.
Para video, la estructura es la misma: "type": "video" y un objeto video con link o id.
Para obtener el media ID, sube el archivo con POST /{PHONE_NUMBER_ID}/media. Límites de tamaño, formatos aceptados y el paso a paso de la subida están en cómo enviar imagen, PDF, audio y video por la API de WhatsApp.
4. Botón de URL dinámica
Un botón de URL puede tener una variable, siempre al final de la URL. Al crearlo, algo como https://mitienda.com/pedido/{{1}}. En el envío, solo mandas el sufijo:
{
"messaging_product": "whatsapp",
"to": "573001234567",
"type": "template",
"template": {
"name": "rastreo_pedido",
"language": { "code": "es_MX" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "#48213" }
]
},
{
"type": "button",
"sub_type": "url",
"index": "0",
"parameters": [
{ "type": "text", "text": "48213?utm_source=whatsapp" }
]
}
]
}
}
Tres reglas que rompen muchas integraciones:
indexes la posición del botón en la plantilla, empezando en 0. Si el botón de URL es el segundo botón aprobado, elindexes1. Meta explica que, cuando hay botones con variable, el tipo y el orden deben coincidir con la plantilla; un índice equivocado genera un error como «Parameter value for URL was expected but was not found».- Codifica los caracteres especiales antes de enviar. La página de componentes menciona espacio (
%20), dos puntos (%3A), barra vertical (%7C) y letras con tilde:Peñase convierte enPe%C3%B1a. Un valor sin codificar puede invalidar la URL y hacer fallar el envío. - Un botón de URL sin variable no entra en el envío. Solo incluye el componente
buttonsi ese botón tiene una parte dinámica.
Los ejemplos de Meta usan index a veces como texto ("0") y a veces como número (0). Elige uno y estandarízalo en tu código. Si quieres medir el clic en GA4 con UTM en ese sufijo, mira la guía de botón de URL dinámica con UTM.
5. Respuesta rápida con payload
Los botones de respuesta rápida se definen al crear la plantilla con un texto fijo (hasta 25 caracteres) y no necesitan nada en el envío. Lo que sí puedes mandar es un payload propio, que vuelve en el webhook cuando el cliente toca el botón. Así sabes exactamente qué campaña, pedido u oferta generó la respuesta.
La sintaxis aparece en la documentación de plantillas de carrusel, donde Meta describe el payload como opcional y entregado en messages.button.payload:
{
"messaging_product": "whatsapp",
"to": "573001234567",
"type": "template",
"template": {
"name": "confirmar_cita",
"language": { "code": "es_MX" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Ana" },
{ "type": "text", "text": "5/11 a las 14 h" }
]
},
{
"type": "button",
"sub_type": "quick_reply",
"index": 0,
"parameters": [
{ "type": "payload", "payload": "confirmar:cita:98765" }
]
},
{
"type": "button",
"sub_type": "quick_reply",
"index": 1,
"parameters": [
{ "type": "payload", "payload": "reprogramar:cita:98765" }
]
}
]
}
}
Cuando la clienta toca «Confirmar», llega al webhook un mensaje con "type": "button":
{
"context": { "from": "5215550000000", "id": "wamid.HBgLNTUx..." },
"from": "573001234567",
"id": "wamid.HBgLNTUy...",
"timestamp": "1761750000",
"type": "button",
"button": { "payload": "confirmar:cita:98765", "text": "Confirmar" }
}
context.id es el wamid de la plantilla enviada, y button.payload trae tu clave. En el ejemplo de la referencia del webhook de botón, sin payload propio, payload y text traen la misma etiqueta del botón. Recuerda que, al crearla, los botones de respuesta rápida deben ir agrupados (todos antes o todos después de los demás tipos), y la plantilla admite hasta 10.
6. Código de cupón (copy_code)
Las plantillas de marketing pueden tener un botón «copiar código», que copia un texto de hasta 20 caracteres al portapapeles del celular. En el envío, el botón lleva sub_type: "copy_code" y un parámetro coupon_code, según la página de plantillas de cupón:
{
"messaging_product": "whatsapp",
"to": "573001234567",
"type": "template",
"template": {
"name": "cupon_buen_fin",
"language": { "code": "es_MX" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "parameter_name": "coupon_code", "text": "BUENFIN25" },
{ "type": "text", "parameter_name": "discount", "text": "25%" }
]
},
{
"type": "button",
"sub_type": "copy_code",
"index": 0,
"parameters": [
{ "type": "coupon_code", "coupon_code": "BUENFIN25" }
]
}
]
}
}
Como en la página de Meta, el ejemplo usa una plantilla con variables nombradas en el cuerpo; en una plantilla posicional, quita parameter_name. Fíjate que el código aparece dos veces: una en el cuerpo (para que el cliente lo lea) y otra en el botón (para copiar). Son campos independientes; si generas cupones únicos por cliente, llena los dos con el mismo valor. La plantilla admite un botón de copiar código.
7. Ejemplo combinado: imagen, variables nombradas, URL y respuesta rápida
En la vida real, una plantilla de marketing suele combinar varias piezas. Supón una plantilla aprobada con encabezado de imagen, cuerpo Hola {{first_name}}, ¡llegó la colección {{collection}}!, botón 0 de URL dinámica y botón 1 de respuesta rápida «No quiero recibir»:
curl -X POST "https://graph.facebook.com/v26.0/$PHONE_NUMBER_ID/messages" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "573001234567",
"type": "template",
"template": {
"name": "coleccion_nueva",
"language": { "code": "es_MX" },
"components": [
{
"type": "header",
"parameters": [
{ "type": "image", "image": { "id": "1339522734477770" } }
]
},
{
"type": "body",
"parameters": [
{ "type": "text", "parameter_name": "first_name", "text": "Bruna" },
{ "type": "text", "parameter_name": "collection", "text": "Verano 2027" }
]
},
{
"type": "button",
"sub_type": "url",
"index": "0",
"parameters": [
{ "type": "text", "parameter_name": "slug", "text": "verano-2027" }
]
},
{
"type": "button",
"sub_type": "quick_reply",
"index": "1",
"parameters": [
{ "type": "payload", "payload": "optout:campana_verano" }
]
}
]
}
}'
El parameter_name en el botón de URL solo aplica si la plantilla es nombrada y la URL usa {{slug}}; en una plantilla posicional, quita esa clave. Para tarjetas con imagen y botón por tarjeta, la estructura cambia (componente carousel), y está en la guía de plantilla carrusel.
El campo language
language.code debe ser exactamente el código con el que se creó la plantilla. Una plantilla llamada pedido_confirmado en es_MX y otra con el mismo nombre en es_AR son dos plantillas distintas. Algunos códigos de la lista de idiomas admitidos:
| Idioma | Código |
|---|---|
| Español | es |
| Español (México) | es_MX |
| Español (Argentina) | es_AR |
| Español (Colombia) | es_CO |
| Español (Chile) | es_CL |
| Español (Perú) | es_PE |
| Español (España) | es_ES |
| Portugués (Brasil) | pt_BR |
¿Enviaste es para una plantilla creada en es_MX? El resultado es el error 132001, como si la plantilla no existiera.
La respuesta de la API
Cuando el envío es aceptado, la API devuelve:
{
"messaging_product": "whatsapp",
"contacts": [{ "input": "573001234567", "wa_id": "573001234567" }],
"messages": [{ "id": "wamid.HBgLMTY1MDM4Nzk0MzkVAgARGBJBRkJENzEx...", "message_status": "accepted" }]
}
Guarda el id (el wamid): es el que aparece en los webhooks de estado (sent, delivered, read, failed). El campo message_status solo aparece cuando la plantilla está bajo pacing. Un 200 significa «aceptado», no «entregado»; algunos errores solo aparecen después, en el webhook con status: failed.
Errores comunes de parámetros
Estos son los códigos que más aparecen al enviar plantillas, con la descripción de la tabla de errores de Meta:
| Código | Qué significa | Causa típica |
|---|---|---|
132000 | La cantidad de parámetros no coincide con la plantilla | Falta o sobra un text en el cuerpo; olvidaste el componente del botón de URL dinámica |
132001 | La plantilla no existe en ese idioma o no está aprobada | language.code incorrecto, error en el nombre, plantilla todavía PENDING |
132005 | Texto traducido demasiado largo | Revisa en el WhatsApp Manager que la traducción de la plantilla esté completa |
132007 | El contenido de la plantilla viola una política de WhatsApp | Revisa el contenido según las reglas de revisión |
132012 | Parámetros con un formato distinto al de la plantilla | parameter_name en plantilla posicional (o al revés); text donde se esperaba image |
132015 | Plantilla pausada por baja calidad | Edita la plantilla y espera la nueva aprobación |
132016 | Plantilla pausada demasiadas veces y desactivada para siempre | Crea una plantilla nueva con otro contenido |
131008 | Falta un parámetro obligatorio | Campo obligatorio ausente, como language o name |
131009 | Valor de parámetro inválido | Un valor fuera del formato aceptado en algún campo |
100 | Parámetro no admitido o mal escrito | Error de tipeo en una clave (paramters, lenguage) |
Los errores 132000 y 132012 tienen su propio análisis: errores 132000 y 132012. Para los demás códigos, consulta la guía de errores de la API de WhatsApp.
Un checklist rápido antes de abrir un ticket:
- Abre la plantilla en el WhatsApp Manager (o consúltala por la API de plantillas) y confirma
status: APPROVED, el idioma y elparameter_format. - Cuenta los marcadores de cada componente y compáralos con los
parametersenviados. - Verifica que cada botón con variable tenga un componente
buttoncon elindexcorrecto. - Abre la URL del archivo en una ventana privada y codifica los valores de URL dinámica.
Cuánto cuesta enviar una plantilla
El precio depende de la categoría de la plantilla y del país del destinatario. Para destinatarios en Brasil, desde el 1/10/2026 Meta cobra R$ 0,3217 (reales brasileños) por plantilla de marketing y R$ 0,0350 por plantilla de utilidad o autenticación. También desde el 1/10/2026, las plantillas de utilidad enviadas dentro de la ventana de atención se cobran. Cómo Meta decide la categoría está en marketing, utilidad o autenticación.
Cómo enviar plantillas con Vectalk
Vectalk es un proxy de la Cloud API oficial: el cuerpo de la solicitud es el mismo de Meta, solo cambian la URL y el token. Todos los payloads de este artículo funcionan en POST https://apis.vectalk.com.br/api/messages; basta con guardar el JSON en un archivo y enviarlo:
curl -X POST https://apis.vectalk.com.br/api/messages \
-H "Authorization: Bearer $VECTALK_TOKEN" \
-H "Content-Type: application/json" \
-d @plantilla.json
Con un token de instancia, el phone_number_id se resuelve automáticamente. Con un token de organización, incluye "phone_number_id" en el cuerpo para indicar desde qué número sale, según la documentación de autenticación. La referencia del envío está en Plantilla en la API de Vectalk.
Las plantillas en sí las creas y sigues en el panel (Templates, con la Biblioteca de Modelos) o por la API de plantillas, con token de instancia. En la facturación, cada mensaje entregado se descuenta del saldo prepago de tu cuenta, y no necesitas registrar una tarjeta en Meta. Crea tu cuenta en apps.vectalk.com.br/register.
Preguntas frecuentes
¿Tengo que enviar el texto de la plantilla en la solicitud?
No. El texto fijo ya está aprobado por Meta. En el envío van solo el nombre, el idioma y los valores de las variables, del archivo del encabezado y de los botones dinámicos.
¿Qué diferencia hay entre variable posicional y nombrada?
La posicional usa {{1}}, {{2}} y exige los valores en el orden del texto. La nombrada usa {{first_name}} y cada valor lleva parameter_name, en cualquier orden. El formato se elige al crear la plantilla; si nadie lo elige, se usa el posicional.
¿Puedo cambiar la imagen del encabezado en cada envío?
Sí. La imagen aprobada es solo un ejemplo para la revisión. En cada envío mandas el archivo que quieras en el componente header, por link o id, siempre que sea del mismo tipo definido en la plantilla (imagen, video o documento).
¿Por qué recibo un 200 y el mensaje no llega?
El 200 solo confirma que la API aceptó la solicitud. Los errores de plantilla, de número o de límite pueden llegar después, en el webhook de estado con failed y el código del error. Monitorea los webhooks, no solo la respuesta HTTP.
Fuentes oficiales consultadas el 29/10/2026: Template fundamentals (Meta), Template components (Meta), Template media (Meta), Utility templates (Meta), Custom marketing templates (Meta), Coupon code templates (Meta), Media card carousel templates (Meta), Supported languages (Meta), Send messages (Meta), Document messages (Meta), Button messages webhook (Meta), Error codes (Meta), Envío de plantillas en la API de Vectalk, Autenticación 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.
