> ## 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.

# Template em massa no WhatsApp

> Dispare um template aprovado do WhatsApp para vários contatos numa única requisição

`POST /v1/messages/send-bulk-template` envia um template aprovado do WhatsApp para uma lista de
contatos de uma vez. Por serem templates, os envios valem **fora da janela de 24 horas** — é o
caminho para iniciar conversa com quem não falou com você recentemente.

**Escopo:** `messages:create`.

<Note>
  O template precisa estar **aprovado na Meta** antes. Esta chamada dispara um template existente;
  ela não cria nem submete templates para aprovação.
</Note>

## Campos da requisição

| Campo          | Tipo    | Obrigatório | Descrição                                                                               |
| -------------- | ------- | ----------- | --------------------------------------------------------------------------------------- |
| `inboxId`      | string  | Sim         | Inbox (conexão de WhatsApp) que envia as mensagens.                                     |
| `templateName` | string  | Sim         | Nome do template como registrado no WhatsApp Business.                                  |
| `languageCode` | string  | Não         | Formato `pt_BR`, `en_US` ou só `en`. Default: `pt_BR`.                                  |
| `components`   | array   | Não         | Parâmetros dinâmicos do template (veja abaixo).                                         |
| `contacts`     | array   | Condicional | Lista de `{ name, phone }`. Obrigatório se você não enviar arquivo.                     |
| `file`         | arquivo | Condicional | CSV ou XLSX com as colunas `name` e `phone`. Obrigatório se você não enviar `contacts`. |

`phone` precisa do código do país, sem símbolos: `5511999999999`. `name` não pode ser vazio.

## Enviar para uma lista de contatos

```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_DO_INBOX",
    "templateName": "boas_vindas",
    "languageCode": "pt_BR",
    "components": [
      { "type": "body", "parameters": [{ "type": "text", "text": "João" }] }
    ],
    "contacts": [
      { "name": "João Silva", "phone": "5511999999999" },
      { "name": "Maria Santos", "phone": "5511888888888" }
    ]
  }'
```

Para listas grandes, envie um arquivo em vez do array: `multipart/form-data` com o CSV ou XLSX no
campo `file` e os demais campos no formulário. O arquivo precisa das colunas `name` e `phone`:

```csv theme={null}
name,phone
João Silva,5511999999999
Maria Santos,5511888888888
```

<Note>
  Combinar **arquivo** com `components` de parâmetros dinâmicos é um caso que vale testar com
  poucos contatos antes de usar em produção — se os parâmetros não forem aplicados como você espera,
  fale com o suporte.
</Note>

## Parâmetros do template

Um template aprovado tem lacunas (`{{1}}`, `{{2}}`) e blocos: cabeçalho, corpo e botões. Você
preenche isso em `components`, e o número e o tipo de parâmetros precisam **casar exatamente** com o
template aprovado — caso contrário a Meta recusa o envio.

| Bloco (`type`) | O que aceita em `parameters`                                |
| -------------- | ----------------------------------------------------------- |
| `header`       | `text`, `image`, `video`, `document`                        |
| `body`         | `text`, `currency`, `date_time`                             |
| `button`       | `text` (botão de URL dinâmica), `payload` (resposta rápida) |

Tipos de parâmetro:

| `type`      | Campos                               | Exemplo                                                                                   |
| ----------- | ------------------------------------ | ----------------------------------------------------------------------------------------- |
| `text`      | `text`                               | `{ "type": "text", "text": "João" }`                                                      |
| `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": "boleto.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": "produto_1" }`                                           |

<Tip>
  Imagem, vídeo e documento entram no cabeçalho por **URL pública HTTPS** — você não precisa fazer
  upload de arquivo nem preparar nada antes. Basta que a URL seja acessível pela Meta.
</Tip>

### Exemplos por tipo de template

