> ## Documentation Index
> Fetch the complete documentation index at: https://docs.whaapy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Enviar Templates

> Envía mensajes template pre-aprobados por Meta

Los **templates** son mensajes pre-aprobados por Meta que puedes enviar en cualquier momento, incluso fuera de la ventana de 24 horas. Son ideales para:

* Notificaciones transaccionales (confirmación de pedido, envío, etc.)
* Re-engagement con clientes inactivos
* Marketing autorizado
* Alertas y recordatorios

<Warning>
  Los templates deben ser aprobados por Meta antes de usarlos. La aprobación puede tomar hasta 24 horas.
</Warning>

***

## Formatos de Envío

Whaapy soporta **tres formas** de enviar templates:

<Tabs>
  <Tab title="Shortcut Whaapy">
    Formato simplificado con `templateName` y `template_parameters`:

    ```json theme={null}
    {
      "to": "+5215512345678",
      "type": "template",
      "templateName": "orden_confirmada",
      "template_parameters": ["Juan", "#12345"]
    }
    ```
  </Tab>

  <Tab title="Shortcut con Header">
    Incluye imagen/video/documento en el header con `header_media`:

    ```json theme={null}
    {
      "to": "+5215512345678",
      "type": "template",
      "templateName": "promo_imagen",
      "template_parameters": ["20%", "Enero 2026"],
      "header_media": {
        "type": "image",
        "url": "https://example.com/promo.jpg"
      }
    }
    ```
  </Tab>

  <Tab title="Formato Meta Completo">
    Control total con `template.components`:

    ```json theme={null}
    {
      "to": "+5215512345678",
      "type": "template",
      "template": {
        "name": "orden_confirmada",
        "language": { "code": "es_MX" },
        "components": [
          {
            "type": "body",
            "parameters": [
              { "type": "text", "text": "Juan" },
              { "type": "text", "text": "#12345" }
            ]
          }
        ]
      }
    }
    ```
  </Tab>
</Tabs>

***

## Shortcut Whaapy (Simplificado)

