[1]What is an exception?
An exception is a shipment whose observed state has left the tolerance for its lane. Nothing else enters the system. A shipment tracking inside tolerance is watched continuously but never shown, which is why the queue stays short enough to be read.
Every exception carries the same five fields from the second it is flagged:
- shipment. The carrier reference it was flagged against, e.g. SHP-48211.
- issue. One of LATE SHORT MISROUTE. The word carries the meaning; the hue is a mark beside it.
- owner. A named person, attached by the first matching routing rule. An unmatched exception falls to a default owner, never to nobody.
- deadline. Attached in the same second as the flag. Rule 07, for example, gives high-value late freight +4 h.
- state. open until the close criteria are met: the carrier has a confirmed recovery plan and the customer accepted the revised date. Then the row leaves the queue on its own.
The lifecycle of one exception, end to end, is walked on the platform tour. What the queue replaces is on Throughline vs. spreadsheets and email.
[2]What is a lane tolerance?
A lane tolerance answers the question "how late is actually late, here". It is the number of hours a shipment on one origin-destination pair may run behind plan before it flags. Tolerance is set per lane, never globally, because a day of slack on a 2,100-mile linehaul is noise and a day on a 250-mile lane is a failed delivery.
Tolerances are fitted, not guessed: 90 days of history are backfilled in week one and fitted in week two, so day-one alerts are not noise. A manual value always wins over a fitted one.
GET /v1/lanes/{lane}/tolerance
{
"lane": "ORD-DFW",
"flag_after_h": 8,
"source": "fitted",
"history_days": 90,
"manual_override": null,
"fitted_at": "2026-08-21T06:00:00Z"
}
This is the tolerance SHP-48211 broke in clause [3]: observed delay 38 h against a fitted 8 h. [illustrative]
[3]How do you read the queue over REST?
The base URL is https://api.throughline.example/v1. Every request carries a bearer token created in the workspace; tokens are scoped read or read-write. Limits are 120 requests per minute per token, with Retry-After on 429. [reserved .example domain · resolves nowhere]
List open exceptions
GET /v1/exceptions
curl "https://api.throughline.example/v1/exceptions?state=open&issue=late&limit=1" \
--header "Authorization: Bearer tl_live_9f3c2a…"
{
"data": [
{
"id": "exc_01J9ZK7R2M",
"shipment": "SHP-48211",
"issue": "late",
"state": "open",
"lane": "ORD-DFW",
"carrier": { "name": "Estes", "scac": "EXLA" },
"declared_value_usd": 31400,
"observed_delay_h": 38,
"tolerance_h": 8,
"rule": "07",
"owner": { "initials": "KT", "name": "K. Tan" },
"flagged_at": "2026-08-28T14:41:12Z",
"due_by": "2026-08-28T18:41:12Z",
"thread": "/v1/exceptions/exc_01J9ZK7R2M/thread"
}
],
"has_more": true,
"next_cursor": "exc_01J9ZK7R2M"
}
The same shipment, the same rule, the same +4 h deadline as the thread on the platform tour. Timestamps are UTC; the queue renders them in the workspace timezone. [illustrative data]
Query parameters
| Parameter | Type | Accepts | Default |
|---|---|---|---|
| state | string | open · resolved · all | open |
| issue | string | late · short · misroute | all issues |
| lane | string | Origin-destination pair, e.g. ORD-DFW | all lanes |
| carrier | string | SCAC, e.g. EXLA | all carriers |
| limit | integer | 1 to 100 | 25 |
| cursor | string | An id from a previous next_cursor | first page |
[4]How do webhooks arrive?
Every state change POSTs to your endpoint as it happens; webhooks ship on the Operations plan and up. Four event types cover the lifecycle: exception.flagged, exception.routed, exception.escalated and exception.resolved. Delivery retries on a backoff for 24 h, and every payload is signed.
{
"id": "evt_01J9ZK7R9T",
"type": "exception.flagged",
"created_at": "2026-08-28T14:41:12Z",
"data": {
"exception": "exc_01J9ZK7R2M",
"shipment": "SHP-48211",
"issue": "late",
"lane": "ORD-DFW",
"observed_delay_h": 38,
"tolerance_h": 8
}
}
Verify the signature
Each delivery carries a timestamped HMAC of the raw body:
Throughline-Signature: t=1787928072,v1=6b3f9c04a1…
Recompute it with your endpoint secret over t plus the unparsed body, and refuse anything older than five minutes:
import { createHmac, timingSafeEqual } from "node:crypto";
// header: "t=1787928072,v1=6b3f..." · body: the raw request bytes, unparsed
export function verifySignature(header, body, secret) {
const parts = Object.fromEntries(
header.split(",").map((pair) => pair.split("="))
);
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${parts.t}.${body}`)
.digest("hex");
const got = Buffer.from(parts.v1 ?? "", "utf8");
const want = Buffer.from(expected, "utf8");
return got.length === want.length && timingSafeEqual(got, want);
}
[5]How does a 214 become an event?
On the Network plan Throughline reads X12 214 shipment status messages directly off the wire and returns a 997 acknowledgment for every set received. The 204 load tender is read alongside so each 990 response pairs with its tender. The full set list is in the integrations directory.
A minimal 214, envelope omitted:
ST*214*0001~
B10*0745912*SHP-48211*EXLA~
L11*8100448211*BM~
AT7*X1*NS***20260828*134100*LT~
MS1*DALLAS*TX*US~
MS2*EXLA*53124~
SE*7*0001~
Field mapping
| Element | X12 name | Example | Becomes |
|---|---|---|---|
| B10-01 | Reference identification, the carrier pro number | 0745912 | shipment.pro_number |
| B10-02 | Shipment identification number | SHP-48211 | shipment.reference |
| B10-03 | Standard carrier alpha code | EXLA | carrier.scac |
| L11-01/02 | Reference number and qualifier; BM is bill of lading | 8100448211 BM | shipment.bol |
| AT7-01 | Shipment status code; X1 is arrived at delivery | X1 | event.status_code |
| AT7-02 | Status reason code; NS is normal status | NS | event.reason_code |
| AT7-05/06 | Status date and time | 20260828 134100 | event.occurred_at |
| AT7-07 | Time code; LT is local time | LT | event.occurred_at, offset resolution |
| MS1 | Equipment or shipment location | DALLAS TX US | event.location |
| MS2 | Equipment owner and number | EXLA 53124 | event.trailer |
The event lands in the same tolerance model as every other feed: a 214 late against its lane flags exactly the way a polled API status does.
[6]How does data leave?
Your data is yours, queryable where you work:
- CSV and Parquet, exported from any queue, attachments included. Every plan.
- Snowflake secure data share of exception history and the event log. Network plan.
- Read-only SQL workspace over the full exception schema. Network plan.
-- median minutes from carrier signal to flagged row, by lane, last 30 days
SELECT
lane,
approx_percentile(minutes_to_flag, 0.5) AS median_detect_min,
count(*) AS exceptions
FROM exception_events
WHERE flagged_at >= dateadd('day', -30, current_date)
GROUP BY lane
ORDER BY exceptions DESC;
The 38 min median quoted across this site is the illustrative output of exactly this query. Retention and deletion terms live on the trust center.