# API do Chat (IA)

import { Tabs, TabsList, TabsTrigger, TabsContent } from "zudoku/ui/Tabs.js";

A **API do Chat** expõe o **Assistente Fiscal da Brasil NFe** (a nossa IA, com o cérebro fiscal pronto) por HTTP, para você construir a sua própria interface. O cliente manda mensagens em linguagem natural - "emite uma NF-e para o cliente X de R$ 1.500" - e a nossa IA responde, executa a operação e devolve os documentos (DANFE em PDF, XML, cupom HTML).

É a mesma engine do chat do painel, autenticada pelo **token de empresa** (o mesmo do MCP e da REST API).

> **Quer ver funcionando antes de integrar?** Abra a [demo interativa](https://api.brasilnfe.com.br/ai/chat) - o mesmo assistente, sem instalar nada.

## Chat API ou MCP? Qual usar

São dois caminhos diferentes para integrar IA:

| | **API do Chat** (esta página) | [Servidor MCP](/mcp) |
| --- | --- | --- |
| Quem traz o modelo de IA | **Nós** (a nossa IA) | **Você** (o seu LLM/agente) |
| O que você constrói | só a interface (UI) | o agente inteiro |
| Você quer... | usar a nossa IA pronta | conectar a sua IA às nossas ferramentas |
| Transporte | REST (JSON) ou SSE | MCP (JSON-RPC / Streamable HTTP) |

Resumo: se você quer **a nossa IA respondendo o seu usuário**, use a Chat API. Se você já tem uma IA e só quer **dar a ela as ferramentas fiscais**, use o MCP.

## Endpoint base e autenticação

| Recurso | URL |
| --- | --- |
| Base da API | `https://api.brasilnfe.com.br/services/AiChatApi` |

Use o **mesmo token de empresa** da REST API. Envie em um destes cabeçalhos:

```bash
Authorization: Bearer SEU_TOKEN
# ou
Token: SEU_TOKEN
```

O token identifica a empresa: CNPJ, regime tributário, certificado digital e contingência são resolvidos automaticamente a cada turno. O contexto fiscal fica travado na empresa do token - não há risco de cruzar dados entre clientes.

## Fluxo geral

1. **Crie uma sessão** (`POST /sessions`) - recebe um `sessionToken`.
2. **Envie mensagens** para essa sessão (`POST /sessions/{token}/messages`).
3. A IA responde com texto + executa ferramentas (emitir, consultar, baixar) + gera artefatos.
4. **Renderize os artefatos** (PDF/XML/HTML) pelas URLs que vêm na resposta.

A sessão guarda o histórico no servidor - você não precisa reenviar a conversa a cada mensagem, só o `sessionToken`.

## Endpoints

| Método | Rota | Descrição |
| --- | --- | --- |
| `POST` | `/sessions` | Cria uma sessão de chat. Retorna `sessionToken`. |
| `POST` | `/sessions/{token}/messages` | Envia uma mensagem. Modo JSON ou streaming (ver abaixo). |
| `GET` | `/sessions/{token}/messages` | Histórico da conversa. |
| `GET` | `/sessions/{token}/artifacts` | Lista os artefatos gerados na sessão. |
| `GET` | `/sessions/{token}/artifacts/{messageId}/{kind}` | Baixa um artefato inline (`kind` = `pdf`, `xml`, `html`). |
| `GET` | `/sessions/{token}/artifacts/{messageId}/download` | Baixa um anexo (`Content-Disposition: attachment`). |
| `GET` | `/quota` | Saldo de créditos disponível. |

Todos exigem o cabeçalho `Authorization: Bearer SEU_TOKEN`.

## 1. Criar uma sessão

```bash
curl -X POST https://api.brasilnfe.com.br/services/AiChatApi/sessions \
  -H "Authorization: Bearer SEU_TOKEN" \
  -H "Content-Length: 0"
```

> **POST exige `Content-Length`.** Esta chamada não tem corpo, mas alguns servidores retornam `411 Length Required` se o cliente não enviar `Content-Length`. Mande `-H "Content-Length: 0"` (ou um corpo vazio `-d '{}'`).

Resposta:

```json
{
  "sessionToken": "f3a9...c21"
}
```

Guarde o `sessionToken` - ele identifica a conversa nas chamadas seguintes. O provedor e o modelo de IA são definidos pela configuração da conta (igual ao chat do painel) - você não escolhe nem precisa informar nada disso.

## 2. Enviar uma mensagem

O corpo aceita:

| Campo | Tipo | Descrição |
| --- | --- | --- |
| `message` | string | **Obrigatório.** A mensagem do usuário em linguagem natural. |
| `tipoAmbiente` | int | `1` = produção (emissão real), `2` = homologação (teste). Default `2`. |

O **cabeçalho `Accept` decide o formato** da resposta:

- `Accept: application/json` (ou ausente) -> resposta **única e consolidada**.
- `Accept: text/event-stream` -> **streaming SSE**, igual ao chat do painel.

<Tabs>
  <TabsList>
    <TabsTrigger value="json">JSON (consolidado)</TabsTrigger>
    <TabsTrigger value="sse">Streaming (SSE)</TabsTrigger>
  </TabsList>

  <TabsContent value="json">

```bash
curl -X POST https://api.brasilnfe.com.br/services/AiChatApi/sessions/f3a9...c21/messages \
  -H "Authorization: Bearer SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Emite uma NF-e de teste para o cliente X de R$ 1.500", "tipoAmbiente": 2 }'
```

Resposta:

```json
{
  "sessionToken": "f3a9...c21",
  "reply": "Pronto! Emiti a NF-e 1234, autorizada pela SEFAZ.",
  "stopReason": "end_turn",
  "toolCalls": [
    { "name": "nfe_emitir", "ok": true, "summary": "NF-e 1234 autorizada" }
  ],
  "artifacts": [
    {
      "messageId": 88,
      "kind": "pdf",
      "fileName": "DANFE-1234.pdf",
      "url": "/services/AiChatApi/sessions/f3a9...c21/artifacts/88/pdf"
    }
  ],
  "usage": {
    "quotaSource": "credits",
    "quotaUsedPct": 0,
    "balanceBrl": 92.0,
    "balanceCredits": 1840,
    "turnCostBrl": 0.15,
    "turnCostCredits": 3
  },
  "error": null
}
```

> **Sobre o `usage`:** `quotaUsedPct` só é relevante para os modos demo/plano (cota com teto por período); para créditos comprados ele fica sempre `0`. Use **`balanceCredits`/`balanceBrl`** (saldo após o turno) e **`turnCostCredits`/`turnCostBrl`** (quanto este turno custou) para refletir o consumo. No modo streaming (SSE), consulte `GET /quota` para o saldo atualizado.

  </TabsContent>

  <TabsContent value="sse">

```bash
curl -N -X POST https://api.brasilnfe.com.br/services/AiChatApi/sessions/f3a9...c21/messages \
  -H "Authorization: Bearer SEU_TOKEN" \
  -H "Accept: text/event-stream" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Emite uma NF-e de teste para o cliente X de R$ 1.500" }'
```

O servidor envia eventos `Server-Sent Events` conforme a IA processa:

```text
event: text
data: {"delta":"Vou emitir a NF-e..."}

event: tool_call_start
data: {"callId":"c1","name":"nfe_emitir"}

event: tool_result
data: {"callId":"c1","ok":true,"summary":"NF-e 1234 autorizada"}

event: artifact
data: {"messageId":88,"kind":"pdf","fileName":"DANFE-1234.pdf","url":"/services/AiChatApi/sessions/f3a9...c21/artifacts/88/pdf"}

event: usage
data: {"quotaUsedPct":0,"quotaSource":"credits"}

event: done
data: {"reason":"end_turn"}
```

  </TabsContent>
</Tabs>

### Eventos do streaming (SSE)

| Evento | Quando | Payload |
| --- | --- | --- |
| `text` | a IA escreve a resposta | `{ delta }` |
| `tool_call_start` | a IA começa uma ferramenta | `{ callId, name }` |
| `tool_call_delta` | argumentos chegando | `{ callId, delta }` |
| `tool_call_end` | argumentos completos | `{ callId, args }` |
| `tool_result` | a ferramenta terminou | `{ callId, ok, summary }` |
| `artifact` | um documento foi gerado | `{ messageId, kind, fileName, url }` |
| `usage` | fim do turno | `{ quotaUsedPct, quotaSource }` |
| `done` | turno encerrado | `{ reason }` |
| `error` | falha no turno | `{ code, message, retryAfterSeconds }` |
| `session_changed` | a conversa foi reiniciada por inatividade | `{ sessionToken, reason }` |

> Se você receber `session_changed`, passe a usar o novo `sessionToken` nas próximas mensagens.

## 3. Visualização (artefatos)

Quando a IA emite um documento, ela gera um **artefato** binário. Cada artefato vem com `messageId`, `kind` e uma `url` já pronta. Você decide como mostrar:

- **PDF (`kind: "pdf"`)** - o DANFE pronto. Renderize num `<iframe src="...">` ou ofereça download.
- **XML (`kind: "xml"`)** - o XML autorizado. Útil para guardar ou processar.
- **HTML (`kind: "html"`)** - o cupom da NFC-e. Embeda direto.

```bash
curl https://api.brasilnfe.com.br/services/AiChatApi/sessions/f3a9...c21/artifacts/88/pdf \
  -H "Authorization: Bearer SEU_TOKEN" --output danfe.pdf
```

Para listar todos os artefatos de uma sessão (útil ao reabrir a conversa):

```bash
curl https://api.brasilnfe.com.br/services/AiChatApi/sessions/f3a9...c21/artifacts \
  -H "Authorization: Bearer SEU_TOKEN"
```

Além do documento renderizado, a resposta da mensagem traz os dados estruturados (`toolCalls[].summary`, número, status) - você pode montar a sua própria visualização sem depender do nosso PDF.

## 4. Cobrança

Cada turno consome **créditos** da carteira da empresa do token (proporcional ao tamanho da resposta e às ferramentas usadas). Detalhes:

- O acesso via API consome **apenas créditos comprados** - não usa a cota grátis mensal do plano (essa é exclusiva do chat do painel).
- Sem saldo, o turno é recusado com **HTTP 402**. Compre créditos no painel para continuar.
- Consulte o saldo a qualquer momento:

```bash
curl https://api.brasilnfe.com.br/services/AiChatApi/quota \
  -H "Authorization: Bearer SEU_TOKEN"
```

```json
{
  "source": "credits",
  "credits": {
    "balanceBrl": 92.0,
    "balanceCredits": 1840,
    "valuePerCreditBrl": 0.05,
    "expirationMonths": 8
  }
}
```

> **Produção vs homologação:** use `tipoAmbiente: 2` (homologação) enquanto integra - as NF-e de teste não têm valor fiscal. Mude para `tipoAmbiente: 1` só quando for emitir de verdade.

## Códigos de status

| HTTP | `code` | Significado |
| --- | --- | --- |
| `401` | `missing_token` / `invalid_token` | Token ausente ou inválido. |
| `403` | `no_owner` / `session_blocked` | Empresa sem dono vinculado, ou sessão bloqueada. |
| `400` | `missing_message` | O campo `message` não foi enviado. |
| `404` | `session_not_found` / `invalid_session` | Sessão inexistente para este token. |
| `402` | `credits_empty` / `credits_exhausted` | Sem créditos. Compre mais no painel. |
| `429` | `rate_limited` / `global_cap` | Excedeu o rate-limit. Veja `Retry-After`. |
| `409` | `session_stale_empresa` | A empresa mudou; recrie a sessão. |
| `502` | `server_exception` | Falha temporária. Tente novamente. |

No modo JSON, o corpo sempre traz `error: { code, message }` quando há falha; no SSE, vem como evento `error`.

## Boas práticas para produção

- **Uma sessão por conversa.** Reutilize o `sessionToken` enquanto for o mesmo diálogo; crie uma nova sessão para conversas independentes.
- **Trate o 402.** Mostre ao usuário um aviso de "sem créditos" e o link para comprar.
- **Respeite o `Retry-After`** nos `429`.
- **Comece em homologação** (`tipoAmbiente: 2`) e só promova para produção depois de validar o fluxo.
- **Guarde os artefatos** que precisar (PDF/XML) do seu lado - as URLs são escopadas à sessão.
