
# API de Campanhas

Uma campanha reúne tudo o que o bot de IA precisa para falar com seus contatos: suas instruções, os canais em que é executada, seu horário de funcionamento e seu comportamento de acompanhamento. A API de Campanhas permite listar, criar, atualizar, duplicar, ativar, arquivar e ajustar campanhas a partir do seu próprio código, em vez de usar o painel.

Todos os endpoints abaixo são relativos à URL base `https://api.youraiconnector.com/v1`. Cada solicitação deve ser autenticada — consulte [Acesso à API](../integrations/api-access.md) e [Autenticação](authentication.md) para saber como obter e passar sua chave de API. O acesso à API é um recurso pago; sem ele, as solicitações são rejeitadas com um `403`.

> **Atenção:** Alguns exemplos mostram a forma de consulta simples `?apiKey=YOUR_API_KEY`, outros usam o cabeçalho `X-API-Key`. Ambos funcionam em qualquer lugar — use o que melhor se adaptar à sua configuração.

---

## Tipos de campanha

Ao criar uma campanha, você deve escolher um destes tipos:

| Tipo | Para que serve |
|---|---|
| `Incoming from Unknown Contacts` | O bot responde a pessoas que enviam mensagens para você pela primeira vez. |
| `Outgoing` | O bot inicia conversas com contatos que você adiciona à campanha. |
| `Keywords` | **Inerte - não use.** Uma campanha `Keywords` é inerte: ela ainda é aceita para compatibilidade com versões anteriores, mas é invisível para o roteamento de entrada em todos os canais e nada lê suas palavras-chave de gatilho. Use um Ponto de Entrada do tipo **Palavra-chave** em um Agente de IA. |
| `Combined` | Uma mistura de comportamento de entrada e saída. |

**O uso de maiúsculas e minúsculas não importa.** `type`, `status`, `booking_provider`, `first_response_mode`, `bot.anthropic_model` e `bot.ai_speed` aceitam qualquer variação — `"live"`, `"Live"` e `"LIVE"` são a mesma coisa — e o valor é armazenado em sua forma canônica, que é o que é retornado quando você lê a campanha. A única exceção é o par de pausa: `"Paused"` e `"paused"` são dois estados genuinamente diferentes, portanto, uma grafia ambígua como `"PAUSED"` é rejeitada com um `400` instruindo você a escolher um.

### Os dois estados de pausa

| Status | Quem define | O que significa |
|---|---|---|
| `Paused` | Verificações de segurança da própria plataforma (baixo engajamento, erros de envio repetidos, limite atingido) e as novas superfícies de Agentes e Transmissões | A campanha está retida. Uma varredura agendada pode remover uma pausa de segurança automaticamente assim que o motivo for resolvido. |
| `paused` | O botão Pausar do painel, pareado com `resumed` no Retomar | Uma pessoa pausou manualmente. Envios agendados são removidos e reconstruídos ao retomar. |

Ambos interrompem a campanha: o roteamento de entrada só funciona enquanto o status for exatamente `Live`. **Pela API, use `Paused` para pausar e `Live` para retomar** — o par em minúsculas existe para o botão do painel e é mantido funcional para ele.

