
# API de Modelos do WhatsApp

Os modelos de mensagem do WhatsApp são mensagens pré-escritas que foram aprovadas para envio fora da janela normal de conversação de 24 horas — por exemplo, uma mensagem de boas-vindas, um lembrete de marcação ou um aviso de reativação. Esta API permite listar, criar, editar, submeter, verificar, eliminar e enviar modelos programaticamente.

Todos os caminhos abaixo são relativos ao URL base da API:

```
https://api.youraiconnector.com/v1
```

Todos os pedidos devem ser autenticados. Consulte [Autenticação](authentication.md) para os quatro métodos aceites. Os exemplos nesta página utilizam o cabeçalho `X-API-Key` (e uma forma de parâmetro de consulta para cURL).

::: note
**Nota:** Os modelos funcionam através do canal da API do WhatsApp Business, pelo que esta parte da API requer acesso à API e um plano que inclua canais do WhatsApp. Sem estes, os pedidos são rejeitados com um `403`.
:::


---

## Trabalhar com subcontas (agências)


---

## Estados de aprovação

Como as mensagens enviadas fora de uma conversação aberta devem ser primeiro revistas pelo WhatsApp, cada modelo tem um `status` de aprovação:

| Estado | Significado |
|---|---|
| `draft` | Criado ou guardado, mas ainda não enviado para revisão. Ainda pode editá-lo. |
| `received` | Submetido e aceite na fila de revisão. |
| `pending` | Sob revisão. |
| `approved` | Autorizado para envio. |
| `rejected` | Rejeitado. O campo `rejection_reason` explica o motivo; corrija-o e submeta novamente. |

Apenas os modelos `draft` e `rejected` podem ser editados ou (re)submetidos. Assim que um modelo está `approved`, fica bloqueado — crie um novo se precisar de alterações.

> **Aprovação automática:** Alguns canais não requerem um passo de revisão externa. Os modelos criados ou submetidos para uma campanha num desses canais são guardados imediatamente como `approved`, sem um ID de conteúdo (`sid`).

---

## Modelos em contas ligadas à Meta

Estes endpoints funcionam da mesma forma, independentemente da ligação WhatsApp que a sua conta utiliza, mas o que acontece por trás difere:

- Numa **ligação WhatsApp gerida**, os modelos são registados junto do fornecedor de mensagens e `sid` é o ID de conteúdo do fornecedor (`HXXXXXXXX…`).
- Numa conta cujo número funciona na **sua própria Conta WhatsApp Business** (qualquer uma das opções de ligação Meta), os modelos são criados e revistos **nessa Conta WhatsApp Business** e `sid` é o ID de modelo da própria Meta — uma cadeia numérica como `"3394843740694756"`. O `status` continua a utilizar os valores na tabela acima, e o `rejection_reason` continua a conter a explicação da Meta.

Existem dois endpoints adicionais para isto: um para perguntar em que ligação se encontra e outro para reconciliar a sua lista de modelos com a sua Conta WhatsApp Business. Os modelos que já existem na Conta WhatsApp Business são importados para a sua biblioteca através da sincronização, pelo que um `GET /whatsapp-templates` posterior listá-los-á como qualquer outro modelo.

### Verificar em que ligação os modelos funcionam

`GET /whatsapp-templates/provider`

| Campo | Descrição |
|---|---|
| `provider` | `twilio` quando os modelos são registados junto do fornecedor de mensagens gerido, `meta` quando residem na sua própria Conta WhatsApp Business. |
| `lane` | Que ligação Meta está a ser utilizada — `meta_cloud_api` (a sua própria aplicação Meta) ou `meta_embedded` (ligada através da nossa aplicação Meta). `null` numa ligação gerida. |
| `waba_id` | A Conta WhatsApp Business onde os modelos são criados, ou `null`. |
| `templates_enabled` | `false` quando a ligação Meta ainda não está concluída (sem Conta WhatsApp Business ou token de acesso armazenado). A criação ou submissão de modelos falha com um `400` até que esteja. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/whatsapp-templates/provider?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates/provider", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/whatsapp-templates/provider",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "provider": "meta",
  "lane": "meta_cloud_api",
  "waba_id": "2357661648036355",
  "templates_enabled": true
}
```

### Sincronizar modelos da Meta

Atualiza o estado de aprovação de cada modelo que reside na sua Conta WhatsApp Business e importa qualquer modelo que lá exista, mas que ainda não esteja na sua biblioteca. É seguro chamar tantas vezes quantas desejar. Numa ligação gerida, não há nada para sincronizar, pelo que a chamada não faz nada e apenas reporta quantos modelos possui.

`POST /whatsapp-templates/meta-sync`

| Campo | Descrição |
|---|---|
| `imported` | Modelos encontrados na Conta WhatsApp Business que foram adicionados à sua biblioteca por esta chamada. |
| `updated` | Modelos existentes cujo estado ou detalhes foram alterados. |
| `total` | Modelos na sua biblioteca após a sincronização. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/meta-sync?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates/meta-sync", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/meta-sync",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "provider": "meta",
  "imported": 2,
  "updated": 5,
  "total": 12
}
```

