Enviar plantilla (HSM)
Las plantillas certificadas (HSM) son la única forma de iniciar conversación con un cliente fuera de la ventana de 24 horas. Deben estar previamente aprobadas por Meta. Útiles para confirmaciones de pedido, OTPs, recordatorios de pago, marketing y notificaciones masivas.
Aprobación previa requerida. Crea tus plantillas en Meta Business Manager.
Una vez aprobadas por Meta, se sincronizan automáticamente al panel de Wis.Chat
(wis.chat/wischat → Plantillas), donde puedes ver el listado, el idioma de cada una
y su estado de aprobación. Sólo las plantillas con estado APPROVED pueden enviarse
por la API.
Prueba rápida con hello_world
Para verificar conectividad sin necesidad de crear y esperar la aprobación de una plantilla
propia, puedes usar hello_world: una plantilla pre-aprobada por defecto en toda
cuenta de WhatsApp Business. Aparece automáticamente en tu panel de Wis.Chat junto al resto.
Es útil para tu primera llamada a la API:
{
"messaging_product": "whatsapp",
"to": "593999999999",
"type": "template",
"template": {
"name": "hello_world",
"language": { "code": "en_US" }
}
}El mensaje llega en inglés con un texto fijo ("Hello World!") que no se puede modificar: es solo para verificación técnica. Para mensajes reales, crea tus propias plantillas.
Ejemplo con variables en el body
Una plantilla típica de producción tiene variables marcadas con {{1}}, {{2}}, etc.
en el cuerpo, que se reemplazan dinámicamente al enviar. En este ejemplo, una plantilla
confirmacion_pago con 3 variables:
{
"messaging_product": "whatsapp",
"to": "593999999999",
"type": "template",
"template": {
"name": "confirmacion_pago",
"language": { "code": "es" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Juan Pérez" }, // {{1}} — nombre
{ "type": "text", "text": "$ 145.50" }, // {{2}} — monto
{ "type": "text", "text": "FAC-001-4521" } // {{3}} — referencia
]
}
]
}
}El nombre exacto de la plantilla, el idioma y la cantidad de variables debe coincidir con lo que tienes aprobado. Consulta tus plantillas disponibles en el panel para ver el nombre exacto, el idioma y los placeholders esperados de cada una.
Estructura del objeto template
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
name |
string | requerido | Nombre exacto de la plantilla aprobada (case-sensitive). Lo encuentras en wis.chat/wischat → Plantillas, sincronizado desde Meta. |
language |
object | requerido | Objeto con code: el idioma de la plantilla. Ej: es, en_US, pt_BR.
El idioma de cada plantilla también se ve en el panel. |
components |
array | opcional | Lista de componentes (header, body, button) con sus parámetros dinámicos. |
Componentes disponibles
Body con variables
El componente body es el más común. Contiene las variables {{1}}, {{2}}, etc. que se reemplazan en el orden del array parameters.
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Oscar" },
{ "type": "text", "text": "#4521" }
]
}Header con imagen
Si tu plantilla aprobada tiene un header con imagen, video o documento, lo envías así:
{
"type": "header",
"parameters": [
{
"type": "image",
"image": { "link": "https://midominio.com/banner.jpg" }
}
]
}Otros tipos de header soportados:
{ "type": "video", "video": { "link": "..." } }{ "type": "document", "document": { "link": "...", "filename": "..." } }{ "type": "text", "text": "Encabezado" }
Botón URL dinámico
Para botones que tienen una URL con un parámetro al final (ej: https://app.com/orden/{{1}}):
{
"type": "button",
"sub_type": "url",
"index": "0",
"parameters": [
{ "type": "text", "text": "4521" }
]
}Botón quick reply (con payload)
{
"type": "button",
"sub_type": "quick_reply",
"index": "0",
"parameters": [
{ "type": "payload", "payload": "CONFIRMAR_4521" }
]
}Ejemplo completo · plantilla con todo
Plantilla con header de imagen, body con 3 variables y botón URL dinámico:
{
"messaging_product": "whatsapp",
"to": "593999999999",
"type": "template",
"template": {
"name": "confirmacion_pedido",
"language": { "code": "es" },
"components": [
{
"type": "header",
"parameters": [
{ "type": "image", "image": { "link": "https://midominio.com/logo.jpg" } }
]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Oscar" },
{ "type": "text", "text": "#4521" },
{ "type": "text", "text": "$ 145.50" }
]
},
{
"type": "button",
"sub_type": "url",
"index": "0",
"parameters": [
{ "type": "text", "text": "4521" }
]
}
]
}
}Implementaciones por lenguaje
curl -X POST "https://api.wis.chat/v22.0/TU_ID_LINEA/messages" \ -H "Authorization: Bearer TU_CODIGO_SEGURIDAD" \ -H "Content-Type: application/json" \ -d '{ "messaging_product": "whatsapp", "to": "593999999999", "type": "template", "template": { "name": "confirmacion_pago", "language": { "code": "es" }, "components": [{ "type": "body", "parameters": [ {"type":"text","text":"Juan Pérez"}, {"type":"text","text":"$ 145.50"}, {"type":"text","text":"FAC-001-4521"} ] }] } }'
function enviarPlantillaWischat($tid, $token, $to, $nombre, $idioma, $variables = []) { $params = array_map(function($v) { return ['type' => 'text', 'text' => $v]; }, $variables); $payload = [ 'messaging_product' => 'whatsapp', 'to' => $to, 'type' => 'template', 'template' => [ 'name' => $nombre, 'language' => ['code' => $idioma], 'components' => empty($params) ? [] : [[ 'type' => 'body', 'parameters' => $params, ]], ], ]; $ch = curl_init("https://api.wis.chat/v22.0/{$tid}/messages"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_POSTFIELDS => json_encode($payload), CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", 'Content-Type: application/json', ], ]); $res = json_decode(curl_exec($ch), true); curl_close($ch); return $res; } // Uso: $resp = enviarPlantillaWischat( 15, 'TU_CODIGO_SEGURIDAD', '593999999999', 'confirmacion_pago', 'es', ['Juan Pérez', '$ 145.50', 'FAC-001-4521'] ); if ($resp['success'] ?? false) { $wamid = $resp['messages'][0]['id']; echo "Mensaje enviado: {$wamid}"; } else { echo "Error: " . ($resp['message'] ?? 'desconocido'); }
async function enviarPlantilla({ tid, token, to, nombre, idioma, variables = [] }) { const params = variables.map(v => ({ type: 'text', text: v })); const res = await fetch(`https://api.wis.chat/v22.0/${tid}/messages`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ messaging_product: 'whatsapp', to, type: 'template', template: { name: nombre, language: { code: idioma }, components: params.length ? [{ type: 'body', parameters: params }] : [], }, }), }); return await res.json(); } // Uso: const data = await enviarPlantilla({ tid: 15, token: 'TU_CODIGO_SEGURIDAD', to: '593999999999', nombre: 'confirmacion_pago', idioma: 'es', variables: ['Juan Pérez', '$ 145.50', 'FAC-001-4521'], }); console.log(data);
import requests def enviar_plantilla(tid, token, to, nombre, idioma, variables=None): variables = variables or [] params = [{'type': 'text', 'text': v} for v in variables] payload = { 'messaging_product': 'whatsapp', 'to': to, 'type': 'template', 'template': { 'name': nombre, 'language': {'code': idioma}, 'components': [{'type': 'body', 'parameters': params}] if params else [], }, } res = requests.post( f'https://api.wis.chat/v22.0/{tid}/messages', headers={ 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json', }, json=payload ) return res.json() # Uso: data = enviar_plantilla( tid=15, token='TU_CODIGO_SEGURIDAD', to='593999999999', nombre='confirmacion_pago', idioma='es', variables=['Juan Pérez', '$ 145.50', 'FAC-001-4521'], ) print(data)
Listar plantillas disponibles
Puedes consultar el listado de plantillas activas y su estado de aprobación desde tu panel
en wis.chat/wischat → Plantillas.
Sólo las que tienen estado APPROVED son aptas para envío.