Nenhum desses é o que acontece quando a IA para de responder dentro de uma conversa. Esse é um interruptor por contato, `is_bot_active` no contato — definido quando um humano assume, quando o contato opta por sair ou quando a IA conclui o chat. O status da própria campanha permanece inalterado, e todas as outras conversas nela continuam funcionando. Veja [pausar ou retomar a IA para um contato](messages.md#pause-or-resume-the-ai-for-one-contact).

> **Criar uma campanha não decide quem responde a um canal.** O roteamento é gerenciado por **Pontos de Entrada** em um Agente de IA, não por campanhas. Cada canal tem um Ponto de Entrada padrão que nomeia o Agente que responde a contatos novos e desconhecidos nele: defina-o com `PUT /entry-points/channel-defaults`, verifique se a escada está ativa para a conta com `GET /entry-points/routing-status`, limpe-o com `DELETE /entry-points/channel-defaults`. `POST /channels/campaign` ainda grava o mapa de roteamento de campanha legado por canal, mas esse mapa não é mais consultado para roteamento de entrada em nenhuma conta; ele é mantido apenas para reversão. Não desenvolva com base nele. Veja [Roteie um canal para uma campanha](channels.md#route-a-channel-to-a-campaign) para ver ambas as superfícies lado a lado.

---

## Listar campanhas

`GET /campaigns`

Retorna suas campanhas, da mais recente para a mais antiga. Campanhas arquivadas são excluídas, a menos que você passe `archived=true`.

**Parâmetros de consulta**

| Parâmetro | Obrigatório | Descrição |
|---|---|---|
| `limit` | Não | Número máximo de campanhas a retornar. O padrão é `50`, o máximo é `100`. |
| `cursor` | Não | Cursor de paginação. Passe o valor `next_cursor` da resposta anterior para obter a próxima página. |
| `archived` | Não | Defina como `true` para incluir campanhas arquivadas. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/campaigns?limit=20&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/campaigns?limit=20", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.campaigns, data.next_cursor);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/campaigns",
    params={"limit": 20},
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["campaigns"], data["next_cursor"])
```

**Resposta**

```json
{
  "success": true,
  "campaigns": [
    {
      "id": "NBCXrhqGPSFsd6MV7pRo",
      "name": "Inbound WhatsApp Leads",
      "type": "Incoming from Unknown Contacts",
      "status": "Live",
      "enabled": true,
      "archived": false,
      "created_at": 1700000000000,
      "ai_mode": true,
      "language": "en",
      "enabled_channels": ["whatsapp", "instagram"]
    }
  ],
  "next_cursor": "NBCXrhqGPSFsd6MV7pRo"
}
```

Quando `next_cursor` for `null`, você atingiu a última página.

---

## Obter uma campanha

`GET /campaigns/{campaignId}`

Retorna o documento completo da campanha, incluindo a configuração do bot ativo (`bot`), configurações de acompanhamento, canais habilitados e quaisquer palavras-chave. Os carimbos de data/hora são retornados em milissegundos da época.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY"
```

**JavaScript**

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

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
campaign = res.json()["campaign"]
```

**Resposta**

```json
{
  "success": true,
  "campaign": {
    "id": "NBCXrhqGPSFsd6MV7pRo",
    "name": "Inbound WhatsApp Leads",
    "type": "Incoming from Unknown Contacts",
    "status": "Live",
    "language": "en",
    "ai_mode": true,
    "enabled": true,
    "archived": false,
    "created_at": 1700000000000,
    "enabled_channels": ["whatsapp", "instagram"],
    "bot": {
      "instructions": "Greet warmly and ask about their goals.",
      "goal": "Book a discovery call.",
      "ai_speed": "balanced",
      "anthropic_model": "standard",
      "max_messages": 20
    }
  }
}
```

::: note
**Nota:** Uma campanha pertencente a uma conta diferente retorna `404 Campaign not found` (não `403`), portanto, você não pode saber se um ID existe em outra conta.
:::


---

## Criar uma campanha

`POST /campaigns`

Cria uma nova campanha. `name` e `type` são obrigatórios; todo o resto é opcional. Você pode incluir qualquer outro campo de campanha na mesma solicitação — por exemplo, `language`, `ai_mode` ou um objeto de configuração `bot` completo — e ele será armazenado com a nova campanha. O proprietário e o horário de criação são definidos automaticamente.

**Campos da requisição**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `name` | Sim | O nome da campanha. |
| `type` | Sim | Um dos quatro tipos de campanha acima. |
| `language` | Não | Idioma no qual o bot responde (por exemplo, `"en"`). |
| `ai_mode` | Não | Se o modo IA está ativado (`true`/`false`). Em uma campanha respondida por um Agente de IA, as leituras retornam o botão **Ativo** do Agente em vez de um valor armazenado — veja a nota abaixo sobre atualização. |
| `bot` | Não | O objeto de configuração do bot (veja [Campos de configuração do bot](#bot-configuration-fields)). |
| `list_id` | Não | ID da lista de contatos a ser anexada. |
| `event_id` | Não | ID do tipo de evento que a IA pode agendar. |
| `event_ids` | Não | Vários tipos de evento de uma vez, como uma matriz de IDs de tipo de evento — o primeiro é o padrão. Envie `event_id` ou `event_ids`, não ambos. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Spring Promo",
    "type": "Outgoing",
    "language": "en",
    "ai_mode": true,
    "bot": {
      "instructions": "Greet warmly and ask about their goals.",
      "goal": "Book a discovery call."
    }
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/campaigns", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Spring Promo",
    type: "Outgoing",
    language: "en",
    ai_mode: true,
    bot: {
      instructions: "Greet warmly and ask about their goals.",
      goal: "Book a discovery call.",
    },
  }),
});
const { campaign_id } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/campaigns",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "Spring Promo",
        "type": "Outgoing",
        "language": "en",
        "ai_mode": True,
        "bot": {
            "instructions": "Greet warmly and ask about their goals.",
            "goal": "Book a discovery call.",
        },
    },
)
campaign_id = res.json()["campaign_id"]
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

---

## Atualizar uma campanha

`PUT /campaigns/{campaignId}`

Atualiza parcialmente uma campanha — envie apenas os campos que deseja alterar. Este é o único verbo de atualização geral; não existe um `PATCH /campaigns/{campaignId}` (as duas rotas `PATCH` são os alternadores restritos de [ativar](#enable-or-disable-a-campaign) e [arquivar](#archive-or-restore-a-campaign)).

**Quais campos você pode alterar.** Tudo o que o editor de campanha escreve, incluindo `name`, `status`, `type`, `language`, `ai_mode`, `enabled_channels`, as configurações de gatilho e gotejamento, as flags de agendamento e acompanhamento, os campos de monitoramento do Instagram/Facebook e toda a configuração de `bot`. Identidade e propriedade são bloqueadas durante a vida útil da campanha: `user`, `id` e `created_at` são rejeitados, assim como qualquer nome de campo que o endpoint não reconheça. A rejeição é por solicitação, não por campo — uma chave desconhecida retorna um `400` e **nada** nessa solicitação é gravado.

**`ai_mode` em uma campanha com suporte de Agente reflete o Agente.** Quando uma campanha é respondida por um Agente de IA, a leitura da campanha retorna `ai_mode` derivado do botão **Ativo** daquele Agente — o único interruptor que realmente decide se a IA responde. Escrever `ai_mode` em tal campanha é aceito, mas não alterará o que você lê de volta; em vez disso, ative ou desative o botão Ativo do Agente (no painel ou via API de Agentes). Em campanhas clássicas sem Agente, `ai_mode` lê e grava o valor armazenado como antes.

**Campos do bot são mesclados, não sobrescritos.** Envie as configurações do bot como chaves pontuadas (`"bot.instructions": "..."`) ou como um objeto aninhado (`"bot": { "instructions": "..." }`) — ambos gravam folha por folha, portanto, os campos que você omitir mantêm seus valores atuais. `bot.instructions`, `bot.goal`, `bot.rules` e `bot.personality` são todos editáveis desta forma, assim como qualquer outra configuração de bot listada em [Campos de configuração do bot](#bot-configuration-fields). O mesmo se aplica a `test_bot`, `frequency` e `follow_up_config`.

Para substituir uma configuração de bot por completo — excluindo qualquer campo que você não enviar — use `bot_replace` (ou `test_bot_replace`) com o objeto completo. Você não pode combinar uma substituição e uma mesclagem para o mesmo objeto em uma única solicitação; isso retorna um `400`.

::: note
**Nota:** Gravar `bot.*` através da API entra em vigor **imediatamente** na campanha ativa. O editor do painel funciona de forma diferente: as edições lá são salvas como rascunho e só entram em vigor quando o cliente clica em Publicar. Portanto, se um cliente tiver alterações não publicadas no painel, elas permanecem em `test_bot` e uma leitura da API de `bot` mostra corretamente o que a IA está usando no momento.
:::


Alguns campos são definidos por meio de uma chave dedicada em vez de serem escritos diretamente: use `list_id` para a lista de contatos, `event_id` para o tipo de evento (ou `event_ids`, uma matriz ordenada de IDs de tipo de evento, para permitir que a IA agende vários — o primeiro é o padrão; uma matriz vazia desvincula todos eles) e `contact_ids` (uma matriz de IDs de contato) para os contatos da campanha. As entradas da base de conhecimento são gerenciadas por meio da [API de FAQs](faqs.md), não por este endpoint.

**Tags substituem, elas não são mescladas.** Envie `tags` como o array completo e ele se tornará o conjunto de tags da campanha — veja [Tags de campanha](#campaign-tags) para os campos e para os endpoints que adicionam ou editam uma única tag.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Spring Promo v2", "enabled_channels": ["whatsapp"] }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "Spring Promo v2",
      enabled_channels: ["whatsapp"],
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"name": "Spring Promo v2", "enabled_channels": ["whatsapp"]},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

---

## Excluir uma campanha

`DELETE /campaigns/{campaignId}`

Exclui permanentemente uma campanha. Isso não pode ser desfeito — se você puder precisar da campanha novamente, [arquive-a](#archive-or-restore-a-campaign) em vez disso.

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  { 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/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true
}
```

---

## Duplicar uma campanha

`POST /campaigns/{campaignId}/duplicate`

Cria uma cópia da campanha com todas as suas configurações preservadas. A cópia começa **desativada** e seu nome recebe um sufixo `(copy)`, para que nunca envie mensagens até que você a ative explicitamente.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/duplicate?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/duplicate",
  { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const { campaign_id } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/duplicate",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
new_campaign_id = res.json()["campaign_id"]
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "aZ9plnewCopyId01234"
}
```

> Cópias duplicadas **dentro de uma mesma conta**.

---


## Ativar ou desativar uma campanha

`PATCH /campaigns/{campaignId}/enabled`

Liga ou desliga uma campanha. Uma campanha desativada para de interagir com os contatos, mas mantém toda a sua configuração.

**Campos da requisição**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `enabled` | Sim | `true` para ativar, `false` para desativar. Deve ser um booleano. |

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/enabled?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/enabled",
  {
    method: "PATCH",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ enabled: true }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.patch(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/enabled",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"enabled": True},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "enabled": true
}
```

---

## Arquivar ou restaurar uma campanha

`PATCH /campaigns/{campaignId}/archived`

Arquiva ou restaura uma campanha. Campanhas arquivadas são ocultadas da lista padrão de campanhas, mas mantêm todos os seus dados e podem ser restauradas a qualquer momento.

**Campos da requisição**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `archived` | Sim | `true` para arquivar, `false` para restaurar. Deve ser um booleano. |

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/archived?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "archived": true }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/archived",
  {
    method: "PATCH",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ archived: true }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.patch(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/archived",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"archived": True},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "archived": true
}
```

---

## Atualize a configuração do bot

`PUT /campaigns/{campaignId}/bot-config`

Esta é a maneira segura de alterar configurações individuais do bot. Cada campo que você envia é **mesclado** à configuração existente do bot, portanto, quaisquer campos que você omitir serão preservados. Use isso em vez do endpoint de atualização de campanha sempre que quiser apenas ajustar uma parte do bot.

As chaves dos campos devem usar apenas letras, números, sublinhados e hifens.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/bot-config?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "instructions": "Always answer in a friendly, concise tone.",
    "ai_speed": "balanced"
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/bot-config",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      instructions: "Always answer in a friendly, concise tone.",
      ai_speed: "balanced",
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/bot-config",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "instructions": "Always answer in a friendly, concise tone.",
        "ai_speed": "balanced",
    },
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

### Campos de configuração do bot

Todos os campos do bot são opcionais. Envie apenas aqueles que deseja definir. Quaisquer campos adicionais do bot além dos listados aqui são aceitos e armazenados como estão.

| Campo | Tipo | Descrição |
|---|---|---|
| `instructions` | string | As instruções principais que orientam como o bot fala com os contatos. |
| `rules` | string | Regras rígidas que o bot deve sempre seguir. |
| `goal` | string | O resultado que o bot deve buscar em cada conversa. |
| `personality` | string | Descrição do tom de voz e da personalidade do bot. |
| `ai_speed` | string | Quanto raciocínio a IA aplica antes de responder. Um de `fast`, `fast_thinker`, `balanced`, `thorough`. |
| `anthropic_model` | string | O nível de qualidade da IA usado para as respostas desta campanha. Um de `standard`, `economy` (obsoleto), `max`, `mini`. `max` e `mini` só entram em vigor em contas elegíveis para esses níveis. |
| `max_messages` | integer | Número máximo de mensagens do bot por conversa. |
| `alert_human_when` | string | Condições sob as quais o bot deve alertar um membro da equipe humana. |
| `availability` | object | O cronograma de horário ativo do bot. Você pode defini-lo aqui ou usar o [endpoint de horário ativo](#set-the-bot-active-hours) dedicado. |
| `follow_up_config` | object | Configuração de comportamento de acompanhamento, armazenada conforme fornecida. |

---

## Defina as horas ativas do bot

`PUT /campaigns/{campaignId}/active-hours`

Define o cronograma de disponibilidade do bot. Fora das janelas configuradas, o bot não responde automaticamente. Isso grava o campo `availability` da configuração do bot.

**Campos da requisição**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `availability` | Sim | Um objeto indexado por dia da semana. As chaves permitidas são `monday` até `sunday`; qualquer outra chave retorna um `400`. Os dias que você omitir permanecerão inalterados. |

Cada dia da semana contém uma única janela de tempo ou uma matriz de janelas. Uma janela possui um `start_time` e `end_time` no formato de 24 horas `HH:MM`.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/active-hours?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "availability": {
      "monday": { "start_time": "09:00", "end_time": "17:00" },
      "tuesday": [
        { "start_time": "09:00", "end_time": "12:00" },
        { "start_time": "13:00", "end_time": "17:00" }
      ]
    }
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/active-hours",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      availability: {
        monday: { start_time: "09:00", end_time: "17:00" },
        tuesday: [
          { start_time: "09:00", end_time: "12:00" },
          { start_time: "13:00", end_time: "17:00" },
        ],
      },
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/active-hours",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "availability": {
            "monday": {"start_time": "09:00", "end_time": "17:00"},
            "tuesday": [
                {"start_time": "09:00", "end_time": "12:00"},
                {"start_time": "13:00", "end_time": "17:00"},
            ],
        }
    },
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

---

## Listar as funções personalizadas de uma campanha

`GET /campaigns/{campaignId}/custom-functions`

Retorna as funções personalizadas vinculadas a esta campanha, resolvidas em definições completas. Funções personalizadas são ações HTTP externas que o bot pode chamar durante uma conversa — por exemplo, verificar o estoque em sua loja ou criar um registro em seu CRM.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const { custom_functions } = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
custom_functions = res.json()["custom_functions"]
```

