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

# Envío masivo de plantillas en WhatsApp

> Envíe una plantilla aprobada de WhatsApp a varios contactos en una sola solicitud

`POST /v1/messages/send-bulk-template` envía una plantilla aprobada de WhatsApp a una lista de
contactos de una sola vez. Por ser plantillas, estos envíos valen **fuera de la ventana de 24
horas**: esta es la forma de iniciar una conversación con quien no ha hablado con usted recientemente.

**Scope:** `messages:create`.

<Note>
  La plantilla debe estar **aprobada en Meta** de antemano. Esta llamada dispara una plantilla
  existente; no crea ni envía plantillas para aprobación.
</Note>

## Campos de la solicitud

| Campo          | Tipo    | Obligatorio | Descripción                                                                       |
| -------------- | ------- | ----------- | --------------------------------------------------------------------------------- |
| `inboxId`      | string  | Sí          | Inbox (conexión de WhatsApp) que envía los mensajes.                              |
| `templateName` | string  | Sí          | Nombre de la plantilla como está registrada en WhatsApp Business.                 |
| `languageCode` | string  | No          | Formato `pt_BR`, `en_US` o solo `en`. Por defecto: `pt_BR`.                       |
| `components`   | array   | No          | Parámetros dinámicos de la plantilla (vea abajo).                                 |
| `contacts`     | array   | Condicional | Lista de `{ name, phone }`. Obligatorio si no envía un archivo.                   |
| `file`         | archivo | Condicional | CSV o XLSX con las columnas `name` y `phone`. Obligatorio si no envía `contacts`. |

`phone` necesita el código del país, sin símbolos: `5511999999999`. `name` no puede estar vacío.

## Enviar a una lista de contactos

```bash cURL theme={null}
curl -X POST https://api.nuvia.ai/v1/messages/send-bulk-template \
  -H "Authorization: Bearer $NUVIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inboxId": "ID_DEL_INBOX",
    "templateName": "boas_vindas",
    "languageCode": "pt_BR",
    "components": [
      { "type": "body", "parameters": [{ "type": "text", "text": "Juan" }] }
    ],
    "contacts": [
      { "name": "Juan Silva", "phone": "5511999999999" },
      { "name": "María Santos", "phone": "5511888888888" }
    ]
  }'
```

Para listas grandes, envíe un archivo en lugar del array: `multipart/form-data` con el CSV o XLSX
en el campo `file` y los demás campos en el formulario. El archivo necesita las columnas `name` y
`phone`:

```csv theme={null}
name,phone
Juan Silva,5511999999999
María Santos,5511888888888
```

<Note>
  Combinar **archivo** con `components` de parámetros dinámicos es un caso que vale la pena probar
  con pocos contactos antes de usarlo en producción. Si los parámetros no se aplican como usted
  espera, contacte al soporte.
</Note>

## Parámetros de la plantilla

Una plantilla aprobada tiene marcadores (`{{1}}`, `{{2}}`) y bloques: encabezado, cuerpo y botones.
Esto se completa en `components`, y el número y el tipo de parámetros deben **coincidir
exactamente** con la plantilla aprobada, de lo contrario Meta rechaza el envío.

| Bloque (`type`) | Qué acepta en `parameters`                                   |
| --------------- | ------------------------------------------------------------ |
| `header`        | `text`, `image`, `video`, `document`                         |
| `body`          | `text`, `currency`, `date_time`                              |
| `button`        | `text` (botón de URL dinámica), `payload` (respuesta rápida) |

Tipos de parámetro:

| `type`      | Campos                               | Ejemplo                                                                                    |
| ----------- | ------------------------------------ | ------------------------------------------------------------------------------------------ |
| `text`      | `text`                               | `{ "type": "text", "text": "Juan" }`                                                       |
| `image`     | `image.link`                         | `{ "type": "image", "image": { "link": "https://..." } }`                                  |
| `video`     | `video.link`                         | `{ "type": "video", "video": { "link": "https://..." } }`                                  |
| `document`  | `document.link`, `document.filename` | `{ "type": "document", "document": { "link": "https://...", "filename": "factura.pdf" } }` |
| `currency`  | `currencyCode`, `currencyAmount`     | `{ "type": "currency", "currencyCode": "BRL", "currencyAmount": 150.5 }`                   |
| `date_time` | `dateTime` (ISO 8601)                | `{ "type": "date_time", "dateTime": "2026-01-31T23:59:59Z" }`                              |
| `payload`   | `payload`                            | `{ "type": "payload", "payload": "producto_1" }`                                           |

<Tip>
  Imagen, video y documento entran en el encabezado por **URL pública HTTPS**. No necesita subir
  ningún archivo ni preparar nada antes. Basta con que la URL sea accesible por Meta.
</Tip>

### Ejemplos por tipo de plantilla