### Falar diretamente com a Meta (avançado)

Se precisar de algo que os endpoints acima não expõem — cabeçalhos de modelo, rodapés, botões ou um modelo totalmente criado manualmente — o `/v1/meta-templates` transmite o seu pedido diretamente para a API de modelos da Meta, sem armazenar nada na sua biblioteca de modelos. Só funciona em contas cujo número funciona na sua própria Conta WhatsApp Business; numa ligação gerida, cada chamada devolve `400` pedindo-lhe que ligue primeiro uma aplicação Meta.

| Endpoint | O que faz |
|---|---|
| `GET /meta-templates` | Lista os modelos na sua Conta WhatsApp Business com o seu estado mais recente. Adicione `?name=` para filtrar por um nome de modelo exato. Devolve `{ "success": true, "templates": [...] }`. |
| `POST /meta-templates` | Cria um modelo e submete-o para revisão da Meta num único passo. Requer `name`, `language` e `body` (ou um array `components` completo em vez de `body`). Opcional: `variables` (array de strings), `category` (`MARKETING`, `UTILITY` ou `AUTHENTICATION`), `header`, `footer`, `buttons`. Devolve `201` com `{ "success": true, "template": {...} }`. |
| `DELETE /meta-templates/{name}` | Elimina o modelo pelo seu nome Meta — **todos os idiomas** do mesmo. Adicione `?hsm_id=` com o ID de modelo da Meta para remover apenas um idioma. Devolve `{ "success": true, "name": "..." }`. |

Um modelo que a Meta recusa devolve `400` com a explicação da própria Meta em `error`.

---

## Listar modelos

Devolve todos os modelos na sua conta, com um resumo simplificado de cada um.

`GET /whatsapp-templates`

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/whatsapp-templates?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/whatsapp-templates",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "data": [
    {
      "id": "template_abc123",
      "name": "welcome_message",
      "status": "approved",
      "language": "en",
      "body": "Hi {{first_name}}, thanks for reaching out!"
    },
    {
      "id": "template_def456",
      "name": "appointment_reminder",
      "status": "pending",
      "language": "en",
      "body": "Hi {{first_name}}, this is a reminder about your appointment."
    }
  ]
}
```

---

## Obter um modelo

Devolve os detalhes completos de um único modelo, incluindo as suas variáveis, estado e carimbos de data/hora.

`GET /whatsapp-templates/{templateId}`

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "template": {
    "id": "template_abc123",
    "name": "welcome_message",
    "body": "Hi {{first_name}}, thanks for reaching out!",
    "language": "en",
    "variables": ["first_name"],
    "status": "approved",
    "sid": "HXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "type": "general",
    "category": "marketing",
    "rejection_reason": null,
    "campaign_id": "campaign123",
    "date_created": "2026-06-01T10:00:00.000Z",
    "date_updated": "2026-06-02T08:30:00.000Z",
    "submitted_at": "2026-06-01T10:05:00.000Z",
    "approved_at": "2026-06-02T08:30:00.000Z"
  }
}
```

Um modelo que não existe na sua conta devolve `404` com `{ "success": false, "error": "Template not found" }`.

---

## Criar um modelo

Cria um modelo para a mensagem de abertura de uma campanha e submete-o para aprovação num único passo.

`POST /whatsapp-templates`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `campaign_id` | Sim | A campanha a que o modelo pertence. |
| `name` | Sim | Um nome para o modelo. |
| `language` | Sim | Código de idioma, por exemplo `en`, `es`, `de`, `pt_BR`, `zh_CN`. |
| `body` | Sim | O texto da mensagem, até 1024 caracteres. |
| `variables` | Não | Lista ordenada de nomes de variáveis utilizados no corpo. |