El formato más sencillo para templates con solo parámetros de texto en el body.

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.whaapy.com/messages/v1 \
    -H "Authorization: Bearer wha_TU_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "to": "+5215512345678",
      "type": "template",
      "templateName": "orden_confirmada",
      "template_parameters": ["Juan Pérez", "#ORD-12345", "$1,500.00"]
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch('https://api.whaapy.com/messages/v1', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer wha_TU_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      to: '+5215512345678',
      type: 'template',
      templateName: 'orden_confirmada',
      template_parameters: ['Juan Pérez', '#ORD-12345', '$1,500.00']
    })
  });
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.whaapy.com/messages/v1',
      headers={
          'Authorization': 'Bearer wha_TU_API_KEY',
          'Content-Type': 'application/json'
      },
      json={
          'to': '+5215512345678',
          'type': 'template',
          'templateName': 'orden_confirmada',
          'template_parameters': ['Juan Pérez', '#ORD-12345', '$1,500.00']
      }
  )
  ```
</RequestExample>

### Campos

<ParamField body="templateName" type="string" required>
  Nombre exacto del template como aparece en Meta Business Manager.
</ParamField>

<ParamField body="template_parameters" type="array">
  Array de strings con los valores para cada `{{placeholder}}` del template body, en orden.
</ParamField>

<Note>
  El shortcut asume idioma `es_MX` por defecto. Para otros idiomas, usa el formato Meta completo.
</Note>

***

## Shortcut con Header Media

Para templates que incluyen imagen, video o documento en el header.

<RequestExample>
  ```json Request theme={null}
  {
    "to": "+5215512345678",
    "type": "template",
    "templateName": "promo_navidad",
    "template_parameters": ["30%", "25 de diciembre"],
    "header_media": {
      "type": "image",
      "url": "https://example.com/navidad-promo.jpg"
    }
  }
  ```
</RequestExample>

### Con Video

```json theme={null}
{
  "to": "+5215512345678",
  "type": "template",
  "templateName": "tutorial_producto",
  "template_parameters": ["Producto X"],
  "header_media": {
    "type": "video",
    "url": "https://example.com/tutorial.mp4"
  }
}
```

### Con Documento

```json theme={null}
{
  "to": "+5215512345678",
  "type": "template",
  "templateName": "factura_mensual",
  "template_parameters": ["Enero 2026", "$2,500.00"],
  "header_media": {
    "type": "document",
    "url": "https://example.com/factura.pdf"
  }
}
```

### Campos de header\_media

<ParamField body="header_media.type" type="string" required>
  Tipo de media: `image`, `video`, o `document`.
</ParamField>

<ParamField body="header_media.url" type="string" required>
  URL pública del archivo multimedia.
</ParamField>

<ParamField body="header_media.media_id" type="string">
  Media ID de Meta previamente subido con el mismo número/WABA que enviará el template.
</ParamField>

### Ejemplo con `media_id` (sin URL pública)

```json theme={null}
{
  "to": "+5215512345678",
  "type": "template",
  "templateName": "orden_servicio",
  "template_parameters": ["Juan"],
  "header_media": {
    "type": "document",
    "media_id": "155857201882704"
  }
}
```

<Note>
  Compatibilidad legacy: si un cliente envía accidentalmente el `media_id` en `header_media.url`, Whaapy intenta normalizarlo automáticamente.
</Note>

***

## Formato Meta Completo

Control total sobre todos los componentes del template: header, body, footer y botones.

### Template Básico

<RequestExample>
  ```json Request theme={null}
  {
    "to": "+5215512345678",
    "type": "template",
    "template": {
      "name": "orden_confirmada",
      "language": {
        "policy": "deterministic",
        "code": "es_MX"
      },
      "components": [
        {
          "type": "body",
          "parameters": [
            { "type": "text", "text": "Juan Pérez" },
            { "type": "text", "text": "#ORD-12345" }
          ]
        }
      ]
    }
  }
  ```
</RequestExample>

### Template con Header de Imagen

<RequestExample>
  ```json Request theme={null}
  {
    "to": "+5215512345678",
    "type": "template",
    "template": {
      "name": "confirmacion_pago",
      "language": { "policy": "deterministic", "code": "es_MX" },
      "components": [
        {
          "type": "header",
          "parameters": [
            {
              "type": "image",
              "image": { "link": "https://example.com/recibo.jpg" }
            }
          ]
        },
        {
          "type": "body",
          "parameters": [
            { "type": "text", "text": "Juan Pérez" },
            { "type": "text", "text": "#ORD-12345" }
          ]
        }
      ]
    }
  }
  ```
</RequestExample>

### Template con Currency y Date

<RequestExample>
  ```json Request theme={null}
  {
    "to": "+5215512345678",
    "type": "template",
    "template": {
      "name": "resumen_compra",
      "language": { "policy": "deterministic", "code": "es_MX" },
      "components": [
        {
          "type": "body",
          "parameters": [
            { "type": "text", "text": "Juan Pérez" },
            { "type": "text", "text": "#ORD-12345" },
            {
              "type": "currency",
              "currency": {
                "fallback_value": "$1,500.00 MXN",
                "code": "MXN",
                "amount_1000": 1500000
              }
            },
            {
              "type": "date_time",
              "date_time": {
                "fallback_value": "25 de enero, 2026"
              }
            }
          ]
        }
      ]
    }
  }
  ```
</RequestExample>

### Template con Botones

<RequestExample>
  ```json Request theme={null}
  {
    "to": "+5215512345678",
    "type": "template",
    "template": {
      "name": "confirmacion_envio",
      "language": { "policy": "deterministic", "code": "es_MX" },
      "components": [
        {
          "type": "body",
          "parameters": [
            { "type": "text", "text": "Juan" },
            { "type": "text", "text": "#ENV-789" }
          ]
        },
        {
          "type": "button",
          "sub_type": "quick_reply",
          "index": "0",
          "parameters": [
            { "type": "payload", "payload": "confirmar_recibido_789" }
          ]
        },
        {
          "type": "button",
          "sub_type": "url",
          "index": "1",
          "parameters": [
            { "type": "text", "text": "ENV-789" }
          ]
        }
      ]
    }
  }
  ```
</RequestExample>

***

## Tipos de Parámetros

| Tipo        | Uso                            | Ejemplo                                                 |
| ----------- | ------------------------------ | ------------------------------------------------------- |
| `text`      | Texto simple                   | `{ "type": "text", "text": "Juan Pérez" }`              |
| `currency`  | Valores monetarios formateados | Ver ejemplo abajo                                       |
| `date_time` | Fechas y horas                 | Ver ejemplo abajo                                       |
| `image`     | Imagen en header               | `{ "type": "image", "image": { "link": "url" } }`       |
| `video`     | Video en header                | `{ "type": "video", "video": { "link": "url" } }`       |
| `document`  | PDF en header                  | `{ "type": "document", "document": { "link": "url" } }` |
| `payload`   | Datos de callback para botones | `{ "type": "payload", "payload": "data" }`              |

### Parámetro Currency

```json theme={null}
{
  "type": "currency",
  "currency": {
    "fallback_value": "$1,500.00 MXN",
    "code": "MXN",
    "amount_1000": 1500000
  }
}
```

<Note>
  `amount_1000` es el monto multiplicado por 1000. Para \$1,500.00 MXN, el valor es 1500000.
</Note>

### Parámetro Date/Time

```json theme={null}
{
  "type": "date_time",
  "date_time": {
    "fallback_value": "25 de enero, 2026 a las 3:00 PM"
  }
}
```

***

## Estructura de Botones

Los botones en templates tienen diferentes tipos:

| sub\_type     | Descripción                | Parámetros                      |
| ------------- | -------------------------- | ------------------------------- |
| `quick_reply` | Botón de respuesta rápida  | `payload` (string de callback)  |
| `url`         | Botón que abre URL         | `text` (sufijo dinámico de URL) |
| `catalog`     | Abre catálogo de productos | N/A                             |

### Política de IDs (`payload`) para quick reply

Cuando envías templates por `POST /messages/v1`, Whaapy resuelve el `payload` del botón en este orden:

1. `override` explícito en request **solo si** `allowButtonIdOverride=true`
2. `buttonId` configurado en el template del negocio
3. fallback automático: `btn_{index}_{templateName}`

Esto garantiza trazabilidad estable en webhooks por negocio.

<ParamField body="allowButtonIdOverride" type="boolean">
  Opcional (default: `false`). Si está en `true`, permite sobreescribir el payload de quick reply desde `template.components`.
</ParamField>

### Ejemplo de override explícito (avanzado)

```json theme={null}
{
  "to": "+5215512345678",
  "type": "template",
  "templateName": "confirmacion_envio",
  "template_parameters": ["Juan", "#ENV-789"],
  "allowButtonIdOverride": true,
  "template": {
    "components": [
      {
        "type": "button",
        "sub_type": "quick_reply",
        "index": "0",
        "parameters": [
          { "type": "payload", "payload": "confirmar_envio_custom" }
        ]
      }
    ]
  }
}
```

### Ejemplo Quick Reply

```json theme={null}
{
  "type": "button",
  "sub_type": "quick_reply",
  "index": "0",
  "parameters": [
    { "type": "payload", "payload": "confirmar_pedido_12345" }
  ]
}
```

### Ejemplo URL Dinámica

Si tu template tiene botón URL con `https://example.com/track/{{1}}`:

