DocsAPI LogoDocsAPI

EDI 214 Shipment Status Processing: Reading the Real Codes

A shipment shows 'arrived at delivery' and a lender releases a collateral hold. Three EDI events later, the load is marked returned to shipper.

Nupura Ughade
Nupura Ughade
|
September 12, 2026
|
11 min read
EDI 214 Shipment Status Processing: Reading the Real Codes

An asset-based lender monitoring a borrower's inventory in transit watches a shipment tracker flip to "arrived at delivery location" on a Tuesday afternoon. The system treats that as good news: goods pledged as collateral have reached the buyer, the sale is about to complete, and the borrowing base can be adjusted accordingly. What the tracker does not surface, because nobody built the parsing logic to look for it, is that the same shipment generates three more EDI status events over the following two days: an attempted delivery that failed, a hold for a missed appointment, and finally a refusal by the consignee that sends the trailer back to the shipper's dock. The inventory never changed hands. It is now sitting exactly where it started, and the lender's system still shows it as delivered, because "arrived" was the last status code anyone's dashboard actually rendered.

This is not a hypothetical edge case in freight EDI, it is the ordinary behavior of the ANSI X12 214 transaction set when a pipeline treats it as a single delivered/not-delivered flag instead of what it actually is: a running sequence of coded events, each with its own status code, reason code, and timestamp, that has to be read in order to know what actually happened to a shipment. This post is part of a cluster on shipping document processing, and it covers the part of EDI-based shipment tracking that most explanations skip over: the exact structure of the 214's status code system, why a real-time visibility platform has to be built around parsing that structure rather than trusting free-text carrier updates, and a full worked sequence showing how a delivery exception actually looks inside the data.

What the EDI 214 transaction set actually is

The 214, formally the Transportation Carrier Shipment Status Message, is the EDI transaction a motor carrier sends to report what is happening to a shipment as it moves. It follows the load tender and its acceptance, typically exchanged as a 204 and a 990, and it continues firing throughout the shipment's life, from dispatch through pickup, transit, and delivery, with a separate 210 handling the freight invoice only after the movement is done. A single shipment under active transit commonly generates somewhere between five and ten separate 214 transactions before it closes out, each one representing a discrete event rather than a continuous status.

The transaction is built from a small set of segments, and almost all of the operational meaning lives in one of them:

SegmentNameWhat it carries
STTransaction Set HeaderIdentifies the transaction as a 214 and assigns a control number
B10Beginning Segment for Transportation Carrier Shipment StatusThe shipment identification number (SID) and the carrier's SCAC code
L11Business Instructions and Reference NumberPRO number, bill of lading number, purchase order number tying the status to the underlying shipment
LXAssigned NumberA loop counter separating multiple status events reported in one transaction
AT7Shipment Status DetailsThe status code, reason code, and event date and time. This is the segment that matters
MS1Equipment, Shipment, or Real Property LocationThe city, state, and country where the event occurred
MS2Equipment or Container Owner and TypeThe trailer or equipment number reporting the event
SETransaction Set TrailerCloses out the transaction and confirms segment count

Everything a downstream system needs to reconstruct what physically happened to a shipment is packed into the AT7 loop, and a 214 can carry more than one AT7 if the carrier is batching several events into a single transmission rather than sending one 214 per event. Reading a 214 correctly means reading every AT7 loop it contains, in the order the events occurred, not just pulling the last one and discarding the rest.

The AT7 segment: where the actual status codes live

The AT7 segment has seven possible elements, and the first two are the ones that carry the entire meaning of the event:

ElementX12 data elementNameWhat it means
AT7011650Shipment Status CodeThe milestone that occurred, e.g. arrived, departed, delivered, delayed
AT7021651Shipment Status or Appointment Reason CodeWhy the status happened, e.g. normal status, consignee held, refused
AT7031652Shipment Appointment Status CodeWhether the event relates to a pickup or delivery appointment
AT7041651Appointment Reason CodeReason tied specifically to an appointment change, if applicable
AT705373DateEvent date, CCYYMMDD format
AT706337TimeEvent time, HHMM in 24-hour format
AT707623Time CodeTime zone qualifier; absent means the carrier's local time, not UTC