Os marcadores de posição de variáveis podem ser escritos como `{{first_name}}`, `{first_name}` ou `[first_name]` — todos são normalizados para o formato de chavetas duplas.

O resultado depende dos canais da campanha:

- **Campanha da API WhatsApp Business:** o conteúdo é enviado para revisão do WhatsApp. A resposta contém `campaign_status` (`received` ou `pending`) e um `template_sid`.
- **Um canal sem passo de revisão externa:** o modelo é guardado e aprovado automaticamente (`campaign_status: "approved"`, `template_sid: null`).
- **Sem canal WhatsApp na campanha:** nada é criado e `campaign_status` é `not_applicable`.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "campaign123",
    "name": "welcome_message",
    "language": "en",
    "body": "Hi {{first_name}}, thanks for reaching out!",
    "variables": ["first_name"]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaign_id: "campaign123",
    name: "welcome_message",
    language: "en",
    body: "Hi {{first_name}}, thanks for reaching out!",
    variables: ["first_name"],
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaign_id": "campaign123",
        "name": "welcome_message",
        "language": "en",
        "body": "Hi {{first_name}}, thanks for reaching out!",
        "variables": ["first_name"],
    },
)
data = res.json()
```

**Resposta** (submetido para revisão)

```json
{
  "success": true,
  "campaign_status": "pending",
  "template_sid": "HXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
```

---

## Criar um modelo autónomo

Cria um modelo na sua biblioteca de modelos sem o associar à mensagem de abertura de uma campanha. Este é o passo de criação do ciclo de vida que o resto desta página segue: crie-o aqui, edite-o, submeta-o para revisão, verifique o seu estado e elimine-o quando já não precisar dele.

`POST /whatsapp-templates/docs`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `name` | Sim | Um nome para o modelo. |
| `language` | Sim | Código do idioma, por exemplo `en`, `es`, `de`, `pt_BR`, `zh_CN`. |
| `body` | Sim | O texto da mensagem, até 1024 caracteres. |
| `variables` | Não | Lista ordenada de nomes de variáveis utilizados no corpo. |
| `status` | Não | `draft` (predefinição) guarda-o sem submeter; `submitted` coloca-o imediatamente na fila para revisão do WhatsApp. |
| `type` | Não | `general` (predefinição) ou `smart_followup`. |
| `category` | Não | `marketing`, `utility`, `authentication` ou `authentication-international`. |
| `campaign_id` | Não | Associa o modelo a uma das suas campanhas. |

> **Modelos de autenticação (código único).** O WhatsApp não aceita modelos de autenticação de texto livre: o corpo da mensagem é predefinido pelo WhatsApp e o modelo deve conter um botão de "copiar código". Quando cria um modelo com `category: "authentication"`, submetemo-lo nessa forma fixa por si. O seu `body` é mantido como a pré-visualização apresentada na aplicação, mas o texto que o seu contacto recebe é a redação do próprio WhatsApp (o código, um lembrete de segurança e uma nota de validade de 10 minutos). Declare exatamente uma variável, por exemplo `["code"]`, e passe o código quando enviar (consulte o campo `variables` em [Enviar um modelo para um contacto](#send-a-template-to-a-contact)). O código deve ter menos de 15 caracteres.

> **Que criação devo utilizar?** Utilize esta quando pretender um modelo que possa editar e submeter você mesmo. Utilize `POST /whatsapp-templates` (acima) quando pretender definir a mensagem de abertura de uma campanha — essa opção requer `campaign_id` e escreve diretamente na campanha.

Um modelo criado como `submitted` é enviado para revisão do WhatsApp em segundo plano, pelo que deve verificar o endpoint de estado para obter o resultado, em vez de esperar que este surja na resposta.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/docs?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "welcome_message",
    "language": "en",
    "body": "Hi {{first_name}}, thanks for reaching out!",
    "variables": ["first_name"],
    "status": "draft",
    "category": "marketing"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates/docs", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "welcome_message",
    language: "en",
    body: "Hi {{first_name}}, thanks for reaching out!",
    variables: ["first_name"],
    status: "draft",
    category: "marketing",
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/docs",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "welcome_message",
        "language": "en",
        "body": "Hi {{first_name}}, thanks for reaching out!",
        "variables": ["first_name"],
        "status": "draft",
        "category": "marketing",
    },
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "template_id": "template_abc123",
  "status": "draft"
}
```

A falta de um `name`, `language` ou `body`, um idioma não suportado, um `status` que não seja `draft` ou `submitted`, um `type` ou `category` desconhecido, ou um corpo com mais de 1024 caracteres devolve `400` com uma `error` explicativa. Um `campaign_id` que não seja uma das suas campanhas devolve `404`.

---

## Atualizar um modelo

Edita um modelo que ainda não foi aprovado. Apenas modelos com o estado `draft` ou `rejected` podem ser editados. Forneça qualquer combinação de `name`, `body`, `language` e `variables` — apenas os campos que enviar serão alterados.

`PUT /whatsapp-templates/{templateId}`

> A edição **não** submete o modelo para revisão. Utilize o endpoint de submissão posteriormente.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Hi {{first_name}}, here is an update for you.",
    "variables": ["first_name"]
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      body: "Hi {{first_name}}, here is an update for you.",
      variables: ["first_name"],
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "body": "Hi {{first_name}}, here is an update for you.",
        "variables": ["first_name"],
    },
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "template_id": "template_abc123"
}
```

Tentar editar um modelo que já se encontra `approved` (ou que, de outra forma, não é editável), não enviar campos ou enviar um valor inválido devolve `400` com uma `error` explicativa.

---

## Submeter um modelo para aprovação

Submete um modelo `draft` ou `rejected` para revisão. Os modelos num canal que não exija revisão externa são aprovados imediatamente; todos os outros são enviados para o WhatsApp e o `status` devolvido (normalmente `received` ou `pending`) é guardado no modelo.

`POST /whatsapp-templates/{templateId}/submit`

> Os **modelos de seguimento** devem declarar e utilizar as suas variáveis obrigatórias antes de poderem ser submetidos: um marcador de posição para o nome próprio, mais um marcador de posição de contexto pessoal para seguimentos inteligentes.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/submit" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/submit",
  { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/submit",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "template_id": "template_abc123",
  "status": "pending",
  "sid": "HXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
```

