Sending a DB Schenker Transport Order via EDIFACT

Step-by-step guide to building a DB Schenker IFTMIN EDIFACT transport order, validating segments, and confirming receipt via IFTMCS.

Sending a DB Schenker Transport Order via EDIFACT

Most of what we run through the test harness at this blog is REST: JSON payloads, OAuth scopes, sandbox keys you can generate at 2am. DB Schenker's freight booking flow is not that. If you're integrating road or multimodal freight with them, you're building an IFTMIN transport order over UN/EDIFACT, and the sooner you accept that, the sooner you stop fighting the wrong problem. This walkthrough covers the actual segments, the transmission channel, the confirmation loop, and one version mismatch that has quietly broken more go-lives than any payload bug.

Why EDIFACT still runs DB Schenker freight bookings

Because most forwarders built their EDI stacks in the 1990s and never had a commercial reason to rip them out. DSV — which now owns the former DB Schenker network — frames its own REST API as the newer option, not the default: DSV describes API as "a great alternative to the traditional EDI connections" for submitting bookings, printing labels, and tracking shipments. Read that framing carefully: it positions EDI as the baseline and API as the alternative, which tells you where the actual transaction volume still sits for a lot of shippers and forwarders in Europe.

This is also why carrier connectivity in freight looks so different from parcel. On the parcel side, nShift, EasyPost, and ShipEngine compete on SDK polish and time-to-first-label. On the freight side, TMS platforms like Cargoson, MercuryGate, Transporeon, and Alpega earn their keep by maintaining pre-built IFTMIN/IFTMCS/IFTSTA adapters per forwarder, so your integration team isn't hand-rolling segment mapping and directory-version tracking for every trading partner. That abstraction is the whole value proposition — more on that at the end.

What you need before you start

You need a live trading-partner relationship, not just API docs. Concretely, get these four things confirmed before you write a single segment:

On that last point, DB Schenker's catalogue lists IFTMIN S93A as "an EDI for booking shipments" — that's your reference point for which directory version the booking message itself expects.

Step-by-step: building and sending the IFTMIN message

The mechanics below assume you're transmitting to DB Schenker's Norway EDI endpoint per their published catalogue; confirm the equivalent for other countries with your local contact first.

  1. Confirm the message directory version with your DB Schenker contact. Don't infer it from a generic EANCOM guide. Booking runs on S93A per DB Schenker's own catalogue — get that in writing from your integration contact, not just from documentation you found.
  2. Build the interchange envelope (UNB/UNZ) with the party qualifiers agreed in your trading-partner setup. This wraps every message you send and is where sender/recipient GLNs live.
  3. Construct the message header. The UNECE spec is explicit here: instruction messages must contain, in segment UNH composite S009, data element 0065 set to IFTMIN, 0052 set to D, 0054 carrying the release version, and 0051 set to UN. Get 0054 wrong — put D96A where DB Schenker expects S93A — and you've already triggered the failure mode covered below. Follow with BGM to declare the beginning of the message and the document/message number.
  4. Populate the core segments. IFTMIN carries pickup and delivery locations, commodity, weights, service level, and references the commercial shipment, while NAD identifies shipper, consignee, and carrier, and TDT and EQD describe mode and equipment needs. This is where most mapping bugs live — a missing NAD qualifier for the carrier party is a silent rejection, not a helpful error.
  5. Add TSR for contract and service conditions. Per the UNECE definition, TSR is "a segment to identify the contract, conditions of carriage, services, and priority requirements for the transport". If your booking needs a specific service level or priority handling, this is the segment that carries it — don't bury it in a free-text FTX instead.
  6. Close the message with UNT and validate that your segment count in UNT matches what you actually sent. Most translator libraries do this automatically; don't skip manual validation on your first few test sends.
  7. Transmit via the agreed channel and log the interchange control number (from your UNB) somewhere queryable. You'll need it to reconcile against the IFTMCS response and, later, against IFTSTA milestones.

A stripped skeleton of the header and core segments looks like this (illustrative structure, not a validated production message):