```json theme={null}
{
  "type": "button",
  "sub_type": "url",
  "index": "0",
  "parameters": [
    { "type": "text", "text": "ENV-789" }
  ]
}
```

El resultado será `https://example.com/track/ENV-789`.

<ParamField body="button.index" type="string" required>
  Índice del botón (0-9). Corresponde al orden de los botones en el template.
</ParamField>

***

## Respuesta Exitosa

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "messaging_product": "whatsapp",
    "contacts": [
      { "input": "+5215512345678", "wa_id": "5215512345678" }
    ],
    "messages": [
      { "id": "550e8400-e29b-41d4-a716-446655440000", "wamid": "wamid.HBgLNTIxNTUx..." }
    ],
    "data": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "conversationId": "660e8400-e29b-41d4-a716-446655440001",
      "messageType": "template",
      "direction": "outbound",
      "status": "sent",
      "createdAt": "2026-01-22T10:30:00Z"
    }
  }
  ```
</ResponseExample>

***

## Errores Comunes

### Template No Encontrado (132000)

```json theme={null}
{
  "error": "template_not_found",
  "message": "El template no existe o no está aprobado",
  "code": "132000",
  "details": {
    "template_name": "orden_confirmada",
    "language": "es_MX"
  }
}
```

**Soluciones**:

1. Verifica el nombre exacto en Meta Business Manager
2. Confirma que el template está aprobado (no en revisión)
3. Verifica el código de idioma correcto

### Número de Parámetros Incorrecto

```json theme={null}
{
  "error": "invalid_template_parameters",
  "message": "El template requiere 3 parámetros, se recibieron 2"
}
```

**Solución**: Revisa cuántos `{{placeholders}}` tiene tu template y envía la misma cantidad de parámetros.

### Permisos de Media (`MEDIA_PERMISSION_DENIED`)

```json theme={null}
{
  "error": "Media permission denied",
  "message": "No se pudo acceder al media_id del header con las credenciales actuales de WhatsApp.",
  "code": "MEDIA_PERMISSION_DENIED",
  "hint": "Verifica que el media fue subido con el mismo número/WABA/token que envía el template."
}
```

**Soluciones**:

1. Confirma que el `media_id` pertenece al mismo WABA/número emisor.
2. Re-sube el archivo usando la misma credencial que envía el template.
3. Si no tienes `media_id` válido, usa `header_media.url` pública.

***

## Códigos de Idioma Comunes

| Código  | Idioma                  |
| ------- | ----------------------- |
| `es_MX` | Español (México)        |
| `es_ES` | Español (España)        |
| `es_AR` | Español (Argentina)     |
| `en_US` | Inglés (Estados Unidos) |
| `pt_BR` | Portugués (Brasil)      |

<Tip>
  Usa `"policy": "deterministic"` para asegurar que se use exactamente el idioma especificado. Sin esto, WhatsApp puede seleccionar un idioma diferente basado en las preferencias del usuario.
</Tip>

***

## Crear Templates en Meta

1. Ve a [Meta Business Suite](https://business.facebook.com)
2. Navega a **WhatsApp Manager** → **Message Templates**
3. Click en **Create Template**
4. Selecciona categoría (Marketing, Utility, Authentication)
5. Define header, body, footer y botones
6. Envía para aprobación

<Note>
  Los templates de categoría "Authentication" tienen reglas especiales y mayores restricciones.
</Note>

***

## Próximos Pasos

<CardGroup cols={2}>
  <Card title="Mensajes Interactivos" icon="hand-pointer" href="/api-reference/messages/send-interactive">
    Botones y listas sin templates
  </Card>

  <Card title="Reintentar Mensajes" icon="rotate" href="/api-reference/messages/retry">
    Reenviar después de template
  </Card>
</CardGroup>
