
# API para Agências

Como agência, você pode usar a mesma API REST que seus clientes usam, mas direcionar solicitações individuais para uma de suas **subcontas** gerenciadas em vez de sua própria conta. Isso permite que você crie ferramentas que integram um cliente de ponta a ponta — criando suas campanhas, treinando sua IA em uma base de conhecimento, importando seus contatos, conectando seus canais de mensagens e comprando números de telefone — tudo sem precisar fazer login em cada subconta manualmente.

Esta página cobre apenas o comportamento específico de agências: como agir em nome de uma subconta com o parâmetro `sub_account_id`. Para o básico (geração de chave, autenticação, URL base, formato de erro, limites de taxa), comece pelo guia de [Acesso à API](../integrations/api-access.md). Tudo o que está lá também se aplica aqui — você se autentica com a chave de API da **sua conta de agência**.

::: note
**Nota:** Esta página é técnica. Se você não for um desenvolvedor, compartilhe-a com a pessoa responsável pela criação da sua integração.
:::


***

## Como funciona o "agir em nome de"

Por padrão, toda solicitação de API atua na conta que possui a chave de API — sua conta de agência. Para atuar em uma conta de cliente gerenciada, adicione o parâmetro opcional `sub_account_id` à solicitação, definido como o ID da conta desse cliente.

- **Omitir `sub_account_id`** → a solicitação atua em sua própria conta de agência.
- **Incluir `sub_account_id`** → a solicitação atua nessa subconta, mas apenas após a plataforma confirmar que a subconta é realmente sua.

Você sempre se autentica com a chave de API da **sua conta de agência**. Você nunca precisa da chave da própria subconta e nunca manipula as credenciais da subconta.

### Onde inserir

- **Endpoints GET / DELETE** → passe-o como um parâmetro de consulta: `?sub_account_id=THE_SUB_ACCOUNT_ID` (junto com seu `apiKey`, se você se autenticar por consulta).
- **Endpoints POST / PUT / PATCH** → inclua-o no corpo da requisição JSON como `"sub_account_id": "THE_SUB_ACCOUNT_ID"`.
- **Assistentes de IA** → nada a configurar. O [servidor MCP](../integrations/connect-ai-clients.md) carrega a mesma configuração em suas ferramentas de leitura, portanto, uma conexão com sua chave de agência pode gerar relatórios sobre cada cliente: basta nomear o cliente em sua solicitação ("quantos contatos o Bella's Bistro tem?"). Ações de escrita também estão disponíveis: cada endpoint que aceita `sub_account_id` é exposto como uma ferramenta, para que você possa criar, alterar e enviar em nome de um cliente a partir da mesma conexão.

### Encontrando o ID de uma subconta

O `sub_account_id` é o identificador único da conta do cliente. Você pode obter a lista de suas subcontas e seus respectivos IDs nos endpoints da API de **SubAccounts** (veja o guia de [Sub-Accounts](sub-accounts.md)) ou na página de **Sub Accounts** na barra lateral.

***

## A propriedade é sempre verificada

Quando você passa um `sub_account_id`, a plataforma verifica se a conta é uma subconta real **e** se ela pertence à sua agência. Somente então a solicitação é processada.

Se o ID for desconhecido, não for uma subconta ou pertencer a uma agência diferente, a solicitação falhará com uma resposta **`404`**:

```json
{
  "success": false,
  "error_code": 404,
  "error": "Sub-account not found."
}
```

> **Por que 404 e não 403?** Uma resposta "proibido" (forbidden) diria a um estranho que o id existe, mas não pertence a ele. Retornar o mesmo `404` para "não existe" e "não é seu" significa que o endpoint não pode ser usado para descobrir quais ids de conta pertencem a outras agências. Trate um `404` aqui como "esta não é uma subconta que você gerencia."

***

## Onde `sub_account_id` é suportado

`sub_account_id` é aceito em praticamente todos os endpoints de **recurso** — qualquer chamada que crie, leia, atualize ou exclua dados da própria conta. Na prática, você pode provisionar e executar toda a configuração de uma subconta com sua chave de agência:

- **Configuração de IA** — campanhas, agentes, perguntas frequentes, fontes de base de conhecimento (rastreamento de site **e** upload de documentos), grupos de base de conhecimento, transmissões, funções personalizadas, servidores MCP
- **Contatos e CRM** — contatos (incluindo importação), listas, tags, tarefas, negócios, agendamentos, eventos
- **Canais e números** — conectar WhatsApp / WhatsApp Web / Telegram / Instagram & Messenger / LINE, pesquisar / comprar / gerenciar números de telefone, modelos de WhatsApp, roteamento de canal
- **Mensagens e conteúdo** — enviar mensagens, sessões de chat, exportações de chat, resumos diários
- **Configurações e integrações** — webhooks, configuração de widget de chat, configuração de white-label, SMS BYOK e outras configurações de conta, análises

Em todos eles, o parâmetro é **opcional** — deixe-o de fora e a chamada atuará em sua própria conta de agência, para que uma única integração sirva para ambos. Créditos e uso sempre vêm da conta que você direciona: cobranças por campanhas, mensagens, tags e números de uma subconta são debitadas do saldo **da subconta**.

### Onde NÃO se aplica

Alguns endpoints são de nível de agência ou auto-endereçados e ignoram `sub_account_id`:

- **Gerenciamento das próprias subcontas** — os endpoints de SubAccounts (criar / listar / atualizar uma subconta) e o endpoint de limite de gastos BYOK já nomeiam a subconta em seu próprio caminho de URL. Os [endpoints de precificação e política](#set-per-client-ai-pricing-and-policy) e os [endpoints de monitoramento de chat](#read-a-sub-accounts-conversations) seguem o mesmo padrão.
- **Cópia de um Agente entre contas** — `POST /v1/subaccounts/agents/copy` nomeia ambas as contas, tomando o destino como `targetUserId`. Veja o [exemplo prático](#worked-example-ship-a-template-agent-into-every-new-client) abaixo. (O `POST /v1/subaccounts/campaigns/copy` mais antigo funciona da mesma forma, mas está obsoleto junto com o restante da [API de Campanhas](../api/campaigns.md).)
- **Ajuste de créditos e os dois rollups de toda a agência** — [`POST /v1/subaccounts/credits`](#grant-or-deduct-credits-directly) identifica a subconta por `email`; [`GET /v1/subaccounts/credit-usage`](#read-credit-usage-and-campaign-health-across-your-book) e `GET /v1/subaccounts/campaign-status` geram relatórios de todas as subcontas de uma só vez, portanto, não há uma conta única para direcionar.
- **A conta da sua própria agência** — O gerenciamento de chaves de API, relatórios de uso da agência, gerenciamento de equipe e seus [níveis de precificação](#manage-your-pricing-tiers-over-the-api) sempre atuam na conta da sua agência.
- **Webhooks de mensagens recebidas** — os endpoints para os quais sistemas externos enviam dados estão vinculados à conta cujas credenciais os configuraram, portanto, não há nada para redirecionar.

> A lista sempre atualizada e legível por máquina de quais parâmetros cada endpoint aceita encontra-se na referência da API do seu painel (**Settings → Integrations → API Key**) e na especificação OpenAPI em `GET /v1/docs/openapi.yaml`. Lançamos alterações na API com frequência — trate essas fontes como a referência definitiva.

::: master-only
<figure><img src="../.gitbook/assets/v2-api-access-key-section.png" alt="Página de configurações da Chave de API com chave mascarada e controle de Regenerar"><figcaption><p>Configurações → Integrações → Chave de API — a chave da sua agência fica aqui, junto com o link para a referência completa da API.</p></figcaption></figure>
:::

***

## Exemplo prático: conectar Instagram e Messenger para uma subconta

Conectar Instagram e Messenger é um fluxo baseado em navegador. Você o inicia com a API, entrega a URL de consentimento retornada ao cliente (ou abre para ele), espera que ele autorize no navegador e, em seguida, escolhe qual página conectar — tudo isso direcionando a subconta dele com `sub_account_id`.

### Passo 1 — Iniciar a conexão

Chame o endpoint de conexão com o `sub_account_id` do cliente no corpo. Nenhuma credencial é enviada aqui; a plataforma retorna uma URL de consentimento que o cliente deve abrir em um navegador, além de um token de correlação de uso único.

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/channels/meta/connect?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sub_account_id": "abc123def456"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.dmchamp.com/v1/channels/meta/connect", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    sub_account_id: "abc123def456",
  }),
});

const data = await res.json();
// data.oauth_url -> open this in the client's browser
```

**Python**

```python
import requests

