Broker Shield API
Vet a carrier from your TMS, receive a signed proof-of-diligence PDF, and attach it to the load. All endpoints are JSON over HTTPS.
Authentication
Every request requires a bearer token. Generate one from API Access in your workspace. Keys are prefixed cv_live_ and are shown only once.
Authorization: Bearer cv_live_xxxxxxxxxxxxxxxxxxxxxxxx
Base URL: https://broker-shield.com/api/public/v1
Process flow
- 1POST /vettingsYour TMS submits dot_number + order_number. We respond 202 immediately.
- 2Background processingFMCSA snapshot, recorded carrier call, transcription, risk analysis, PDF generation. Typically 1–2 minutes.
- 3vetting.completed webhookWe POST a signed payload to your configured endpoint with a 24h signed pdf_url.
- 4Attach to loadDownload the PDF and attach to the load record in your TMS.
- 5GET /vettings/:id/pdfNeed a fresh signed URL later? Call this endpoint anytime.
Create a vetting
/api/public/v1/vettingsSubmits a vetting job. Returns 202 Accepted immediately. The PDF arrives via webhook once processing completes.
Idempotency: pass an Idempotency-Key header (defaults to order_number). Re-submitting the same key returns the original record with "duplicate": true.
POST https://broker-shield.com/api/public/v1/vettings
Authorization: Bearer cv_live_...
Content-Type: application/json
Idempotency-Key: LD-48211
{
"dot_number": "1234567",
"order_number": "LD-48211",
"external_client_id": "shipper-acme",
"notes": "Reefer, 42k lbs, ATL -> DAL"
}
202 Accepted
{
"vetting_id": "vt_01HF...",
"status": "pending",
"dot_number": "1234567",
"order_number": "LD-48211",
"created_at": "2026-05-15T20:14:11Z",
"message": "Vetting accepted. Webhook will be delivered once processing completes."
}Get a vetting
/api/public/v1/vettings/:idReturns the current status and (when ready) a fresh 24h signed pdf_url. Use this if a webhook is delayed or you want to poll for status from a TMS that can't host a webhook.
GET https://broker-shield.com/api/public/v1/vettings/vt_01HF...
Authorization: Bearer cv_live_...
200 OK
{
"vetting_id": "vt_01HF...",
"status": "completed",
"dot_number": "1234567",
"order_number": "LD-48211",
"external_client_id": "shipper-acme",
"carrier_name": "ACME TRUCKING LLC",
"pdf_status": "ready",
"pdf_url": "https://...supabase.co/.../vt_01HF.pdf?token=...",
"pdf_url_expires_at": "2026-05-16T20:14:11Z",
"last_error": null,
"created_at": "2026-05-15T20:14:11Z",
"signed_off_at": "2026-05-15T20:15:48Z"
}Re-fetch a signed PDF URL
/api/public/v1/vettings/:id/pdfReturns a fresh 24h signed URL for the PDF without re-fetching the rest of the record. Useful when the URL from a webhook delivery has expired.
Add ?redirect=1 to return a 302 redirect straight to the file (handy for browser links or curl -L).
If the PDF is not yet generated, returns 409 with the current pdf_status.
GET https://broker-shield.com/api/public/v1/vettings/vt_01HF.../pdf
Authorization: Bearer cv_live_...
200 OK
{
"vetting_id": "vt_01HF...",
"pdf_url": "https://...supabase.co/.../vt_01HF.pdf?token=...",
"pdf_url_expires_at": "2026-05-16T20:14:11Z"
}List vettings
/api/public/v1/vettingsLists vettings for your workspace, newest first. Use for reconciliation against your TMS.
Query params: limit (max 100, default 25), status, dot_number, order_number, external_client_id, cursor (ISO timestamp; returns rows older than the cursor).
GET https://broker-shield.com/api/public/v1/vettings?status=completed&limit=50
Authorization: Bearer cv_live_...
200 OK
{
"items": [
{ "vetting_id": "vt_01HF...", "status": "completed", ... },
{ "vetting_id": "vt_01HE...", "status": "completed", ... }
],
"next_cursor": "2026-05-10T11:02:00Z"
}Webhooks
Configure an endpoint from API Access. Each endpoint gets a unique signing secret (whsec_...). We retry failed deliveries with exponential backoff.
Event: vetting.completed
POST https://your-tms.example.com/hooks/broker-shield
content-type: application/json
x-vetting-signature: t=1715800451,v1=9a8b... # HMAC-SHA256
{
"event": "vetting.completed",
"vetting_id": "vt_01HF...",
"tenant_id": "ten_...",
"order_number": "LD-48211",
"external_client_id": "shipper-acme",
"dot_number": "1234567",
"pdf_url": "https://...supabase.co/.../vt_01HF.pdf?token=...",
"pdf_url_expires_at": "2026-05-16T20:14:11Z",
"summary": { "legal_name": "ACME TRUCKING LLC", "authority": "ACTIVE" },
"created_at": "2026-05-15T20:15:48Z"
}Respond with any 2xx status to acknowledge. Non-2xx (or no response within 10s) triggers a retry.
Verifying webhook signatures
The x-vetting-signature header has the form t=<unix-ts>,v1=<hex>. Compute HMAC_SHA256(secret, "<t>." + raw_body) and compare in constant time. Reject events with a timestamp more than 5 minutes old to prevent replay attacks.
Node / TypeScript
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody: string, header: string, secret: string) {
const parts = Object.fromEntries(header.split(",").map(p => p.split("=")));
const t = Number(parts.t), v1 = parts.v1;
if (!t || !v1) return false;
if (Math.abs(Date.now() / 1000 - t) > 300) return false; // 5 min skew
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(v1, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}Python
import hmac, hashlib, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t, v1 = int(parts["t"]), parts["v1"]
if abs(time.time() - t) > 300:
return False
mac = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(mac, v1)Ruby
require "openssl"
def verify(raw_body, header, secret)
parts = header.split(",").map { |p| p.split("=", 2) }.to_h
t, v1 = parts["t"].to_i, parts["v1"]
return false if (Time.now.to_i - t).abs > 300
mac = OpenSSL::HMAC.hexdigest("SHA256", secret, "#{t}.#{raw_body}")
Rack::Utils.secure_compare(mac, v1)
endErrors
| Status | Meaning |
|---|---|
| 400 | Invalid JSON or validation failed (details in body). |
| 401 | Missing, malformed, or revoked API key. |
| 404 | Vetting not found (or not in your workspace). |
| 409 | PDF not ready yet (returned by /vettings/:id/pdf). |
| 500 | Server error. Safe to retry after a short backoff. |
Error bodies are always JSON: { "error": "...", "details"?: {...} }.