---

## Verificar o estado da aprovação

Um endpoint leve para consultar o estado atual de um modelo. O estado é lido a partir do registo guardado, que é atualizado periodicamente em segundo plano, pelo que uma aprovação ou rejeição muito recente pode demorar algum tempo a aparecer.

`GET /whatsapp-templates/{templateId}/status`

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/status",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/status",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "template_id": "template_abc123",
  "name": "welcome_message",
  "status": "approved",
  "sid": "HXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "rejection_reason": null,
  "date_updated": "2026-06-02T08:30:00.000Z"
}
```

---

## Eliminar um modelo

Remove o registo do modelo da sua conta.

`DELETE /whatsapp-templates/{templateId}`

::: warning
**Importante:** Numa ligação gerida, apenas o registo guardado é removido — o conteúdo que o WhatsApp já aprovou pode permanecer registado junto do fornecedor de mensagens. Numa conta que funciona na sua própria Conta WhatsApp Business, o modelo é também eliminado dessa conta. De qualquer forma, se uma campanha ainda utilizar este modelo, redirecione essa campanha para outro modelo **antes** de a eliminar, caso contrário, os envios que dependem dele falharão.
:::


**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123",
  { method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "template_id": "template_abc123",
  "note": "The template record was removed from your account. Content already approved by WhatsApp may remain registered with the messaging provider."
}
```

---

## Enviar um modelo para um contacto

Envia um modelo aprovado para um contacto, mesmo quando não existe uma conversa aberta — isto reabre a sessão de chat. Pode direcionar o contacto por `contactId` ou por `phoneNumber`, e escolher o modelo por `whatsappTemplateId` ou por `templateName`.

`POST /whatsapp-templates/send`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `contactId` | Um destes dois | O ID do contacto. |
| `phoneNumber` | Um destes dois | O número de telefone do contacto (com código de país, sem espaços). Pesquisado ou criado, se necessário. |
| `whatsappTemplateId` | Um destes dois | O ID do modelo. |
| `templateName` | Um destes dois | O nome do modelo, conforme apresentado na aplicação. |
| `firstName` | Não | Utilizado para preencher um contacto recém-criado. |
| `lastName` | Não | Utilizado para preencher um contacto recém-criado. |
| `email` | Não | Utilizado para preencher um contacto recém-criado. |
| `variables` | Não | Valores explícitos para as variáveis do modelo, indexados pelo nome da variável, por exemplo `{ "code": "482913" }`. Um valor aqui indicado prevalece sobre os campos do contacto para essa variável; as variáveis que omitir continuam a ser preenchidas a partir do contacto, conforme descrito abaixo. É desta forma que passa um código único para um modelo de autenticação. |