res = requests.post(
    "https://api.dmchamp.com/v1/channels/meta/connect",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "sub_account_id": "abc123def456",
    },
)

data = res.json()
# data["oauth_url"] -> open this in the client's browser
```

**Resposta:**

```json
{
  "success": true,
  "oauth_url": "https://www.facebook.com/v21.0/dialog/oauth?client_id=...&state=...",
  "state_token": "8sFq2yV0kQ7m4n1pZr3tWb6cXe9hJl2aD5gK7uN0oI",
  "expires_at": "2026-06-10T12:30:00.000Z"
}
```

Envie o cliente para `oauth_url` em um navegador para autorizar. O `state_token` correlaciona esta tentativa e é um segredo de curta duração — não o registre em logs. A tentativa expira em `expires_at`; se expirar, comece novamente.

### Passo 2 — Fazer polling até que as páginas carreguem

Após a autorização do cliente, faça a sondagem (polling) do endpoint de status (com o mesmo `sub_account_id`, desta vez como um parâmetro de consulta) até que as páginas conectáveis apareçam.

**cURL**

```bash
curl "https://api.dmchamp.com/v1/channels/meta/status?apiKey=YOUR_API_KEY&sub_account_id=abc123def456"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.dmchamp.com/v1/channels/meta/status?sub_account_id=abc123def456",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);

const data = await res.json();
// Wait until data.status === "pages_loaded", then read data.pages
```

**Python**

```python
import requests

res = requests.get(
    "https://api.dmchamp.com/v1/channels/meta/status",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"sub_account_id": "abc123def456"},
)

data = res.json()
# Wait until data["status"] == "pages_loaded", then read data["pages"]
```

**Resposta:**

```json
{
  "success": true,
  "status": "pages_loaded",
  "pages": [
    {
      "id": "1098765432101234",
      "name": "Acme Studio",
      "category": "Hair Salon",
      "instagram_business_account": {
        "id": "17841400000000000",
        "username": "acme.studio"
      }
    }
  ],
  "selected_page": null
}
```

O campo `status` passa por `pending` → `token_received` → `pages_loaded` → `connected`. Aguarde por `pages_loaded` antes de selecionar uma página. Dois estados de erro terminal também podem aparecer em vez de progredir: `failed` e `expired` (o cliente recusou o consentimento ou o período de ~30 minutos do token de estado expirou) — um campo `reason` é incluído quando qualquer um deles ocorre. Pare a sondagem e reinicie na Etapa 1 se vir um deles; não espere por `pending` para sempre. Tokens de acesso à página nunca são retornados.

### Passo 3 — Selecione a página para conectar

Escolha um dos IDs de página do Passo 2 e selecione-o. Selecionar uma página conecta tanto o Instagram quanto o Messenger para essa página. Inclua `sub_account_id` no corpo novamente.

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/channels/meta/select-page?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "page_id": "1098765432101234",
    "sub_account_id": "abc123def456"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.dmchamp.com/v1/channels/meta/select-page", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    page_id: "1098765432101234",
    sub_account_id: "abc123def456",
  }),
});

const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.dmchamp.com/v1/channels/meta/select-page",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "page_id": "1098765432101234",
        "sub_account_id": "abc123def456",
    },
)

data = res.json()
```

**Resposta:**

```json
{
  "success": true,
  "page_id": "1098765432101234",
  "instagram_business_account_id": "17841400000000000"
}
```

É isso — o Instagram e o Messenger agora estão conectados na subconta do cliente. Você forneceu apenas o `page_id`; a credencial subjacente é resolvida no servidor e nunca passa pela sua integração.

***

## Exemplo prático: comprar um número para uma subconta

A compra de um número funciona da mesma forma: pesquise com `sub_account_id` na consulta e, em seguida, compre com ele no corpo da requisição. Os créditos são deduzidos do saldo **da subconta**, e o número é provisionado na subconta.

**Pesquisa (cURL):**

```bash
curl "https://api.dmchamp.com/v1/phone-numbers/available?apiKey=YOUR_API_KEY&country_code=US&sub_account_id=abc123def456"
```

**Compra (JavaScript):**

```javascript
const res = await fetch("https://api.dmchamp.com/v1/phone-numbers", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phone_number: "+14155551234",
    country_code: "US",
    display_name: "Support line",
    sub_account_id: "abc123def456",
  }),
});

const data = await res.json();
```

**Compra (Python):**

```python
import requests

res = requests.post(
    "https://api.dmchamp.com/v1/phone-numbers",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "phone_number": "+14155551234",
        "country_code": "US",
        "display_name": "Support line",
        "sub_account_id": "abc123def456",
    },
)

data = res.json()
```

**Resposta:**

```json
{
  "success": true,
  "phone_number": "+14155551234",
  "channel": "whatsapp",
  "whatsapp_status": "PURCHASED",
  "outgoing_status": "PURCHASED",
  "status": "PURCHASED",
  "purchase_credits": 11.5,
  "monthly_credits": 11.5
}
```

O número é provisionado no estado `PURCHASED` e o registro do remetente do WhatsApp continua em segundo plano. Faça a sondagem de `GET /v1/phone-numbers/{phoneNumber}/status?sub_account_id=abc123def456` até que o status atinja `ONLINE` antes de enviar.

***

## Exemplo prático: enviar um Agente modelo para cada novo cliente

O padrão usual de agência é manter um Agente mestre na sua conta de agência, configurado da maneira que você deseja que cada cliente comece, e criar uma cópia dele em cada nova subconta no momento do provisionamento. São três chamadas e nada precisa ser repetido depois: a cópia mantém suas configurações até que você as altere.

### Passo 1 — Copiar o Agente

`POST /v1/subaccounts/agents/copy`

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts/agents/copy?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agentId": "YOUR_TEMPLATE_AGENT_ID",
    "targetUserId": "abc123def456",
    "newName": "Inbound Instagram Leads",
    "copyFaqs": true
  }'
```

A resposta contém o ID do novo Agente em `data.agent_id`. As FAQs, a base de conhecimento e a biblioteca de mídia são incluídas; os modelos de WhatsApp, postagens sociais conectadas e contatos da conta de origem, deliberadamente, não são. Lista completa de campos na [API de Agentes de IA](../api/agents.md#copy-an-agent-into-a-sub-account-agencies).

Observe que este endpoint utiliza `targetUserId` em vez de `sub_account_id` — ele nomeia ambas as contas. As duas chamadas abaixo usam o parâmetro `sub_account_id` normal.

### Passo 2 — Ativá-lo

A cópia sempre chega pausada, portanto, não pode enviar mensagens a ninguém até que você autorize. Este também é o momento de definir o nível de IA em que você deseja que o cliente esteja; ele permanece lá, então não há necessidade de reaplicá-lo em uma programação.

```bash
curl -X PATCH "https://api.dmchamp.com/v1/agents/NEW_AGENT_ID/active?apiKey=YOUR_API_KEY&sub_account_id=abc123def456" \
  -H "Content-Type: application/json" \
  -d '{ "active": true }'

curl -X PUT "https://api.dmchamp.com/v1/agents/NEW_AGENT_ID?apiKey=YOUR_API_KEY&sub_account_id=abc123def456" \
  -H "Content-Type: application/json" \
  -d '{ "anthropic_model": "max" }'
```

Para impedir que o cliente altere o nível posteriormente, [bloqueie os níveis permitidos](#set-per-client-ai-pricing-and-policy) na subconta em vez de reenviar o valor.

### Passo 3 — Direcionar os canais do cliente para ela

A cópia também chega sem roteamento, portanto, nada chega até ela até que você a torne a responsável pelas respostas nos canais que o cliente conectou. Uma chamada por canal:

```bash
curl -X PUT "https://api.dmchamp.com/v1/entry-points/channel-defaults?apiKey=YOUR_API_KEY&sub_account_id=abc123def456" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "instagram", "agent_id": "NEW_AGENT_ID" }'
```

A partir daqui, uma primeira mensagem de um contato desconhecido nesse canal é captada pelo Agente copiado automaticamente. Veja [Apontar um canal para um Agente](../api/entry-points.md#point-a-channel-at-an-agent) para os outros canais e roteamento por número.

> **Defina o fuso horário do cliente ao criar a subconta.** Passe `time_zone_id` em `POST /v1/subaccounts`. As horas ativas da campanha são avaliadas no próprio fuso horário da subconta, portanto, um cliente criado sem um terá seu agendamento lido em relação ao UTC — o que altera silenciosamente quando o assistente tem permissão para responder.

***

## Pule o assistente de configuração para um cliente que você mesmo configura

`POST /v1/subaccounts`

Por padrão, na primeira vez que o proprietário de uma nova subconta faz login, ele é guiado pelo Assistente de Configuração. Para clientes com serviço completo — onde você cria a campanha e conecta os canais antes mesmo de o cliente fazer login — passe `guided_onboarding: false` ao criar a conta. Eles serão direcionados para o painel de controle, e a entrada **Assistente de Configuração** ficará oculta na barra lateral deles.

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "client@example.com",
    "first_name": "Alex",
    "last_name": "Client",
    "business_name": "Client Co",
    "guided_onboarding": false,
    "usage_limits": { "monthly_credits": 500 }
  }'
```

