Integration guide
For engineers connecting a TMS, load board, factoring or insurance platform to HaulProof. Everything here works today against https://haulproof.ca. OpenAPI 3.1 · Live status · support@haulproof.ca
1. Keys
hp_test_: sandbox, 10 fictional carriers (USDOT 99990001 to 99990010) on records, search, tiers, tier files, badges and webhooks. Nothing is metered.hp_live_: partner key, all carriers, 25 requests a second with bursts of 100, scopes as agreed.
Send the key on every call as Authorization: Bearer <key>. Keys are shown once, stored only as a SHA-256 hash, can carry an end date and can be revoked one by one. Scopes: read, search, watchlists, alerts. Errors are JSON {"error": {"code", "message"}}; a 429 comes with Retry-After.
curl -H "Authorization: Bearer hp_test_…" "https://haulproof.ca/v1/tiers?ids=99990007,MC123456"2. The nightly tier file (to show a tier next to every carrier)
GET /v1/tier-files returns download links valid for one hour: scope "all" is every active US carrier and every Canadian provincial profile (about 2.2 million rows, about 47 MB gzipped); scope "watchlist" is one of your watchlists. Gzipped CSV, UTF-8, header row. Columns: key, usdot, docket, nsc, cvor, nir, name, jurisdiction, status, tier, hard_rules, reasons, as_of. Rebuilt after the nightly refresh, done by 07:00 Toronto time.
3. Tiers for up to 100 carriers per call
GET /v1/tiers?ids=... takes a USDOT, MC123456, an NSC (MB9002108, AB123-4567), an Ontario CVOR (9 digits), a Quebec NIR (R-123456-7) or a key, and returns per id: key, name, dot_number, provinces, tier, hard_rules, reasons [{signal, weight, label}] and as_of, or error: not_found. Add lang=fr for French labels. Not metered.
4. Signed webhooks for changes
- POST /v1/watchlists, then POST /v1/watchlists/{id}/items/bulk (up to 10,000 identifiers per call; unknown ones are listed back).
- POST /v1/webhooks {"url", "min_severity"} returns the endpoint's signing secret once.
- POST /v1/webhooks/{id}/test sends one signed sample alert right away.
Each alert is POSTed as JSON {"event": "alert.created", "data": {...}} with X-HaulProof-Timestamp and X-HaulProof-Signature: sha256=<HMAC-SHA256(secret, "<timestamp>.<raw body>")>. Answer 2xx within 10 seconds; failed deliveries are retried after 1, 5 and 30 minutes, 2 hours and 12 hours. Types include authority.revoked, insurance.bipd_dropped, safety.placed_oos, safety.risk_elevated and canada.tier_changed. Polling instead: GET /v1/alerts?since_id=.
5. The full record on demand
GET /v1/carriers/{id}: the merged US and Canadian record (status, authority and its history, insurance and cancellations, 24 months of inspections and crashes, equipment, linked carriers, provincial ratings, the tier with its reasons). One call is one metered lookup; answers are cached until the next nightly refresh and carry as_of.
6. A badge to embed
GET /v1/badges/{id}.svg?lang=en returns a 230×44 SVG with the carrier's tier and "Checked with HaulProof" ("Verified by HaulProof" when the carrier verified its profile). Fetch it server-side with your key and cache it until the next refresh.
7. Sample code
curl.sh: Every call with curlhaulproof.mjs: Node 18+ client: tiers, record, bulk watchlist, tier file, webhook testhaulproof.py: The same in Python 3 (standard library only)verify-webhook.mjs: Webhook receiver that checks the signature (Node)verify_webhook.py: Webhook receiver that checks the signature (Python)
All read the key from HP_KEY (and the webhook secret from HP_WEBHOOK_SECRET). Try them with a sandbox key first.
8. Limits
- Per key: 10 requests a second, bursts of 30; partner keys 25 a second, bursts of 100.
- /v1/tiers: 100 ids per call. Bulk watchlist: 10,000 items per call. Tier file links: valid 1 hour.
- Data refresh: nightly, done by 07:00 Toronto time.