AT701 alone tells you what happened. AT702 tells you why. A code pair of X1 (arrived at delivery location) with no reason code attached is a routine milestone. The same X1 followed shortly by an AH (attempted delivery) with an AT702 of B5 (held per consignee) is the first signal that the delivery is not going to close cleanly. Neither element means much read alone, and a pipeline that only captures AT701 and discards AT702 is throwing away exactly the field that distinguishes a normal event from an exception.

The status code vocabulary: pickup, transit, delivery, exception

Element 1650, the code list behind AT701, defines a few dozen values, and carriers implement subsets of it. The core lifecycle codes that appear across nearly every implementation guide are these:

CodeMeaningLifecycle stage
X3Arrived at pickup locationPre-pickup
AFCarrier departed pickup location with shipmentPickup confirmed
P1Departed terminal locationLinehaul / transit
X6En route to delivery locationIn transit
X4Arrived (on hand) at terminal locationTerminal handling
X1Arrived at delivery locationPre-delivery
AVAvailable for deliveryPre-delivery, at destination terminal
AHAttempted deliveryDelivery attempt, not necessarily successful
D1Completed unloading at delivery locationDelivery, carrier-dependent gloss
J1Delivered to a connecting lineInterline handoff, not final delivery
A7Refused by consigneeException
A3Returned to shipperException, terminal
SDShipment delayedException
CAShipment cancelledException, terminal
K1Arrived at customsCross-border hold
PRCustoms holdCross-border exception
ZZCustoms clearedCross-border, resolved

Two entries in that table deserve more attention than the rest, because they are the two most common sources of a system silently reporting a delivery that never happened. The first is D1. Its literal element 1650 definition is "completed unloading at delivery location," which is close to but not identical to "delivered." Some carriers, including large LTL carriers, use D1 in their own implementation guides as a plain stand-in for delivered. Others treat it strictly as the unload-complete milestone and expect a downstream system to combine it with the absence of any later exception code before calling the shipment closed. A pipeline that hardcodes "D1 means delivered" across every carrier will get it right for some and wrong for others, and the two are indistinguishable without checking that carrier's specific implementation guide.

The second is J1, delivered to a connecting line. This code means the shipment changed custody to an interline partner carrier, not that it reached the consignee. Freight is still moving, a different carrier now controls it, and the location in MS1 identifies the terminal where the handoff happened, not the final destination. Treating J1 as proof of delivery, which a naive keyword match on "delivered" in the code's own description would do, produces a false delivery confirmation on every interline shipment a system processes.

The reason code layer: what actually distinguishes routine from exception

AT702 draws from element 1651, a separate code list from AT701's element 1650, and it is the layer that most tracking dashboards discard because they only render the milestone, not the context. The most important entry in that list is one that means nothing is wrong: NS, normal status, which shows up attached to routine AT7 events like X6 to confirm the shipment is proceeding as expected. A pipeline that alerts on every AT702 value indiscriminately will generate noise on every single in-transit update, since most of them carry NS. The entries that actually warrant a workflow action are the exception reasons:

CodeMeaningTypical pairing
NSNormal status, nothing to flagX6, X3, AF routine events
B5Held per consigneeAH, SD
HBHeld pending appointmentSD, X9
BSRefused by customerA7
BERoad conditionsSD
A2Incorrect addressAH, SD
A1Missed deliveryAH, SD
AGConsignee related delaySD
CACustoms, import or exportK1, PR

The pairing matters more than either code alone. An SD (shipment delayed) with a reason of BE (road conditions) is operationally different from an SD with a reason of B5 (held per consignee): one resolves itself once weather clears, the other requires someone to actually contact the consignee. A rules engine that only stores AT701 loses this distinction entirely and routes both to the same generic "delayed" bucket.

Why real-time visibility runs on parsing codes, not free-text updates

