# TexFiscal: emissão de NFS-e, NF-e e NFC-e por API > Emissão, consulta e cancelamento de Nota Fiscal de Serviço eletrônica (NFS-e, > municipal) e de Nota Fiscal de mercadoria (NF-e modelo 55 e NFC-e modelo 65, > direto na SEFAZ) pelo seu sistema. A API fala o contrato REST v2 que a maioria > dos ERPs já integra: mesmos caminhos, mesmos campos, mesmos literais de status. > Quem já emite por API troca a URL base e o token, sem mexer no código. Ambientes: produção em https://nfse.texfiscal.com.br, homologação em https://homologacao-nfse.texfiscal.com.br. Cada ambiente tem o seu token; o token de um apresentado no outro recebe 401. Autenticação é HTTP Basic com o token no usuário e senha VAZIA. O cliente gera e revoga os próprios tokens no painel (https://painel.texfiscal.com.br, menu Tokens), onde também consulta cada nota (payload, retorno, XML, PDF), os webhooks com as entregas e cada requisição feita à API com o motivo da recusa. ## As três regras que evitam quase todo erro de integração 1. A `ref` (na query do POST) é a chave de idempotência. Repetir o POST com a mesma `ref` não emite de novo, devolve a nota existente. Derive a `ref` da sua transação, nunca de relógio nem de aleatório. 2. Nota em `processando_autorizacao` NUNCA se reenvia. Só a consulta resolve. Reenviar duplica documento fiscal. 3. `aliquota` é PERCENTUAL (2.00 = dois por cento) e `discriminacao` tem MÍNIMO de 11 caracteres (a prefeitura diz "no mínimo 10" e recusa 10; descreva o serviço com 15 ou mais para ter folga). Os dois são causa comum de recusa da prefeitura, e recusa da prefeitura consome numeração de RPS. ## Reforma Tributária: IBS e CBS O objeto `servico.ibs_cbs` é OPCIONAL até 31/12/2026 e passa a ser EXIGIDO em 01/01/2027 para empresas do Simples Nacional (01/10/2026 fora do Simples). Enquanto for opcional, omitir é válido. É TUDO OU NADA: `cst` (3 dígitos), `classificacao_tributaria` (6) e `indicador_operacao` (6) vão juntos, ou nenhum vai. Meio grupo é recusado pelo schema do provedor com o RPS já consumido, então a recusa acontece aqui antes de reservar número. Os três podem ficar no cadastro do emissor; o payload sempre vence o cadastro. Quais códigos usar e enquadramento tributário: é conversa com o contador, não há default e não escolhemos por ninguém. ## Contrato - [OpenAPI 3.1](https://nfse.texfiscal.com.br/openapi.json): definição completa de rotas, campos e erros. - [Guia de integração](https://nfse.texfiscal.com.br/llms-full.txt): este arquivo com o guia inteiro embutido. - [Municípios atendidos](https://nfse.texfiscal.com.br/v2/municipios): cobertura verificada, com a data de cada verificação. - [Saúde](https://nfse.texfiscal.com.br/health): sem token. ## Rotas - `POST /v2/nfse?ref=`: emite. Repetir com a MESMA ref não reemite: devolve a nota que já existe (é assim que o retry após timeout fica seguro). A exceção é ref cuja nota foi CANCELADA: ela responde 409 `ref_encerrada`, porque a nota cancelada continua existindo como documento fiscal. Para substituir uma nota cancelada, use uma ref NOVA, por exemplo `PED-4471-R2`. A ref DISTINGUE MAIÚSCULA de minúscula: `ped-1` e `PED-1` são duas notas, e dois documentos fiscais para a mesma venda. Derive sempre da mesma forma. - `GET /v2/nfse/`: consulta (pergunta à prefeitura quando está em processamento). - `DELETE /v2/nfse/`: cancela. Corpo: `justificativa` (texto, mínimo 15 caracteres) e `motivo` (opcional, 1, 2 ou 3, default 1). Os dois NÃO são a mesma coisa e confundi-los declara errado ao município: a `justificativa` é registro NOSSO e nunca chega à prefeitura (o padrão ABRASF não tem campo livre para ela); o `motivo` É a declaração ao fisco, e vale 1 erro na emissão, 2 serviço não prestado, 3 erro de assinatura. O PRAZO para cancelar é definido por cada PREFEITURA, não por nós, e não há nada aqui que o conheça: passado o prazo, a resposta é 422 `cancelamento_recusado` vinda do município. Não planeje um botão de cancelar que valha para sempre. - `GET /v2/nfse//pdf`: DANFSe. Exige token, então NUNCA cole este endereço num e-mail ao cliente final: ele recebe 401 em JSON e ainda vê o endereço da sua API fiscal. Foi o que aconteceu com um integrador em 17/09/2026. - `GET /v2///pdf/link`: devolve `url` de curta validade que abre o PDF SEM token, para entregar ao navegador de quem recebeu a nota. Vale para os três recursos (nfse, nfe, nfce). `?validade=`, padrão 900 e teto 604800 (sete dias). Quem tiver o endereço abre AQUELE documento até expirar: entregue só ao destinatário da nota. Depois de expirado responde página, não JSON. Use isto no lugar de escrever um proxy autenticado no seu servidor. - `GET /v2/nfse//xml`: XML autorizado. - `GET /v2/nfse?de=AAAA-MM-DD&ate=AAAA-MM-DD&status=&pagina=N`: listagem paginada (50 por página, da mais recente para a mais antiga) para conciliar sem consultar ref por ref. Sem payload, XML nem PDF; não pergunta à prefeitura. Vale igual para `GET /v2/nfe` e `GET /v2/nfce`. - `POST /v2/nfse/validar`: valida o payload SEM emitir (cadastro do emissor e regras do documento), sem consumir RPS nem falar com a prefeitura. 200 `valido` ou 422 `requisicao_invalida` com os mesmos `erros[]` da emissão. Não exige certificado: serve para homologar a integração antes de o A1 chegar. Existe também para `nfe` e `nfce`. - `GET /v2/hooks`, `POST /v2/hooks` (corpo `{"url": "https://..."}`; com `"substituir": true` troca um ativo, rotacionando o segredo) e `DELETE /v2/hooks/`: o webhook do emissor no ambiente do token, um por ambiente. O `segredo` volta UMA vez, na resposta do POST. - `GET /v2/uso?mes=AAAA-MM`: uso do mês, com o campo `cobravel` calculado pelo MESMO critério do fechamento que gera a fatura: `status IN ('autorizada', 'cancelamento_pendente', 'cancelada')`, somando NFS-e, NF-e e NFC-e do emissor (só em produção; em homologação `cobravel` é sempre 0). Repare que **nota cancelada continua contando**: o cancelamento não devolve o número. Nota recusada, denegada ou ainda em processamento não conta. - `GET /v2/municipios`: cobertura verificada. ## NF-e e NFC-e (mercadoria, SEFAZ) Mesma ref, mesma idempotência, mesmos literais de status. O emissor precisa estar habilitado para NF-e/NFC-e (senão 422 `dfe_nao_habilitado`). Hoje a cobertura é a Bahia: NF-e na SEFAZ-BA e NFC-e na SVRS. - `POST /v2/nfce?ref=` e `POST /v2/nfe?ref=`: emitem de forma síncrona. Payload no formato de mercado (`natureza_operacao`, `items[]` com `cfop`, `codigo_ncm`, `icms_situacao_tributaria`..., `formas_pagamento[]`, destinatário `cnpj_destinatario`/`cpf_destinatario`...). Os dados do EMITENTE saem do cadastro, nunca do payload. `numero` e `serie` são opcionais: sem eles o serviço numera. Nota recusada pela SEFAZ volta 422 com `status_sefaz`; corrija e reenvie com a MESMA ref (mesmo número e mesma chave). 202 = sem desfecho: NUNCA se reenvia, consulte. - `GET /v2/nfce/` e `GET /v2/nfe/`: estado; pendente consulta a SEFAZ. - `GET /v2/nfce/por-numero//` e `GET /v2/nfce/chave/<44 dígitos>` (idem para `nfe`): recuperação para quem PERDEU a ref. Mesma representação do GET por ref, escopada ao emissor do token. - RESPOSTA PERDIDA (timeout, queda de rede, HTTP 0 no seu cliente): o desfecho é DESCONHECIDO, nunca rejeição. A emissão é síncrona e a SEFAZ pode ter autorizado. Regra: (1) gere a `ref` e GRAVE-A antes do POST; (2) sem resposta, consulte `GET /v2//` (ou por número, se a ref se perdeu) antes de qualquer reenvio; (3) reenviar o mesmo número com ref nova responde 409 `numero_em_uso` com `ref_existente`, `status_existente` e `documento`: adote o documento se estiver autorizado; (4) se nem a consulta responder, deixe a nota como pendente e repita a consulta em minutos. Caso real: 22/09/2026, NFC-e autorizada em 1,5 s e resposta perdida no caminho; o cliente gravou "rejeitada" e reemitiu o mesmo número. - Webhooks: NF-e e NFC-e disparam `nfe_autorizada`, `nfe_erro`, `nfe_cancelada`, `nfe_denegada` (e `nfce_*`) para a URL cadastrada no painel ou por `POST /v2/hooks`, com o mesmo envelope da NFS-e (`evento`, `ref`, `evento_id`, `ocorrido_em` + a representação do GET). - `GET /v2/nfce/.xml`: nfeProc (NFe + protNFe), o arquivo que vale por 5 anos. - `DELETE /v2/nfce/`: cancela (justificativa de 15 a 255 caracteres, que VAI a SEFAZ). O prazo é da SEFAZ: 24 h na NF-e; na NFC-e da Bahia, 30 minutos. - `POST /v2/nfe//carta_correcao`: CC-e, só NF-e. A resposta traz `caminho_xml_carta_correcao`; `GET /v2/nfe//carta_correcao` lista as registradas e `GET /v2/nfe//carta_correcao.xml?sequencia=N` devolve o procEventoNFe (documento de guarda de 5 anos). - `POST /v2/nfce/inutilizacao` e `POST /v2/nfe/inutilizacao`: inutiliza faixa. `GET /v2/nfce/inutilizacoes` (e `nfe`) lista as registradas, com protocolo. - DENEGAÇÃO (cStat 110, 301, 302, 303): status `denegado`, estado FINAL. O número foi consumido; o XML denegado sai em `/xml`; reemitir a mesma ref responde 409 `numero_denegado`. Duplicidade 539 (outra nota com o mesmo número e chave diferente) vira erro com `chave_existente` e reemissão da ref responde 409 `numero_consumido`: em ambos, emita com OUTRO número. - NFC-e: `qrcode_url` da resposta é o conteúdo EXATO do QR Code do cupom; imprima sem alterar. A NFC-e só aceita CFOP de venda a consumidor (5101, 5102, 5103, 5104, 5115, 5405, 5656, 5667, 5910, 5933) e o CSOSN tem de combinar; fora disso a recusa vem antes de numerar, com a explicação. ## Literais de status - `autorizado`: A prefeitura autorizou. Há número, código de verificação, XML e DANFSe. - `processando_autorizacao`: Enviada, sem veredito ainda. Consulte ou espere o webhook. NUNCA reenvie: reenvio duplica documento fiscal. - `erro_autorizacao`: A prefeitura recusou. Corrija e reenvie com a MESMA ref. - `cancelado`: Cancelada com sucesso. - `processando_cancelamento`: Pedido de cancelamento sem veredito. Repita o DELETE ou consulte. - `denegado`: Só NF-e e NFC-e (desde 22/09/2026): a SEFAZ denegou o uso (cStat 110, 301, 302 ou 303, irregularidade cadastral do emitente ou do destinatário). É estado FINAL: o número foi consumido, o XML denegado fica em /xml, e reemitir a mesma ref responde 409 numero_denegado. Trate como recusa que exige outro número. ## Códigos de erro Trate pelo `codigo`, nunca pelo texto da `mensagem`. - `nao_autorizado` (HTTP 401): Token ausente, inválido, ou de um ambiente diferente do endereço chamado. - `emissor_bloqueado` (HTTP 403): O contrato do emissor está suspenso, encerrado ou ainda não foi ligado. Emitir e cancelar ficam barrados; CONSULTAR continua liberado, inclusive o XML e o PDF das notas já emitidas. - `ref_invalida` (HTTP 422): A ref não passou na regra: até 64 caracteres entre letras, números, ponto, hífen e sublinhado, sem começar com ponto e sem sufixo de arquivo reservado. - `json_invalido` (HTTP 422): O corpo não é um objeto JSON válido. - `requisicao_invalida` (HTTP 422): O payload foi recusado na validação. O array erros[] traz campo, mensagem e correção. Nada foi transmitido à prefeitura e nenhum RPS foi consumido. - `emissor_nao_configurado` (HTTP 422): O emissor do token está sem certificado, sem inscrição municipal, com o certificado vencido ou com o cadastro incompleto. Emissor SUSPENSO não cai aqui: cai em emissor_bloqueado (403). - `estado_invalido` (HTTP 422): A operação não cabe no estado atual da nota (cancelar nota que não está autorizada, por exemplo). - `justificativa_invalida` (HTTP 422): A justificativa de cancelamento tem menos de 15 caracteres. - `motivo_invalido` (HTTP 422): O campo motivo do cancelamento aceita 1 (erro na emissão), 2 (serviço não prestado) ou 3 (erro de assinatura). - `cancelamento_recusado` (HTTP 422): A prefeitura recusou o cancelamento. - `mes_invalido` (HTTP 422): O parâmetro mês não está em AAAA-MM. - `dfe_nao_habilitado` (HTTP 422): O emissor do token não está habilitado para NF-e e NFC-e (só NFS-e). Fale com o suporte para ligar. - `uf_nao_atendida` (HTTP 422): A emissão desse modelo ainda não atende a UF do emissor. Hoje: Bahia (NF-e na SEFAZ-BA, NFC-e na SVRS). - `emitente_divergente` (HTTP 422): O cnpj_emitente do payload não é o CNPJ do emissor dono do token. O token de um CNPJ nunca emite por outro. - `numero_em_uso` (HTTP 409): A série e o número informados já pertencem a outra ref deste emissor (autorizada, pendente ou cancelada). O corpo traz `ref_existente`, `status_existente` e `documento` (a mesma representação do GET por ref): se o status for autorizado, ADOTE esse documento em vez de emitir de novo. É o que acontece quando a resposta da primeira tentativa se perdeu na rede. - `numero_denegado` (HTTP 409): A SEFAZ DENEGOU o uso deste número: ele foi consumido e não volta. Emita com outro número. A nota denegada fica com status `denegado` e o XML em /xml. - `numero_consumido` (HTTP 409): A SEFAZ já tem OUTRA nota com o número desta ref e chave diferente (cStat 539). O número não pode ser reutilizado: consulte a chave informada na recusa e emita com outro número. - `numero_usado` (HTTP 422): A faixa a inutilizar contém número de nota autorizada, pendente ou cancelada. - `correcao_invalida` (HTTP 422): O texto da carta de correção precisa de 15 a 1000 caracteres. - `sequencia_invalida` (HTTP 422): A sequência da carta de correção vai de 1 a 20. - `carta_correcao_recusada` (HTTP 422): A SEFAZ recusou a carta de correção. status_sefaz e mensagem_sefaz trazem o motivo. Logo depois da autorização a SEFAZ-BA pode responder 494 (chave inexistente) por alguns minutos: repita em instantes. - `assinatura_suspensa` (HTTP 402): A assinatura da conta está suspensa, cancelada, ou o período de teste terminou. Emitir em produção fica bloqueado; consultar, XML e PDF das notas já emitidas continuam liberados. Homologação continua liberada. - `franquia_excedida` (HTTP 402): A conta atingiu a franquia de notas do mês no plano contratado e o plano não vende nota excedente. Trocar de plano no painel libera na hora. Só acontece em produção. - `cnpjs_excedidos` (HTTP 402): A conta tem mais emissores ativos do que o plano inclui e o plano não vende CNPJ adicional. Trocar de plano ou desativar um emissor libera. Só acontece em produção. - `ref_encerrada` (HTTP 409): Esta referência já tem uma nota CANCELADA e não pode ser reutilizada. A nota cancelada continua existindo como documento fiscal: emita a substituta com uma referência NOVA (por exemplo, sufixo -R2). - `nao_encontrado` (HTTP 404): Não existe nota com essa ref neste emissor e ambiente. - `xml_indisponivel` (HTTP 404): Não há XML autorizado guardado para essa ref. - `danfe_indisponivel` (HTTP 404): Não há NF-e ou NFC-e autorizada para essa ref neste emissor e ambiente. Situação permanente enquanto a nota não autorizar. - `danfe_falhou` (HTTP 422): A nota está autorizada, mas o DANFE não pode ser gerado agora. O XML autorizado continua em /xml. Tente o PDF de novo em instantes. - `danfse_indisponivel` (HTTP 404): Não há nota autorizada para essa ref. Situação permanente enquanto a nota não autorizar. - `danfse_falhou` (HTTP 422): A nota está autorizada mas o DANFSe não pode ser gerado agora. O XML autorizado continua em /xml. Tente o PDF de novo em instantes. - `link_indisponivel` (HTTP 422): O endereço assinado não pode ser montado agora. O PDF autenticado em /pdf continua funcionando. - `cobertura_indisponivel` (HTTP 422): A lista de cidades atendidas não pode ser lida agora. - `contrato_indisponivel` (HTTP 404): O arquivo de contrato pedido (OpenAPI, Postman, llms.txt) não está disponível. - `servico_indisponivel` (HTTP 422): O serviço não respondeu a tempo ou está fora do ar. O DESFECHO DESTA CHAMADA É DESCONHECIDO: consulte GET /v2/nfse/{a mesma ref} antes de qualquer reenvio, e nunca reenvie com ref nova. - `rota_inexistente` (HTTP 404): Caminho não atendido por este serviço. - `metodo_nao_permitido` (HTTP 405): Método errado na rota. O cabeçalho Allow diz quais valem. - `corpo_excede_limite` (HTTP 413): Corpo acima de 512 KB. - `limite_de_requisicoes` (HTTP 429): Acima de 10 requisições por segundo por token (rajada de 40); sem token, 3 por segundo por IP. O cabeçalho Retry-After diz quanto esperar. - `numero_invalido` (HTTP 422): Na consulta por número: a série vai de 0 a 999 e o número tem de ser maior que zero. - `chave_invalida` (HTTP 422): A chave de acesso precisa ter 44 posições (com ou sem o prefixo NFe; o CNPJ pode ter letras) e o dígito verificador tem de fechar. - `periodo_invalido` (HTTP 422): Parâmetro de listagem fora do formato: de e ate em AAAA-MM-DD (de não maior que ate), status entre os literais públicos, pagina inteira a partir de 1. - `webhook_url_invalida` (HTTP 422): A URL do webhook precisa ser https, sem usuário embutido, e não pode apontar para endereço local ou privado. - `webhook_ja_cadastrado` (HTTP 409): Já existe um webhook ativo para este emissor neste ambiente. Repita com "substituir": true para trocar a URL: isso ROTACIONA o segredo e o seu receptor precisa adotar o novo. - `falha_interna` (HTTP 500): Falha não prevista. O incidente foi registrado. ## Compatibilidade: o que pode mudar sem aviso e o que não muda Esta seção é um compromisso, não uma observação. Escreva seu cliente contando com ela e ele não quebra quando o serviço evoluir. O que PODE aparecer a qualquer momento, e por isso não quebra nada: - códigos de erro novos. Trate código desconhecido pelo STATUS HTTP: 4xx é problema no pedido ou no cadastro e não adianta repetir igual; 5xx é nosso. - campos novos na resposta. IGNORE campo que você não conhece, nunca recuse a resposta inteira por causa dele. - rotas novas e parâmetros de consulta OPCIONAIS novos. O que NÃO muda em `/v2`, e só mudaria com aviso de 90 dias e os cabeçalhos `Deprecation` e `Sunset` antes: - caminho e método das rotas que já existem; - nome e significado dos campos que já existem; - os literais de `status` (`autorizado`, `processando_autorizacao`, `erro_autorizacao`, `cancelado`, `processando_cancelamento` e, desde 22/09/2026 e só em NF-e/NFC-e, `denegado`); literal novo pode aparecer como campo novo aparece: trate desconhecido como final e não autorizado; - o status HTTP que um código de erro já existente devolve. Duas coisas que valem repetir porque são onde mais se erra: 202 NÃO é falha (a nota está em processamento e reenviar duplica documento fiscal), e um `codigo` que você não reconhece nunca deve virar retry automático. ## Webhook: o aviso que o serviço manda para você Você pode cadastrar UMA url por ambiente, no painel ou por `POST /v2/hooks`. Assim que a nota muda de desfecho, o serviço faz um POST nela com `Content-Type: application/json`. Eventos: `nfse_autorizada`, `nfse_erro`, `nfse_cancelada` e `nfse_cancelamento_nao_efetivado`; para NF-e e NFC-e, `nfe_autorizada`, `nfe_erro`, `nfe_cancelada`, `nfe_denegada` e os `nfce_*` equivalentes; `teste` quando você aperta o botão de teste no painel. O `nfse_cancelamento_nao_efetivado` é o mais importante: ele significa que o cancelamento que você pediu NÃO valeu na prefeitura e a nota voltou a estar autorizada. Ele existe para pedir que alguém aja. `nfe_denegada` e `nfce_denegada` dizem que a SEFAZ denegou o uso e o número foi consumido: estado final, com o XML denegado em /xml. Corpo: { "evento": "nfse_autorizada", "ref": "venda-000123", "evento_id": 4471, "ocorrido_em": "2026-09-03T01:20:00-03:00", ... os mesmos campos de GET /v2/nfse/ } Cabeçalhos: `X-TexFiscal-Evento`, `X-TexFiscal-Tentativa`, `X-TexFiscal-Assinatura`, que é o HMAC-SHA256 do CORPO CRU com o segredo do seu webhook, e `X-Webhook-Token`, o segredo em claro, para o receptor escrito para a Focus continuar funcionando. Prefira a assinatura: ela prova o corpo, o token só prova a origem. Confira antes de confiar no conteúdo, e compare com `hash_equals` ou equivalente, nunca com `==`. Três regras do receptor, e a segunda já causou prejuízo em outros integradores: 1. Responda 2xx rápido. Timeout nosso: 5 s na tentativa imediata. Faca o trabalho pesado depois de responder. 2. DESCARTE evento fora de ordem. Uma entrega que falhou e reagendada pode chegar DEPOIS de um evento mais novo da mesma ref: por exemplo, uma `nfse_autorizada` reagendada chegando depois da `nfse_cancelada`. Guarde o `ocorrido_em` do último evento aplicado por ref e ignore o que for mais antigo. Sem isso você marca como autorizada uma nota cancelada. 3. Trate entrega REPETIDA. Reenviamos até 16 vezes com espera crescente enquanto você não responder 2xx, e uma entrega pode chegar duas vezes se a sua resposta se perder. Use o `evento_id`, que é único e estável, como chave de deduplicação. Não ter webhook cadastrado é uma escolha válida: nesse caso consulte `GET /v2/nfse/`. Mas não fique consultando em laco apertado, porque cada consulta de nota pendente pergunta a prefeitura. Espere 2 s, depois 4, 8, 16, até 60 s entre consultas, e desista de consultar depois de 5 minutos: o serviço resolve sozinho e o próximo GET traz o desfecho. ## O que este serviço nunca faz - Responder 502, 503 ou 504 em rota de API. Falha de dependência sai como 422 com corpo próprio, porque a borda da Cloudflare troca o corpo de 5xx pela página dela e o `fetch` do cliente cairia no catch sem a mensagem. - Reenviar sozinho uma nota sem resposta conclusiva. - Truncar ou "arrumar" dado fiscal em silêncio: o que passa do limite é recusado com mensagem e correção, porque cortar um nome ou arredondar um valor produz uma nota diferente da que o cliente pediu e ele só descobre na apuração. - Guardar o seu token em claro, ou o seu certificado fora do cofre. - Entregar PDF ou XML por link público. # Guia de integração completo O texto abaixo é o MESMO guia que uma pessoa lê em docs/integracao-cliente.md. Ele fica aqui embutido de propósito: guia para máquina e guia para gente que divergem produzem integração que passa no teste e falha na prefeitura. --- # TexFiscal: guia de integração para o cliente Serviço de emissão de NFS-e da SeuSaúde. A API fala o contrato REST v2 de NFS-e que a maioria dos ERPs já integra (mesmos caminhos, campos e literais de `status`): quem já emite por API troca a URL base e o token. ## Endereços | Ambiente | URL base | Para que serve | |---|---|---| | Homologação | `https://homologacao-nfse.texfiscal.com.br` | Testes. As notas vão ao ambiente de teste da prefeitura e não têm valor fiscal. | | Produção | `https://nfse.texfiscal.com.br` | Notas reais. | Se a sua integração é anterior a 19/09/2026, o `.env` pode apontar para o endereço antigo do serviço. Ele continua atendendo, não será desligado e não há prazo para trocar: os dois endereços respondem o mesmo contrato, com o mesmo token. Integração nova usa os endereços da tabela acima. Cada ambiente tem o próprio token. Um token de homologação apresentado em produção (ou vice-versa) recebe 401. ## Autenticação HTTP Basic com o token como usuário e senha vazia, no mesmo formato do contrato v2: ``` Authorization: Basic base64("SEU_TOKEN:") ``` Exemplo com curl: `curl -u "SEU_TOKEN:" https://homologacao-nfse.texfiscal.com.br/v2/nfse/venda-123` Você mesmo gera os tokens no painel (`https://painel.texfiscal.com.br`, menu Tokens), um por sistema e por ambiente. O valor aparece uma vez e não pode ser recuperado (guardamos só o hash): se perder, revogue e gere outro. No painel também ficam as notas com payload, retorno, XML e PDF, os webhooks com as entregas, as requisições feitas à API (com o motivo de cada recusa) e um simulador que emite em homologação. ## Emitir `POST /v2/nfse?ref=` com o JSON abaixo. A `ref` é a chave de idempotência: repetir o POST com a mesma `ref` **não** emite de novo, devolve a nota que já existe. Até 64 caracteres entre letras, números, ponto, hífen e sublinhado; maiúsculas e minúsculas são diferentes. Não pode começar com ponto nem terminar com sufixo de arquivo reservado (`.env`, `.ini`, `.log`, `.sql`, `.md`, `.bak`, `.old`, `.key`, `.pem`, `.p12`, `.pfx` e afins): essas refs são recusadas com 422 na emissão. ```json { "data_emissao": "2026-08-27T10:00:00", "optante_simples_nacional": true, "regime_especial_tributacao": 6, "prestador": { "cnpj": "11521336000116" }, "tomador": { "cpf": "52998224725", "razao_social": "Maria de Assunção Silva", "email": "maria@exemplo.com", "endereco": { "logradouro": "Rua das Flores", "numero": "100", "bairro": "Centro", "codigo_municipio": "2910800", "uf": "BA", "cep": "44001000" } }, "servico": { "valor_servicos": 150.00, "aliquota": 2.00, "iss_retido": false, "item_lista_servico": "4.10", "codigo_tributario_municipio": "0410", "discriminacao": "Consulta nutricional." } } ``` Regras que valem a pena saber antes: - Valores com ponto decimal e no máximo duas casas (`150.00`, nunca `"1.500,00"`). - `aliquota` em percentual (`2.00` = dois por cento). Abaixo de 1 é recusada como fração. - `data_emissao` em ISO 8601; em formato brasileiro é recusada. Omita para usar agora. - `prestador.cnpj`, se enviado, precisa ser o do emissor do token. - Limites do padrão ABRASF: nome do tomador 150, logradouro 125, bairro e complemento 60, número 10, e-mail 80, discriminação 2000. O que passa é recusado com mensagem, nunca cortado. - `optante_simples_nacional` e `regime_especial_tributacao` podem ser omitidos: valem os do cadastro do emissor. Respostas: | HTTP | Significado | |---|---| | 200 | Nota autorizada na hora (`status: autorizado`). | | 202 | Em processamento (`status: processando_autorizacao`). Consulte depois ou espere o webhook. | | 422 | Recusada: `codigo`, `mensagem` e `erros[]` com `codigo`, `mensagem` e `correcao` por campo. Nada foi transmitido; corrija e reenvie com a mesma `ref`. | | 401 | Token ausente, inválido ou do outro ambiente. | | 405 | Método não aceito na rota; o cabeçalho `Allow` diz quais valem. | | 429 | Muitas requisições (10 por segundo por IP), com corpo JSON (`codigo: limite_de_requisicoes`). | | 413 | Corpo acima de 512 KB, com corpo JSON (`codigo: corpo_excede_limite`). | A resposta da nota tem os campos: `ref`, `status`, `numero`, `numero_rps`, `serie_rps`, `codigo_verificacao`, `data_emissao`, `url` (PDF), `caminho_danfse`, `caminho_xml_nota_fiscal`, `mensagem`, `erros`. O campo `status_texfiscal` traz o estado interno, só para diagnóstico. Literais de `status`: `autorizado`, `erro_autorizacao`, `processando_autorizacao`, `cancelado`, `processando_cancelamento`. ## Consultar `GET /v2/nfse/`. Se a nota estiver em processamento, a consulta pergunta à prefeitura antes de responder. 404 quando a `ref` não existe. ## PDF e XML - `GET /v2/nfse//pdf`: DANFSe em PDF (gerado do XML autorizado). Exige o token, como tudo aqui: não é link público. - `GET /v2/nfse//xml`: XML da NFS-e autorizada, como a prefeitura devolveu. ## Cancelar `DELETE /v2/nfse/` com `{"justificativa": "texto com ao menos 15 caracteres", "motivo": 1}`. Só nota autorizada cancela. 200 com `status: cancelado`; 202 com `processando_cancelamento` quando a prefeitura não respondeu de forma conclusiva (repita o DELETE ou consulte: o serviço confere e resolve); 422 `cancelamento_recusado` quando a prefeitura recusou. Duas coisas para saber antes de desenhar o seu botão de cancelar: - **Prazo: existe, é de cada PREFEITURA e nós não o conhecemos.** O município define até quando uma NFS-e pode ser cancelada, e esse prazo não está em lugar nenhum desta API: nós transmitimos o pedido e devolvemos a resposta que vier. Quem integra supondo um cancelamento que vale para sempre descobre o prazo levando 422 `cancelamento_recusado` no dia em que já não dá para consertar. Confirme o prazo do seu município, trate a nota como definitiva depois dele e resolva o que passou do prazo pela via administrativa da prefeitura, não pela API. - **São dois campos, e confundi-los declara errado ao município.** O padrão ABRASF não tem campo livre para justificativa: o que vai no XML é um código numérico. Por isso o corpo tem os dois: - `motivo` (opcional, `1`, `2` ou `3`, padrão `1`) **é a declaração ao fisco.** `1` erro na emissão, `2` serviço não prestado, `3` erro de assinatura. Informe o valor correto: `1` e `2` dizem coisas diferentes ao município, e cancelar um serviço que de fato não foi prestado declarando `1` é declaração fiscal errada. Valor fora de `1..3` é recusado com 422 `motivo_invalido`, em vez de virar `1` em silêncio. - `justificativa` (obrigatória, mínimo 15 caracteres) **fica conosco e nunca chega à prefeitura.** Ela é gravada na trilha de eventos da nota no momento do PEDIDO, junto com o motivo, e vale também quando o cancelamento fica em 202 sem desfecho, que é justamente o caso que alguém vai querer auditar depois. Escreva pensando em quem for ler a trilha daqui a dois anos; o que precisar ser dito à prefeitura vai pelo canal dela. ## Webhook (opcional) Cadastramos uma URL sua (https) por ambiente. A cada transição da nota que importa para você (`nfse_autorizada`, `nfse_erro`, `nfse_cancelada` e `nfse_cancelamento_nao_efetivado`, este último quando um pedido de cancelamento em processamento não constou na prefeitura e a nota voltou a autorizada: repita o DELETE) fazemos `POST` nela com o mesmo JSON do GET, mais `evento` e `ref` no topo. Cabeçalhos: - `X-Webhook-Token`: o segredo combinado (o mesmo cabeçalho do contrato v2; se o seu receptor já valida esse cabeçalho, nada muda). - `X-TexFiscal-Assinatura`: HMAC-SHA256 do corpo com o segredo, em hexadecimal, para quem quiser conferir a integridade. - `X-TexFiscal-Evento` e `X-TexFiscal-Tentativa`. Responda 2xx rápido: a primeira tentativa, feita na hora da transição, espera **5 segundos**; as reentregas agendadas esperam 20. O "Reenviar" e o "Enviar teste" do painel também esperam 5 segundos, porque rodam enquanto a tela aguarda. Receptor que demora mais que isso na primeira tentativa recebe o aviso pela reentrega, cerca de 1 minuto depois. Sem 2xx, reentregamos com recuo (1, 5, 15, 60 minutos, depois a cada 6 horas) por até 3 dias. Um aviso pode chegar mais de uma vez em situação de falha: trate por `ref` e `status`, de forma idempotente. ## Uso `GET /v2/uso?mes=AAAA-MM`: quantidade e valor por `status` no mês, no ambiente do token. É a mesma base da cobrança. ## Erros que a prefeitura devolve Chegam em `erros[]` com o código da prefeitura (ex.: `E160` XML fora do schema, `E10` RPS já informado, `E221` alíquota informada indevidamente), a mensagem e a correção sugerida. Nota em `erro_autorizacao` pode ser reenviada com a mesma `ref` depois de corrigida. ## O que nunca fazemos - Reenviar uma nota sem resposta conclusiva. Só consulta resolve; reenvio duplica documento fiscal. - Responder 502, 503 ou 504 em rota de API. - Guardar o seu token em claro, ou o seu certificado fora do cofre.