**Resposta**

```json
{
  "success": true,
  "custom_functions": [
    {
      "id": "fn_abc123",
      "name": "check_stock",
      "description": "Looks up whether a product is in stock.",
      "url": "https://example.com/api/stock",
      "method": "POST",
      "input": [
        { "name": "sku", "type": "string" }
      ],
      "ai_action": "Tell the customer whether the item is available.",
      "created_at": 1700000000000,
      "updated_at": 1700000500000
    }
  ]
}
```

---

## Vincular uma função personalizada a uma campanha

`POST /campaigns/{campaignId}/custom-functions`

Vincula uma [função personalizada](../ai-automation/custom-functions.md) existente a esta campanha para que o bot possa chamá-la durante uma conversa. Vincular uma função que já está vinculada não realiza nenhuma ação.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `custom_function_id` | Sim | ID da função personalizada a ser vinculada. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "custom_function_id": "fn_abc123" }'
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "custom_function_id": "fn_abc123"
}
```

---

## Desvincular uma função personalizada de uma campanha

`DELETE /campaigns/{campaignId}/custom-functions/{customFunctionId}`

Desvincular uma função que não está vinculada não realiza nenhuma ação.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions/fn_abc123?apiKey=YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "custom_function_id": "fn_abc123"
}
```