UNH+1+IFTMIN:D:93A:UN'
BGM+610+569952+9'
DTM+137:202607300800:203'
TSR+3::95:2'
NAD+CZ+5412345678908::9'
NAD+CN+8798765432106::9'
TDT+20+++30:::Road transport'

Real messages run far longer — goods item groups, packaging levels, and reference segments add up fast — but this is the skeleton your translator config needs to hit before anything else.

Confirming receipt: reading the IFTMCS response

A clean accept looks like an IFTMCS referencing your original document/message number with a positive status code and no error segments. A rejection carries an error or reason code pointing back at a specific segment or composite — read that first, not the whole message.

The first thing to check on any rejection, before you assume a data problem, is version mismatch: does the IFTMCS you received expect the same directory release as the IFTMIN you sent? This is the single most common silent failure in this workflow, and it deserves its own section.

Failure mode — the S93A/D96A version trap

Here's the trap: DB Schenker doesn't run one directory version across its whole message set. Their own catalogue lists IFTMIN as S93A for booking and IFTMCS as S93A for the receipt confirmation, but IFTSTA runs on D96A for status observations. Teams that assume "we're on S93A" or "we're on D96A" as a blanket setting for the whole integration get one of two outcomes: silent parser rejections on the booking side, or malformed, unparseable status updates on the tracking side. Neither shows up until you're already sending production volume.

The fix is boring but effective: pin directory version per message type in your translator config, not globally. Most EDI toolkits (EdiFabric's IFTMIN builder example explicitly sets MessageVersionNumber to "D" and MessageReleaseNumber to "96A" as separate fields in the UNH composite) support this natively — the mistake is copy-pasting one config block across message types instead of setting it per type.

Before wiring in production volume, run one deliberate test: send a minimal IFTMIN in your sandbox or test interchange and confirm the returned IFTMCS actually references the version you expect. Don't skip this because the booking "looks right" — a version mismatch often produces a message that parses fine structurally but fails semantic validation downstream, which is worse than an outright rejection because it surfaces later, further from the cause.

Tracking shipment milestones with IFTSTA

IFTSTA is how you find out what happened to the shipment you booked, and reconciling it against your internal shipment record is where most of the ongoing engineering effort goes after go-live. DB Schenker's IFTSTA carries observations as segments with data elements, for example STS+ANT meaning "reading arrived at terminal" — parse these status codes against a lookup table, not a hardcoded switch statement, because the code list is longer than the three or four events you'll see in testing.

Worth noting: this isn't universal across European forwarders. DSV, for the same shipment lifecycle, lets partners choose the format. DSV's developer portal confirms it has a number of standard messages available to any partner that can provide or consume them, available in EDIFACT D10B and/or XML as indicated. If you're building a mapping layer that has to serve both DB Schenker and DSV, that's two different directory conventions and, for DSV, a choice of wire format on top — reason enough to isolate the EDIFACT parsing logic behind a stable internal schema rather than passing segment output straight into your TMS.

How you know it worked / next steps

You're done with the core loop when you can check off all three of these against a real interchange, not a mocked one:

  • The envelope was acknowledged at the transport layer (no bounce, no malformed-interchange rejection).
  • You received an IFTMCS referencing your original message number with an accept status, not an error code.
  • The first IFTSTA milestone arrived within the SLA window your DB Schenker contact quoted you — and it parsed against your internal shipment record without a manual fix.

If any of those three fail repeatedly, go back to the version-pinning check before you assume it's a payload problem — it usually isn't.

This is exactly the maintenance burden that pushes integration teams toward a TMS abstraction layer. Cargoson, alongside MercuryGate, Alpega, and Transporeon, exists specifically so you don't maintain this segment mapping and version-pinning logic per forwarder — the same way EasyPost, ShipEngine, and nShift solve the equivalent problem for parcel carriers on the REST side. If you're only integrating one forwarder, hand-rolling IFTMIN is entirely reasonable. If you're adding a second or third, that's the point where the maintenance cost of per-forwarder EDIFACT logic starts to outweigh the cost of a TMS license.