Todos os artigos
FT-MSG-IDEMPOTENCY· 1.0.0Integrações

Idempotency em mensagens EDI — Idempotency-Key + fingerprint

Como o Lefia detecta e bloqueia mensagens duplicadas at-insert via fingerprint SHA-256 + header Idempotency-Key (padrão Stripe). UNIQUE parciais por message_type, race fallback via 23505.

Atualizado em 8/6/2026

Idempotency em mensagens EDI



Atualizado 2026-05-06. Aplicado em /api/edi-hub/receive-message e /api/webhook/[integrationId].

Por que existe



Source SAP retransmite SalesOrder/BillingDocument quando perde ack. Webhook externo manda evento 2x quando sua infra dá timeout. Sem idempotency, Lefia reprocessa o mesmo pedido N vezes — destino recebe duplicata, sistema externo sangra.

Solução padrão Stripe: chave de idempotência única por mensagem, bloqueia at-insert no banco.

2 modos de detecção



1. Header Idempotency-Key (recomendado pra clientes que controlam o source)



Cliente envia header HTTP:

POST /api/edi-hub/receive-message
Idempotency-Key: order-12345-attempt-2
Content-Type: application/json

{ ...payload... }


Lefia armazena a chave. Re-envio com mesma key → 200 OK com a resposta original (não cria nova mensagem).

2. Fingerprint SHA-256 (auto, pra qualquer source)



Quando não vem Idempotency-Key, Lefia calcula fingerprint:

SHA-256(message_type_id + concat(duplicate_key_fields))


duplicate_key_fields é configurado por message_type (ex: ["E1EDK01.BELNR", "E1EDK01.DOCDAT"] pra ORDERS05). Se já existe mensagem com mesmo fingerprint dentro da janela configurada → bloqueia at-insert.

UNIQUE INDEX parcial



CREATE UNIQUE INDEX uq_messages_fingerprint
ON messages (tenant_id, environment_id, fingerprint)
WHERE fingerprint IS NOT NULL;


Race condition tratado: se 2 requests simultâneos passam pelo SELECT, INSERT, um vai pegar 23505 (unique violation) — fallback retorna a mensagem existente.

Configuração



Em message_types:
  • duplicate_key_fields (jsonb): paths usados no fingerprint
  • duplicate_window_hours (int): janela em horas (ex: 24h)


Sem duplicate_key_fields configurado, fallback é [docnum]. Sem duplicate_window_hours, default é 168h (7 dias).

Como ver duplicatas detectadas



/admin/audit filtra por action = "message.duplicate_blocked". Cada bloqueio gera audit_event com fingerprint + reference original.

Roadmap relacionado



  • #60 Idempotency at-insert (delivered)
  • Casos não cobertos hoje: payload semantically equivalent mas serialized diferente (ex: chave em ordem diferente). Próxima evolução é canonicalize antes de hash.

Histórico de versões

  • 1.0.0

    Idempotency at-insert pra mensagens EDI. Fingerprint SHA-256 (chave + payload) + Idempotency-Key header (Stripe pattern). UNIQUE parciais. Reaproveita duplicate_key_fields. Race fallback via 23505. Aplicado em /api/edi-hub/receive-message e /api/webhook/[integrationId].

    5/6/2026 · new