esesistemasDocumentação

API de NF-e

Integre o seu sistema e emita NF-e

A API recebe os dados da nota, confere, assina com o certificado A1 da empresa e transmite à SEFAZ. O seu sistema informa os tributos; a API confere as contas e as regras e devolve o que estiver errado antes de gastar um número.

Homologaçãohttps://api-hml.esesistemas.com.brTestes, sem valor fiscal. Chaves começam com nfe_test_.
Produçãohttps://api.esesistemas.com.brNotas com valor fiscal. Chaves começam com nfe_live_.

Todas as chamadas usam HTTPS e JSON (Content-Type: application/json). Valores em dinheiro e quantidades vão como texto com ponto decimal ("150.00"), para não perder centavos.

Guia rápido

  1. Crie a conta em painel.esesistemas.com.br e confirme o e-mail.
  2. Cadastre a empresa e envie o certificado A1 (.pfx). Ele comprova que a empresa é sua e fica guardado cifrado; a API nunca o devolve.
  3. Configure a empresa em “Abrir painel da empresa”: dados do emitente, série e próximo número de homologação.
  4. Crie uma chave de homologação em “Chaves de API” e guarde no seu sistema (nunca no código-fonte).
  5. Emita notas de teste até sair tudo autorizado, inclusive cancelamento e carta de correção, se usar.
  6. Libere a produção no painel, crie a chave de produção e troque o endereço e a chave no seu sistema.

Autenticação

Envie a chave em todas as chamadas, num destes cabeçalhos:

Authorization: Bearer nfe_test_...
X-API-Key: nfe_test_...

Cada chave pertence a uma empresa e a um ambiente, e só pode o que foi marcado ao criá-la: emitir, consultar, cancelar, inutilizar e configurar. Uma chave de homologação não funciona em produção, e vice-versa. Para conferir a chave:

Proteja a chave.Quem tem a chave emite em nome da empresa. Guarde em variável de ambiente ou cofre de segredos, nunca no navegador nem em aplicativo de celular. Vazou? Revogue no painel e crie outra: a antiga para na hora.

Emitir uma NF-e

POST /v1/nfe/emissoes (permissão emitir). Mande sempre um Idempotency-Key único por nota: se a conexão cair e você repetir a chamada com a mesma chave e o mesmo documento, recebe a mesma emissão de volta, sem gastar outro número.

O corpo (nota.json), numa venda dentro do estado para consumidor final:

{
  "document": {
    "external_id": "venda-1001",
    "nature_of_operation": "Venda de mercadoria",
    "operation": {"final_consumer": true, "presence_indicator": "1"},
    "recipient": {
      "document": "11444777000161",
      "name": "CLIENTE LTDA",
      "state_registration_indicator": "9",
      "address": {"postal_code": "01001000", "street": "Praca da Se", "number": "1", "district": "Se",
                  "municipality": "Sao Paulo", "municipality_ibge_code": "3550308", "state": "SP"}
    },
    "items": [{
      "code": "TINTA-18L", "description": "Tinta acrilica 18L", "ncm": "32091010", "cfop": "5102", "unit": "UN",
      "quantity": "2", "unit_price": "150.00", "discount": "10.00",
      "icms": {"origin": "0", "csosn": "101", "credit_rate": "1.25", "credit_amount": "3.63"},
      "pis": {"cst": "01", "base": "290.00", "rate": "0.65", "amount": "1.89"},
      "cofins": {"cst": "01", "base": "290.00", "rate": "3.00", "amount": "8.70"}
    }],
    "freight_mode": "9",
    "payments": [{"method": "17", "amount": "290.00", "indicator": "0", "integration_type": "2"}],
    "expected_total": "290.00"
  },
  "transmit": true
}
external_id
A sua referência (número do pedido ou da venda). Só pode existir uma nota ativa por referência; use-a para buscar depois.
transmit
true: prepara e transmite na mesma chamada. false: só prepara e assina; transmita depois com POST /v1/nfe/{id}/transmitir.
expected_total
O total que o seu sistema calculou. Se a soma dos itens ou dos pagamentos não bater, a nota é recusada antes de gastar o número.
payments[].method
Código tPag da SEFAZ (01 dinheiro, 03 crédito, 04 débito, 15 boleto, 17 PIX…). Cartão e PIX pedem integration_type.

A resposta traz a emissão, com o número, a chave de acesso e, se transmitiu, o resultado da SEFAZ (resumida):

