Developers · API Reference
The OnTime Trucking API
Quote and track your freight programmatically. REST + JSON, Bearer-token auth, scoped keys, RFC 7807 errors. Read it here, grab the machine-readable spec, and integrate in minutes.
- Base URL
- https://ontimetrucking.com/api/v1
Step 1
Authenticate
Every request carries an 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_live_xxxxxxxxStep 2
Scopes & access
A key can only do what OnTime Trucking has granted your account — its scopes. Each endpoint below lists the scope it requires; calling one you aren't granted returns 403 insufficient_scope (and names the missing scope).
| scope | grants |
|---|---|
| quote:read | Create and read your own rate quotes. |
| rate:read | Price a shipment for live transit days (live carrier call). |
| shipment:read | Look up shipment details by PRO. |
| shipment:write | Book a saved quote — assign a PRO and schedule the pickup (live carrier booking). |
| tracking:read | Read live tracking status and events by PRO. |
How keys and scopes fit together
- Keys inherit your account's scopes. You don't pick scopes when you mint a key — it automatically carries whatever your account is granted.
- Scope changes apply to your existing keys instantly. If OTT grants a new scope (say
tracking:read), your current key can use it on the very next call — no need to mint a new one. Removing a scope takes effect just as fast. - A key can never exceed its grant. There's no way to widen a key beyond what OTT has granted your account.
Reference
Endpoints
| Method | Path | Scope | Description |
|---|---|---|---|
| POST | /quotes | quote:read | Create a rate quote — returns customer totals + transit days per carrier option. |
| GET | /quotes/{id} | quote:read | Fetch one of your saved quotes by id. |
| POST | /quotes/{id}/book | shipment:write | Book a saved quote — assigns a PRO and schedules the pickup in one call. Idempotent. |
| GET | /shipments/{pro} | shipment:read | Shipment details by 9-digit PRO. |
| GET | /shipments/{pro}/tracking | tracking:read | Live tracking timeline by PRO (TForce in V1). |
Which id goes in the path?
{id}(quotes) — the “Quote #” shown on your quote page (also thequoteIdreturned byPOST /quotes).{pro}(shipments) — the 9-digit PRO assigned when a shipment is booked.
Full request/response schemas live in the OpenAPI spec — point your codegen, Postman, or AI agent at it for the canonical types.
Reference
Accessorial codes
Pass accessorial services on POST /quotes as an accessorials array of these codes — for example "accessorials": ["LIFD", "RESD"]. Send the code, not a label like “liftgate”.
| code | service | applies to |
|---|---|---|
| LIFO | Liftgate Pickup | Pickup |
| INPU | Inside Pickup | Pickup |
| LAPU | Limited Access Pickup | Pickup |
| TRPU | Tradeshow Pickup | Pickup |
| RESP | Residential Pickup | Pickup |
| IBFD | In-Bond Freight Pickup | Pickup |
| LIFD | Liftgate Delivery | Delivery |
| INDE | Inside 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 | Hazmat | Hazmat |
Good to know
- Codes are case-insensitive —
lifdandLIFDboth work. - An unknown code returns
422 validation_failedlisting the valid codes — it is never silently ignored. - Accessorial charges are folded into each option's
totalUSD; they are not itemized as separate line items.
Step 3
Make your first call
Replace $OTT_KEY with your key. Prefer a guided walkthrough? The interactive quickstart in your portal takes you from key → first verified call with a live test button.
export OTT_KEY=ott_live_xxxxxxxx
# Fetch one of YOUR quotes by its id
# (the "Quote #" on the quote page, or the quoteId from POST /quotes)
curl https://ontimetrucking.com/api/v1/quotes/759124905 \
-H "Authorization: Bearer $OTT_KEY"
# Live tracking by PRO
curl https://ontimetrucking.com/api/v1/shipments/123456789/tracking \
-H "Authorization: Bearer $OTT_KEY"
# Create a rate quote (add accessorials by code — see the Accessorial codes table)
curl -X POST https://ontimetrucking.com/api/v1/quotes \
-H "Authorization: Bearer ***" \
-H "Content-Type: application/json" \
-d '{"originZip":"11735","destZip":"10001","weightLbs":500,"accessorials":["LIFD","RESD"]}'
# Create a rate quote with per-commodity line items (mixed classes + NMFC per line).
# When you send commodities[], each line is rated with its own class; top-level
# weightLbs/pieces/freightClass become optional aggregates.
curl -X POST https://ontimetrucking.com/api/v1/quotes \
-H "Authorization: Bearer ***" \
-H "Content-Type: application/json" \
-d '{"originZip":"11735","destZip":"10001","commodities":[
{"freightClass":"70","weightLbs":800,"pieces":2,"nmfc":"156600"},
{"freightClass":"125","weightLbs":300,"pieces":1,"nmfc":"049880"}
]}'
# Book that quote — assigns a PRO and schedules the pickup in one call.
# ship-from/ship-to are reused from the saved quote unless you override them here.
# notes prints on the OTT BOL; customerReference (your PO / pickup ref) rides to
# the carrier so it lands on carrier paperwork + tracking.
curl -X POST https://ontimetrucking.com/api/v1/quotes/qt_3Fa9c2/book \
-H "Authorization: Bearer ***" \
-H "Content-Type: application/json" \
-d '{"pickupDate":"2026-07-15","readyTime":"09:00","closeTime":"17:00","customerReference":"PO-88231","notes":"Call dock 30 min before arrival"}'You only ever see your own data — a quote or shipment that isn't yours returns 404 (never a hint that it exists).
X-Request-Id — keep it for support
X-Request-Id header — a unique id for that one call, mirrored in our logs. On errors it's also in the body as requestId. Quote it to support and we find your exact request instantly.Sample — POST /quotes (201)
{
"quoteId": "qt_3Fa9c2",
"options": [
{ "carrierName": "TForce Freight", "totalUSD": 312.40, "transitDays": 2 },
{ "carrierName": "Ward", "totalUSD": 318.75, "transitDays": 2 }
],
"expiresAt": "2026-06-29T20:14:00Z"
}Sample — POST /quotes/{id}/book (201)
{
"quoteId": "qt_3Fa9c2",
"status": "BOOKED",
"pro": "773117166",
"bolNumber": "BOL-260715-3FA9C2",
"pickupConfirmation": "WBU68949302",
"carrierName": "TForce",
"customerReference": "PO-88231"
}Booking is idempotent — safe to retry
pro and pickupConfirmation with a 200 (not a second shipment). One booking per quote — a retry after a network blip never double-books. Provide pickupDate, readyTime, and closeTime (HH:MM, 24h); ship-from and ship-to are reused from the saved quote unless you pass shipFrom/shipTo overrides. On some shipments the carrier PRO is assigned shortly after booking rather than instantly — pro is null until then; the booking and pickup are confirmed either way.Sample — GET /shipments/{pro} (200)
{
"pro": "123456789",
"status": "IN_TRANSIT",
"originZip": "11735",
"destZip": "10001",
"weightLbs": 500,
"freightClass": "100",
"dims": { "lengthIn": 48, "widthIn": 40, "heightIn": 48 },
"pieces": 2,
"nmfc": "156600",
"customerReference": "PO-88231",
"accessorials": ["LIFD"],
"commodities": []
}Sample — GET /shipments/{pro}/tracking (200)
{
"pro": "123456789",
"status": { "code": "IN_TRANSIT", "description": "In transit" },
"pickup": { "date": "2026-06-25T14:02:00Z" },
"delivery": null,
"events": [
{
"date": "2026-06-26T09:14:00Z",
"description": "Departed service center",
"serviceCenter": "Edison, NJ"
},
{
"date": "2026-06-25T14:02:00Z",
"description": "Picked up",
"serviceCenter": "Farmingdale, NY"
}
]
}Every response is clean and ready to use — one all-in price, transit time, live status, and your shipment documents. No rate tables to decode, no surcharges to add up.
Reference
Errors (RFC 7807)
New to RFC 7807?
type, title, status, and detail, served as application/problem+json. We add a stable code so you switch on one short string instead of parsing a sentence.Switch on code, not the human title. Every error echoes the requestId too.
{
"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"
}| code | meaning |
|---|---|
| missing_key / invalid_key / revoked_key / expired_key | 401 — authentication failed |
| insufficient_scope / no_tenant_scope | 403 — key lacks the required scope |
| not_found | 404 — not found, or not yours (no existence leak) |
| conflict | 409 — already being booked / booked (idempotent replay returns the existing booking) |
| validation_failed | 422 — bad input (e.g. PRO not 9 digits, or a lane OTT dispatches itself) |
| rate_limited | 429 — slow down; see Retry-After header |
| not_implemented | 501 — endpoint/feature not enabled |
| upstream_unavailable / upstream_error | 503 — carrier temporarily unavailable; see Retry-After |
Reference
Rate limits
Per-client caps, shared across our servers. On exceed you get 429 with a Retry-After header (seconds) — wait, then retry.
| policy | endpoints | limit |
|---|---|---|
| api_default | reads (quotes/{id}, shipments, tracking) | 60 / min |
| api_rate | POST /quotes + POST /quotes/{id}/book (live carrier calls) | 20 / 10 min |
| api_docs | documents | 30 / 5 min |
Ready to build?
Grab a key in the portal, then point your tooling at the spec. Questions? Email sales@ontimetrucking.com or call dispatch at (800) 248-4630 — include your requestId so we can trace the call.