---

## Vincular uma fonte de base de conhecimento a uma campanha

`POST /campaigns/{campaignId}/kb-sources`

Vincula uma fonte de base de conhecimento (criada via [API de FAQs](faqs.md)) a esta campanha para que o bot possa utilizá-la ao responder. Vincular uma fonte que já está vinculada não realiza nenhuma ação.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `kb_source_id` | Sim | ID da fonte de base de conhecimento a ser vinculada. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/kb-sources?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "kb_source_id": "kb_abc123" }'
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "kb_source_id": "kb_abc123"
}
```

---

## Desvincular uma fonte de base de conhecimento de uma campanha

`DELETE /campaigns/{campaignId}/kb-sources/{kbSourceId}`

Desvincular uma fonte que não está vinculada não realiza nenhuma ação.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/kb-sources/kb_abc123?apiKey=YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "kb_source_id": "kb_abc123"
}
```

---

## Vincular um servidor MCP a uma campanha

`POST /campaigns/{campaignId}/mcp-servers`

Vincula um servidor MCP a esta campanha, dando ao bot acesso às ferramentas desse servidor durante uma conversa. Vincular um servidor que já está vinculado não realiza nenhuma ação.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `mcp_server_id` | Sim | ID do servidor MCP a ser vinculado. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/mcp-servers?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "mcp_server_id": "mcp_abc123" }'
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "mcp_server_id": "mcp_abc123"
}
```

---

## Desvincular um servidor MCP de uma campanha

`DELETE /campaigns/{campaignId}/mcp-servers/{mcpServerId}`

Desvincular um servidor que não está vinculado não realiza nenhuma ação.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/mcp-servers/mcp_abc123?apiKey=YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "mcp_server_id": "mcp_abc123"
}
```

---

## Biblioteca de mídia da campanha

A biblioteca de mídia armazena imagens, vídeos, documentos e notas de voz que o bot pode enviar durante uma conversa.

### Listar a biblioteca de mídia de uma campanha

`GET /campaigns/{campaignId}/media-library`

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library?apiKey=YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "media_items": [
    {
      "id": "media_abc123",
      "item_id": "media_abc123",
      "title": "Pricing sheet",
      "description": "Send when the contact asks about pricing.",
      "media_url": "https://example.com/pricing.pdf",
      "media_content_type": "application/pdf",
      "type": "document",
      "agent_id": "",
      "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
      "media_home": "campaign"
    }
  ]
}
```

`media_url` é uma URL assinada capturada no momento do upload — ela pode já estar expirada quando você a ler; o painel a re-assina sob demanda.

### Fazer upload de um item de mídia

`POST /campaigns/{campaignId}/media-library`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `base64Data` | Sim | O arquivo, codificado em base64 (sem prefixo data-URL). |
| `mimeType` | Sim | Tipo MIME do arquivo (por exemplo, `image/png`). |
| `title` | Sim | Rótulo curto exibido na biblioteca e no prompt da IA. |
| `description` | Sim | Instrução que diz ao bot **quando** enviar este item. |
| `fileName` | Não | Nome original do arquivo, usado para criar o nome do objeto de armazenamento. |
| `sendMessage` | Não | Frase preferencial que o bot deve usar ao enviar este item. |
| `maxSendsPerConversation` | Não | Número máximo de vezes que o bot pode enviar este item para um contato em uma conversa. O padrão é `1`. |
| `sendAsVoiceNote` | Não | Para um upload de áudio, transcodifique-o para uma nota de voz do WhatsApp. O padrão é `false` (armazenado como um arquivo de áudio simples). |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "base64Data": "iVBORw0KGgoAAAANSUhEUgAA...",
    "mimeType": "image/png",
    "title": "Product photo",
    "description": "Send when the contact asks what the product looks like."
  }'
```

**Resposta**

```json
{
  "success": true,
  "itemId": "media_abc123",
  "mediaUrl": "https://example.com/product.png",
  "storagePath": "ai_media/campaigns/NBCXrhqGPSFsd6MV7pRo/media_abc123.png",
  "mediaContentType": "image/png",
  "type": "image",
  "isVoiceNote": false
}
```

### Atualizar um item de mídia

`PATCH /campaigns/{campaignId}/media-library/{itemId}`

Edita apenas os metadados do item — para substituir o arquivo em si, exclua o item e envie um novo.

| Campo | Descrição |
|---|---|
| `title` | Rótulo curto. |
| `description` | Instrução de quando enviar. |
| `send_message` | Redação preferida para o bot usar. |
| `max_sends_per_conversation` | Inteiro não negativo, ou `null` para limpar o limite. |

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library/media_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Updated pricing sheet" }'
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "item_id": "media_abc123"
}
```

### Excluir um item de mídia

`DELETE /campaigns/{campaignId}/media-library/{itemId}`

Excluir um item que já foi removido não realiza nenhuma ação.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library/media_abc123?apiKey=YOUR_API_KEY"
```

**Resposta**

```json
{ "success": true, "deleted": true }
```

---

## Tags de campanha

Uma tag de campanha é um rótulo que você ensina o bot a aplicar a um contato durante uma conversa — `hot-lead`, `not-interested`, `booked-a-call`. Cada tag possui três partes:

| Campo | Tipo | Descrição |
|---|---|---|
| `name` | string, obrigatório | O rótulo em si. É isso que o bot aplica ao contato e o que você usará para correspondência posteriormente, portanto, mantenha-o curto e estável. |
| `description` | string | A instrução que diz ao bot **quando** aplicar esta tag. Esta é a parte que realiza o trabalho — "a pessoa confirma que entrou na comunidade" é usada, "lead quente" não é. |
| `webhook` | string | Uma URL que recebe um `POST` no momento em que a tag é aplicada a um contato. Deixe em branco se não precisar de uma. |
| `tag_id` | string | Opcional. Vincula esta entrada a uma tag existente em sua conta em vez de uma nova. Forneça-a se quiser gerenciar esta tag específica posteriormente com os endpoints de tag única abaixo. |