Omita o campo (ou envie `true`) e o assistente se comportará exatamente como sempre se comportou, portanto, as integrações existentes não precisam de alterações. Para devolver o assistente a um cliente mais tarde, exiba novamente o item `guided_onboarding` com `PUT /v1/subaccounts/{subAccountUid}/menu-visibility` (abaixo) — a visibilidade do menu controla se o assistente pode ser acessado, `guided_onboarding` controla apenas o redirecionamento do primeiro login.

***

## Desative Tarefas, Resumos Diários ou a Biblioteca de Mídia para um cliente

`POST /v1/subaccounts`

Esses três recursos estão ativados para cada novo cliente, a menos que você especifique o contrário, e eles se comportam de maneira diferente de todos os outros recursos neste guia: eles são de **opt-out** (desativação opcional), não de opt-in. Deixá-los de fora da `features` não é suficiente por si só, porque a lista `features` de uma integração mais antiga simplesmente nunca os mencionou — não conseguimos distinguir entre "a agência desativou isso" e "esta lista foi escrita antes de a opção existir".

Portanto, declare isso explicitamente com `feature_settings`:

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "client@example.com",
    "first_name": "Alex",
    "last_name": "Client",
    "business_name": "Client Co",
    "feature_settings": {
      "tasks": false,
      "daily_summaries": false,
      "ai_media_library": true
    }
  }'
```

Cada chave é opcional; tudo o que você omitir permanecerá ativado. Com `tasks: false`, a IA para de criar tarefas para aquele cliente e nenhum e-mail de "Nova Tarefa Criada" é enviado; com `daily_summaries: false`, o resumo noturno nunca é gerado ou enviado por e-mail.

`feature_settings` é a única coisa que desativa esses três recursos no momento da criação. Deixá-los de fora da `features` não faz nada por si só, não importa como o restante da sua lista esteja — isso é intencional, para que uma integração mais antiga não perca silenciosamente todos os três.

Para alterar qualquer um desses itens posteriormente, envie a lista `features` completa para `PUT /v1/subaccounts/{subAccountUid}/features` — lá, a presença na lista ativa um recurso e a ausência o desativa.

***

## Faça login automático de seus clientes em suas subcontas (SSO)

`POST /v1/subaccounts/{subAccountUid}/sso-link`

Uma única chamada com a chave de API da sua agência retorna uma URL pronta para abrir que conecta o cliente diretamente à sua própria subconta — sem tela de login, sem etapa de senha, nada para construir por cima. Abra em uma nova aba, em um redirecionamento ou em um iframe dentro do seu próprio produto.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `redirect` | Não | Página no aplicativo em que você deseja que o cliente termine, por exemplo, `"/chats"` ou `"/agents"`. Retornado como `deep_link_url` na resposta. |
| `app_base_url` | Não | Host do painel para o link. O padrão é o seu domínio de white-label (ou o domínio da plataforma, caso não tenha um). Deve ser `https`. |

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/sso-link" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "redirect": "/chats" }'
```

**Resposta**

```json
{
  "success": true,
  "url": "https://app.yourdomain.com/auth?redirect=%2Fchats#token=eyJhbGciOi…",
  "deep_link_url": "https://app.yourdomain.com/chats",
  "expires_at": "2026-07-22T15:04:05.000Z",
  "sub_account_uid": "SUB_ACCOUNT_UID"
}
```

Como usar bem:

- **Um salto.** Abrir o `url` autentica o cliente e o leva diretamente para a página `redirect` do painel — sem tela de login, sem página intermediária. O `deep_link_url` nomeia o mesmo destino, para integradores que preferem navegar por um frame explicitamente após o login; uma vez que a sessão exista, qualquer caminho do painel funciona nesse contexto de navegador.
- **Crie sob demanda, abra imediatamente.** O link contém uma credencial de login e expira após cerca de uma hora. Solicite-o no lado do servidor no momento em que o cliente clicar, e nunca o armazene ou envie por e-mail.
- O token de login viaja no fragmento da URL (`#…`), que os navegadores nunca enviam para servidores, e é removido da barra de endereços no momento em que é consumido.
- **Apenas suas próprias subcontas.** O endpoint recusa qualquer conta que sua agência não possua.
- Um link expirado mostra um erro claro com um caminho para tentar novamente — crie um novo.

***

## Ocultar itens de navegação em uma subconta

`PUT /v1/subaccounts/{subAccountUid}/menu-visibility`

Controla quais itens da barra lateral e de configurações uma subconta vê — útil quando você incorpora o painel e deseja apenas as superfícies que seu produto ainda não cobre. Tudo o que não estiver listado permanece visível; envie `null` como o valor total de `menuVisibility` para redefinir tudo como visível. Ocultar um item oculta a entrada do menu — combine isso com os recursos que você concede à subconta para um controle rígido.

**cURL**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/menu-visibility" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "menuVisibility": {
      "side_nav": { "Dashboard": false, "Campaigns": false, "Automations": false },
      "settings_nav": { "team": false }
    }
  }'
```

**Resposta**

```json
{
  "success": true,
  "data": {
    "subAccountUid": "SUB_ACCOUNT_UID",
    "menuVisibility": {
      "side_nav": { "Dashboard": false, "Campaigns": false, "Automations": false },
      "settings_nav": { "team": false }
    }
  }
}
```

`side_nav` aceita estas 13 chaves, que correspondem aos nomes dos itens da barra lateral: `Dashboard`, `DailySummaries`, `Chats`, `Contacts`, `Deals`, `Tasks`, `Automations`, `Campaigns`, `Appointments`, `Settings`, `Help`, `CreditsCounter` (o saldo de crédito exibido na barra lateral) e `guided_onboarding` (o Assistente de Configuração). Três chaves adicionais — `AiInsights`, `Sub Accounts` e `Agency Reselling` — são aceitas, mas não fazem nada: elas só se aplicavam ao painel clássico desativado, portanto, defini-las não tem efeito em suas subcontas. Chaves ausentes significam visível; quando você mesmo faz login na subconta, os itens ocultos são exibidos temporariamente para que você possa sempre reverter as alterações.

Ocultar uma página do menu nunca concede acesso a ela. `Automations` precisa que o recurso `automations` seja concedido na subconta — defina a chave como `true` sem ele e a página ainda não aparecerá. `Tasks` e `DailySummaries` funcionam de forma inversa: eles estão ativados para cada cliente, a menos que você os desative (veja [Desative Tarefas, Resumos Diários ou a Biblioteca de Mídia para um cliente](#turn-tasks-daily-summaries-or-the-media-library-off-for-a-client)).

***

## Escolha quais tipos de canal um cliente pode conectar

`PUT /v1/subaccounts/{subAccountUid}/features`

As chaves de **Tipos de Canal** que você vê em um nível de plano são IDs de recursos comuns, portanto, você pode defini-las por cliente a partir da API em vez do painel. Este é um dos endpoints que nomeia a subconta em sua própria URL, por isso não requer `sub_account_id`.

| ID do Recurso | Canal |
|---|---|
| `channel_chat_widget` | Widget de Chat do Site |
| `channel_whatsapp_api` | API do WhatsApp Business |
| `channel_whatsapp_web` | WhatsApp Web (número vinculado por QR) |
| `channel_instagram` | Instagram |
| `channel_messenger` | Facebook Messenger |
| `channel_telegram` | Telegram |
| `channel_line` | LINE |
| `channel_viber` | Viber |
| `channel_email` | Caixa de entrada de e-mail |
| `channel_sms` | SMS |
| `channel_imessage` | iMessage |

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/features" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "features": [
      "channels_3",
      "channel_chat_widget",
      "channel_whatsapp_web",
      "channel_instagram",
      "image_understanding",
      "contact_tagging",
      "incoming_campaigns",
      "webhooks"
    ]
  }'
```

Três coisas para acertar:

