Guia: Emitir NFe
Guia completo para emitir uma Nota Fiscal Eletrônica (NFe) via engineAPI. Todos os campos, regimes tributários e tratamento de erros.
Emitir NFe
Emita uma Nota Fiscal Eletrônica (NFe, modelo 55) e transmita para a SEFAZ em uma única chamada REST.
Endpoint: POST https://api.engineapi.com.br/v1/nfe
Quais campos enviar? Este guia cobre os campos mais usados. A lista completa, navegável por grupo (Identificação, Destinatário, Itens, Impostos, Transporte, Pagamento...) e gerada direto do contrato real, está no Catálogo de campos: NFe. Para navegar por endpoint em vez de por documento, veja a Referência por Endpoint.
Este é o guia completo (todos os campos, regimes tributários, tratamento de erros). Se você só quer o checklist rápido antes da 1ª emissão, veja Primeira Emissão.
Pré-requisitos
Conta criada
Obtenha seu token via POST /v1/auth/login ou uma API Key (ek_live_/ek_test_) no Dashboard.
Empresa cadastrada
Cadastre o CNPJ emissor via POST /v1/companies. Guarde o id retornado.
Certificado digital enviado
Faça upload do .pfx via POST /v1/companies/{id}/certificate (campo multipart file). Sem certificado, a emissão falha.
Ambiente definido
Todo emissor nasce em homologação (ambienteFiscal: 2). Ver Sandbox para o estado real de ir pra produção.
Notas em homologação (ambienteFiscal: 2) são transmitidas para o SEFAZ de teste e não têm validade fiscal. Use para testar sem risco.
Emissor (issuerId) é opcional no payload de NFe/NFCe. Com um único emissor
cadastrado, pode omitir (a API usa o seu emissor). Com dois ou mais, informe
issuerId (UUID) para escolher o CNPJ; sem ele a API responde 400. Ver
Autenticação.
Exemplos de Request
curl -X POST https://api.engineapi.com.br/v1/nfe \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"naturezaOperacao": "VENDA DE MERCADORIA",
"idDest": 1,
"indFinal": 1,
"destinatario": {
"cnpjCpf": "99888777000100",
"nome": "Cliente Exemplo SA",
"endereco": {
"logradouro": "Av Goiás",
"numero": "500",
"bairro": "Centro",
"codigoMunicipio": "5208707",
"municipio": "Goiânia",
"uf": "GO",
"cep": "74063010"
},
"indicadorIE": 9
},
"items": [{
"codigo": "PROD001",
"descricao": "Camiseta Algodão P",
"ncm": "61091000",
"cfop": "5102",
"unidade": "UN",
"quantidade": 2,
"valorUnitario": 59.90,
"icms": {
"origem": 0,
"csosn": "102"
}
}],
"pagamentos": [{ "forma": "01", "valor": 119.80 }]
}'
Vendendo para outra empresa (contribuinte de ICMS)? Informe indicadorIE: 1 e a
ie real do destinatário: a SEFAZ valida o vínculo IE×CNPJ no cadastro dela.
Sem a IE a nota é rejeitada com cStat 728; com IE que não pertence ao CNPJ, cStat
234. Destinatário isento usa indicadorIE: 2 (sem ie); consumidor que não é
contribuinte (o exemplo acima), indicadorIE: 9.
ICMS por Regime Tributário
O campo icms do item muda conforme o regime da empresa emissora:
PIS, COFINS e IPI
O motor é passthrough: o que você informa é o que vai no documento, nada é
calculado aqui. Quando uma combinação não pode ser escrita no documento com
segurança, a emissão recusa com 422 antes de consumir número fiscal (ver
Erros e Rejeições). Campo aceito e ignorado em silêncio não
existe nesta API.
Campos de Referência
Nomes de campo divergentes
deste contrato são rejeitados com 400.
Raiz
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
naturezaOperacao | string | Não | Ex: "VENDA DE MERCADORIA" |
serie/numero/tpNF/idDest/indFinal/indPres/finNFe | número | Não | Campos de identificação (ide), todos opcionais |
destinatario | objeto | Sim | Dados do destinatário |
items | array | Sim (mín. 1) | Não é itens |
transporte | objeto | Não | Dados de transporte (modFrete obrigatório se enviado) |
pagamentos | array | Sim (mín. 1) | Não é pagamento singular |
troco | número | Não | Troco (NFCe/venda a consumidor) |
informacoesComplementares/informacoesFisco | string | Não | Informações adicionais |
resolverTributacao | boolean | Não | Ativa a emissão assistida (ver acima) |
Destinatário
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
cnpjCpf | string (11–14 dígitos) | Sim | Não é cnpj/cpf separados |
nome | string | Sim | Razão social ou nome |
ie | string | Não | Não é inscricaoEstadual. Obrigatória (e validada pela SEFAZ contra o CNPJ) quando indicadorIE: 1 |
indicadorIE | número | Não | 1 = contribuinte (exige ie) · 2 = isento · 9 = não contribuinte |
email | string | Não | N/A |
endereco.* | objeto | Sim | Endereço completo (todos os subcampos obrigatórios exceto complemento) |
Item
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
codigo | string | Sim | Código interno do produto |
descricao | string | Sim | Descrição do produto |
ncm | string (8 dígitos exatos) | Sim | Nomenclatura Comum do Mercosul |
cfop | string | Sim | Ver CFOP |
unidade | string | Sim | UN, KG, MT, CX, etc. |
quantidade | número (min 0.0001) | Sim | Quantidade |
valorUnitario | número (min 0.01) | Sim | Valor por unidade em R$ |
valorTotal | número | Não | Calculado se ausente |
cest | string (7 dígitos) | Não | Código Especificador da ST, sem pontuação (422 CEST_INVALIDO se divergir) |
icms/ibsCbs | objeto | Não | Dados tributários do item (obrigatórios de fato só sem resolverTributacao) |
pis/cofins | objeto | Não | cst + baseCalculo/aliquota/valor, transmitidos como informados (ver acima) |
ipi | objeto | Não | Só NF-e. cst + baseCalculo/aliquota/valor + cEnq opcional. Compõe o total da nota |
Ciclo de vida da NFe
stateDiagram-v2
[*] --> PROCESSING : POST /v1/nfe
PROCESSING --> AUTHORIZED : SEFAZ aprova
PROCESSING --> REJECTED : SEFAZ rejeita (400 com erros[])
AUTHORIZED --> CANCELED : POST /v1/nfe/{idOuChave}/cancelar (até 24h)
CANCELED --> [*]
AUTHORIZED --> [*] : XML + PDF disponíveis
AUTHORIZED : ✅ AUTHORIZED
REJECTED : ❌ REJECTED
PROCESSING : ⏳ PROCESSING
CANCELED : 🚫 CANCELED
Contingência SVC
Quando a SEFAZ do estado do emissor está fora do ar, a engineAPI reroteia
automaticamente a transmissão para a SEFAZ Virtual de Contingência (SVC): você não
aciona nada, não muda o payload, não escolhe rota. A mesma chamada POST /v1/nfe segue
funcionando; só muda, por baixo, qual webservice recebe a nota.
Zero campo novo no payload. Não existe (nem precisa existir) um campo do tipo
contingencia/forcarContingencia no corpo do POST /v1/nfe: a decisão é 100%
automática, calculada a cada emissão a partir do status real da SEFAZ da UF do emissor.
Como a engineAPI decide, a cada POST /v1/nfe:
- Consulta o status da SEFAZ da UF do emissor (cache de 5 minutos).
- UP: transmite normal, nada muda.
- DOWN: reroteia para a SVC-AN ou a SVC-RS (o mapa por UF é definido pelo fisco; a engineAPI escolhe a rota certa automaticamente).
- Quando a SEFAZ da UF volta a ficar UP, a próxima emissão já transmite normal de novo, sem nenhuma ação sua.
A engineAPI implementa contingência via SVC (SVC-AN/SVC-RS), não via EPEC. Se
algum dia você inspecionar o XML autorizado, o jeito de confirmar que uma nota saiu em
contingência é o campo tpEmis da identificação: normal usa tpEmis = 1, SVC-AN usa
tpEmis = 6, SVC-RS usa tpEmis = 7. A engineAPI não expõe um status separado tipo
CONTINGENCY no Invoice: a nota chega a AUTHORIZED (ou REJECTED) do mesmo jeito,
só que autorizada pela SVC.
Consultar o status da SEFAZ
Para monitorar disponibilidade antes de decidir se vale a pena reagendar um lote, use:
curl https://api.engineapi.com.br/v1/nfe/sefaz-status/GO \
-H "Authorization: Bearer SEU_TOKEN"
{
"data": {
"uf": "GO",
"status": "UNKNOWN",
"message": "Serviço em Operação",
"responseTimeMs": 245,
"checkedAt": "2026-07-20T18:00:00.000Z",
"cStat": 107
},
"meta": {
"requestId": "req_abc123",
"timestamp": "2026-07-20T18:00:00.000Z"
}
}
Esta rota pública não usa o certificado de nenhum emissor, por isso status
sempre vem UNKNOWN nela (nunca UP/DOWN): é desenho deliberado, sem uma consulta
REAL (cert-backed) a engineAPI nunca fabrica "no ar"/"fora do ar". message e cStat
continuam vindo do provider (ex.: cStat 107 = Serviço em Operação); só status fica
UNKNOWN aqui. A decisão UP/DOWN que de fato aciona o reroteamento pra SVC roda por
dentro, com o certificado do SEU emissor, no momento de cada emissão, e não é o que esta
rota devolve. Use-a para inspecionar message/cStat, não para prever se a próxima
emissão vai sair via SVC.
GET /v1/nfe/sefaz-status (sem UF) devolve o mesmo formato para as 27 UFs de uma vez,
com o mesmo cache de 5 minutos. Ver também SEFAZ e Webservices.
Response de sucesso
Mesmo contrato de resposta nos dois caminhos de emissão (síncrono e fila), envelopado em
{ data, meta }:
{
"data": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "AUTHORIZED",
"accessKey": "35260211222333000181550010000000011000000019",
"protocol": "135260000001234",
"number": 1,
"series": 1,
"model": "55",
"amount": "119.8",
"destCNPJ": "99888777000100",
"destName": "Cliente Exemplo SA",
"createdAt": "2026-07-06T12:00:00.000Z",
"updatedAt": "2026-07-06T12:00:01.000Z",
"downloads": {
"xml": "/v1/nfe/xml/35260211222333000181550010000000011000000019",
"pdf": "/v1/nfe/pdf/35260211222333000181550010000000011000000019"
}
},
"meta": {
"requestId": "req_abc123",
"timestamp": "2026-07-06T12:00:00.000Z"
}
}
Sem issuer/customer embutidos (o parceiro já conhece os dois: foi ele quem cadastrou
o emissor e enviou o destinatário no payload) e sem xmlPath/pdfPath (caminho de arquivo
INTERNO do container). amount é string decimal ("119.8", o Decimal do banco
serializa sem zero à direita, mesmo formato do webhook), nunca number cru (evita
imprecisão de ponto flutuante) nem o Decimal.js interno serializado
({"s":1,"e":2,"d":[...]}", bug já corrigido). downloads.xml/downloads.pdf
substituem os caminhos internos: são os endpoints reais de download
(GET /v1/nfe/xml/{accessKey}, GET /v1/nfe/pdf/{accessKey}).
DANFE, mudança de contrato (30/07/2026): GET /v1/nfe/pdf/{accessKey} devolvia
text/html. Agora devolve application/pdf: o DANFE oficial gerado a partir do XML
autorizado da nota (ambiente, CST/CSOSN e informações complementares vêm do documento).
Sem XML autorizado armazenado no ambiente, a resposta é 409 com
code: DANFE_INDISPONIVEL, nunca um documento aproximado.
Status persistido (Invoice.status) | Significado | Ação |
|---|---|---|
AUTHORIZED | Aprovada pela SEFAZ | Nenhuma. Nota válida |
REJECTED | Rejeitada (resposta HTTP é 400, ver abaixo) | Corrija e reenvie com nova Idempotency-Key |
CANCELED | Cancelada | Nota cancelada |
Emissão em lote
POST /v1/nfe/batch enfileira várias notas de uma vez. Cada item de notas[] segue
exatamente o mesmo contrato de POST /v1/nfe: mesmo schema, mesmos campos
obrigatórios, mesmas recusas. Não existe um "formato de lote" separado.
curl -X POST https://api.engineapi.com.br/v1/nfe/batch \
-H "x-api-key: ek_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{
"notas": [
{
"destinatario": {
"cnpjCpf": "99888777000100",
"nome": "Cliente Exemplo SA",
"endereco": {
"logradouro": "Av. Paulista",
"numero": "1000",
"bairro": "Bela Vista",
"codigoMunicipio": "3550308",
"municipio": "São Paulo",
"uf": "SP",
"cep": "01310100"
}
},
"items": [
{
"codigo": "SKU-001",
"descricao": "Camiseta Algodão",
"ncm": "61091000",
"cfop": "6102",
"unidade": "UN",
"quantidade": 2,
"valorUnitario": 59.9,
"icms": { "csosn": "102" }
}
],
"pagamentos": [{ "forma": "01", "valor": 119.8 }]
}
]
}'
Resposta 201: confirma o enfileiramento, não a autorização:
{
"data": { "queued": 1, "ids": ["4b1f0a6e-..."] },
"meta": { "requestId": "req_abc123", "timestamp": "2026-07-31T12:00:00.000Z" }
}
Regras do lote
São dois limites independentes: o de notas e o de tamanho do corpo. O que morder primeiro depende de quantos itens suas notas têm.
| Regra | Comportamento |
|---|---|
| Máximo de notas por chamada | 50. Acima disso: 422 com code: LOTE_ACIMA_DO_LIMITE, e nenhuma nota do envio é enfileirada |
| Máximo de tamanho do corpo | 100 kB (limite global da API, não só do lote). Acima disso: 413 com code: PAYLOAD_TOO_LARGE e details.limiteBytes/details.tamanhoBytes |
| Validação de cada nota | Idêntica ao POST /v1/nfe. Qualquer nota inválida reprova o lote inteiro com 400 |
| Onde o erro aponta | errors[].field traz o índice da nota, ex.: notas.2.items.0.ncm |
| Ordem de checagem | Forma antes de tamanho de lote: um envio com 60 notas em que uma é inválida recebe 400 (validação), não 422. Corrija a nota e o 422 do limite aparece no reenvio |
| Emissor | Um lote emite por um emissor: use issuerId na raiz do corpo. issuerId dentro de uma nota divergindo do lote → 422 ISSUER_DIVERGENTE_NO_LOTE |
numero explícito | Se você mandar numero nas notas, a API não checa colisão entre as notas do mesmo lote. Duas notas com o mesmo numero/série: a primeira emite, a segunda falha na SEFAZ por duplicidade e aparece como FAILED em GET /v1/nfe/queue. Omita numero para deixar a numeração automática cuidar disso |
| Campos não previstos no contrato | São descartados antes do enfileiramento (mesmo comportamento do endpoint singular) |
Quantas notas cabem de verdade? Uma nota com 1 item ocupa ~1,3 kB de JSON;
com 10 itens, ~3,5 kB. Na prática: 50 notas de 1 item cabem folgado (~64 kB),
mas 50 notas de 10 itens somam ~175 kB e batem no 413. Se você emite notas
com muitos itens, use lotes menores; o corpo do 413 diz o tamanho enviado e
o limite.
Mudança de contrato (31/07/2026): até esta versão o lote não validava nada: um
payload que o POST /v1/nfe recusaria era aceito e só quebrava lá na frente, dentro da
fila. Agora o lote recusa na porta, com o mesmo 400/422 do endpoint singular. Se a sua
integração de lote enviava algo que o singular já recusava, ela passa a receber a recusa;
o corpo do erro diz exatamente qual nota e qual campo.
Consultando o desfecho
O 201 é só o aceite na fila. O desfecho fiscal de cada nota vem de
GET /v1/nfe/queue (filtrável por status: PENDING, PROCESSING, DONE, FAILED):
curl https://api.engineapi.com.br/v1/nfe/queue?status=FAILED \
-H "x-api-key: ek_live_sua_chave"
Item que falhou traz lastError (texto) e resultData.error com o código estruturado
(code): o mesmo código que o endpoint singular devolveria para o mesmo payload. Recusa
determinística (ex.: CST_REGIME_INCOMPATIVEL, PAGAMENTO_DIVERGENTE,
CADASTRO_EMISSOR_INCOMPLETO, EMISSOR_INEXISTENTE) vai direto para FAILED, sem
retry: repetir não muda o desfecho, e nenhum número da sequência fiscal é consumido.
Corrija o payload e reenvie.
EMISSOR_INEXISTENTE cobre o caso de o emissor ser removido entre o
enfileiramento e o processamento: a nota falha na hora, sem gastar tentativas e
sem consumir número. Reenvie o lote apontando para um emissor válido.
Cancelamento
Cancele uma NFe autorizada em até 24 horas após a autorização. Prazo geral, também regulamentado por UF.
curl -X POST https://api.engineapi.com.br/v1/nfe/{idOuChave}/cancelar \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "justificativa": "Erro nos dados do destinatário informado na venda" }'
idOuChave aceita o id (UUID) da NFe ou a chave de acesso (44 dígitos). O campo
do corpo é justificativa (mínimo 15 caracteres); a rota aceita tanto JWT do painel
quanto x-api-key de integração.
O prazo da NFCe (modelo 65) é muito menor: 30 minutos, padrão nacional por UF. Não assuma o mesmo prazo para os dois modelos; veja o guia de NFCe para os detalhes.
Se o cancelamento for solicitado fora do prazo, a SEFAZ rejeita (cStat 501) e a engineAPI
repassa o desfecho verbatim no 400, mesmo formato descrito em
Tratamento de Erros abaixo.
O DANFE de uma nota cancelada não traz carimbo de cancelamento. O PDF servido por
GET /v1/nfe/pdf/{accessKey} é o documento gerado a partir do XML de autorização:
ele não muda quando o cancelamento é homologado, porque o evento de cancelamento é um
documento fiscal separado (o XML do evento, com o protocolo). Para provar que a nota foi
cancelada, use o status do documento (CANCELED) ou o XML do evento, nunca a ausência
de carimbo no DANFE.
Carta de Correção (CC-e)
curl -X POST https://api.engineapi.com.br/v1/nfe/{accessKey}/carta-correcao \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "correcao": "Corrijo o endereço do destinatário para Av Brasil, 500" }'
Só permitida em NFe com status AUTHORIZED; limite de 20 CC-e por nota. O corpo aceita
correcao (mínimo 15 caracteres). Assim como o cancelamento, aceita JWT ou x-api-key.
Inutilização
Inutilize uma faixa de numeração que nunca será usada. O caso típico é uma nota
rejeitada de forma definitiva, que consome o número mas nunca chega a AUTHORIZED: sem
inutilizar, esse número fica um gap permanente na sequência fiscal.
curl -X POST https://api.engineapi.com.br/v1/nfe/inutilizar \
-H "x-api-key: SUA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"serie": 1,
"numInicial": 45,
"numFinal": 45,
"justificativa": "Numero pulado por falha no sistema de emissao"
}'
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
serie | número (ou string só-dígitos) | Sim | Série da faixa a inutilizar (inteiro 0–999) |
numInicial | número (ou string só-dígitos) | Sim | Primeiro número da faixa (inteiro 1–999999999) |
numFinal | número (ou string só-dígitos) | Sim | Último número da faixa (inteiro 1–999999999), precisa ser maior ou igual a numInicial, e a faixa (numFinal - numInicial + 1) não pode passar de 10.000 números por chamada |
justificativa | string | Sim | Entre 15 e 255 caracteres (limite do campo xJust do leiaute) |
issuerId | string (UUID) | Não | Só necessário com 2+ emissores cadastrados |
ano | número | Não | Ano-calendário da numeração inutilizada, entre anoCorrente - 5 e anoCorrente. Default: ano corrente. Use quando o número foi pulado num ano e a inutilização só está sendo feita no ano seguinte (ex.: gap aberto em dezembro, inutilizado em janeiro). Sem isso a inutilização seria registrada no ano ERRADO |
O corpo é validado com mensagens de erro no padrão pt-BR do resto da API
(ex.: "Justificativa deve ter no mínimo 15 caracteres (exigência SEFAZ)"). Os campos numéricos aceitam number ou string só-dígitos
("45"), mas rejeitam null, "", array e boolean com uma mensagem pt-BR acionável (nunca o
"Invalid input" genérico) e nunca coagem silenciosamente pra 0.
Quando a SEFAZ homologa a inutilização (cStat 102), a resposta vem HTTP 200 com
success true:
{
"data": {
"success": true,
"protocol": "135260000009876",
"message": "Inutilização série 1 nº 45 a 45 homologada",
"xml": "<inutNFe>...</inutNFe>"
},
"meta": {
"requestId": "req_abc123",
"timestamp": "2026-07-20T12:00:00.000Z"
}
}
O campo xml (XML de retorno da SEFAZ) é servido quando a ACBrLib o devolve:
trate como opcional na sua integração, não como garantia contratual. protocol e
message são os campos com prova de emissão real.
Quando a SEFAZ rejeita o pedido (qualquer cStat diferente de 102), a engineAPI
NÃO traduz isso em 4xx: o desfecho vem no próprio corpo, ainda em HTTP 200, com
success false:
{
"data": {
"success": false,
"cStat": 563,
"xMotivo": "Rejeicao: Ja existe pedido de Inutilizacao com a mesma faixa de numeracao",
"erros": [
{ "codigo": "563", "descricao": "Rejeicao: Ja existe pedido de Inutilizacao com a mesma faixa de numeracao" }
],
"message": "Rejeicao: Ja existe pedido de Inutilizacao com a mesma faixa de numeracao"
},
"meta": {
"requestId": "req_abc123",
"timestamp": "2026-07-20T12:00:00.000Z"
}
}
Ramifique pelo campo success, não pelo status HTTP: rejeição da SEFAZ na
inutilização não é 400. Ver também Erros e Rejeições.
Outros status possíveis (nenhum é "sempre 200": só o desfecho da SEFAZ é):
| Status | Quando | Corpo |
|---|---|---|
400 | serie/numInicial/numFinal/justificativa inválidos (shape, tipo ou numFinal < numInicial), ou 2+ emissores cadastrados sem issuerId | Erro de validação padrão (RFC 7807) |
403 | Partner sem nenhum emissor cadastrado | Erro padrão |
404 | issuerId informado não pertence ao partner autenticado | Erro padrão |
422 | CERTIFICADO_AUSENTE (emissor sem certificado A1 instalado) ou FAIXA_INUTILIZACAO_MUITO_GRANDE (mais de 10.000 números na faixa) | { code, message, details } |
500 | Timeout do worker que fala com a ACBrLib/SEFAZ (120s): a inutilização pode ou não ter sido homologada do lado da SEFAZ, a resposta não chegou a tempo | Erro genérico |
O 500 de timeout é ambíguo de propósito (a SEFAZ pode ter homologado sem a
resposta voltar a tempo). Nunca reenvie a mesma faixa sem cuidado: um reenvio comum
pode colidir com uma inutilização que JÁ foi homologada (cStat 563, "já existe
pedido"). Envie sempre com o header Idempotency-Key: <uuid> (suportado
globalmente por todo POST/PUT/PATCH da API). Reenviar com a MESMA key repete o
resultado já concluído (replay), sem reprocessar. Gere uma key nova só quando for de
fato uma faixa diferente.
Mesmo motor por trás do POST /v1/nfce/inutilizar (modelo 65):
o worker ACBr e o desfecho da SEFAZ são genéricos por modelo; só o endpoint muda.
Tratamento de Erros
Rejeição SEFAZ (400)
Quando a SEFAZ rejeita a nota (cStat de rejeição), a API responde HTTP 400
com o envelope de erro padrão (RFC 7807) carregando error.erros[], o
cStat/xMotivo verbatim da SEFAZ, sem tradução:
{
"error": {
"type": "https://engineapi.com.br/errors/BAD_REQUEST",
"title": "Requisição Inválida",
"status": 400,
"detail": "SEFAZ rejeitou (CStat=696): Rejeicao: Obrigatoria as informacoes do responsavel tecnico pela emissao do DF-e",
"erros": [
{
"codigo": "696",
"descricao": "Rejeicao: Obrigatoria as informacoes do responsavel tecnico pela emissao do DF-e"
}
],
"instance": "/v1/nfe",
"requestId": "req_uofirusmuamw",
"timestamp": "2026-07-05T11:06:14.453Z"
}
}
Ramifique pelo error.erros[].codigo (o cStat da SEFAZ) e consulte a
tabela oficial de rejeições
da Fazenda. A nota fica com status REJECTED e o webhook invoice.rejected
é disparado com o mesmo erros[]. Rejeição é um desfecho determinístico:
reenviar com a mesma Idempotency-Key devolve a mesma rejeição (replay,
sem retransmitir à SEFAZ). Corrija os dados e emita com uma key nova.
Falhas de infraestrutura genuínas (timeout, indisponibilidade da SEFAZ)
continuam respondendo 500 e podem ser retentadas com a mesma key.
Certificado A1 ausente e cadastro do emissor incompleto (IE, endereço) não
chegam a 500: a API valida ANTES de acionar o worker ACBr e responde 422
estruturado: ver Pré-voo do emissor.
Webhook após emissão
A engineAPI dispara automaticamente um evento invoice.authorized quando a SEFAZ aprova:
{
"id": "9f1c2b3a-...-uuid",
"type": "invoice.authorized",
"timestamp": "2026-04-26T18:30:00.000Z",
"data": {
"invoiceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model": "55",
"number": 1,
"series": 1,
"accessKey": "35260211222333000181550010000000011000000019",
"status": "AUTHORIZED",
"amount": 119.80
}
}
Configure seus webhooks em Dashboard → Configurações → Webhooks ou via guia de webhooks.