Os nomes das tags devem ser exclusivos dentro de uma campanha. O bot aplica tags **por nome**, portanto, duas entradas que compartilham o mesmo nome não têm um vencedor definido.

### Definir todas as tags de uma campanha

`PUT /campaigns/{campaignId}` com um array `tags`.

Isso substitui as tags da campanha exatamente pelo que você enviar, que é a mesma coisa que a aba Tags do painel faz ao salvar. **Envie o array completo todas as vezes** — uma tag que você omitir é uma tag que você excluiu. Enviar `[]` limpa todas elas.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tags": [
      {
        "name": "hot-lead",
        "description": "The person confirms they want to buy, or asks how to get started right away.",
        "webhook": "https://example.com/hooks/campaign-events"
      },
      {
        "name": "not-interested",
        "description": "The person declines the offer or says they are not a fit."
      }
    ]
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      tags: [
        {
          name: "hot-lead",
          description:
            "The person confirms they want to buy, or asks how to get started right away.",
          webhook: "https://example.com/hooks/campaign-events",
        },
        {
          name: "not-interested",
          description: "The person declines the offer or says they are not a fit.",
        },
      ],
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "tags": [
            {
                "name": "hot-lead",
                "description": "The person confirms they want to buy, or asks how to get started right away.",
                "webhook": "https://example.com/hooks/campaign-events",
            },
            {
                "name": "not-interested",
                "description": "The person declines the offer or says they are not a fit.",
            },
        ]
    },
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

Leia as tags de volta com [`GET /campaigns/{campaignId}`](#get-a-campaign).

### Adicionar uma tag

`POST /campaigns/{campaignId}/tags`

Adiciona uma única tag sem reenviar o restante. Use isso quando estiver adicionando a um conjunto que você não criou nesta solicitação.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/tags?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "tag": { "name": "booked-a-call", "description": "The person confirms a booked time." } }'
```

Postar exatamente a mesma tag duas vezes não faz nada na segunda vez. Postar o mesmo `tag_id` com um nome ou descrição diferente adiciona uma **segunda** entrada em vez de editar a primeira — use o endpoint abaixo para editar no local.

### Atualizar ou remover uma tag

`PUT /campaigns/{campaignId}/tags/{tagId}`
`DELETE /campaigns/{campaignId}/tags/{tagId}`

Estes endereçam uma entrada pelo seu `tag_id`, portanto, eles só funcionam em tags que foram criadas com um. Se uma tag não tiver `tag_id`, altere-a com o `PUT /campaigns/{campaignId}` de array completo acima.

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/tags/tag_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "tag": { "name": "hot-lead", "description": "Updated instruction." } }'
```

Uma `tagId` que não está na campanha retorna `404` com `"Tag not found in campaign tags"`.

---

## Alternar os canais de uma campanha

`POST /campaigns/{campaignId}/channels`

Adiciona ou remove canais da matriz `enabled_channels` da campanha sem reenviar a matriz inteira — mais seguro que [`PUT /campaigns/{campaignId}`](#update-a-campaign) quando outra coisa pode estar editando a campanha ao mesmo tempo.

Envie uma única alternância ou um lote — não ambos na mesma solicitação:

```json
{ "channel": "whatsapp", "action": "add" }
```

```json
{ "add": ["whatsapp", "instagram"], "remove": ["sms"] }
```

| Campo | Descrição |
|---|---|
| `channel` | Um canal para alternar. Combine com `action`. |
| `action` | `"add"` ou `"remove"`. Combine com `channel`. |
| `add` | Matriz de canais para adicionar. Forma de lote — use em vez de `channel`/`action`. |
| `remove` | Matriz de canais para remover. Forma de lote. |

Canais válidos: `whatsapp`, `whatsapp_web`, `sms`, `instagram`, `messenger`, `facebook`, `chat_widget`, `custom_channel`, `imessage`, `telegram`, `instagram_private`, `line`, `viber`, `tiktok`, `email`, `linkedin`, `skool`.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/channels?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "whatsapp", "action": "add" }'
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "added": ["whatsapp"],
  "removed": []
}
```

> Isso altera apenas quais canais a campanha anuncia — não decide quem responde a um canal. Consulte [Tipos de campanha](#campaign-types) acima e [Roteie uma campanha para canais de entrada](#route-a-campaign-to-incoming-channels) abaixo para isso.

---

## Comentário-para-DM (Instagram e Facebook)

O Comentário-para-DM transforma um comentário em uma de suas publicações em uma conversa privada: alguém comenta, o bot envia uma DM e a campanha assume a conversa a partir daí. Ele é configurado inteiramente através do objeto de campanha, portanto, não há nada exclusivo da interface do usuário nele.

Conecte a Página do Facebook primeiro — veja [Conexão de Canal](channels.md#instagram--messenger-meta). Em seguida, defina os campos abaixo com [`PUT /campaigns/{campaignId}`](#update-a-campaign).

> **A campanha deve estar `Live`.** O monitoramento de comentários apenas detecta campanhas cujo `status` é `Live` (qualquer variação de maiúsculas/minúsculas — veja [Tipos de campanha](#campaign-types)). Qualquer outro status a desativa silenciosamente, e um inventado como `"Active"` agora é rejeitado com um `400` em vez de ser armazenado. Os status válidos incluem `Draft`, `Pending Approval`, `Scheduled`, `Live`, `Paused`, `Completed`, `Sent` e `Failed`.

**Campos**

| Campo | Tipo | Descrição |
|---|---|---|
| `monitor_instagram_posts` | boolean | Monitorar todas as publicações do Instagram na página conectada. |
| `instagram_post_ids` | string[] | Monitorar apenas estas publicações do Instagram. Deixe vazio quando `monitor_instagram_posts` estiver ativado. |
| `instagram_comment_delay_minutes` | number | Aguardar este número de minutos após um comentário antes de enviar a DM. |
| `monitor_facebook_posts` | boolean | Monitorar todas as publicações do Facebook na página conectada. |
| `facebook_post_ids` | string[] | Monitorar apenas estas publicações do Facebook. |
| `facebook_comment_delay_minutes` | number | Atraso antes da DM, em minutos. |
| `public_comment_reply_instructions` | string | Orientação para a resposta visível deixada no próprio comentário. Substitui o texto padrão "verifique suas DMs". |
| `first_response_mode` | string | `"ai"` (padrão) gera a primeira DM e a resposta pública. `"exact_text"` envia seu texto literalmente, sem geração de IA e sem cobrança de créditos. |
| `first_response_exact_text` | string | A primeira DM literal, usada quando `first_response_mode` é `"exact_text"`. Obrigatório para que esse modo entre em vigor. |
| `first_response_exact_text_variants` | string[] | Textos extras para a primeira DM. Um é escolhido aleatoriamente por envio, para que DMs repetidas não sejam idênticas. |
| `public_comment_reply_exact_text` | string | A resposta pública literal no modo `"exact_text"`. Deixe em branco para pular a resposta pública e enviar apenas a DM. |
| `public_comment_reply_exact_text_variants` | string[] | Textos extras para a resposta pública. |
| `monitor_instagram_followers` | boolean | Tratar um novo seguidor como um gatilho e enviar uma DM de boas-vindas (contas pessoais do Instagram). |
| `follower_outreach_instructions` | string | Orientação para essa DM de boas-vindas para novos seguidores. |
| `respond_to_instagram_story_replies` | boolean | Se a IA responde a respostas aos seus Stories do Instagram. Padrão `true`. Defina `false` para que as respostas aos Stories caiam no chat (com o Story anexado) sem uma resposta da IA. Configuração em tempo real — não faz parte do rascunho, portanto não precisa ser publicada. |

**Limpando um campo**

Esses campos são removidos em vez de definidos como `null` quando você envia `null`, para que o bot retorne aos seus padrões: `instagram_post_ids`, `facebook_post_ids`, `instagram_comment_delay_minutes`, `facebook_comment_delay_minutes`, `public_comment_reply_instructions`, `follower_outreach_instructions`, `first_response_exact_text`, `first_response_exact_text_variants`, `public_comment_reply_exact_text`, `public_comment_reply_exact_text_variants`.

> **Uma chave desconhecida rejeita toda a solicitação.** `PUT /campaigns/{campaignId}` valida todo o corpo em relação a uma lista de permissões. Uma chave que não é reconhecida retorna `400` para a solicitação como um todo — ela não é ignorada silenciosamente, e nenhum dos outros campos naquele corpo é gravado.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "Live",
    "monitor_instagram_posts": true,
    "instagram_comment_delay_minutes": 2,
    "first_response_mode": "exact_text",
    "first_response_exact_text": "Hey! Sending the details over now.",
    "first_response_exact_text_variants": [
      "Hi there, here are the details you asked for.",
      "Thanks for commenting, here is what you need."
    ],
    "public_comment_reply_exact_text": "Just sent you a DM."
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      status: "Live",
      monitor_instagram_posts: true,
      instagram_comment_delay_minutes: 2,
      first_response_mode: "ai",
      public_comment_reply_instructions:
        "Tell them to check their message requests folder too.",
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "status": "Live",
        "monitor_facebook_posts": True,
        "facebook_post_ids": None,
        "facebook_comment_delay_minutes": 5,
    },
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

> A resposta visível deixada no comentário requer o recurso de resposta a comentários em seu plano. Sem ele, a DM ainda é enviada e a resposta pública é ignorada.

---

## Otimizar uma campanha com IA

`POST /campaigns/{campaignId}/optimize`

Executa a mesma reescrita de IA que os fluxos de Otimizar e feedback de "polegar para baixo" do painel: recebe seu feedback, reescreve as instruções do bot e prepara o resultado como uma nova revisão de rascunho para você analisar.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `user_feedback` | Um destes dois é obrigatório | Feedback em formato livre descrevendo o que melhorar. |
| `thumbs_down_feedback` | Um destes dois é obrigatório | Feedback capturado a partir de um "polegar para baixo" em uma resposta específica do bot. |
| `thumbs_down_message` | Não | A mensagem do bot à qual o feedback de "polegar para baixo" se refere. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/optimize?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_feedback": "Make the tone more casual and mention the free trial earlier." }'
```