<AccordionGroup>
  <Accordion title="Cabeçalho com imagem" icon="image">
    Template: cabeçalho de imagem + `Olá {{1}}! Confira nossa promoção de {{2}}.`

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

  <Accordion title="Botão com URL dinâmica" icon="link">
    Template: `Seu pedido #{{1}} foi confirmado.` + botão que aponta para `.../track/{{1}}`.

    O `index` do botão é uma **string** e começa em `"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 no cabeçalho" icon="file-pdf">
    Template: cabeçalho de documento + `Olá {{1}}, segue seu boleto.`

    ```json theme={null}
    "components": [
      {
        "type": "header",
        "parameters": [
          {
            "type": "document",
            "document": {
              "link": "https://example.com/boleto.pdf",
              "filename": "boleto_janeiro.pdf"
            }
          }
        ]
      },
      { "type": "body", "parameters": [{ "type": "text", "text": "João Silva" }] }
    ]
    ```
  </Accordion>

  <Accordion title="Valor monetário e data" icon="money-bill">
    Template: `Sua fatura de {{1}} no valor de {{2}} vence em {{3}}.`

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

    A formatação final (`R$ 150,50`, `31/01/2026`) é feita pelo WhatsApp, conforme o idioma do
    aparelho de quem recebe.
  </Accordion>
</AccordionGroup>

Templates de **carrossel** não são aceitos neste endpoint — use o envio individual de template.

## A resposta é parcial

O processamento é síncrono e um contato com erro **não interrompe** os demais. A resposta traz o
resultado consolidado:

```json theme={null}
{
  "totalProcessed": 100,
  "sentCount": 95,
  "errorCount": 5,
  "sentContacts": [
    { "conversationId": "65f1a2b3c4d5e6f7a8b9c0d1", "phoneNumber": "5511999999999" }
  ],
  "errors": [
    { "row": 5, "contact": "João Silva", "error": "Número de telefone inválido" },
    { "row": 23, "contact": "Maria Santos", "error": "Template não encontrado para este inbox" }
  ]
}
```

<Warning>
  Um `200` **não** significa que todo mundo recebeu. Sempre leia `errorCount` e `errors` — se você
  ignorar esse bloco, falhas de envio passam despercebidas. `row` aponta a linha do arquivo ou o
  índice no array `contacts`.
</Warning>

Reenviar para quem falhou é responsabilidade da sua integração: não há retry automático.

## Limites que valem conhecer

* **Tier da conta no WhatsApp** — a Meta limita o número de conversas únicas iniciadas por dia
  (1.000, 10.000 ou 100.000, conforme o tier da sua conta). O disparo respeita esse teto: acima
  dele, os envios passam a falhar.
* **Tamanho de mídia** — imagem até 5 MB (JPG, PNG), vídeo até 16 MB (MP4, 3GPP), documento até
  100 MB (PDF, DOC/DOCX, XLS/XLSX, PPT/PPTX, TXT), áudio até 16 MB.
* **Links de mídia** — precisam ser URLs HTTPS públicas, acessíveis pela Meta. URL atrás de login
  ou de rede interna falha.
* **Tamanho do lote** — envie em blocos de até 1.000 contatos por requisição.

## Boas práticas

<Steps>
  <Step title="Teste com um contato só">
    Dispare primeiro para o seu próprio número e confira o resultado no aparelho — parâmetro fora
    de ordem só aparece na mensagem final.
  </Step>

  <Step title="Normalize os telefones antes">
    Use o formato internacional sem símbolos (`5511999999999`). Número inválido vira linha em
    `errors`, não exceção.
  </Step>

  <Step title="Guarde os conversationId">
    `sentContacts` devolve a conversa criada para cada contato — é por ela que você acompanha a
    resposta, via [webhooks](/guias/webhooks) ou `GET /v1/messages/conversation/{id}`.
  </Step>

  <Step title="Trate os erros como fila de reprocessamento">
    Reenvie apenas as linhas de `errors`, depois de corrigir o motivo.
  </Step>
</Steps>

## Próximos passos

<CardGroup cols={2}>
  <Card title="Webhooks" icon="webhook" href="/guias/webhooks">
    Receba as respostas de quem foi impactado pelo disparo.
  </Card>

  <Card title="Referência da API" icon="code" href="/api-reference/messages">
    Contrato completo do endpoint e dos demais envios.
  </Card>
</CardGroup>
