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

# Campaigns API: gerencie públicos e mensagens

> Adicione contatos a campanhas evergreen, agende envios por contato, cancele mensagens em fila e inspecione o status das mensagens da campanha pela API do BluBash.

A Campaigns API permite gerenciar programaticamente o público e a entrega de mensagens de campanhas **evergreen** (contínuas) no seu workspace BluBash. Você pode adicionar contatos individualmente ou em massa com agendamento por contato, cancelar mensagens em fila antes de serem enviadas e inspecionar o status das mensagens em qualquer ponto do ciclo de entrega.

<Warning>
  O recurso de Campanhas precisa estar habilitado na assinatura do seu workspace para usar os endpoints de escrita. Endpoints de leitura (listar e obter mensagens) podem funcionar mesmo sem o recurso habilitado. Se você tentar uma operação de escrita sem o recurso, receberá uma resposta `403 Forbidden`.
</Warning>

## URL base

```text theme={null}
https://api.blubash.io/api/v1/campaigns
```

## Autenticação

Todas as requisições exigem sua Workspace API Key no cabeçalho `X-API-Key`.

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

Para obter sua chave de API, vá até **Configurações → API Keys** no painel de administração do seu workspace.

## Pré-requisitos

As campanhas evergreen precisam ser criadas e configuradas na plataforma BluBash antes de você usar essa API. A API cobre apenas a ingestão de público e o disparo de mensagens.

<Steps>
  <Step title="Crie uma campanha evergreen na plataforma">
    No painel de administração do BluBash, crie uma nova campanha com `kind = EVERGREEN`.
  </Step>

  <Step title="Configure canal, mensagem e limites de taxa">
    Configure o canal, o template de mensagem e as regras de rate limit da campanha.
  </Step>

  <Step title="Copie o campaignId">
    Salve a campanha e copie o `campaignId` mostrado na plataforma. Você usará esse ID em todas as chamadas de API abaixo.
  </Step>
</Steps>

***

## Adicionar contatos ao público

```http theme={null}
POST /api/v1/campaigns/:campaignId/audience
```

Adiciona um ou mais contatos a uma campanha e define o agendamento de envio por contato. Cada entrada no array `items` representa um contato e sua configuração de agendamento.

<Tip>
  Para adicionar um único contato, envie o array `items` com um único elemento — o endpoint é o mesmo para operações individuais e em lote.
</Tip>

### Corpo da requisição

<ParamField body="default_schedule_timezone" type="string">
  Um nome de fuso horário IANA (por exemplo, `America/New_York`) aplicado a todos os itens cujo `datetime` não tem offset UTC e nenhum `schedule.timezone` foi especificado.
</ParamField>

<ParamField body="items" type="array" required>
  Um array de objetos de contato + agendamento. Pelo menos um item é obrigatório.

  <Expandable title="Campos do item">
    <ParamField body="contact_id" type="string">
      O ID de um contato existente no seu workspace. Obrigatório se `identifier` não for fornecido.
    </ParamField>

    <ParamField body="identifier" type="string">
      O identificador de canal do contato (por exemplo, um número de telefone do WhatsApp). Obrigatório se `contact_id` não for fornecido. Usado para localizar ou criar o contato.
    </ParamField>

    <ParamField body="name" type="string">
      Nome de exibição do contato. Usado ao criar um novo registro de contato.
    </ParamField>

    <ParamField body="email" type="string">
      Endereço de e-mail do contato. Usado para localização e criação do contato.
    </ParamField>

    <ParamField body="custom_fields" type="object">
      Valores de campos personalizados como um objeto chave-valor. As chaves precisam ter o prefixo `cf_` (por exemplo, `{ "cf_plan": "pro" }`).
    </ParamField>

    <ParamField body="tags" type="string[]">
      Tags a aplicar ao contato. As tags são normalizadas automaticamente (com trim, lowercase e deduplicação) e aplicadas de forma aditiva — tags existentes não são removidas.
    </ParamField>

    <ParamField body="schedule" type="object">
      Agendamento de envio para este contato. Padrão é `immediate` se omitido.

      <Expandable title="Campos de schedule">
        <ParamField body="type" type="string" required>
          `immediate` para enviar o quanto antes ou `absolute` para agendar para uma data e hora específicas.
        </ParamField>

        <ParamField body="datetime" type="string">
          Datetime no formato ISO 8601. Obrigatório quando `type` for `absolute`. Se o valor incluir um offset UTC ou `Z`, o campo `timezone` é ignorado. Se não houver offset, você precisa informar `schedule.timezone` ou `default_schedule_timezone`.
        </ParamField>

        <ParamField body="timezone" type="string">
          Nome de fuso horário IANA para converter um `datetime` sem offset UTC para UTC.
        </ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

### Resolução de contato

Quando você fornece `identifier` em vez de `contact_id`, a plataforma resolve o contato nesta ordem:

1. Procura o contato pelo identificador do canal (por exemplo, número de WhatsApp normalizado).
2. Se não encontrar e `email` for fornecido, busca por e-mail (sem diferenciar maiúsculas e minúsculas).
   * Se encontrar sem identificador de canal, vincula o `identifier` ao contato.
   * Se encontrar com um identificador de canal **diferente**, retorna um erro `400` para evitar fusão silenciosa de identidades.
