# Diligens

> Coloque no seu ERP, checkout, gateway ou e-commerce um assistente de IA com a sua marca, que responde com os números de cada cliente, executa ações no seu sistema e manda dicas sobre as vendas.

A Diligens é um assistente de IA white label para plataformas que processam pedidos: ERPs e sistemas de gestão, checkouts, gateways de pagamento e plataformas de e-commerce, além de fintechs e bancos. A empresa dona do sistema coloca o chat dentro do próprio produto, com a própria marca, e cada cliente dela pergunta em português ("Quanto vendi em setembro?", "Por que meu caixa caiu?", "Qual produto me dá mais margem?") e recebe a resposta calculada com os próprios pedidos e contas. Feita para o mercado brasileiro: Pix, boleto, cartão, NF-e, contas a pagar e a receber.

O que o assistente faz:

- Responde com os números de cada usuário final: vendas, canais, formas de pagamento e a conversão de cada uma (Pix e boleto expirados, cartão recusado, estornos), taxas do gateway ou do marketplace, margem por produto e fluxo de caixa, explicando o que mudou.
- Executa ações no sistema do cliente (por exemplo, criar um cupom) a partir da documentação da API dele (OpenAPI, Postman, Markdown ou texto livre), que vira um catálogo de ações revisável no painel. Toda ação que altera dados mostra um resumo e espera a confirmação do usuário.
- Manda dicas curtas sobre as vendas ao longo do dia.

Como a integração funciona:

1. Depois da primeira recarga de créditos paga, a empresa gera uma API key no painel da Diligens (uma por conta) e a guarda no backend.
2. O backend dela chama `POST /chat/token` com o `external_id` do usuário logado e recebe um token de chat que vale 5 minutos.
3. O SDK JavaScript do chat, no front, usa esse token para conversar com a IA e executa as ações no navegador, com a sessão que o usuário já tem no sistema.
4. O backend envia os pedidos com `PUT /orders`, o catálogo de produtos com `PUT /products` e as contas a receber e a pagar com `PUT /financial-entries` (lotes de até 300 pedidos, 500 produtos e 200 contas, idempotente): o histórico uma vez e depois cada mudança.

Segurança e privacidade: a IA nunca chama o sistema do cliente diretamente e a Diligens não guarda senha nem token dele. Documento, e-mail e telefone são gravados encriptados. O histórico das conversas é apagado depois de 7 dias, e o painel mostra métricas de uso sem o texto das conversas. O tratamento de dados segue a LGPD.

Preço: uso pré-pago em reais. A empresa recarrega créditos no painel (de R$ 50 a R$ 5.000 por recarga, por Pix, boleto ou cartão) e cada resposta da IA desconta do saldo. Conta nova começa sem saldo: a API e o chat funcionam depois da primeira recarga paga.

A documentação técnica é pública, em https://docs.diligensai.com.br/: guias de integração e a referência de cada rota da API, com exemplos em cURL, Node.js, Python e PHP.

A seguir, todas as páginas da documentação, na ordem da navegação: primeiro os guias e depois a referência da API.

---

# Introdução

> Conheça a Diligens, o que o assistente faz dentro do seu produto e como os componentes da integração se relacionam.

Página: https://docs.diligensai.com.br/guias/introducao

A Diligens integra ao seu produto um **assistente de IA com a sua marca**. O seu usuário faz perguntas como faria a um analista, por exemplo "Quanto vendi em setembro?" ou "Por que meu caixa caiu?", e recebe a resposta com os próprios números, sem sair da sua plataforma.

A Diligens foi feita para quem processa pedidos, como ERPs, checkouts, gateways de pagamento e plataformas de e-commerce, além de fintechs e bancos. Ela também fala a língua do mercado brasileiro, com Pix, boleto, NF-e e contas a pagar e a receber.

- **Entende o negócio de cada usuário**

  Com os pedidos e as contas que você envia, a IA analisa vendas, canais, formas de pagamento, margem e caixa, e explica o que mudou.