- **A chamada substitui toda a lista de recursos.** Envie todos os recursos que o cliente deve manter, não apenas aqueles que você está alterando. Os mesmos IDs funcionam como `features` em `POST /v1/subaccounts` quando você cria a conta.
- **Tipos de canal e contagem de canais são restrições separadas, e ambas se aplicam.** `channels_1` / `channels_3` / `channels_unlimited` controlam *quantas* conexões; os IDs `channel_*` controlam *quais tipos*. O exemplo acima significa "até 3 conexões, e apenas Widget de Chat, WhatsApp Web ou Instagram".
- **Não enviar nenhum ID de `channel_*` significa nenhuma restrição de canal.** Esse é o comportamento original, e é por isso que os clientes existentes não foram afetados quando isso foi lançado. Envie um ou mais e todo o resto aparecerá como bloqueado na página de Canais do cliente com uma nota de upgrade em vez de um botão Conectar. Canais que o cliente já conectou continuam funcionando.

> Definir a lista de canais em um **nível de plano**, para que cada cliente que compra esse nível a herde, é feito no painel, nas configurações do seu plano de agência. Este endpoint a define em uma subconta específica.

***

## Defina um limite exato de membros da equipe para um cliente

`PUT /v1/subaccounts/{subAccountUid}/limits`

Os recursos do `team_seats_*` oferecem apenas níveis predefinidos (3 / 5 / 10 / ilimitado). Para dar a um cliente um número **exato** de assentos na equipe — 2, 7, 15, qualquer um — defina `usage_limits.team_seats_limit` em vez disso. Ele prevalece sobre as predefinições, e a plataforma o aplica em cada convite, adição direta e aceitação de convite: assim que o limite é atingido, convites adicionais são recusados pelo servidor.

- Um número inteiro positivo é o limite exato.
- `0` significa que os membros da equipe **não estão incluídos** — o cliente não pode convidar ninguém.
- `-1` significa ilimitado.
- `null` limpa o limite personalizado e retorna para qualquer predefinição do `team_seats_*` que esteja na lista de recursos.

Reduzir o limite nunca remove membros existentes da equipe; apenas impede que novos sejam adicionados.

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/limits" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "usageLimits": { "team_seats_limit": 7 }
  }'
```

Você também pode defini-lo no momento da criação: `POST /v1/subaccounts` aceita `usage_limits.team_seats_limit` com a mesma semântica. Para ler o valor atual, busque a subconta com `GET /v1/subaccounts?email=...` e verifique `usage_limits.team_seats_limit` (ausente/`null` = os padrões decidem). O mesmo endpoint também atualiza `credits`, `monthly_credits`, `roll_over_to_next_month`, `rollover_cap_months`, `rollover_expiry_days` e `byok_monthly_limit_usd` — envie apenas as chaves que deseja alterar.

**Como isso interage com os limites de assentos do plano SaaS.** Seus planos SaaS podem ter sua própria franquia de assentos (definida no editor de planos — veja [Assentos de equipe em um plano](agency-accounts.md#step-3--set-up-pricing-tiers)), que é aplicada automaticamente quando um cliente assina. Um limite que você define por meio deste endpoint conta como uma concessão **manual**: comprar um plano substitui esse limite pela franquia de assentos do próprio plano (essa compra é uma escolha explícita de plano), mas **renovações** mensais automáticas **nunca sobrescrevem um limite manual** — portanto, uma exceção única que você concede a um cliente persiste durante seu ciclo de faturamento. Limpar o limite manual com `null` devolve o controle do campo ao plano na próxima renovação.

**Limite o que um cliente carrega entre renovações.** Duas chaves `usage_limits` adicionais ficam ao lado de `roll_over_to_next_month`. Ambas também são aceitas por `POST /v1/subaccounts` no momento da criação, e `null` limpa qualquer uma delas.

| Chave | O que faz |
|---|---|
| `rollover_cap_months` | Meses de franquia que o cliente pode manter. Um número de 0 a 120, frações permitidas (`0.5` = meio mês). A cada renovação, o saldo não utilizado é reduzido para, no máximo, esse número de vezes a franquia concedida na renovação, antes que os novos créditos sejam adicionados; `0` não transporta nada. |
| `rollover_expiry_days` | Um número inteiro de dias, de 1 a 3650. Créditos deixados sem uso por tanto tempo são descartados na primeira renovação após atingirem essa idade. O consumo sempre abate os créditos mais antigos primeiro, portanto, um cliente que gasta sua franquia a cada mês nunca perde nada. |

Se não forem definidos, ambos recorrem ao plano do cliente; um valor enviado aqui prevalece sobre o do plano. Apenas créditos recorrentes (a franquia mensal e os créditos do plano) estão sujeitos a eles: recargas, recargas automáticas e adições únicas nunca são limitadas ou expiradas. Cada redução é registrada no histórico de créditos do cliente como um **Ajuste de Crédito de Limite de Rolagem** ou um **Ajuste de Crédito de Créditos Expirados** e nunca conta como uso. Os equivalentes no nível do plano são `rollover_cap_months` e `rollover_expiry_days` em um nível de preço — veja [Os campos em um nível](#the-fields-on-a-tier) e [Limitando o que é transferido](sub-accounts.md#capping-what-rolls-over).

***

## Definir precificação e política de IA por cliente

`PUT /v1/subaccounts/{subAccountUid}/max-tier` · `/ai-tiers` · `/max-rate` · `/action-pricing` · `/insider-rate` · `/locked-bot-fields` · `/notifications` · `/zero-credit-reply`

Oito chaves adicionais por cliente, juntamente com `/limits`, `/features` e `/menu-visibility` acima. Cada uma utiliza o uid da subconta na URL (sem parâmetro de corpo/query `sub_account_id` — o destino já está nomeado no caminho) e tem o mesmo escopo: sua chave de agência, e a subconta deve pertencer à sua agência.

**Quais modelos de IA um cliente pode usar**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/max-tier" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'
```

`{ "enabled": boolean }` inclui o cliente no (ou o exclui do) nível Max AI — nossa infraestrutura pelo preço de lista da plataforma. Ativar isso para um cliente BYOK altera o custo de IA dele de "gratuito na minha própria chave" para "cobrado contra meu pool de créditos", portanto, é uma decisão deliberada por cliente em vez de um padrão para toda a agência.

Para restringir QUAIS níveis as campanhas e Agentes de um cliente podem escolher (em vez de apenas limitar o Max), use `ai-tiers`:

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/ai-tiers" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "allowed_ai_tiers": ["standard", "economy"] }'
```

`allowed_ai_tiers` é uma matriz extraída de `standard`, `economy`, `max`, `mini` — ela SUBSTITUI a lista de permissões do cliente. Envie `null` (ou `[]`) para limpar a restrição e permitir que eles escolham qualquer nível. Isso é importante porque uma subconta que escolhe seu próprio nível de IA gasta do **seu** pool de créditos, então é a alavanca para definir quais modelos um cliente revendedor pode usar para aumentar sua fatura.

**Uma resposta de espera enquanto o cliente está sem créditos**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/zero-credit-reply" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true, "message": "Thanks for your message, we will get back to you shortly." }'
```