It is worth being precise about why the entire freight visibility industry, from TMS platforms to load boards to lender monitoring tools, is architected around parsing AT701/AT702 pairs rather than ingesting whatever free-text status string a carrier's own tracking portal happens to display. A free-text update, "shipment delayed due to weather in the midwest," carries the same underlying event as an SD/BE pair, but it requires natural language interpretation to extract, it is inconsistent across carriers in wording and detail, and it cannot be reliably matched, deduplicated, or aggregated across thousands of shipments a day without a model in the loop guessing at meaning every single time. A coded AT701/AT702 pair is deterministic. X3 means the same specific thing whether it comes from a national LTL carrier or a regional truckload carrier implementing the same X12 element 1650 code list, and a rules engine can match it exactly, every time, with zero interpretation risk.

The practical difference shows up at scale. A lender or 3PL platform tracking a few dozen shipments a day could plausibly get away with a human reading free-text updates. A platform tracking tens of thousands of active shipments across hundreds of carriers cannot, because the volume makes manual reading impossible and free-text parsing with a language model introduces a nonzero error rate on every single event, compounding across every shipment in the portfolio. Structured EDI status codes are what make automated exception routing, SLA tracking, and collateral monitoring actually reliable at that scale: a system can define a fixed rule, "route to a human reviewer any shipment where the sequence includes AH followed by SD or A7 without a subsequent D1 or X1 within 48 hours," and that rule executes identically across every shipment, every carrier, every day, because the underlying codes are fixed by the X12 standard rather than reworded by whichever carrier happens to be reporting.

There is a second, quieter reason structured parsing matters specifically for lending and trade finance use cases. A carrier's own tracking portal, and even the SMS or email notifications many consumer-facing platforms generate from that portal, is optimized to answer "where is my package," a single current-state question. It is not built to preserve the full sequence of prior events, which is exactly what a lender needs when reconciling a shipment against a borrowing base: not just where the goods are right now, but whether the delivery that the system logged three days ago actually completed cleanly or was quietly reversed by an exception nobody surfaced. The 214's AT7 loop is an append-only event log by design. A portal's current-status display is not, and reconstructing history from it after the fact is often not possible at all.

Worked example: a status sequence that signals a real delivery exception

Consider an inventory finance lender monitoring a truckload shipment of finished goods pledged as collateral under a borrowing-base facility. The lender's platform ingests every 214 transaction tied to the shipment's PRO number as it arrives. Here is the actual sequence of AT7 events the carrier transmits over four days, each with its status code, reason code, and event timestamp:

DayTimeAT701 (status)AT702 (reason)What it means
108:15X3NSCarrier arrived at pickup location
109:40AFNSCarrier departed with shipment, pickup confirmed
118:00X6NSEn route to delivery location, routine transit update
214:20X1NSArrived at delivery location
215:05AHB5Attempted delivery, held per consignee, first exception signal
215:10SDHBShipment delayed, held pending appointment
310:30X9NADelivery appointment requested, carrier contacted consignee
316:45AHBSSecond attempted delivery, refused by customer this time
409:00A7BSFormally refused by consignee
413:15A3NSReturned to shipper, final status

A system reading only the last-known status field, or one that treats "arrived at delivery location" as a stopping point because it superficially reads as complete, closes this shipment out as delivered at the day 2, 14:20 event and never looks again. Under that read, the lender's borrowing base treats the pledged inventory as sold and in the buyer's possession, and the collateral value tied to that shipment gets released from the active monitoring pool.

The actual sequence tells a different story entirely. The X1 at 14:20 was never followed by a D1 or any other successful-delivery code. Instead it was followed by an AH with reason B5 (held per consignee), then an SD held pending appointment, then a second failed attempt this time refused outright (AH/BS), then a formal refusal (A7/BS), and finally A3, returned to shipper. The goods are physically back where they started. A correctly built parsing rule watches for exactly this pattern: any X1 or AV event not followed within a defined window by a D1, CB, or equivalent completed-delivery code, especially if it is instead followed by AH, SD, A7, or A3, gets flagged and routed to a human reviewer rather than auto-closed. That single rule, built on the AT701/AT702 pair rather than on any single code read in isolation, is the difference between a lender correctly holding the collateral flag on inventory that just came back to the shipper's dock and a lender who released a hold on inventory that no longer exists anywhere near the buyer.

Building the parsing rules that actually catch this