O corpo do modelo suporta substituição avançada de variáveis:

- **Variáveis básicas:** `{{first_name}}`, `{{email}}`, `{{company}}`
- **Valores predefinidos:** `{{first_name|there}}` mostra `there` se o campo estiver vazio
- **Transformações:** `{{company|uppercase}}`, `{{name|lowercase}}`, `{{name|capitalize}}`
- **Combinado:** `{{company|Your Company|uppercase}}`

> **Créditos:** O envio de um modelo consome créditos. O custo exato depende do país do destinatário e da categoria do modelo.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/send?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contactId": "contact123",
    "whatsappTemplateId": "template_abc123"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates/send", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    contactId: "contact123",
    whatsappTemplateId: "template_abc123",
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/send",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "contactId": "contact123",
        "whatsappTemplateId": "template_abc123",
    },
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "data": "WhatsApp template message sent successfully"
}
```

Um pedido sem um identificador de contacto e sem ambos os identificadores de modelo devolve `400`. Se a sua conta não tiver as credenciais de mensagens necessárias para enviar, a resposta é `403`.

---

## Criar ou atualizar o modelo ativo de uma campanha

Um segundo par de endpoints para o modelo de abertura de uma campanha, delimitado pelo caminho em vez de por um `campaign_id` no corpo. Estes são os que deve utilizar para uma campanha que já está ativa: ao contrário de [Criar um modelo](#create-a-template) acima, a atualização aqui também submete novamente os rascunhos de seguimento da campanha para revisão, para que o modelo de abertura e os seus seguimentos permaneçam sincronizados.

`POST /whatsapp-templates/campaign/{campaignId}` cria o modelo de abertura da campanha. `PUT /whatsapp-templates/campaign/{campaignId}` edita-o — a campanha tem de ter já um modelo, caso contrário, isto devolve `400`.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `name` | Sim | Um nome para o modelo. |
| `language` | Sim | Código do idioma, por exemplo `en`, `es`, `de`, `pt_BR`, `zh_CN`. |
| `body` | Sim | O texto da mensagem, até 1024 caracteres. |
| `variables` | Sim | Lista ordenada de nomes de variáveis utilizados no corpo. Passe uma matriz vazia se o modelo não utilizar nenhum. |

**cURL** (criar)

```bash
curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/campaign/campaign123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "welcome_message",
    "language": "en",
    "body": "Hi {{first_name}}, thanks for reaching out!",
    "variables": ["first_name"]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates/campaign/campaign123", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "welcome_message",
    language: "en",
    body: "Hi {{first_name}}, thanks for reaching out!",
    variables: ["first_name"],
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/campaign/campaign123",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "welcome_message",
        "language": "en",
        "body": "Hi {{first_name}}, thanks for reaching out!",
        "variables": ["first_name"],
    },
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "campaign_status": "pending",
  "template_sid": "HXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "message": "WhatsApp template created and campaign updated successfully."
}
```

Para editar, altere o método para `PUT` e utilize os mesmos campos — isto submete novamente o modelo de abertura (e os rascunhos de seguimento da campanha, numa campanha da API do WhatsApp) para revisão.

Uma campanha que não pertence à sua conta devolve `404`; uma campanha que pertence a outra conta para a qual não tem autorização devolve `403`. Editar uma campanha sem um modelo existente devolve `400`.

---

## Enviar um modelo para um contacto existente

Uma alternativa mais simples, delimitada pelo caminho, ao [Enviar um modelo para um contacto](#send-a-template-to-a-contact) acima: tanto o modelo como o contacto têm de existir previamente — nada é pesquisado pelo nome ou criado no momento.

`POST /whatsapp-templates/{templateId}/send-to-contact`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `contactId` | Sim | O ID do contacto. Tem de pertencer à sua conta. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/send-to-contact?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "contactId": "contact123" }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/send-to-contact",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ contactId: "contact123" }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/send-to-contact",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"contactId": "contact123"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "data": "WhatsApp template message sent successfully"
}
```