Quando o saldo do cliente (ou seu pool) está vazio, a IA não pode responder e o contato não ouve nada. Com `enabled: true`, cada contato que escreve durante a interrupção recebe `message` uma vez (máx. 500 caracteres, enviado como está em todos os canais), e a IA responde a essas conversas de verdade assim que os créditos retornarem. `enabled: false` mantém o texto salvo para mais tarde; `enabled: false` sem `message` remove a configuração. O mesmo interruptor que **Resposta de espera quando sem créditos** no modal de Edição da subconta — veja [Uma resposta de espera enquanto um cliente está sem créditos](sub-accounts.md#a-holding-reply-while-a-client-is-out-of-credits).

**Quanto um cliente paga por ação de IA e a margem de lucro da taxa do WhatsApp**

Duas maneiras de definir sua taxa voltada para o cliente, da mais simples à mais granular:

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/max-rate" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "rate": 0.35 }'
```

`rate` é o preço em créditos que o saldo PRÓPRIO da subconta queima por ação de IA do modelo Max — sua margem de lucro voltada para o cliente sobre o que seu pool realmente paga. `null` limpa a substituição de volta para o preço de lista da plataforma. A taxa deve ser pelo menos o que uma ação Max custa ao seu próprio pool (para que você nunca possa precificar um cliente abaixo do seu custo) e não mais que 10 créditos; uma solicitação fora desse intervalo é rejeitada com o piso calculado na mensagem de erro.

Para precificação por tipo de ação em vez de uma taxa Max fixa, use `action-pricing`:

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/action-pricing" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "actionPricing": {
      "AI_MESSAGE": 0.6,
      "CHAT_SUMMARY": 0.15,
      "wa_carrier_multiplier": null
    }
  }'
```

`actionPricing` é uma MESCLAGEM no mapa existente do cliente — uma chave que você não menciona é deixada como estava, e `null` redefine essa chave de volta ao seu padrão. As chaves reconhecidas:

| Chave | Preços |
|---|---|
| `AI_MESSAGE` | Uma resposta de IA |
| `AI_TOOL_USE` | Uma chamada de ferramenta de IA |
| `EVALUATION_CALL` | Uma passagem de avaliação de chat |
| `INTERRUPTION_HANDLING` | Lidar com uma interrupção no meio da resposta |
| `CONTACT_TAG` | Uma tag de contato atribuída por IA |
| `CHAT_SUMMARY` | Um resumo de chat |
| `wa_carrier_multiplier` | Um multiplicador de margem aplicado a cada taxa de WhatsApp não relacionada à IA que o cliente paga: aluguel mensal de número, taxas de entrega em faixa gerenciada e custos de repasse de modelos Meta/Twilio. |

As taxas por ação devem ser um número maior que 0 e até 10; `wa_carrier_multiplier` deve ser de pelo menos `1` (sem desconto abaixo do custo) e até 10. Enviar uma chave não reconhecida, ou um valor fora do intervalo, rejeita a solicitação INTEIRA e nomeia cada chave ofensiva, para que um erro de digitação nunca salve silenciosamente um preço que não é realmente aplicado.

Se você for um membro do [Champions Circle](https://skool.com/dm-champions), o `insider-rate` repassa sua taxa de 20% de desconto do Max/Lead Finder para um cliente em vez de aplicá-la a toda a agência:

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/insider-rate" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'
```

Ativá-lo exige que sua própria conta de agência possua uma assinatura Circle; desativá-lo nunca exige, portanto, um membro com assinatura expirada sempre pode reverter a configuração de um cliente.

**Bloquear seções do playbook de um cliente**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/locked-bot-fields" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "locked_bot_fields": ["instructions", "rules"] }'
```

`locked_bot_fields` é uma matriz extraída de `instructions`, `goal`, `rules`, `personality`, `conclude_unless` — ela SUBSTITUI a lista bloqueada do cliente. Uma seção bloqueada é rejeitada no lado do servidor se a própria SUB-CONTA tentar alterá-la (diretamente ou por chave de API), enquanto você (via `sub_account_id`) e a visualização de administrador do painel do próprio cliente ainda podem editar qualquer coisa. Envie `null` (ou `[]`) para desbloquear tudo. Útil para clientes "done-for-you" onde você é o dono do playbook e é avaliado pelo resultado.

**Definir as preferências de notificação de um cliente em nome dele**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/notifications" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "notifications": {
      "settings": {
        "credit_alerts": { "enabled": true, "channels": ["email", "in_app"] },
        "new_contacts": { "enabled": false }
      }
    }
  }'
```

`notifications` substitui todo o conjunto de preferências de notificação do cliente (não é uma mesclagem por chave — envie todas as categorias que deseja manter, correspondendo a como a página de Configurações da própria subconta salva). Cada categoria em `settings` aceita `enabled` (booleano) e até três `channels` de `email`, `in_app`, `webhook`. Envie `null` para redefinir para os padrões da plataforma.

Todos os sete endpoints respondem `{ "success": true, "data": { "subAccountUid": "...", ...the field(s) you set... } }` e são registrados em auditoria com o valor antes/depois. Erros comuns: `403` se sua conta não for de Agência/Dev ou a subconta não for sua para gerenciar, `400` se não for uma subconta de agência ou se um valor estiver fora do intervalo.

***

## Pause um cliente que suspendeu a assinatura dele

`POST /v1/subaccounts/{subAccountUid}/pause` · `POST /v1/subaccounts/{subAccountUid}/unpause`

Quando um cliente suspende a assinatura com você, pause a conta dele em vez de excluí-la: tudo o que ele envia para imediatamente — mensagens de saída, transmissões, respostas de IA em todos os canais — e, ao fazer login, ele verá um bloqueio de **Conta pausada** em tela cheia (com sua mensagem opcional) em vez do aplicativo. Nada é excluído ou desconectado: agentes, campanhas, canais conectados, contatos e histórico de chat permanecem exatamente como estão, portanto, retomar a conta coloca o cliente exatamente de onde ele parou — sem necessidade de refazer a configuração.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `message` | Não | Exibido para o cliente na tela de bloqueio. Deixe em branco para usar o texto padrão. |
| `reason` | Não | Nota interna da agência armazenada com a pausa e no log de auditoria — nunca exibida para o cliente. |

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/pause" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Your account is on hold — contact us to reactivate it.", "reason": "Subscription suspended per client email" }'
```

**Resposta**

```json
{
  "success": true,
  "data": {
    "subAccountUid": "SUB_ACCOUNT_UID",
    "paused": true,
    "level": "hard_blocked"
  }
}
```

Quando o cliente retornar, `POST /v1/subaccounts/SUB_ACCOUNT_UID/unpause` (sem corpo) remove o bloqueio — o envio de mensagens e as respostas de IA são retomados imediatamente.

Vale a pena saber:

- **É o mesmo estado da alternância de Bloqueio rígido no painel** ([Bloqueando / Pausando uma Subconta](sub-accounts.md#blocking-pausing-a-sub-account)) — um cliente pausado via API aparece como bloqueado no painel e vice-versa, e retomar a conta remove um bloqueio feito de qualquer um dos lados. O estado atual pode ser lido no campo `agency_block` em `GET /v1/subaccounts` (`level` de `"none"`, `"soft_blocked"` ou `"hard_blocked"`).
- **Ambas as chamadas são idempotentes.** Pausar um cliente já pausado apenas atualiza a mensagem, o motivo e o carimbo de data/hora; retomar um cliente ativo não altera nada.
- **O cliente não é notificado por e-mail automaticamente** — muitas agências usam white-label, portanto, informar o cliente fica a seu critério.
- **Sua própria cobrança do DM Champ permanece inalterada.** Pausar um cliente afeta apenas o seu relacionamento com ele.
- **Assistentes de IA também podem fazer isso**: o [servidor MCP](../integrations/connect-ai-clients.md) expõe esses endpoints como as ferramentas `pause_subaccount` e `unpause_subaccount`.

***

## Conceder ou deduzir créditos diretamente

`POST /v1/subaccounts/credits`

Adiciona ou remove uma quantidade exata de créditos do saldo de uma subconta — o equivalente via API ao ajuste manual de crédito do painel. Esta é uma alteração de saldo única, distinta das configurações recorrentes `monthly_credits`, `roll_over_to_next_month`, `rollover_cap_months` e `rollover_expiry_days` em [`PUT /v1/subaccounts/{subAccountUid}/limits`](#set-an-exact-team-member-limit-for-a-client).

Este é o único endpoint nesta página que identifica a subconta por **e-mail** em vez de `sub_account_id`.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `email` | Sim | O e-mail da subconta, conforme existe em sua agência. |
| `amount` | Sim | Número diferente de zero de créditos. Positivo adiciona, negativo deduz. |
| `description` | Não | Exibido junto ao ajuste no histórico de crédito do cliente. O padrão é uma linha genérica "Ajustado pela agência via API". |

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts/credits" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "client@example.com", "amount": 500, "description": "Q3 bonus credits" }'
```

**Resposta**

```json
{
  "success": true,
  "data": {
    "email": "client@example.com",
    "previous_balance": 1200,
    "adjustment": 500,
    "new_balance": 1700
  }
}
```

Um `amount` negativo que levaria o saldo abaixo de zero é recusado com `400`, informando o saldo disponível e o valor que você tentou deduzir. Se o cliente estiver em seu próprio faturamento Stripe (modo de revenda), um valor adicionado também conta como créditos que ele comprou, portanto, sobrevive à sua próxima redefinição mensal da mesma forma que uma recarga real faria; em um cliente alocado padrão, ele é tratado como parte de sua franquia recorrente. De qualquer forma, são uma adição única, portanto, um limite de rolagem ou uma expiração definida na conta (ou em seu plano) nunca os reduz — apenas a franquia recorrente e os créditos do plano estão sujeitos a isso.

***

## Ler as conversas de uma subconta

`GET /v1/subaccounts/{subAccountUid}/chats` · `GET /v1/subaccounts/{subAccountUid}/chats/{contactId}/messages`

Permite criar uma visualização de monitoramento ou suporte das conversas de um cliente sem precisar fazer login na conta dele. Primeiro, liste os contatos com uma prévia da última mensagem e, em seguida, leia o histórico completo de mensagens de um contato.

**Listar contatos**

```bash
curl "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/chats?apiKey=YOUR_AGENCY_API_KEY&pageSize=25"
```

| Parâmetro de consulta | Obrigatório | Descrição |
|---|---|---|
| `pageSize` | Não | Contatos por página. Padrão 25, máximo 50. |
| `lastActivityAt` | Não | Cursor de paginação — passe o `lastActivityAt` da página anterior para continuar. |
| `searchQuery` | Não | Filtrar por nome ou número de telefone do contato. |

**Resposta**

```json
{
  "success": true,
  "data": {
    "contacts": [
      {
        "contactId": "contact456",
        "firstName": "Jamie",
        "lastName": "Lee",
        "phoneNumber": "+14155551234",
        "email": "jamie@example.com",
        "channel": "whatsapp",
        "lastActivityAt": "2026-08-30T14:22:00.000Z",
        "lastMessage": { "body": "Thanks, that fixed it!", "direction": "inbound", "timestamp": "2026-08-30T14:22:00.000Z" },
        "isBotActive": true,
        "markChatClosed": false
      }
    ],
    "subAccountName": "Client Co",
    "subAccountEmail": "client@example.com",
    "hasMore": true,
    "lastActivityAt": "2026-08-30T14:22:00.000Z"
  }
}
```

Os contatos são ordenados pela atividade mais recente primeiro. Continue paginando com `lastActivityAt` enquanto `hasMore` for `true`.

**Ler mensagens de um contato**

```bash
curl "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/chats/contact456/messages?apiKey=YOUR_AGENCY_API_KEY&pageSize=30"
```

| Parâmetro de consulta | Obrigatório | Descrição |
|---|---|---|
| `pageSize` | Não | Mensagens por página. Padrão 30, máximo 100. |
| `beforeTimestamp` | Não | Cursor de paginação — buscar mensagens mais antigas que este timestamp ISO. |

**Resposta**

```json
{
  "success": true,
  "data": {
    "messages": [
      {
        "messageId": "msg789",
        "body": "Thanks, that fixed it!",
        "direction": "inbound",
        "timestamp": "2026-08-30T14:22:00.000Z",
        "status": "received",
        "channel": "whatsapp",
        "botReply": false,
        "mediaUrl": null,
        "mediaContentType": null,
        "name": "Jamie Lee",
        "role": null
      }
    ],
    "contactInfo": { "firstName": "Jamie", "lastName": "Lee", "phoneNumber": "+14155551234", "channel": "whatsapp" },
    "hasMore": false,
    "oldestTimestamp": "2026-08-30T14:22:00.000Z"
  }
}
```

As mensagens são retornadas da mais recente para a mais antiga; navegue pelo histórico retrocedendo as páginas com `beforeTimestamp`.

***

## Ler o uso de créditos e a integridade das campanhas em toda a sua carteira

`GET /v1/subaccounts/credit-usage` · `GET /v1/subaccounts/campaign-status`

Dois resumos em estilo de painel sobre todas as subcontas que você gerencia, para criar seus próprios relatórios de agência em vez de clicar em cada cliente individualmente.

**Uso de créditos**

```bash
curl "https://api.dmchamp.com/v1/subaccounts/credit-usage?apiKey=YOUR_AGENCY_API_KEY&from=2026-08-01&to=2026-08-31"
```

| Parâmetro de consulta | Obrigatório | Descrição |
|---|---|---|
| `from` / `to` | Sim | Intervalo de datas ISO. |
| `subAccountId` | Não | Omita para um resumo de toda a agência, uma linha por subconta. Inclua para alternar para o modo de detalhes: o resumo daquela subconta mais seus registros de uso brutos e paginados. |
| `limitCount` | Não | Apenas modo de detalhes. Padrão 500, máximo 2000. |
| `startAfterTimestamp` | Não | Apenas modo de detalhes — cursor de paginação. |

```json
{
  "success": true,
  "data": {
    "subAccounts": [
      {
        "subAccountId": "abc123def456",
        "subAccountName": "Client Co",
        "subAccountEmail": "client@example.com",
        "totalCreditsUsed": 842,
        "totalCostUsd": 3.15,
        "byReason": { "AI reply": 620, "Chat summary": 80 },
        "topCampaigns": [{ "campaignName": "Inbound Leads", "creditsUsed": 500 }]
      }
    ],
    "totals": { "totalCreditsUsed": 842, "totalCostUsd": 3.15, "totalRecords": 214 },
    "dateRange": { "from": "2026-08-01", "to": "2026-08-31" },
    "hasMore": false,
    "lastTimestamp": null
  }
}
```

Passe `subAccountId` e a mesma resposta também trará `records`: cobranças individuais com `amount`, `reason`, `campaignName`, `contactName` e `timestamp`. Um cliente que gasta usando sua própria chave BYOK, em vez dos seus créditos, terá os valores de custo/token ocultos (`costsRedacted: true`) — isso é telemetria de custo da plataforma, não algo para exibir a um revendedor.

**Status da campanha**

```bash
curl "https://api.dmchamp.com/v1/subaccounts/campaign-status?apiKey=YOUR_AGENCY_API_KEY&pageSize=20"
```

| Parâmetro de consulta | Obrigatório | Descrição |
|---|---|---|
| `pageSize` | Não | Subcontas por página. Padrão 10, máximo 50. |
| `lastDocumentId` | Não | Cursor de paginação. |
| `searchQuery` | Não | Filtrar por nome ou e-mail da subconta. |

```json
{
  "success": true,
  "data": {
    "totalSubAccounts": 34,
    "subAccountsWithIssues": 3,
    "totalLiveCampaigns": 51,
    "totalPausedCampaigns": 6,
    "subAccounts": [
      {
        "userId": "abc123def456",
        "email": "client@example.com",
        "displayName": "Jamie Lee",
        "businessName": "Client Co",
        "totalCampaigns": 2,
        "liveCampaigns": 1,
        "pausedCampaigns": 1,
        "hasIssues": true,
        "issueDetails": ["1 campaign paused"],
        "lastCampaignActivity": "2026-08-29T09:00:00.000Z"
      }
    ],
    "hasMore": true,
    "lastDocumentId": "abc123def456",
    "pageSize": 20
  }
}
```

`hasIssues` / `issueDetails` sinalizam subcontas que merecem atenção — uma campanha pausada, ou uma sem nenhum canal direcionado a ela, por exemplo. Use isso para criar um painel de verificação de integridade em toda a sua carteira, em vez de abrir cada cliente para notar uma campanha parada.

> Para mensagens de série temporal e atividade de crédito em todos os clientes (uma série pronta para gráficos, em vez de um instantâneo de um momento específico), consulte `GET /analytics/agency-rollup` no guia da API de Analytics.

***

## Entregue uma conta de cliente já configurada

`PUT /v1/snapshots/default` · `POST /v1/snapshots/{snapshotId}/apply`

Um [snapshot](snapshots.md) é um modelo reutilizável: um ou mais agentes de IA, além de sua base de conhecimento, ferramentas e mídia, capturados a partir da sua própria conta. Dois endpoints o colocam no seu fluxo de provisionamento.

**Automático — todo novo cliente já nasce com ele.** Marque um snapshot como padrão uma vez, e todas as contas que você criar a partir de então chegarão com ele instalado. Isso cobre contas criadas através do `POST /v1/subaccounts`, contas que você cria no painel e contas criadas automaticamente quando um cliente paga através do seu link de checkout.

Primeiro, encontre o id do snapshot:

```bash
curl "https://api.dmchamp.com/v1/snapshots" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY"
```

Em seguida, defina-o como padrão:

```bash
curl -X PUT "https://api.dmchamp.com/v1/snapshots/default" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "snapshot_id": "SNAPSHOT_ID" }'
```

Essa é toda a integração. Envie `{"snapshot_id": null}` para desativá-lo novamente. Você pode fazer a mesma coisa pelo painel clicando na estrela na página **Snapshots**.

Para ler o que está marcado como favorito atualmente (digamos, antes de um script de provisionamento decidir se deve definir um), `GET /v1/snapshots/default` retorna `{ "success": true, "data": { "default_snapshot_id": "SNAPSHOT_ID" } }` — `null` quando nada está marcado. `GET /v1/snapshots` (usado para encontrar o id acima) retorna o mesmo `default_snapshot_id` junto com o array `snapshots` completo, portanto, a maioria das integrações precisa apenas dessa chamada. Os campos do objeto de instantâneo completo estão no guia [Snapshots](snapshots.md).

**Sob demanda — instale em uma conta específica.** Útil para integrar um cliente existente ou para fornecer a um cliente um segundo modelo posteriormente.

```bash
curl -X POST "https://api.dmchamp.com/v1/snapshots/SNAPSHOT_ID/apply" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "sub_account_id": "SUB_ACCOUNT_UID" }'
```

Omita o `sub_account_id` e ele será instalado na sua própria conta de agência. Assim como os endpoints `/subaccounts`, estes nomeiam a conta de destino no caminho ou no corpo, em vez de através do parâmetro `sub_account_id` ambiente.

Vale a pena saber antes de construir sobre isso:

- **Agentes instalados começam pausados.** Conecte os canais do cliente primeiro e, em seguida, ative o agente. Isso vale tanto para o caminho automático quanto para o sob demanda.
- **O provisionamento nunca falha por causa de um snapshot.** Se a instalação não puder ser concluída, a conta do cliente ainda será criada e poderá ser usada — ela apenas chegará vazia e você poderá aplicar o snapshot posteriormente.
- **Canais, calendários e conexões OAuth nunca são copiados.** Cada conta conecta os seus próprios. Ferramentas que usam uma chave de API simples continuam funcionando imediatamente.
- **Aplicar duas vezes cria uma segunda cópia.** Nada é sobrescrito.

***

## Crie o próprio modelo via API

`POST /v1/snapshots` · agentes, funções personalizadas e mídia via API

A seção acima distribui um snapshot criado por alguém no painel. A parte de criação também está exposta, portanto, todo o ciclo — montar a configuração principal uma vez, capturá-la e entregá-la a cada cliente — pode ser executado via código.

As peças, na ordem em que um script de provisionamento as utiliza:

1. **Crie suas funções personalizadas.** `POST /v1/custom-functions` cria uma; `GET /v1/custom-functions` lista o que você tem, e `GET`, `PUT` e `DELETE` em `/v1/custom-functions/{customFunctionId}` leem, atualizam e removem uma. `POST /v1/custom-functions/test` testa uma definição antes de você salvá-la.
2. **Crie e configure o agente.** `POST /v1/agents` o cria, `PUT /v1/agents/{agentId}` o atualiza, e `PATCH /v1/agents/{agentId}/active` com `{ "active": false }` o mantém pausado enquanto você trabalha (a mesma chamada com `true` o coloca em operação). `GET /v1/agents` os lista.
3. **Dê habilidades ao agente.** `POST /v1/agents/{agentId}/custom-functions` com `{ "custom_function_id": "..." }` anexa uma função ao agente; o `DELETE /v1/agents/{agentId}/custom-functions/{customFunctionId}` correspondente a remove.
4. **Preencha a biblioteca de mídia.** `POST /v1/agents/{agentId}/media-library` faz o upload de um item (JSON com `base64Data`, `mimeType`, `title`, `description`); `GET` lista os itens do agente, e `PATCH`/`DELETE` em `/{itemId}` atualizam ou removem um.
5. **Capture-o como um snapshot.**

```bash
curl -X POST "https://api.dmchamp.com/v1/snapshots" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Master setup v1",
    "agent_ids": ["AGENT_ID"],
    "include_knowledge": true,
    "include_tools": true,
    "include_media": true
  }'
