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

# Disparos de campanha pela API

> Guia completo para criar, segmentar, iniciar e acompanhar campanhas de mensagem em massa via gateway público — templates, variáveis, filtros e monitoramento.

Campanhas pela API permitem que integrações externas disparem mensagens em massa com a **mesma flexibilidade do painel**: escolher plataforma, modelo de mensagem, cadência de envio e público-alvo filtrado por tags, funil, canal de entrada, datas e dezenas de outros critérios.

Este guia cobre o fluxo completo, do zero ao acompanhamento da execução. Para autenticação e chaves, veja a [Introdução à API](/api-reference/introduction). Para o conceito de disparo no produto, veja [Disparos](/conceitos/disparos) e [Modelos de mensagem](/conceitos/modelos-de-mensagem).

## Pré-requisitos

| Requisito             | Detalhe                                                                                                                                                              |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Chave secreta**     | Todas as rotas desta seção exigem `sk_` no cabeçalho `X-API-Key`.                                                                                                    |
| **Ambiente ativo**    | O ambiente de desenvolvedores deve estar habilitado em [Configurações → Desenvolvedores](https://crmspark.com.br/dash/settings/developers).                          |
| **Plano ativo**       | A organização precisa de assinatura ativa (`SUBSCRIPTION_INACTIVE` retorna 403).                                                                                     |
| **Modelo cadastrado** | Templates são criados e editados no painel ([Modelos de mensagem](https://crmspark.com.br/dash/templates)); a API apenas lista e consulta.                           |
| **Chats existentes**  | O público-alvo são chats já presentes no CRM. Para incluir contatos novos, use [formulários](/conceitos/formularios-de-captacao) ou a API de chats antes do disparo. |

```http theme={null}
X-API-Key: sk_sua_chave_secreta
Content-Type: application/json
```

Base URL: [gateway.crmspark.com.br](https://gateway.crmspark.com.br) (prefixo `/v1`).

## Como funciona

Uma campanha passa por estados previsíveis:

```mermaid theme={null}
stateDiagram-v2
  [*] --> draft: POST /v1/campaigns
  draft --> processing: POST /v1/campaigns/{id}/start
  processing --> completed: todos os envios concluídos
  processing --> failed: erro irrecuperável
  processing --> canceled: POST /v1/campaigns/{id}/cancel
  draft --> canceled: cancelamento manual
```

**Por que criar e iniciar em duas etapas?**

No painel, ao clicar em "Iniciar", o Spark abre o diálogo de variáveis do modelo antes de disparar. A API reproduz esse comportamento:

1. **`POST /v1/campaigns`** — salva a campanha em `draft` com template, plataforma, cadência e filtros.
2. **`POST /v1/campaigns/{id}/start`** — valida variáveis manuais, resolve o público e enfileira os envios.

Isso permite inspecionar a configuração, estimar o público com `audience-preview` e só então confirmar o disparo com as variáveis corretas.

## Visão geral dos endpoints

| Método | Rota                                          | Descrição                                    |
| ------ | --------------------------------------------- | -------------------------------------------- |
| `GET`  | `/v1/message-templates`                       | Lista modelos disponíveis                    |
| `GET`  | `/v1/message-templates/{id}`                  | Detalhe do modelo (inclui variáveis manuais) |
| `GET`  | `/v1/campaigns/filters/schema`                | Catálogo de campos, operadores e formatos    |
| `POST` | `/v1/campaigns/audience-preview`              | Estima quantidade de chats no público        |
| `POST` | `/v1/campaigns`                               | Cria campanha em rascunho                    |
| `GET`  | `/v1/campaigns`                               | Lista campanhas da organização               |
| `GET`  | `/v1/campaigns/{id}`                          | Detalhe + histórico de execuções             |
| `GET`  | `/v1/campaigns/{id}/executions/{executionId}` | Progresso em tempo real                      |
| `POST` | `/v1/campaigns/{id}/start`                    | Inicia o disparo                             |
| `POST` | `/v1/campaigns/{id}/cancel`                   | Cancela execução em andamento                |

A referência interativa ao lado (OpenAPI) documenta cada campo, código de erro e exemplo de request/response.

***

## Tutorial passo a passo

<Steps>
  <Step title="Escolher o modelo de mensagem">
    Liste os templates e anote o `id` do modelo desejado:

    ```bash theme={null}
    curl -s https://gateway.crmspark.com.br/v1/message-templates \
      -H "X-API-Key: sk_…"
    ```

    Resposta resumida:

    ```json theme={null}
    {
      "templates": [
        {
          "id": "message_template_abc123",
          "name": "Promoção VIP",
          "description": null,
          "isOfficial": true,
          "metaTemplateStatus": "APPROVED",
          "createdAt": "2025-05-01T12:00:00.000Z",
          "updatedAt": "2025-05-10T08:30:00.000Z"
        }
      ]
    }
    ```

    O campo `isOfficial` indica se o modelo passou pela aprovação da Meta (WhatsApp Business / Instagram). Modelos oficiais só podem ser usados com `platform: "whatsapp"` ou `"instagram"`.
  </Step>

  <Step title="Consultar variáveis do modelo">
    Antes de iniciar, veja quais variáveis **manuais** você precisará preencher:

    ```bash theme={null}
    curl -s https://gateway.crmspark.com.br/v1/message-templates/message_template_abc123 \
      -H "X-API-Key: sk_…"
    ```

    ```json theme={null}
    {
      "id": "message_template_abc123",
      "name": "Promoção VIP",
      "isOfficial": true,
      "items": [
        { "id": "…", "order": 0, "type": "text", "text": "Olá {{1}}, aproveite {{2}}!", "mediaId": null }
      ],
      "commonVariables": [
        { "key": "var1", "label": "Nome da promoção", "required": true },
        { "key": "var2", "label": "Código do cupom", "required": true }
      ]
    }
    ```

    Cada entrada em `commonVariables` corresponde a uma variável **manual** do modelo. Variáveis automáticas (nome do chat, contato, etc.) **não aparecem** aqui — o Spark resolve por destinatário no envio.
  </Step>

  <Step title="Montar filtros de público-alvo">
    Consulte o catálogo de campos:

    ```bash theme={null}
    curl -s https://gateway.crmspark.com.br/v1/campaigns/filters/schema \
      -H "X-API-Key: sk_…"
    ```

    Estime o tamanho do público **antes** de criar a campanha:

    ```bash theme={null}
    curl -s -X POST https://gateway.crmspark.com.br/v1/campaigns/audience-preview \
      -H "X-API-Key: sk_…" \
      -H "Content-Type: application/json" \
      -d '{
        "filters": [
          {
            "combinator": "and",
            "statements": [
              { "field": "chat.platform", "operator": "equals", "value": "whatsapp" },
              { "field": "chat.tagIds", "operator": "has-any", "value": ["tag_vip123"] }
            ]
          }
        ]
      }'
    ```

    ```json theme={null}
    { "estimatedCount": 142 }
    ```

    Se `estimatedCount` for 0, revise os filtros antes de prosseguir.
  </Step>

  <Step title="Criar a campanha (rascunho)">
    ```bash theme={null}
    curl -s -X POST https://gateway.crmspark.com.br/v1/campaigns \
      -H "X-API-Key: sk_…" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Promoção maio 2025",
        "description": "Clientes VIP no WhatsApp Business",
        "platform": "whatsapp",
        "templateId": "message_template_abc123",
        "cadence": 10,
        "filters": [
          {
            "combinator": "and",
            "statements": [
              { "field": "chat.platform", "operator": "equals", "value": "whatsapp" },
              { "field": "chat.tagIds", "operator": "has-any", "value": ["tag_vip123"] }
            ]
          }
        ]
      }'
    ```

    ```json theme={null}
    {
      "campaignId": "campaign_xyz789",
      "status": "draft"
    }
    ```

    Guarde o `campaignId` — você usará nas próximas chamadas.
  </Step>

  <Step title="Iniciar o disparo">
    Envie `commonVariables` se o template tiver variáveis manuais:

    ```bash theme={null}
    curl -s -X POST https://gateway.crmspark.com.br/v1/campaigns/campaign_xyz789/start \
      -H "X-API-Key: sk_…" \
      -H "Content-Type: application/json" \
      -d '{
        "commonVariables": {
          "var1": "Promoção de maio",
          "var2": "15OFF"
        }
      }'
    ```

    ```json theme={null}
    {
      "executionId": "campaign_execution_def456",
      "status": "processing"
    }
    ```

    Se o modelo não tiver variáveis manuais, envie `{}` ou omita o body.
  </Step>

  <Step title="Acompanhar progresso">
    Faça polling até `status` ser `completed`, `failed` ou `canceled`:

    ```bash theme={null}
    curl -s https://gateway.crmspark.com.br/v1/campaigns/campaign_xyz789/executions/campaign_execution_def456 \
      -H "X-API-Key: sk_…"
    ```

    ```json theme={null}
    {
      "id": "campaign_execution_def456",
      "status": "processing",
      "targetCount": 142,
      "sentCount": 87,
      "failedCount": 2,
      "progress": 61
    }
    ```

    Intervalo sugerido: a cada 10–30 segundos. `progress` vai de 0 a 100 com base em `sentCount + failedCount` sobre `targetCount`.
  </Step>
</Steps>

***

## Plataforma e compatibilidade com templates

O campo `platform` no body da campanha define o **canal de envio** e impacta cadência, limites e compatibilidade com templates:

| Valor           | Canal                           | Templates oficiais | Observação                                                        |
| --------------- | ------------------------------- | ------------------ | ----------------------------------------------------------------- |
| `whatsapp`      | WhatsApp Business (API oficial) | ✅ Sim              | Permite envio fora da janela de 24h com modelo aprovado pela Meta |
| `whatsapp_lite` | WhatsApp Lite                   | ❌ Não              | Apenas modelos internos; envio dentro da janela de resposta       |
| `instagram`     | Instagram Direct                | ✅ Sim              | Modelos oficiais aprovados pela Meta                              |

<Warning title="platform ≠ filtro de público">
  O campo `platform` da campanha **não restringe automaticamente** quais chats entram no público. Para enviar só para chats de um canal, inclua explicitamente um filtro `chat.platform` (veja [Filtros de público-alvo](#filtros-de-público-alvo)).
</Warning>

Se você escolher um template oficial (`isOfficial: true`) com `platform: "whatsapp_lite"`, a API retorna `CAMPAIGN_TEMPLATE_PLATFORM_MISMATCH`.

***

## Cadência

`cadence` é um número inteiro ≥ 1 que representa **quantas mensagens por minuto** a campanha tenta enviar.

Exemplo: `"cadence": 10` → até 10 mensagens por minuto, espaçadas uniformemente.

Cada plataforma tem limites internos de throughput. Valores muito altos não aceleram além do teto da plataforma — o Spark respeita os limites para evitar bloqueios nos canais.

<Tip title="Comece conservador">
  Para a primeira integração, use cadência baixa (5–10) e aumente gradualmente conforme observar taxa de sucesso e respostas dos destinatários.
</Tip>

***

## Variáveis do modelo

Modelos podem ter trechos dinâmicos (`{{1}}`, `{{2}}`, …). Na API existem dois comportamentos:

### Variáveis manuais (`commonVariables`)

Valores que **você informa uma vez** no start e que valem **iguais para todos** os destinatários da execução.

| No modelo (texto) | Chave na API | Exemplo                    |
| ----------------- | ------------ | -------------------------- |
| `{{1}}`           | `var1`       | `"Promoção de maio"`       |
| `{{2}}`           | `var2`       | `"15OFF"`                  |
| `{{3}}`           | `var3`       | `"https://loja.com/promo"` |

```json theme={null}
{
  "commonVariables": {
    "var1": "Promoção de maio",
    "var2": "15OFF"
  }
}
```

**Como saber o que enviar:** chame `GET /v1/message-templates/{id}` e leia o array `commonVariables`. Cada item tem `key` (ex.: `var1`), `label` (nome amigável) e `required`.

Se faltar alguma variável manual obrigatória, a API responde com `CAMPAIGN_MISSING_COMMON_VARIABLES`.

### Variáveis automáticas (dados do chat)

Configuradas no painel como **Automático — dados do chat** (nome, contato, aniversário, campo personalizado). O Spark preenche **por destinatário** no momento do envio.

**Não envie** variáveis automáticas no body — elas não aparecem em `commonVariables` e são resolvidas internamente.

<Note title="Dados vazios">
  Se um chat não tiver nome ou campo personalizado preenchido, a variável automática correspondente pode sair em branco na mensagem. Antes de disparar em massa, confira a qualidade dos dados ou use filtros para excluir chats incompletos.
</Note>

### Exemplo combinado

Modelo: `"Olá {{1}}, aproveite {{2}} com desconto exclusivo!"`

* `{{1}}` → automática (nome do chat) — **não enviar**
* `{{2}}` → manual (nome da promoção) — enviar como `"var2": "Black Friday"`

Body do start:

```json theme={null}
{
  "commonVariables": {
    "var2": "Black Friday"
  }
}
```

Cada destinatário recebe: `"Olá Maria, aproveite Black Friday com desconto exclusivo!"` (Maria vem do chat; Black Friday é fixo).

Para detalhes sobre tipos de variável e configuração no painel, veja [Modelos de mensagem](/conceitos/modelos-de-mensagem).

***

## Filtros de público-alvo

Filtros definem **quais chats** recebem a campanha. A estrutura é a mesma usada no painel de disparos.

### Estrutura JSON

```json theme={null}
{
  "filters": [
    {
      "combinator": "and",
      "statements": [
        {
          "field": "chat.platform",
          "operator": "equals",
          "value": "whatsapp"
        },
        {
          "field": "chat.tagIds",
          "operator": "has-any",
          "value": ["tag_abc", "tag_def"]
        }
      ]
    }
  ]
}
```

### Regras de combinação

| Nível                          | Regra                                               |
| ------------------------------ | --------------------------------------------------- |
| **Entre grupos** (`filters[]`) | **AND** — todos os grupos devem ser satisfeitos     |
| **Dentro de cada grupo**       | `combinator: "and"` ou `"or"` entre as `statements` |

**Exemplo com OR dentro de um grupo:**

"Chats com tag VIP **ou** tag Premium, desde que sejam WhatsApp":

```json theme={null}
{
  "filters": [
    {
      "combinator": "or",
      "statements": [
        { "field": "chat.tagIds", "operator": "has-any", "value": ["tag_vip"] },
        { "field": "chat.tagIds", "operator": "has-any", "value": ["tag_premium"] }
      ]
    },
    {
      "combinator": "and",
      "statements": [
        { "field": "chat.platform", "operator": "equals", "value": "whatsapp" }
      ]
    }
  ]
}
```

Grupo 1 (VIP ou Premium) **AND** Grupo 2 (WhatsApp).

### Campos disponíveis

Consulte sempre `GET /v1/campaigns/filters/schema` para a lista atualizada. Referência completa:

| Campo                                   | Tipo    | Formato do valor | Descrição                                                        |
| --------------------------------------- | ------- | ---------------- | ---------------------------------------------------------------- |
| `chat.platform`                         | string  | string           | Plataforma: `whatsapp`, `whatsapp_lite`, `instagram`, `telegram` |
| `chat.status`                           | string  | string           | Status do chat (`open`, `closed`, etc.)                          |
| `chat.monetization`                     | number  | number           | Valor de monetização                                             |
| `chat.entrypointId`                     | string  | ID semântico     | Canal de entrada vinculado                                       |
| `chat.funnelStepId`                     | string  | ID semântico     | Etapa do funil                                                   |
| `chat.trackingLinkId`                   | string  | ID semântico     | Link de rastreamento                                             |
| `chat.isGroupChat`                      | boolean | boolean          | Se é grupo                                                       |
| `chat.isAnswered`                       | boolean | boolean          | Se o time já respondeu                                           |
| `chat.needsHumanAttention`              | boolean | boolean          | Se precisa de atenção humana                                     |
| `chat.attentionReason`                  | string  | string           | Motivo de atenção                                                |
| `chat.tagIds`                           | array   | array de IDs     | Tags vinculadas                                                  |
| `chat.assignedUserId`                   | string  | ID semântico     | Responsável pelo chat                                            |
| `chat.virtual.participants-count`       | number  | number           | Quantidade de participantes                                      |
| `chat.virtual.dm-participant.contact`   | string  | string           | Contato do participante (DM)                                     |
| `chat.virtual.dm-participant.birthDate` | date    | ISO 8601         | Aniversário do participante                                      |
| `chat.virtual.lastMessageAt`            | date    | ISO 8601         | Data da última mensagem                                          |
| `chat.virtual.lastUpdateAt`             | date    | ISO 8601         | Data da última atualização                                       |

IDs semânticos (`tag_…`, `entrypoint_…`, `funnel_step_…`) são os mesmos retornados pela API interna e visíveis no painel.

### Operadores por tipo

| Tipo             | Operadores                                                                                                                                                           |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **string**       | `equals`, `not-equals`, `contains`, `not-contains`, `in`, `not-in`, `exists`, `not-exists`, `starts-with`, `ends-with`, `not-starts-with`, `not-ends-with`           |
| **number**       | `equals`, `not-equals`, `in`, `not-in`, `greater-than`, `less-than`, `greater-than-or-equal`, `less-than-or-equal`, `exists`, `not-exists`, `between`, `not-between` |
| **boolean**      | `equals`, `not-equals`, `exists`, `not-exists`                                                                                                                       |
| **date**         | `equals`, `not-equals`, `in`, `not-in`, `greater-than`, `less-than`, `greater-than-or-equal`, `less-than-or-equal`, `exists`, `not-exists`, `between`, `not-between` |
| **array** (tags) | `has-any`, `has-all`, `has-none`, `exists`, `not-exists`                                                                                                             |

Operadores `exists` / `not-exists` **não exigem** `value`. Operadores `between` / `not-between` usam `value` e `value2`.

### Exemplos práticos de filtros

**Apenas WhatsApp Business:**

```json theme={null}
{ "field": "chat.platform", "operator": "equals", "value": "whatsapp" }
```

**Chats com tag "Cliente ativo" (has-all garante que a tag está presente):**

```json theme={null}
{ "field": "chat.tagIds", "operator": "has-all", "value": ["tag_cliente_ativo"] }
```

**Chats sem nenhuma das tags "Inativo" ou "Bloqueado":**

```json theme={null}
{ "field": "chat.tagIds", "operator": "has-none", "value": ["tag_inativo", "tag_bloqueado"] }
```

**Última mensagem nos últimos 7 dias:**

```json theme={null}
{
  "field": "chat.virtual.lastMessageAt",
  "operator": "greater-than",
  "value": "2025-05-20T00:00:00.000Z"
}
```

**Aniversariantes em maio (intervalo de datas):**

```json theme={null}
{
  "field": "chat.virtual.dm-participant.birthDate",
  "operator": "between",
  "value": "1990-05-01T00:00:00.000Z",
  "value2": "1990-05-31T23:59:59.999Z"
}
```

**Chats em etapa específica do funil:**

```json theme={null}
{ "field": "chat.funnelStepId", "operator": "equals", "value": "funnel_step_novo_lead" }
```

**Chats que ainda não foram respondidos:**

```json theme={null}
{ "field": "chat.isAnswered", "operator": "equals", "value": false }
```

**Canal de entrada específico:**

```json theme={null}
{ "field": "chat.entrypointId", "operator": "in", "value": ["entrypoint_whatsapp_loja", "entrypoint_whatsapp_suporte"] }
```

<Tip title="Sempre valide com audience-preview">
  Antes de iniciar uma campanha grande, chame `POST /v1/campaigns/audience-preview` com os mesmos filtros. Isso evita surpresas com público vazio ou maior que o esperado.
</Tip>

***

## Estimar público (`audience-preview`)

Calcula quantos chats corresponderiam aos filtros **sem criar campanha** e **sem enviar mensagens**.

```json theme={null}
POST /v1/campaigns/audience-preview

{
  "filters": [
    {
      "combinator": "and",
      "statements": [
        { "field": "chat.platform", "operator": "equals", "value": "whatsapp" },
        { "field": "chat.isAnswered", "operator": "equals", "value": true }
      ]
    }
  ]
}
```

Resposta:

```json theme={null}
{ "estimatedCount": 142 }
```

O endpoint aceita os mesmos filtros de `POST /v1/campaigns`. Omitir `filters` conta **todos** os chats da organização.

***

## Status e monitoramento

### Status da campanha

| Status       | Significado                     |
| ------------ | ------------------------------- |
| `draft`      | Criada, ainda não iniciada      |
| `processing` | Execução em andamento           |
| `completed`  | Todos os envios finalizados     |
| `canceled`   | Cancelada manualmente           |
| `failed`     | Falha irrecuperável na execução |

### Detalhe com execuções

`GET /v1/campaigns/{id}` retorna a campanha e o histórico de execuções:

```json theme={null}
{
  "id": "campaign_xyz789",
  "name": "Promoção maio 2025",
  "status": "processing",
  "platform": "whatsapp",
  "templateId": "message_template_abc123",
  "cadence": 10,
  "executions": [
    {
      "id": "campaign_execution_def456",
      "status": "processing",
      "targetCount": 142,
      "sentCount": 87,
      "failedCount": 2,
      "startedAt": "2025-05-28T14:00:00.000Z",
      "completedAt": null
    }
  ]
}
```

### Progresso de uma execução

`GET /v1/campaigns/{id}/executions/{executionId}`:

```json theme={null}
{
  "id": "campaign_execution_def456",
  "status": "processing",
  "targetCount": 142,
  "sentCount": 87,
  "failedCount": 2,
  "progress": 63
}
```

| Campo         | Descrição                                           |
| ------------- | --------------------------------------------------- |
| `targetCount` | Chats que entraram no público ao iniciar            |
| `sentCount`   | Mensagens enviadas com sucesso                      |
| `failedCount` | Envios que falharam (canal, janela, template, etc.) |
| `progress`    | Percentual concluído (0–100)                        |

### Cancelar

```http theme={null}
POST /v1/campaigns/{id}/cancel
```

Resposta: `204 No Content`. Interrompe a execução em andamento; envios já enfileirados podem ainda ser processados conforme o estado da fila.

***

## Exemplo completo (TypeScript com SDK)

```ts theme={null}
import SparkCRM from "sparkcrm";

const client = new SparkCRM({ apiKey: process.env.SPARK_API_KEY! });

// 1. Escolher template
const { templates } = await client.messageTemplates.list();
const template = templates.find((t) => t.name === "Promoção VIP");
if (!template) throw new Error("Template não encontrado");

// 2. Ver variáveis manuais
const detail = await client.messageTemplates.retrieve(template.id);
const needsVariables = detail.commonVariables.length > 0;

// 3. Estimar público
const preview = await client.campaigns.previewAudience({
  filters: [
    {
      combinator: "and",
      statements: [
        { field: "chat.platform", operator: "equals", value: "whatsapp" },
        { field: "chat.tagIds", operator: "has-any", value: ["tag_vip123"] },
      ],
    },
  ],
});
console.log(`Público estimado: ${preview.estimatedCount} chats`);

// 4. Criar rascunho
const { campaignId } = await client.campaigns.create({
  name: "Promoção maio 2025",
  platform: "whatsapp",
  templateId: template.id,
  cadence: 10,
  filters: [
    {
      combinator: "and",
      statements: [
        { field: "chat.platform", operator: "equals", value: "whatsapp" },
        { field: "chat.tagIds", operator: "has-any", value: ["tag_vip123"] },
      ],
    },
  ],
});

// 5. Iniciar
const { executionId } = await client.campaigns.start(campaignId, {
  commonVariables: needsVariables
    ? { var1: "Promoção de maio", var2: "15OFF" }
    : undefined,
});

// 6. Polling
let done = false;
while (!done) {
  const exec = await client.campaigns.retrieveExecution(campaignId, executionId);
  console.log(`Progresso: ${exec.progress}% (${exec.sentCount}/${exec.targetCount})`);
  if (["completed", "failed", "canceled"].includes(exec.status)) done = true;
  else await new Promise((r) => setTimeout(r, 15_000));
}
```

Veja [SDK TypeScript](/sdks/typescript/introducao) para instalação e configuração.

***

## Erros comuns

Todas as respostas de erro seguem `{ statusCode, code, message }`. Trate pelo campo **`code`**, não pela mensagem.

| Código                                | HTTP | Quando ocorre                                 | O que fazer                                   |
| ------------------------------------- | ---- | --------------------------------------------- | --------------------------------------------- |
| `CAMPAIGN_NAME_ALREADY_EXISTS`        | 422  | Nome duplicado na organização                 | Escolha outro `name`                          |
| `MESSAGE_TEMPLATE_NOT_FOUND`          | 404  | `templateId` inválido                         | Liste templates e use ID correto              |
| `CAMPAIGN_TEMPLATE_PLATFORM_MISMATCH` | 422  | Template oficial em plataforma incompatível   | Use `whatsapp` ou `instagram` para oficiais   |
| `CAMPAIGN_INVALID_FILTERS`            | 422  | Campo, operador ou valor incompatível         | Consulte `filters/schema` e corrija           |
| `CAMPAIGN_MISSING_COMMON_VARIABLES`   | 422  | Variáveis manuais não preenchidas             | Envie todas as `commonVariables` exigidas     |
| `CAMPAIGN_NOT_DRAFT`                  | 422  | Tentativa de iniciar campanha já processada   | Crie nova campanha ou consulte execuções      |
| `CAMPAIGN_ALREADY_PROCESSING`         | 422  | Execução ativa em andamento                   | Aguarde ou cancele antes de reiniciar         |
| `CAMPAIGN_SIZE_LIMIT_EXCEEDED`        | 422  | Público maior que limite de 24h da plataforma | Reduza filtros ou divida em campanhas menores |
| `CAMPAIGN_NOT_FOUND`                  | 404  | ID de campanha inexistente                    | Verifique o `campaignId`                      |
| `SUBSCRIPTION_INACTIVE`               | 403  | Plano inativo                                 | Regularize assinatura no painel               |
| `VALIDATION_ERROR`                    | 422  | Body malformado                               | Revise tipos e campos obrigatórios            |

### Limite de tamanho da campanha

O Spark valida se o público cabe no volume máximo estimado para 24 horas na plataforma escolhida. Se `audience-preview` retornar um número alto, considere segmentar (por tag, funil ou data) em múltiplas campanhas sequenciais.

***

## Boas práticas

1. **Segmente antes de escalar** — use tags, funil e datas para públicos menores e mais relevantes.
2. **Sempre faça preview** — `audience-preview` evita disparos para 0 contatos ou bases enormes acidentais.
3. **Valide variáveis** — consulte `GET /v1/message-templates/{id}` antes do start; erros de variável só aparecem na hora de iniciar.
4. **Respeite janelas de canal** — modelos internos no WhatsApp/Instagram só funcionam dentro da janela de resposta; fora dela, use modelos oficiais.
5. **Monitore `failedCount`** — falhas podem indicar chats sem janela, números inválidos ou problemas de template.
6. **Cadência conservadora** — comece com 5–10 msg/min e ajuste conforme resultados.
7. **Nomes descritivos** — `name` deve ser único e identificável (`"Promoção maio VIP"`, não `"Campanha 1"`).
8. **Idempotência no start** — se a requisição de start falhar por timeout de rede, consulte `GET /v1/campaigns/{id}` antes de tentar novamente para evitar duplicar execuções.

***

## Leitura adicional

* [Disparos](/conceitos/disparos) — conceito de campanha no produto
* [Modelos de mensagem](/conceitos/modelos-de-mensagem) — variáveis manuais vs automáticas, oficiais vs internos
* [Desenvolvedores](/conceitos/desenvolvedores) — chaves, ambiente e envio individual de templates
* [SDK TypeScript](/sdks/typescript/introducao) — cliente oficial `sparkcrm`
