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="…".
FlowAuthorization 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.
TokensAccess 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

AuthAuthorization: Bearer <key>; keys stored as hashes
FormatJSON over HTTPS · OpenAPI 3.1 · llms.txt · /health
Rate limits30–600 requests/min by plan (pricing). 429s include Retry-After.
QuotaMonthly lookups, reset each UTC month

Endpoints

MethodPathPurpose
POST/v1/signupCreate a free account and API key
POST/v1/loginIssue 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}/riskVerdict and risk score 0–100 with reason codes
GET/v1/carriers/mc/{mc}/riskRisk score by MC, FF, or MX number
GET/v1/carriers/{dot}/historyField changes and dated FMCSA change history
GET/v1/receipts/{id}Saved copy of an earlier lookup response (free)
POST/v1/carriers/batchUp to 500 identifiers per call (Pro, Business)
GET/v1/search?name=Name search over the stored snapshot
POST/v1/monitorsWatch a carrier; email or webhook alerts on change
GET/v1/monitorsList monitored carriers
GET/v1/export/monitors.csvCSV export of monitored carriers (Business)
GET/v1/usageLookups used this UTC month and plan limits
GET/v1/accountAccount, plan, webhook secret, key prefixes
POST/v1/billing/checkoutStripe Checkout session for a paid plan
POST/v1/eligibilityCheck a carrier against your load rules (details)

Data returned and sources

AuthorityActive, pending, inactive, or revoked, with authority age
InsuranceBI&PD, cargo, and bond filings; flagged when missing or lapsed
SafetyOut-of-service orders, prior revocations, undeliverable mail
ChangesRecent phone, email, and address changes from the daily diff
Shared contactsPhone, email, or address reused across other carriers
MonitoringEmail or HMAC-SHA256 signed webhook alerts on change
SourceVerified against official FMCSA records (carrier registration, authority, and insurance)
RefreshFetched 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.
IdentifiersUSDOT, MC, FF, MX; name search

Sample results

Fictional carriers from the sample dataset, to show each outcome.

USDOTCarrierVerdictAuthorityInsuranceOOSContactScore
9900001Northline Freight LLCPASSPASS ActivePASS On filePASS NoPASS OK0 low
9900002Harbor Pine Logistics LLCREVIEWFLAG Active, 25 daysPASS On filePASS NoPASS OK20 low
9900005Plainfield Hauling LLCREVIEWPASS ActivePASS On filePASS NoFLAG Shared phone18 low
9900004Kite Route Carriers LLCFAILPASS ActiveFAIL LapsedPASS NoPASS OK50 high
9900003Red Mesa Transport IncFAILFAIL RevokedFAIL MissingFAIL YesPASS OK100 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.

  • FAIL if 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.
  • REVIEW if 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.
  • PASS otherwise.

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>.