# Documentation > Complete documentation for Large Language Models --- ## Document: Webhooks URL: /webhooks # Webhooks import { Tabs, TabsList, TabsTrigger, TabsContent } from "zudoku/ui/Tabs.js"; Webhooks são **URLs HTTP do seu servidor** que o Brasil NFe chama automaticamente quando algo acontece nas suas notas. Em vez de você ficar consultando a API em loop, é o Brasil NFe que avisa você - enviando um POST com o payload em JSON. Use webhooks para integrar com **ERP, sistema de logística, CRM, dashboards internos** ou qualquer coisa que precise reagir a eventos fiscais em tempo (quase) real. ## Cadastro pelo painel O cadastro é feito em **[api.brasilnfe.com.br](https://api.brasilnfe.com.br) → Painel → Webhooks**: 1. Clique em **Adicionar webhook**. 2. Informe um **nome** (ex.: "Integração ERP") e a **URL** que vai receber os POSTs (precisa ser HTTPS em produção). 3. Salve. **O secret é gerado uma única vez** - copie e guarde no seu cofre de segredos. Se perder, use **Regerar secret** (a integração antiga deixa de funcionar imediatamente). 4. Em **Empresa → Credenciais**, selecione qual webhook esta empresa específica utiliza. ### Resolução por empresa Para cada disparo, o Brasil NFe escolhe o webhook assim: 1. Se a empresa tem um webhook **explicitamente vinculado** (campo `IdWebhook`), ele é usado. 2. Senão, se existir **exatamente um webhook ativo** sob a mesma conta, ele é usado como fallback. 3. Se houver mais de um ativo e nenhum explicitamente vinculado, **nenhum disparo acontece** - a configuração é ambígua. ## O envelope do payload Cada disparo é um **POST** com `Content-Type: application/json` e o seguinte envelope: ```json { "event": "nfe.lote.finalizado", "deliveryId": "9f86d081884c7d659a2feaa0c55ad015", "timestamp": "2026-04-28T17:32:11.0123456Z", "data": { /* específico do evento - ver Eventos suportados abaixo */ } } ``` > O envelope (`event`, `deliveryId`, `timestamp`, `data`) é sempre **camelCase**. O conteúdo de `data` segue o casing da entidade no domínio: eventos de **lote** (`nfe.lote.*`) usam camelCase; eventos de **documento de entrada** (`documento.entrada.*`) usam **PascalCase** (ver schema de cada evento na seção [Eventos suportados](#eventos-suportados)). | Campo | Descrição | | --- | --- | | `event` | Nome do evento (ex.: `nfe.lote.finalizado`). Veja a [tabela de eventos](#eventos-suportados). | | `deliveryId` | Identificador único da tentativa de entrega. **Use como chave de idempotência** no seu lado. | | `timestamp` | ISO-8601 em UTC, momento em que o disparo foi montado. | | `data` | Carga específica do evento. | ## Headers enviados | Header | Conteúdo | | --- | --- | | `User-Agent` | `BrasilNFe-Webhooks/1.0` | | `X-Webhook-Event` | Nome do evento (mesmo valor de `event` no body). | | `X-Webhook-Delivery` | Mesmo valor de `deliveryId`. **Idêntico em todas as tentativas** do mesmo evento. | | `X-Webhook-Timestamp` | Mesmo valor de `timestamp`. Reflete o momento em que o **disparo original** foi montado, não a tentativa atual. | | `X-Webhook-Signature` | `sha256=` - HMAC-SHA256 do **body bruto** com o secret do webhook. | | `X-Webhook-Attempt` | Número da tentativa atual (`1` no disparo original, `2..5` em retentativas). | ### Headers extras por evento Eventos de **documento de entrada** (`documento.entrada.recebida`, `documento.entrada.cancelada`) carregam dois headers adicionais para facilitar roteamento sem precisar parsear o body: | Header | Conteúdo | | --- | --- | | `X-Document-Model` | Modelo fiscal - `10` (NFS-e), `55` (NF-e), `57` (CT-e). | | `X-Document-Chave` | Chave de acesso (44 dígitos para NF-e/CT-e; código de verificação para NFS-e quando aplicável; pode vir vazio se a nota ainda não tem chave). | ## Verificação da assinatura A assinatura é o **HMAC-SHA256 do corpo bruto da requisição**, codificada em hexadecimal e prefixada por `sha256=`. **Sempre compare em tempo constante** para evitar timing attacks. Node.js PHP Python .NET ```js import crypto from "crypto"; function verificarAssinatura(req, secret) { const body = req.rawBody; const recebida = req.headers["x-webhook-signature"] || ""; const esperada = "sha256=" + crypto.createHmac("sha256", secret).update(body).digest("hex"); const a = Buffer.from(recebida); const b = Buffer.from(esperada); return a.length === b.length && crypto.timingSafeEqual(a, b); } ``` ```php ```python import hmac, hashlib def verificar_assinatura(body: bytes, header_recebido: str, secret: str) -> bool: esperada = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest() return hmac.compare_digest(esperada, header_recebido) ``` ```csharp using System.Security.Cryptography; using System.Text; public static bool VerificarAssinatura(string body, string headerRecebido, string secret) { using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(body)); var esperada = "sha256=" + BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); return CryptographicOperations.FixedTimeEquals( Encoding.UTF8.GetBytes(esperada), Encoding.UTF8.GetBytes(headerRecebido ?? string.Empty)); } ``` > **Importante.** Calcule o HMAC sobre o **body exatamente como recebido** - sem reserializar o JSON. Frameworks que parseiam o body antes de você ler costumam reordenar chaves e quebram a assinatura. No Express, use `express.raw()`; no ASP.NET, leia o stream antes do model binding. ## Idempotência Use o `X-Webhook-Delivery` (= `deliveryId` no body) como **chave de idempotência** com TTL de pelo menos 24h. Em caso de retentativas (veja abaixo) ou raros timeouts dos dois lados, **o mesmo `deliveryId` pode chegar mais de uma vez** - armazene-o e processe apenas a primeira ocorrência. > **A idempotência via `deliveryId` é a defesa primária.** O Brasil NFe garante que o mesmo evento sempre carrega o mesmo `deliveryId`, em todas as tentativas. ### Janela anti-replay (defesa secundária) Como o `X-Webhook-Timestamp` reflete o momento do **disparo original**, e retentativas podem chegar até **~1h12min depois**, use uma janela de **±2 horas** se quiser validar timestamp como camada adicional. A proteção primária contra replay deve ser a idempotência por `deliveryId`. ## Resposta esperada Seu endpoint deve responder **`2xx` em até 15 segundos**. Qualquer outra coisa - erro HTTP (4xx/5xx), timeout, exceção de rede - marca a tentativa como falha. **A entrega é então re-tentada automaticamente** seguindo a tabela de backoff abaixo. Se você precisa de garantia de entrega no seu lado, **devolva 2xx imediatamente ao receber** e processe de forma assíncrona do seu lado (fila interna). É o padrão recomendado. ## Retentativas e backoff O Brasil NFe faz **até 5 tentativas** de entrega para cada evento, com backoff exponencial: | Tentativa | Quando dispara | Header `X-Webhook-Attempt` | | --- | --- | --- | | 1 | Imediatamente, no momento do evento. | `1` | | 2 | 30 segundos após a tentativa 1 falhar. | `2` | | 3 | 2 minutos após a tentativa 2 falhar. | `3` | | 4 | 10 minutos após a tentativa 3 falhar. | `4` | | 5 | 1 hora após a tentativa 4 falhar. | `5` | Tempo total máximo: **~1h12min** entre o evento e a quinta (e última) tentativa. Após a quinta tentativa falhar, o evento é considerado **perdido** - mas todas as tentativas ficam registradas em **Painel → Webhooks → Logs** para auditoria. ### O que se mantém entre tentativas - **`deliveryId`** - idêntico em todas as tentativas. Use para idempotência. - **`event`** - idêntico. - **Body completo** - byte-a-byte idêntico (logo, **a `X-Webhook-Signature` também é idêntica**). - **`X-Webhook-Timestamp`** - reflete o disparo original, não muda. ### O que muda entre tentativas - **`X-Webhook-Attempt`** - incrementa de 1 a 5. - **URL e secret** - usam a configuração **atual** no momento da tentativa. Se você atualizou o webhook entre o disparo original e a retentativa, a próxima tentativa vai para a nova URL com o novo secret. Webhook desativado entre tentativas → não há mais retentativas. ### Eventos de teste não são re-tentados O evento `test.ping` (botão "Testar" no painel) **nunca é re-tentado** - é uma checagem manual e o resultado é mostrado no painel imediatamente. ## Eventos suportados Mais eventos serão adicionados gradualmente. **O nome do evento é estável** - quando um evento entra na lista, o nome não muda. ### `test.ping` Disparado pelo botão **Testar** no painel. **Nunca é re-tentado** (resultado mostrado imediatamente no painel). **Headers extras:** nenhum. **Casing:** camelCase. Campos (`data`) Exemplo JSON | Campo | Tipo | Descrição | | --- | --- | --- | | `test` | boolean | Sempre `true` neste evento. | | `message` | string | Mensagem informativa. | | `sentAt` | datetime | Momento do envio (ISO-8601 em UTC). | ```json { "event": "test.ping", "deliveryId": "9f86d081884c7d659a2feaa0c55ad015", "timestamp": "2026-04-28T17:32:11.0123456Z", "data": { "test": true, "message": "Teste de webhook a partir do BrasilNFe.", "sentAt": "2026-04-28T17:32:11.0123456Z" } } ``` ### `nfe.lote.finalizado` Disparado quando **todas** as notas de um envio em lote via [`/EnviarNotaFiscalLote`](/api#tag/nf-e-e-nfce/post/-enviarnotafiscallote) receberam resposta da SEFAZ (sucesso ou erro). É o evento de fechamento do lote - uma única notificação por lote, independente de quantas notas ele tem. **Headers extras:** nenhum. **Casing:** camelCase. Campos (`data`) Exemplo JSON | Campo | Tipo | Descrição | | --- | --- | --- | | `codLote` | string | Identificador do lote no Brasil NFe. | | `tipoAmbiente` | int | `1` = produção, `2` = homologação. | | `modeloDocumento` | int | `55` (NF-e) ou `65` (NFC-e). | | `status` | int | `4` = lote finalizado com ao menos uma nota emitida; `5` = lote finalizado sem nenhuma nota emitida. | | `qtdTotal` | int | Total de notas no lote. | | `qtdEmitida` | int | Quantas foram autorizadas (`codStatus` 100 ou 150). | | `qtdErro` | int | `qtdTotal - qtdEmitida`. | | `notas[]` | array | Uma entrada por nota do lote (ver schema abaixo). | Cada item de `notas[]`: | Campo | Tipo | Descrição | | --- | --- | --- | | `id` | long | Id interno da nota no Brasil NFe. | | `numero` | long | Número da nota. | | `serie` | int | Série. | | `chaveAcesso` | string | Chave de 44 dígitos (vazio quando a nota não foi autorizada). | | `numeroProtocolo` | string | Protocolo SEFAZ (vazio em caso de erro). | | `codStatus` | int | Código de status SEFAZ (ex.: `100` autorizado, `150` autorizado fora do prazo). | | `dsStatus` | string | Descrição do status SEFAZ. | | `error` | string | null | Mensagem de erro quando a nota não foi autorizada. | ```json { "event": "nfe.lote.finalizado", "deliveryId": "5d41402abc4b2a76b9719d911017c592", "timestamp": "2026-04-28T17:32:11.0123456Z", "data": { "codLote": "L-2026-0042", "tipoAmbiente": 1, "modeloDocumento": 55, "status": 4, "qtdTotal": 2, "qtdEmitida": 2, "qtdErro": 0, "notas": [ { "id": 12345, "numero": 100, "serie": 1, "chaveAcesso": "35260400000000000000550010000001001000000017", "numeroProtocolo": "135260000000001", "codStatus": 100, "dsStatus": "Autorizado o uso da NF-e", "error": null }, { "id": 12346, "numero": 101, "serie": 1, "chaveAcesso": "35260400000000000000550010000001011000000024", "numeroProtocolo": "135260000000002", "codStatus": 100, "dsStatus": "Autorizado o uso da NF-e", "error": null } ] } } ``` ### `documento.entrada.recebida` Disparado quando uma **nova nota de entrada** é detectada para o CNPJ da empresa: NF-e/CT-e via manifestação automática na SEFAZ, ou NFS-e capturada pelo scraping do portal nacional/da prefeitura. Mesmo que a nota já chegue cancelada, o evento de criação é sempre `recebida` - o consumidor identifica pelo campo `Status` do payload. **Headers extras:** `X-Document-Model`, `X-Document-Chave` (ver acima). **Casing:** PascalCase. Campos (`data`) Exemplo JSON | Campo | Tipo | Descrição | | --- | --- | --- | | `Chave` | string | Chave de acesso (44 dígitos para NF-e/CT-e; código de verificação para NFS-e). | | `IdentificadorInterno` | string | Identificador interno do emissor (apenas NFS-e, quando disponível). | | `CodLote` | string | Código do lote (apenas NFS-e). | | `Numero` | long | Número da nota (extraído da chave para NF-e/CT-e). | | `ModeloDocumento` | int | `10` (NFS-e), `55` (NF-e), `57` (CT-e). | | `Valor` | decimal | Valor total da nota. | | `ValorIcms` | decimal | null | Valor de ICMS (quando aplicável). | | `CnpjEmissor` | string | CNPJ/CPF do emissor da nota. | | `NomeEmissor` | string | Razão social/nome do emissor. | | `IeEmissor` | string | null | Inscrição estadual do emissor (apenas NF-e/CT-e). | | `CnpjDestinatario` | string | CNPJ da empresa destinatária (a sua empresa). | | `NomeDestinatario` | string | Razão social da destinatária. | | `NumeroProtocolo` | string | Protocolo de autorização SEFAZ. | | `Cfops` | string | CFOPs presentes nos itens (separados por vírgula). | | `DigestValue` | string | Digest do XML assinado / código de verificação NFS-e. | | `Status` | int | `1` = autorizado, `2` = cancelado, `3` = uso denegado. | | `DtEmissao` | datetime | Data de emissão (ISO-8601). | | `DtRecebimento` | datetime | Quando o Brasil NFe identificou e armazenou a nota. | ```json { "event": "documento.entrada.recebida", "deliveryId": "7d793037a0760186574b0282f2f435e7", "timestamp": "2026-04-28T17:32:11.0123456Z", "data": { "Chave": "35260400000000000000550010000001001000000017", "IdentificadorInterno": null, "CodLote": null, "Numero": 100, "ModeloDocumento": 55, "Valor": 1250.00, "ValorIcms": 225.00, "CnpjEmissor": "00000000000000", "NomeEmissor": "Fornecedor Exemplo LTDA", "IeEmissor": "1234567890123", "CnpjDestinatario": "11111111111111", "NomeDestinatario": "Sua Empresa LTDA", "NumeroProtocolo": "135260000000001", "Cfops": "5102,5405", "DigestValue": "xYz123abc456def789ghi012jkl345mn", "Status": 1, "DtEmissao": "2026-04-28T14:00:00", "DtRecebimento": "2026-04-28T17:30:00" } } ``` ### `documento.entrada.cancelada` Disparado quando uma nota de entrada **previamente recebida** (NF-e modelo 55 ou CT-e modelo 57) é marcada como cancelada - gatilho é o registro do evento SEFAZ **110111** (Cancelamento) associado à chave da nota. > Cancelamentos de NFS-e **não** passam por aqui: o cancelamento de NFS-e do portal nacional vem como nova varredura e dispara `documento.entrada.recebida` com `Status: 2` (se a nota ainda não estava cadastrada). Notas NFS-e que já existiam no Brasil NFe não recebem evento adicional. **Headers extras:** `X-Document-Model`, `X-Document-Chave` (ver acima). **Casing:** PascalCase. O schema dos campos é **idêntico** ao de [`documento.entrada.recebida`](#documentoentradarecebida). A única diferença prática é o campo `Status`, que neste evento sempre vem como `2` (cancelado). Exemplo JSON ```json { "event": "documento.entrada.cancelada", "deliveryId": "098f6bcd4621d373cade4e832627b4f6", "timestamp": "2026-04-29T10:15:42.0123456Z", "data": { "Chave": "35260400000000000000550010000001001000000017", "IdentificadorInterno": null, "CodLote": null, "Numero": 100, "ModeloDocumento": 55, "Valor": 1250.00, "ValorIcms": 225.00, "CnpjEmissor": "00000000000000", "NomeEmissor": "Fornecedor Exemplo LTDA", "IeEmissor": "1234567890123", "CnpjDestinatario": "11111111111111", "NomeDestinatario": "Sua Empresa LTDA", "NumeroProtocolo": "135260000000001", "Cfops": "5102,5405", "DigestValue": "xYz123abc456def789ghi012jkl345mn", "Status": 2, "DtEmissao": "2026-04-28T14:00:00", "DtRecebimento": "2026-04-28T17:30:00" } } ``` ## Boas práticas de segurança - Use uma URL **dedicada** ao webhook (ex.: `/webhooks/brasilnfe`) - não compartilhe com outros endpoints públicos. - **Sempre HTTPS** em produção. Endereços HTTP planos serão chamados, mas o secret e o payload trafegam em claro. - Valide a **assinatura antes** de qualquer parsing custoso ou consulta a banco. - Guarde o secret em **variável de ambiente ou cofre** (Vault, AWS Secrets Manager, etc.), nunca no código. - Se o secret vazou, **regere imediatamente** pelo painel - o secret antigo é invalidado na hora. ## Teste local Para testar sem precisar gerar uma nota real: 1. No painel, abra **Webhooks** e clique no ícone de **avião** (Testar) ao lado do webhook. 2. O Brasil NFe envia um POST com `event: "test.ping"` e payload mínimo. 3. Veja o resultado imediatamente (HTTP status, duração) e o request/response completo em **Logs**. Para desenvolvimento local atrás de NAT, use um túnel como [ngrok](https://ngrok.com/) ou [localtunnel](https://localtunnel.github.io/www/) e cadastre a URL pública gerada. --- ## Document: Suporte Técnico URL: /support # Suporte Técnico Se você tiver alguma dúvida sobre nossa API ou SDKs, entre em contato conosco pelo [WhatsApp](https://wa.me/+5531971685947) (horário comercial) ou envie-nos um e-mail contato@brasilnfe.com.br. Nós responderemos você rapidamente, prometo S2. Estamos aqui para ajudá-lo a se integrar conosco o mais rápido possível. Também adoramos feedback, então não tenha vergonha de compartilhar suas ideias conosco. --- ## Document: Segurança URL: /security # Segurança Sabemos da importância que é a segurança da informação por isso adotamos as melhores praticas do mercado. É efetuado “backup” (copia de segurança) em tempo real, inclusive nos sábados, domingos e feriados nacionais dos arquivos que compõem as bases de dados existentes em nossos servidores. Todos os dados sensíveis são armazenados criptografados. --- ## Document: Rate limit URL: /rate-limit # Rate limit A API aplica um limite de requisições por janela de tempo para proteger o serviço contra abuso e garantir disponibilidade. Quando o limite é atingido, novas requisições recebem `HTTP 429 Too Many Requests` até a janela ser liberada. ## Limite padrão - **60 requisições por 60 segundos** por origem. - Janela deslizante (sliding window): cada requisição é registrada com o seu instante e expira após 60s. - Endpoints específicos podem ter limites próprios - consulte a referência do endpoint. ## Como a origem é identificada A chave de limitação é resolvida automaticamente, do mais específico para o mais genérico: 1. **Empresa** (`audience: "empresa"`) - quando há `Token` válido no header e ele resolve para uma empresa cadastrada. **É o cenário recomendado** - o limite é aplicado por empresa, independentemente do IP de origem. 2. **Token** (`audience: "token"`) - quando há `Token` no header mas ele não resolve para uma empresa (token inválido/desconhecido). O bucket usa um hash do token. 3. **Usuário** (`audience: "user"`) - quando a requisição está autenticada por sessão (painel web), sem `Token`. 4. **IP** (`audience: "ip"`) - fallback quando não há nenhuma das opções acima. Sempre envie o header `Token` para garantir que o limite seja contabilizado por empresa, e não pelo IP de saída do seu servidor (que pode ser compartilhado). ## Headers de resposta Toda resposta da API inclui os headers de telemetria do limite: | Header | Descrição | | --- | --- | | `X-RateLimit-Limit` | Limite máximo da janela atual. | | `X-RateLimit-Remaining` | Quantas requisições ainda podem ser feitas antes do limite. | | `X-RateLimit-Reset` | Timestamp Unix (em segundos, UTC) em que a janela será totalmente liberada. | | `Retry-After` | **Apenas em 429.** Segundos a aguardar antes de tentar novamente. | ## Resposta quando o limite é excedido Status: `429 Too Many Requests` ```json { "error": "rate_limited", "message": "Limite de 60 requisições por 60s atingido para esta empresa. Tente novamente em 12s.", "scope": "Fiscal", "audience": "empresa", "limit": 60, "retryAfterSeconds": 12, "resetAt": "2026-04-28T14:32:18Z" } ``` Headers acompanhando o 429: ```http HTTP/1.1 429 Too Many Requests X-RateLimit-Limit: 60 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1761661938 Retry-After: 12 Content-Type: application/json; charset=utf-8 ``` ## Como tratar o 429 no seu cliente A regra de ouro: **sempre respeite o `Retry-After`**. Não tente reenviar imediatamente - você só vai prolongar o bloqueio. Estratégia recomendada: 1. Ao receber `429`, leia o header `Retry-After` (em segundos). 2. Aguarde esse tempo antes da próxima requisição àquele recurso. 3. Para requisições em lote, considere espaçar emissões (ex.: ao emitir 1000 NF-e, distribua ao longo do minuto em vez de disparar tudo de uma vez). 4. Implemente **backoff exponencial com jitter** caso o `Retry-After` esteja ausente: 1s, 2s, 4s, 8s… com até ±20% de variação aleatória. ### Exemplo (C#) ```csharp async Task EnviarComRetryAsync(HttpClient client, HttpRequestMessage req) { for (int tentativa = 0; tentativa < 3; tentativa++) { var resp = await client.SendAsync(req); if ((int)resp.StatusCode != 429) return resp; var retryAfter = resp.Headers.RetryAfter?.Delta ?? TimeSpan.FromSeconds(5); await Task.Delay(retryAfter); } throw new InvalidOperationException("Rate limit não liberado após 3 tentativas."); } ``` ### Exemplo (Node.js) ```js async function enviarComRetry(url, options, maxTentativas = 3) { for (let i = 0; i < maxTentativas; i++) { const resp = await fetch(url, options); if (resp.status !== 429) return resp; const retryAfter = Number(resp.headers.get("retry-after") ?? 5); await new Promise((r) => setTimeout(r, retryAfter * 1000)); } throw new Error("Rate limit não liberado após múltiplas tentativas."); } ``` ## Boas práticas - **Monitore `X-RateLimit-Remaining`** em respostas de sucesso. Se esse valor estiver consistentemente baixo, distribua melhor a carga. - **Não use múltiplos tokens** para contornar o limite - todos os tokens da mesma empresa caem no mesmo bucket (`audience: "empresa"`). - **Cache local de consultas** (ex.: `Status`, `ConsultarNFe`) reduz chamadas redundantes. - **Webhooks ao invés de polling**: configure [webhooks](/webhooks) para receber atualizações ao invés de consultar a API repetidamente. ## Precisa de um limite maior? Empresas com volume alto podem solicitar um aumento de limite. Entre em contato pelo [suporte](/support) informando: - Token ou CNPJ da empresa. - Volume médio e de pico esperado (req/min). - Tipo de operação (emissão, consulta, eventos, etc.). --- ## Document: Percentual URL: /percentage # Percentual Tratando de porcentagem os retornos/solicitações em nossa API deve seguir de acordo com o seguinte exemplo: 18% –> 18.00; 7.60 –> 7.60. De maneira alguma deve ser enviado 18% –> 0.18; 7.60 –> 0.0760. --- ## Document: Servidor MCP URL: /mcp # Servidor MCP import { Tabs, TabsList, TabsTrigger, TabsContent } from "zudoku/ui/Tabs.js"; O **MCP Server da Brasil NFe** expõe a API fiscal como ferramentas (tools), recursos (resources) e prompts via [Model Context Protocol](https://modelcontextprotocol.io). Qualquer cliente MCP - Claude Desktop, ChatGPT Apps, Cursor, Windsurf, Copilot, Zed, JetBrains AI, Continue.dev - passa a emitir **NF-e, NFC-e, NFS-e, CT-e, MDF-e e DC-e** por linguagem natural, com a mesma autenticação e os mesmos contratos da REST API. Esta página é a referência técnica para desenvolvedores. Para a visão geral e configuração visual, veja [/ai/](https://www.brasilnfe.com.br/ai/). > **Quer ver funcionando antes de integrar?** Abra a [demo interativa](https://api.brasilnfe.com.br/ai/chat) - um chat já conectado ao MCP da Brasil NFe, sem instalar nada. ## Endpoint e discovery | Recurso | URL | | --- | --- | | MCP (JSON-RPC) | `POST https://api.brasilnfe.com.br/services/Mcp` | | MCP (SSE stream) | `GET https://api.brasilnfe.com.br/services/Mcp` | | Encerrar sessão | `DELETE https://api.brasilnfe.com.br/services/Mcp` | | Manifesto MCP | `GET https://api.brasilnfe.com.br/.well-known/mcp` | | Info / status | `GET https://api.brasilnfe.com.br/services/Mcp/info` | **Transport:** Streamable HTTP. **Spec:** `2025-11-25` (versão atual; o `initialize` negocia fallback para `2025-06-18`, `2025-03-26` e `2024-11-05`). ## Autenticação O servidor usa o **mesmo token de empresa** da API REST. Envie em um destes dois cabeçalhos: ```bash Authorization: Bearer SEU_TOKEN # ou Token: SEU_TOKEN ``` O token identifica a empresa: o servidor resolve automaticamente CNPJ, regime tributário, certificado digital e estado de contingência em cada chamada. Cada token tem rate-limit isolado (default **60 req/min**). ## Cabeçalhos de sessão e protocolo | Cabeçalho | Direção | Uso | | --- | --- | --- | | `Mcp-Session-Id` | resp / req | Devolvido no `initialize`, deve ser reenviado em chamadas subsequentes | | `MCP-Protocol-Version` | resp | Versão negociada da spec | | `X-Request-Id` | resp | ID único de cada chamada (auditoria) | | `X-RateLimit-Limit` / `Remaining` / `Reset` | resp | Telemetria do rate-limit | | `Retry-After` | resp (429) | Segundos para retry quando excede rate-limit | ## Métodos JSON-RPC suportados | Método | Função | | --- | --- | | `initialize` | Abre a sessão e negocia versão + capabilities. | | `notifications/initialized` | Confirma o fim do handshake (notificação). | | `ping` | Health-check da sessão. | | `tools/list` · `tools/call` | Lista e executa tools. | | `resources/list` · `resources/templates/list` · `resources/read` | Lista e lê resources. | | `prompts/list` · `prompts/get` | Lista e expande prompts. | | `tasks/list` · `tasks/get` · `tasks/result` · `tasks/cancel` | Execução assíncrona de tools longas (ver *Operações longas: tasks e progress*). | | `completion/complete` | Autocomplete de argumentos (aceito; retorna vazio nesta versão). | | `logging/setLevel` | Ajuste de nível de log (aceito, no-op). | As capabilities anunciadas no `initialize` são `tools`, `resources`, `prompts`, `logging`, `completions` e `tasks`. **Batch JSON-RPC não é suportado** (removido na spec `2025-06-18`): envie uma requisição por vez - um array retorna `-32600`. ## Configuração nos clientes MCP Claude Desktop Cursor Windsurf VS Code Copilot Edite `claude_desktop_config.json` (no macOS: `~/Library/Application Support/Claude/`, no Windows: `%APPDATA%\Claude\`). O Claude Desktop só conecta em servidores locais (stdio), então use o bridge `mcp-remote` (requer Node.js instalado) para alcançar o endpoint HTTP: ```json { "mcpServers": { "brasilnfe": { "command": "npx", "args": [ "mcp-remote", "https://api.brasilnfe.com.br/services/Mcp", "--header", "Authorization:${AUTH_HEADER}" ], "env": { "AUTH_HEADER": "Bearer SEU_TOKEN" } } } } ``` Reinicie o Claude Desktop. As 34 tools aparecem no menu de ferramentas. Crie ou edite `.cursor/mcp.json` no seu projeto (ou em `~/.cursor/mcp.json` para uso global): ```json { "mcpServers": { "brasilnfe": { "url": "https://api.brasilnfe.com.br/services/Mcp", "headers": { "Authorization": "Bearer SEU_TOKEN" } } } } ``` Em `~/.codeium/windsurf/mcp_config.json`: ```json { "mcpServers": { "brasilnfe": { "serverUrl": "https://api.brasilnfe.com.br/services/Mcp", "headers": { "Authorization": "Bearer SEU_TOKEN" } } } } ``` Em `.vscode/mcp.json` (Copilot Chat com MCP habilitado): ```json { "servers": { "brasilnfe": { "type": "http", "url": "https://api.brasilnfe.com.br/services/Mcp", "headers": { "Authorization": "Bearer SEU_TOKEN" } } } } ``` ## Catálogo de tools São **34 tools** organizadas em 8 categorias. Cada uma tem `inputSchema`/`outputSchema` JSON (gerados a partir dos contratos C# da API) e anotações que orientam o cliente MCP. ### Anotações de comportamento | Hint | Significado | | --- | --- | | `readOnlyHint` | A tool não muda estado. Cliente pode chamar sem confirmação. | | `destructiveHint` | Operação irreversível (cancelamento, inutilização). Cliente deve pedir confirmação obrigatória. | | `idempotentHint` | Múltiplas chamadas com mesmo input produzem mesmo resultado. | | `openWorldHint` | Resultado depende de sistemas externos (SEFAZ). | ### NF-e / NFC-e | Tool | Descrição | | --- | --- | | `nfe_emitir` | Emite NF-e (modelo 55) ou NFC-e (modelo 65). | | `nfe_emitir_complementar` | NF-e complementar (finalidade=2) referenciando nota original. | | `nfe_previsualizar` | Pré-visualização do DANFE em PDF, sem enviar à SEFAZ. | ### NFS-e | Tool | Descrição | | --- | --- | | `nfse_emitir` | Emite NFS-e via Portal Nacional ou prefeitura específica. | | `nfse_consultar` | Consulta status e dados de NFS-e por número, série e ambiente. | ### CT-e · MDF-e · DC-e · NF-EnerCom | Tool | Descrição | | --- | --- | | `cte_emitir` | Emite Conhecimento de Transporte Eletrônico. | | `cte_desacordo` | Registra desacordo do tomador (evento tipo 4). Prazo: 45 dias. | | `mdfe_emitir` | Emite MDF-e agrupando vários CT-e/NF-e. | | `mdfe_encerrar` | Encerra MDF-e autorizado (evento tipo 3). | | `dce_emitir` | Emite DC-e (Declaração de Conteúdo) para PF/MEI. | | `nfenercom_gerar_arquivo` | Gera arquivo NF-EnerCom (pré-requisito). | | `nfenercom_emitir` | Emite NF-EnerCom (Energia Comercializada). | ### Eventos fiscais | Tool | Descrição | | --- | --- | | `evento_cancelar` | Cancela NF-e, NFC-e, NFS-e, CT-e, MDF-e ou DC-e. **Destructive.** | | `evento_carta_correcao` | CC-e em NF-e ou CT-e (evento tipo 2). Limite: 20 por nota. | | `evento_manifestar` | Manifestação do destinatário (ciência, confirmação, desconhecimento, não realizada). | | `evento_inutilizar` | Inutiliza faixa de numeração não utilizada. **Destructive, irreversível.** | ### Consultas SEFAZ e cadastros | Tool | Descrição | | --- | --- | | `sefaz_status` | Status do serviço de autorização da SEFAZ por UF e ambiente. | | `cadastro_consultar` | Consulta dados cadastrais na SEFAZ por CPF/CNPJ/IE e UF. | | `cliente_consultar` | Pesquisa unificada de cliente/fornecedor no cadastro local; faz fallback para BrasilAPI (Receita Federal) quando o termo é um CNPJ válido. Suporta paginação. | | `produto_consultar` | Pesquisa produto no cadastro por código, NCM, GTIN/EAN ou descrição. Suporta paginação. | | `tributacao_consultar` | Lista regras de tributação da empresa (CFOP, CSTs, alíquotas ICMS/PIS/COFINS/IPI/IBS/CBS). Suporta paginação. | | `nota_listar` | Lista NF-e/NFC-e/NFS-e emitidas ou recebidas em um intervalo. | | `imposto_calcular` | Simula ICMS, PIS, COFINS, IPI, ST sem emitir nota. | ### Cadastros | Tool | Descrição | | --- | --- | | `cliente_criar` | Cadastra novo cliente (destinatário/fornecedor/transportadora). Recusa duplicata por `CpfCnpj`. | | `cliente_editar` | Atualiza cliente existente. Exige `Id`; envie payload completo (campos omitidos são zerados). | | `produto_criar` | Cadastra novo produto. NCM aceito com 7 ou 8 dígitos (zero-pad automático); EAN/GTIN validado por dígito verificador. | | `produto_editar` | Atualiza produto existente. Exige `Id`; envie payload completo. | | `tributacao_criar` | Cadastra regra de tributação (CFOP + CSTs + alíquotas). `CstIbsCbs` obrigatório (Reforma Tributária). | | `tributacao_editar` | Atualiza regra de tributação existente. Exige `Id`; envie payload completo. | ### Arquivos · Downloads | Tool | Descrição | | --- | --- | | `arquivo_baixar` | XML ou PDF/DANFE de uma nota por chave. Retorna em base64. | | `arquivo_baixar_evento` | Arquivo de evento por chave + sequencial. | | `arquivo_baixar_periodo` | Exporta XMLs/PDFs de um período em ZIP base64 (long-running). | ### Auxiliares | Tool | Descrição | | --- | --- | | `fci_gerar` | Gera FCI (Ficha de Conteúdo de Importação). | | `health` | Health-check da API. | ## Resources Resources são leituras estáveis identificadas por URI no esquema `brasilnfe://`. O agente lê resources antes de tomar decisões (ex: confirmar emitente antes de emitir). | URI | Conteúdo | | --- | --- | | `brasilnfe://empresa/atual` | Snapshot da empresa do token (razão social, CNPJ, IE, regime, contingência). | | `brasilnfe://nfe/{chave}/xml` | XML autorizado de uma NF-e/NFC-e emitida. | | `brasilnfe://nfe/{chave}/danfe` | DANFE em PDF. | | `brasilnfe://nfe-entrada/{chave}/xml` | XML de NF-e recebida (entrada). | | `brasilnfe://nfe-entrada/{chave}/danfe` | DANFE de NF-e recebida. | | `brasilnfe://evento/{chave}/{tipoArquivo}` | XML (1) ou PDF (2) de evento (CC-e, cancelamento, manifestação). | Os resources com placeholders (`{chave}`, `{tipoArquivo}`) são listados em `resources/templates/list`. ## Prompts prontos São 5 fluxos guiados (templates de mensagem) que o cliente MCP exibe como ações de um clique. Cada um valida regras fiscais antes de chamar a tool correspondente. | Prompt | Argumentos | O que faz | | --- | --- | --- | | `emitir_nfe_simples` | `cnpjCliente*`, `descricaoProduto*`, `valor*`, `ambiente`, `naturezaOperacao` | Guia o agente a emitir NF-e modelo 55, lendo `empresa/atual` e validando SEFAZ antes. | | `cancelar_nota` | `chave`, `numeroNFSe`, `justificativa*` | Valida prazo legal e tamanho da justificativa antes de chamar `evento_cancelar`. | | `diagnostico_emissao` | `uf` | Verifica empresa, SEFAZ e contingência. Conclui com PRONTO ou ATENÇÃO. | | `relatorio_periodo` | `dataInicio*`, `dataFim*`, `modeloDocumento` | Lista notas, agrupa por status, identifica top 5 destinatários. | | `carta_correcao` | `chave*`, `correcao*` | Valida o que pode ser corrigido (não permite valores, datas, partes, série). | ## Exemplo end-to-end ### 1. Initialize ```bash curl -X POST "https://api.brasilnfe.com.br/services/Mcp" \ -H "Authorization: Bearer SEU_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-11-25", "capabilities": {}, "clientInfo": { "name": "meu-app", "version": "1.0" } } }' ``` A resposta inclui `serverInfo`, `capabilities` e `instructions`. **Anote o `Mcp-Session-Id`** dos response headers - ele é obrigatório nas próximas chamadas. ### 2. Listar tools ```bash curl -X POST "https://api.brasilnfe.com.br/services/Mcp" \ -H "Authorization: Bearer SEU_TOKEN" \ -H "Mcp-Session-Id: SESSAO_DEVOLVIDA" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }' ``` ### 3. Chamar tool ```bash curl -X POST "https://api.brasilnfe.com.br/services/Mcp" \ -H "Authorization: Bearer SEU_TOKEN" \ -H "Mcp-Session-Id: SESSAO_DEVOLVIDA" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "sefaz_status", "arguments": { "Uf": "SP", "TipoAmbiente": 2 } } }' ``` ### 4. Ler resource ```bash curl -X POST "https://api.brasilnfe.com.br/services/Mcp" \ -H "Authorization: Bearer SEU_TOKEN" \ -H "Mcp-Session-Id: SESSAO_DEVOLVIDA" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 4, "method": "resources/read", "params": { "uri": "brasilnfe://empresa/atual" } }' ``` ## Códigos de erro JSON-RPC | Código | Significado | | --- | --- | | `-32700` | Parse error (JSON inválido) | | `-32600` | Invalid Request (formato JSON-RPC inválido, batch ou corpo vazio) | | `-32601` | Method not found | | `-32602` | Invalid params (parâmetro obrigatório ausente, URI mal formada, etc.) | | `-32603` | Internal error | | `-32001` | Auth required (token ausente) | | `-32002` | Auth invalid (token não identifica a empresa) | | `-32003` | Rate limited (com `data.retryAfterSeconds`) | Os códigos `-32004` (session required) e `-32005` (session expired) são reservados para erros de sessão. ## Operações longas: tasks e progress Tools longas (anotadas como `LongRunning`, ex: `arquivo_baixar_periodo`) podem ser executadas de duas formas. ### Progress por SSE Envie `_meta.progressToken` no `tools/call` e mantenha aberto um `GET /services/Mcp` com o mesmo `Mcp-Session-Id`. O servidor emite `notifications/progress` a cada 5 segundos até a tool concluir. ### Execução assíncrona (tasks) Para não bloquear a chamada, inclua um objeto `task` no `params` do `tools/call`. O servidor registra a operação em background e devolve **imediatamente** um descritor de task; o resultado é buscado depois. - Só tools `LongRunning` aceitam `task`. Em qualquer outra, o campo retorna erro (`taskSupport=forbidden`). - TTL default **600s**, máximo **3600s** (`task.ttl` em ms). Até **50 tasks** simultâneas por empresa. - Status possíveis: `working`, `input_required`, `completed`, `failed`, `cancelled`. Enquanto não-terminal, o descritor traz `pollInterval` (ms). - Mudanças de status também são empurradas por SSE via `notifications/tasks/status`. | Método | Função | | --- | --- | | `tasks/get` | Status atual da task (não bloqueia). | | `tasks/result` | Faz long-poll (até 90s) e devolve o resultado da tool quando a task fica terminal. | | `tasks/list` | Lista as tasks da empresa autenticada. | | `tasks/cancel` | Cancela uma task ainda em execução. | ```json // tools/call assíncrono: inclua "task" no params { "jsonrpc": "2.0", "id": 5, "method": "tools/call", "params": { "name": "arquivo_baixar_periodo", "arguments": { "DataInicio": "2026-01-01", "DataFim": "2026-01-31" }, "task": { "ttl": 900000 } } } ``` A resposta traz `result.task` com `taskId` e `status: "working"`. Para colher o resultado: ```json { "jsonrpc": "2.0", "id": 6, "method": "tasks/result", "params": { "taskId": "SEU_TASK_ID" } } ``` ## Auditoria Cada `tools/call`, `resources/read` e `prompts/get` registra: timestamp UTC, request id, sessão, empresa, método, tool/resource/prompt, latência em ms, sucesso/erro. Os logs ficam acessíveis pelo painel da Brasil NFe. ## Boas práticas para produção 1. **Sempre leia `brasilnfe://empresa/atual` antes da primeira emissão** da sessão para confirmar emitente e regime. 2. **Use o prompt `diagnostico_emissao` antes de operações em massa** - ele valida SEFAZ e contingência. 3. **Default para `tipoAmbiente: 2` (homologação)** em qualquer integração nova; promova para produção (`1`) deliberadamente. 4. **Ajuste o rate-limit no app.config** (`Mcp:RateLimit:RequestsPerMinute`) conforme volume. 5. **Mantenha o log de auditoria do MCP integrado ao seu SIEM** - todo `tools/call` é rastreável por `requestId` + `sessionId`. 6. **Encerre sessões ociosas com `DELETE /services/Mcp`** + `Mcp-Session-Id` para liberar recursos. --- ## Document: Arquivos URL: /files # Arquivos Os retornos/solicitações que possuem arquivos (pdf, xml e excel) são bytes convertidos em BASE64, ou seja, nas solicitações que possui a necessidade de envio de arquivos deve-se converter os bytes do mesmo em BASE64 antes do envio, e para tratar os retornos deve fazer a conversão de BASE64 para bytes. --- ## Document: Introdução URL: /docs # Introdução Esta seção contém informações essenciais para o aproveitamento integral da API de gerenciamento de documentos fiscais no Brasil. Abrange os principais protocolos, métodos de autenticação, padrões de requisição e respostas, além de diretrizes de uso que são fundamentais para desenvolvedores e integradores que desejam trabalhar eficientemente com a API. Consulte os documentos relacionados e os exemplos de código para uma compreensão completa das funcionalidades disponíveis. Esta é a nossa API, é um passo importante para o lançamento do produto que criamos para você, temos orgulho em dizer que estamos realizando um salto gigantesco para o mercado de emissão de documentos fiscais brasileiro. A API é Restful, isso significa que usamos URLs previsíveis e orientados a recursos para operar em larga escala. A própria API fala exclusivamente em JSON, incluindo erros, mas nossas bibliotecas SDK convertem respostas em objetos específicos de linguagem apropriados. --- ## Document: Data e hora URL: /datetime # Data e hora Todas as datas retornadas/solicitações em nossa API estarão no formato UTC ISO no horário de Brasília -03:00. Isso significa que não importa onde você esteja, você sempre receberá os horários UTC e deverá lidar com as conversões para o horário local sempre que necessário. Exemplo: 2021-01-01T23:59:00 --- ## Document: Autenticação URL: /autentication # Autenticação Para as solicitações o corpo da requisição [body] deve ser enviado no formato JSON com o header **Content-Type** definido para **application/json**. A autenticação é realizada através do cabeçalho HTTP (HTTP headers). É necessário o envio do TOKEN da sua empresa, que pode ser obtido pelo painel ou via API. ```bash -H "Token: seu_token" -H "Content-Type: application/json" ``` **Mantenha as credenciais de acesso em segurança.** Nunca publique as credenciais de acesso no código fonte do site, aplicativo ou software onde o usuário possa ter fácil acesso. **Para aplicações web e aplicativos mobile iOS/Android** recomendamos que o processo de emissão seja realizado no servidor (back-end). No código fonte do aplicativo deve possuir somente as solicitações, enquanto o processo deve ser realizado em seu servidor. --- ## Document: Visão Geral URL: /ai-docs # Visão Geral A Brasil NFe oferece **dois caminhos** para integrar inteligência artificial à emissão fiscal. Eles são complementares - escolha pelo que você já tem. ## Qual usar? | | [Servidor MCP](/mcp) | [API do Chat](/ai-chat) | | --- | --- | --- | | Quem traz o modelo de IA | **Você** (o seu LLM/agente) | **Nós** (a nossa IA) | | O que você constrói | o agente inteiro | só a interface (UI) | | Você quer... | conectar a sua IA às nossas ferramentas | usar a nossa IA pronta | | Transporte | MCP (JSON-RPC / Streamable HTTP) | REST (JSON) ou SSE | | Autenticação | token de empresa (Bearer / Token) | token de empresa (Bearer / Token) | Resumo: se você **já tem uma IA** e só quer dar a ela as ferramentas fiscais, use o **MCP**. Se você quer **a nossa IA respondendo o seu usuário** e vai montar só a tela, use a **API do Chat**. ## Servidor MCP Expõe a API fiscal como ferramentas (tools), recursos e prompts via [Model Context Protocol](https://modelcontextprotocol.io). Qualquer cliente MCP - Claude, ChatGPT, Cursor, Copilot - passa a emitir NF-e, NFC-e, NFS-e, CT-e, MDF-e e DC-e por linguagem natural. - **[Servidor MCP (IA)](/mcp)** - referência técnica completa. ## API do Chat A nossa IA (com o cérebro fiscal pronto) por HTTP, para você construir a sua própria interface. - **[Guia de integração](/ai-chat)** - conceitos, fluxo, streaming e cobrança. - **[Referência da API](/api-chat-ia)** - endpoints, schemas e playground interativo. > **Quer ver funcionando?** Abra a [demo interativa](https://api.brasilnfe.com.br/ai/chat) - o assistente já conectado, sem instalar nada. --- ## Document: API do Chat (IA) URL: /ai-chat # 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. JSON (consolidado) Streaming (SSE) ```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. ```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"} ``` ### 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 `