```

A partir daí, segue-se a seção anterior: marque-o como padrão para que cada novo cliente já nasça com ele, ou aplique-o sob demanda. A manutenção é feita da mesma forma: `PATCH /v1/snapshots/{snapshotId}` com `{ "name": "..." }` renomeia um, `DELETE /v1/snapshots/{snapshotId}` exclui um (e remove a marcação de padrão, caso estivesse marcado), e `GET /v1/snapshots/apply-targets` lista todas as contas nas quais você pode realizar a instalação.

Os endpoints de agente, função personalizada e mídia aceitam `sub_account_id`, portanto, as mesmas chamadas também podem manter um agente diretamente dentro da conta de um cliente. As chamadas de snapshot sempre atuam na sua conta de agência — o modelo permanece com você. Os esquemas completos de solicitação e resposta para todos eles estão na [Referência da API](../api/reference.md).

***

## Gerencie seus níveis de preços via API

`GET /v1/agency/pricing-tiers` · `POST /v1/agency/pricing-tiers` · `PATCH /v1/agency/pricing-tiers/{tierIndex}` · `DELETE /v1/agency/pricing-tiers/{tierIndex}`

Os planos que você vende em **Modo SaaS → Níveis de Preços** podem ser lidos e alterados via código, para que seu próprio painel administrativo ou script de provisionamento possa adicionar um plano, ajustar um preço ou fornecer um link de checkout sem que ninguém precise abrir o painel. Autentique-se com sua chave de API de agência como em qualquer outra chamada nesta página; esses endpoints são de nível de agência, portanto, não utilizam `sub_account_id`. Toda gravação executa a mesma validação e a mesma sincronização de produto e preço do Stripe que o salvamento no painel, portanto, um plano criado aqui é indistinguível de um que você configurou manualmente.

### Liste seus níveis

```bash
curl "https://api.dmchamp.com/v1/agency/pricing-tiers" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "data": {
    "tiers": [
      {
        "tierIndex": 0,
        "credits": 1000,
        "price_cents": 2900,
        "currency": "usd",
        "label": "Starter",
        "billing_interval": "month",
        "trial_days": 14,
        "trial_credits": 250,
        "trial_card_required": false,
        "trial_hard_expiry": true,
        "stripe_price_id": "price_1PxAbC…",
        "stripe_product_id": "prod_QxAbC…",
        "checkout_url": "https://app.yourdomain.com/v1/checkout?id=YOUR_AGENCY_UID&tierIndex=0"
      }
    ],
    "count": 1,
    "max_tiers": 20
  }
}
```

Cada nível retorna com seu **`tierIndex`** — sua posição na sua lista de Planos, que é como as outras três chamadas o endereçam — e um **`checkout_url`** pronto para compartilhar, o mesmo link que a aba **Pagamentos** fornece, já apontando para o [domínio white label](white-labeling.md) no qual o plano é vendido.

### Adicione um nível

O corpo é um objeto de nível; ele é anexado ao final da sua lista.

```bash
curl -X POST "https://api.dmchamp.com/v1/agency/pricing-tiers" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Starter",
    "credits": 1000,
    "price_cents": 2900,
    "currency": "usd",
    "billing_interval": "month",
    "trial_days": 14,
    "trial_credits": 250,
    "trial_card_required": false,
    "trial_hard_expiry": true,
    "features": ["channels_3", "channel_whatsapp_web", "webhooks"]
  }'