> **Créditos:** O envio consome créditos, com o mesmo preço que o endpoint acima. Um `contactId` que esteja em falta ou que não pertença à sua conta devolve `403`; um `templateId` que não exista devolve `404`.

---

## Envio em massa de um modelo

Envie um modelo para muitos contactos numa única chamada, com uma previsão de custos que pode mostrar antes de confirmar.

### Estimar o custo primeiro

Devolve o custo do envio, discriminado por país de destino, sem enviar nada nem gastar créditos. O preço do modelo é por país de destino, pelo que este cálculo tem de ser efetuado no servidor com base nos contactos reais, em vez de ser estimado no lado do cliente.

`POST /whatsapp-templates/{templateId}/estimate-bulk-cost`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `contactIds` | Sim | Contactos a orçamentar, até 500 por chamada. As duplicadas são contadas uma vez. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/estimate-bulk-cost?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "contactIds": ["contact123", "contact456"] }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/estimate-bulk-cost",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ contactIds: ["contact123", "contact456"] }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/estimate-bulk-cost",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"contactIds": ["contact123", "contact456"]},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "data": {
    "countries": [
      {
        "countryCode": "1",
        "name": "United States",
        "iso": "US",
        "flag": "🇺🇸",
        "contactCount": 120,
        "costPerContact": 0.5,
        "subtotal": 60.0
      }
    ],
    "totalContacts": 120,
    "totalTemplateCost": 60.0,
    "templateCategory": "marketing",
    "skippedContacts": 2
  }
}
```

`skippedContacts` conta os ids que estavam em falta, que não eram seus ou que não tinham número de telefone — a estimativa cobre apenas o resto, pelo que um valor diferente de zero significa que o envio real chegará a menos contactos do que os selecionados.

### Enviar o lote

Envia o modelo a todos os contactos da lista, resolvendo quaisquer variáveis inteligentes por contacto e cobrando créditos por envio.

`POST /whatsapp-templates/{templateId}/bulk-send`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `contactIds` | Sim | Contactos para os quais enviar, até 5000 por chamada. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/bulk-send?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "contactIds": ["contact123", "contact456"] }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/bulk-send",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ contactIds: ["contact123", "contact456"] }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/bulk-send",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"contactIds": ["contact123", "contact456"]},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "data": { "sent": 118, "failed": 2, "total": 120 }
}
```

Um contacto que falhe (não encontrado, não associado à sua conta ou erro de envio) é ignorado e contabilizado em `failed` em vez de interromper o lote. Um `contactIds` vazio, mais de 5000 ids num envio (500 numa estimativa) ou um `templateId` em falta devolve `400`.

---

## Tentar novamente uma mensagem falhada

Dois endpoints para reenviar uma mensagem que falhou, sem criar um novo registo de mensagem nem gastar créditos novamente.

`POST /whatsapp-templates/messages/{contactId}/{messageId}/retry-template` tenta novamente uma mensagem de modelo falhada especificamente — resolve novamente o conteúdo do modelo a partir da campanha se a mensagem falhada ainda não o contiver. Apenas mensagens com o estado `failed` e tipo `template` podem ser tentadas novamente desta forma.

`POST /whatsapp-templates/messages/{contactId}/{messageId}/retry` é independente do canal e funciona para qualquer mensagem não baseada em modelo que tenha falhado (por exemplo, WhatsApp Web), encaminhando para o caminho de envio correto com base no canal da mensagem. Aceita o estado `failed`, `failed_connection`, `limit_exceeded` ou `queued_retry`.