<AccordionGroup>
  <Accordion title="Encabezado con imagen" icon="image">
    Plantilla: encabezado de imagen + `¡Hola {{1}}! Descubre nuestra promoción de {{2}}.`

    ```json theme={null}
    "components": [
      {
        "type": "header",
        "parameters": [
          { "type": "image", "image": { "link": "https://example.com/promo.jpg" } }
        ]
      },
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "Juan" },
          { "type": "text", "text": "Black Friday" }
        ]
      }
    ]
    ```
  </Accordion>

  <Accordion title="Botón con URL dinámica" icon="link">
    Plantilla: `Su pedido #{{1}} ha sido confirmado.` + botón que apunta a `.../track/{{1}}`.

    El `index` del botón es una **string** y comienza en `"0"`:

    ```json theme={null}
    "components": [
      { "type": "body", "parameters": [{ "type": "text", "text": "12345" }] },
      {
        "type": "button",
        "sub_type": "url",
        "index": "0",
        "parameters": [{ "type": "text", "text": "12345" }]
      }
    ]
    ```
  </Accordion>

  <Accordion title="Documento en el encabezado" icon="file-pdf">
    Plantilla: encabezado de documento + `Hola {{1}}, aquí está su factura.`

    ```json theme={null}
    "components": [
      {
        "type": "header",
        "parameters": [
          {
            "type": "document",
            "document": {
              "link": "https://example.com/factura.pdf",
              "filename": "factura_enero.pdf"
            }
          }
        ]
      },
      { "type": "body", "parameters": [{ "type": "text", "text": "Juan Silva" }] }
    ]
    ```
  </Accordion>

  <Accordion title="Importe monetario y fecha" icon="money-bill">
    Plantilla: `Su factura de {{1}} por un valor de {{2}} vence el {{3}}.`

    ```json theme={null}
    "components": [
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "Enero/2026" },
          { "type": "currency", "currencyCode": "BRL", "currencyAmount": 150.5 },
          { "type": "date_time", "dateTime": "2026-01-31T23:59:59Z" }
        ]
      }
    ]
    ```

    El formato final (`R$ 150,50`, `31/01/2026`) lo aplica WhatsApp, según el idioma del
    dispositivo de quien recibe el mensaje.
  </Accordion>
</AccordionGroup>

Las plantillas de **carrusel** no se aceptan en este endpoint. Use el envío individual de
plantilla.

## La respuesta es parcial

El procesamiento es síncrono y un contacto con error **no interrumpe** a los demás. La respuesta
trae el resultado consolidado:

```json theme={null}
{
  "totalProcessed": 100,
  "sentCount": 95,
  "errorCount": 5,
  "sentContacts": [
    { "conversationId": "65f1a2b3c4d5e6f7a8b9c0d1", "phoneNumber": "5511999999999" }
  ],
  "errors": [
    { "row": 5, "contact": "Juan Silva", "error": "Número de teléfono inválido" },
    { "row": 23, "contact": "María Santos", "error": "Plantilla no encontrada para este inbox" }
  ]
}
```

<Warning>
  Un `200` **no** significa que todos lo recibieron. Lea siempre `errorCount` y `errors`. Si se
  ignora ese bloque, las fallas de envío pasan desapercibidas. `row` señala la línea del archivo o
  el índice en el array `contacts`.
</Warning>

Reenviar a quienes fallaron es responsabilidad de su integración: no hay retry automático.

## Límites que vale la pena conocer

* **Tier de la cuenta en WhatsApp**: Meta limita el número de conversaciones únicas iniciadas por
  día (1.000, 10.000 o 100.000, según el tier de su cuenta). El envío respeta ese tope: por encima
  de él, los envíos empiezan a fallar.
* **Tamaño de medios**: imagen hasta 5 MB (JPG, PNG), video hasta 16 MB (MP4, 3GPP), documento
  hasta 100 MB (PDF, DOC/DOCX, XLS/XLSX, PPT/PPTX, TXT), audio hasta 16 MB.
* **Enlaces de medios**: deben ser URLs HTTPS públicas, accesibles por Meta. Una URL detrás de un
  login o de una red interna falla.
* **Tamaño del lote**: envíe en bloques de hasta 1.000 contactos por solicitud.

## Buenas prácticas

<Steps>
  <Step title="Pruebe con un solo contacto">
    Envíe primero a su propio número y verifique el resultado en el dispositivo. Un parámetro
    fuera de orden solo aparece en el mensaje final.
  </Step>

  <Step title="Normalice los teléfonos antes">
    Use el formato internacional sin símbolos (`5511999999999`). Un número inválido se convierte
    en una línea en `errors`, no en una excepción.
  </Step>

  <Step title="Guarde los conversationId">
    `sentContacts` devuelve la conversación creada para cada contacto: es por ella que se hace
    seguimiento de la respuesta, vía [webhooks](/es/guias/webhooks) o
    `GET /v1/messages/conversation/{id}`.
  </Step>

  <Step title="Trate los errores como cola de reprocesamiento">
    Reenvíe solo las líneas de `errors`, después de corregir el motivo.
  </Step>
</Steps>

## Próximos pasos

<CardGroup cols={2}>
  <Card title="Webhooks" icon="webhook" href="/es/guias/webhooks">
    Reciba las respuestas de quienes fueron impactados por el envío.
  </Card>

  <Card title="Referencia de la API" icon="code" href="/es/api-reference/messages">
    Contrato completo del endpoint y de los demás envíos.
  </Card>
</CardGroup>