{
  "emission": {
    "emission_id": "6f0c…", "state": "autorizada", "environment": "homologacao",
    "series": 1, "invoice_number": 1201, "access_key": "3526…",
    "protocol": "135260000000001", "response_cstat": "100", "response_reason": "Autorizado o uso da NF-e"
  },
  "external_id": "venda-1001",
  "replayed": false,
  "transmission": {"attempted": true, "delivery": "...", "http_status": 200}
}

A transmissão é feita uma vez por chamada, nunca repetida por conta própria. Se a SEFAZ não confirmar a tempo, a nota fica em falha_ambigua e a API consulta a situação antes de qualquer reenvio, para nunca autorizar a mesma venda duas vezes.

Estados da nota

preparadaConferida, numerada e assinada; ainda não foi à SEFAZ.
transmitindo · processandoNa SEFAZ. Consulte de novo em alguns segundos.
falha_ambiguaA SEFAZ não respondeu com certeza. Use “consultar situação”; não emita outra nota para a mesma venda.
autorizadaNota válida. XML e DANFE disponíveis.
rejeitadaA SEFAZ recusou (motivo em response_reason). Corrija e use a nova tentativa.
denegadaUso denegado pela SEFAZ (irregularidade fiscal do emitente ou do destinatário). O número fica usado.
canceladaCancelamento registrado na SEFAZ.

Consultar, XML e DANFE

GET /v1/nfe/{id}Estado atual da nota.
GET /v1/nfe/emissoes?external_id=venda-1001Busca pela sua referência.
POST /v1/nfe/{id}/consultar-situacaoPergunta à SEFAZ (útil em falha_ambigua e processando).
GET /v1/nfe/{id}/xml-autorizadoXML autorizado (nfeProc), o que deve ser guardado por 5 anos e enviado ao cliente.
GET /v1/nfe/{id}/danfeDANFE em PDF.
GET /v1/nfe/{id}/eventosHistórico de eventos (cancelamento, cartas de correção).

Em vez de consultar repetidamente, prefira os webhooks: a API avisa o seu sistema quando a nota muda de estado.

Rejeição e nova tentativa

Quando a SEFAZ rejeita, corrija o que o motivo indicar e chame POST /v1/nfe/emissoes/{id}/nova-tentativa com o documento corrigido (mesmo external_id, mesmos itens e quantidades). A nota é preparada com a mesma série, número e chave, sem buraco na numeração.

Cancelar

POST /v1/nfe/{id}/cancelar (permissão cancelar), em nota autorizada e dentro do prazo da SEFAZ. O confirm: true é obrigatório: sem ele nada é enviado.

{"confirm": true, "justification": "Pedido cancelado pelo cliente antes da entrega", "operator": "Maria (financeiro)"}

A justificativa precisa ter de 15 a 255 caracteres. O XML do evento fica em GET /v1/nfe/{id}/cancelamento/xml.

Carta de correção

POST /v1/nfe/{id}/carta-correcao corrige dados que não mudam valores, impostos, destinatário nem data. Cada carta substitui a anterior, então envie o texto completo.

{"confirm": true, "text": "Onde se le Rua das Flores 100, leia-se Rua das Flores 1000.", "operator": "Maria (financeiro)"}

O texto vai de 15 a 1000 caracteres. As cartas ficam em GET /v1/nfe/{id}/cartas-correcao.

Inutilizar números

Se um número foi pulado e nunca vai ser usado, inutilize-o: POST /v1/nfe/inutilizacoes (permissão inutilizar). GET /v1/nfe/inutilizacoes/pendentes mostra os buracos da numeração.

{"confirm": true, "series": 1, "number_from": 1205, "number_to": 1205,
 "justification": "Numero pulado por falha no sistema de vendas", "operator": "Maria (financeiro)"}

Webhooks

Cadastre um endereço HTTPS do seu sistema com POST /v1/webhooks/endpoints (permissão configurar). O segredo whsec_… aparece só nessa resposta: guarde-o.

{"url": "https://meusistema.com.br/nfe/webhook", "events": ["nfe.autorizada", "nfe.rejeitada", "nfe.cancelada"]}

Eventos: nfe.autorizada, nfe.rejeitada, nfe.denegada, nfe.cancelada e nfe.resultado_pendente (lista vazia = todos). O corpo:

