{"openapi":"3.1.0","paths":{"/v1/nfse":{"post":{"description":"**Com uma chave `ext_sk_live_`, esta chamada emite uma NFS-e real**, e desfazê-la exige cancelamento formal, que tem prazo. Para ensaiar a integração inteira use `ext_sk_test_`: mesmo corpo, mesmo `202`, mesmo polling, com `livemode: false`.\n\nDevolve `202` na hora — a emissão é assíncrona. O `Location` traz a rota de consulta e o `Retry-After`, o intervalo de polling. O `202` diz ACEITA, não autorizada: acompanhe o `status` até `issued` ou `failed`. Recusas de cadastro chegam aqui, de forma síncrona, antes do `202`.\n\n### O que o teste não prova\n\nA nota de teste sai na **produção restrita** do Emissor Nacional: outro ambiente, com cadastro municipal próprio. Ela é emitida de verdade e pode falhar — e uma rejeição de lá é informação sobre o ambiente, não sobre o seu payload. Fora do alcance do teste ficam:\n\n- Diferenças de cadastro municipal entre os dois ambientes: um município pode estar habilitado num e não no outro, ou exigir dados diferentes em cada um. Nota aceita no teste pode ser rejeitada em produção, e o contrário também.\n- Se o código de tributação bate com o serviço que a sua `description` narra: ele é resolvido pelo CNAE, nunca pelo texto. Os dois ambientes aceitam a nota do mesmo jeito.\n- Recusa de cancelamento por janela: a nota de teste é apagada em 7 dias, então ensaiar a recusa exige emitir no fim do mês e cancelar nos primeiros dias do seguinte. Fora disso a nota já expirou, e a resposta é `404`, não `nfse.cancel_window_closed`.\n- `cancellation.status: \"failed\"` é permanente em produção — um novo `POST /cancel` responde `409 nfse.cancel_in_progress`, sem saída pela API —, mas libera nova tentativa no modo de teste. Não use o teste para prever a produção neste ponto.\n- A RECUSA do cancelamento chega diferente: no teste, `cancellation.error.message` traz o motivo do SEFIN; em produção é sempre a genérica \"não recebemos a confirmação\", porque lá a incerteza é real. Não faça parsing da mensagem — trate pelo `cancellation.status`.","operationId":"PublicNfseController_create","parameters":[{"name":"Idempotency-Key","in":"header","description":"Chave escolhida por você para tornar a emissão segura de repetir. **Opcional, e altamente recomendada** em qualquer cliente com retry automático. Use uma chave nova por nota.\n\n- **Mesmo corpo + mesma chave** devolve a **primeira resposta**, sem emitir de novo — é o que torna o retry de rede seguro.\n- **Corpo diferente com a mesma chave** é `409 idempotency_key_reuse`, por toda a janela de 24h: a chave identifica UMA emissão.\n- **Mesmo corpo, primeira ainda em voo**, é `409 idempotency_key_in_use`, que é **repetível**: aguarde e consulte antes de reenviar.\n- **Janela de 24h.** Passada, a chave é esquecida e o mesmo corpo emite uma segunda nota.\n\n**Sem o header não há replay.** A API ainda barra duplicata evidente — mesmo valor e mesma descrição em menos de um minuto — com `409 duplicate_suspected`, mas essa rede é frouxa: não pega o retry que chega depois do minuto nem duas chamadas simultâneas, e recusa duas notas legitimamente iguais no mesmo minuto.\n\n**Melhor ainda: mande `reference`.** É a única proteção que **não expira** — sendo único por empresa, a segunda emissão com a mesma referência é `409 nfse.reference_in_use`, trazendo o `id` da original. Os dois se somam: a `Idempotency-Key` REPETE a resposta no retry imediato; o `reference` RECUSA a segunda nota para sempre.","required":false,"schema":{"type":"string","example":"a3f1c9e2-7b64-4d18-9f02-5c8e1d7a6b30"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNfseDto"}}}},"responses":{"202":{"description":"Nota aceita e enfileirada, `status` `queued`. Uma repetição atendida pela idempotência devolve este mesmo corpo, com o status que a nota original tiver no momento.","headers":{"Location":{"description":"Rota de consulta da nota recém-aceita (`/v1/nfse/{id}`). É por aqui que o polling acompanha o status até `issued` ou `failed`.","schema":{"type":"string"}},"Retry-After":{"description":"Segundos a esperar antes da próxima tentativa. Obedeça a este número em vez de escolher um intervalo: é ele que mantém o cliente dentro do limite por chave.","schema":{"type":"integer"}},"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NfseResponseDto"}}}},"400":{"description":"Payload inválido: campo mal formatado, ausente ou fora do domínio aceito. Corrigir e reenviar resolve.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"invalid_request","code":"nfse.customer_document_invalid","message":"O documento do tomador deve ter 11 dígitos (CPF) ou 14 (CNPJ), apenas números.","param":"customer.document","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"401":{"description":"Chave de API ausente, mal formada, revogada ou inexistente. Nenhuma nota é criada.","headers":{"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"authentication_error","code":"unauthorized","message":"Chave de API inválida ou revogada.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"403":{"description":"A chave é válida mas não pode fazer isto: falta o escopo exigido, ou a API pública está desligada para a empresa.","headers":{"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"permission_error","code":"feature_not_enabled","message":"A API não está habilitada para esta empresa. Fale com o suporte para solicitar acesso.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"409":{"description":"Conflito com uma requisição anterior — mesma `Idempotency-Key` ainda em voo, mesma `Idempotency-Key` com outro corpo, `reference` já usada, ou nota equivalente criada há menos de um minuto. Consulte antes de reenviar.","headers":{"Retry-After":{"description":"Segundos a esperar antes da próxima tentativa. Obedeça a este número em vez de escolher um intervalo: é ele que mantém o cliente dentro do limite por chave.","schema":{"type":"integer"}},"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"invalid_request","code":"duplicate_suspected","message":"Uma nota idêntica foi criada há menos de 60 segundos. Se esta é uma segunda nota intencional, reenvie com o header Idempotency-Key próprio, ou com um reference diferente em cada nota.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"422":{"description":"A requisição está bem formada mas não pode prosseguir: pendência de cadastro da empresa, ou município que exige o tomador. Reenviar sem mudar nada não resolve. (A `Idempotency-Key` reaproveitada com outro corpo saiu daqui e virou `409`: ela é conflito de estado da chave, não corpo improcessável.)","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"invalid_request","code":"nfse.customer_required","message":"A prefeitura deste município exige a identificação do tomador na nota. Envie `customer.document` com o CPF (11 dígitos) ou o CNPJ (14) de quem contratou o serviço.","param":"customer.document","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"429":{"description":"Limite de requisições por chave excedido. Aguarde o que o header `Retry-After` indicar.","headers":{"Retry-After":{"description":"Segundos a esperar antes da próxima tentativa. Obedeça a este número em vez de escolher um intervalo: é ele que mantém o cliente dentro do limite por chave.","schema":{"type":"integer"}},"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"rate_limit_error","code":"rate_limit_exceeded","message":"Limite de requisições excedido. Tente novamente em instantes.","retryable":true,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"500":{"description":"Falha nossa. `retryable` é `true`: repita com a MESMA `Idempotency-Key` para não duplicar a nota, e cite o `request_id` se persistir.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"api_error","code":"internal_error","message":"Erro interno. Tente novamente ou contate o suporte com o request_id.","retryable":true,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}}},"summary":"Emitir NFS-e","tags":["NFS-e"]},"get":{"description":"Encontra a nota pelo `reference` que **você** enviou na emissão — o caminho de volta quando o `id` se perde.\n\nDevolve um envelope de lista com no máximo uma nota, já que `reference` é único por empresa dentro de cada modo. Sem resultado, `200` com `data` vazio — nunca `404`. Como toda leitura desta API, é escopada pelo MODO da chave.","operationId":"PublicNfseController_findByReference","parameters":[{"name":"reference","required":true,"in":"query","description":"O `reference` que você enviou na emissão. **Obrigatório** — esta rota é busca por referência, não listagem: sem ele a resposta é `400 invalid_request`.\n\nComo `reference` é único por empresa dentro de cada modo, o resultado tem no máximo uma nota. Sem resultado, é `200` com `data` vazio.","schema":{"maxLength":64,"example":"INV-2026-0042","type":"string"}}],"responses":{"200":{"description":"Envelope de lista. `data` traz a nota encontrada, ou vem vazio.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NfseListResponseDto"}}}},"400":{"description":"Payload inválido: campo mal formatado, ausente ou fora do domínio aceito. Corrigir e reenviar resolve.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"invalid_request","code":"nfse.customer_document_invalid","message":"O documento do tomador deve ter 11 dígitos (CPF) ou 14 (CNPJ), apenas números.","param":"customer.document","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"401":{"description":"Chave de API ausente, mal formada, revogada ou inexistente. Nenhuma nota é criada.","headers":{"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"authentication_error","code":"unauthorized","message":"Chave de API inválida ou revogada.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"403":{"description":"A chave é válida mas não pode fazer isto: falta o escopo exigido, ou a API pública está desligada para a empresa.","headers":{"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"permission_error","code":"feature_not_enabled","message":"A API não está habilitada para esta empresa. Fale com o suporte para solicitar acesso.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"429":{"description":"Limite de requisições por chave excedido. Aguarde o que o header `Retry-After` indicar.","headers":{"Retry-After":{"description":"Segundos a esperar antes da próxima tentativa. Obedeça a este número em vez de escolher um intervalo: é ele que mantém o cliente dentro do limite por chave.","schema":{"type":"integer"}},"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"rate_limit_error","code":"rate_limit_exceeded","message":"Limite de requisições excedido. Tente novamente em instantes.","retryable":true,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"500":{"description":"Falha nossa. `retryable` é `true`: repita com a MESMA `Idempotency-Key` para não duplicar a nota, e cite o `request_id` se persistir.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"api_error","code":"internal_error","message":"Erro interno. Tente novamente ou contate o suporte com o request_id.","retryable":true,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}}},"summary":"Buscar NFS-e por referência","tags":["NFS-e"]}},"/v1/nfse/{id}/cancel":{"post":{"description":"**Cancelamento é definitivo**, e com `ext_sk_live_` a nota deixa de valer como documento fiscal.\n\nSó cancela nota emitida **no mês corrente**: uma de 30/08 pode ser cancelada até 31/08, e em 01/09 não mais. Leia o instante exato em `cancelable_until`, não recalcule a regra. Fora da janela, o suporte ainda pode cancelar.\n\nA resposta é `202`: acompanhe o objeto `cancellation`. Ele é `pending` até o desfecho, `succeeded` quando o `status` chega a `canceled`, e `failed` quando **não recebemos a confirmação** — aí nunca reemita, acione o suporte para conferir o estado da nota.","operationId":"PublicNfseController_cancel","parameters":[{"name":"id","required":true,"in":"path","description":"Id da nota, com ou sem o prefixo `nfse_`. O UUID cru funciona; um prefixo de outro tipo de recurso é `400`, não `404`.","schema":{"example":"nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelNfseDto"}}}},"responses":{"202":{"description":"Pedido aceito. `cancellation.status` é `pending`; consulte a nota até ele virar `succeeded` ou `failed`.","headers":{"Location":{"description":"Rota de consulta da nota recém-aceita (`/v1/nfse/{id}`). É por aqui que o polling acompanha o status até `issued` ou `failed`.","schema":{"type":"string"}},"Retry-After":{"description":"Segundos a esperar antes da próxima tentativa. Obedeça a este número em vez de escolher um intervalo: é ele que mantém o cliente dentro do limite por chave.","schema":{"type":"integer"}},"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NfseResponseDto"}}}},"400":{"description":"Payload inválido: campo mal formatado, ausente ou fora do domínio aceito. Corrigir e reenviar resolve.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"invalid_request","code":"nfse.customer_document_invalid","message":"O documento do tomador deve ter 11 dígitos (CPF) ou 14 (CNPJ), apenas números.","param":"customer.document","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"401":{"description":"Chave de API ausente, mal formada, revogada ou inexistente. Nenhuma nota é criada.","headers":{"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"authentication_error","code":"unauthorized","message":"Chave de API inválida ou revogada.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"403":{"description":"A chave é válida mas não pode fazer isto: falta o escopo exigido, ou a API pública está desligada para a empresa.","headers":{"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"permission_error","code":"feature_not_enabled","message":"A API não está habilitada para esta empresa. Fale com o suporte para solicitar acesso.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"404":{"description":"Não existe nota com este id para a empresa da chave. Mesmo corpo para id inexistente e para nota de outra empresa.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"}}}},"409":{"description":"A nota não está em estado de ser cancelada: já cancelada (`nfse.already_canceled`), com pedido em andamento (`nfse.cancel_in_progress`) ou não cancelável por esta API (`nfse.not_cancelable`). Nenhum dos três se repete.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"}}}},"422":{"description":"A nota é de um mês anterior (`nfse.cancel_window_closed`). Pela API, o cancelamento só vale dentro do mês da emissão.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"}}}},"429":{"description":"Limite de requisições por chave excedido. Aguarde o que o header `Retry-After` indicar.","headers":{"Retry-After":{"description":"Segundos a esperar antes da próxima tentativa. Obedeça a este número em vez de escolher um intervalo: é ele que mantém o cliente dentro do limite por chave.","schema":{"type":"integer"}},"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"rate_limit_error","code":"rate_limit_exceeded","message":"Limite de requisições excedido. Tente novamente em instantes.","retryable":true,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"500":{"description":"Falha nossa. `retryable` é `true`: repita com a MESMA `Idempotency-Key` para não duplicar a nota, e cite o `request_id` se persistir.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"api_error","code":"internal_error","message":"Erro interno. Tente novamente ou contate o suporte com o request_id.","retryable":true,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}}},"summary":"Cancelar NFS-e","tags":["NFS-e"]}},"/v1/nfse/{id}":{"get":{"description":"Destino do `Location` do `202`. Consulte até o `status` chegar a `issued` ou `failed`, respeitando o `Retry-After`.\n\nEscopada pelo MODO da chave: a de teste enxerga só notas de teste, e a de produção só notas reais. O que não é do modo responde `404`, igual a um id inexistente.","operationId":"PublicNfseController_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"Id da nota, com ou sem o prefixo `nfse_`. O UUID cru funciona; um prefixo de outro tipo de recurso é `400`, não `404`.","schema":{"example":"nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f","type":"string"}}],"responses":{"200":{"description":"Estado atual da nota.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NfseResponseDto"}}}},"400":{"description":"Payload inválido: campo mal formatado, ausente ou fora do domínio aceito. Corrigir e reenviar resolve.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"invalid_request","code":"nfse.customer_document_invalid","message":"O documento do tomador deve ter 11 dígitos (CPF) ou 14 (CNPJ), apenas números.","param":"customer.document","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"401":{"description":"Chave de API ausente, mal formada, revogada ou inexistente. Nenhuma nota é criada.","headers":{"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"authentication_error","code":"unauthorized","message":"Chave de API inválida ou revogada.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"403":{"description":"A chave é válida mas não pode fazer isto: falta o escopo exigido, ou a API pública está desligada para a empresa.","headers":{"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"permission_error","code":"feature_not_enabled","message":"A API não está habilitada para esta empresa. Fale com o suporte para solicitar acesso.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"404":{"description":"Não existe nota com este id para a empresa da chave. Mesmo corpo para id inexistente e para nota de outra empresa.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"}}}},"429":{"description":"Limite de requisições por chave excedido. Aguarde o que o header `Retry-After` indicar.","headers":{"Retry-After":{"description":"Segundos a esperar antes da próxima tentativa. Obedeça a este número em vez de escolher um intervalo: é ele que mantém o cliente dentro do limite por chave.","schema":{"type":"integer"}},"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"rate_limit_error","code":"rate_limit_exceeded","message":"Limite de requisições excedido. Tente novamente em instantes.","retryable":true,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"500":{"description":"Falha nossa. `retryable` é `true`: repita com a MESMA `Idempotency-Key` para não duplicar a nota, e cite o `request_id` se persistir.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"api_error","code":"internal_error","message":"Erro interno. Tente novamente ou contate o suporte com o request_id.","retryable":true,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}}},"summary":"Consultar NFS-e","tags":["NFS-e"]}},"/v1/nfse/{id}/pdf":{"get":{"description":"Devolve os BYTES do PDF no corpo, nunca um redirect para URL assinada. Aceita a chave de API **ou** o token temporário que o `links.pdf` da consulta já traz na query: ele vale 15 minutos, serve só esta nota, e é o que faz o link abrir para quem não tem a chave. Só existe depois de `issued`; antes disso, `409 nfse.not_issued`. Vale nos dois modos — em `livemode: false` o PDF vem carimbado **sem validade jurídica**.","operationId":"PublicNfseController_downloadPdf","parameters":[{"name":"id","required":true,"in":"path","description":"Id da nota, com ou sem o prefixo `nfse_`. O UUID cru funciona; um prefixo de outro tipo de recurso é `400`, não `404`.","schema":{"example":"nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f","type":"string"}}],"responses":{"200":{"description":"PDF da nota. `Content-Disposition: attachment`, com nome derivado do id.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}}},"400":{"description":"Payload inválido: campo mal formatado, ausente ou fora do domínio aceito. Corrigir e reenviar resolve.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"invalid_request","code":"nfse.customer_document_invalid","message":"O documento do tomador deve ter 11 dígitos (CPF) ou 14 (CNPJ), apenas números.","param":"customer.document","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"401":{"description":"Chave de API ausente, mal formada, revogada ou inexistente. Nenhuma nota é criada.","headers":{"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"authentication_error","code":"unauthorized","message":"Chave de API inválida ou revogada.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"403":{"description":"A chave é válida mas não pode fazer isto: falta o escopo exigido, ou a API pública está desligada para a empresa.","headers":{"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"permission_error","code":"feature_not_enabled","message":"A API não está habilitada para esta empresa. Fale com o suporte para solicitar acesso.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"404":{"description":"Não existe nota com este id para a empresa da chave.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"}}}},"409":{"description":"A nota existe, mas ainda não foi autorizada (`nfse.not_issued`). Espere o `Retry-After` e consulte o status antes de tentar de novo.","headers":{"Retry-After":{"description":"Segundos a esperar antes da próxima tentativa. Obedeça a este número em vez de escolher um intervalo: é ele que mantém o cliente dentro do limite por chave.","schema":{"type":"integer"}},"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"}}}},"429":{"description":"Limite de requisições por chave excedido. Aguarde o que o header `Retry-After` indicar.","headers":{"Retry-After":{"description":"Segundos a esperar antes da próxima tentativa. Obedeça a este número em vez de escolher um intervalo: é ele que mantém o cliente dentro do limite por chave.","schema":{"type":"integer"}},"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"rate_limit_error","code":"rate_limit_exceeded","message":"Limite de requisições excedido. Tente novamente em instantes.","retryable":true,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"500":{"description":"Falha nossa. `retryable` é `true`: repita com a MESMA `Idempotency-Key` para não duplicar a nota, e cite o `request_id` se persistir.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"api_error","code":"internal_error","message":"Erro interno. Tente novamente ou contate o suporte com o request_id.","retryable":true,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}}},"summary":"Baixar o PDF da nota","tags":["NFS-e"]}},"/v1/nfse/{id}/xml":{"get":{"description":"O XML autorizado — é ele o documento fiscal; o PDF é a representação impressa. Mesma forma do PDF: só depois de `issued`, e vale nos dois modos. Em `livemode: false` ele traz `tpAmb: 2`, a marca de que a nota não tem validade fiscal.","operationId":"PublicNfseController_downloadXml","parameters":[{"name":"id","required":true,"in":"path","description":"Id da nota, com ou sem o prefixo `nfse_`. O UUID cru funciona; um prefixo de outro tipo de recurso é `400`, não `404`.","schema":{"example":"nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f","type":"string"}}],"responses":{"200":{"description":"XML autorizado, sem reprocessamento. `Content-Disposition: attachment`, com nome derivado do id.","content":{"application/xml":{"schema":{"type":"string"}}},"headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}}},"400":{"description":"Payload inválido: campo mal formatado, ausente ou fora do domínio aceito. Corrigir e reenviar resolve.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"invalid_request","code":"nfse.customer_document_invalid","message":"O documento do tomador deve ter 11 dígitos (CPF) ou 14 (CNPJ), apenas números.","param":"customer.document","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"401":{"description":"Chave de API ausente, mal formada, revogada ou inexistente. Nenhuma nota é criada.","headers":{"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"authentication_error","code":"unauthorized","message":"Chave de API inválida ou revogada.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"403":{"description":"A chave é válida mas não pode fazer isto: falta o escopo exigido, ou a API pública está desligada para a empresa.","headers":{"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"permission_error","code":"feature_not_enabled","message":"A API não está habilitada para esta empresa. Fale com o suporte para solicitar acesso.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"404":{"description":"Não existe nota com este id para a empresa da chave.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"}}}},"409":{"description":"A nota existe mas ainda não foi autorizada (`nfse.not_issued`) — não há XML para baixar.","headers":{"Retry-After":{"description":"Segundos a esperar antes da próxima tentativa. Obedeça a este número em vez de escolher um intervalo: é ele que mantém o cliente dentro do limite por chave.","schema":{"type":"integer"}},"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"}}}},"429":{"description":"Limite de requisições por chave excedido. Aguarde o que o header `Retry-After` indicar.","headers":{"Retry-After":{"description":"Segundos a esperar antes da próxima tentativa. Obedeça a este número em vez de escolher um intervalo: é ele que mantém o cliente dentro do limite por chave.","schema":{"type":"integer"}},"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"rate_limit_error","code":"rate_limit_exceeded","message":"Limite de requisições excedido. Tente novamente em instantes.","retryable":true,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"500":{"description":"Falha nossa. `retryable` é `true`: repita com a MESMA `Idempotency-Key` para não duplicar a nota, e cite o `request_id` se persistir.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"api_error","code":"internal_error","message":"Erro interno. Tente novamente ou contate o suporte com o request_id.","retryable":true,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}}},"summary":"Baixar o XML autorizado","tags":["NFS-e"]}},"/v1/activities":{"get":{"description":"Os valores de `cnae` e `service_code` que o `POST /v1/nfse` aceita desta empresa — e só eles: **toda linha desta lista é emitível**.\n\nUse `is_default` para saber se o `cnae` sozinho basta: sendo `false`, mande o par `cnae` + `service_code` para chegar naquela linha. A nota que não manda nenhum dos dois sai na linha que tem `is_main` e `is_default` ao mesmo tempo.\n\nUma atividade do seu cadastro na Receita Federal pode **não aparecer aqui**. Isso não significa que o seu cadastro esteja errado: a atividade ainda não está liberada para emissão, quase sempre por pendência de cadastro do nosso lado. Fale com o suporte.\n\nSem filtro por query string e sem paginação — a lista inteira vem numa resposta só.","operationId":"PublicActivitiesController_list","parameters":[],"responses":{"200":{"description":"Envelope de lista. `data` vem vazio quando nenhuma atividade da empresa está liberada para emissão — resposta legítima, não erro.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivityListResponseDto"}}}},"401":{"description":"Chave de API ausente, mal formada, revogada ou inexistente. Nenhuma nota é criada.","headers":{"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"authentication_error","code":"unauthorized","message":"Chave de API inválida ou revogada.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"403":{"description":"A chave é válida mas não pode fazer isto: falta o escopo exigido, ou a API pública está desligada para a empresa.","headers":{"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"permission_error","code":"feature_not_enabled","message":"A API não está habilitada para esta empresa. Fale com o suporte para solicitar acesso.","retryable":false,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"429":{"description":"Limite de requisições por chave excedido. Aguarde o que o header `Retry-After` indicar.","headers":{"Retry-After":{"description":"Segundos a esperar antes da próxima tentativa. Obedeça a este número em vez de escolher um intervalo: é ele que mantém o cliente dentro do limite por chave.","schema":{"type":"integer"}},"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"rate_limit_error","code":"rate_limit_exceeded","message":"Limite de requisições excedido. Tente novamente em instantes.","retryable":true,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}},"500":{"description":"Falha nossa. `retryable` é `true`: repita com a MESMA `Idempotency-Key` para não duplicar a nota, e cite o `request_id` se persistir.","headers":{"RateLimit-Limit":{"description":"Requisições que esta chave pode fazer por minuto no balde desta rota — um para leitura (`GET`), outro para escrita. É PISO garantido, não teto: dimensione por ele.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Quantas ainda cabem na janela corrente, no mesmo balde.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a cota voltar INTEIRA. Não é quando repetir: para isso existe o `Retry-After` do `429`, que marca a primeira vaga e por construção nunca é maior que este número.","schema":{"type":"integer"}},"Request-Id":{"description":"Identificador desta requisição. Cite-o ao abrir chamado — é por ele que achamos a chamada do seu lado no nosso log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiErrorDto"},"example":{"error":{"type":"api_error","code":"internal_error","message":"Erro interno. Tente novamente ou contate o suporte com o request_id.","retryable":true,"request_id":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"}}}}}},"summary":"Listar as atividades em que a empresa pode emitir","tags":["NFS-e"]}}},"info":{"title":"API de NFS-e — Ext Contabilidade","description":"Emissão de NFS-e para empresas da Ext Contabilidade. REST, JSON, valores em\ncentavos. A emissão é assíncrona: `202` e depois polling, com um envelope de\nerro único em toda falha.\n\n## Início rápido\n\n1. `POST /v1/nfse` com `amount_in_cents` e `description`. Mande também\n   `reference` — o id da fatura no seu sistema —, que é por onde você\n   reencontra a nota se o `id` se perder.\n   Emitindo fora da atividade **principal** da empresa, mande `cnae` (7\n   dígitos) — e `service_code` (6) junto, se aquele CNAE tiver mais de uma\n   linha. Omitidos, a nota sai na principal.\n2. Guarde o `id` do `202`. Ele significa ACEITA, não autorizada.\n3. Consulte o header `Location` no intervalo do `Retry-After` até o `status`\n   virar um desfecho.\n4. Em `issued`, baixe `GET /v1/nfse/{id}/pdf` e `GET /v1/nfse/{id}/xml`.\n\nOs dois tropeços mais comuns da primeira integração: `amount_in_cents` é em\nCENTAVOS (`R$ 1.250,50` = `125050`), e `failed` e `indeterminate` pedem ações\nopostas.\n\n## Autenticação\n\n`Authorization: Bearer ext_sk_...`\n\nNão há auto-contratação: quem libera a empresa é o suporte\n(`suporte@extcontabilidade.com.br`), e antes disso toda rota responde\n`403 feature_not_enabled`. Liberada, as chaves saem em\n[Minha conta → API](https://extcontabilidade.com.br/app/user/my-api), exibidas\n**uma única vez**.\n\n## Teste e produção\n\nA chave carrega o modo, e ele volta em `livemode` em toda resposta — inclusive\nno envelope de erro:\n\n- `ext_sk_test_...` — a nota é emitida de verdade, na **produção restrita** do\n  Emissor Nacional: o ambiente do Sistema Nacional da NFS-e sem validade\n  fiscal. Expira em 7 dias, não é cobrada e não consome sequencial da\n  produção. Pode terminar `failed` — o `status` vem do SEFIN, o sistema\n  nacional que autoriza as notas, e a rejeição chega em `error.message` com o\n  código `E0xxx`.\n- `ext_sk_live_...` — documento fiscal já na primeira chamada. Desfazer exige\n  cancelamento formal, que tem prazo.\n\nO código é o mesmo nos dois: integre com a chave de teste e troque a variável\nde ambiente para ir a produção. Os modos não se enxergam — o que não é do modo\nda chave responde `404`.\n\nO modo de teste é MAIS exigente, de propósito: ele recusa\n`nfse.municipal_registration_missing` e `nfse.nbs_invalid`, pendências de\ncadastro que a emissão real aceita para o SEFIN rejeitar depois. Em troca não\nprova tudo — o que fica fora do alcance dele está em `POST /v1/nfse`.\n\n## Ciclo de vida da nota\n\nO `202` diz ACEITA, não autorizada. Consulte o `Location` até chegar a um\nDESFECHO — nem todo `status` é um:\n\n| `status` | Desfecho? | O que fazer |\n| --- | --- | --- |\n| `queued` | não | Aceita, ainda não enviada. Continue consultando. |\n| `processing` | não | Envio em curso. **Transitório** — nunca o trate como desfecho. |\n| `issued` | sim | Autorizada. `access_key` e `links` existem: baixe PDF e XML. |\n| `failed` | sim | Recusada, e **nenhum documento fiscal existe**. Corrija o que `error` aponta e emita de novo: é seguro. |\n| `indeterminate` | sim | **Pode existir NFS-e autorizada** sem registro aqui. **Nunca reemita** — acione o suporte. |\n| `canceled` | sim | Cancelamento confirmado. Nota em cancelamento ainda aparece como `issued`. |\n\nReemitir uma nota já autorizada gera **documento fiscal em duplicidade**, que\nsó se desfaz com cancelamento formal — e cancelamento tem prazo.\n\n```js\nswitch (nfse.status) {\n  case 'queued':\n  case 'processing':\n    return agendarNovaConsulta(nfse);  // ainda não é desfecho\n\n  case 'issued':\n    return guardar(nfse);\n\n  case 'failed':\n    return corrigirEReemitir(nfse);    // não há documento: é seguro\n\n  case 'indeterminate':\n    return abrirChamado(nfse);         // pode existir: NÃO reemitir\n\n  default:\n    return abrirChamado(nfse);         // status desconhecido: NÃO reemitir\n}\n```\n\nO `default` é a parte que importa: status novo pode aparecer, e o desfecho\nseguro para um desconhecido é sempre não reemitir.\n\n## Erros\n\nToda falha sai no mesmo envelope: `type` (família), `code` (detalhe),\n`message`, `param`, `retryable` e `request_id` — cite o `request_id` ao abrir\nchamado. Trate pelo `code`, nunca pelo status HTTP: o mesmo `422` cobre\nsituações muito diferentes.\n\n`code` é uma lista **aberta**: código novo nasce quando uma recusa genérica\nganha nome próprio, e acrescentar um não é breaking change — por isso o seu\n`switch` precisa de um ramo `default`. Código já publicado nunca é renomeado\nnem muda de significado. `type` é **fechado**: são as famílias do `enum` no\nschema, e uma nova só entra com versão nova da API.\n\nO catálogo completo está na seção **Erros**, ao fim desta referência.\n\n## Limites\n\n**60 requisições de escrita e 300 de leitura por 60 segundos, por chave**, em baldes\nSEPARADOS: o `POST` da emissão gasta de um, as consultas e os downloads do\noutro — **o polling não gasta a cota de emissão**. São piso garantido, não\nteto.\n\nToda resposta traz `RateLimit-Limit`, `RateLimit-Remaining` e `RateLimit-Reset`;\nexcedido o limite, `429` com `Retry-After`. Corpo de até 10 MB.\n\nNo `Retry-After: 3` que a API sugere, cada nota em voo gasta\n20 leituras por minuto: até **15 notas simultâneas** cabem\nobedecendo à dica ao pé da letra. Acima disso, espace o polling.","version":"1.0.0","contact":{"name":"Suporte Ext Contabilidade","url":"https://extcontabilidade.com.br/devs","email":"suporte@extcontabilidade.com.br"}},"tags":[{"name":"NFS-e","description":"Emissão, consulta, cancelamento e download de notas fiscais de serviço. A emissão é assíncrona: o `202` diz que a nota foi aceita, não que foi autorizada."},{"name":"Erros","description":"Todo `code` que a API emite hoje, com o status HTTP e o que fazer em cada\num. O envelope e a regra de tratamento estão em **Erros**, na introdução.\n\n| `code` | HTTP | O que fazer |\n| --- | --- | --- |\n| `unauthorized` | 401 | Chave ausente, inválida ou revogada. |\n| `feature_not_enabled` | 403 | A empresa não tem a API liberada. Fale com o suporte. |\n| `insufficient_scope` | 403 | A chave não tem o escopo da rota. Gere outra com o escopo certo. |\n| `company.issuer_not_enabled` | 422 | Emissão automática desligada para a empresa. |\n| `company.issuer_not_configured` | 422 | Emissor Nacional não configurado. |\n| `company.issuer_not_approved` | 422 | A empresa ainda não foi liberada para emitir. Acione o suporte. |\n| `company.certificate_missing` | 422 | Certificado ausente ou vencido no cofre. |\n| `nfse.cnae_missing_ctribnac` | 422 | O CNAE principal não tem código de tributação mapeado. Acione o suporte. |\n| `nfse.cnae_missing_nbs` | 422 | O CNAE principal não tem código NBS mapeado. Acione o suporte. |\n| `nfse.municipality_not_covered` | 422 | Cidade/UF do cadastro não resolvem para código IBGE. |\n| `nfse.cnae_not_allowed` | 422 | O `cnae` enviado não está no cadastro da empresa na Receita, ou a linha padrão dele está bloqueada. Mande outro, ou omita o campo para sair na atividade principal. |\n| `nfse.cnae_without_default` | 422 | O `cnae` tem linha no catálogo, mas nenhuma é a padrão. Mande o `service_code` da linha que você quer, ou acione o suporte. |\n| `nfse.service_code_unknown` | 422 | O `service_code` não existe no catálogo. Ele tem **6** dígitos (`010501`) — o clássico é mandar aí o CNAE, que tem 7. |\n| `nfse.service_code_not_allowed` | 422 | O `service_code` existe, mas não está entre as linhas da empresa. Mandando o par, `cnae` e `service_code` precisam ser da MESMA linha. |\n| `nfse.nbs_invalid` | 422 | **Só em `livemode: false`.** O código NBS do CNAE não consta da tabela oficial. Em produção a nota é aceita e o SEFIN rejeita depois (E0316). |\n| `nfse.municipal_registration_missing` | 422 | **Só em `livemode: false`.** O município exige a Inscrição Municipal do prestador e ela não está cadastrada. Em produção a nota é aceita e o SEFIN rejeita depois (E0120). |\n| `nfse.customer_document_invalid` | 400 | CPF/CNPJ com máscara ou fora de 11/14 dígitos. |\n| `nfse.customer_required` | 422 | O município do prestador exige a identificação do tomador e a nota veio sem `customer.document`. Vale nos dois modos — **nos demais municípios, nota sem tomador continua válida**. |\n| `nfse.customer_name_required` | 422 | Tomador **pessoa física** (CPF) sem `customer.name`: o nome é impresso na nota e não pode ficar vazio, e não há cadastro público de nome de PF de onde tirá-lo. Em CNPJ é opcional — ali o nome sai do cadastro da Receita. |\n| `nfse.export_incomplete` | 422 | Falta campo obrigatório da exportação. A mensagem lista todos os ausentes. |\n| `nfse.country_unsupported` | 422 | País do tomador fora da Tabela de Países (ISO alpha-2). |\n| `nfse.currency_unsupported` | 422 | Moeda fora da lista aceita. Use a sigla ISO (USD, EUR, GBP). |\n| `nfse.foreign_amount_invalid` | 422 | Valor na moeda estrangeira ausente, zero ou negativo. |\n| `nfse.customer_type_conflict` | 422 | Campo incompatível com o customer.type declarado. |\n| `nfse.cancel_window_closed` | 422 | Nota de mês anterior: pela API, só dentro do mês da emissão. Fale com o suporte — ainda pode ser possível. |\n| `nfse.not_cancelable` | 409 | Estado incompatível: nota não autorizada, substituída, ou anexada manualmente. Só nota autorizada por esta API é cancelável por aqui. |\n| `nfse.already_canceled` | 409 | Esta nota já foi cancelada — nada a fazer. |\n| `nfse.cancel_in_progress` | 409 | Já há um pedido de cancelamento em andamento. **Não repita o POST**: o cancelamento é irreversível, e o segundo pedido seria rejeitado. Consulte a nota para acompanhar. |\n| `nfse.not_issued` | 409 | Pediu PDF ou XML de nota ainda não autorizada. **Repetível quando a nota está `queued` ou `processing`** — ali o envelope vem com `retryable: true` e `Retry-After`. Em `failed` e `indeterminate` vem `retryable: false`: o arquivo não nasce sozinho. |\n| `nfse.reference_in_use` | 409 | Já existe nota desta empresa com esse `reference`, e a mensagem traz o `id` dela. É a idempotência do `reference` funcionando. Para uma segunda nota intencional, use outra referência. |\n| `duplicate_suspected` | 409 | Requisição idêntica há menos de 60s. Reenvie com `Idempotency-Key` própria, ou com `reference` diferente, se a segunda nota é intencional. |\n| `idempotency_key_in_use` | 409 | A primeira requisição com essa chave, e com o MESMO corpo, ainda está em voo. **Repetível.** |\n| `idempotency_key_reuse` | 409 | Mesma `Idempotency-Key` com corpo diferente, em qualquer momento das 24h da chave. Use uma chave nova. |\n| `invalid_request` | 400 | Validação do corpo: campo obrigatório ausente, tipo errado ou campo desconhecido (o clássico é `amount` em vez de `amount_in_cents`). `message` traz a primeira causa; `errors[]`, todas. |\n| `invalid_id` | 400 | O id não é um UUID (com ou sem o prefixo `nfse_`). |\n| `not_found` | 404 | Não há nota com esse id para a empresa da chave. |\n| `route_not_found` | 404 | Rota inexistente sob `/v1`. |\n| `malformed_json` | 400 | Corpo não é JSON válido. |\n| `payload_too_large` | 413 | Corpo acima de 10 MB. |\n| `unsupported_media_type` | 415 | Falta `Content-Type: application/json`. |\n| `rate_limit_exceeded` | 429 | Estourou o limite. Espere o `Retry-After`. **Repetível.** |\n| `internal_error` | 500 | Falha nossa, não mapeada. Cite o `request_id` ao abrir chamado. **Repetível.** |\n| `service_unavailable` | 503 | Indisponibilidade temporária nossa. **Repetível.** |\n\nAs linhas marcadas como repetíveis são as únicas com `retryable: true`, e\n`nfse.not_issued` é CONDICIONAL, pelo status da nota. Não deduza `retryable`\ndo `code`: leia o campo do envelope e obedeça ao `Retry-After`."}],"servers":[{"url":"https://api.extcontabilidade.com.br"}],"components":{"securitySchemes":{"apiKey":{"scheme":"bearer","bearerFormat":"ext_sk","type":"http","description":"Chave de API da empresa (`ext_sk_...`)."}},"schemas":{"CreateNfseCustomerAddressDto":{"type":"object","properties":{"street":{"type":"string","description":"Logradouro do tomador no exterior (`xLgr` da DPS).","maxLength":255,"example":"Portland Street"},"number":{"type":"string","description":"Número do endereço (`nro` da DPS).","maxLength":60,"example":"175"},"district":{"type":"string","description":"Bairro ou distrito (`xBairro` da DPS). Obrigatório pelo leiaute, mesmo onde o endereço local não usa bairro.","maxLength":60,"example":"Back Bay"},"city":{"type":"string","description":"Cidade (`xCidade` da DPS).","maxLength":60,"example":"Boston"},"state":{"type":"string","description":"Estado, província ou região (`xEstProvReg` da DPS). Texto livre — não é a sigla de UF brasileira.","maxLength":60,"example":"MA"},"postal_code":{"type":"string","description":"Código postal no formato do país de destino (`cEndPost` da DPS). Formato livre: não é validado como CEP brasileiro.","maxLength":20,"example":"02114"},"country":{"type":"string","description":"País do tomador em **ISO 3166-1 alpha-2** — dois caracteres, como `US`, `PT` ou `MZ`. Vai para `endExt/cPais` e para `tribMun/cPaisResult`, o país em que o resultado do serviço se verifica. `BR` não é aceito: nota com tomador no Brasil não é exportação.","pattern":"^[A-Za-z]{2}$","example":"US"}},"required":["street","number","district","city","state","postal_code","country"]},"CreateNfseCustomerDto":{"type":"object","properties":{"type":{"type":"string","description":"Onde está o tomador. `br` (padrão) é o comportamento de sempre — identificação por CPF/CNPJ, operação doméstica tributável. `foreign` faz a nota sair como **exportação de serviço**: sem ISS, com o grupo de comércio exterior, e exigindo `customer.address` completo, `customer.tax_id` (ou `customer.tax_id_absence_reason`) e `foreign_amount`.\n\nOmitir o campo é o mesmo que `br`. Nenhum payload que funciona hoje muda de comportamento.","enum":["br","foreign"],"default":"br","example":"foreign"},"document":{"type":"string","description":"CPF (11 dígitos) ou CNPJ (14), **apenas dígitos** — máscara com pontos, barra ou traço é RECUSADA com 400, não limpa.","pattern":"^\\d{11}$|^\\d{14}$","example":"12345678000190"},"name":{"type":"string","description":"Nome do tomador. **Obrigatório quando `customer.document` é um CPF** — o nome é impresso na nota, não há cadastro público de nome de pessoa física, e sem ele a nota é `422 nfse.customer_name_required`. Em CNPJ é opcional: vence o nome do cadastro da Receita, e este campo é o **fallback** se a consulta não resolver.","maxLength":300,"example":"Maria Souza"},"tax_id":{"type":"string","description":"Número de identificação fiscal do tomador no país dele (`NIF`). Exportação exige **este campo ou** `customer.tax_id_absence_reason` — nunca os dois vazios.","maxLength":40,"example":"98-7654321"},"tax_id_absence_reason":{"type":"string","description":"Motivo de o tomador não ter identificação fiscal informada. `exempt` = dispensado; `not_required` = não exigência no país dele. Use quando não houver `customer.tax_id`.","enum":["exempt","not_required"],"example":"not_required"},"address":{"description":"Endereço do tomador **no exterior**, completo. Obrigatório quando `customer.type` é `foreign`, e recusado quando é `br` — no caminho doméstico o endereço vem do cadastro da Receita, não do payload.","allOf":[{"$ref":"#/components/schemas/CreateNfseCustomerAddressDto"}]}}},"CreateNfseForeignAmountDto":{"type":"object","properties":{"currency":{"type":"string","description":"Sigla ISO de três letras da moeda do faturamento — `USD`, `EUR`, `GBP`. **Não** é o código numérico do BACEN: a tradução é nossa. Moeda fora da lista aceita é `422 nfse.currency_unsupported`, com as siglas no corpo do erro.","pattern":"^[A-Za-z]{3}$","example":"EUR"},"amount_in_cents":{"type":"integer","description":"Valor na moeda estrangeira **em centavos**, número inteiro. 3.600,00 se envia como `360000`.\n\nDuas casas decimais para qualquer moeda, inclusive as que não têm centavo (iene) ou têm três casas (dinar kuwaitiano): é o que a DPS recebe, e prometer outra precisão seria prometer o que a emissão não carrega.","minimum":1,"example":360000}},"required":["currency","amount_in_cents"]},"CreateNfseDto":{"type":"object","properties":{"amount_in_cents":{"type":"integer","description":"Valor total do serviço **em centavos**, inteiro. R$ 1.250,50 se envia como `125050` — nunca `1250.50`, que é recusado com 400 em vez de virar R$ 12,50 em silêncio. Mínimo `100` (**R$ 1,00**), máximo `9007199254740991`. É o valor que vai para a nota, sem retenções.","minimum":100,"maximum":9007199254740991,"example":125050},"description":{"type":"string","description":"Descrição do serviço, impressa na nota. Entre 1 e 2000 caracteres — o teto é da nota fiscal, não nosso.\n\nSai como enviado, com UMA exceção: os marcadores `{{data}}`, `{{mes}}`, `{{mes_extenso}}`, `{{mes_anterior}}`, `{{mes_anterior_extenso}}` e `{{ano}}` são substituídos na emissão, resolvidos na data em que a chamada é recebida. Texto sem `{{` passa intacto.","minLength":1,"maxLength":2000,"example":"Desenvolvimento de software sob encomenda — competência 07/2026"},"customer":{"description":"Tomador do serviço. Omitir o objeto inteiro emite uma nota **sem tomador**, que é válida.\n\n**Alguns municípios exigem o tomador**: neles, a nota sem `customer.document` é recusada na hora com `422 nfse.customer_required`, antes do `202`. Nos demais, nota sem tomador passa normalmente.\n\n**Tomador no exterior é exportação de serviço**, e sai por aqui: mande `customer.type: \"foreign\"` com `customer.address` completo, a identificação fiscal (`customer.tax_id` ou `customer.tax_id_absence_reason`) e `foreign_amount`. Sem o `type`, o tomador é tratado como brasileiro — omitir `customer` continua significando *nota sem tomador*, e **não** *tomador no exterior*.","allOf":[{"$ref":"#/components/schemas/CreateNfseCustomerDto"}]},"foreign_amount":{"description":"Valor da nota na moeda do faturamento. Obrigatório quando `customer.type` é `foreign`; recusado fora da exportação.\n\n**Não substitui `amount_in_cents`.** Os dois são exigidos: o valor em reais é o que vai para a nota e para a apuração, e a API **não** converte câmbio — a taxa é decisão sua.","allOf":[{"$ref":"#/components/schemas/CreateNfseForeignAmountDto"}]},"reference":{"type":"string","description":"Seu identificador para esta nota — tipicamente o id da fatura no seu sistema. **Opcional, e a forma mais confiável de reencontrar a nota**: o `id` que devolvemos se perde se o seu processo morrer antes de gravá-lo, e a `Idempotency-Key` é esquecida em 24h.\n\n- **Único por empresa**, dentro de cada modo. Reenviar o mesmo é `409 nfse.reference_in_use`, com o `id` da nota que já existe na mensagem — o retry nunca vira nota duplicada, **sem prazo de validade**. Precisando de duas notas para a mesma fatura, use referências diferentes.\n- A mesma referência serve uma vez em teste e uma em produção: os modos não se enxergam.\n- Consultável em `GET /v1/nfse?reference=...`.\n- Até 64 caracteres, **sem espaços** — espaço é recusado em vez de aparado, porque `\"INV-1 \"` e `\"INV-1\"` seriam chaves diferentes.\n\nCom `Idempotency-Key` junto, os dois se somam: o retry imediato ganha o REPLAY da chave, e só uma referência de verdade repetida cai no `409`.","maxLength":64,"pattern":"^\\S+$","example":"INV-2026-0042"},"cnae":{"type":"string","description":"CNAE em que a nota deve sair, 7 dígitos, **apenas números** (a máscara `6202-3/00` é recusada com 400). Opcional.\n\n- **Omitido**, a nota sai na atividade **principal** — o certo para a maioria.\n- **Enviado**, precisa estar no cadastro da empresa na Receita Federal, principal ou secundária; um que não esteja é `422 nfse.cnae_not_allowed`.\n- A nota sai na linha **padrão** daquele CNAE; precisando de outra, mande `service_code` junto. CNAE sem linha padrão é `422 nfse.cnae_without_default`.\n\nO `cnae` e o `service_code` da resposta confirmam a linha em que a nota saiu.","pattern":"^\\d{7}$","example":"6203100"},"service_code":{"type":"string","description":"Código de tributação nacional da linha, 6 dígitos, **apenas números**. Opcional, e o **desempate** do `cnae` — não confunda os dois: aqui são 6 dígitos (`010501`), não os 7 do CNAE.\n\nA linha da nota sai desta tabela:\n\n| o que você manda | linha da nota |\n| --- | --- |\n| nada | a padrão da atividade **principal** da empresa |\n| `cnae` | a padrão **daquele CNAE** |\n| `cnae` + `service_code` | a linha **exata** |\n| `service_code` | a primeira linha com aquele código, principal antes de secundária |\n\nMande o par quando o seu CNAE tiver mais de uma linha e você precisar da que não é a padrão. Código fora do catálogo é `422 nfse.service_code_unknown`; código que existe mas não é da empresa, `422 nfse.service_code_not_allowed`.","pattern":"^\\d{6}$","example":"010501"}},"required":["amount_in_cents","description"]},"NfseResponseCustomerAddressDto":{"type":"object","properties":{"street":{"type":"string","description":"Logradouro.","example":"Portland Street"},"number":{"type":"string","description":"Número.","example":"175"},"district":{"type":"string","description":"Bairro ou distrito.","example":"Back Bay"},"city":{"type":"string","description":"Cidade.","example":"Boston"},"state":{"type":"string","description":"Estado, província ou região.","example":"MA"},"postal_code":{"type":"string","description":"Código postal.","example":"02114"},"country":{"type":"string","description":"País em ISO 3166-1 alpha-2.","example":"US"}}},"NfseResponseCustomerDto":{"type":"object","properties":{"document":{"type":"string","description":"CPF ou CNPJ do tomador, apenas dígitos. Ausente em nota sem tomador.","example":"12345678000190"},"name":{"type":"string","description":"Nome do tomador como gravado na nota. Em CNPJ vem do cadastro da Receita, não do que foi enviado.","example":"Maria Souza"},"type":{"type":"string","description":"Onde está o tomador: `br` (operação doméstica) ou `foreign` (exportação de serviço).","enum":["br","foreign"],"example":"foreign"},"tax_id":{"type":"string","description":"NIF do tomador no exterior, quando informado.","example":"98-7654321"},"tax_id_absence_reason":{"type":"string","description":"Motivo de não haver NIF: `exempt` (dispensado) ou `not_required` (não exigência).","enum":["exempt","not_required"],"example":"not_required"},"address":{"description":"Endereço do tomador no exterior. Ausente em nota doméstica.","allOf":[{"$ref":"#/components/schemas/NfseResponseCustomerAddressDto"}]}}},"NfseResponseForeignAmountDto":{"type":"object","properties":{"currency":{"type":"string","description":"Sigla ISO da moeda do faturamento.","example":"EUR"},"amount_in_cents":{"type":"integer","description":"Valor na moeda estrangeira, em centavos.","example":360000}},"required":["currency","amount_in_cents"]},"NfseResponseErrorDto":{"type":"object","properties":{"code":{"type":"string","description":"Código estável do motivo da falha, num vocabulário PRÓPRIO — disjunto dos códigos de erro HTTP. Em `error` da nota: `issue_failed` e `nfse.not_processed` vêm com `status: failed`; `nfse.issue_interrupted` vem com `status: indeterminate` e é o único que **não** admite reenvio. Em `cancellation.error`, `nfse.cancel_failed` é o único valor.","example":"issue_failed"},"message":{"type":"string","description":"Mensagem em português, já pronta para ser mostrada ao usuário final. Nunca contém a resposta crua do SEFIN.","example":"A emissão da nota falhou. Confira os dados enviados; se estiverem corretos, fale com o suporte ANTES de reemitir."}},"required":["code","message"]},"NfseResponseCancellationDto":{"type":"object","properties":{"status":{"type":"string","description":"Estado do PEDIDO de cancelamento, diferente do `status` da nota. `pending` = enviado, sem desfecho; `succeeded` = a nota está cancelada (o `status` também é `canceled`); `failed` = não recebemos a confirmação — **não emita outra nota**, acione o suporte.\n\n**Com `ext_sk_live_`, `failed` é permanente**: um novo `POST /cancel` sobre a mesma nota responde `409 nfse.cancel_in_progress`, e não há caminho pela API para sair desse estado. **Com `ext_sk_test_` o mesmo `failed` libera nova tentativa** — o modo de teste existe para ensaiar a recusa do SEFIN, não a trava permanente da produção.","enum":["pending","succeeded","failed"],"example":"pending"},"requested_at":{"type":"string","description":"Quando o cancelamento foi pedido. ISO 8601 em UTC.","format":"date-time","example":"2026-08-31T19:00:38.539Z"},"reason":{"type":"string","description":"O `reason` enviado no pedido. Um pedido sem `reason` é registrado como `other`.","enum":["issuance_error","service_not_provided","other"],"example":"issuance_error"},"error":{"description":"Presente só em `failed`. Nunca manda reemitir: o desfecho real é incerto, e reemitir sobre nota que já pode ter sido cancelada geraria duplicidade.","example":{"code":"nfse.cancel_failed","message":"Não recebemos a confirmação do cancelamento. NÃO gere uma nova nota fiscal — acione o suporte para conferir o estado da nota no SEFIN."},"allOf":[{"$ref":"#/components/schemas/NfseResponseErrorDto"}]}},"required":["status","requested_at"]},"NfseResponseLinksDto":{"type":"object","properties":{"pdf":{"type":"string","description":"URL absoluta do PDF da nota, com um token de download (`?t=`) que vale **15 minutos** e serve só esta nota. Dentro desse prazo o link abre sozinho — dá para entregá-lo a quem não tem a chave. Vencido, consulte a nota de novo e use o link novo; a resposta traz um a cada leitura. A rota também aceita a chave de API (escopo `nfse:read`), como sempre aceitou.","format":"uri","example":"https://api.extcontabilidade.com.br/v1/nfse/nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f/pdf?t=eyJhbGciOiJIUzI1NiJ9..."},"xml":{"type":"string","description":"URL absoluta do XML autorizado. Mesmo token temporário e mesma chave do PDF.","format":"uri","example":"https://api.extcontabilidade.com.br/v1/nfse/nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f/xml?t=eyJhbGciOiJIUzI1NiJ9..."}},"required":["pdf","xml"]},"NfseResponseDto":{"type":"object","properties":{"object":{"type":"string","description":"Discriminador do tipo do recurso. Sempre `nfse` neste corpo — permite roteamento genérico no cliente.","example":"nfse"},"id":{"type":"string","description":"Identificador da nota, sempre prefixado (`nfse_<uuid>`) — é o que se usa nas rotas de consulta e download, que também aceitam o UUID cru.","example":"nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f"},"livemode":{"type":"boolean","description":"Se a operação valeu em PRODUÇÃO. `true` é nota fiscal de verdade. `false` é nota de teste: nasce de uma chave `ext_sk_test_`, sai no ambiente sem validade fiscal, não é cobrada e expira em 7 dias.\n\nSai em TODA resposta de `/v1/nfse`, erro incluído, e é determinado pela chave — nunca pelo corpo enviado.","example":true},"status":{"type":"string","description":"Situação da nota:\n\n- `queued` — aceita, ainda não enviada. Continue consultando.\n- `processing` — envio em curso. Estado **transitório**: não pare o polling nele.\n- `issued` — autorizada. `access_key` e `links` existem; é o único estado em que PDF e XML baixam.\n- `failed` — recusada, e **nenhum documento existe**. Corrigir o que `error` aponta e reemitir é seguro.\n- `indeterminate` — a emissão começou sem desfecho confirmado: **pode existir NFS-e autorizada** sem registro aqui. **Nunca reemita** — daria nota em duplicidade. Acione o suporte.\n- `canceled` — cancelada. Nota em cancelamento ainda aparece `issued`.\n\nDesfechos: `issued`, `failed`, `indeterminate` e `canceled`. Valor desconhecido, trate como `indeterminate`.","enum":["queued","processing","issued","failed","indeterminate","canceled"],"example":"issued"},"amount_in_cents":{"type":"integer","description":"Valor total **em centavos**, inteiro, como gravado na nota: `125050` são R$ 1.250,50. Mesma unidade da ida — o contrato nunca troca de unidade.","example":125050},"description":{"type":"string","description":"Descrição **exatamente como gravada na nota**. Notas antigas criadas pela interface web podem trazer marcadores não expandidos (`{{mes_extenso}}`): a leitura não os expande, senão publicaria texto diferente do impresso na nota.","example":"Desenvolvimento de software sob encomenda — competência 07/2026"},"competence":{"type":"string","description":"Competência — o mês fiscal da nota —, data pura `YYYY-MM-DD`. Você não a envia: é a data em que a chamada foi recebida, no fuso do Brasil. **Ausente** apenas em nota criada pela interface da EXT sem competência gravada.","example":"2026-01-31"},"customer":{"description":"Tomador da nota. Ausente em nota sem tomador.","allOf":[{"$ref":"#/components/schemas/NfseResponseCustomerDto"}]},"foreign_amount":{"description":"Valor da nota na moeda do faturamento, na exportação. Ausente em nota doméstica. O `amount_in_cents` da raiz continua sendo o valor em reais que foi para a nota.","allOf":[{"$ref":"#/components/schemas/NfseResponseForeignAmountDto"}]},"reference":{"type":"string","description":"O `reference` que você enviou na emissão, intacto. Ausente quando a nota não tem um — inclusive em toda nota criada pela interface da EXT.","maxLength":64,"example":"INV-2026-0042"},"service_code":{"type":"string","description":"Código de tributação nacional, 6 dígitos: a linha em que a nota saiu — sempre a que foi **usada**, tenha você mandado o campo ou não. Ausente em nota antiga e em nota criada pela interface da EXT sem escolha explícita de atividade.","pattern":"^\\d{6}$","example":"010501"},"cnae":{"type":"string","description":"CNAE (7 dígitos) da linha em que a nota saiu — o par do `service_code`, com a mesma convenção. Omitindo `cnae` no `POST`, volta o da atividade principal da empresa.","pattern":"^\\d{7}$","example":"6202300"},"access_key":{"type":"string","description":"Chave de acesso da NFS-e, 50 dígitos. Só existe depois de autorizada, e **sobrevive ao cancelamento**: o cancelamento é evento à parte no leiaute nacional e não apaga a autorização, então a chave continua sendo a de consulta da nota no portal. Ausente em quem nunca chegou a ser autorizada.","example":"31260712345678000190550010000000011234567890"},"failed_attempts":{"type":"number","description":"Quantas tentativas de emissão FALHARAM. `0` nunca significa \"nada foi enviado\": em `indeterminate` a emissão começou e morreu sem registrar a falha. **Quem decide se pode reemitir é o `status`**, nunca este número.","example":0},"created_at":{"type":"string","description":"Quando a nota foi aceita pela API. ISO 8601 em UTC.","format":"date-time","example":"2026-07-15T13:42:07.512Z"},"issued_at":{"type":"string","description":"Quando a nota foi autorizada na prefeitura. ISO 8601 em UTC, presente em `issued` e em `canceled` — cancelar não muda a data em que a nota saiu. Pode faltar em nota `canceled` anterior a agosto/2026, quando o carimbo passou a ser gravado.","format":"date-time","example":"2026-07-15T13:43:19.004Z"},"cancelable_until":{"type":"string","description":"Até quando esta nota pode ser cancelada por esta API: o fim do mês da emissão, no fuso de Brasília.\n\n**A PRESENÇA do campo é a resposta**: ele existe quando, e só quando, um `POST /v1/nfse/{id}/cancel` seria aceito agora. Ausente, a rota recusa — `422` fora da janela, `409` se já houver pedido.\n\nLeia o instante daqui em vez de recalcular: a regra é medida em Brasília, e comparar o mês em UTC erra as últimas três horas de todo dia 31 — justamente quando a janela fecha. Fora dela, o suporte ainda pode cancelar.","format":"date-time","example":"2026-09-01T02:59:59.000Z"},"cancellation":{"description":"Estado do pedido de cancelamento, ausente enquanto nenhum foi feito. Fica FORA do `status` porque descreve o PEDIDO: uma nota com cancelamento pendente continua `issued`, porque continua valendo.","allOf":[{"$ref":"#/components/schemas/NfseResponseCancellationDto"}]},"failed_at":{"type":"string","description":"Quando a falha foi constatada. ISO 8601 em UTC, **ausente** em nota que não está `failed` nem `indeterminate`. Em falha por espera esgotada é o vencimento da janela, não um carimbo de erro.","format":"date-time","example":"2026-07-15T13:43:19.004Z"},"error":{"description":"Detalhe da falha, **ausente** quando a nota não está `failed` nem `indeterminate`.\n\n**Programe pelo `status`, não por este campo** — ele existe para você saber o PORQUÊ e escrever a mensagem ao seu usuário.\n\n| `error.code` | `status` | O que aconteceu | O que fazer |\n| --- | --- | --- | --- |\n| `issue_failed` | `failed` | Falha ANTES da autorização — o caso dominante. Nada foi emitido. | Confira o payload e emita de novo. |\n| `nfse.not_processed` | `failed` | Nenhuma tentativa chegou a ser registrada. | Acione o suporte: a causa é de habilitação na EXT, não do seu payload. |\n| `nfse.issue_interrupted` | `indeterminate` | A emissão começou e o resultado não pôde ser confirmado. | **Nunca reenvie.** A nota pode existir sem que o registro aqui se completasse, e reemitir gera documento fiscal em duplicidade. |\n\n**A nota nunca fica em polling infinito.** Pendente sem nenhuma tentativa por 15 minutos vira `failed` (`nfse.not_processed`); em processamento sem sinal por 60 minutos vira `indeterminate` (`nfse.issue_interrupted`) — essa é a que você não pode reemitir.","allOf":[{"$ref":"#/components/schemas/NfseResponseErrorDto"}]},"links":{"description":"URLs de download do PDF e do XML, presentes em `issued` e em `canceled` — em nota cancelada o PDF vem com a tarja CANCELADA, que é o documento a arquivar. Nos demais estados não há arquivo, e o download é `409 nfse.not_issued`.\n\n**No modo de teste (`ext_sk_test_`) o campo some em `canceled`**, e é a única diferença de corpo entre os modos: lá o DANFSe é re-renderizado na hora do download e sairia sem a tarja.","allOf":[{"$ref":"#/components/schemas/NfseResponseLinksDto"}]}},"required":["object","id","livemode","status","amount_in_cents","failed_attempts"]},"PublicApiErrorItemDto":{"type":"object","properties":{"code":{"type":"string","description":"Sempre `invalid_request`, o mesmo `code` do envelope: o item tem a forma do erro, mas não diferencia as causas entre si.","example":"invalid_request"},"message":{"type":"string","description":"A causa, como o validador a descreve.","example":"property amount should not exist"}},"required":["code","message"]},"PublicApiErrorDetailDto":{"type":"object","properties":{"type":{"type":"string","description":"Família do erro, estável e pequena o bastante para virar `switch` no cliente: `invalid_request` (o pedido está errado — corrija e reenvie), `authentication_error` (chave ausente, inválida ou revogada), `permission_error` (a chave é válida mas não pode fazer isto), `rate_limit_error` (excedeu a janela — respeite o `Retry-After`) e `api_error` (falha nossa).\n\n**A lista é FECHADA**, e é isso que a torna segura num `switch`: família nova só entra com versão nova da API. Quem cresce é o `code`. Ainda assim, mantenha um ramo `default` tratando o desconhecido como `api_error` — é a rede para a resposta de um intermediário que não seja nossa.","enum":["invalid_request","authentication_error","permission_error","rate_limit_error","api_error"],"example":"invalid_request"},"code":{"type":"string","description":"Código versionado do erro, específico dentro da família. É o valor a usar em lógica de decisão — nunca a `message`, que pode ser reescrita sem aviso. Nunca é o código cru do provedor.\n\n**O conjunto é ABERTO e cresce**: toda vez que uma recusa genérica ganha nome próprio, um código novo aparece, e isso não é breaking change. Trate código desconhecido pelo caminho genérico da família em `type` — mostrar `message`, obedecer a `retryable`, registrar o `request_id`. Código já publicado não é renomeado nem reaproveitado para outro significado.","example":"nfse.customer_required"},"message":{"type":"string","description":"Explicação em português, pronta para log ou para exibição ao usuário final. Texto sujeito a melhoria; não faça parsing dele.","example":"A prefeitura deste município exige a identificação do tomador na nota."},"param":{"type":"string","description":"Campo do payload que causou a recusa, quando a causa é de payload. Ausente — e não `null` — quando o erro não aponta para um campo.","example":"customer.document"},"retryable":{"type":"boolean","description":"Se repetir a MESMA requisição pode dar outro resultado. `false` quer dizer que insistir não resolve — reenviar sem mudar nada só queima a janela de rate limit. Em `true`, use `Retry-After` (header) para saber quando, e mande a mesma `Idempotency-Key` para não duplicar a nota.","example":false},"livemode":{"type":"boolean","description":"Se a operação recusada valia em PRODUÇÃO. Mesmo campo do corpo da nota, determinado pela chave usada.\n\n**Ausente quando ainda não havia credencial** — `401` de chave inválida, `403` de permissão e o `503` do kill switch saem sem ele. Ausência NÃO é `false`: dizer `false` ali afirmaria que a chamada recusada era de teste, o que induziria a ler como inofensiva uma recusa de produção.","example":true},"provider_code":{"type":"string","description":"Código cru do provedor (SEFIN), apenas para depuração e para citar em chamado. **Nunca** use em lógica: ele não é versionado e pode sumir.","example":"E0015"},"request_id":{"type":"string","description":"Identificador desta requisição, o mesmo do header `Request-Id`. Cite este valor ao abrir chamado — é por ele que a EXT acha a requisição no log.","example":"9f8c1a52-3e77-4b0d-8c21-6a4f2b91e0d3"},"errors":{"description":"Todas as causas da recusa, presente apenas em `invalid_request` de validação do corpo — `message` traz só a primeira. Ausente nos demais erros.","type":"array","items":{"$ref":"#/components/schemas/PublicApiErrorItemDto"}}},"required":["type","code","message","retryable","request_id"]},"PublicApiErrorDto":{"type":"object","properties":{"error":{"description":"Toda falha da API sai neste envelope, em qualquer status — não há um segundo formato para 4xx e 5xx. O único acréscimo é o `errors` da falha de validação, que não substitui nada.","allOf":[{"$ref":"#/components/schemas/PublicApiErrorDetailDto"}]}},"required":["error"]},"CancelNfseDto":{"type":"object","properties":{"reason":{"type":"string","description":"Motivo do cancelamento, registrado no evento fiscal. Omitido, assume `other`.","enum":["issuance_error","service_not_provided","other"],"example":"issuance_error"},"description":{"type":"string","description":"Justificativa livre, de **15 a 255 caracteres** — o limite é da nota fiscal, não nosso, e fora dele o cancelamento é recusado. Validamos aqui para a recusa chegar na hora, e não minutos depois com a nota presa. Omitida, usamos uma padrão.","minLength":15,"maxLength":255,"example":"Valor lançado errado no pedido 4471."}}},"NfseListResponseDto":{"type":"object","properties":{"object":{"type":"string","description":"Discriminador do tipo do recurso. Sempre `list` neste corpo — os itens de `data` trazem o próprio `object: \"nfse\"`.","example":"list"},"has_more":{"type":"boolean","description":"Se há mais resultados além dos devolvidos. **Sempre `false` no v1**, já que a única busca é por `reference`, único por empresa. O campo existe desde já para o seu código não precisar mudar quando a paginação chegar.","example":false},"data":{"description":"As notas encontradas, no mesmo formato de `GET /v1/nfse/{id}`. Vazio quando nenhuma nota da empresa tem o `reference` consultado.","type":"array","items":{"$ref":"#/components/schemas/NfseResponseDto"}}},"required":["object","has_more","data"]},"ActivityResponseDto":{"type":"object","properties":{"cnae":{"type":"string","description":"CNAE da linha, 7 dígitos, **apenas números** — exatamente o formato que o `cnae` do `POST /v1/nfse` aceita. Mande este valor como veio, sem a máscara do CONCLA.","example":"6202300"},"service_code":{"type":"string","description":"Código de tributação nacional da linha, 6 dígitos — o `service_code` do `POST /v1/nfse`. Só é necessário quando `is_default` é `false`.","example":"010501"},"description":{"type":"string","description":"A linha por extenso: item da LC 116, descrição e o CNAE com máscara. Serve para você reconhecer a atividade — **não é campo de envio**, e o CNAE com máscara que aparece aqui é recusado com `400` no campo `cnae`.","example":"1.05 Licenciamento ou cessão de direito de uso de programas de computação (6202-3/00)"},"is_main":{"type":"boolean","description":"Se o CNAE é a atividade **principal** da empresa na Receita Federal. A nota que não manda `cnae` sai na linha que tem `is_main` e `is_default` ao mesmo tempo.","example":true},"is_default":{"type":"boolean","description":"Se é a linha **padrão** do seu CNAE. Sendo `true`, mandar só o `cnae` chega nela. Sendo `false`, é preciso mandar `cnae` **e** `service_code` juntos — sem o par ela é inalcançável, porque a linha padrão é global por CNAE e não por empresa.","example":true}},"required":["cnae","service_code","description","is_main","is_default"]},"ActivityListResponseDto":{"type":"object","properties":{"object":{"type":"string","description":"Discriminador do tipo do recurso. Sempre `list` neste corpo.","example":"list"},"has_more":{"type":"boolean","description":"Se há mais resultados além dos devolvidos. **Sempre `false` no v1** — a lista é o cadastro da empresa, que cabe inteiro numa resposta. O campo existe desde já para o seu código não precisar mudar se a paginação chegar.","example":false},"data":{"description":"As atividades em que a empresa pode emitir. Vazio quando nenhuma está liberada — o que é resposta legítima, não erro.","type":"array","items":{"$ref":"#/components/schemas/ActivityResponseDto"}}},"required":["object","has_more","data"]}}},"security":[{"apiKey":[]}]}