3. Se ainda não encontrar, cria um novo contato usando `name`, `email` e `identifier`.

### Regras de agendamento

| Formato do `datetime`                                       | Comportamento                                                                     |
| ----------------------------------------------------------- | --------------------------------------------------------------------------------- |
| Com `Z` ou offset numérico (por exemplo, `+03:00`, `-0500`) | Tratado como instante absoluto; `timezone` é ignorado                             |
| Sem offset (por exemplo, `2026-03-11T09:00:00`)             | Requer `schedule.timezone` ou `default_schedule_timezone` para conversão para UTC |

<Warning>
  Se `datetime` não tem offset UTC e nenhum fuso horário foi fornecido (em `schedule.timezone` ou `default_schedule_timezone`), a requisição retorna um erro `400`.
</Warning>

### Exemplo de requisição

```json theme={null}
{
  "default_schedule_timezone": "America/New_York",
  "items": [
    {
      "contact_id": "<CONTACT_ID_1>",
      "schedule": {
        "type": "immediate"
      }
    },
    {
      "identifier": "5511999999999",
      "name": "Jane Smith",
      "email": "jane@example.com",
      "custom_fields": {
        "cf_plan": "pro",
        "cf_source": "api"
      },
      "tags": ["Hot Lead", "VIP Customer"],
      "schedule": {
        "type": "absolute",
        "datetime": "2026-03-10T14:30:00.000Z"
      }
    },
    {
      "contact_id": "<CONTACT_ID_3>",
      "schedule": {
        "type": "absolute",
        "datetime": "2026-03-11T09:00:00",
        "timezone": "America/New_York"
      }
    }
  ]
}
```

### Campos da resposta

<ResponseField name="items" type="array" required>
  Uma entrada por item de entrada, na mesma ordem da requisição.

  <Expandable title="Campos do item">
    <ResponseField name="messageId" type="string">
      ID da mensagem de campanha criada para este contato.
    </ResponseField>

    <ResponseField name="contactId" type="string">
      ID do contato resolvido ou criado.
    </ResponseField>

    <ResponseField name="scheduled_at" type="string | null">
      O horário de envio agendado formatado no fuso horário IANA ou no offset fornecido. `null` para envios imediatos.
    </ResponseField>

    <ResponseField name="scheduled_at_utc" type="string | null">
      O horário de envio canônico em UTC (sempre termina em `Z`). `null` para envios imediatos.
    </ResponseField>

    <ResponseField name="timezone" type="string | null">
      O nome de fuso horário IANA (por exemplo, `America/New_York`) ou offset numérico (por exemplo, `-05:00`) usado para o agendamento. `null` para envios imediatos.
    </ResponseField>
  </Expandable>
</ResponseField>

### Exemplo de resposta

```json theme={null}
{
  "items": [
    {
      "messageId": "<MESSAGE_ID>",
      "contactId": "<CONTACT_ID_1>",
      "scheduled_at": null,
      "scheduled_at_utc": null,
      "timezone": null
    },
    {
      "messageId": "<MESSAGE_ID>",
      "contactId": "<CONTACT_ID_2>",
      "scheduled_at": "2026-03-10T14:30:00.000Z",
      "scheduled_at_utc": "2026-03-10T14:30:00.000Z",
      "timezone": null
    },
    {
      "messageId": "<MESSAGE_ID>",
      "contactId": "<CONTACT_ID_3>",
      "scheduled_at": "2026-03-11T09:00:00.000-05:00",
      "scheduled_at_utc": "2026-03-11T14:00:00.000Z",
      "timezone": "America/New_York"
    }
  ]
}
```

***

## Cancelar uma mensagem em fila

```http theme={null}
POST /api/v1/campaigns/:campaignId/messages/:messageId/cancel
```

Cancela a entrega de uma mensagem específica de campanha que ainda está na fila. Cancelar uma mensagem não afeta nenhuma outra mensagem da campanha.

<Info>
  Apenas mensagens com status `QUEUED` podem ser canceladas. Mensagens que já estão `SENDING`, `SENT` ou `FAILED` não podem ser canceladas.
</Info>

### Resposta

```json theme={null}
{
  "id": "<MESSAGE_ID>",
  "status": "CANCELLED"
}
```

***

## Listar mensagens da campanha

```http theme={null}
GET /api/v1/campaigns/:campaignId/messages?limit=50&offset=0
```

Retorna uma lista paginada de mensagens da campanha, cada uma com seu status atual.

### Parâmetros de query

<ParamField query="limit" type="number" default="50">
  Número de resultados a retornar por página.
</ParamField>

<ParamField query="offset" type="number" default="0">
  Número de resultados a pular para paginação.
</ParamField>

***

## Obter uma mensagem específica

```http theme={null}
GET /api/v1/campaigns/:campaignId/messages/:messageId
```

Retorna os detalhes e o status atual de uma única mensagem da campanha.

***

## Status das mensagens