**Resposta** (`202` — a reescrita é executada em segundo plano)

```json
{ "success": true, "campaign_id": "NBCXrhqGPSFsd6MV7pRo" }
```

Faça polling em [`GET /campaigns/{campaignId}`](#get-a-campaign) e observe `test_bot.status`: ele muda para `"Optimizing"` imediatamente, e depois volta para `"Draft"` assim que a reescrita for concluída em `test_bot`. A partir daí, ele se comporta como qualquer rascunho do painel — revise-o e publique-o no painel para colocá-lo no ar. Um `409` significa que uma otimização já está em execução para esta campanha.

> A otimização consome créditos, da mesma forma que qualquer outra operação de IA em sua conta.

---

## Atribuir um contato a uma campanha

`POST /campaigns/{campaignId}/contacts/{contactId}/assign`

Coloca um contato existente em uma campanha e, se você solicitar, envia a mensagem de abertura da campanha imediatamente. Esta é a maneira de enviar o modelo de WhatsApp aprovado de uma campanha para um contato: o modelo com o qual uma campanha foi aprovada pertence a essa campanha, portanto, ele não aparece na biblioteca da [API de Modelos](templates.md) e não pode ser enviado através de `/whatsapp-templates/send`.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `sendOpeningMessage` | Não | `true` envia a mensagem de abertura da campanha (o modelo de WhatsApp aprovado em uma campanha de WhatsApp) assim que o contato é atribuído. O padrão é `false`. |
| `triggerAIResponse` | Não | `true` permite que a IA escreva sua própria primeira mensagem. O padrão é `false`. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/contacts/contact_abc123/assign?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "sendOpeningMessage": true }'
```

**Resposta**

```json
{
  "success": true,
  "data": { "contactId": "contact_abc123", "campaignId": "NBCXrhqGPSFsd6MV7pRo" }
}
```

> **Créditos:** O envio da mensagem de abertura em uma campanha de WhatsApp é cobrado como qualquer envio de modelo, precificado pelo país do destinatário e pela categoria do modelo. Em outros canais, a mensagem de abertura é uma mensagem de saída normal.

---

## Direcionar uma campanha para canais de entrada

Esses endpoints gerenciam qual campanha responde a contatos novos e desconhecidos em um canal. **Prefira Pontos de Entrada** para novas integrações (veja a nota em [Tipos de campanha](#campaign-types)) — eles continuam úteis para trabalhar com campanhas que utilizam o método de roteamento antigo e para resolver conflitos de propriedade de canal entre duas campanhas de entrada.

### Atribuir uma campanha a canais de entrada

`POST /campaigns/{campaignId}/incoming-routing`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `channels` | Sim | Matriz de canais para os quais esta campanha deve responder a contatos novos e desconhecidos. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/incoming-routing?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channels": ["whatsapp", "instagram"] }'
```

**Resposta**

```json
{
  "success": true,
  "uid": "abc123",
  "campaignId": "NBCXrhqGPSFsd6MV7pRo",
  "channels": ["whatsapp", "instagram"],
  "failed": []
}
```