Getting this right does not require modeling the entire X12 element 1650 and 1651 code lists in full generality on day one. It requires a small number of deterministic rules applied consistently: define a fixed set of "delivery-complete" codes per carrier, since D1's meaning varies by implementation guide, rather than assuming one global definition; treat J1 as an interline handoff event, never as a final delivery signal, regardless of how its own description reads; watch for any exception-family code, AH, SD, A7, A3, CA, appearing after an X1 or AV event, since that sequence specifically indicates a delivery that started but did not complete cleanly; and preserve the full AT7 sequence per shipment rather than overwriting a "last known status" field with each new event, because the sequence itself, not any single snapshot, is what reveals an exception pattern. None of this requires guessing at carrier intent. It requires reading AT701 and AT702 together, in order, for every event a shipment generates, rather than treating the 214 as a single-value status flag.

Where this fits with the rest of the shipment paper trail

Shipment status data from EDI 214s rarely stands alone in a lending or logistics finance workflow. An advance ship notice, transmitted as an 856, tells a receiver what to expect before the shipment arrives, and the 214's status sequence is what confirms or contradicts that expectation as the shipment actually moves; the mismatch between the two is covered in more depth in the piece on advance ship notice processing. Once a delivery genuinely completes, the physical proof, a signed receipt or electronic signature capture, becomes the document that should independently confirm what the 214's final status codes already indicated, a cross-check discussed in the post on proof of delivery processing. And because the 214 is also what typically triggers freight invoice eligibility under a 210, an exception sequence that a pipeline misreads as a clean delivery does not just create a collateral monitoring gap, it can also trigger premature invoice approval, which is covered separately in the post on freight invoice automation.

The common failure across all of these is the same one: treating an EDI transaction as a single answer to a yes-or-no question when it is actually a structured, ordered record of everything that happened. A 214 with ten AT7 events is not noise to be collapsed into one status field, it is the entire audit trail of a shipment's life, and the exceptions that matter most to a lender or logistics operator are almost never the first event or the last one in isolation. They are the pattern in between.

Written by Nupura Ughade.

Common questions

Frequently asked questions

An EDI 214, the Transportation Carrier Shipment Status Message, is how a motor carrier reports what is happening to a shipment as it moves, from pickup through transit to delivery. It follows the 204 load tender and precedes the 210 freight invoice, and a single shipment typically generates five to ten separate 214 transactions over its life.

AT7 is the Shipment Status Details segment, and it carries the actual status code (AT701), a reason code (AT702), and the date and time of the event. It is the segment that holds the operational meaning of a 214; the surrounding segments mostly identify the shipment and where the event occurred.

Not reliably. The X12 element 1650 definition of D1 is 'completed unloading at delivery location,' and some carriers use it in their own implementation guides as a plain synonym for delivered while others treat it strictly as the unload-complete milestone. A pipeline processing multiple carriers should check each carrier's specific guide rather than assume D1 means the same thing everywhere.

J1 means the shipment was delivered to a connecting line, an interline partner carrier, not to the consignee. Custody of the freight changed hands but the shipment is still moving. Treating J1 as proof of final delivery produces a false delivery confirmation on any shipment that changes carriers mid-route.

Structured AT701 and AT702 codes are deterministic and mean the same thing regardless of which carrier sent them, since they draw from a shared X12 code list. Free-text tracking updates vary in wording across carriers and require interpretation to extract meaning, which does not scale reliably across thousands of shipments a day the way a fixed rule matched against a known code does.

By watching the full ordered sequence of AT7 events rather than just the most recent one, specifically flagging any case where an arrival code like X1 or AV is followed by an exception-family code such as AH, SD, A7, or A3 instead of a completed-delivery code, since that pattern indicates a delivery that started but did not close cleanly.

Nupura Ughade

Content Marketing Lead, DocsAPI

Nupura Ughade creates clear, insightful content on OCR, document AI, and fintech. She combines technical depth with real-world finance use cases to help engineers and operations leaders navigate digital transformation with confidence.

Ready to Transform Your Lending Process?

See how DocsAPI's AI-powered industry classification can help you process loans faster, improve accuracy, and scale your operations.