Guia: NFCe
Emita Notas Fiscais de Consumidor Eletrônicas (NFCe, modelo 65) para varejo via engineAPI.
NFCe: Nota Fiscal de Consumidor Eletrônica
A NFCe (modelo 65) é o documento fiscal para vendas a consumidor final no varejo. Substitui o cupom fiscal e deve ser emitida no momento da venda.
Endpoint base: https://api.engineapi.com.br/v1/nfce
Quais campos enviar? Este guia cobre os campos mais usados. A lista completa, navegável por grupo (Identificação, Itens, Impostos, Pagamento...) e gerada direto do contrato real, está no Catálogo de campos: NFCe. Para navegar por endpoint em vez de por documento, veja a Referência por Endpoint.
Emissor (issuerId) é opcional no payload de NFCe. Com um único emissor, pode
omitir; com dois ou mais, informe issuerId (UUID) para escolher o CNPJ; sem ele a API
responde 400. Ver Autenticação.
Pré-requisitos
Empresa e certificado, como na NFe
NFCe usa o mesmo cadastro de emissor da NFe: CNPJ (POST /v1/companies), IE,
endereço completo e certificado digital A1. Ver Emitir NFe (seção
Pré-requisitos, no topo do guia).
CSC: Código de Segurança do Contribuinte (obrigatório fora do sandbox)
Todo emissor real (fora do ambiente sandbox/homologação de testes) precisa ter
csc (o token) e cscId (o ID do token) cadastrados em Dashboard → Emissores →
(selecione o emissor) → aba NFCe, antes de emitir. Gere/consulte o CSC no portal
da SEFAZ do seu estado (Contribuinte → NFCe → Autorização de Uso do CSC). Sem ele, a
API responde 422 CSC_AUSENTE, nenhum número da sequência fiscal é consumido.
Emissores em sandbox (toda conta nova nasce assim) emitem normalmente sem CSC.
Diferenças NFe vs NFCe
| Aspecto | NFe (modelo 55) | NFCe (modelo 65) |
|---|---|---|
| Destinatário | destinatario completo (endereço obrigatório) | destCPF/destNome opcionais, sem endereço |
| CFOP | 5102, 6102... | Predominantemente estadual |
| QR Code | Não | Sim, no campo qrCode da resposta |
| Cancelamento | Até 24h | Até 30 minutos |
| Uso | B2B e B2C | Somente B2C (varejo) |
Emitir NFCe
O item da NFCe reusa o mesmo grupo ibsCbs da NFe (Reforma Tributária); os demais campos
são específicos.
Produto com redução de alíquota (alimento, medicamento, cesta básica, o
caso comum no varejo de NFC-e) precisa de pNominal e pRedAliq além da
alíquota efetiva p em cada componente do ibsCbs. A regra é a mesma da
NF-e: ver IBS/CBS com redução de alíquota.
Com resolverTributacao: true o Cérebro Fiscal resolve isso sozinho.
curl -X POST https://api.engineapi.com.br/v1/nfce \
-H "x-api-key: SUA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"destCPF": "12345678909",
"destNome": "Consumidor Final",
"items": [{
"codigo": "PROD001",
"descricao": "Camiseta Azul M",
"ncm": "61091000",
"cfop": "5102",
"unidade": "UN",
"quantidade": 1,
"valorUnitario": 89.90,
"icms": { "origem": 0, "csosn": "400" }
}],
"pagamentos": [
{ "forma": "03", "valor": 89.90 }
]
}'
Campos de referência
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
serie/numero | número | Não | Identificação, geralmente alocados automaticamente |
destCPF/destNome | string | Não | Dados do consumidor final (sem endereço) |
items | array | Sim (mín. 1) | Mesmos campos de item da NFe (codigo, descricao, ncm, cfop, unidade, quantidade, valorUnitario, cest, icms/pis/cofins/ibsCbs) |
pagamentos | array | Sim (mín. 1) | { forma, valor }, não é objeto singular pagamento |
troco | número | Não | N/A |
informacoesComplementares | string | Não | N/A |
resolverTributacao | boolean | Não | Emissão assistida (Cérebro Fiscal), mesmo contrato da NFe, ver Emitir NFe |
pis/cofins e cest seguem exatamente as regras da NF-e (valores informados
vão para o documento; combinação sem efeito é recusada com 422, ver
PIS, COFINS e IPI). IPI não existe no
modelo 65 e ICMS-ST não é escrito: os campos ipi e
icms.baseCalculoST/aliquotaST/valorST são aceitos no contrato apenas para
poder recusar: com valor diferente de zero devolvem 422 IPI_NAO_SUPORTADO
e 422 ICMS_ST_NAO_SUPORTADO, em vez de emitir a NFC-e sem o dado.
Formas de Pagamento
| Código | Forma |
|---|---|
01 | Dinheiro |
02 | Cheque |
03 | Cartão de Crédito |
04 | Cartão de Débito |
05 | Crédito Loja |
10 | Vale Alimentação |
11 | Vale Refeição |
13 | Vale Presente |
15 | Boleto |
99 | Outros |
Response de Sucesso
Envelopado em { data, meta }, sem aninhamento nfce:
{
"data": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "AUTHORIZED",
"accessKey": "35260211222333000181650010000000011000000019",
"protocol": "135260000001234",
"number": 1,
"series": 1,
"model": "65",
"amount": "89.9",
"destCNPJ": "12345678909",
"destName": "Consumidor Final",
"qrCode": "https://www.fazenda.sp.gov.br/nfce/qrcode?p=35260...",
"createdAt": "2026-07-06T12:00:00.000Z",
"updatedAt": "2026-07-06T12:00:01.000Z",
"downloads": {
"xml": "/v1/nfce/xml/35260211222333000181650010000000011000000019",
"pdf": "/v1/nfce/pdf/35260211222333000181650010000000011000000019"
}
},
"meta": {
"requestId": "req_abc123",
"timestamp": "2026-07-06T12:00:00.000Z"
}
}
Sem issuer/customer embutidos e sem xml/xmlPath/pdfPath/invoiceId/message
(caminho de arquivo interno / shape antigo, removido). status é "AUTHORIZED" (inglês,
mesmo enum de sempre). amount é string decimal ("89.9", sem zero à direita). qrCode é exclusivo da
NFCe, não é coluna do banco, só existe na resposta da emissão (guarde-o no seu lado se
precisar reimprimir o cupom depois). Não há campos cupomUrl/xmlUrl, use
downloads.xml/downloads.pdf (mesmas rotas de download descritas abaixo).
Download do Cupom Fiscal
O DANFCE é retornado como PDF (Content-Type: application/pdf), não passa pelo
envelope {data,meta}, o corpo é o arquivo:
curl https://api.engineapi.com.br/v1/nfce/pdf/{accessKey} \
-H "x-api-key: SUA_API_KEY" \
-o cupom.pdf
O documento é gerado a partir do XML autorizado da própria nota, no layout de cupom
fiscal: ambiente (tpAmb), tributação (CST/CSOSN) e informações complementares saem do
documento, nunca de uma remontagem.
Mudança de contrato (30/07/2026): esta rota devolvia text/html. Agora devolve
application/pdf. Se você salvava a resposta como .html, passe a salvar como .pdf.
Quando o XML autorizado não está armazenado neste ambiente (por exemplo, emissão em
sandbox), a resposta é 409 com code: DANFE_INDISPONIVEL, nunca um documento
aproximado.
Download do XML
curl https://api.engineapi.com.br/v1/nfce/xml/{accessKey} \
-H "x-api-key: SUA_API_KEY"
Cancelamento
curl -X POST https://api.engineapi.com.br/v1/nfce/{idOuChave}/cancelar \
-H "x-api-key: SUA_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "justificativa": "Erro na forma de pagamento informada na venda" }'
{idOuChave} aceita o UUID (id da resposta de emissão) ou a chave de acesso
(44 dígitos numéricos) da NFCe, mesmo contrato do cancelamento de NFe.
Sempre escopado ao seu partner (usar id/chave de outro partner devolve o mesmo 404 de
"não existe"). O campo do corpo é justificativa (mínimo 15 caracteres), não motivo.
O DANFCE de uma nota cancelada não traz carimbo de cancelamento. O PDF servido por
GET /v1/nfce/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 DANFCE.
Prazo: 30 minutos contados da autorização de uso, desde que a mercadoria não tenha circulado. Esse é o prazo padrão nacional (Ajuste SINIEF 07/18) e é definido por UF; em Goiás, está fixado no art. 167-S-Q do RCTE-GO. É muito menor que o prazo da NFe (24h): não assuma o mesmo prazo para os dois modelos.
Fora do prazo: cStat 501
Se o cancelamento for solicitado após o prazo da UF, a SEFAZ rejeita e a engineAPI repassa
o desfecho verbatim no corpo 400:
{
"error": {
"erros": [
{
"codigo": "501",
"descricao": "Rejeicao: Prazo de cancelamento superior ao previsto na Legislacao"
}
]
}
}
A nota permanece com status AUTHORIZED. Não existe cancelamento extemporâneo de NFCe
via API. O remédio legal é emitir uma nota de devolução. Veja também o catálogo de
Erros e Rejeições.
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.
Este endpoint é exclusivo da NFCe (modelo 65). Para NFe (modelo 55), use
POST /v1/nfe/inutilizar: mesmo contrato, mesmo motor por
trás (worker ACBr e desfecho da SEFAZ são genéricos por modelo).
curl -X POST https://api.engineapi.com.br/v1/nfce/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 PDV local"
}'
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
serie | número (ou string só-dígitos) | Sim | Série da faixa a inutilizar (inteiro 0–999); ver Campos de referência |
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.
Contingência
🗓 Fora do contrato hoje. A NFCe (modelo 65) não tem contingência: toda emissão sai com
tpEmis=1 (normal), fixo. Diferente da NFe, que reroteia automaticamente para a SEFAZ
Virtual de Contingência (SVC) quando a SEFAZ da UF está fora do ar,
a NFCe não tem SVC (o leiaute nem prevê essa rota para o modelo 65) nem o modo offline
(tpEmis=9, DPEC/venda com transmissão diferida) implementado. Se a SEFAZ da UF do emissor
estiver indisponível no momento da venda, o POST /v1/nfce falha em vez de emitir por rota
alternativa.
Se sua operação de varejo depende de continuar vendendo com a SEFAZ fora do ar (o cenário
que o modo offline resolve no ACBr/PDV tradicional), fale com o time em
suporte@engineapi.com.br, é candidato de roadmap, sem
data. Para acompanhar a disponibilidade da SEFAZ antes de uma venda, use
GET /v1/nfe/sefaz-status/{uf} (rota compartilhada entre os dois modelos, ver
Consultar o status da SEFAZ).