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.

Content Enricher

An EDI message almost never carries everything downstream needs. The Content Enricher fills the gaps by consulting an external reference: GLN → address, GTIN → label, country code → long name. The pattern of context injection.

Problem

EDIFACT and X12 messages were designed to be compact: they carry identifiers (13-digit GLN, 13-digit GTIN, 14-digit SIRET) rather than labels. The sender assumes the receiver can resolve these identifiers to associated information — name, address, VAT, product category. But when feeding an ERP, an invoicing system or a supplier portal, we often need those fields in clear text. Without enrichment, those lookups happen at 200 places in downstream code.

Forces

  • Protocol compactness vs application richness. EDIFACT optimises 1980s bandwidth. Modern applications want everything in clear.
  • External source of truth. The reference (Master Data Management, ERP) is the authority — not the transient message.
  • Lookup cost. Each lookup has a cost (latency, network). For an ORDERS with 200 lines, 200 sequential lookups become prohibitive.
  • Temporal consistency. The name associated with a GLN can change (rebranding, merger). We must choose between freezing enrichment at receipt time or re-resolving at read time.

Solution

EIP §336 (Hohpe & Woolf, 2003) models the Content Enricher as a component that: (a) receives a canonical message, (b) extracts identifiers to enrich, (c) consults an external reference, (d) injects resolved fields into the message and (e) republishes. Lookup can be synchronous (immediate) or asynchronous (pre-cache updated by a separate flow).

plaintext topology.txt
Canonical ORDERS          Content Enricher        Enriched ORDERS
   (GLN only)               (resolves GLN → addr)
   ─────────────             ─────────────────         ───────────────
   {                                                    {
     "buyer": {              ┌─────────────┐             "buyer": {
       "gln": "871234..."  ──▶ external    │              "gln": "871234...",
     }                       │ partner-db  │              "name": "Carrefour",
   }                         │             ├──▶          "address": {
                              └─────────────┘                "street": "...",
                                                            "city": "Massy",
                                                            "country": "FR"
                                                          }
                                                        }
                                                      }

Topology

Three main variants:

  • Synchronous Enricher. Lookup on every message. Simple, but sensitive to reference latency and availability.
  • Cached Enricher. Lookup with TTL cache (10 minutes to 24 hours per domain). Drastically reduces load on the reference.
  • Pre-loaded Enricher. Local replica of the reference kept up to date by changefeed (CDC, master data events). Near-zero latency, but requires an event-driven master-data infrastructure.

EDI implementation

Three emblematic enrichments:

  • GLN → partner record. The partner sends NAD+BY+8712345600014::9. The enricher resolves to a partner record with name, address, IBAN, VAT, contact. The lookup table is typically the ERP's customer/supplier database.
  • GTIN → product. The partner sends LIN+1++4006381333931:EN. The enricher resolves to a product record with label, category, packaging, VAT rate. The lookup table is the product catalogue (PIM).
  • ISO 3166 country code → name + tax zone. The message carries FR; the enricher injects "France", EU zone, default currency EUR. Case of a static embedded reference (no external lookup needed).

Before enrichment:

json order-before.json
{
  "type": "ORDER",
  "number": "PO-12345",
  "buyer": { "gln": "8712345600014" },
  "supplier": { "gln": "5412345600015" },
  "lines": [
    { "item": { "gtin": "4006381333931" }, "quantity": 24 }
  ]
}

After enrichment:

json order-after.json
{
  "type": "ORDER",
  "number": "PO-12345",
  "buyer": {
    "gln": "8712345600014",
    "name": "Carrefour Centrale Massy",
    "address": {
      "street": "33 Avenue Émile Zola",
      "city": "Massy",
      "postalCode": "91300",
      "country": "FR"
    },
    "vatId": "FR12652014051"
  },
  "supplier": {
    "gln": "5412345600015",
    "name": "Brasserie XY",
    "vatId": "BE0405841683"
  },
  "lines": [
    {
      "item": {
        "gtin": "4006381333931",
        "name": "Pelikan rollerball ink black",
        "category": "stationery"
      },
      "quantity": 24
    }
  ]
}

Cache, freshness and failure policy

Three questions to settle:

  • What TTL? For relatively stable partner data (name, address), 24 hours is reasonable. For more volatile data (product price, stock), 5-15 minutes or no cache.
  • What if lookup fails? Three options: (a) block the message in a Dead Letter Channel for review, (b) pass the message downstream without enrichment with an enrichmentSkipped: true flag, (c) apply a documented default value. The right answer depends on business criticality.
  • What to freeze in the message? If the order must survive a reference change (a GLN that changes name), freeze the snapshot of enriched fields. Otherwise, only store the identifier and re-resolve at read time.

Anti-patterns

  • Synchronous lookup without timeout. If the reference takes 30 seconds to answer, we block the EDI pipeline for 30 seconds. Always set a strict timeout (1-3 seconds) with circuit breaker.
  • Lookup inside a loop. 200 lines = 200 sequential calls = unacceptable. Bulk lookup is mandatory.
  • No cache. For stable data, not caching pointlessly multiplies load on the ERP.
  • Cache without invalidation. A 24h cache with no invalidation mechanism will propagate stale data for 24h after a master change. If the reference publishes mutation events, the enricher must subscribe.
  • Silent partial enrichment. If only half of the fields could be resolved, the message must explicitly signal it. The downstream consumer must see the trace of incompleteness.

Sources