{"id": "evt_…", "type": "nfe.autorizada", "created_at": "2026-11-04T13:00:00Z",
 "data": {"emission_id": "6f0c…", "external_id": "venda-1001", "state": "autorizada", "previous_state": "transmitindo",
          "environment": "homologacao", "series": 1, "invoice_number": 1201, "access_key": "3526…",
          "protocol": "135260000000001", "cstat": "100", "reason": "Autorizado o uso da NF-e"}}

Toda entrega é assinada no cabeçalho X-NFe-Assinatura: t=<unix>,v1=<hex>, com v1 = HMAC-SHA256 do segredo sobre "<t>.<corpo>". Confira antes de confiar no conteúdo, recuse t com mais de 5 minutos e use X-NFe-Entrega para ignorar repetições (a entrega é “pelo menos uma vez”).

Responda com 2xx em até 5 segundos. Sem 2xx, a API tenta de novo em 1 min, 5 min, 30 min, 2 h e 6 h. GET /v1/webhooks/entregas mostra as tentativas, e POST /v1/webhooks/endpoints/{id}/teste manda um evento de teste.

Erros

Os erros vêm com {"detail": ...}. Na emissão, as pendências da nota vêm todas juntas, uma por campo, e nada é gravado:

{"detail": {"message": "O documento tem pendências; nada foi gravado nem enviado", "issues": [
  {"field": "items[0].pis.amount", "code": "tributo_divergente",
   "message": "valor 1.90 não confere com base × alíquota (1.89)"}
]}}
401Chave ausente, errada ou revogada.
403A chave não tem a permissão desta rota, ou é de outro ambiente.
404Nota não encontrada para esta empresa.
402A cota do plano grátis acabou no mês. A emissão em produção volta no mês seguinte ou ao assinar um plano; a homologação segue liberada.
409Conflito: external_id já tem nota ativa, Idempotency-Key repetida com outro conteúdo, ou produção ainda não liberada.
422Dados da nota com pendências (veja issues), certificado ausente ou vencido, empresa não configurada.
429Muitas chamadas à SEFAZ em pouco tempo. Espere e tente de novo.

Escopo e limites

  • Hoje: NF-e modelo 55, emitente do Simples Nacional (CRT 1 ou 4), venda (finalidade 1), CSOSN 101, 102, 103, 300, 400 e 500.
  • Ainda não: NFC-e, regime normal, ICMS-ST, IPI, frete/seguro/outras despesas, DIFAL, importação e exportação, devolução. Esses casos são recusados com o motivo, nunca ignorados.
  • Planos: conta cada documento autorizado em produção, somando as empresas da conta. O plano grátis tem 60 por mês e para na cota; os pagos seguem emitindo e cobram o excedente. Veja em “Plano e uso” no painel.
  • SEFAZ: até 5 autorizações, 10 consultas e 5 eventos a cada 10 minutos por empresa e ambiente.
  • Certificado: o painel avisa quando o A1 estiver perto de vencer. Troque em “Empresas → Trocar o certificado”.

Vindo de outro emissor

  1. Mantenha a numeração. A série e o próximo número de produção são os da última nota autorizada no emissor antigo, mais 1. Confira com o contador antes de liberar.
  2. Traga o histórico. Em “Abrir painel da empresa → Importar notas”, envie XMLs autorizados (ou um ZIP): a API sugere o cadastro de produtos e regras a partir deles. Os arquivos não ficam guardados.
  3. Teste em paralelo. Integre em homologação enquanto o emissor antigo continua em produção.
  4. Vire num momento calmo. Pare de emitir no antigo, confira o último número, libere a produção aqui e troque endereço e chave no seu sistema.
  5. Guarde os XMLs antigos. Notas emitidas antes continuam valendo e precisam ser guardadas por 5 anos; cancelamentos e cartas delas seguem no emissor antigo.

Ir para produção

  • Notas autorizadas em homologação com os seus casos reais (consumidor final, empresa, PIX, cartão…).
  • Cancelamento e carta de correção testados, se o seu sistema usa.
  • Webhooks recebendo e conferindo a assinatura.
  • XML autorizado sendo guardado e enviado ao cliente.
  • Série e próximo número de produção conferidos com o contador.
  • Verificação em duas etapas ativa no painel (exigida para liberar a produção).

No painel, o administrador libera a produção em “Empresas → Liberar a produção” e cria a chave de produção. No seu sistema, troque o endereço para https://api.esesistemas.com.br e a chave para a nfe_live_…. Os campos do corpo não mudam.

Dúvidas?Escreva para contato@esesistemas.com.br. A referência completa de rotas e campos está em api-hml.esesistemas.com.br/docs.