`channels` lista apenas os canais que foram efetivamente roteados para esta campanha; `failed` lista todos os que não foram. Se todos os canais solicitados falharem, a própria solicitação falhará.

### Limpar o roteamento de entrada de uma campanha

`DELETE /campaigns/{campaignId}/incoming-routing`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `channelToUnassign` | Não | Limpa o roteamento apenas para este canal. Omita para limpar todos os canais que esta campanha atende atualmente. |

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/incoming-routing?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channelToUnassign": "instagram" }'
```

**Resposta**

```json
{
  "success": true,
  "uid": "abc123",
  "campaignId": "NBCXrhqGPSFsd6MV7pRo",
  "channelsRemoved": ["instagram"]
}
```

### Reativar uma campanha inativa

`POST /campaigns/{campaignId}/reactivate`

Retorna uma campanha de `Ended`, `Completed`, `Paused` ou `Draft` e reivindica seus canais novamente. Funciona apenas em campanhas `Incoming from Unknown Contacts` ou `Combined` — uma campanha que já está `Live` é tratada como sucesso, sem nada a fazer.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/reactivate?apiKey=YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "data": {
    "success": true,
    "channelsReactivated": ["whatsapp"],
    "channelsBlockedByConflict": [],
    "campaignType": "Incoming from Unknown Contacts"
  }
}
```

Um canal já reivindicado pelo agente de uma campanha diferente aparece em `channelsBlockedByConflict` em vez de falhar toda a chamada — use [parar uma campanha de entrada conflitante](#stop-a-conflicting-incoming-campaign) abaixo para liberá-lo primeiro, caso queira que esta campanha assuma o controle. Um `400` é retornado para um tipo de campanha que não suporta reativação ou para um status que não seja um dos inativos mencionados acima.

### Parar uma campanha de entrada conflitante

`POST /campaigns/{campaignId}/stop-incoming`

Libera os canais desta campanha de qualquer OUTRA campanha que os detenha atualmente, para que esta campanha possa reivindicá-los em seguida. Esta é a versão REST do que o painel faz automaticamente quando você inicia uma campanha de entrada em um canal que outra pessoa já está atendendo.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/stop-incoming?apiKey=YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "ended_campaign_ids": [],
  "released_channels": ["whatsapp"],
  "cleared_entire_field": false
}
```

`released_channels` retorna vazio quando esta campanha já possui todos os canais que anuncia — não há nada para assumir.

---

## Estimativas de custo

Estime quanto custará lançar uma campanha antes de enviá-la.

### Estimativa de custo de modelo do WhatsApp

`GET /campaigns/{campaignId}/template-cost-estimate`

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/template-cost-estimate?apiKey=YOUR_API_KEY"
```

**Resposta**

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

`billing_mode` é `"credits"` na via gerenciada do WhatsApp. Em uma via onde a Meta cobra diretamente da sua própria Conta Comercial do WhatsApp, `costPerContact`, `subtotal` e `totalTemplateCost` retornam `null` — nunca `0`, o que seria lido como gratuito — já que não há valor de crédito a ser reportado.

### Estimativa de custo de SMS

`GET /campaigns/{campaignId}/sms-cost-estimate`

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/sms-cost-estimate?apiKey=YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "billing_mode": "twilio_direct",
  "data": {
    "totalContacts": 120,
    "messageLength": 87,
    "segmentsPerMessage": 1,
    "totalSegments": 120,
    "estimatedCostUsd": 0.96,
    "priceUnit": "USD per segment",
    "billedByTwilio": true
  }
}
```

O SMS é sempre enviado através da sua própria conta Twilio (veja [provedor de SMS](../settings/sms-provider.md)), portanto, isso é sempre cobrado diretamente pela Twilio — `estimatedCostUsd` é uma estimativa dessa fatura da Twilio, não uma cobrança de crédito.

---

## Verificações de limite

Verifique um limite antes de iniciar, em vez de descobrir por meio de um envio com falha.

### Verificações no escopo da campanha

`GET /campaigns/{campaignId}/limits/ai-credit-messaging` — se iniciar ou agendar esta campanha excederia o limite de mensagens de crédito de IA da sua conta.

`GET /campaigns/{campaignId}/limits/messaging` — se excederia o limite diário de mensagens da sua conta.

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/limits/messaging?apiKey=YOUR_API_KEY"
```

**Resposta** (limite não excedido)

```json
{
  "success": true,
  "data": "Campaign is within the daily messaging limit."
}
```

Um `400` é retornado quando o limite for excedido, com o motivo em `error`.

### Verificações no escopo da conta

`GET /campaigns/limits/campaigns` — se você atingiu o limite mensal de criação de campanhas da sua assinatura.

`GET /campaigns/limits/contacts` — se você atingiu o limite de contatos da sua assinatura.

```bash
curl "https://api.youraiconnector.com/v1/campaigns/limits/campaigns?apiKey=YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "data": "You can create 3 more campaigns this month."
}
```

---

## Totais de estatísticas da campanha

`GET /campaigns/stats/totals`

Totais de enviados e respondidos para cada campanha E cada agente de IA em sua conta, durante um período recente — os mesmos números que a página de lista de campanhas mostra ao lado de cada linha, em uma única chamada em vez de uma solicitação por campanha.

| Parâmetro de consulta | Descrição |
|---|---|
| `days` | Tamanho do período recente, 1-365. O padrão é 90. |

```bash
curl "https://api.youraiconnector.com/v1/campaigns/stats/totals?days=30&apiKey=YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "byCampaign": {
    "NBCXrhqGPSFsd6MV7pRo": { "sent": 1204, "replied": 318 }
  },
  "byAgent": {
    "agent_abc123": { "sent": 1204, "replied": 318 }
  },
  "windowDays": 30
}
```

`byAgent` é seu próprio consolidado, não uma soma de `byCampaign` — o tráfego de uma conta nativa de Agente de IA pode não conter nenhuma campanha, portanto, caso contrário, seria invisível aqui.

---

## Testar uma campanha no playground

O playground permite que você mantenha uma conversa com o bot de uma campanha sem tocar em um canal real ou em um contato real. É o mesmo ambiente de teste do painel de experimentação do dashboard, e está totalmente disponível via API.

O fluxo é: criar um contato de teste oculto, enviar uma mensagem e, em seguida, consultar a campanha pela resposta do bot. As respostas são geradas de forma assíncrona, portanto, elas chegam em `test_messages` na campanha, em vez de no corpo da resposta.