Nenhum dos endpoints aceita um corpo de pedido.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/messages/contact123/msg_abc789/retry-template?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/messages/contact123/msg_abc789/retry-template",
  { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/messages/contact123/msg_abc789/retry-template",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "data": "Message retry initiated successfully"
}
```

Para a versão independente do canal, altere o caminho para `.../msg_abc789/retry`. Uma mensagem cujo estado não seja elegível para nova tentativa, ou (no endpoint de modelo) que não seja uma mensagem de modelo, devolve `400`. Um contacto ou mensagem em falta devolve `404`.

---

## Perfil do WhatsApp Business

Faça a gestão do perfil do WhatsApp Business (informações, morada, descrição, e-mail, websites, categoria da empresa e logótipo) apresentado aos contactos no WhatsApp. Funciona tanto numa ligação gerida como numa conta que executa a sua própria Conta WhatsApp Business.

### Guardar o perfil

`PUT /whatsapp-templates/profile`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `phoneNumber` | Sim | O número de WhatsApp a que este perfil pertence. Deve estar ligado à sua conta. |
| `about` | Não | Texto curto de "Informações" apresentado no perfil. |
| `address` | Não | Morada da empresa. |
| `description` | Não | Descrição mais longa da empresa. |
| `email` | Não | E-mail de contacto apresentado no perfil. |
| `websites` | Não | Matriz de URLs de websites. Cada um deve ser um URL válido. |
| `vertical` | Não | Categoria da empresa, por exemplo `Retail` ou `Professional Services`. |
| `profilePictureHandle` | Não | O identificador devolvido pelo endpoint de carregamento de imagem abaixo, para definir a fotografia de perfil. |

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/whatsapp-templates/profile?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumber": "+31612345678",
    "about": "We reply within a few hours",
    "email": "support@example.com",
    "websites": ["https://example.com"]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates/profile", {
  method: "PUT",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phoneNumber: "+31612345678",
    about: "We reply within a few hours",
    email: "support@example.com",
    websites: ["https://example.com"],
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/whatsapp-templates/profile",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "phoneNumber": "+31612345678",
        "about": "We reply within a few hours",
        "email": "support@example.com",
        "websites": ["https://example.com"],
    },
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "data": "WhatsApp Business profile updated successfully"
}
```

Um `phoneNumber` em falta, um URL de website inválido ou um `phoneNumber` não ligado à sua conta devolve `400` ou `404`.

### Carregar uma fotografia de perfil

Transfere uma imagem a partir de um URL fornecido por si e carrega-a para o WhatsApp, devolvendo um identificador. Passe esse identificador como `profilePictureHandle` na chamada de guardar perfil acima para a definir como fotografia — este endpoint apenas carrega a imagem, não a define por si só.

`POST /whatsapp-templates/profile/picture`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `phoneNumber` | Sim | O número de WhatsApp a que este perfil pertence. |
| `fileUrl` | Sim | Um URL publicamente acessível para a imagem a carregar. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/profile/picture?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumber": "+31612345678",
    "fileUrl": "https://example.com/logo.png"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates/profile/picture", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phoneNumber: "+31612345678",
    fileUrl: "https://example.com/logo.png",
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/profile/picture",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "phoneNumber": "+31612345678",
        "fileUrl": "https://example.com/logo.png",
    },
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "data": "1234567890123456"
}
```

`data` é o identificador da imagem carregada. Um `phoneNumber` ou `fileUrl` em falta, ou um `phoneNumber` sem token de acesso ao WhatsApp em ficheiro, devolve `400`; um `fileUrl` inacessível ou inválido devolve um erro a descrever o motivo da falha da transferência.

---

## Verificar o estado de um remetente

Consulta (e atualiza) o estado de envio em tempo real de um número de WhatsApp ligado junto do fornecedor de mensagens. Útil para confirmar que um número está realmente apto a enviar mensagens antes de depender dele.

`GET /whatsapp-templates/sender-status/{phoneNumber}`

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/whatsapp-templates/sender-status/+31612345678" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/sender-status/+31612345678",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/whatsapp-templates/sender-status/+31612345678",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "data": "ONLINE"
}
```

`data` é um de `ONLINE` (a enviar normalmente), `PENDING` (ainda a ser verificado) ou `DELETED` (o fornecedor já não reconhece este remetente — volte a ligar o número). Um `phoneNumber` sem informações de empresa do WhatsApp em ficheiro devolve `404`.

---

## Gerar modelos de seguimento com IA

A plataforma pode escrever os modelos de seguimento de WhatsApp de uma campanha por si — os lembretes enviados quando uma conversa fica inativa — a partir das instruções e do objetivo da própria campanha. Existe um endpoint de tarefa que é executado em segundo plano, além de três endpoints mais antigos mantidos para integrações existentes. Todos eles utilizam créditos de IA.

### Iniciar uma tarefa de geração

