Work with EDIFACT as a developer
This developer reference explains how to implement and test an EDIFACT partner contract for Workspace. It is for integrators who build mapping definitions, fixture evidence, or a Partner API client. When complete, you have a reproducible test chain from unchanged EDIFACT bytes to the canonical model and back.
This page does not replace the agreed partner profile or the message description in the UN/EDIFACT directory. Start with Understand and introduce EDI/EDIFACT and set up the partner through Set up an EDIFACT trading partner.
Define the development boundary
Split the implementation into testable units:
| Unit | Input | Output | It does not own |
|---|---|---|---|
| Transport adapter | Byte stream and transport metadata | Unchanged, completely stored bytes | EDIFACT syntax or Commerce effects. |
| EDIFACT validation | Bytes and profile revision | Validated interchange and messages | Partner-specific business meaning. |
| Mapping | Validated message and immutable mapping revision | SalesOrderRequestV1 or EDIFACT output | Transport and price decisions. |
| Commerce application | Canonical order and Commerce binding | Order, review, or rejection | EDIFACT segments. |
| Acknowledgement | Stable syntax, mapping, or business status | CONTRL, APERAK, or ORDRSP | Transport receipt. |
This separation is the primary regression guard. Do not render a transport failure as APERAK, and do not treat HTTP 202 Accepted as business acceptance of an order.
Preserve the bytes
EDIFACT is a byte-stream contract:
- Read to EOF and treat an interrupted stream as a failure.
- Check and store the size and SHA-256 of the bytes actually received.
- Do not normalize line endings, spaces, character sets, or service characters before the integrity check.
- Detect
UNAbefore parsing the remaining segments. - Honor the release character.
?',?+, and?:represent data, not separators. - Keep each interchange at or below 16 MiB.
- For HTTP, use
Content-Type: application/edifactor the documentedapplication/octet-streamfallback.
When a client loads files from a text editor, check byte order marks, automatic character-set conversion, and appended line endings. Visually identical text can have a different SHA-256 value.
Validate the envelope before business segments
At minimum, validate these relationships before mapping:
UNBandUNZform one complete interchange.- Control references and declared message count match.
- Every
UNHhas a correspondingUNT. UNTreferences the correct message and segment count.- Type, version, release, and agency in
UNHmatch the active revision. - Syntax version and service characters match the directory contract.
- Sender and recipient resolve to the expected trading partner.
Use an EDIFACT-aware parser. A regular expression or strings.Split cannot model release characters, repetitions, components, and segment boundaries reliably.
Version mapping definitions
A profile revision contains a closed declarative mapping definition. For inbound ORDERS, sources are EDIFACT paths and targets are SalesOrderRequestV1 paths. For outbound ORDRSP, the direction is reversed.
An EDIFACT path follows this model:
SEGMENT[qualifierElement.qualifierComponent=value].element.component#repetitionExamples:
| Path | Meaning |
|---|---|
BGM.2.1 | First component of the second BGM element. |
DTM[1.1=137].1.2 | Second component of the first DTM element when its first component is 137. |
QTY[1.1=21].1.2 | Quantity value from QTY qualified by 21. |
LIN.3.1 | First component of the third LIN element. |
An omitted component or repetition defaults to 1. Qualifiers and paths must exist in the selected message directory. A plausible-looking segment path that is not valid for ORDERS is rejected.
This shortened example shows the inbound definition format. It is not a complete production mapping:
{
"version": 1,
"directory": "D.96A",
"messageType": "ORDERS",
"direction": "inbound",
"canonicalType": "SalesOrderRequestV1",
"rules": [
{
"target": "schemaVersion",
"operations": [{ "kind": "constant", "value": "1" }],
"required": true
},
{
"target": "externalOrderRef",
"sources": ["BGM.2.1"],
"operations": [{ "kind": "normalize", "format": "trim" }],
"required": true
},
{
"target": "orderedAt",
"sources": ["DTM[1.1=137].1.2"],
"operations": [
{
"kind": "date",
"format": "20060102",
"outputFormat": "2006-01-02T15:04:05Z"
}
],
"required": true
},
{
"target": "lines[].orderedQuantity.value",
"sources": ["QTY[1.1=21].1.2"],
"operations": [
{ "kind": "decimal", "decimalMark": ".", "scale": 3 }
],
"required": true
}
]
}The declarative contract supports only constant, normalize, date, decimal, coalesce, code_map, and unit_map. Unknown fields, scripts, unsupported paths, duplicate targets, or contradictions between direction, directory, message type, and canonical type are rejected.
Verify the canonical result
A valid inbound mapping produces SalesOrderRequestV1. This shortened but structurally complete example shows the shape:
{
"schemaVersion": "1",
"externalOrderRef": "PO-1001",
"buyer": {
"scheme": "GLN",
"qualifier": "14",
"value": "4000001000001"
},
"seller": {
"scheme": "GLN",
"qualifier": "14",
"value": "4000002000008"
},
"orderedAt": "2026-07-16T00:00:00Z",
"currency": "EUR",
"lines": [
{
"lineNumber": 1,
"item": { "gtin": "04012345000016" },
"orderedQuantity": { "value": "2", "unit": "PCE" }
}
]
}Observe these invariants:
schemaVersionis"1".- Buyer, seller, external order reference, order date, and currency are unambiguous.
- Every line has a positive, unique line number.
- At least a GTIN, SKU, or customer catalog reference identifies the item.
- Quantities are positive decimal strings without comma or exponent.
- External prices and tax rates are input evidence; Workspace validates price, tax, and Commerce bindings authoritatively.
Compare expected JSON semantically as a closed canonical object. Duplicate keys, unknown fields, trailing JSON values, and an empty expected payload are invalid fixture expectations.
Build the fixture suite
Create a compact but meaningful suite for every partner profile:
| Category | Minimum case |
|---|---|
| Positive | Complete order with several lines and realistic identifiers. |
| Optionality | Permitted address, price, or date fields are absent. |
| Qualifier | The same segment with another qualifier must not map accidentally. |
| Escape | Data includes an escaped service character. |
| Envelope | Wrong reference, segment count, or message release is rejected. |
| Mapping | Required reference or item identifier is absent. |
| Codes | Unknown unit, currency, or partner identifier is handled explicitly. |
| Limits | Maximum agreed line count and payload size. |
| Idempotency | The same interchange is retried after an uncertain network result. |
| Outbound | ORDRSP is generated byte-for-byte against the approved expected object. |
Do not keep fixtures as unbound local truth. Store input and expected output as authorized Storage objects, bind their SHA-256 values to the profile revision, and start server-side verification. Create new evidence after a change; never reinterpret a terminal verification result.
Implement a robust Partner API client
The production client has two independent loops:
Submit ORDERS
- Produce exactly one complete interchange.
- Store the bytes durably and calculate SHA-256.
- Create one stable idempotency key for those exact bytes.
- Submit the bytes to
/inbox. - After an uncertain network result, retry with the same key and bytes.
- Store
uploadIdandinterchangeIdas technical correlations only.
Process the mailbox
- List due deliveries with a cursor.
- Claim exactly one entry.
- Read its payload to EOF with the claim token.
- Verify size, SHA-256, and the final integrity trailer.
- Store and process the bytes locally and idempotently.
- Acknowledge only then with the same claim token.
The complete HTTP contract, including scopes, headers, status codes, and retry semantics, is in Integrate the B2B Exchange Partner API.
Handle failures by stage
| Stage | Example | Developer action |
|---|---|---|
| Transport | Timeout or interrupted download | Do not process bytes; continue under the same idempotency or claim contract. |
| Syntax | Invalid envelope or unknown syntax version | Check the partner profile and original bytes; do not retry unchanged forever. |
| Mapping | Required path is absent or a code cannot be translated | Create a new revision and new fixtures; do not mutate the active revision. |
| Application | Item is missing or a Commerce gate requires review | Perform business review; do not use mapping as a data-repair mechanism. |
| Outbound | Partner destination is unavailable | Retry delivery of the same stored bytes under the controlled policy. |
Log the HTTP status, stable error code, your correlation, connection ID, and time. Never log an API key, claim token, credential, complete EDIFACT payload, or raw internal error.
Accept the implementation
Your implementation is ready when:
- parser and mapping process the same fixtures reproducibly,
- positive and negative server-side fixture evidence succeeds,
- the real transport has passed complete integrity checks,
- network retries create no second Commerce effect,
- the client processes
CONTRL,APERAK, andORDRSPseparately, - a new partner-profile version creates a new immutable revision, and
- logs and error responses disclose neither secrets nor payloads.