ediverse Explore the platform

Spotlight PEPPOL BIS Billing 3.0 The EU e-invoicing mandate is here — France Sept 2026, Belgium Jan 2026, Germany 2025.

Idempotency Key (sender side)

The sender-side counterpart of the Idempotent Receiver. Without a stable idempotency key on the sender side, receiver-side deduplication is pointless.

Problem

A naive retry that regenerates a fresh UNB CTRL, a fresh ISA13 or a fresh eb:MessageId on every attempt fully breaks the Idempotent Receiver pattern: the partner receives two different keys, concludes that two separate orders were issued and books both. The idempotency pattern is not enough on the receiver side — it must be primed on the sender side.

Forces

  • Persisting the key is non-negotiable. An in-memory counter is lost at restart: the key must live in a database, an outbox or the send journal.
  • The key must be generated before the first send, not at send time. Otherwise a crash after the send but before persistence leaves the system unable to retry with the same key.
  • One key per business intent, not per network attempt. One business order = one key, regardless of how many retries.
  • The key must be contractual. Sender and receiver must agree on its structure, length and uniqueness window (often 90 days in EDI).

Solution

When the business application decides to issue a message, it generates a deterministic idempotency key (typically <type>-<date>-<counter> or a persisted UUID v4), stores it alongside the message in an outbox, then lets the sender worker use it as UNB CTRL / ISA13 / eb:MessageId on every attempt. The naive code becomes a two-step workflow: register the intent (with its key), then execute the send (which can be retried any number of times with the same key).

plaintext idempotency-key-flow.txt
T0  Business app    → outbox.insert(domainEvent, idempotencyKey=ORD-20260514-0001)
T1  Sender worker   → SELECT ... FROM outbox WHERE status='pending'
T2  Sender worker   → POST AS2 with UNB CTRL = ORD-20260514-0001
T3  Network         ↛ network error, no MDN received
T4  Sender worker   → retry (same bytes) reusing the same key
T5  Partner         → recognises duplicate (Idempotent Receiver) → re-emits the same MDN
T6  Sender worker   → outbox.markDelivered(idempotencyKey)

EDI implementation

In EDI, the sender worker reads the outbox, takes the tuple (idempotencyKey, payload), writes the key into the matching envelope segment (UNB+...+ORD-20260514-0001' in EDIFACT, ISA13=900042001 in X12, eb:MessageId in AS4) and sends. A retransmission replays the same outbox row with the same key. The outbox table carries a status (pending, sent, acked) and a tries column to observe network redundancies. Walmart, Stellantis and OpenPEPPOL all require a stable sender-side idempotency key in their partner manuals.

Anti-patterns

  • New key per retry. Defeats receiver-side deduplication — exactly what the pattern is meant to prevent.
  • In-memory counter. Crash = reset counter = collisions or duplications.
  • Key generated at send time, not before. If the worker crashes between send and persistence, retry traceability is lost.
  • Key decoupled from business intent. If the same order can produce two keys, the idempotency benefit vanishes.

Sources