> **O Playground utiliza os créditos de custo da API.** Uma conversa de teste iniciada com uma chave de API é cobrada na taxa normal de mensagens de IA, da mesma forma que uma resposta real, e aparece no seu histórico de uso como uma entrada comum. Testar a partir do painel permanece gratuito. A diferença é intencional: uma execução de teste realiza o mesmo trabalho de IA que uma ativa, portanto, um playground de API sem medição seria uma forma de executar IA ilimitada por conta de terceiros.

### Passo 1 - Criar o contato de teste

`POST /campaigns/{campaignId}/try-out/contact`

Cria o contato de teste oculto e o vincula à campanha. Todos os campos do corpo são opcionais; qualquer coisa que você omitir usará uma identidade de exemplo integrada (John Doe).

| Campo | Obrigatório | Descrição |
|---|---|---|
| `first_name` | Não | Primeiro nome do contato de teste. |
| `last_name` | Não | Sobrenome do contato de teste. |
| `email` | Não | E-mail do contato de teste. |
| `phone` | Não | Número de telefone do contato de teste. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/contact?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "first_name": "Maria", "last_name": "Lopez" }'
```

**Resposta**

```json
{
  "success": true,
  "contactId": "8kQx1vNbA2fLpR7d"
}
```

### Passo 2 - Registrar a mensagem recebida

`POST /campaigns/{campaignId}/try-out/messages`

Adiciona mensagens ao thread de teste. Envie a mensagem do visitante aqui primeiro, para que ela apareça no histórico da conversa que o bot lê.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `messages` | Sim | Matriz de objetos de mensagem, máximo de 200 por solicitação. |
| `messages[].body` | Sim | O texto da mensagem. |
| `messages[].direction` | Sim | `"inbound"` para o visitante, `"outbound"` para o bot. |
| `messages[].timestamp` | Não | String ISO-8601 ou milissegundos da época (epoch). |
| `messages[].role` | Não | Rótulo de função opcional. |
| `messages[].name` | Não | Nome de exibição opcional. |
| `ignoreCounter` | Não | Inteiro. Redefine o contador de ignorar da campanha na mesma gravação. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/messages?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "body": "Do you ship to Belgium?",
        "direction": "inbound",
        "timestamp": "2026-07-22T09:30:00Z"
      }
    ]
  }'
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "appended": 1
}
```

### Passo 3 - Pedir ao bot para responder

`POST /campaigns/{campaignId}/try-out/test-message`

Envia a mensagem para o pipeline de IA. Esta é a chamada que realmente produz uma resposta do bot.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `message` | Sim | O texto da mensagem mais recente do visitante. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/test-message?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Do you ship to Belgium?" }'
```

**Resposta**

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

`"Published"` significa que a mensagem foi enviada para o pipeline de IA. `"Ignored"` significa que uma mensagem de teste mais recente substituiu esta — o playground agrupa uma rajada rápida em uma única resposta, aproximadamente quatro segundos após a última mensagem, da mesma forma que uma conversa real espera alguém terminar de digitar. Devido a essa janela de agrupamento, esta chamada leva alguns segundos para retornar.

### Passo 4 - Ler a resposta

`GET /campaigns/{campaignId}`

A resposta do bot é adicionada à matriz `test_messages` da campanha. Consulte a campanha até que uma nova entrada `outbound` apareça.

```json
{
  "success": true,
  "campaign": {
    "id": "NBCXrhqGPSFsd6MV7pRo",
    "test_messages": [
      { "body": "Do you ship to Belgium?", "direction": "inbound" },
      { "body": "Yes, we ship across the EU.", "direction": "outbound" }
    ]
  }
}
```

### Redefinir o playground

`POST /campaigns/{campaignId}/try-out/reset`

Limpa todo o sandbox: exclui o contato de teste, apaga `test_messages` e libera os bloqueios de resposta do bot. Use isso entre as execuções de teste.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/reset?apiKey=YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

### Outros endpoints do playground

| Endpoint | O que faz |
|---|---|
| `DELETE /campaigns/{campaignId}/try-out/contact` | Exclui apenas o contato de teste atual e desvincula-o, deixando `test_messages` intacto. Tem sucesso mesmo quando nenhum contato está vinculado. |
| `POST /campaigns/{campaignId}/try-out/transfer` | Inicia um playground novo preenchido com uma conversa existente, em uma única solicitação: substitui o contato de teste e sobrescreve `test_messages`. O corpo aceita `first_name`, `last_name`, `messages` (pode estar vazio) e `ignoreCounter`. Prefira isso em vez de excluir-então-criar-então-anexar, o que triplica o consumo do seu limite de taxa. |
| `POST /campaigns/{campaignId}/try-out/messages/replace` | Sobrescreve `test_messages` por completo em vez de anexar. Use para truncar ou retroceder uma thread. |
| `POST /campaigns/{campaignId}/try-out/contact/reset-ignore-counter` | Redefine apenas o contador de ignorar do contato de teste, para fluxos de refazer e repetir após um envio. |

---

## Erros da API de Campanhas

Os endpoints de campanha retornam o envelope de erro padrão:

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

| Status | Quando ocorre em um endpoint de campanha |
|---|---|
| `400` | Um campo obrigatório está ausente ou inválido (por exemplo, um `type` incorreto, um `enabled` não booleano ou uma chave de dia da semana desconhecida). Também retornado por um endpoint de [verificação de limite](#limit-checks) quando o limite seria excedido, e por [reativar](#reactivate-a-dormant-campaign) para um tipo ou status de campanha que não oferece suporte a isso. |
| `404` | A campanha não foi encontrada — ou ela não existe ou pertence a outra conta. |
| `409` | Uma [otimização](#optimize-a-campaign-with-ai) já está em execução para esta campanha. |

Os códigos compartilhados que todo endpoint pode retornar — `401`, `403` (seu plano não inclui acesso à API), `429` (limite de taxa) e `500` — estão listados com orientações de nova tentativa em [Erros e Paginação](errors-and-pagination.md).

---

## Relacionado

- [Direcione um canal para uma campanha](channels.md#route-a-channel-to-a-campaign) — aponte o Instagram, WhatsApp ou qualquer outro canal para o Agente de IA que deve respondê-lo, usando Pontos de Entrada.
- [Gere modelos de acompanhamento com IA](templates.md#generate-follow-up-templates-with-ai) — inicie um trabalho em segundo plano que escreve os modelos de acompanhamento de WhatsApp de uma campanha.
- [API de FAQs](faqs.md) — gerencie as entradas de perguntas e respostas que suas campanhas usam.
- [Acesso à API](../integrations/api-access.md) — gere sua chave de API.
- [Autenticação](authentication.md) — todas as maneiras de transmitir sua chave.