| Status      | Descrição                                 |
| ----------- | ----------------------------------------- |
| `PENDING`   | Aguardando processamento                  |
| `QUEUED`    | Em fila para entrega — pode ser cancelada |
| `SENDING`   | Sendo enviada no momento                  |
| `SENT`      | Entregue com sucesso                      |
| `FAILED`    | Falha na entrega                          |
| `CANCELLED` | Cancelada via API                         |

***

## Fluxo recomendado

<Steps>
  <Step title="Configure a campanha na plataforma">
    Crie uma campanha evergreen no painel de administração do BluBash e copie o `campaignId`.
  </Step>

  <Step title="Adicione contatos ao público">
    Chame `POST /api/v1/campaigns/:campaignId/audience` com seu array `items`.
  </Step>

  <Step title="Escolha um agendamento por contato">
    Use `schedule.type = immediate` para entrega instantânea, ou `schedule.type = absolute` com um datetime para entrega agendada.
  </Step>

  <Step title="Cancele se necessário">
    Se você precisar parar uma mensagem em fila, chame `POST /api/v1/campaigns/:campaignId/messages/:messageId/cancel`.
  </Step>

  <Step title="Monitore o status das mensagens">
    Use `GET /api/v1/campaigns/:campaignId/messages` para inspecionar o status de entrega ao longo da campanha.
  </Step>
</Steps>

***

## Exemplos de código

<CodeGroup>
  ```bash Immediate send (cURL) theme={null}
  curl -X POST https://api.blubash.io/api/v1/campaigns/{campaignId}/audience \
    -H "X-API-Key: your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "items": [
        {
          "identifier": "5511999999999",
          "name": "Jane Smith",
          "tags": ["Hot Lead"],
          "schedule": {
            "type": "immediate"
          }
        }
      ]
    }'
  ```

  ```bash Scheduled send (cURL) theme={null}
  curl -X POST https://api.blubash.io/api/v1/campaigns/{campaignId}/audience \
    -H "X-API-Key: your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "default_schedule_timezone": "America/New_York",
      "items": [
        {
          "contact_id": "contact_abc123",
          "schedule": {
            "type": "absolute",
            "datetime": "2026-04-01T10:00:00"
          }
        }
      ]
    }'
  ```

  ```bash Batch with mixed schedules (cURL) theme={null}
  curl -X POST https://api.blubash.io/api/v1/campaigns/{campaignId}/audience \
    -H "X-API-Key: your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "default_schedule_timezone": "America/New_York",
      "items": [
        {
          "contact_id": "contact_001",
          "schedule": { "type": "immediate" }
        },
        {
          "identifier": "5511888887777",
          "name": "Pedro Lima",
          "custom_fields": {
            "cf_plan": "enterprise"
          },
          "tags": ["VIP Customer", "Hot Lead"],
          "schedule": {
            "type": "absolute",
            "datetime": "2026-04-05T09:00:00",
            "timezone": "America/New_York"
          }
        },
        {
          "contact_id": "contact_003",
          "schedule": {
            "type": "absolute",
            "datetime": "2026-04-06T12:00:00.000Z"
          }
        }
      ]
    }'
  ```

  ```bash Cancel a message (cURL) theme={null}
  curl -X POST https://api.blubash.io/api/v1/campaigns/{campaignId}/messages/{messageId}/cancel \
    -H "X-API-Key: your_api_key"
  ```

  ```bash List messages (cURL) theme={null}
  curl -X GET "https://api.blubash.io/api/v1/campaigns/{campaignId}/messages?limit=50&offset=0" \
    -H "X-API-Key: your_api_key"
  ```
</CodeGroup>

***

## Referência de erros

| Status | Mensagem                                                                              | Causa                                                                             |
| ------ | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `400`  | `Each item must provide contact_id or identifier`                                     | Um item não tem nem `contact_id` nem `identifier`                                 |
| `400`  | `Identifier is invalid`                                                               | `identifier` está vazio ou é inválido após normalização                           |
| `400`  | `schedule.timezone (IANA) is required when datetime has no UTC offset`                | `datetime` não tem offset e nenhum fuso horário foi fornecido                     |
| `400`  | `Campaign is not an evergreen campaign`                                               | O `campaignId` pertence a uma campanha não evergreen                              |
| `400`  | `Cannot add contacts to cancelled or failed campaigns`                                | O status da campanha é `CANCELLED` ou `FAILED`                                    |
| `400`  | `Cannot add contacts while the campaign is paused. Resume the campaign first.`        | O status da campanha é `PAUSED`                                                   |
| `400`  | `Contact found by email already has a different identifier for this campaign channel` | A busca por e-mail encontrou um contato com um identificador de canal conflitante |
| `400`  | `Contact does not have a valid channel identifier for this campaign channel`          | O contato resolvido não tem identificador válido para o canal da campanha         |
| `401`  | —                                                                                     | Chave de API ausente ou inválida                                                  |
| `403`  | `Campaigns feature is not available in your current plan`                             | O recurso de Campanhas não está habilitado na sua assinatura                      |
| `404`  | `Campaign not found`                                                                  | O `campaignId` não existe no seu workspace                                        |
