Table of contents
API v1.2 · Public reference
Verified against route handlers · August 2026
Start here
Overview
The OTT API follows the shipment lifecycle: create a quote, book an eligible option, then retrieve and track the resulting shipment. Every response is tenant-scoped, so your key can only read records associated with your account.
- Base URL
- https://ontimetrucking.com/api/v1
- Machine-readable contract
- /api/openapi.json
Step 1
Authenticate every request
Send your API key as a Bearer token over HTTPS. Mint one in the portal under API Access. Keys are prefixed ott_live_, shown once at creation (copy it immediately), and you can keep 2 active keys at a time so you can rotate with zero downtime.
Authorization: Bearer $OTT_KEY| Scope | Grants |
|---|---|
| quote:read | Create and retrieve your own rate quotes (runs the live carrier rate-shop). |
| shipment:read | Retrieve shipment details by PRO. |
| shipment:write | Book an eligible saved quote and request pickup (live carrier booking). |
| tracking:read | Retrieve live tracking status and events by PRO. |
A key inherits your account's scopes — you don't choose them at mint time. Scope changes (granting or removing) apply to your existing keys instantly, on the very next call, and a key can never exceed what OTT has granted your account. Calling an endpoint you aren't granted returns 403 insufficient_scope and names the missing scope.
Step 2
Follow the integration flow
- 01
Quote
Send the lane and freight details.
- 02
Book
Turn an eligible quote into a shipment.
- 03
Retrieve
Read the shipment using its PRO.
- 04
Track
Poll the live event timeline.
curl -X POST https://ontimetrucking.com/api/v1/quotes \
-H "Authorization: Bearer $OTT_KEY" \
-H "Content-Type: application/json" \
-d '{
"originZip": "11735",
"destZip": "10001",
"weightLbs": 500,
"pieces": 1,
"freightClass": "100",
"accessorials": ["LIFD", "RESD"]
}'Reference
Endpoint directory
/quotesPrice a shipment and get transit days for every eligible carrier option.GET/quotes/{id}Fetch one saved quote by its API quote ID or visible carrier quote number.POST/quotes/{id}/bookCreate the BOL, assign a PRO, and request pickup in one idempotent call.GET/shipments/{pro}Read shipment details using its carrier PRO number.GET/shipments/{pro}/trackingRead the current status and carrier event timeline.GET/shipments/{pro}/documents/{type}Download your shipment's Bill of Lading (HTML), Proof of Delivery (PDF), or Invoice (PDF) by PRO.There is no quote-list endpoint in v1. To retrieve a quote later, retain the quoteId returned by POST /quotes.
/quotesCreate a rate quote
Price a shipment and get transit days for every eligible carrier option.
Required scope quote:read
| Field | Type | Description |
|---|---|---|
| originZip | string · required | Origin postal code (US, Canada, or Mexico). |
| destZip | string · required | Destination postal code (US, Canada, or Mexico). |
| originCountry / destCountry | US · CA · MX | Optional; each defaults to US. |
| weightLbs | number · 1–30,000 | Total weight for the flat single-commodity path. Legacy: the flat body can't carry dimensions — prefer commodities[]. |
| pieces | integer · 1–1,000 | Handling-unit or pallet count; defaults to 1. |
| freightClass | string | NMFC freight class used for rating; defaults to 100. |
| nmfc | string · max 30 | Optional single-commodity item number. Prints on the BOL; does not affect price. |
| commodities[] | array · max 30 · recommended | Per-line items (see below) — the canonical, recommended request shape. When present, becomes the rating source of truth. |
| commodities[].dimsIn | object · REQUIRED | Per-line dimensions in inches: { length, width, height }. Now required for accurate rating (carriers re-rate on measured size). Compatibility aliases dims and dimensions are accepted. All three values are required together; any side over 336" (28 ft) is refused (422). A request missing dimensions is refused with 422. |
| ratingMode | STANDARD · VOLUME | Optional; defaults to STANDARD (class-based LTL). Use VOLUME to force spot rating for a large load — priced off linear feet, not freight class. You don't have to set it: OTT auto-runs a spot quote per carrier when a load trips that carrier's cubic-capacity rule (TForce Item 575: 750+ cu ft and under 6 lb/cu ft). |
| linearFeet | number · 8–28 | Required when ratingMode is VOLUME: the trailer floor space the freight occupies, in linear feet. Ignored for STANDARD. |
| accessorials[] | string[] · max 20 | Optional OTT service codes or documented friendly labels (see Accessorial codes). Unknown, blank, or malformed values return 422. |
curl -X POST https://ontimetrucking.com/api/v1/quotes \
-H "Authorization: Bearer $OTT_KEY" \
-H "Content-Type: application/json" \
-d '{
"originZip": "11735",
"destZip": "10001",
"weightLbs": 500,
"pieces": 1,
"freightClass": "100",
"accessorials": ["LIFD", "RESD"]
}'curl -X POST https://ontimetrucking.com/api/v1/quotes \
-H "Authorization: Bearer $OTT_KEY" \
-H "Content-Type: application/json" \
-d '{
"originZip": "11735",
"destZip": "10001",
"commodities": [
{
"freightClass": "70",
"weightLbs": 800,
"pieces": 2,
"nmfc": "156600",
"dimsIn": { "length": 48, "width": 40, "height": 48 }
},
{
"freightClass": "125",
"weightLbs": 300,
"pieces": 1,
"nmfc": "049880"
}
]
}'curl -X POST https://ontimetrucking.com/api/v1/quotes \\
-H "Authorization: Bearer ***" \\
-H "Content-Type: application/json" \\
-d '{
"originZip": "91764",
"destZip": "75069",
"ratingMode": "VOLUME",
"linearFeet": 14,
"commodities": [
{
"freightClass": "100",
"weightLbs": 6200,
"pieces": 6,
"dimsIn": { "length": 48, "width": 40, "height": 60 }
}
]
}'{
"quoteId": "qt_3Fa9c2",
"options": [
{ "carrierOptionId": "v1.9VLCDygJZ8Z9WxAdpD4hGfaS3pHG9HOeODuBejZI9FQ", "carrierName": "TForce Freight", "totalUSD": 312.40, "transitDays": 2 },
{ "carrierOptionId": "v1.EYiAHNfjMugZqAbVW_tVF0SAjGPiyVmt5FXvyrk6psM", "carrierName": "Saia LTL Freight", "totalUSD": 318.75, "transitDays": 2 }
],
"expiresAt": "2026-07-24T20:14:00Z"
}Each totalUSD is your final all-in price — nothing to add on top. expiresAt tells you how long OTT stands behind the displayed rate. The saved quote stays retrievable past it, but booking an expired quote is refused with 409 conflict (quotes are valid for 3 days by default) — request a fresh quote to book at current pricing.
/quotes/{id}Retrieve a quote
Fetch one saved quote by its API quote ID or visible carrier quote number.
Required scope quote:read
Use the API quoteId returned by quote creation. The visible carrier “Quote #” is also accepted for convenience; the API id is matched first.
curl https://ontimetrucking.com/api/v1/quotes/qt_3Fa9c2 \
-H "Authorization: Bearer $OTT_KEY"{
"id": "qt_3Fa9c2",
"originZip": "11735",
"destZip": "10001",
"weightLbs": 500,
"pro": null,
"createdAt": "2026-07-21T18:42:00Z"
}/quotes/{id}/bookBook a quote
Create the BOL, assign a PRO, and request pickup in one idempotent call.
Required scope shipment:write
| Field | Type | Description |
|---|---|---|
| pickupDate | YYYY-MM-DD · required | Cannot be in the past. |
| readyTime | HH:MM · required | Earliest pickup time in 24-hour format. |
| closeTime | HH:MM · required | Dock close; must be later than readyTime. |
| shipFrom / shipTo | object · optional | Address override; saved quote/profile values are reused when omitted. |
| requester | object · optional | Pickup requester company, contact, email, and phone. |
| customerReference | string · max 50 | Your PO or pickup reference; sent to the carrier and returned on shipment reads. |
| poNumber | string · max 30 | Purchase-order number. Prints on the OTT BOL and rides to the carrier — TForce as a structured PO reference; Saia, XPO, and Old Dominion in carrier/driver instructions. |
| pickupReference | string · max 30 | Your pickup/warehouse reference. Prints on the OTT BOL and rides to the carrier — TForce in the native pickup Reference Number field; Saia, XPO, and Old Dominion in carrier/driver instructions. |
| notes | string · max 500 | Special handling or pickup instructions printed on the OTT BOL. |
curl -X POST https://ontimetrucking.com/api/v1/quotes/qt_3Fa9c2/book \
-H "Authorization: Bearer $OTT_KEY" \
-H "Content-Type: application/json" \
-d '{
"pickupDate": "2026-07-23",
"readyTime": "09:00",
"closeTime": "17:00",
"customerReference": "PO-88231",
"poNumber": "PO-88231",
"pickupReference": "WH-4471",
"notes": "Call dock 30 minutes before arrival"
}'{
"quoteId": "qt_3Fa9c2",
"status": "BOOKED",
"pro": "773117166",
"bolNumber": "BOL-260723-3FA9C2",
"pickupConfirmation": "WBU68949302",
"carrierName": "TForce",
"customerReference": "PO-88231"
}/shipments/{pro}Retrieve a shipment
Read shipment details using its carrier PRO number.
Required scope shipment:read
curl https://ontimetrucking.com/api/v1/shipments/773117166 \
-H "Authorization: Bearer $OTT_KEY"{
"pro": "773117166",
"status": "IN_TRANSIT",
"originZip": "11735",
"destZip": "10001",
"weightLbs": 500,
"freightClass": "100",
"dims": { "lengthIn": 48, "widthIn": 40, "heightIn": 48 },
"pieces": 1,
"nmfc": "156600",
"customerReference": "PO-88231",
"accessorials": ["LIFD"],
"commodities": []
}Note the response returns dimensions as dims.lengthIn (and null when none were captured) — the read shape, not the dimsIn.length request shape.
/shipments/{pro}/trackingTrack a shipment
Read the current status and carrier event timeline.
Required scope tracking:read
curl https://ontimetrucking.com/api/v1/shipments/773117166/tracking \
-H "Authorization: Bearer $OTT_KEY"{
"pro": "773117166",
"status": { "code": "IN_TRANSIT", "description": "In transit" },
"pickup": { "date": "2026-07-23T14:02:00Z" },
"delivery": null,
"events": [
{
"date": "2026-07-24T09:14:00Z",
"description": "Departed service center",
"serviceCenter": "Edison, NJ"
},
{
"date": "2026-07-23T14:02:00Z",
"description": "Picked up",
"serviceCenter": "Farmingdale, NY"
}
]
}Live tracking is TForce-backed in v1. Carrier brownouts return 503 with a Retry-After header; OTT self-dispatch shipments return an honest OTT dispatch status rather than a fabricated carrier timeline.
/shipments/{pro}/documents/{type}Retrieve a shipment document
Download your shipment's Bill of Lading (HTML), Proof of Delivery (PDF), or Invoice (PDF) by PRO.
Required scope shipment:read
curl https://ontimetrucking.com/api/v1/shipments/773117166/documents/bol \\
-H "Authorization: Bearer ***"curl https://ontimetrucking.com/api/v1/shipments/773117166/documents/bol?format=pdf \\
-H "Authorization: Bearer ***" -o bol.pdfcurl https://ontimetrucking.com/api/v1/shipments/773117166/documents/pod \\
-H "Authorization: Bearer ***" -o pod.pdfcurl https://ontimetrucking.com/api/v1/shipments/773117166/documents/invoice \\
-H "Authorization: Bearer ***" -o invoice.pdfbol returns your shipment's OnTime Trucking Bill of Lading as a printable text/html document by default; add ?format=pdf to get it as application/pdf.pod returns the carrier Proof of Delivery as application/pdf once the shipment has been delivered; before then it returns 404. invoice returns your OnTime Trucking invoice as application/pdf once the shipment is booked. All are for shipments on your own account, looked up by their carrier PRO, and contain customer-facing information only — never carrier cost or margin.
Reference
Accessorial codes
Pass accessorial services on POST /quotes as an accessorials array. Canonical codes are recommended — for example ["LIFD", "RESD"] — and are case-insensitive. Friendly accessorial labels such as liftgate, residential, appointment, limitedAccess, inside, tradeShow, and hazmat are normalized to the matching code. Add pickup or delivery to choose the side; a directionless label defaults to delivery.
| Code | Service | Applies to |
|---|---|---|
| INPU | Inside pickup | Pickup |
| LIFO | Liftgate pickup | Pickup |
| LAPU | Limited-access pickup | Pickup |
| TRPU | Tradeshow pickup | Pickup |
| RESP | Residential pickup | Pickup |
| IBFD | In-bond freight pickup | Pickup |
| INDE | Inside delivery | Delivery |
| LIFD | Liftgate delivery | Delivery |
| LADL | Limited-access delivery | Delivery |
| NTFN | Call before delivery | Delivery |
| TRDS | Tradeshow delivery | Delivery |
| RESD | Residential delivery | Delivery |
| GWHD | Grocery warehouse delivery | Delivery |
| HAZD | Hazardous materials | Shipment |
Reference
Errors use RFC 7807
Switch on the stable code, not the human-readable title or detail. Every error echoes the requestId too.
Validation responses may also include an errors[] array. Each item has a machine-readable path, code, message, and optional expected value so an integration can correct the exact field.
{
"type": "https://ontimetrucking.com/technology/api#errors",
"title": "Resource not found",
"status": 404,
"detail": "Shipment not found.",
"code": "not_found",
"requestId": "0be4c35a-0f05-4905-97da-d3367748eaa4"
}| HTTP | Code | Meaning |
|---|---|---|
| 401 | missing_key · invalid_key · revoked_key · expired_key | Authentication failed. |
| 403 | insufficient_scope · no_tenant_scope | The key cannot perform this operation. |
| 404 | not_found | Not found, or the record does not belong to your account (no existence leak). |
| 409 | conflict | A concurrent booking claim is already in progress on this quote. |
| 422 | validation_failed | Bad input — e.g. a field out of range, a side over 336", or a lane OTT dispatches itself. |
| 429 | rate_limited | Slow down; wait for the Retry-After interval before retrying. |
| 503 | upstream_unavailable · upstream_error | A carrier service is temporarily unavailable; see Retry-After. |
Reference
Rate limits and retries
Per-client caps, shared across our servers. On exceed you get 429 with a Retry-After header (seconds) — wait, then retry.
| Policy | Applies to | Limit |
|---|---|---|
| api_default | Most calls — quote reads, booking, shipment reads, tracking | 60 / minute |
| api_rate | Quote creation (POST /quotes) — the live carrier rate-shop | 20 / 10 minutes |
Limits are per client and shared across OTT servers. On a 429 or a retryable 503, respect the Retry-After header before trying again.
Reference
Changelog
The OTT API is versioned in the OpenAPI spec (info.version). Changes are additive and backward-compatible within v1 — new optional fields and endpoints may appear; existing request and response shapes are not broken.
Item dimensions are now required on POST /quotes. Carriers re-rate every shipment on measured size and weight and bill the difference (extreme-length, cube, reweigh). A class-only quote can't price those adjustments, so we now rate on real dimensions to keep your quotes accurate and avoid surprise re-rates.
- Required now — a quote missing any line item's
length,width, orheightis refused with422 validation_failed. Send each commodity withdimsIn: { length, width, height }in inches. - Canonical shape — use the
commodities[]array (each line withdimsIn,weightLbs,pieces,freightClass). The legacy flatweightLbs/freightClassbody can't carry dimensions and is no longer accepted for rating. - Commodity description field — each commodity line now accepts an optional plain-text
description(e.g."machine parts"). Prints on the BOL; does not affect the rate.
Documentation accuracy update — no request or response shapes changed. Brings the published reference in line with current live behavior.
- Extended dimension ceiling — the per-side limit is now
336"(28 ft), not96". Loads up to the ceiling are rated online with per-carrier extreme-length handling; only a side over 336" is refused with422. - Carrier selection with
carrierOptionId—POST /quotesreturns acarrierOptionIdon every option; pass it unchanged toPOST /quotes/{id}/bookto book that exact priced option. A stale or unknown id returns409. - Quote-expiry booking gate clarified — booking an unbooked quote past its validity window (default 3 days) is refused with
409 conflict. (Previously documented as advisory-only or a 422; the live behavior is a 409.)
Invoice document retrieval on GET /shipments/{pro}/documents/{type}. Backward-compatible — bol and pod are unchanged.
- Invoice PDF — request
type=invoiceto download your OnTime Trucking invoice asapplication/pdfonce the shipment is booked (before booking it returns404). It is the same invoice shown in the OTT portal, and contains customer-facing information only — never carrier cost or margin. No new scope: it uses the existingshipment:read. - Bill of Lading as PDF — add
?format=pdftotype=bolto receive the BOL asapplication/pdf. Backward-compatible: omit it andbolstill returns HTML as before.
Volume / spot rating for large and multi-pallet loads on POST /quotes. Backward-compatible — standard LTL requests are unchanged.
- Volume / spot quoting — send
ratingMode: "VOLUME"withlinearFeet(8–28) to rate a large load off linear feet instead of freight class. Omit both for standard class-based LTL. - Automatic cubic-capacity spot rating — OTT computes each load's cube and density and auto-runs a spot quote per carrier when it trips that carrier's cubic-capacity rule (TForce Item 575: 750+ cu ft and under 6 lb/cu ft), with no pallet-count threshold. Volume lanes that need a manual spot quote return a clear
422pointing to OTT dispatch (not a retryable error). - Safer input compatibility — per-commodity
dimsanddimensionsare accepted as aliases fordimsIn; common accessorial labels are normalized to OTT codes; malformed or conflicting values return structured field errors.
Backward-compatible additions and stricter request validation. The endpoint path is unchanged (/api/v1) — the path only changes for a breaking redesign.
- Multi-commodity quoting — send
commodities[](max 30 lines) onPOST /quotes; each line rates with its own freight class and the top-levelweightLbs/pieces/freightClassbecome optional aggregates. When present,commodities[]is the rating source of truth. - Per-commodity NMFC — each line accepts an
nmfcitem number (also available as a single-commoditynmfc). Prints on the BOL; does not affect price. commodities[].dimsInvalidation — dimensions live only inside a per-linedimsInobject ({ length, width, height }in inches, all three required together). A side over 336" (28 ft), or dimensions sent at the wrong level, is now rejected with422naming the offending field — before any carrier call.- Booking
notesandcustomerReferenceonPOST /quotes/{id}/book—customerReference(your PO, echoed on shipment reads) andnotes(special-handling text printed on the OTT BOL).
- Initial public v1:
POST /quotes,GET /quotes/{id},POST /quotes/{id}/book,GET /shipments/{pro}, andGET /shipments/{pro}/tracking. - Bearer API-key auth with account-inherited scopes, RFC 7807 errors with a stable
code, per-client rate limits, andX-Request-Idon every response.
Ready to integrate?
Start with a scoped key and one test quote.
Grab a key in the portal, then point your tooling at the spec. For support, include the endpoint, timestamp, and X-Request-Id — and never email your API key. Questions? sales@ontimetrucking.com or call dispatch at (800) 248-4630.
