SmartLoads Partner API and webhooks
Connect any TMS, spreadsheet or in-house tool to SmartLoads. Read an agency's loads, AI carrier leads and Cover Cards with an API key, and get signed webhooks the moment a lead comes in, matches a load or a load is booked.
The SmartLoads Partner API is a REST API at https://api.smartloads.io/v1. An agency admin creates an API key in SmartLoads, and any tool can then read that agency's loads, AI carrier leads and Cover Cards as JSON. Webhooks push events such as lead.matched_load and load.booked to your URL within about 30 seconds, signed with HMAC-SHA256, or straight into a Slack channel.
Why SmartLoads has its own API
SmartLoads is an integration partner in its own right, not an add-on to one TMS. A brokerage, an agency or a TMS vendor connects to SmartLoads, so an agency's integrations don't depend on which TMS its parent brokerage runs. It's the same model TMS vendors use to connect to Truckstop.
What you can build with it:
- Sync the live board into your TMS or a spreadsheet. SmartLoads has already turned shipper emails, PDFs and sheets into clean loads, so you read the finished loads.
- Route AI carrier leads to your own tools. Every call the SmartLoads AI agents answer becomes a lead with the carrier, MC number, lane and the rate they asked for.
- Pull Cover Cards. Customer paperwork forwarded to SmartLoads comes back as structured fields matched to a load.
- React to events as they happen. Webhooks for new leads, lead-to-load matches, Cover Cards, bookings and loads leaving the board.
Authentication
Every request carries an agency API key:
Authorization: Bearer sl_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
X-API-Key: sl_live_… also works.
- Agency admins create keys in Settings → Connectors → SmartLoads API. A key is shown once; SmartLoads stores only its SHA-256 hash.
- Create one key per tool or partner so each can be revoked on its own. An agency can have 10 active keys.
- A key belongs to one agency, and every response is that agency's data only.
- Scopes:
loads:read,leads:read,cover_cards:read.
Conventions
- Base URL:
https://api.smartloads.io/v1. JSON in and out. - Formats: timestamps are ISO 8601 UTC; dates such as
board_dateandship_dateareYYYY-MM-DD. - Pagination: lists return
{ "data": [...], "next_cursor": "…" | null }, oldest first. Passnext_cursorback as?cursor=.limitdefaults to 50, max 200. - Incremental sync: save the newest
updated_atyou've seen and ask for?updated_since=<timestamp>next time. - Rate limit: 120 requests per minute per key. Authenticated responses carry
X-RateLimit-LimitandX-RateLimit-Remaining; over the limit you get429withRetry-After: 60. - Errors: an HTTP status plus
{ "error": { "code": "…", "message": "…" } }.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_status, invalid_cursor, invalid_updated_since | Bad parameter |
| 401 | missing_api_key, invalid_api_key | No key, wrong key, or revoked |
| 403 | insufficient_scope | The key lacks the scope for this endpoint |
| 404 | not_found | Unknown endpoint, or no such record for this agency |
| 429 | rate_limited | More than 120 requests a minute |
| 503 | rate_limit_unavailable | The limit couldn't be checked; retry shortly |
Endpoints
GET /me
Checks a key and returns its agency and scopes.
curl https://api.smartloads.io/v1/me -H "Authorization: Bearer $SMARTLOADS_KEY"
{ "agency": { "id": "…", "name": "Example Brokerage" },
"key": { "name": "TMS sync", "prefix": "sl_live_ab12", "scopes": ["loads:read", "leads:read", "cover_cards:read"] } }
GET /loads and /loads/{id}
Scope loads:read. Query parameters:
status:active(the default: on the board, not archived),open,booked,covered,archivedorallupdated_since,limit,cursor
{
"id": "3a2489ad-…",
"load_number": "SL-TX-2025-0418",
"status": "open",
"board_date": "2026-09-14", "ship_date": "2026-09-14", "delivery_date": null,
"customer": { "name": "Example Steel" },
"source": "email_xlsx",
"pickup": { "city": "JEWETT", "state": "TX" },
"delivery": { "city": "WEIMAR", "state": "TX" },
"equipment": { "type": "Flatbed", "length_ft": 20, "tarp_required": false },
"freight": { "commodity": "Rebar", "weight_lbs": 48075 },
"miles": 120,
"rate": { "basis": "flat", "customer_total": 1000, "per_ton": null, "target_pay": 750, "max_pay": 850 },
"posted": { "truckstop_at": "…", "dat_at": null },
"updated_at": "…"
}
For per-ton loads, rate.basis is per_ton and rate.per_ton is the customer's rate per ton.
GET /leads and /leads/{id}
Scope leads:read. These are AI carrier leads: calls answered by the AI phone agents, plus carrier chat and board requests. Query parameters are updated_since (the default is the last 7 days), limit and cursor.
{
"id": "…", "created_at": "…", "updated_at": "…",
"status": "pending",
"load_id": "…",
"channel": "synthflow",
"carrier": { "name": "Example Trucking", "mc": "1234567", "contact_name": "Dana", "phone": "+15125550100" },
"lane": { "pickup": { "city": "Jewett", "state": "TX" }, "delivery": { "city": "Weimar", "state": "TX" } },
"equipment": "Flatbed",
"rate_asked": "900",
"summary": "Has a 48' flatbed empty in Jewett tomorrow morning.",
"high_intent": true
}
The AI quotes only the starting rate and never books; a person books every load.
GET /cover-cards and /cover-cards/{id}
Scope cover_cards:read. These are customer load documents forwarded to SmartLoads, read into fields and matched to a load. Query parameters:
status:matched(the default),possible,unmatchedorallsince,limit,cursor
Webhooks
Agency admins add endpoints in Settings → Connectors → SmartLoads API → Webhooks: a URL, the events to send and an optional name. SmartLoads POSTs each event, usually within 30 seconds.
| Event | When it fires |
|---|---|
lead.created | A carrier lead comes in: AI phone agents, carrier chat, Telegram or the public board |
lead.matched_load | A lead is tied to a load on the board, at intake or when someone attaches it later |
cover_card.ready | Forwarded paperwork has been read, and again when someone confirms its load |
load.booked | A load is booked |
load.covered | A booked load has its carrier |
load.removed | A load leaves the board: archived, cleared or deleted |
Each request looks like this:
POST https://your-tms.example.com/smartloads
Content-Type: application/json
X-SmartLoads-Event: lead.matched_load
X-SmartLoads-Delivery: 0b8f…
X-SmartLoads-Signature: t=1790000000,v1=<hex HMAC-SHA256>
{ "id": "5c1f…", "type": "lead.matched_load", "created_at": "2026-09-26T18:04:11Z",
"agency_id": "…", "data": { "lead": { … }, "load": { … } } }
idis the event id. The same event sent to two endpoints has the same id, so use it to drop duplicates.datauses the same shapes as the read API and shows the record as it was when the event happened.
Verify the signature
Each endpoint has a signing secret (whsec_…), shown once when you add it. v1 is the hex HMAC-SHA256 of "<t>.<raw body>". Recompute it from the raw body, compare in constant time, and reject any t more than 5 minutes old.
import crypto from "node:crypto";
function isFromSmartLoads(rawBody, header, secret) {
if (!header) return false;
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (!(Math.abs(Date.now() / 1000 - Number(parts.t)) <= 300)) return false;
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
const given = Buffer.from(parts.v1 ?? "");
const wanted = Buffer.from(expected);
return given.length === wanted.length && crypto.timingSafeEqual(given, wanted);
}
Retries
- Reply with any
2xxwithin 10 seconds; do slow work after you reply. Redirects aren't followed. - A failed delivery is retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours: 7 tries over about a day.
- If every delivery to an endpoint fails for 3 days, SmartLoads switches it off and shows why. Switching it back on resumes delivery.
- The URL must be public
https. Private and internal addresses are refused.
Slack
Paste a Slack incoming-webhook URL as the endpoint and events arrive as readable Slack messages. A matched lead shows the carrier, MC number, contact and phone, the load and lane, the rate they asked for, the call summary and a link to the lead. It's the quickest way to ping a channel when an AI lead matches a load.
Frequently asked questions
Does SmartLoads replace our TMS?
No. Your TMS stays the system of record for settlements, carriers and accounting. SmartLoads runs the operational front end: load intake, the live board, posting and carrier calls. The API is how your TMS or tools read what SmartLoads produces.
Can the API book or cover loads?
Not in v1, which is read-only plus webhooks. Writing loads in from a TMS is next. Booked and covered status will only ever come from a person's action, never from AI.
How do we get access?
If your agency uses SmartLoads, an admin creates a key in Settings → Connectors → SmartLoads API. If you're a TMS vendor or developer building for broker agencies, contact us through the form on smartloads.io.
Is there a sandbox?
Not yet. A sandbox agency with sample loads and leads is on the roadmap alongside partner apps.