Pular para o conteúdo principal

Catálogo de eventos de webhook — NFS-e

Esta página documenta os 7 eventos de webhook do eventType service_invoice. O corpo vem sempre no formato {"action": "...", "payload": {...}}: a ação do evento em action e a nota em payload. Veja Payloads de emissão para a regra geral dos dois envelopes.

Diferente de NF-e e NFC-e, o payload de NFS-e não tem array lastEvents. O histórico de tentativas não é exposto — você recebe apenas flowStatus e, em erro, flowMessage.

Política de entrega​

  • Entrega: at-least-once. Garanta idempotência por X-Hook-Id.
  • Retry: reentrega automática em falha de rede ou resposta não-2xx — até 16 tentativas ao longo de ~45 h, exceto nos códigos de recusa definitiva. Veja os números da política de entrega.
  • Timeout: responda 2xx rápido e processe de forma assíncrona.
  • Assinatura: valide o HMAC do cabeçalho antes de processar. Veja Dúvidas frequentes.

Como issued_error e issued_failed se distinguem​

A distinção chega pronta no campo action do corpo: issued_error ou issued_failed. Use esse campo.

Para referência, a NFE.io aplica uma regra simples e literal: se flowMessage começa com o texto "max retry", o evento é issued_failed — a emissão esgotou o número de tentativas. Qualquer outro texto em flowMessage gera issued_error — uma rejeição pontual da prefeitura ou uma falha de comunicação isolada.

No cancelamento, a regra tem um passo antes: se o cancelamento falhou e a nota continua emitida (status: "Issued") — o caso comum de recusa da prefeitura —, o evento é sempre cancelled_failed, qualquer que seja o flowMessage. Nota já com status: "Cancelled" sempre gera cancelled_successfully. Nos demais status vale a regra do texto: flowMessage começando com "max retry" gera cancelled_failed; qualquer outro erro gera cancelled_error.

Não tente inferir o tipo de erro pelo flowStatus

flowStatus é o mesmo (Error ou IssueFailed/CancelFailed) nos dois casos. A distinção entre issued_error e issued_failed chega pronta no campo action do corpo. O cabeçalho X-Hook-Event traz só o tipo (service_invoice).

Eventos de emissão​

service_invoice.issued_successfully​

Quando dispara: a NFS-e foi emitida e autorizada pela prefeitura.

Payload:

{
"action": "issued_successfully",
"payload": {
"id": "d4911190b46ba44f",
"externalId": "seu-id-externo",
"environment": "Production",
"flowStatus": "Issued",
"provider": {
"tradeName": "Atacado Ferreira & Filhos LTDA",
"taxRegime": "SimplesNacional",
"specialTaxRegime": "MicroempresaMunicipal",
"legalNature": "SociedadeEmpresariaLimitada",
"companyRegistryNumber": 6202300,
"regionalTaxNumber": 355030999,
"municipalTaxNumber": "44338200330345",
"issRate": 0.0,
"id": "265f492ca6f35591",
"name": "Atacado Ferreira & Filhos LTDA",
"federalTaxNumber": 44338200330345,
"email": "contato@atacadoferreira.example.com",
"address": {
"street": "Avenida Central",
"number": "955",
"city": { "code": "3550308", "name": "Sao Paulo" },
"state": "SP",
"postalCode": "39257-113",
"country": "BRA"
},
"status": "Active",
"type": "LegalPerson, Company"
},
"borrower": {
"id": "654b17b903ade39e",
"name": "Carlos Eduardo Lima",
"federalTaxNumber": 30817158650,
"email": "contato842@example.com",
"address": {
"street": "Rua Sete de Setembro",
"number": "291",
"city": { "code": "3304557", "name": "Rio de Janeiro" },
"state": "RJ",
"postalCode": "87858-233",
"country": "BRA"
},
"status": "Active",
"type": "NaturalPerson"
},
"apiVersion": 2,
"issuedOn": "2026-08-17T21:46:14-03:00",
"number": 6909,
"status": "Issued",
"rpsType": "Rps",
"rpsStatus": "Normal",
"taxationType": "WithinCity",
"rpsSerialNumber": "ZZ",
"rpsNumber": 3610,
"cityServiceCode": "5771",
"federalServiceCode": "15.01",
"servicesAmount": 70.00,
"baseTaxAmount": 70.00,
"issRate": 0.02,
"issTaxAmount": 0.0,
"amountNet": 70.00
}
}

Em NFS-e, o federalTaxNumber de provider e de borrower chega como número quando o valor é numérico e a nota foi criada pela API v1/v2 — é o caso deste exemplo (apiVersion: 2). Chega como string quando o valor é alfanumérico ou quando a nota foi criada pela API v3.

Como número, o zero à esquerda some: o CNPJ 09505320001905 chega como 9505320001905. Normalize para string com zeros à esquerda antes de comparar: 14 dígitos para CNPJ, 11 para CPF. Campos nulos são omitidos, não vêm como null.

Idempotency key: payload.id ou o cabeçalho X-Hook-Id.

service_invoice.issued_error​

Quando dispara: a prefeitura rejeitou a nota, ou houve falha pontual de comunicação — não é esgotamento de retry.

Payload: mesmo shape de issued_successfully, com:

{
"action": "issued_error",
"payload": {
"flowStatus": "IssueFailed",
"flowMessage": "[1001] XML não compatível com Schema. The 'CodigoServico' element is invalid - The value '040802.001' is invalid according to its datatype '...:tpCodigoServico' - The Pattern constraint failed.",
"status": "Error"
}
}

Mensagens de issued_error variam bastante — vêm de rejeições distintas da prefeitura ou de falha de validação de schema, como no exemplo acima. Trate flowMessage como texto livre para exibição/log, não como valor para parsing por padrão fixo.

flowStatus em issued_error é sempre IssueFailed

Mesmo quando a ação do webhook é issued_error (não issued_failed), o campo flowStatus no corpo aparece como IssueFailed — é o mesmo estado interno para os dois casos. A distinção entre as duas ações chega pronta no campo action do corpo. Ela segue o prefixo "max retry" de flowMessage, nunca o flowStatus.

Idempotency key: payload.id.

service_invoice.issued_failed​

Quando dispara: a NFE.io esgotou as tentativas de comunicação com o webservice da prefeitura. flowMessage sempre começa com "max retry".

Payload: mesmo shape de issued_successfully, com:

{
"action": "issued_failed",
"payload": {
"flowStatus": "IssueFailed",
"flowMessage": "max retry: falha na comunicacao com o webservice da prefeitura apos numero maximo de tentativas",
"status": "IssueFailed"
}
}

Idempotency key: payload.id.

Eventos de cancelamento​

service_invoice.cancelled_successfully​

Quando dispara: o cancelamento foi homologado pela prefeitura.

Payload: mesmo shape de issued_successfully, com:

{
"action": "cancelled_successfully",
"payload": {
"flowStatus": "Cancelled",
"status": "Cancelled",
"rpsStatus": "Cancelled"
}
}

Idempotency key: payload.id.

service_invoice.cancelled_error​

Quando dispara: o cancelamento falhou por um erro pontual (não é esgotamento de retry) e a nota não está com status Issued nem Cancelled. A recusa comum da prefeitura, com a nota ainda emitida, chega como cancelled_failed (veja abaixo).

Payload: mesmo shape base, com:

{
"action": "cancelled_error",
"payload": {
"flowStatus": "CancelFailed",
"flowMessage": "Falha de comunicacao com o webservice da prefeitura",
"status": "Error"
}
}

O exemplo é ilustrativo: status diferente de Issued e de Cancelled é o que separa este evento do cancelled_failed e do cancelled_successfully.

Idempotency key: payload.id.

service_invoice.cancelled_failed​

Quando dispara: o cancelamento falhou e a nota continua emitida (status: "Issued") — por exemplo, a prefeitura recusou o pedido —, ou a NFE.io esgotou as tentativas de comunicar o cancelamento (flowMessage começando com "max retry").

Payload: mesmo shape base, com:

{
"action": "cancelled_failed",
"payload": {
"flowStatus": "CancelFailed",
"flowMessage": "Prazo de cancelamento expirado junto a prefeitura",
"status": "Issued"
}
}

Note que status permanece Issued — o cancelamento falhou, a nota continua válida.

Idempotency key: payload.id.

Evento sem exemplo observado: pulled​

pulled existe no contrato de eventos de NFS-e, mas não teve nenhuma ocorrência registrada em 180 dias de produção até a publicação desta página. Não documentamos payload de exemplo para não apresentar uma estrutura hipotética como real. Se você assinar este evento e receber uma entrega, entre em contato — vamos atualizar esta página com o caso real.

Como validar a assinatura​

Veja o exemplo de validação de HMAC em Dúvidas frequentes.

Veja também​

NFE.io

A NFE.io é uma empresa de tecnologia que fornece soluções para automatizar e simplificar a emissão e gestão de notas fiscais eletrônicas. Com suas ferramentas, as empresas podem economizar tempo e reduzir erros, aumentando a eficiência e precisão do processo de emissão de notas fiscais.

Um dos principais cases de sucesso da NFE.io é a implementação da solução na empresa de transporte Rodonaves. Com a automatização da emissão e gestão de notas fiscais eletrônicas, a Rodonaves conseguiu reduzir em até 80% o tempo gasto nesse processo, o que se traduziu em uma significativa melhoria na eficiência operacional. Além disso, a empresa também conseguiu eliminar erros e atrasos na emissão de notas fiscais, o que melhorou a relação com seus clientes e aumentou a confiança dos órgãos fiscais.

Outro exemplo é a implementação da NFE.io na empresa de comércio eletrônico, a Loja Integrada. Com a automatização da emissão de notas fiscais, a Loja Integrada conseguiu aumentar a velocidade de emissão de notas em até 10 vezes, o que permitiu que a empresa atendesse a uma maior quantidade de clientes e, consequentemente, aumentar as suas vendas.

Além desses exemplos, a NFE.io também tem outros cases de sucesso com empresas de setores como indústria, construção, varejo e serviços, mostrando a versatilidade e eficácia da sua solução.

Em resumo, a NFE.io é uma empresa de tecnologia que oferece soluções para automatizar e simplificar a emissão e gestão de notas fiscais eletrônicas, ajudando as empresas a economizar tempo e reduzir erros, melhorando a eficiência e precisão do processo. Com cases de sucesso em diferentes setores, a NFE.io tem se destacado como uma empresa líder em automação fiscal.