```

A resposta traz o nível criado, incluindo o `tierIndex` no qual ele foi inserido e seu `checkout_url`.

### Edite um nível

Envie apenas os campos que deseja alterar; todo o restante do plano permanece como estava.

```bash
curl -X PATCH "https://api.dmchamp.com/v1/agency/pricing-tiers/0" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "price_cents": 3900, "trial_hard_expiry": true }'
```

Um campo que a API não reconhece é recusado em vez de ignorado, e o erro o nomeia — assim, um erro de digitação nunca pode gravar silenciosamente uma configuração que parece ativa, mas não faz nada. Alterar o preço, os créditos, a moeda ou a periodicidade de cobrança cria um novo preço no seu Stripe; clientes que já assinaram permanecem no que assinaram.

### Exclua um nível

```bash
curl -X DELETE "https://api.dmchamp.com/v1/agency/pricing-tiers/2" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY"
```

A mesma regra do painel: um plano que ainda possui assinantes ativos não pode ser excluído. A solicitação retorna recusada, informando quantos assinantes estão nele — cancele ou migre-os primeiro. Uma exclusão bem-sucedida responde com seus níveis restantes, já renumerados.

### Os campos em um nível

| Campo | O que é |
|---|---|
| `label` / `description` | O nome do plano e a linha opcional exibida na sua página de checkout. |
| `credits` | Créditos que o cliente recebe **por mês** em um plano mensal ou anual, e **por período de faturamento** em um semanal. |
| `price_cents` | Preço por intervalo de faturamento, na menor unidade monetária (`2900` = $29.00). Em um plano anual, este é o preço do ano inteiro. |
| `currency` | Código ISO em minúsculas — `usd`, `eur`, `gbp` e assim por diante. |
| `billing_interval` / `billing_interval_count` | `month` (o padrão), `year`, ou `week` com uma contagem de 1–52 para "a cada N semanas". |
| `trial_days` | Duração do teste gratuito, de 0 a 90. `0` (ou omiti-lo) significa sem teste. |
| `trial_credits` | Créditos com os quais o cliente inicia o teste. O padrão é `credits` do plano. |
| `trial_card_required` | `false` permite que o cliente inicie o teste sem inserir um cartão. O padrão é `true`. |
| `trial_hard_expiry` | `true` devolve créditos de teste não utilizados ao seu pool e bloqueia a conta do cliente quando um teste termina sem um upgrade. O padrão é `false` — veja [Expiração rígida após o teste](agency-accounts.md#step-3--set-up-pricing-tiers). |
| `rollover_cap_months` | Meses de franquia que clientes neste plano podem transportar entre renovações — um número de 0 a 120, frações permitidas. `0` não transporta nada; `null` (o padrão) significa sem limite. Veja [Limitando o que é transferido](sub-accounts.md#capping-what-rolls-over). |
| `rollover_expiry_days` | Dias após os quais créditos não utilizados são descartados na próxima renovação — um número inteiro de 1 a 3650. `null` (o padrão) significa que eles nunca expiram. |
| `features` / `feature_settings` | O que os clientes neste plano recebem — os mesmos IDs de recurso que [Escolha quais tipos de canal um cliente pode conectar](#choose-which-channel-types-a-client-can-connect). |
| `team_seats_limit` | Assentos de equipe que o plano concede: um número exato, `0` para nenhum, `-1` para ilimitado. |
| `white_label_config` | Em qual de seus [domínios white label](white-labeling.md#up-to-three-white-labels) o plano é vendido. |

Os campos de teste só fazem sentido em um plano que possui um teste: salve um nível com `trial_days: 0` e eles serão descartados. Os IDs de produto e preço do Stripe do plano são gerenciados para você e não podem ser definidos manualmente.

Três coisas para acertar:

- **Os índices de nível são posições, não IDs permanentes.** Excluir um plano desloca todos os planos subsequentes para baixo, portanto, busque a lista novamente após qualquer alteração — e copie novamente os links de checkout que você publicou, exatamente como faria após excluir um plano no painel.
- **O Modo SaaS deve ser configurado primeiro.** Esses endpoints precisam de uma conta de agência com white labeling e uma chave Stripe já salva; sem isso, não há conta Stripe para o produto e preço do plano residirem.
- **Vinte planos é o limite**, o mesmo que no painel. O campo `max_tiers` na resposta da lista informa o limite atual.

Os esquemas completos de solicitação e resposta estão na [Referência da API](../api/reference.md), em **Agência**.

***

## Defina seu preço por crédito via API

`GET /v1/agency/credit-price` · `PATCH /v1/agency/credit-price`

O preço que os clientes pagam por recargas avulsas (**Modo SaaS → Preço por Crédito**) também pode ser lido e alterado via código. Isso foi criado para casos em que o preço precisa ser ajustado automaticamente: uma agência que vende créditos em uma moeda, mas cobra em outra, pode permitir que uma tarefa agendada revise o preço conforme a taxa de câmbio muda, em vez de alguém editá-lo manualmente toda semana.

### Ler o preço atual

```bash
curl "https://api.dmchamp.com/v1/agency/credit-price" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "data": {
    "price_per_credit_cents": 125,
    "price_per_credit_currency": "brl",
    "note": "USD 0.25 per credit at our reference rate",
    "minimum_cents": 60
  }
}
```

`minimum_cents` é o preço mais baixo que a plataforma permite nessa moeda, portanto, uma tarefa pode verificar um novo preço antes de enviá-lo. Todos os três valores são `null` até que um preço tenha sido definido.

### Alterá-lo

```bash
curl -X PATCH "https://api.dmchamp.com/v1/agency/credit-price" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "price_per_credit_cents": 130, "currency": "brl" }'
```

Envie apenas o que deseja alterar. `price_per_credit_cents` é o preço na menor unidade monetária (`130` = R$1,30); `currency` é um código ISO em letras minúsculas; `note` é uma linha opcional de até 200 caracteres exibida aos clientes diretamente abaixo do preço por crédito na página de Faturamento — útil para um preço de referência em outra moeda, como *"USD 0,25 por crédito à nossa taxa de referência"*. Envie `"note": ""` para removê-la. A resposta tem o mesmo formato da leitura acima, para que uma tarefa possa comparar e pular a gravação quando nada tiver mudado.

As mesmas regras do painel se aplicam: o preço não pode ficar abaixo do mínimo da plataforma para aquela moeda, e a conta precisa de white labeling. Ao contrário dos endpoints de níveis de preço, nenhuma chave do Stripe é necessária para ler ou alterar este valor.

### Dê a uma tarefa uma chave que não possa fazer mais nada

Colocar sua chave de agência completa em um agendador concede mais acesso do que uma atualização de preço necessita. Em vez disso, crie uma **chave com escopo** limitada à área de **Preço de Crédito da Agência**: essa chave pode ler e alterar o preço por crédito e nada mais — ela não pode acessar subcontas, planos, créditos ou sua conexão com o Stripe.

```bash
curl -X POST "https://api.dmchamp.com/v1/api-keys" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "label": "FX price updater", "scopes": { "read_only": false, "tags": ["Agency Credit Price"] } }'
```

A resposta contém a nova chave em `api_key` **apenas uma vez** — ela nunca é exibida novamente, portanto, armazene-a imediatamente. Defina `"read_only": true` para uma chave que precise apenas ler o preço e adicione `"expires_at"` (uma data ISO) se desejar que ela pare de funcionar automaticamente. Apenas a chave do proprietário da conta pode criar chaves com escopo; liste ou revogue-as com `GET /v1/api-keys` e `DELETE /v1/api-keys/{id}`.

***

## Permita que uma subconta leia seus preços

`GET /v1/subaccounts/agency-pricing`

Todos os outros endpoints nesta página são chamados com a **sua chave de agência**, opcionalmente direcionando um cliente via `sub_account_id`. Este é o oposto: ele é chamado com a **própria chave de API da subconta**, sem `sub_account_id`, para que a página de recarga do próprio cliente (ou uma integração que você crie para ele) possa exibir o que você cobra dele sem nunca ver sua conta de agência.

```bash
curl "https://api.dmchamp.com/v1/subaccounts/agency-pricing" \
  -H "X-API-Key: THE_SUB_ACCOUNTS_OWN_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "data": {
    "tiers": [{ "credits": 1000, "price_cents": 2900, "currency": "usd" }],
    "price_per_credit_cents": 125,
    "price_per_credit_currency": "brl",
    "price_per_credit_note": "USD 0.25 per credit at our reference rate",
    "agency_display_name": "Client Co's Growth Partner"
  }
}
```

Isso reflete exatamente o que [`GET /v1/agency/pricing-tiers`](#list-your-tiers) e [`GET /v1/agency/credit-price`](#read-the-current-price) retornam para você como agência, menos qualquer coisa que o cliente não precise ver (ids do Stripe, `max_tiers`, etc.). Funciona apenas para uma conta que seja, de fato, uma subconta com uma agência vinculada — chamá-lo de sua própria conta de agência retorna um erro de permissão.

***

## Pontos a considerar

- **Use a chave da sua agência.** Autentique cada chamada com a chave de API da conta da sua agência — não a da subconta. O parâmetro `sub_account_id` é o que redireciona a ação.
- **Os créditos vêm da subconta.** Compras e cobranças recorrentes são debitadas do saldo de créditos da subconta alvo, não do seu.
- **Um `404` significa "não é sua subconta".** Verifique novamente o id e se a conta é uma que você gerencia.
- **O parâmetro é opcional onde quer que seja aceito.** Omita-o e o mesmo endpoint atuará na conta da sua agência, para que você possa reutilizar uma única integração para ambos.

***

## Relacionado

- [Acesso à API](../integrations/api-access.md) — autenticação, URL base, erros, limites de taxa.
- [Subcontas](sub-accounts.md) — liste e gerencie as contas que você pode direcionar.
- [Recarga Automática de Subconta](sub-account-auto-recharge.md) — conceda créditos a uma subconta via webhook + API.
- [API de Campanhas](../api/campaigns.md) — crie, atualize e copie campanhas, incluindo a referência completa de campos.
- [API de Conexão de Canal](../api/channels.md) — conecte os canais de um cliente e direcione-os para uma campanha.
- Guia da API de Analytics (na seção de API) — o rollup de subcontas da agência e todos os outros endpoints de relatório.
- [Snapshots](snapshots.md) — o que um snapshot captura e como criar um no painel.