- **Executa ações, além de responder**

  Envie a documentação da sua API em [Integrações](https://app.diligensai.com.br/api-docs), no painel. A partir dela, a IA passa a consultar dados e a executar ações, como criar um cupom.

- **Ações com confirmação**

  Toda ação que altera ou apaga dados (PUT, PATCH e DELETE) exibe um resumo e aguarda o "Confirmar" do usuário antes de ser executada. Nas ações POST, você define em Integrações se a confirmação é exigida.

- **Suas credenciais ficam com você**

  A IA nunca chama o seu sistema diretamente, e a Diligens não armazena senhas nem tokens dele. O documento, o e-mail e o telefone do comprador, assim como o documento da contraparte, são gravados de forma encriptada.

## O que você vai integrar

A integração é composta por três peças, cada uma com a sua função:

| Peça | Onde é executada | Credencial | Finalidade |
|---|---|---|---|
| Rotas de integração | Seu backend | API key (`X-API-Key`) | Gerar o token do chat e enviar pedidos e contas |
| SDK do chat | Navegador | Token de chat, obtido pelo `getToken` | Exibir o chat, executar as ações e mostrar os insights |
| Painel | Navegador | Login da sua conta | API key, créditos, nome e tom do assistente e catálogo de ações |

A API key permanece apenas no servidor. O SDK recebe do seu backend um token válido por 5 minutos, que dá acesso somente ao chat do usuário logado. Consulte os detalhes em [Autenticação](https://docs.diligensai.com.br/guias/autenticacao.md).

## Por onde começar

- [Quickstart](https://docs.diligensai.com.br/guias/quickstart.md)

  Configure a integração do zero até o chat responder com os números do seu usuário.

- [Autenticação](https://docs.diligensai.com.br/guias/autenticacao.md)

  Conheça a API key do backend e o token de chat usado pelo SDK.

- [SDK do chat](https://docs.diligensai.com.br/guias/sdk.md)

  Adicione o chat ao seu site com uma tag script e o `Diligens.init`.

- [Referência da API](https://docs.diligensai.com.br/referencia/introducao.md)

  Consulte cada rota de integração, com os campos, as respostas e exemplos em cURL, Node.js, Python e PHP.

---

# Quickstart

> Integre o chat do zero até as respostas com os números do seu usuário: API key, rota de token, SDK e envio dos pedidos.

Página: https://docs.diligensai.com.br/guias/quickstart

A integração consiste em algumas rotas no seu backend e no SDK do chat, adicionado ao seu frontend.

## Em resumo

1. **Recarregue e gere a API key** (Painel)

   A API funciona depois da primeira recarga de créditos paga.

2. **Crie a rota de token** (Seu backend)

   Troque a API key por um token de chat válido por 5 minutos, para o usuário da sessão.

3. **Adicione o SDK** (Seu frontend)

   Inclua o script do chat e chame o `Diligens.init`. O SDK cuida da conversa com a IA e executa as ações com a sessão do usuário.

4. **Envie os pedidos e as contas** (Seu backend)

   Envie o histórico completo uma única vez e, depois, cada alteração.

## 1. Recarregue e gere a API key

Crie a conta da sua empresa ([Criar conta](https://app.diligensai.com.br/register)) e faça a primeira recarga de créditos em [Cobrança](https://app.diligensai.com.br/billing). O uso é pré-pago, toda conta nova começa sem saldo, e a API inteira funciona depois da primeira recarga paga: antes dela, a API key não é gerada. Com Pix, o pagamento é confirmado em segundos. Saiba mais em [Créditos](https://docs.diligensai.com.br/guias/creditos.md).

Com a recarga paga, gere a API key em [Configurações › Chaves de API](https://app.diligensai.com.br/settings/api-keys). Cada conta tem uma única chave. Guarde-a em uma variável de ambiente do seu backend, como `DILIGENS_API_KEY`.

## 2. Crie a rota de token

O navegador nunca deve ter acesso à API key. Por isso, o seu backend expõe uma rota que gera o token do chat para o usuário **da sessão**, chamando o `POST /chat/token` da Diligens. Os detalhes da requisição e da resposta estão em [Criar token de chat](https://docs.diligensai.com.br/referencia/token-chat.md).

**Seu backend**

Node.js:

```javascript
// Express. DILIGENS_API_KEY fica numa variável de ambiente.
app.get('/minha-api/chat-token', requireLogin, async (req, res) => {
  const response = await fetch('https://api.diligensai.com.br/api/v1/chat/token', {
    method: 'POST',
    headers: { 'X-API-Key': process.env.DILIGENS_API_KEY, 'Content-Type': 'application/json' },
    // O external_id vem da SUA sessão, nunca do navegador.
    body: JSON.stringify({ external_id: String(req.user.id) }),
  });
  if (!response.ok) return res.status(502).end();
  const { token, expires_at } = await response.json();
  res.json({ token, expires_at });
});
```

Python:

```python
# Flask. DILIGENS_API_KEY fica numa variável de ambiente.
@app.get("/minha-api/chat-token")
@login_required
def chat_token():
    response = requests.post(
        "https://api.diligensai.com.br/api/v1/chat/token",
        headers={"X-API-Key": os.environ["DILIGENS_API_KEY"]},
        # O external_id vem da SUA sessão, nunca do navegador.
        json={"external_id": str(current_user.id)},
        timeout=10,
    )
    if not response.ok:
        return "", 502
    data = response.json()
    return {"token": data["token"], "expires_at": data["expires_at"]}
```

PHP:

```php
<?php
// Laravel. DILIGENS_API_KEY fica no .env.
Route::middleware('auth')->get('/minha-api/chat-token', function (Request $request) {
    $response = Http::withHeaders(['X-API-Key' => env('DILIGENS_API_KEY')])
        // O external_id vem da SUA sessão, nunca do navegador.
        ->post('https://api.diligensai.com.br/api/v1/chat/token', ['external_id' => (string) $request->user()->id]);

    abort_unless($response->successful(), 502);

    return ['token' => $response['token'], 'expires_at' => $response['expires_at']];
});
```

> **Obtenha o external_id da sua sessão**
>
> Se o valor vier de um parâmetro enviado pelo navegador, um usuário poderá acessar o chat e os dados de outro.

## 3. Adicione o SDK

Inclua o script do SDK nas páginas autenticadas do seu sistema e informe, no `getToken`, como obter o token da rota criada no passo anterior:

```html
<script src="https://cdn.diligensai.com.br/v1/diligens.js"></script>
<script>
  Diligens.init({
    getToken: async () => {
      const response = await fetch('/minha-api/chat-token');
      if (!response.ok) throw new Error('Falha ao gerar o token do chat');
      return response.json(); // { token, expires_at }
    },
  });
</script>
```

O botão do chat aparece no canto inferior direito da tela (o canto e a distância às bordas são configuráveis). O SDK renova o token, carrega o histórico, exibe as respostas e os insights e executa as ações, sem nenhum código adicional. As opções, o modo dentro de uma página e a personalização das cores estão em [SDK do chat](https://docs.diligensai.com.br/guias/sdk.md).

Para que a IA consulte dados e execute ações no seu sistema, envie a documentação da sua API em [Integrações](https://app.diligensai.com.br/api-docs), no painel. A documentação é convertida em um catálogo de ações, que você revisa antes de elas ficarem disponíveis no chat (consulte [Ações no seu sistema](https://docs.diligensai.com.br/guias/sdk.md#acoes-no-seu-sistema)).

## 4. Envie os pedidos e as contas

São esses dados que permitem que as respostas tragam os números reais de cada usuário. Envie o histórico completo uma única vez, em lotes de até 300, e, depois, cada alteração pelo seu webhook. Este é o menor pedido válido:

```bash
curl -X PUT "https://api.diligensai.com.br/api/v1/orders" \
  -H "X-API-Key: $DILIGENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "loja-123",
    "orders": [{
      "id": "PED-1002",
      "status": "pending",
      "total_cents": 1500,
      "placed_at": "2026-09-22T09:10:00-03:00",
      "updated_at": "2026-09-22T09:10:00-03:00",
      "items": [{ "name": "Café 500g", "quantity": 1, "unit_price_cents": 1500 }]
    }]
  }'
```

Os campos e as regras estão em [Enviar pedidos](https://docs.diligensai.com.br/referencia/pedidos.md) e [Enviar contas](https://docs.diligensai.com.br/referencia/contas.md).

## Próximos passos

- [SDK do chat](https://docs.diligensai.com.br/guias/sdk.md)

  As opções do `Diligens.init`, o modo dentro de uma página e a personalização.

- [Ações no seu sistema](https://docs.diligensai.com.br/guias/sdk.md#acoes-no-seu-sistema)

  Envie a documentação da sua API no painel e revise as ações.

- [Enviar contas](https://docs.diligensai.com.br/referencia/contas.md)

  Envie as contas a receber e a pagar para que o chat responda sobre o caixa.

- [Privacidade (LGPD)](https://docs.diligensai.com.br/guias/lgpd.md)

  Saiba o que fica encriptado e como apagar um usuário.

---

# Integrar com IA

> Faça a integração com o Claude Code, o Cursor ou o Codex: a documentação em Markdown para agentes e um prompt pronto para começar.

Página: https://docs.diligensai.com.br/guias/integrar-com-ia

Assistentes de programação, como o Claude Code, o Cursor, o Codex e o GitHub Copilot, conseguem fazer boa parte da integração sozinhos, desde que leiam a documentação correta. Esta página reúne o que a Diligens oferece para esses agentes e um prompt pronto para começar.

## Documentação para agentes de IA

- [llms-full.txt](https://docs.diligensai.com.br/llms-full.txt): todas as páginas desta documentação em Markdown, em um único arquivo. É o melhor ponto de partida para um agente, porque ele lê tudo de uma vez.
- [llms.txt](https://docs.diligensai.com.br/llms.txt): o resumo do produto e o índice das páginas, no formato do llmstxt.org.
- **Markdown de cada página**: acrescente `.md` ao endereço de qualquer página (por exemplo, `/referencia/pedidos.md`). O botão **Copiar página**, no topo de cada página, copia esse Markdown e também abre a página no ChatGPT ou no Claude.

Todas as páginas também são servidas em HTML completo, sem depender de JavaScript, para os agentes que leem a página diretamente.

## Prompt para começar

Abra o assistente na raiz do seu projeto e cole o prompt abaixo. Ele indica ao agente onde está a documentação e a ordem da integração, a mesma do [Quickstart](https://docs.diligensai.com.br/guias/quickstart.md).

Prompt:

```text
Integre a Diligens (assistente de IA com a marca da empresa, dentro do sistema) neste projeto.

1. Antes de escrever código, leia a documentação completa em https://docs.diligensai.com.br/llms-full.txt. Siga a documentação: não invente rotas, campos nem códigos de erro.
2. Analise o projeto: a linguagem e o framework do backend, como a sessão do usuário autenticado é obtida, onde ficam as páginas autenticadas do frontend e onde os pedidos, os produtos e as contas a receber e a pagar são criados e alterados.
3. Backend: leia a API key da variável de ambiente DILIGENS_API_KEY. Ela nunca vai para o navegador nem para o repositório.
4. Backend: crie uma rota, acessível apenas ao usuário autenticado, que chame POST /chat/token com o external_id do usuário da sessão (nunca de um parâmetro enviado pelo navegador) e devolva { token, expires_at }.
5. Frontend: inclua o script do SDK (https://cdn.diligensai.com.br/v1/diligens.js) nas páginas autenticadas e chame Diligens.init com um getToken que busque a rota do passo 4.
6. Backend: envie os pedidos com PUT /orders, em lotes de até 300, o catálogo de produtos com PUT /products, em lotes de até 500, e as contas com PUT /financial-entries, em lotes de até 200. Crie um comando que envie o histórico completo uma única vez, com as páginas em sequência, e envie cada alteração no ponto em que o pedido, o produto ou a conta é criado ou alterado, com o updated_at da última alteração. Produto excluído vai com status "archived".
7. Backend: quando um usuário for excluído do sistema, chame DELETE /external-users/:external_id.
8. Trate os erros pelo campo code e, no status 429, aguarde os segundos do header Retry-After antes de repetir a chamada. O PUT /orders, o PUT /products e o PUT /financial-entries respondem 202 com batch_id e gravam em segundo plano: registre o batch_id no log, não reenvie um lote que recebeu 202 e reenvie o que recebeu 500 ou 503.
9. Ao terminar, liste os arquivos criados ou alterados, as variáveis de ambiente necessárias e o que eu preciso fazer no painel da Diligens.
```

> **Revise o código antes de publicar**
>
> O agente pode errar. Confira, principalmente, se o `external_id` do `POST /chat/token` vem da sessão do seu backend e se a API key não aparece no frontend nem no repositório.

## O que continua com você

Algumas etapas são feitas no painel da Diligens e não podem ser delegadas ao agente:

- Fazer a primeira recarga de créditos, em [Cobrança](https://app.diligensai.com.br/billing). A API inteira funciona depois da primeira recarga paga: antes dela, a API key não é gerada.
- Gerar a API key, em [Configurações › Chaves de API](https://app.diligensai.com.br/settings/api-keys), e guardá-la na variável de ambiente `DILIGENS_API_KEY` do servidor.
- Para que a IA consulte dados e execute ações no seu sistema, enviar a documentação da sua API em [Integrações](https://app.diligensai.com.br/api-docs) e revisar o catálogo de ações gerado.

---

# Autenticação

> A API key do seu backend e o token de chat usado pelo SDK: onde cada um fica e quanto tempo vale.

Página: https://docs.diligensai.com.br/guias/autenticacao

A API tem duas credenciais, uma para cada lado da integração. Todas as rotas ficam em `https://api.diligensai.com.br/api/v1`.

| Credencial | Onde fica | Header | Validade |
|---|---|---|---|
| API key | Só no seu backend | `X-API-Key` | Até ser regenerada |
| Token de chat | No navegador, gerenciado pelo SDK | Enviado pelo SDK | 5 minutos |

## API key

O seu backend chama as rotas de integração (token do chat, pedidos, contas e exclusão de usuário) de servidor para servidor, com a API key no header `X-API-Key`:

```http
X-API-Key: csk_...
Content-Type: application/json
```

- A API key é gerada no painel, em [Configurações › Chaves de API](https://app.diligensai.com.br/settings/api-keys), **depois da primeira recarga de créditos paga**. Cada conta tem **uma única chave**.
- Ao regenerar a chave, a anterior é invalidada imediatamente. Atualize a variável de ambiente do seu backend antes de regenerar, para que o chat não fique fora do ar.
- Quando a chave está ausente, é inválida ou foi regenerada, a API responde com o status 401 `APIKEY_002`.
- Enquanto a conta não tiver nenhuma recarga paga (ou se a única tiver sido estornada), toda chamada com a chave responde com o status 402 `APIKEY_003` (consulte [Créditos](https://docs.diligensai.com.br/guias/creditos.md)).
- O nome, a empresa, o tom e as instruções extras do assistente não passam pela API. Configure-os no painel, em [Assistente](https://app.diligensai.com.br/assistant). A configuração vale para todos os seus usuários.

> **A API key nunca vai para o navegador**
>
> A regra também vale para aplicativos mobile. O SDK do chat usa um token de chat, que dura 5 minutos e dá acesso somente ao chat daquele usuário.

## Token de chat

O SDK do chat não usa a API key. Ele se comunica com a Diligens por meio de um token de curta duração, que o seu backend gera para o usuário logado, chamando o [POST /chat/token](https://docs.diligensai.com.br/referencia/token-chat.md). O SDK obtém esse token pelo `getToken` informado no `Diligens.init` (consulte [SDK do chat](https://docs.diligensai.com.br/guias/sdk.md#1-crie-a-rota-de-token-no-seu-backend)).

- O token é válido por **5 minutos** e pode ser reutilizado até expirar. Cada chamada ao `POST /chat/token` gera um novo token, e os anteriores continuam válidos até expirarem.
- O token dá acesso somente ao chat do `external_id` para o qual foi gerado (consulte [Usuário final](https://docs.diligensai.com.br/guias/usuario-final.md)). Ele não serve para as rotas de integração nem para o painel.
- O SDK mantém o token em memória, solicita um novo antes que ele expire e repete a chamada quando a Diligens o recusa. Não é necessário implementar a renovação.

---

# SDK do chat

> Adicione o chat ao seu site com uma tag script e o Diligens.init: rota de token, opções, ações, personalização e modo na página.

Página: https://docs.diligensai.com.br/guias/sdk

O SDK da Diligens coloca o chat do assistente no seu site com uma tag `<script>` e uma chamada ao `Diligens.init`. Ele cuida de toda a comunicação com a Diligens: autenticação, histórico, mensagens, respostas em streaming, execução das ações e insights. A sua parte se resume a uma rota no seu backend, que gera o token do chat, e à inicialização do SDK no frontend.

## Como funciona

Fluxo:

```text
Navegador (SDK)                 Seu backend                    Diligens
       │                             │                             │
       │ 1. getToken()               │                             │
       ├────────────────────────────>│                             │
       │                             │ POST /chat/token (API key)  │
       │                             ├────────────────────────────>│
       │                             │<────────── token ───────────┤
       │<────────── token ───────────┤                             │
       │                             │                             │
       │ 2. Mensagens, histórico e insights, com o token           │
       ├──────────────────────────────────────────────────────────>│
       │<─────────────────────── respostas ────────────────────────┤
       │                             │                             │
       │ 3. Ação, com a sessão do usuário                          │
       ├────────────────────────────>│                             │
       │<──────── resultado ─────────┤                             │
       │ Resultado da ação           │                             │
       ├──────────────────────────────────────────────────────────>│
```

1. Quando precisa de um token, o SDK chama a função `getToken`, definida por você. Ela solicita o token a uma rota do seu backend.
2. O seu backend chama o `POST /chat/token` com a API key e devolve o token ao navegador. A API key nunca sai do servidor.
3. Com o token, o SDK se comunica diretamente com a Diligens. Quando a IA precisa consultar ou alterar dados do seu sistema, o SDK executa a requisição no navegador, com a sessão que o usuário já tem no seu site, e devolve o resultado à IA.

## 1. Crie a rota de token no seu backend

Crie uma rota, acessível apenas ao usuário autenticado, que chame o `POST /chat/token` da Diligens com o `external_id` do usuário da sessão e devolva o token ao navegador. A API key é gerada no painel, em [Configurações › Chaves de API](https://app.diligensai.com.br/settings/api-keys) (consulte [Autenticação](https://docs.diligensai.com.br/guias/autenticacao.md)).

**Seu backend**

Node.js:

```javascript
// Express. DILIGENS_API_KEY fica numa variável de ambiente.
app.get('/minha-api/chat-token', requireLogin, async (req, res) => {
  const response = await fetch('https://api.diligensai.com.br/api/v1/chat/token', {
    method: 'POST',
    headers: { 'X-API-Key': process.env.DILIGENS_API_KEY, 'Content-Type': 'application/json' },
    // O external_id vem da SUA sessão, nunca do navegador.
    body: JSON.stringify({ external_id: String(req.user.id) }),
    signal: AbortSignal.timeout(10_000),
  });
  if (!response.ok) return res.status(502).end();
  const { token, expires_at } = await response.json();
  res.json({ token, expires_at });
});
```

Python:

```python
# Flask. DILIGENS_API_KEY fica numa variável de ambiente.
@app.get("/minha-api/chat-token")
@login_required
def chat_token():
    response = requests.post(
        "https://api.diligensai.com.br/api/v1/chat/token",
        headers={"X-API-Key": os.environ["DILIGENS_API_KEY"]},
        # O external_id vem da SUA sessão, nunca do navegador.
        json={"external_id": str(current_user.id)},
        timeout=10,
    )
    if not response.ok:
        return "", 502
    data = response.json()
    return {"token": data["token"], "expires_at": data["expires_at"]}
```

PHP:

```php
<?php
// Laravel. DILIGENS_API_KEY fica no .env.
Route::middleware('auth')->get('/minha-api/chat-token', function (Request $request) {
    $response = Http::timeout(10)
        ->withHeaders(['X-API-Key' => env('DILIGENS_API_KEY')])
        // O external_id vem da SUA sessão, nunca do navegador.
        ->post('https://api.diligensai.com.br/api/v1/chat/token', ['external_id' => (string) $request->user()->id]);

    abort_unless($response->successful(), 502);

    return ['token' => $response['token'], 'expires_at' => $response['expires_at']];
});
```

> **Obtenha o external_id da sua sessão**
>
> Nunca use um valor enviado pelo navegador. Se o `external_id` vier de um parâmetro da requisição, um usuário poderá abrir o chat e acessar os dados de outro. Consulte [Usuário final](https://docs.diligensai.com.br/guias/usuario-final.md).

O token vale **5 minutos**, e o SDK pede outro à sua rota antes que ele expire: não é necessário guardá-lo no seu backend. Use um timeout na chamada à Diligens, como nos exemplos: se ela estiver fora do ar, a sua rota responde com erro logo, sem prender o seu servidor. O corpo, a resposta e os erros do `POST /chat/token` estão em [Criar token de chat](https://docs.diligensai.com.br/referencia/token-chat.md).

## 2. Adicione o SDK ao seu frontend

Inclua o script do SDK nas páginas do seu sistema e chame o `Diligens.init` com a função que obtém o token da rota criada no passo anterior:

```html
<script src="https://cdn.diligensai.com.br/v1/diligens.js"></script>
<script>
  Diligens.init({
    // Deve retornar { token, expires_at }, no formato da resposta do POST /chat/token.
    getToken: async () => {
      const response = await fetch('/minha-api/chat-token');
      if (!response.ok) throw new Error('Falha ao gerar o token do chat');
      return response.json();
    },
  });
</script>
```

Pronto: o botão do chat aparece no canto inferior direito da tela (consulte [Posição do botão](#posicao-do-botao) para mudá-lo). O SDK não depende de nenhum framework e já inclui tudo o que precisa (estilos, fonte e bibliotecas).

> **Inicialize o SDK somente com o usuário autenticado**
>
> O SDK chama o `getToken` logo na inicialização, para carregar a identidade do assistente e os insights. Por isso, inclua o script apenas nas páginas autenticadas. Em uma aplicação de página única (SPA), chame o `Diligens.init` depois do login e o `Diligens.destroy` no logout.

## Opções do init

- `getToken` (`() => Promise<{ token, expires_at }>`, obrigatório)

  Função que solicita um token ao seu backend. O SDK a chama na inicialização, quando o token atual está a menos de 30 segundos de expirar e depois de uma resposta 401. Chamadas simultâneas compartilham a mesma requisição.

- `mode` (`floating | inline`, padrão: `floating`)

  Onde o chat é exibido. Com `floating`, um botão no canto da tela abre o chat sobre o site. Com `inline`, o chat é montado dentro de uma página do seu sistema (consulte [Chat dentro de uma página](#chat-dentro-de-uma-pagina)).

- `chatRoute` (`string`)

  Obrigatório no modo `inline`. A rota da página que contém o elemento `#diligens-chat`, por exemplo, `/assistente` ou `#/assistente`. O `Diligens.open()` e o clique no balão de insight levam o usuário para ela.

- `navigate` (`(route) => void`)

  Somente no modo `inline`. Navega até a `chatRoute` com o router da sua aplicação, sem recarregar a página. Quando omitida, o SDK usa `location.assign(chatRoute)`.

- `actions` (`objeto`)

  Como o SDK se autentica na sua API ao executar uma ação (consulte [Ações no seu sistema](#acoes-no-seu-sistema)).

  **Campos de actions:**

  - `credentials` (`same-origin | include | omit`, padrão: `same-origin`)

    O `credentials` do `fetch` nas chamadas de ação. O padrão envia os cookies quando a sua API está no mesmo domínio do site. Use `include` quando ela estiver em outro domínio e autenticar por cookie.

  - `getHeaders` (`() => objeto | Promise<objeto>`)

    Retorna os headers de autenticação da sua API, por exemplo, `Authorization`. O SDK a chama a cada ação.

- `theme` (`objeto`)

  As cores do chat, para combiná-lo com o seu sistema (consulte [Personalização](#personalizacao)).

- `appearance` (`auto | light | dark`, padrão: `auto`)

  O tema claro ou escuro do chat. O padrão segue o sistema do visitante. Se o seu site tem tema próprio, informe o tema atual e chame o `Diligens.setAppearance` quando ele mudar (consulte [Personalização](#personalizacao)).

- `position` (`bottom-right | bottom-left`, padrão: `bottom-right`)

  O canto da tela do botão do chat (modo `floating`) e do balão de insight (nos dois modos). O painel abre acima do botão, alinhado ao mesmo lado (consulte [Posição do botão](#posicao-do-botao)).

- `offset` (`objeto`, padrão: `{ x: 16, y: 16 }`)

  A distância do botão e do balão às bordas da tela, em pixels.

  **Campos de offset:**

  - `x` (`number`, padrão: `16`)

    Distância da borda lateral: a direita no `bottom-right` e a esquerda no `bottom-left`.

  - `y` (`number`, padrão: `16`)

    Distância da borda de baixo.

- `zIndex` (`number`, padrão: `2147483647`)

  A camada do botão, do painel e do balão. O padrão fica acima de tudo; use um valor menor quando um elemento do seu site precisar ficar por cima do chat.

## Chat dentro de uma página

No modo `inline`, o chat ocupa um elemento da sua página em vez de abrir sobre o site. Adicione o elemento `#diligens-chat` na página do assistente, com a altura desejada, e informe a rota dessa página na `chatRoute`:

```html
<div id="diligens-chat" style="height: 600px"></div>
```

```javascript
Diligens.init({
  getToken,
  mode: 'inline',
  chatRoute: '/assistente',
  // Opcional: navegação pelo router da sua aplicação, sem recarregar a página.
  navigate: (route) => router.push(route),
});
```

- O SDK observa a página. Quando o elemento `#diligens-chat` aparece, o chat é montado dentro dele e ocupa todo o seu tamanho. Quando o elemento sai da tela, por exemplo, numa troca de rota, o chat é desmontado, e a conversa é preservada.
- Nesse modo, não há botão flutuante. Fora da página do assistente, aparece apenas o balão com os insights novos, que leva o usuário à `chatRoute`.

## Ações no seu sistema

Para que a IA consulte dados e execute ações no seu sistema, envie a documentação da sua API em [Integrações](https://app.diligensai.com.br/api-docs), no painel. A documentação é convertida em um catálogo de ações, que você revisa antes de elas ficarem disponíveis no chat.

- **Consultas** (somente leitura) são executadas diretamente.
- **Ações de escrita** exibem um resumo com os botões "Confirmar" e "Cancelar", e só são executadas depois da confirmação do usuário. PUT, PATCH e DELETE sempre pedem confirmação; em um POST, a escolha é feita em Integrações.
- O SDK executa a requisição no navegador, com o método, a URL e o corpo definidos pelo catálogo, e devolve o resultado (status HTTP e corpo da resposta) à IA automaticamente. A Diligens nunca chama a sua API e não armazena credenciais do seu sistema.

Por padrão, a requisição usa a sessão do usuário por cookie quando a sua API está no mesmo domínio do site. Nos demais casos, configure a opção `actions`:

```javascript
Diligens.init({
  getToken,
  actions: {
    // A API está em outro domínio e autentica por cookie.
    credentials: 'include',
    // Ou: a API autentica por header. A função é chamada a cada ação.
    getHeaders: async () => ({ Authorization: `Bearer ${await getAccessToken()}` }),
  },
});
```

> **A sua API precisa aceitar chamadas do navegador**
>
> As ações partem da origem do site onde o chat é exibido. Configure o CORS da sua API para essa origem, com os métodos usados e os headers enviados, incluindo o `Content-Type` e os retornados pelo `getHeaders`. Com `credentials: 'include'`, responda também com `Access-Control-Allow-Credentials: true` e a origem exata. Uma requisição bloqueada é registrada como falha, e a IA informa o usuário.

## Personalização

O nome do assistente, o nome da empresa e a logo são configurados no painel, em [Assistente](https://app.diligensai.com.br/assistant), e aparecem no chat automaticamente. As cores são definidas na opção `theme`, e o tema claro ou escuro, na opção `appearance`:

```javascript
Diligens.init({
  getToken,
  appearance: currentTheme, // 'light' ou 'dark', o tema atual do seu site
  theme: {
    primaryColor: '#1d4ed8',
    launcherColor: '#1d4ed8',
    linkColor: { light: '#1d4ed8', dark: '#93c5fd' },
    backgroundColor: { light: '#ffffff', dark: '#0f172a' },
  },
});

// Quando o usuário troca o tema do seu site. O chat não fecha.
Diligens.setAppearance('dark');
```

| Opção | O que muda |
|---|---|
| `primaryColor` | Cor principal: mensagens do usuário, botão de enviar, "Confirmar", foco e seleção de texto |
| `primaryTextColor` | Texto sobre a `primaryColor` |
| `headerColor` | Fundo do cabeçalho do chat. Padrão: o `backgroundColor` |
| `headerTextColor` | Texto e ícones do cabeçalho |
| `backgroundColor` | Fundo do chat, do balão de insight, dos cards e da caixa de mensagem |
| `textColor` | Texto principal. Os textos secundários são derivados dele |
| `linkColor` | Links, fontes consultadas e título dos insights. É texto: mantenha contraste com o `backgroundColor` |
| `insightColor` | Detalhes decorativos: indicador de digitação, cursor da resposta, marcação dos insights e o ponto da formiga |
| `dangerColor` | Erros: mensagem de erro, "Tentar novamente", ação que falhou e contador de não lidas |
| `dangerTextColor` | Texto sobre a `dangerColor` (contador de não lidas) |
| `successColor` | Indicador "Online" e ação concluída |
| `borderColor` | Bordas e divisórias |
| `launcherColor` | Botão do chat no canto da tela (modo `floating`) e o fundo da formiga no cabeçalho e no balão de insight, quando a empresa não tem logo |
| `launcherIconColor` | Ícone do botão do chat e o traço da formiga |

- Cada opção aceita qualquer cor CSS (`'#1d4ed8'`, `'rgb(29 78 216)'`, `'oklch(0.5 0.2 260)'`) ou um objeto `{ light, dark }`, com uma cor para cada tema. O tema ativo vem do `appearance`: com o padrão `auto`, o chat segue o tema do sistema do visitante.
- Com `appearance: 'light'` ou `'dark'`, o chat inteiro segue o tema informado, inclusive as cores padrão, as sombras e as barras de rolagem, mesmo que o sistema do visitante esteja no outro tema.
- Sem o `theme`, o chat usa uma paleta neutra. As cores omitidas usam o padrão, e as cores de texto omitidas são escolhidas automaticamente para manter o contraste.
- Valores inválidos são ignorados, com um aviso no console do navegador.

### Posição do botão

Por padrão, o botão do chat fica no canto inferior direito, a 16 pixels das bordas. Se esse canto já é ocupado por outro widget ou pelas notificações do seu sistema, mude o lado com a opção `position` e a distância com a `offset`:

```javascript
Diligens.init({
  getToken,
  position: 'bottom-left',
  offset: { x: 24, y: 80 }, // acima de uma barra fixa no rodapé, por exemplo
  zIndex: 1000, // abaixo dos modais do seu sistema
});
```

O painel do chat e o balão de insight acompanham o botão. Em telas estreitas, o painel ocupa a largura disponível, descontada a distância às bordas.

## Controle do chat

| Função | O que faz |
|---|---|
| `Diligens.open()` | No modo `floating`, abre o chat. No modo `inline`, leva o usuário à `chatRoute` |
| `Diligens.close()` | No modo `floating`, fecha o chat. No modo `inline`, não tem efeito |
| `Diligens.setAppearance(appearance)` | Troca o tema claro ou escuro (`'auto'`, `'light'` ou `'dark'`) sem fechar o chat |
| `Diligens.setTheme(theme)` | Troca as cores sem fechar o chat. Substitui o `theme` anterior por inteiro |
| `Diligens.destroy()` | Remove o chat da página e descarta o token. Chame no logout |

Uma nova chamada ao `Diligens.init` é ignorada enquanto o chat estiver ativo. Para trocar de usuário, chame o `Diligens.destroy()` e, em seguida, o `Diligens.init` com a sessão nova.

## O que o SDK faz por você

- **Token:** Mantém o token em memória, renova-o antes de expirar e repete a chamada após um 401.
- **Conversa:** Carrega o histórico ao abrir o chat, exibe as respostas em streaming, num ritmo de leitura, com as fontes consultadas e oferece os botões "Parar" e "Nova conversa". Quando a pergunta precisa de uma pesquisa na web, mostra o passo em andamento ("Pesquisando…", "Lendo…") até a resposta começar. Enquanto a resposta chega, o chat não rola sozinho: o usuário lê do começo, e um botão leva ao fim da conversa.
- **Insights:** Busca as dicas novas sobre as vendas e os avisos de pedidos não pagos, cancelados ou estornados fora do normal a cada 10 minutos e quando o usuário volta para a aba, e as exibe num balão sobre o site ou dentro da conversa, quando o chat está aberto.
- **Erros:** Exibe "O assistente está indisponível no momento." quando os créditos acabam, uma contagem regressiva quando o limite de mensagens por minuto é atingido e a opção "Tentar novamente" em falhas de rede. Se a Diligens estiver fora do ar (em uma manutenção, por exemplo), o chat continua na página, marcado como offline, e reconecta sozinho quando ela voltar. Nada disso precisa ser tratado pelo seu código.
- **Isolamento:** O chat é renderizado em shadow DOM, sem herdar nem alterar os estilos do seu site, e os atalhos de teclado do chat não chegam à sua página.
- **Acessibilidade:** Segue o tema claro ou escuro do seu site (opção `appearance`) ou do sistema do visitante, e a preferência de movimento reduzido do sistema.

## Problemas comuns

As mensagens abaixo aparecem no console do navegador.

| Mensagem | Causa | Solução |
|---|---|---|
| `Diligens: init() exige a opção "getToken"` | O `init` foi chamado sem o `getToken` | Informe a função que obtém o token |
| `Diligens: o modo "inline" exige a opção "chatRoute"` | O modo `inline` foi usado sem a `chatRoute` | Informe a rota da página do assistente |
| `[diligens] falha ao obter token` | O `getToken` falhou ou não retornou `{ token, expires_at }` | Verifique a rota de token do seu backend e o formato da resposta |
| `[diligens] falha ao executar a ação` | A requisição à sua API foi bloqueada (CORS) ou falhou na rede | Revise o CORS e a opção `actions` |
| `Diligens: theme.<opção> ignorado` | Cor inválida ou opção desconhecida | Use uma cor CSS ou `{ light, dark }` e os nomes da tabela de personalização |
| `Diligens: appearance "<valor>" ignorado` | Valor diferente de `auto`, `light` ou `dark` | Use um dos três valores |
| `Diligens: position "<valor>" ignorado` | Valor diferente de `bottom-right` ou `bottom-left` | Use um dos dois valores |
| `Diligens: offset ignorado` ou `offset.<x ou y> ignorado` | O `offset` não é um objeto, ou a distância não é um número maior ou igual a 0 | Informe `{ x, y }` em pixels |
| `Diligens: zIndex "<valor>" ignorado` | O valor não é um número inteiro | Use um inteiro, por exemplo, `1000` |
| `Diligens: setAppearance() ignorado` ou `setTheme() ignorado` | A função foi chamada antes do `init` ou depois do `destroy` | Chame-a com o chat ativo; no `init`, use as opções `appearance` e `theme` |

---

# Usuário final (external_id)

> O identificador que vincula o histórico do chat aos pedidos e às contas de cada usuário do seu sistema.

Página: https://docs.diligensai.com.br/guias/usuario-final

O `external_id` identifica quem está conversando. Trata-se do identificador do usuário **no seu sistema** (o id do usuário logado, da loja ou da conta), e é ele que vincula todos os dados:

- O histórico da conversa do chat é guardado por `external_id`.
- Os pedidos enviados com um `external_id` são as vendas que o chat desse mesmo usuário considera, nas respostas e nos insights.
- Os produtos enviados com um `external_id` formam o catálogo da loja que o chat desse usuário considera.
- As contas enviadas com um `external_id` são as contas a receber e a pagar que o chat desse usuário considera.

## Como escolher o valor

- Use sempre o mesmo valor para a mesma pessoa ou loja. O valor é único dentro da sua conta e tem até 255 caracteres.
- Cada conta tem até **1.000 `external_id`s**. Uma chamada com um `external_id` novo além desse número responde com o status 403 `EXTUSER_002`, e os que já existem continuam funcionando. Se precisar de mais, fale com o suporte da Diligens.
- Não é necessário cadastrá-lo previamente, porque ele é criado na primeira chamada que o utiliza ([POST /chat/token](https://docs.diligensai.com.br/referencia/token-chat.md), [PUT /orders](https://docs.diligensai.com.br/referencia/pedidos.md), [PUT /products](https://docs.diligensai.com.br/referencia/produtos.md) ou [PUT /financial-entries](https://docs.diligensai.com.br/referencia/contas.md)).
- Se o seu sistema tem várias lojas por login, escolha o nível adequado às perguntas. O id da loja separa as vendas de cada uma, e o id do usuário reúne tudo o que ele vê.

> **Obtenha o external_id da sua sessão**
>
> No `POST /chat/token`, o valor deve vir do usuário autenticado no seu backend, nunca de um parâmetro enviado pelo navegador. Se ele vier do navegador, um usuário poderá acessar o chat e os dados de outro.

## Apagar um usuário final

Para atender a um pedido de exclusão (LGPD), use o [DELETE /external-users/:external_id](https://docs.diligensai.com.br/referencia/apagar-usuario.md), que apaga o usuário e todos os dados dele: histórico, pedidos, compradores e contas. A exclusão não bloqueia o valor, portanto uma nova chamada com o mesmo `external_id` cria o usuário novamente, vazio.

---

# Erros

> O formato padrão de erro da API, os códigos estáveis e como tratar cada um deles.

Página: https://docs.diligensai.com.br/guias/erros

Todos os erros são retornados no mesmo formato. Para decidir como tratá-los, use o `code`, que é estável. A `message` é um texto destinado a pessoas e pode mudar.

```json
{
  "status": 400,
  "code": "COMMON_002",
  "message": "Dados inválidos",
  "details": [{ "field": "external_id", "message": "Campo obrigatório" }]
}
```

- `status` repete o status HTTP.
- `details` só aparece no `COMMON_002`, com o caminho de cada campo inválido (por exemplo, `orders[3].items[0].quantity`) e a mensagem.

## Erros gerais

Estes erros valem para as rotas de integração, chamadas pelo seu backend com a API key.

| Status | `code` | Quando |
|---|---|---|
| 400 | `COMMON_001` | JSON malformado, tipo errado ou data fora do RFC 3339. Esse erro não traz o campo |
| 400 | `COMMON_002` | Campo inválido. O `details[].field` traz o caminho do campo |
| 401 | `APIKEY_002` | API key ausente, inválida ou regenerada |
| 402 | `APIKEY_003` | A conta ainda não tem nenhuma recarga de créditos paga (ou a única foi estornada). Faça uma recarga no painel, em [Cobrança](https://app.diligensai.com.br/billing) |
| 403 | `EXTUSER_002` | A conta chegou ao limite de `external_id`s e a chamada usou um novo (veja [Usuário final](https://docs.diligensai.com.br/guias/usuario-final.md#como-escolher-o-valor)) |
| 404 | `EXTUSER_001` | Usuário final não encontrado (só no `DELETE /external-users`) |
| 413 | `COMMON_004` | Body acima de 5 MB. Reduza o tamanho do lote |
| 429 | `COMMON_005` | O limite de chamadas por minuto da sua API key foi excedido. Aguarde os segundos indicados no header `Retry-After` e repita a chamada (veja [Limites](https://docs.diligensai.com.br/guias/limites.md)) |
| 429 | `INGEST_001` | A cota diária de itens enviados acabou, e o lote não entrou na fila. Esperar o `Retry-After` não adianta (veja [Cota diária](https://docs.diligensai.com.br/guias/limites.md#cota-diaria)) |
| 500 | `COMMON_003` | Erro interno. A chamada pode ser repetida |
| 503 | `COMMON_006` | Serviço indisponível por um momento. Aguarde alguns segundos e repita a chamada |

Os erros do chat não precisam ser tratados pelo seu código: o SDK exibe ao usuário final a mensagem adequada e renova o token quando necessário (consulte [O que o SDK faz por você](https://docs.diligensai.com.br/guias/sdk.md#o-que-o-sdk-faz-por-voce)).

---

# Limites de chamadas

> O número de chamadas por minuto aceito por cada rota e como tratar o status 429 com o header Retry-After.

Página: https://docs.diligensai.com.br/guias/limites

Quando o limite por minuto é excedido, a API responde com o status 429 `COMMON_005` e o header `Retry-After`, que indica quantos segundos aguardar. Aguarde esse tempo e repita a chamada, sem fazer novas tentativas em loop. No chat, esse tratamento é feito pelo SDK.

## Chamadas por minuto

| Rotas | Limite | Contabilizado por |
|---|---|---|
| Dados: `PUT /orders`, `PUT /products`, `PUT /financial-entries` e `DELETE /external-users` | 120 por minuto, somadas | API key |
| Token de chat: `POST /chat/token` | 120 por minuto, à parte das rotas de dados | API key |
| Mensagens do chat, incluindo os resultados das ações e as novas conversas | 20 por minuto | Usuário final |
| Todas as chamadas do chat, somadas | 300 por minuto | Empresa |

- O limite é reiniciado a cada minuto.
- O `POST /chat/token` tem um limite próprio: uma sincronização no limite não impede os usuários de abrir o chat.
- Na sincronização do histórico, envie as páginas em sequência e, se receber 429, aguarde o `Retry-After` antes de enviar a próxima.
- No chat, o SDK exibe uma contagem regressiva e libera o envio depois dos segundos indicados no `Retry-After`. Nada entra no histórico.

## Cota diária

Além do limite por minuto, cada conta envia até **500 mil itens por dia**: pedidos, produtos e lançamentos somados, contando os reenvios. A janela é de 24 horas a partir do primeiro envio.

- Quando a cota acaba, o `PUT /orders`, o `PUT /products` e o `PUT /financial-entries` respondem com o status 429 `INGEST_001`, e o lote não entra na fila.
- Esse erro não traz o header `Retry-After`, e aguardar alguns segundos não adianta, porque a cota só é renovada quando a janela de 24 horas termina.
- Se o histórico da sua empresa for maior, divida a sincronização em alguns dias ou fale com o suporte da Diligens para aumentar a cota.

## Usuários finais

Cada conta tem até **1.000 `external_id`s**. Uma chamada com um `external_id` novo além desse número responde com o status 403 `EXTUSER_002`, e os que já existem continuam funcionando (consulte [Usuário final](https://docs.diligensai.com.br/guias/usuario-final.md#como-escolher-o-valor)).

A cota diária e o limite de `external_id`s valem por conta e podem ser aumentados pelo suporte da Diligens.

## Tamanhos

| O quê | Limite |
|---|---|
| Lote do `PUT /orders` | De 1 a 300 pedidos e até 5 MB |
| Lote do `PUT /products` | De 1 a 500 produtos e até 5 MB |
| Lote do `PUT /financial-entries` | De 1 a 200 lançamentos e até 5 MB |
| Itens de um pedido | De 1 a 200 |
| Pagamentos de um pedido | Até 20 |
| Mensagem do chat | Até 4.000 caracteres |
| Resposta da sua API a uma ação | Até 2 MB (a IA lê os primeiros 16.000 caracteres) |

Acima desses tamanhos, a API responde com o status 413 `COMMON_004`. Os demais códigos estão em [Erros](https://docs.diligensai.com.br/guias/erros.md).

---

# Créditos

> Como o uso da IA é cobrado e o que funciona sem saldo.

Página: https://docs.diligensai.com.br/guias/creditos

O uso da IA é pré-pago. Você compra créditos no painel, em [Cobrança](https://app.diligensai.com.br/billing) (recarga por Pix, boleto ou cartão), e o custo de cada resposta é descontado do saldo.

## Conta nova

Toda conta nova começa sem saldo, e a API inteira (não só o chat) funciona depois da primeira recarga paga. Antes dela, a API key não é gerada, e toda chamada com a chave responde com o status 402 `APIKEY_003`. Por isso, a integração começa pela recarga: com Pix, ela é confirmada em segundos, enquanto o boleto leva até 3 dias úteis.

A recarga pendente não conta. Se a única recarga paga for estornada, a chave continua no painel, mas as chamadas voltam a responder 402 `APIKEY_003` até que uma nova recarga seja paga.

## Sem saldo

Depois da primeira recarga paga, o saldo zerado afeta apenas a IA:

| Recurso | Funciona sem saldo? |
|---|---|
| Geração do token do chat (`POST /chat/token`) | Sim |
| `PUT /orders`, `PUT /products` e `PUT /financial-entries` | Sim, os dados continuam sendo sincronizados |
| `DELETE /external-users` | Sim |
| Mensagens do chat | Não |
| Insights | Não |

Sem saldo, o chat responde com o status 402 `BILLING_001` até que a próxima recarga seja paga, e nada entra no histórico. O SDK informa ao usuário final que o assistente está indisponível no momento, sem nenhum tratamento no seu código.

> **O que o usuário final vê**
>
> Apenas o aviso de que o assistente está indisponível no momento. Saldo e recarga são assuntos do administrador da sua conta, e o chat volta a funcionar automaticamente quando ele faz uma recarga no painel.

---

# Privacidade (LGPD)

> Os dados armazenados com criptografia, a retenção do histórico e como apagar todos os dados de um usuário final.

Página: https://docs.diligensai.com.br/guias/lgpd

Os dados dos seus usuários finais chegam à Diligens pelo seu backend. Esta página descreve como eles são protegidos e como atender aos pedidos dos titulares. O tratamento completo está na [Política de Privacidade](https://app.diligensai.com.br/privacy).

## O que fica encriptado

- O documento, o e-mail e o telefone do comprador ([PUT /orders](https://docs.diligensai.com.br/referencia/pedidos.md)).
- O documento da contraparte ([PUT /financial-entries](https://docs.diligensai.com.br/referencia/contas.md)).

Esses campos nunca são enviados à IA nem registrados nos logs. Os nomes do comprador e da contraparte aparecem nas respostas do chat. Número de cartão e endereço completo (rua e número) não são aceitos, porque esses campos não existem no contrato e são ignorados.

## Retenção do histórico

O histórico do chat é guardado por **7 dias**, para todos os clientes, e não é configurável. Uma vez por dia, as mensagens mais antigas que isso são apagadas, de todos os seus usuários. Pedidos, produtos e contas não são afetados, pois seguem o que o seu backend envia.

## Apagar um usuário final

O [DELETE /external-users/:external_id](https://docs.diligensai.com.br/referencia/apagar-usuario.md) apaga o usuário final e **tudo** dele: o histórico do chat, os pedidos, os compradores, o catálogo de produtos e as contas a receber e a pagar. Use essa rota para atender ao pedido de exclusão do titular.

> **Interrompa o envio antes de apagar**
>
> Os lotes do usuário enviados antes da exclusão e que ainda não tinham sido gravados são descartados: os dados apagados não voltam. Mas a exclusão não bloqueia o `external_id`: uma nova chamada ao `POST /chat/token`, ao `PUT /orders`, ao `PUT /products` ou ao `PUT /financial-entries` com o mesmo valor, feita depois da exclusão, cria o usuário novamente, vazio. A exclusão é irreversível.

---

# Introdução à API

> A URL base, a autenticação por API key e as convenções comuns a todas as rotas.

Página: https://docs.diligensai.com.br/referencia/introducao

A API da Diligens segue o padrão REST, com JSON no corpo das requisições e nas respostas. Todas as rotas ficam em `https://api.diligensai.com.br/api/v1`.

## Autenticação

As rotas desta referência são chamadas pelo seu backend, de servidor para servidor, com a API key no header `X-API-Key`. Os detalhes estão em [Autenticação](https://docs.diligensai.com.br/guias/autenticacao.md).

```http
X-API-Key: csk_...
Content-Type: application/json
```

> **A API key nunca deve ser enviada ao navegador**
>
> A API key também não deve ser incluída em aplicativos mobile. O chat é exibido pelo SDK, que usa um token de curta duração gerado pelo seu backend (consulte [SDK do chat](https://docs.diligensai.com.br/guias/sdk.md)).

## Rotas

| Rota | Para quê |
|---|---|
| [POST /chat/token](https://docs.diligensai.com.br/referencia/token-chat.md) | Gerar o token de chat de um usuário final, usado pelo SDK |
| [PUT /orders](https://docs.diligensai.com.br/referencia/pedidos.md) | Enviar os pedidos (sincronização e webhook) |
| [PUT /products](https://docs.diligensai.com.br/referencia/produtos.md) | Enviar o catálogo de produtos (sincronização e webhook) |
| [PUT /financial-entries](https://docs.diligensai.com.br/referencia/contas.md) | Enviar as contas a receber e a pagar |
| [DELETE /external-users/:external_id](https://docs.diligensai.com.br/referencia/apagar-usuario.md) | Apagar um usuário final e todos os dados dele |

## Convenções

- **Usuário final:** Todos os dados são separados por `external_id`, o id do usuário no seu sistema. Consulte [Usuário final](https://docs.diligensai.com.br/guias/usuario-final.md).
- **Valores:** São expressos em centavos, sempre como números inteiros. Por exemplo, R$ 174,90 é `17490`.
- **Datas:** Seguem o RFC 3339, com fuso horário (`2026-09-20T14:32:00-03:00`). Vencimentos e datas sem hora usam o formato `AAAA-MM-DD`.
- **Envio idempotente:** O `PUT /orders`, o `PUT /products` e o `PUT /financial-entries` usam o `updated_at` para decidir o que gravar. O reenvio não duplica nenhum registro.
- **Gravação em segundo plano:** O `PUT /orders`, o `PUT /products` e o `PUT /financial-entries` validam o lote na hora, respondem com o status 202 e gravam em seguida. Os dados aparecem no chat em segundos.
- **Erros:** Seguem sempre o mesmo formato e são tratados pelo `code`. Consulte [Erros](https://docs.diligensai.com.br/guias/erros.md) e [Limites de chamadas](https://docs.diligensai.com.br/guias/limites.md).

---

# Criar token de chat

> Gera, no seu backend, o token de curta duração que o SDK usa para abrir o chat de um usuário final.

Página: https://docs.diligensai.com.br/referencia/token-chat

`POST https://api.diligensai.com.br/api/v1/chat/token`

Autorização: `X-API-Key: <sua API key>`. Chamada do seu backend, com a API key da conta. Nunca do navegador. Veja [Autenticação](https://docs.diligensai.com.br/guias/autenticacao.md).

Esta rota troca a API key por um token de chat para um usuário final. O seu backend a chama a partir de uma rota própria, acessível apenas ao usuário autenticado, e devolve o token ao navegador. O [SDK do chat](https://docs.diligensai.com.br/guias/sdk.md) usa esse token para conversar com a Diligens, e a API key nunca sai do servidor. O código completo da rota do seu backend está em [Crie a rota de token](https://docs.diligensai.com.br/guias/sdk.md#1-crie-a-rota-de-token-no-seu-backend).

**Requisição**

cURL:

```bash
curl -X POST "https://api.diligensai.com.br/api/v1/chat/token" \
  -H "X-API-Key: $DILIGENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "external_id": "loja-123" }'
```

Node.js:

```javascript
const response = await fetch('https://api.diligensai.com.br/api/v1/chat/token', {
  method: 'POST',
  headers: { 'X-API-Key': process.env.DILIGENS_API_KEY, 'Content-Type': 'application/json' },
  // O external_id vem da SUA sessão, nunca do navegador.
  body: JSON.stringify({ external_id: String(user.id) }),
});
const { token, expires_at } = await response.json();
```

Python:

```python
import os
import requests

response = requests.post(
    "https://api.diligensai.com.br/api/v1/chat/token",
    headers={"X-API-Key": os.environ["DILIGENS_API_KEY"]},
    # O external_id vem da SUA sessão, nunca do navegador.
    json={"external_id": str(user.id)},
    timeout=10,
)
data = response.json()
```

PHP:

```php
<?php
$ch = curl_init('https://api.diligensai.com.br/api/v1/chat/token');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'X-API-Key: ' . getenv('DILIGENS_API_KEY'),
        'Content-Type: application/json',
    ],
    // O external_id vem da SUA sessão, nunca do navegador.
    CURLOPT_POSTFIELDS => json_encode(['external_id' => (string) $user->id]),
]);
$data = json_decode(curl_exec($ch), true);
```

**Resposta**

201:

```json
{
  "token": "Q2F7...",
  "external_id": "loja-123",
  "expires_at": "2026-09-26T15:05:00Z"
}
```

401:

```json
{ "status": 401, "code": "APIKEY_002", "message": "API key inválida" }
```

> **Obtenha o external_id da sua sessão**
>
> Nunca use um valor enviado pelo navegador. Se o `external_id` vier de um parâmetro da requisição, um usuário poderá abrir o chat e acessar os dados de outro. Consulte [Usuário final](https://docs.diligensai.com.br/guias/usuario-final.md).

## Corpo da requisição

- `external_id` (`string`, obrigatório)

  O identificador, no seu sistema, do usuário que vai conversar, com até 255 caracteres. Se ele ainda não existir na Diligens, será criado.

## Resposta

A API responde com o status `201` e o token em `token`, válido até `expires_at`.

## Regras

- O token é válido por **5 minutos** e pode ser usado quantas vezes for necessário até expirar. Cada chamada gera um novo token, e os anteriores continuam válidos até expirarem.
- O token dá acesso somente ao chat daquele `external_id`. Ele não serve para o `PUT /orders` nem para as outras rotas desta referência.
- Não é necessário guardar o token no seu backend. O SDK o mantém em memória e solicita um novo antes que ele expire.
- A rota funciona mesmo sem créditos (consulte [Créditos](https://docs.diligensai.com.br/guias/creditos.md)) e conta no [limite de chamadas](https://docs.diligensai.com.br/guias/limites.md) da API key.

## Erros

| Status | `code` | Quando |
|---|---|---|
| 400 | `COMMON_002` | `external_id` ausente, vazio ou com mais de 255 caracteres |
| 401 | `APIKEY_002` | API key ausente, inválida ou regenerada |
| 402 | `APIKEY_003` | A conta ainda não tem nenhuma recarga de créditos paga |
| 403 | `EXTUSER_002` | O `external_id` é novo e a conta já chegou ao limite de `external_id`s |
| 429 | `COMMON_005` | Limite de chamadas da API key excedido |

Os detalhes do formato de erro estão em [Erros](https://docs.diligensai.com.br/guias/erros.md).

---

# Enviar pedidos

> Sincroniza os pedidos de um usuário final, na carga do histórico ou por webhook. A operação é idempotente e aceita lotes de até 300 pedidos.

Página: https://docs.diligensai.com.br/referencia/pedidos

`PUT https://api.diligensai.com.br/api/v1/orders`

Autorização: `X-API-Key: <sua API key>`. Chamada do seu backend, com a API key da conta. Nunca do navegador. Veja [Autenticação](https://docs.diligensai.com.br/guias/autenticacao.md).

Os pedidos permitem que a IA entenda o negócio de cada usuário. Com eles, a IA responde a perguntas como "quanto vendi este mês?", "qual produto mais vendeu semana passada?" ou "como foram as vendas no Pix em agosto?", sempre com os números daquele usuário.

Os números são calculados pela Diligens a partir dos pedidos que você enviou: faturamento, número de vendas, ticket médio, produtos, formas de pagamento, canais, evolução e últimos pedidos. A IA apenas redige a resposta. Somente o pedido com `status: "paid"` é contabilizado como venda; os pedidos pendentes, cancelados e estornados aparecem separadamente.

**Requisição**

cURL:

```bash
curl -X PUT "https://api.diligensai.com.br/api/v1/orders" \
  -H "X-API-Key: $DILIGENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "loja-123",
    "orders": [{
      "id": "PED-1002",
      "status": "paid",
      "total_cents": 1500,
      "placed_at": "2026-09-22T09:10:00-03:00",
      "updated_at": "2026-09-22T09:12:00-03:00",
      "items": [{ "name": "Café 500g", "quantity": 1, "unit_price_cents": 1500 }],
      "payments": [{ "method": "pix", "amount_cents": 1500, "status": "paid" }]
    }]
  }'
```

Node.js:

```javascript
const response = await fetch('https://api.diligensai.com.br/api/v1/orders', {
  method: 'PUT',
  headers: {
    'X-API-Key': process.env.DILIGENS_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    external_id: 'loja-123',
    orders: [{
      id: 'PED-1002',
      status: 'paid',
      total_cents: 1500,
      placed_at: '2026-09-22T09:10:00-03:00',
      updated_at: '2026-09-22T09:12:00-03:00',
      items: [{ name: 'Café 500g', quantity: 1, unit_price_cents: 1500 }],
      payments: [{ method: 'pix', amount_cents: 1500, status: 'paid' }],
    }],
  }),
});
const { batch_id, received } = await response.json();
```

Python:

```python
import os
import requests

response = requests.put(
    "https://api.diligensai.com.br/api/v1/orders",
    headers={"X-API-Key": os.environ["DILIGENS_API_KEY"]},
    json={
        "external_id": "loja-123",
        "orders": [{
            "id": "PED-1002",
            "status": "paid",
            "total_cents": 1500,
            "placed_at": "2026-09-22T09:10:00-03:00",
            "updated_at": "2026-09-22T09:12:00-03:00",
            "items": [{"name": "Café 500g", "quantity": 1, "unit_price_cents": 1500}],
            "payments": [{"method": "pix", "amount_cents": 1500, "status": "paid"}],
        }],
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())  # {'batch_id': '0199a3c2-7f1e-7b6a-9d1c-2f4e8a6b1c3d', 'received': 1}
```

PHP:

```php
<?php
$body = [
    'external_id' => 'loja-123',
    'orders' => [[
        'id' => 'PED-1002',
        'status' => 'paid',
        'total_cents' => 1500,
        'placed_at' => '2026-09-22T09:10:00-03:00',
        'updated_at' => '2026-09-22T09:12:00-03:00',
        'items' => [['name' => 'Café 500g', 'quantity' => 1, 'unit_price_cents' => 1500]],
        'payments' => [['method' => 'pix', 'amount_cents' => 1500, 'status' => 'paid']],
    ]],
];

$ch = curl_init('https://api.diligensai.com.br/api/v1/orders');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'X-API-Key: ' . getenv('DILIGENS_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode($body),
]);
$result = json_decode(curl_exec($ch), true);
```

**Resposta**

202:

```json
{ "batch_id": "0199a3c2-7f1e-7b6a-9d1c-2f4e8a6b1c3d", "received": 2 }
```

400:

```json
{
  "status": 400,
  "code": "COMMON_002",
  "message": "Dados inválidos",
  "details": [
    { "field": "orders[3].total_cents", "message": "Campo obrigatório" },
    { "field": "orders[3].items[0].quantity", "message": "Deve ser maior que 0" }
  ]
}
```

413:

```json
{ "status": 413, "code": "COMMON_004", "message": "Corpo da requisição muito grande" }
```

503:

```json
{ "status": 503, "code": "COMMON_006", "message": "Serviço indisponível. Tente de novo em instantes" }
```

## Sincronização e webhook

O mesmo endpoint atende aos dois momentos da integração:

- **Sincronização do histórico:** Na primeira integração, pagine todos os pedidos do usuário e envie lotes de até 300. Repita o processo sempre que precisar reconciliar os dados.
- **Webhook:** A cada venda ou alteração no pedido (pago, cancelado, estornado, enviado), envie um lote com 1 pedido.

O chat conhece apenas os dados que você sincronizou. Se o histórico começa em janeiro de 2025, uma pergunta sobre 2024 é respondida com "não há pedidos sincronizados nesse período", e não com "você não vendeu". Por isso, sincronize o histórico completo e envie as alterações pelo webhook. Durante a sincronização, envie as páginas em sequência e respeite o [limite de chamadas](https://docs.diligensai.com.br/guias/limites.md).

Os pedidos também alimentam os [insights](https://docs.diligensai.com.br/guias/sdk.md#o-que-o-sdk-faz-por-voce): algumas vezes por dia, a IA comenta as vendas mais recentes do usuário e envia uma dica no chat dele. Envie as vendas pelo webhook assim que ocorrerem, porque uma venda paga há mais de 24 horas não gera insight. Vale a data do pagamento (`paid_at`) e, sem ela, a do pedido. Com a venda no dia do pedido, vale a do pedido.

Os pedidos que não viraram venda também contam: os cancelados sem pagamento (o Pix ou o boleto que expirou, o cartão recusado), os cancelados por outro motivo e os estornados explicam por que as vendas subiram ou caíram, e um pico deles gera um aviso no chat, por exemplo: "6 dos 16 pedidos das últimas 24 horas no Pix expiraram, contra 9% no mês". Por isso, envie também os cancelamentos e os estornos pelo webhook, com o `cancel_category`, o `cancelled_at` e o `refunded_at`. Os insights podem ser desligados no painel, em [Assistente](https://app.diligensai.com.br/assistant).

**A venda conta no dia do pagamento.** No chat e nos insights, um pedido pago conta pela data do `paid_at`: um boleto feito na segunda e pago na quinta é venda da quinta. Sem `paid_at`, vale a data do pedido (`placed_at`). Se o seu sistema conta a venda no dia em que o pedido foi feito, troque para **dia do pedido** no painel, na [configuração do assistente](https://app.diligensai.com.br/assistant): aí todo pedido pago conta pelo `placed_at`. Mande o `paid_at` de qualquer forma: é a hora do pagamento que a IA cita. No resumo de vendas do chat, pendentes, cancelados e estornados contam pela data do pedido; nos avisos de pedidos que não viraram venda, pela data do evento (`cancelled_at`, `refunded_at`) e, sem ela, pela do pedido.

> **Reenvio seguro**
>
> A rota é idempotente: reenviar um lote não duplica dados, e um webhook atrasado não desfaz um estado mais recente. As regras do `updated_at` estão descritas logo abaixo.

## Gravação em segundo plano

A rota valida o lote na hora e responde com o status **202** assim que ele é aceito. A gravação acontece logo depois, em segundo plano: em geral, em segundos. Em uma sincronização grande, os últimos lotes podem levar alguns minutos para aparecer no chat.

- **O 202 é a confirmação:** O lote foi validado e está guardado. Não reenvie um lote que recebeu 202.
- **A validação continua imediata:** Um erro 400 sai na própria resposta, com o campo, e nada do lote é aceito.
- **Os lotes não são gravados na ordem de envio,** e não precisam ser: vale sempre o `updated_at` mais recente de cada pedido.
- **Se a gravação falhar** (por exemplo, por uma instabilidade momentânea), a Diligens tenta de novo automaticamente. Não é preciso reenviar.
- **Com o status 503 `COMMON_006`,** o lote **não** foi aceito: aguarde alguns segundos e reenvie.

## Regras

- Um lote tem de 1 a 300 pedidos, sempre do mesmo usuário final (`external_id`), e o body tem no máximo 5 MB. Se o usuário final ainda não existir, ele é criado.
- **Valores em centavos**, sempre inteiros: R$ 174,90 corresponde a `17490`. A moeda padrão é `BRL`; para usar outra, informe o `currency` com 3 letras.
- **Datas em RFC 3339 com fuso** (`2026-09-20T14:32:00-03:00`). A exceção é `customer.birth_date`, que usa o formato `AAAA-MM-DD`. Use o fuso real: um horário de Brasília enviado com `Z` (UTC) fica 3 horas adiantado.
- **Datas no futuro são recusadas:** Uma data mais de 1 hora à frente do horário atual retorna o status 400 `COMMON_002` com o campo (por exemplo, `orders[3].paid_at`), e o lote inteiro é recusado. A exceção é o `estimated_delivery_at`, que pode estar no futuro.
- **Envie sempre o pedido completo**, com o estado atual, nunca apenas o campo alterado. Os itens e os pagamentos são substituídos integralmente a cada envio.
- **`updated_at` é obrigatório** e deve ser a data da última alteração do pedido no sistema de origem. **Não é o horário em que você envia** os dados à Diligens: na sincronização do histórico, um pedido de março mantém o `updated_at` de março. A Diligens só grava o pedido se essa data for **mais recente** que a do registro existente. Por isso:
  - Reenviar o mesmo lote não duplica dados: os pedidos já gravados são ignorados.
  - Um webhook atrasado não desfaz um estado mais recente; por exemplo, um "pago" que chega depois de um "cancelado" é ignorado.
  - **Para corrigir dados já enviados** (por exemplo, um histórico enviado com o `updated_at` errado ou sem o `cancelled_at`), reenvie os pedidos corrigidos com um `updated_at` mais recente que o do envio anterior. Com um `updated_at` mais antigo, eles são ignorados e nada muda.
- **Envie a data de cada evento:** `paid_at` no pedido pago, `cancelled_at` no cancelado e `refunded_at` no estornado. Sem ela, o evento é considerado na data do pedido, e o cancelamento recente de um pedido antigo não aparece como recente.
- **O lote é tudo ou nada:** Se a gravação falhar, nenhum pedido do lote é gravado, e a Diligens tenta gravar o lote inteiro de novo. Reenviar também é seguro.
- **Cliente (`customer`):** O campo é opcional. Sem ele, o pedido fica sem comprador (por exemplo, em uma venda no balcão). Um campo não enviado mantém o valor já registrado, e um pedido mais antigo apenas preenche campos vazios, sem sobrescrever dados mais recentes.
- **Não envie** número de cartão nem endereço completo (rua e número). Esses campos não existem no contrato e são ignorados. Os dados gravados com encriptação estão descritos em [Privacidade](https://docs.diligensai.com.br/guias/lgpd.md).
- Os campos `delivery_state`, `customer.state` e `currency` não diferenciam maiúsculas de minúsculas.

## Corpo da requisição

- `external_id` (`string`, obrigatório)

  O identificador do usuário final dono dos pedidos, com até 255 caracteres.

- `orders` (`array de objetos`, obrigatório)

  De 1 a 300 pedidos.

  **Campos de cada pedido:**

  - `id` (`string`, obrigatório)

    O id do pedido no seu sistema, com até 255 caracteres. Ele deve ser único por usuário final.

  - `status` (`enum`, obrigatório)

    `pending`, `paid`, `cancelled`, `refunded`, `partially_refunded` ou `other`. Se o status de origem não corresponder a nenhum desses valores, use `other` e envie o original em `source_status`.

  - `total_cents` (`integer`, obrigatório)

    O total do pedido em centavos, ≥ 0.

  - `placed_at` (`string (RFC 3339)`, obrigatório)

    A data e a hora em que o pedido foi realizado.

  - `updated_at` (`string (RFC 3339)`, obrigatório)

    A data da última alteração do pedido no sistema de origem, e não o horário do envio. O pedido só é gravado se essa data for mais recente que a do registro existente.

  - `items` (`array de objetos`, obrigatório)

    De 1 a 200 itens. Os itens são substituídos integralmente a cada envio.

    **Campos de cada item:**

    - `name` (`string`, obrigatório)

      O nome do produto, com até 500 caracteres. A IA identifica o produto pelo nome ou pelo `sku`.

    - `quantity` (`number`, obrigatório)

      Deve ser maior que 0. Aceita valores fracionários (`1.5` kg).

    - `unit_price_cents` (`integer`, obrigatório)

      O preço unitário em centavos, ≥ 0.

    - `product_id` (`string`)

      O id do produto no seu sistema, o mesmo `id` do produto no [PUT /products](https://docs.diligensai.com.br/referencia/produtos.md). A IA agrupa as vendas por esse id; portanto, um produto renomeado continua sendo contado como um só. É ele que liga a venda ao produto do catálogo: sem ele, a IA não sabe quais produtos do catálogo foram vendidos.

    - `sku` (`string`)

    - `variant_name` (`string`)

      Por exemplo, `M / Azul`.

    - `product_type` (`enum`)

      `physical`, `digital`, `service`, `subscription` ou `other`.

    - `category` (`string`)

    - `brand` (`string`)

    - `unit` (`string`)

      A unidade de medida, por exemplo, `UN` ou `KG`.

    - `discount_cents` (`integer`)

    - `total_cents` (`integer`)

    - `cost_cents` (`integer`)

      O custo unitário. Quando ele é informado em todos os itens, a IA apresenta a margem.

  - `customer` (`objeto`)

    Os dados do comprador. O campo é opcional e, sem ele, o pedido fica sem comprador.

    **Campos do cliente:**

    - `id` (`string`, obrigatório)

      O id do comprador no sistema de origem, com até 255 caracteres.

    - `person_type` (`enum`)

      `individual` (PF) ou `company` (PJ).

    - `name` (`string`)

    - `company_name` (`string`)

    - `document` (`string`)

      O CPF ou o CNPJ. O valor é gravado encriptado.

    - `email` (`string`)

      O valor é gravado encriptado.

    - `phone` (`string`)

      O valor é gravado encriptado.

    - `birth_date` (`string (AAAA-MM-DD)`)

    - `gender` (`string`)

    - `city` (`string`)

    - `state` (`string`)

      A UF.

    - `zip_code` (`string`)

    - `created_at` (`string (RFC 3339)`)

      A data e a hora em que o comprador foi cadastrado no seu sistema.

  - `payments` (`array de objetos`)

    Até 20 pagamentos por pedido. Os pagamentos são substituídos integralmente a cada envio.

    **Campos de cada pagamento:**

    - `method` (`enum`, obrigatório)

      `pix`, `credit_card`, `debit_card`, `boleto`, `cash`, `bank_transfer`, `wallet`, `store_credit` ou `other`.

    - `amount_cents` (`integer`, obrigatório)

    - `status` (`enum`)

      `pending`, `paid`, `failed`, `refunded` ou `other`.

    - `installments` (`integer`)

      De 1 a 99.

    - `gateway` (`string`)

    - `card_brand` (`string`)

    - `paid_at` (`string (RFC 3339)`)

  - `order_number` (`string`)

    O número exibido ao usuário, por exemplo, `#1001`.

  - `source_status` (`string`)

    O status original no seu sistema, por exemplo, `"Em separação"`.

  - `fulfillment_status` (`enum`)

    `unfulfilled`, `ready`, `shipped`, `delivered`, `returned` ou `other`.

  - `channel` (`enum`)

    `online_store`, `marketplace`, `physical_store`, `social`, `whatsapp`, `phone`, `b2b` ou `other`.

  - `marketplace, store_name, salesperson_name` (`string`)

  - `cancel_reason` (`string`)

    O motivo do cancelamento, em texto livre.

  - `cancel_category` (`enum`)

    `customer` (desistência do cliente), `payment_expired` (Pix ou boleto vencido), `payment_declined` (pagamento recusado), `fraud`, `inventory` (falta de estoque) ou `other`.

    Para um pedido que expirou sem pagamento, use `status: "cancelled"` com `cancel_category: "payment_expired"`. Sem a categoria, o pedido cancelado que nunca teve pagamento aprovado e tem um pagamento `pending` ou `failed` também é considerado não pago.

  - `coupon_code, utm_source, utm_medium, utm_campaign` (`string`)

  - `subtotal_cents, discount_cents, shipping_cents, tax_cents, refunded_cents` (`integer`)

  - `fee_cents` (`integer`)

    A taxa do marketplace ou do gateway.

  - `currency` (`string`, padrão: `BRL`)

    O código da moeda, com 3 letras.

  - `shipping_carrier, shipping_service, tracking_code` (`string`)

  - `delivery_city, delivery_state, delivery_zip_code` (`string`)

    `delivery_state` é a UF.

  - `estimated_delivery_at` (`string (RFC 3339)`)

  - `invoice_number` (`string`)

    O número da NF-e.

  - `invoice_key` (`string`)

    A chave da NF-e, com 44 dígitos.

  - `invoice_issued_at, shipped_at, delivered_at` (`string (RFC 3339)`)

  - `paid_at` (`string (RFC 3339)`)

    A data do pagamento. A venda conta nessa data; sem ela, na data do pedido.

  - `cancelled_at` (`string (RFC 3339)`)

    A data do cancelamento. Sem ela, o cancelamento é considerado na data do pedido.

  - `refunded_at` (`string (RFC 3339)`)

    A data do estorno. Sem ela, o estorno é considerado na data do `cancelled_at` e, sem as duas, na data do pedido.

  - `metadata` (`objeto`)

    Um objeto livre, para campos customizados.

> **Quanto mais campos, melhor a análise**
>
> O custo dos itens (`cost_cents`), o canal e a forma de pagamento são os campos que mais contribuem para a análise. Com eles, a IA aborda margem, canal e meio de pagamento, e não apenas faturamento.

Veja a seguir um exemplo de pedido completo, com cliente, itens e dois pagamentos:

```json
{
  "external_id": "loja-123",
  "orders": [
    {
      "id": "PED-1001",
      "order_number": "#1001",
      "status": "paid",
      "fulfillment_status": "shipped",
      "source_status": "Em separação",
      "channel": "marketplace",
      "marketplace": "mercado_livre",
      "salesperson_name": "Ana",
      "subtotal_cents": 15990,
      "discount_cents": 1000,
      "shipping_cents": 2500,
      "fee_cents": 1920,
      "total_cents": 17490,
      "coupon_code": "BLACK10",
      "utm_source": "instagram",
      "utm_campaign": "black-friday",
      "shipping_carrier": "Correios",
      "shipping_service": "SEDEX",
      "tracking_code": "BR123456789BR",
      "delivery_city": "Campinas",
      "delivery_state": "SP",
      "delivery_zip_code": "13010-000",
      "invoice_number": "4521",
      "invoice_key": "35260912345678000190550010000045211000045210",
      "placed_at": "2026-09-20T14:32:00-03:00",
      "paid_at": "2026-09-20T14:33:10-03:00",
      "shipped_at": "2026-09-21T10:00:00-03:00",
      "updated_at": "2026-09-21T10:00:00-03:00",
      "metadata": { "canal_interno": "loja-2" },
      "customer": {
        "id": "CLI-9",
        "person_type": "individual",
        "name": "Maria Souza",
        "document": "12345678909",
        "email": "maria@exemplo.com",
        "phone": "+5519999990000",
        "birth_date": "1990-05-10",
        "city": "Campinas",
        "state": "SP",
        "zip_code": "13010-000",
        "created_at": "2025-01-15T09:00:00-03:00"
      },
      "items": [
        {
          "product_id": "P-10",
          "sku": "CAM-AZ-M",
          "name": "Camiseta básica",
          "variant_name": "M / Azul",
          "product_type": "physical",
          "category": "Vestuário",
          "brand": "Marca X",
          "quantity": 2,
          "unit": "UN",
          "unit_price_cents": 7995,
          "cost_cents": 3500
        }
      ],
      "payments": [
        { "method": "pix", "amount_cents": 7490, "status": "paid", "gateway": "Mercado Pago" },
        { "method": "credit_card", "amount_cents": 10000, "installments": 3, "card_brand": "Visa", "status": "paid" }
      ]
    }
  ]
}
```

## Resposta

- `batch_id` (`string`)

  O identificador do lote. Guarde no seu log: com ele, o suporte localiza o lote se você precisar.

- `received` (`integer`)

  A quantidade de pedidos recebidos no lote. Os pedidos já registrados com um `updated_at` igual ou mais recente são ignorados na gravação, sem erro.

## Erros

| Status | `code` | Quando |
|---|---|---|
| 400 | `COMMON_001` | JSON malformado, tipo incorreto (texto em `total_cents`) ou data fora do formato RFC 3339 |
| 400 | `COMMON_002` | Campo inválido. O `details[].field` informa o caminho, por exemplo, `orders[3].items[0].quantity` |
| 413 | `COMMON_004` | Body acima de 5 MB (reduza o tamanho do lote) |
| 429 | `INGEST_001` | A cota diária de itens da conta acabou, e o lote não entrou (veja [Cota diária](https://docs.diligensai.com.br/guias/limites.md#cota-diaria)) |
| 500 | `COMMON_003` | Erro interno. Reenvie o lote com segurança |
| 503 | `COMMON_006` | Serviço indisponível por um momento, e o lote não foi aceito. Aguarde alguns segundos e reenvie |

Um campo inválido rejeita o lote inteiro. Corrija o campo e reenvie o lote. Os erros gerais (401, 402, 403, 429) estão descritos em [Erros](https://docs.diligensai.com.br/guias/erros.md).

---

# Enviar catálogo de produtos

> Sincroniza o cadastro dos produtos de um usuário final, sem estoque, para a IA opinar sobre um produto antes da primeira venda. A operação é idempotente e aceita lotes de até 500 produtos.

Página: https://docs.diligensai.com.br/referencia/produtos

`PUT https://api.diligensai.com.br/api/v1/products`

Autorização: `X-API-Key: <sua API key>`. Chamada do seu backend, com a API key da conta. Nunca do navegador. Veja [Autenticação](https://docs.diligensai.com.br/guias/autenticacao.md).

Esta rota envia o cadastro dos produtos da loja de um usuário final. **O estoque não entra**, apenas o cadastro. Com o catálogo, a IA conhece o produto antes da primeira venda, e o usuário pode fazer perguntas como "o que acha da garrafa térmica que acabei de cadastrar?", "o preço da pochete está bom?", "quais produtos não venderam nada este mês?" ou "como estão os produtos que lancei?".

A IA responde com base no cadastro (preço, preço "de", custo, categoria, descrição e data do cadastro) e, quando o produto já foi vendido, também com base nos pedidos.

**Requisição**

cURL:

```bash
curl -X PUT "https://api.diligensai.com.br/api/v1/products" \
  -H "X-API-Key: $DILIGENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "loja-123",
    "products": [{
      "id": "P-12",
      "sku": "GAR-TER-500",
      "name": "Garrafa térmica",
      "status": "active",
      "category": "Acessórios",
      "price_cents": 12990,
      "cost_cents": 5000,
      "created_at": "2026-09-28T10:00:00-03:00",
      "updated_at": "2026-09-28T10:00:00-03:00"
    }]
  }'
```

Node.js:

```javascript
const response = await fetch('https://api.diligensai.com.br/api/v1/products', {
  method: 'PUT',
  headers: {
    'X-API-Key': process.env.DILIGENS_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    external_id: 'loja-123',
    products: [{
      id: 'P-12',
      sku: 'GAR-TER-500',
      name: 'Garrafa térmica',
      status: 'active',
      category: 'Acessórios',
      price_cents: 12990,
      cost_cents: 5000,
      created_at: '2026-09-28T10:00:00-03:00',
      updated_at: '2026-09-28T10:00:00-03:00',
    }],
  }),
});
const { batch_id, received } = await response.json();
```

Python:

```python
import os
import requests

response = requests.put(
    "https://api.diligensai.com.br/api/v1/products",
    headers={"X-API-Key": os.environ["DILIGENS_API_KEY"]},
    json={
        "external_id": "loja-123",
        "products": [{
            "id": "P-12",
            "sku": "GAR-TER-500",
            "name": "Garrafa térmica",
            "status": "active",
            "category": "Acessórios",
            "price_cents": 12990,
            "cost_cents": 5000,
            "created_at": "2026-09-28T10:00:00-03:00",
            "updated_at": "2026-09-28T10:00:00-03:00",
        }],
    },
    timeout=30,
)
response.raise_for_status()
```

PHP:

```php
<?php
$body = [
    'external_id' => 'loja-123',
    'products' => [[
        'id' => 'P-12',
        'sku' => 'GAR-TER-500',
        'name' => 'Garrafa térmica',
        'status' => 'active',
        'category' => 'Acessórios',
        'price_cents' => 12990,
        'cost_cents' => 5000,
        'created_at' => '2026-09-28T10:00:00-03:00',
        'updated_at' => '2026-09-28T10:00:00-03:00',
    ]],
];

$ch = curl_init('https://api.diligensai.com.br/api/v1/products');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'X-API-Key: ' . getenv('DILIGENS_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode($body),
]);
$result = json_decode(curl_exec($ch), true);
```

**Resposta**

202:

```json
{ "batch_id": "0199a3c2-7f1e-7b6a-9d1c-2f4e8a6b1c3d", "received": 2 }
```

400:

```json
{
  "status": 400,
  "code": "COMMON_002",
  "message": "Dados inválidos",
  "details": [{ "field": "products[3].price_cents", "message": "Deve ser no mínimo 0" }]
}
```

## Sincronização e webhook

O mesmo endpoint atende aos dois momentos da integração, assim como no [PUT /orders](https://docs.diligensai.com.br/referencia/pedidos.md):

- **Sincronização do catálogo:** Pagine todos os produtos do usuário e envie lotes de até 500, respeitando o [limite de chamadas](https://docs.diligensai.com.br/guias/limites.md).
- **Webhook:** A cada produto criado ou alterado, envie um lote com 1 produto. Quando um produto for excluído no seu sistema, envie-o com `status: "archived"`. Não existe rota de exclusão.

A rota responde com o status **202** assim que o lote é validado, e a gravação acontece em segundo plano, como no [PUT /orders](https://docs.diligensai.com.br/referencia/pedidos.md#gravacao-em-segundo-plano). Não reenvie um lote que recebeu 202.

## Vínculo com os pedidos

O `id` do produto enviado aqui é o mesmo `items[].product_id` dos itens do [PUT /orders](https://docs.diligensai.com.br/referencia/pedidos.md). É esse id que liga as vendas ao produto do catálogo: sem ele nos itens, a IA não sabe quais produtos do catálogo foram vendidos. As duas sincronizações são independentes, e não importa qual chega primeiro.

Cada `id` corresponde ao que você vende com ele. Se o seu sistema tem variações (tamanho, cor) com id próprio, envie cada variação como um produto, com o `variant_name` e o `parent_id` (o produto pai).

> **O preço do catálogo é o de tabela**
>
> O valor efetivamente vendido continua sendo o dos itens do pedido (`unit_price_cents` e `total_cents`): a receita, o preço médio e a margem são sempre calculados a partir dos pedidos. O preço do cadastro serve apenas como informação para a IA.

## Regras

- Um lote tem de 1 a 500 produtos, sempre do mesmo usuário final (`external_id`), e o body tem no máximo 5 MB. Se o usuário final ainda não existir, ele é criado.
- **Valores em centavos**, sempre inteiros. **Datas** em RFC 3339 com fuso. Datas mais de 1 hora no futuro são recusadas com o status 400 `COMMON_002`.
- **Envie sempre o produto completo**, com o estado atual. O cadastro é substituído por inteiro a cada envio, e um campo que não for enviado fica vazio.
- **`updated_at` é obrigatório** e deve ser a data da última alteração do produto no seu sistema, e não o horário do envio. A Diligens só grava o produto se essa data for **mais recente** que a do registro existente; portanto, reenviar não duplica dados. Para corrigir um produto, reenvie-o com um `updated_at` mais recente.
- **Envie sempre o `created_at`**, a data do cadastro no seu sistema. É com ela que a IA sabe que um produto é novo e ainda não teve tempo de vender. Sem ela, o produto não aparece entre os lançamentos.
- **Sem `status`**, o produto entra como `active`.
- **O lote é tudo ou nada:** Se a gravação falhar, nenhum produto do lote é gravado, e a Diligens tenta gravar o lote inteiro de novo. Reenviar também é seguro.

## Corpo da requisição

- `external_id` (`string`, obrigatório)

  O identificador do usuário final dono do catálogo, com até 255 caracteres.

- `products` (`array de objetos`, obrigatório)

  De 1 a 500 produtos.

  **Campos de cada produto:**

  - `id` (`string`, obrigatório)

    O id do produto no seu sistema, com até 255 caracteres. Ele deve ser único por usuário final e é o mesmo `product_id` dos itens dos pedidos.

  - `name` (`string`, obrigatório)

    O nome do produto, com até 500 caracteres.

  - `updated_at` (`string (RFC 3339)`, obrigatório)

    A data da última alteração no seu sistema, e não o horário do envio.

  - `status` (`enum`, padrão: `active`)

    `active`, `inactive`, `draft` ou `archived`.

  - `product_type` (`enum`)

    `physical`, `digital`, `service`, `subscription` ou `other`.

  - `parent_id` (`string`)

    O produto pai das variações, com até 255 caracteres.

  - `sku` (`string`)

    Até 100 caracteres. A IA identifica o produto pelo nome ou pelo `sku`.

  - `gtin` (`string`)

    O EAN (código de barras), apenas com dígitos, até 14.

  - `variant_name` (`string`)

    O nome da variação, com até 255 caracteres, por exemplo, `"M / Azul"`.

  - `description` (`string`)

    A descrição do produto, com até 5.000 caracteres.

  - `category, brand` (`string`)

    Cada campo aceita até 255 caracteres.

  - `unit` (`string`)

    A unidade de venda, com até 20 caracteres, por exemplo, `un` ou `kg`.

  - `price_cents` (`integer`)

    O preço de venda no cadastro, em centavos, ≥ 0.

  - `compare_at_price_cents` (`integer`)

    O preço "de" de uma promoção, em centavos, ≥ 0.

  - `cost_cents` (`integer`)

    O custo unitário, em centavos, ≥ 0.

  - `url` (`string`)

    A URL http(s) da página do produto, com até 2.000 caracteres.

  - `tags` (`array de strings`)

    Até 30 tags, com até 100 caracteres cada.

  - `created_at` (`string (RFC 3339)`)

    A data do cadastro no seu sistema. É com ela que a IA identifica os lançamentos.

Veja a seguir um exemplo com todos os campos de um produto:

```json
{
  "external_id": "loja-123",
  "products": [
    {
      "id": "P-12",
      "parent_id": "P-PAI-12",
      "sku": "GAR-TER-500",
      "gtin": "7891234567895",
      "name": "Garrafa térmica",
      "variant_name": "500 ml / Preta",
      "description": "Aço inox com parede dupla: gelada por 24h e quente por 12h.",
      "status": "active",
      "product_type": "physical",
      "category": "Acessórios",
      "brand": "Trama",
      "unit": "un",
      "price_cents": 12990,
      "compare_at_price_cents": 15990,
      "cost_cents": 5000,
      "url": "https://loja.exemplo.com/garrafa-termica",
      "tags": ["inox", "lançamento"],
      "created_at": "2026-09-28T10:00:00-03:00",
      "updated_at": "2026-09-28T10:00:00-03:00"
    }
  ]
}
```

## Resposta

- `batch_id` (`string`)

  O identificador do lote. Guarde no seu log: com ele, o suporte localiza o lote se você precisar.

- `received` (`integer`)

  A quantidade de produtos recebidos no lote. Os produtos já registrados com um `updated_at` igual ou mais recente são ignorados na gravação, sem erro.

## Erros

| Status | `code` | Quando |
|---|---|---|
| 400 | `COMMON_001` | JSON ou data malformados |
| 400 | `COMMON_002` | Campo inválido, com o caminho em `details[].field`, por exemplo, `products[3].price_cents` |
| 413 | `COMMON_004` | Body acima de 5 MB (reduza o tamanho do lote) |
| 429 | `INGEST_001` | A cota diária de itens da conta acabou, e o lote não entrou (veja [Cota diária](https://docs.diligensai.com.br/guias/limites.md#cota-diaria)) |
| 500 | `COMMON_003` | Erro interno. Reenvie o lote |
| 503 | `COMMON_006` | Serviço indisponível por um momento, e o lote não foi aceito. Aguarde alguns segundos e reenvie |

Um campo inválido rejeita o lote inteiro. Os erros gerais (401, 402, 403, 429) estão descritos em [Erros](https://docs.diligensai.com.br/guias/erros.md).

---

# Enviar contas

> Sincroniza as contas a receber e a pagar de um usuário final, para que o chat responda sobre caixa e vencimentos.

Página: https://docs.diligensai.com.br/referencia/contas

`PUT https://api.diligensai.com.br/api/v1/financial-entries`

Autorização: `X-API-Key: <sua API key>`. Chamada do seu backend, com a API key da conta. Nunca do navegador. Veja [Autenticação](https://docs.diligensai.com.br/guias/autenticacao.md).

Esta rota envia os títulos a receber (clientes que devem ao usuário) e a pagar (fornecedores, contas fixas, impostos) de um usuário final. Cada lançamento corresponde a um título, como uma parcela, um boleto ou uma fatura. Com esses dados, o usuário pode fazer perguntas como "quanto tenho a receber essa semana?", "o que está vencido?", "quanto devo à Distribuidora Azul?" ou "como fica meu caixa no mês que vem?".

Os números são calculados pela Diligens a partir dos lançamentos que você enviou. A IA apenas redige a resposta. O chat conhece apenas os dados que você sincronizou; por isso, uma pergunta sobre um período fora dos vencimentos enviados é respondida com "não há lançamentos sincronizados nesse período".

**Requisição**

cURL:

```bash
curl -X PUT "https://api.diligensai.com.br/api/v1/financial-entries" \
  -H "X-API-Key: $DILIGENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "loja-123",
    "entries": [{
      "kind": "receivable",
      "id": "CR-88120",
      "status": "open",
      "counterparty_name": "Ana Ribeiro",
      "category": "Vendas",
      "amount_cents": 17490,
      "due_date": "2026-10-23",
      "updated_at": "2026-09-22T16:40:00-03:00"
    }]
  }'
```

Node.js:

```javascript
const response = await fetch('https://api.diligensai.com.br/api/v1/financial-entries', {
  method: 'PUT',
  headers: {
    'X-API-Key': process.env.DILIGENS_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    external_id: 'loja-123',
    entries: [{
      kind: 'receivable',
      id: 'CR-88120',
      status: 'open',
      counterparty_name: 'Ana Ribeiro',
      category: 'Vendas',
      amount_cents: 17490,
      due_date: '2026-10-23',
      updated_at: '2026-09-22T16:40:00-03:00',
    }],
  }),
});
const { batch_id, received } = await response.json();
```

Python:

```python
import os
import requests

response = requests.put(
    "https://api.diligensai.com.br/api/v1/financial-entries",
    headers={"X-API-Key": os.environ["DILIGENS_API_KEY"]},
    json={
        "external_id": "loja-123",
        "entries": [{
            "kind": "receivable",
            "id": "CR-88120",
            "status": "open",
            "counterparty_name": "Ana Ribeiro",
            "category": "Vendas",
            "amount_cents": 17490,
            "due_date": "2026-10-23",
            "updated_at": "2026-09-22T16:40:00-03:00",
        }],
    },
    timeout=30,
)
response.raise_for_status()
```

PHP:

```php
<?php
$body = [
    'external_id' => 'loja-123',
    'entries' => [[
        'kind' => 'receivable',
        'id' => 'CR-88120',
        'status' => 'open',
        'counterparty_name' => 'Ana Ribeiro',
        'category' => 'Vendas',
        'amount_cents' => 17490,
        'due_date' => '2026-10-23',
        'updated_at' => '2026-09-22T16:40:00-03:00',
    ]],
];

$ch = curl_init('https://api.diligensai.com.br/api/v1/financial-entries');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'X-API-Key: ' . getenv('DILIGENS_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode($body),
]);
$result = json_decode(curl_exec($ch), true);
```

**Resposta**

202:

```json
{ "batch_id": "0199a3c2-7f1e-7b6a-9d1c-2f4e8a6b1c3d", "received": 2 }
```

400:

```json
{
  "status": 400,
  "code": "COMMON_002",
  "message": "Dados inválidos",
  "details": [{ "field": "entries[3].due_date", "message": "Campo obrigatório" }]
}
```

## Sincronização e webhook

O mesmo endpoint atende aos dois momentos da integração, assim como no [PUT /orders](https://docs.diligensai.com.br/referencia/pedidos.md):

- **Sincronização do histórico:** Envie os títulos em aberto e os pagos dos últimos meses, em lotes de até 200, respeitando o [limite de chamadas](https://docs.diligensai.com.br/guias/limites.md).
- **Webhook:** A cada título criado, pago, baixado ou cancelado, envie um lote com 1 lançamento.

A rota responde com o status **202** assim que o lote é validado, e a gravação acontece em segundo plano, como no [PUT /orders](https://docs.diligensai.com.br/referencia/pedidos.md#gravacao-em-segundo-plano). Não reenvie um lote que recebeu 202.

## Regras

- Um lote tem de 1 a 200 lançamentos, sempre do mesmo usuário final (`external_id`), e o body tem no máximo 5 MB. Se o usuário final ainda não existir, ele é criado.
- **`kind`** indica o lado do título: `receivable` (a receber) ou `payable` (a pagar). O `id` é único por usuário final **e por lado**; portanto, um título a receber e um título a pagar podem ter o mesmo id.
- **Valores em centavos**, sempre inteiros. `amount_cents` é o valor original do título, e `paid_cents` é o valor já pago. Em um título pago parcialmente, o chat considera em aberto apenas o saldo (`amount_cents - paid_cents`).
- **Datas:** `due_date` e `issue_date` usam o formato `AAAA-MM-DD`; `paid_at`, `cancelled_at` e `updated_at` usam RFC 3339 com fuso. `paid_at`, `cancelled_at` e `updated_at` mais de 1 hora no futuro são recusados com o status 400 `COMMON_002`; o `due_date` pode estar no futuro.
- **Envie o `paid_at`** no título pago (`paid` ou `partially_paid`): os totais pagos e o fluxo de caixa realizado contam por ele, e um título pago sem `paid_at` fica de fora deles.
- **Vencido não é status.** Envie `open`, e a Diligens faz o cálculo: um título em aberto com `due_date` anterior à data de hoje (horário de Brasília) é considerado vencido.
- **Envie sempre o lançamento completo**, com o estado atual. **`updated_at` é obrigatório** e deve ser a data da última alteração no sistema de origem, e não o horário do envio. A Diligens só grava o lançamento se essa data for **mais recente** que a do registro existente; portanto, reenviar não duplica dados, e um webhook atrasado não desfaz um estado mais recente. Para corrigir lançamentos já enviados, reenvie-os com um `updated_at` mais recente que o do envio anterior.
- **O lote é tudo ou nada:** Se a gravação falhar, nenhum lançamento do lote é gravado, e a Diligens tenta gravar o lote inteiro de novo. Reenviar também é seguro.

> **Privacidade da contraparte**
>
> O `counterparty_name` aparece nas respostas do chat ("a Ana Ribeiro deve R$ 200,00"). O `counterparty_document` (CPF/CNPJ) é gravado encriptado e **nunca é enviado à IA**. Os pontos, as barras e os traços são removidos.

## Corpo da requisição

- `external_id` (`string`, obrigatório)

  O identificador do usuário final dono dos lançamentos, com até 255 caracteres.

- `entries` (`array de objetos`, obrigatório)

  De 1 a 200 lançamentos.

  **Campos de cada lançamento:**

  - `kind` (`enum`, obrigatório)

    `receivable` (a receber) ou `payable` (a pagar).

  - `id` (`string`, obrigatório)

    O id do título no seu sistema, com até 255 caracteres. Ele deve ser único por usuário final e por lado.

  - `status` (`enum`, obrigatório)

    `open`, `partially_paid`, `paid` ou `cancelled`.

  - `amount_cents` (`integer`, obrigatório)

    O valor original do título em centavos, ≥ 0.

  - `due_date` (`string (AAAA-MM-DD)`, obrigatório)

    A data de vencimento.

  - `updated_at` (`string (RFC 3339)`, obrigatório)

    A data da última alteração no sistema de origem, e não o horário do envio.

  - `description` (`string`)

    A descrição do título, com até 500 caracteres.

  - `category, cost_center` (`string`)

    Cada campo aceita até 100 caracteres. A categoria é o que permite ao chat responder "quanto gasto com fornecedores?".

  - `counterparty_type` (`enum`)

    `individual` ou `company`.

  - `counterparty_name` (`string`)

    O nome da contraparte, com até 255 caracteres. Ele aparece nas respostas do chat.

  - `counterparty_document` (`string`)

    O CPF ou o CNPJ, com até 20 caracteres. O valor é gravado encriptado e nunca é enviado à IA.

  - `document_number` (`string`)

    O número do título, do boleto ou da nota.

  - `invoice_key` (`string`)

    A chave da NF-e, com 44 dígitos.

  - `order_id` (`string`)

    O pedido do `PUT /orders` que originou o recebível.

  - `paid_cents, discount_cents` (`integer`)

  - `interest_cents, fine_cents` (`integer`)

    Os juros e a multa.

  - `currency` (`string`, padrão: `BRL`)

    O código da moeda, com 3 letras.

  - `payment_method` (`enum`)

    `pix`, `boleto`, `credit_card`, `debit_card`, `bank_transfer`, `cash`, `check` ou `other`.

  - `installment_number, installment_count` (`integer`)

    Para a parcela 2 de 3, envie `2` e `3`.

  - `bank_account` (`string`)

  - `issue_date` (`string (AAAA-MM-DD)`)

  - `paid_at` (`string (RFC 3339)`)

    A data do pagamento. Os totais pagos e o fluxo de caixa realizado contam por ela.

  - `cancelled_at` (`string (RFC 3339)`)

  - `metadata` (`objeto`)

    Um objeto livre, para campos customizados.

> **Categoria e contraparte fazem diferença**
>
> Com elas, o chat responde "quanto gasto com fornecedores?" e "quem mais me deve?".

Veja a seguir um exemplo com dois lançamentos, um a pagar e um a receber já pago:

```json
{
  "external_id": "loja-123",
  "entries": [
    {
      "kind": "payable",
      "id": "CP-2026-0931",
      "status": "open",
      "description": "Compra de mercadoria, NF 4521",
      "category": "Fornecedores",
      "cost_center": "Loja centro",
      "counterparty_type": "company",
      "counterparty_name": "Distribuidora Azul Têxtil",
      "counterparty_document": "55.666.777/0001-45",
      "document_number": "4521-2/3",
      "amount_cents": 180000,
      "payment_method": "boleto",
      "installment_number": 2,
      "installment_count": 3,
      "issue_date": "2026-09-01",
      "due_date": "2026-10-31",
      "updated_at": "2026-09-01T10:00:00-03:00"
    },
    {
      "kind": "receivable",
      "id": "CR-88120",
      "status": "paid",
      "counterparty_type": "individual",
      "counterparty_name": "Ana Ribeiro",
      "order_id": "PED-1001",
      "amount_cents": 17490,
      "paid_cents": 17490,
      "payment_method": "pix",
      "due_date": "2026-09-23",
      "paid_at": "2026-09-22T16:40:00-03:00",
      "updated_at": "2026-09-22T16:40:00-03:00"
    }
  ]
}
```

## Resposta

- `batch_id` (`string`)

  O identificador do lote. Guarde no seu log: com ele, o suporte localiza o lote se você precisar.

- `received` (`integer`)

  A quantidade de lançamentos recebidos no lote. Os lançamentos já registrados com um `updated_at` igual ou mais recente são ignorados na gravação, sem erro.

## Erros

| Status | `code` | Quando |
|---|---|---|
| 400 | `COMMON_001` | JSON ou data malformados |
| 400 | `COMMON_002` | Campo inválido, com o caminho em `details[].field`, por exemplo, `entries[3].due_date` |
| 413 | `COMMON_004` | Body acima de 5 MB (reduza o tamanho do lote) |
| 429 | `INGEST_001` | A cota diária de itens da conta acabou, e o lote não entrou (veja [Cota diária](https://docs.diligensai.com.br/guias/limites.md#cota-diaria)) |
| 500 | `COMMON_003` | Erro interno. Reenvie o lote |
| 503 | `COMMON_006` | Serviço indisponível por um momento, e o lote não foi aceito. Aguarde alguns segundos e reenvie |

Um campo inválido rejeita o lote inteiro. Os erros gerais (401, 402, 403, 429) estão descritos em [Erros](https://docs.diligensai.com.br/guias/erros.md).

---

# Apagar usuário final

> Apaga o usuário final e todos os seus dados: histórico, pedidos, compradores, catálogo de produtos e contas. A operação é irreversível.

Página: https://docs.diligensai.com.br/referencia/apagar-usuario

`DELETE https://api.diligensai.com.br/api/v1/external-users/:external_id`

Autorização: `X-API-Key: <sua API key>`. Chamada do seu backend, com a API key da conta. Nunca do navegador. Veja [Autenticação](https://docs.diligensai.com.br/guias/autenticacao.md).

Esta rota apaga o usuário final e **todos** os dados dele: o histórico do chat, os pedidos, os compradores, o catálogo de produtos e as contas a receber e a pagar. Use-a para atender ao pedido de exclusão do titular, conforme a LGPD. Consulte também [Privacidade](https://docs.diligensai.com.br/guias/lgpd.md).

**Requisição**

cURL:

```bash
curl -X DELETE "https://api.diligensai.com.br/api/v1/external-users/loja-123" \
  -H "X-API-Key: $DILIGENS_API_KEY"
```

Node.js:

```javascript
const response = await fetch(
  `https://api.diligensai.com.br/api/v1/external-users/${encodeURIComponent(externalId)}`,
  { method: 'DELETE', headers: { 'X-API-Key': process.env.DILIGENS_API_KEY } },
);
// 404 EXTUSER_001 num retry: já foi apagado
const deleted = response.status === 204 || response.status === 404;
```

Python:

```python
import os
from urllib.parse import quote
import requests

response = requests.delete(
    f"https://api.diligensai.com.br/api/v1/external-users/{quote(external_id, safe='')}",
    headers={"X-API-Key": os.environ["DILIGENS_API_KEY"]},
    timeout=10,
)
# 404 EXTUSER_001 num retry: já foi apagado
deleted = response.status_code in (204, 404)
```

PHP:

```php
<?php
$ch = curl_init('https://api.diligensai.com.br/api/v1/external-users/' . rawurlencode($externalId));
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('DILIGENS_API_KEY')],
]);
curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// 404 EXTUSER_001 num retry: já foi apagado
$deleted = in_array($status, [204, 404], true);
```

**Resposta**

204:

```text
(sem body)
```

404:

```json
{ "status": 404, "code": "EXTUSER_001", "message": "Usuário externo não encontrado" }
```

> **A exclusão é irreversível**
>
> Não há como recuperar os dados de um usuário final apagado.

## Parâmetros de caminho

- `external_id` (`string`, obrigatório)

  O mesmo valor do `POST /chat/token` e do `PUT /orders`, com até 255 caracteres. Codifique-o na URL (`encodeURIComponent`) se ele contiver `/`, espaços ou acentos.

## Resposta

A API responde com o status `204`, sem body.

## Regras

- Se o usuário estiver conversando, a resposta em andamento é cancelada e o token de chat dele deixa de funcionar.
- Os lotes do usuário enviados antes da exclusão e que ainda não tinham sido gravados são descartados: os dados apagados não voltam.
- **A exclusão não bloqueia o `external_id`.** Um novo `POST /chat/token`, `PUT /orders`, `PUT /products` ou `PUT /financial-entries` com o mesmo valor, feito depois da exclusão, cria o usuário novamente, sem dados. Interrompa o envio dos dados dele antes de apagá-lo.
- Uma nova chamada para um usuário já apagado recebe o status 404 `EXTUSER_001`. Em uma nova tentativa (retry), trate o 404 como sucesso.

## Erros

| Status | `code` | Quando |
|---|---|---|
| 400 | `COMMON_002` | `external_id` com mais de 255 caracteres |
| 404 | `EXTUSER_001` | Não existe usuário com esse `external_id` na sua conta (ou ele já foi apagado) |

Os erros gerais (401, 429) estão descritos em [Erros](https://docs.diligensai.com.br/guias/erros.md).
