Inbound mappings: normalizing what your partners send you
Adrian Duyzer
When a partner sends you an EDI document, Tediware translates it to JSON and hands that JSON to your system. Hereâs a fragment of a translated 204, the ship-to party on a delivery stop:
"party_identification_N1_loop": [
{
"party_identification_N1": {
"entity_identifier_code_01": "ship_to_ST",
"name_02": "DC 7034",
"identification_code_qualifier_03": "global_location_number_gln_UL",
"identification_code_04": "0012345045535"
},
"geographic_location_N4": {
"city_name_01": "SMYRNA",
"state_or_province_code_02": "DE",
"postal_code_03": "19977"
}
}
]
ship_to_ST is the translator being helpful: it decodes coded elements into a readable label with the bare code stuck on the end, so you get ship_to_ST rather than ST, case_CA rather than CA. The parties come back as an array of occurrences that you have to pick through by qualifier, since a document can carry a ship-to, a bill-to and a shipper in the same list, in whatever order the partner emitted them.
None of that is a problem while you have one partner. You read the translation, you write your ingestion code against it, youâre done. Then the second partner shows up sending the same document type with a different set of segments present, possibly on a different X12 release, and your reader grows a branch. Then a third one shows upâŠ
Inbound mappings move that normalization into Tediware. You define a canonical shape once, and each partner gets a JSONata mapping from their translation into it, assigned on that partnerâs inbound transaction setting. Your ingestion code targets one structure no matter who sent the document.
The canonical shape
An inbound mappingâs target is an implementation you own, something like âCanonical 204â. Thatâs the same machinery outbound mappings already use: the Validate pane runs your mapped output against the implementationâs JSON schema, and the AI assistant uses that schema as the target when it writes the transformation.
The canonical shape is X12-shaped. This feature buys you consistency across partners, and an arbitrary reshaping into your own domain model is out of scope for now. What it does change, relative to raw translation JSON, is the stuff that makes translation annoying to consume: code elements carry bare codes, so the fragment above becomes "entity_identifier_code_01": "ST" and "identification_code_qualifier_03": "UL"; segments the canonical implementation defines as occurring once come through as a single object, so a read like date_time_G62[0].date_02 becomes date_time_G62.date_02; and anything your system doesnât consume can simply not survive the mapping.
You donât have to build the canonical implementation from scratch, and Iâd rather you didnât. Authoring one by hand takes real X12 knowledge and tempts you into specifying fields no partner actually sends. Duplicate a live partnerâs implementation and rename the copy, and every field in it is a field you genuinely receive. One canonical implementation covers partners on different releases too: inbound mapping selection matches on the transaction set code, so a canonical 204 built from a 5010 partner is a valid target for a partner sending 4010.
Once youâve got your initial canonical implementation built, adding to it and modifying it is easy: just edit the implementation.
You might not need this
Inbound mappings are optional, and plenty of teams should stay without them indefinitely. Partners implementing the same document type frequently translate to similar or identical shapes, and you can onboard several before you hit a difference that matters. When the difference is one renamed key, handling it in your own code is simpler than maintaining a mapping per partner.
The case for adopting them is when partner shapes have genuinely diverged in ways your code branches on, or your partners span X12 releases, or every new partner means new parsing code and youâd like your ingestion contract to stop depending on anyone elseâs choices. Starting without mappings doesnât lock you out of any of that later.
The honest caveat, which Iâd rather say up front than have you discover during a cutover: turning on your first mapping changes the shape your system receives. Thatâs a one-time change to your ingestion code and itâs the last time the shape changes, since after that new partners are absorbed by their mappings. The path we recommend is to promote your first partnerâs implementation into the canonical one and write your ingestion code against the mapped shape from the start, so the cutover never happens at all.
What API consumers see
Following the documented artifact rule, the "usage": "output" artifact on a result is the delivered document. On a mapped inbound flow that artifact is now the mapped document. Previously it was the extractorâs carried-forward output, meaning you downloaded the unmapped translation and a working mapping looked exactly like a mapping that had never run. The translation is still there on the same result, saved as the "usage": "translation" artifact, which is what you want for reproducing a bad mapping.
Delivery always happens, including when the mapping is broken, because the partnerâs data has already arrived and we shouldnât stop it reaching you. And the canonical shape is checked, not assumed: every mapped document is validated against the canonical implementationâs schema before delivery, the same check outbound documents get before theyâre sent to a partner. A mapping can fail two ways, and both set mappingFailed: true on the resultâs detail with a mappingError telling you which. If the transformation fails to run ("kind": "expression"), the delivered output is the untransformed translation. If it runs but produces output that doesnât match the canonical shape ("kind": "validation"), the delivered output is that mapped document, with the schema violations listed in mappingError.errors. So the contract is one flag: unflagged means the output passed validation and is the canonical shape; flagged means route it to an exception path instead of parsing it as one. Infrastructure problems, 5xx responses and timeouts from the transform service, still raise and go to the error path, so a transient outage doesnât quietly ship you unchecked documents as successes.
Getting the codes back with $code
Since the canonical shape carries bare codes and the translation carries decoded ones, every inbound mapping was reimplementing the same string split. Thereâs now a $code function registered in JSONata, in the editor and at runtime both:
$code("ship_to_ST") â "ST"
$code("case_CA") â "CA"
$code("ST") â "ST"
It returns everything after the last underscore, X12 codes never containing one. A value that arrives bare comes back unchanged, so itâs safe on codes the translator couldnât decode. Use it only on qualifiers and code elements: applied to free-form text or a reference value that happens to contain an underscore, it will happily truncate the value for you.
In the editor
Direction is chosen when you create a mapping, and inbound requires an implementation, since the canonical target is the point of the feature. After that the editor is the editor, with a few differences. Inbound mappings have a direction badge, and the mappings list now marks direction with a blue or green square in front of the name.
The source pane is where inbound differs most. An inbound source has to be something the translation pipeline could actually have produced, so you canât have it generated for you and there are two ways to get a real one. From sample EDI takes a pasted or uploaded sample file from your partner, translates it, and drops the bare transaction set in as your sample, with a picker if the file holds more than one set. Create source appears wherever you view a resultâs JSON payload, so a real processed document becomes a source in a couple of clicks. Sources are capped at 25KB, which fits a realistic single document, though a fat one will need its repeated line items trimmed down to a representative few.
The AI mapping assistant knows which direction itâs working in. On an inbound mapping its context explains how translated loops render and tells it to select entries by qualifier rather than by position, since occurrence order and count vary between documents; itâs told to use $code where the target wants bare codes, and that $buildHL and $placeholder are outbound tools it has no business reaching for here. Placeholders donât apply inbound at all (they mark data an outbound source canât supply), so extraction is skipped. The Output pane shows JSON only, an inbound mappingâs output being JSON by definition.
These are schema-to-schema transformations, which is about as mechanical as mapping work gets, so the assistantâs first drafts tend to be good.
Try it
Create a mapping, choose Inbound, pick your canonical implementation, and load a source with From sample EDI using a sample file from the partner. Assign the mapping on that partnerâs inbound transaction setting and run a document through.
The full writeup lives in the Mappings help, and the consumer side is in the Inbound EDI API docs. If you set one up and it behaves oddly, or the canonical shape isnât giving you what you hoped, email me at adrian@tediware.com.