CarrierVet
Vérifiez les transporteurs routiers américains par numéro MC ou USDOT : verdict PASS/REVIEW/FAIL, signaux de risque, historique, justificatifs.
Serveur MCP hébergé
npx add-mcp 'https://carriervet.dev/mcp'S’installe dans Claude Code, Codex, Cursor et plus
Documentation
API reference
Beta: CarrierVet is in active testing. Verify critical decisions against FMCSA SAFER, and report anything that looks wrong to hello@carriervet.dev.
Base URL: https://carriervet.dev. Send Authorization: Bearer cv_live_... on every /v1 route except signup, login, and the Stripe webhook.
Quickstart
1. Get a free key (50 lookups/month, no card). 2. Call the API or connect an AI agent. USDOT 53467 (Werner Enterprises, a real carrier) is always free to look up.
curl -s https://carriervet.dev/v1/carriers/53467 -H "authorization: Bearer $KEY"
Using an AI assistant for dispatch rather than code? See carrier vetting for dispatchers. Integration patterns: workflows. Every field explained: example.
AI agents (MCP)
MCP server https://carriervet.dev/mcp · Streamable HTTP · Authorization: Bearer <key>. Same key, quota and per-minute limits as REST. Tools: lookup_carrier, get_risk, check_load_eligibility, get_history, search_carriers, batch_lookup, get_receipt, get_usage, list_watchlist, add_to_watchlist, remove_from_watchlist. Agent docs: llms.txt · llms-full.txt · openapi.json.
ChatGPT
ChatGPT on the web: Plugins → + → Add custom MCP server. Paste the URL, choose OAuth, create, then sign in to CarrierVet when asked. Availability depends on your ChatGPT plan and workspace settings.
Server URL: https://carriervet.dev/mcp
Authentication: OAuth
Claude
claude.ai or Claude Desktop: Settings → Connectors → Add custom connector. Paste the URL, then sign in to CarrierVet when asked. (Free Claude plans allow one custom connector.)
https://carriervet.dev/mcp
Claude Code
Run in a terminal, then type /mcp in Claude Code to sign in. Or skip OAuth and send an API key with --header "Authorization: Bearer YOUR_KEY".
claude mcp add --transport http carriervet https://carriervet.dev/mcp
Cursor
Add to ~/.cursor/mcp.json (or.cursor/mcp.json in a project).
{
"mcpServers": {
"carriervet": {
"url": "https://carriervet.dev/mcp",
"headers": { "Authorization": "Bearer YOUR_KEY" }
}
}
}
Any MCP client
Streamable HTTP. Send an API key as an Authorization header (below: VS Code's.vscode/mcp.json), or leave the header out and let the client sign in with OAuth.
{
"servers": {
"carriervet": {
"type": "http",
"url": "https://carriervet.dev/mcp",
"headers": { "Authorization": "Bearer YOUR_KEY" }
}
}
}
OAuth (ChatGPT, Claude and other OAuth clients)
Clients that can't send an API key connect with OAuth 2.1: paste https://carriervet.dev/mcp, then sign in to CarrierVet (or create a free account) and allow access. The app gets the same plan, quota and rate limits as your API keys; each tool call counts exactly like the REST call it wraps. API keys keep working on /mcp as before.
| Discovery | /.well-known/oauth-protected-resource (RFC 9728) · /.well-known/oauth-authorization-server (RFC 8414). A 401 from /mcp carries WWW-Authenticate: Bearer resource_metadata="…". |
|---|---|
| Flow | Authorization code with PKCE (S256 only); state required; iss returned on every authorization response (RFC 9207); resource must be https://carriervet.dev/mcp (RFC 8707). Dynamic client registration at POST /oauth/register (RFC 7591); redirect URIs are matched exactly. |
| Tokens | Access tokens last 1 hour and work only on /mcp. Refresh tokens last 30 days and rotate on every use; reusing an old one disconnects the app. Only hashes are stored. Revoke with POST /oauth/revoke (RFC 7009). |
| Disconnect | /connected-apps (email and password), or with an API key: GET /v1/connected-apps and DELETE /v1/connected-apps/{id}. |
Example prompt: “Vet carrier MC123456 before I tender a load: is its authority active, is insurance on file, and what is its risk score and why?”
Auth, format and limits
| Auth | Authorization: Bearer <key>; keys stored as hashes |
|---|---|
| Format | JSON over HTTPS · OpenAPI 3.1 · llms.txt · /health |
| Rate limits | 30–600 requests/min by plan (pricing). 429s include Retry-After. |
| Quota | Monthly lookups, reset each UTC month |
Endpoints
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/signup | Create a free account and API key |
| POST | /v1/login | Issue a new key for an existing account |
| GET | /v1/carriers/{dot} | Verdict and carrier profile by USDOT number |
| GET | /v1/carriers/mc/{mc} | Verdict and carrier profile by MC, FF, or MX number |
| GET | /v1/carriers/{dot}/risk | Verdict and risk score 0–100 with reason codes |
| GET | /v1/carriers/mc/{mc}/risk | Risk score by MC, FF, or MX number |
| GET | /v1/carriers/{dot}/history | Field changes and dated FMCSA change history |
| GET | /v1/receipts/{id} | Saved copy of an earlier lookup response (free) |
| POST | /v1/carriers/batch | Up to 500 identifiers per call (Pro, Business) |
| GET | /v1/search?name= | Name search over the stored snapshot |
| POST | /v1/monitors | Watch a carrier; email or webhook alerts on change |
| GET | /v1/monitors | List monitored carriers |
| GET | /v1/export/monitors.csv | CSV export of monitored carriers (Business) |
| GET | /v1/usage | Lookups used this UTC month and plan limits |
| GET | /v1/account | Account, plan, webhook secret, key prefixes |
| POST | /v1/billing/checkout | Stripe Checkout session for a paid plan |
| POST | /v1/eligibility | Check a carrier against your load rules (details) |
Data returned and sources
| Authority | Active, pending, inactive, or revoked, with authority age |
|---|---|
| Insurance | BI&PD, cargo, and bond filings; flagged when missing or lapsed |
| Safety | Out-of-service orders, prior revocations, undeliverable mail |
| Changes | Recent phone, email, and address changes from the daily diff |
| Shared contacts | Phone, email, or address reused across other carriers |
| Monitoring | Email or HMAC-SHA256 signed webhook alerts on change |
| Source | Verified against official FMCSA records (carrier registration, authority, and insurance) |
| Refresh | Fetched live on first lookup; re-fetched when the stored copy is older than 24 h (10 minutes if an FMCSA source did not answer). Out-of-service orders are checked on every lookup. Daily difference files applied at 13:45 UTC. Each response includes data_quality and an x-data-freshness header. |
| Identifiers | USDOT, MC, FF, MX; name search |
Sample results
Fictional carriers from the sample dataset, to show each outcome.
| USDOT | Carrier | Verdict | Authority | Insurance | OOS | Contact | Score |
|---|---|---|---|---|---|---|---|
9900001 | Northline Freight LLC | PASS | PASS Active | PASS On file | PASS No | PASS OK | 0 low |
9900002 | Harbor Pine Logistics LLC | REVIEW | FLAG Active, 25 days | PASS On file | PASS No | PASS OK | 20 low |
9900005 | Plainfield Hauling LLC | REVIEW | PASS Active | PASS On file | PASS No | FLAG Shared phone | 18 low |
9900004 | Kite Route Carriers LLC | FAIL | PASS Active | FAIL Lapsed | PASS No | PASS OK | 50 high |
9900003 | Red Mesa Transport Inc | FAIL | FAIL Revoked | FAIL Missing | FAIL Yes | PASS OK | 100 severe |
Verdict: FAIL if any check fails · REVIEW if any check is flagged or the level is elevated or higher · PASS otherwise. Levels: low <25 · elevated 25–49 · high 50–74 · severe 75–100. A flag is a reason to review, not a finding of wrongdoing.
Reference
POST/v1/signup
Create a free account. Requires accept_terms: true, meaning you accept the Terms of Service; without it the call returns 400 terms_not_accepted. Returns the API key once. The key works for 5 lookups until you click the link emailed to you; GET /v1/verify-email?token=… confirms it and POST /v1/verify-email/resend sends a new link.
curl -s -X POST https://carriervet.dev/v1/signup \
-H 'content-type: application/json' \
-d '{"email":"desk@example.com","password":"a-long-password","accept_terms":true}'
POST/v1/login
Mint another key from email and password. The previous key cannot be retrieved; only its hash is stored. An account holds at most 10 active keys; at the cap, login revokes the oldest key a login created.
curl -s -X POST https://carriervet.dev/v1/login \
-H 'content-type: application/json' \
-d '{"email":"broker@example.com","password":"local-demo-password"}'
GET/v1/account
Account, plan, webhook signing secret, and key prefixes.
curl -s https://carriervet.dev/v1/account -H "authorization: Bearer $KEY"
POST/v1/keys
Create a named key. The secret is returned once. Maximum 10 active keys.
curl -s -X POST https://carriervet.dev/v1/keys \
-H "authorization: Bearer $KEY" -H 'content-type: application/json' \
-d '{"name":"tms-prod"}'
POST/v1/keys/{id}/rotate
Revoke the named key and return a replacement.
curl -s -X POST https://carriervet.dev/v1/keys/key_demo_business/rotate -H "authorization: Bearer $KEY"
DELETE/v1/keys/{id}
Revoke a key. The last active key cannot be revoked.
curl -s -X DELETE https://carriervet.dev/v1/keys/key_demo_business -H "authorization: Bearer $KEY"
GET/v1/carriers/{dot}
Overall verdict, then the full profile. Counts as one lookup, including a not-found response.
curl -s https://carriervet.dev/v1/carriers/53467 -H "authorization: Bearer $KEY"
Example response shape · 200 · application/json (illustrative values from a fictional sample carrier)
{
"verdict": "PASS",
"verdict_reasons": [],
"verdict_basis": ["authority", "insurance", "out_of_service", "operating_status", "safety_rating"],
"carrier": {
"dot_number": "9900001",
"legal_name": "Northline Freight LLC",
"authority": { "status": "active", "grant_date": "2014-06-01", ... },
"insurance": { "status": "on_file", ... },
"safety": { "out_of_service": false, ... },
...
},
"receipt_id": "rcpt_0123456789abcdef01234567",
"usage": { "used": 3, "limit": 50, "remaining": 47, "warning": null }
}
GET/v1/carriers/mc/{mc}
Verdict and profile by MC, FF, or MX number. Authority status, and the verdict, use the matched docket's motor-carrier authority; a broker-only or forwarder-only docket FAILs as not_a_carrier.
curl -s https://carriervet.dev/v1/carriers/mc/MC138328 -H "authorization: Bearer $KEY"
GET/v1/carriers/{dot}/risk
Overall verdict, then a score from 0 to 100 plus reason codes. The verdict here also uses the contact check and the risk level. A hard failure is listed in gates and keeps the score at 50 or more, so a carrier that fails a check is never scored low. The x-data-freshness header and snapshot_date show how current the record is. Weights live in the risk config.
curl -s https://carriervet.dev/v1/carriers/53467/risk -H "authorization: Bearer $KEY"
Example response shape · 200 · application/json (illustrative values from a fictional sample carrier)
{
"verdict": "FAIL",
"verdict_reasons": [
{ "code": "revoked_authority", "check": "authority", "result": "FAIL",
"message": "Operating authority is revoked or suspended." },
{ "code": "insurance_missing", "check": "insurance", "result": "FAIL",
"message": "Required insurance is not on file with FMCSA." },
{ "code": "out_of_service", "check": "out_of_service", "result": "FAIL",
"message": "Carrier is under an out-of-service order." },
{ "code": "risk_level_severe", "check": "risk_level", "result": "FLAG",
"message": "Risk level is severe." }
],
"verdict_basis": ["authority", "insurance", "out_of_service", "operating_status", "safety_rating", "contact", "risk_level"],
"dot_number": "9900003",
"score": 100,
"level": "severe",
"gates": ["insurance_missing", "revoked_authority", "out_of_service"],
"reasons": [
{ "code": "revoked_authority", "points": 45,
"message": "Operating authority is revoked or suspended.",
"details": { "authority_status": "revoked" } },
{ "code": "out_of_service", "points": 40,
"message": "Carrier is under an out-of-service order.",
"details": { "oos_status": "out_of_service" } },
{ "code": "insurance_missing", "points": 25,
"message": "Required insurance is not on file with FMCSA.",
"details": { "insurance_status": "missing" } }
],
"rules_version": "2026.10.27",
"snapshot_date": "2026-10-27",
"receipt_id": "rcpt_0123456789abcdef01234567",
"meta": { "disclaimer": "Carrier data is derived from records maintained by the Federal Motor Carrier Safety Administration (FMCSA). It is provided as-is, ..." }
}
GET/v1/carriers/mc/{mc}/risk
The same risk response by MC, FF, or MX number. The authority status used is that docket's own, as on the MC profile lookup.
curl -s https://carriervet.dev/v1/carriers/mc/MC138328/risk -H "authorization: Bearer $KEY"
GET/v1/carriers/{dot}/history
Two lists, newest first. changes: differences between CarrierVet's stored copies. fmcsa_history: dated changes from FMCSA's daily authority, insurance, and revocation files and from census rows with a new MCS-150 date. Each row has fmcsa_date (the date FMCSA recorded it; fmcsa_date_basis names the column), observed_at (when CarrierVet saw it), and old_value and new_value. Company officer name changes show both values as withheld. Rows are only added, never edited. Recording started on 2026-10-27, so older changes are not included.
curl -s https://carriervet.dev/v1/carriers/53467/history -H "authorization: Bearer $KEY"
GET/v1/receipts/{id}
Every charged lookup returns receipt_id (also in the x-receipt-id header). This returns the saved copy of that response: time, endpoint, identifier, the body as sent, and its SHA-256. Any API key on the account that made the lookup can read it. It appears a few seconds after the lookup. It records what CarrierVet returned then; it is not a new check. Free.
curl -s https://carriervet.dev/v1/receipts/rcpt_0123456789abcdef01234567 -H "authorization: Bearer $KEY"
POST/v1/eligibility
Check a carrier against your own load rules. Send dot or mc plus optional rules: follow_verdict (default true: FAIL → ineligible, REVIEW → needs_review), require_active_authority (default true), no_active_oos (default true), min_bipd_usd, require_cargo, max_risk_score (above → needs_review), min_authority_age_days (below → needs_review), block_reason_codes (present → ineligible), review_reason_codes (present → needs_review). Returns eligibility (eligible, needs_review, ineligible), failed_rules, each rule's result and detail, and CarrierVet's verdict and risk score unchanged. All plans. One lookup. MCP tool: check_load_eligibility. See workflow examples.
curl -s -X POST https://carriervet.dev/v1/eligibility \
-H "authorization: Bearer $KEY" -H "content-type: application/json" \
-d '{"dot":"53467","rules":{"min_bipd_usd":1000000,"max_risk_score":40,"min_authority_age_days":180}}'
POST/v1/carriers/batch
Up to 500 DOT or MC numbers. Pro and Business. Duplicate ids are billed once. Carriers not stored yet are fetched from FMCSA (a few at a time, up to about 25 seconds per call). Each result has status: ok (with verdict, verdict_reasons, carrier, freshness), not_found, or not_checked (FMCSA slow or the time budget ran out; retry it). Only ok and not_found are charged; summary gives the counts.
curl -s -X POST https://carriervet.dev/v1/carriers/batch \
-H "authorization: Bearer $KEY" -H 'content-type: application/json' \
-d '{"identifiers":["53467","MC133655"]}'
GET/v1/search?name=
Name search over the indexed snapshot. Minimum 2 characters.
curl -s "https://carriervet.dev/v1/search?name=Werner" -H "authorization: Bearer $KEY"
POST/v1/monitors
Watch a carrier. Free cannot monitor. Starter is email only. Pro and Business can set a webhook.
curl -s -X POST https://carriervet.dev/v1/monitors \
-H "authorization: Bearer $KEY" -H 'content-type: application/json' \
-d '{"dot_number":"53467","alert_email":"ops@example.com","webhook_url":"https://example.com/hooks/carriervet"}'
GET/v1/monitors
List monitors for the calling account.
curl -s https://carriervet.dev/v1/monitors -H "authorization: Bearer $KEY"
DELETE/v1/monitors/{dot}
Stop monitoring a USDOT number.
curl -s -X DELETE https://carriervet.dev/v1/monitors/53467 -H "authorization: Bearer $KEY"
GET/v1/export/monitors.csv
Business plan. One row per monitored carrier, plus a final disclaimer column.
curl -s https://carriervet.dev/v1/export/monitors.csv -H "authorization: Bearer $KEY"
GET/v1/usage
Lookups used this UTC month, monitor count, and plan limits.
curl -s https://carriervet.dev/v1/usage -H "authorization: Bearer $KEY"
POST/v1/billing/checkout
Paid plans open after beta. Prices: Starter $49/mo, Pro $149/mo, Business $399/mo; yearly is 10× monthly. Until then this returns 403 paid_signups_paused. The free plan (50 lookups/month, no card) is available now through POST /v1/signup.
curl -s -X POST https://carriervet.dev/v1/billing/checkout \
-H "authorization: Bearer $KEY" -H 'content-type: application/json' \
-d '{"plan":"starter","interval":"month"}'
POST/v1/billing/portal
Stripe Customer Portal: switch plan or billing interval (prorated) or cancel at the end of the period. Requires a completed checkout. Checkout returns 409 already_subscribed if the account already has a plan.
curl -s -X POST https://carriervet.dev/v1/billing/portal -H "authorization: Bearer $KEY"
POST/v1/stripe/webhook
Stripe subscription webhook. Rejects requests without a valid Stripe-Signature.
curl -s -X POST https://carriervet.dev/v1/stripe/webhook \
-H 'content-type: application/json' \
-d '{"id":"evt_demo","type":"checkout.session.completed"}'
Webhook signatures
Alert posts include X-CarrierVet-Signature: t=<unix>,v1=<hex>. The hex is HMAC-SHA256 of t + "." + rawBody using the account webhook secret from GET /v1/account.
Alert payloads (carrier.changed) include a disclaimer field, alert emails end with the same notice, and CSV exports carry it in a final disclaimer column: informational only, not a guarantee, derived from FMCSA records and provided as-is.
Carrier corrections
Carriers who think a record or flag is wrong can request a review by email. See /corrections.
Verdict
Carrier profile, risk, and batch responses start with verdict: PASS, REVIEW, or FAIL. Existing fields are unchanged.
FAILif any check is FAIL: motor-carrier authority revoked, inactive, or none; a broker-only, freight-forwarder-only, or intrastate-only entity (not_a_carrier,intrastate_only); insurance missing or lapsed; an out-of-service order; an inactive USDOT number or not allowed to operate; or an Unsatisfactory safety rating.REVIEWif any check is FLAG: authority pending or under 90 days old (counted from the start of the current authority period; administrative record corrections do not reset it), insurance status unknown, a Conditional safety rating, or incomplete or old data (data_incomplete: an FMCSA source did not answer for this lookup;data_stale: an older stored copy was served because FMCSA did not answer a refresh in time). On/v1/carriers/{dot}/risk, also a recent or shared phone, email, or address, or a risk level of elevated, high, or severe.PASSotherwise.
verdict_reasons lists {"code","check","result","message"}, FAIL reasons first, and is empty on PASS. Codes match the risk reason codes where one exists, plus no_authority, not_a_carrier, intrastate_only, authority_unconfirmed (authority shown as active, but no authority record on file and SAFER does not show it as authorized; REVIEW, confirm before tendering), insurance_unknown, data_incomplete, data_stale, and risk_level_elevated / _high / _severe. verdict_basis lists the checks used: profile and batch lookups use authority, insurance, out_of_service, operating_status, safety_rating (plus data_quality when a source did not answer); the risk endpoint adds contact and risk_level. Profile responses also include checks (PASS/FLAG/FAIL per check). The verdict is informational and not a guarantee.
Entity type and data quality
carrier.entity_type is motor_carrier, broker_only, freight_forwarder_only, intrastate_only, no_carrier_authority, or unknown. authority.status is motor-carrier authority only; broker and freight-forwarder authority are in authority.broker_status and authority.freight_forwarder_status. authority.grant_date starts the current authority period and authority.original_grant_date is the first grant on record.
carrier.data_quality has completeness (complete or partial), missing (checks a failed source would have backed), and the status of each FMCSA source for this copy. A partial copy is rechecked after 10 minutes and is never labeled fresh or live.
Risk reason codes
authority_under_90_days, recent_address_change, recent_phone_change, recent_email_change, multiple_contact_changes, shared_phone, shared_email, shared_address, insurance_lapsed, insurance_missing, revoked_authority, inactive_authority, pending_authority, no_authority, out_of_service, usdot_inactive, not_allowed_to_operate, unsatisfactory_rating, conditional_rating, prior_revocation, undeliverable_mail.
Hard failures (revoked_authority, inactive_authority, no_authority, out_of_service, insurance_lapsed, insurance_missing, usdot_inactive, not_allowed_to_operate, unsatisfactory_rating) are listed in gates and keep the score at 50 or more. Placeholder phone numbers such as 000-000-0000 are ignored for shared-phone checks.
Set RISK_CONFIG_JSON to override points, windows, and shared-contact thresholds. Unknown JSON is ignored and the built-in config is used. With an override, rules_version ends in +custom.<hash>, and shared-contact thresholds are at least 1.
Errors and rate limits
Every error is JSON: {"error":{"code","message","docs_url","retry_after"}}. retry_after (seconds) and the Retry-After header are set on 429, 202, and 503. Wait that long before retrying.
400 invalid_request · 400 terms_not_accepted · 401 unauthorized · 402 quota_exceeded · 403 plan_forbidden · 403 turnstile_failed · 404 not_found · 403 email_unverified (confirm your email to go past 5 lookups) · 409 email_taken · 409 key_limit · 409 already_subscribed (use the billing portal to switch plans) · 429 rate_limited · 202 fetching_from_fmcsa (new carrier being fetched; retry, not charged) · 503 fmcsa_unavailable / fmcsa_throttled (FMCSA down and carrier not cached; not charged) · 403 paid_signups_paused (paid plans open after beta) · 500 internal.
Limits: per-account requests per minute by plan, per-IP limits on signup, login, and public pages, and 3 tokenless API signups per IP per hour. Cached carriers keep working when FMCSA is down; the x-data-freshness header says whether a profile is fresh (stored, under a day old, complete), live (just fetched, complete), live-partial or cached-partial (an FMCSA source did not answer), stale-revalidating, or stale-upstream-down.
AI agents and MCP
Plain-language guides: /llms.txt, /llms-full.txt. OpenAPI: /openapi.json. MCP server (Streamable HTTP): https://carriervet.dev/mcp with Authorization: Bearer <key>.