Pular para o conteúdo

Idempotência (numero-origem)

A API do FazNota usa o campo numero-origem (também aceito como numero_origem, legado) para garantir idempotência nas emissões. Esse é um dos campos mais importantes da integração — e o mais ignorado por quem está começando.

Por que importa

Imagine este cenário:

  1. Sua aplicação envia POST /nfe/emissao com um pedido.
  2. A API do FazNota processa e emite a NFe com sucesso.
  3. Mas a resposta se perde na rede (timeout no cliente, falha intermediária).
  4. Sua aplicação interpreta como “falha” e tenta de novo com o mesmo pedido.

Sem idempotência, você teria duas NFes emitidas para o mesmo pedido. Com idempotência, a segunda chamada retorna o recibo original, sem duplicar a emissão.

Como funciona

Em qualquer emissão (NFe, NFCe, NFSe), envie no body:

{
"numero-origem": "PED-2026-00042",
...
}

Se você já enviou uma emissão com esse mesmo numero-origem (no escopo da sua empresa), a API do FazNota retorna:

{
"status": "050",
"descricao": "Já existe uma nota com este número de origem em nosso sistema.",
"data": { "recibo": "rec_da_emissao_original" },
"meta": { ... }
}

Note que HTTP 200 + status: "050". Não é erro — é proteção.

Como escolher um bom numero-origem

EstratégiaExemploComentário
ID do pedido no seu sistema"PED-2026-00042"Direto e legível. Recomendado.
UUID v4"550e8400-e29b-41d4-a716-446655440000"Garantido único, mas opaco.
Composto`“loja:42pedido:00042

Padrão de retry seguro

async function emitirComRetry(payload, token, maxTentativas = 3) {
for (let tentativa = 1; tentativa <= maxTentativas; tentativa++) {
try {
const res = await fetch(`${BASE}/nfe/emissao`, {
method: 'POST',
headers: {
Authorization: `Token ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
const json = await res.json();
// Sucesso ou duplicidade (idempotência funcionando)
if (json.status === '001' || json.status === '050') {
return json.data.recibo;
}
// Erro de validação: não adianta retentar
if (json.status === '999') {
throw new Error(`Validação: ${json.data?.erro}`);
}
} catch (err) {
if (tentativa === maxTentativas) throw err;
// Backoff exponencial: 1s, 2s, 4s
await new Promise((r) => setTimeout(r, 1000 * Math.pow(2, tentativa - 1)));
}
}
}

O ponto crucial: o payload deve ter o mesmo numero-origem em todas as tentativas. Se mudar, a idempotência se perde.

Escopo do numero-origem

  • Por empresa. Cada empresa (cada Token) tem seu próprio espaço de numero-origem.
  • Por tipo de documento. Vou poder usar "PED-001" simultaneamente em NFe e em NFCe — são tabelas diferentes. (Recomenda-se evitar para clareza.)
  • Permanente. Uma vez registrado, o numero-origem permanece reservado para aquela emissão.

E se eu não enviar numero-origem?

A emissão funciona normalmente, mas sem proteção contra duplicidade. Em caso de retry, você pode criar duas NFes para o mesmo pedido.

Formatos aceitos

A API aceita ambos os formatos do campo, por compatibilidade:

{ "numero-origem": "PED-001" } // ✅ Preferido (consistente com outros campos)
{ "numero_origem": "PED-001" } // ✅ Aceito (legado)

Em novas integrações, prefira numero-origem (com hífen) — segue o padrão dos demais campos da API.