`POST /campaigns/{campaignId}/template-generation`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `type` | Não | `all` (o padrão) escreve todo o conjunto de seguimento. `cold_only` escreve apenas as mensagens para contactos que nunca responderam. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/campaign_abc123/template-generation?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "type": "all" }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/campaign_abc123/template-generation",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ type: "all" }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/campaigns/campaign_abc123/template-generation",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"type": "all"},
)
data = res.json()
```

**Resposta** (`202`)

```json
{ "success": true, "campaign_id": "campaign_abc123", "type": "all" }
```

A chamada retorna assim que a tarefa é colocada na fila. Leia a campanha (`GET /campaigns/{campaignId}`, consulte a [API de Campanhas](campaigns.md)) e observe o seu objeto `template_generation_status` até que termine:

| Campo | Descrição |
|---|---|
| `status` | `processing` enquanto a tarefa é executada, depois `completed` ou `failed`. |
| `progress` | 0 a 100. |
| `current_template`, `total_templates` | Quantos modelos foram escritos até ao momento, do total que a tarefa irá escrever — 11 para uma campanha de saída ou combinada, 9 caso contrário. |
| `error` | O motivo pelo qual uma tarefa `failed` parou, por exemplo, falta de créditos. |
| `started_at`, `completed_at` | Quando a tarefa começou e terminou. |

Os modelos gerados são aplicados à campanha como qualquer outro, pelo que aparecem em [Listar modelos](#list-templates) e passam pela aprovação do WhatsApp antes de poderem ser enviados. Um `400` significa que `type` foi algo diferente de `all` ou `cold_only`; um `404` significa que a campanha não existe ou pertence a outra conta.

Os Agentes têm um equivalente a esta chamada, `POST /agents/{agentId}/template-generation`, que escreve os seguimentos para um Agente e termina durante a chamada no caso habitual — consulte [Gerar mensagens de seguimento](agents.md#generate-follow-up-messages) na API de Agentes de IA.

### Os endpoints de geração mais antigos

Três endpoints anteriores fazem o mesmo trabalho e são mantidos para que as integrações existentes continuem a funcionar. O novo código deve utilizar o endpoint de tarefa acima.

| Endpoint | O que faz |
|---|---|
| `POST /whatsapp-templates/campaign/{campaignId}/generate-async` | Inicia a geração de seguimento para a campanha em segundo plano e retorna `202` com `{ "success": true, "data": { "result": "success", "message": "..." } }`. Os créditos são cobrados antecipadamente (ignorados numa conta que traz a sua própria chave de IA) e o `template_generation_status` da campanha reporta o progresso exatamente como acima. |
| `POST /whatsapp-templates/campaign/{campaignId}/generate-followups` | Gera todos os nove modelos de seguimento durante a chamada — para uma campanha criada antes da existência de seguimentos automáticos, ou uma que precise de ser escrita novamente — e retorna `200` com `templatesGenerated` dentro de `data`. |
| `POST /whatsapp-templates/agent/{agentId}/generate-followups` | A mesma geração síncrona endereçada pelo Agente. A resposta adiciona `agent_id`, `campaign_id` e `target`: `"campaign"` quando os modelos foram escritos na campanha do Agente, `"agent"` (com `campaign_id: null`) quando o Agente não tem campanha e estes foram armazenados no próprio Agente. Um Agente em falta ou estrangeiro é um `404`. |

Todos os três necessitam de seguimentos automáticos na conta e créditos suficientes — um `400` indica qual está em falta — e o par endereçado à campanha retorna `403` quando a campanha pertence a outra conta.

---

## Erros da API de modelos

Os endpoints de modelos devolvem o envelope de erro padrão:

```json
{
  "success": false,
  "error": "Template not found"
}
```

Um `404` nestes endpoints significa geralmente que o recurso não foi encontrado — ou não existe ou pertence a outra conta. Alguns endpoints (a criação/atualização com âmbito de campanha e envios para um contacto existente) devolvem `403` em vez disso quando a campanha ou o contacto pertence a outra pessoa em vez de não existir de todo. Alguns endpoints incluem também um campo `error_code` que reflete o estado HTTP. Os códigos partilhados que qualquer endpoint pode devolver — `400`, `401`, `403` (o seu plano não inclui acesso à API), `429` (limite de taxa) e `500` — estão listados com orientações de repetição em [Erros e Paginação](errors-and-pagination.md).

---

## Próximos passos

- [Autenticação](authentication.md) — as quatro formas de autenticar um pedido.
- [Erros e Limites de Taxa](errors-and-pagination.md) — códigos de estado e o limite de 300 pedidos/min.
- [API de Campanhas](campaigns.md) — gerir as campanhas às quais os modelos estão associados.
