PageSourceSearch

https://longmile.io/assets/docs-Dm_BmrKm.js

js longmile.io collected 2026-10-07 17:42:55 UTC 97,651 bytes, 477 lines download raw bytes

1import{K as v,ao as O,Z as b,U as e,X as I,L as _,p as M,y as f,C as P}from"./index-BMeCVODT.js";import{B as q}from"./badge-Ci-u1EfO.js";import{B as U}from"./button-CObxkfXi.js";import{T as D}from"./CarrierProfileRedesign-A32vVTvy.js";import{M as F}from"./menu-CipKeDj_.js";import{E}from"./external-link-qNYYdTFb.js";import{S as G}from"./sparkles-B8lvt0Cs.js";import{C as z}from"./chevron-right-DYcYSl0z.js";import{C as B}from"./copy-hbh-gSVE.js";import"./crash-policy-DRWmjOrQ.js";import"./checkbox-Wo0Evv-p.js";import"./index-DrIHrbPl.js";import"./dialog-qxcHA_5o.js";import"./index-BoB__lYu.js";import"./index-CRT7k_MH.js";import"./tslib.es6-BH4kZpVT.js";import"./input-Dgdsyybk.js";import"./label-B3y2N9vq.js";import"./select-xICMCiu-.js";import"./index-CPPAh3dI.js";import"./chevron-up-C29QzkhG.js";import"./switch-B24miZUc.js";import"./textarea-C8ozUCwZ.js";import"./shield-check-BysA--2W.js";import"./accordion-DylJ7RqZ.js";import"./card-glKQFS3l.js";import"./dropdown-menu-CtuviH33.js";import"./index-DBP4Qj3v.js";import"./company-type-DiM6EdlD.js";import"./credit-card-DUaOwYX3.js";import"./settings-Ca1kXMZ8.js";import"./download-BPdoprpx.js";import"./circle-alert-DSOyXjU3.js";import"./circle-check-CpiowtTv.js";import"./community-BuuvI_e1.js";import"./lock-CCkGkVTM.js";import"./users-CIlEuXLD.js";import"./clock-DLsuFlZA.js";import"./truck-Ce-RRimW.js";import"./bell-BLkRrzc-.js";import"./mail-szqV75ij.js";import"./circle-x-D189CN3w.js";import"./calendar-clock-CBz7cyC5.js";/**
2 * @license lucide-react v0.453.0 - ISC
3 *
4 * This source code is licensed under the ISC license.
5 * See the LICENSE file in the root directory of this source tree.
6 */const H=v("CodeXml",[["path",{d:"m18 16 4-4-4-4",key:"1inbqp"}],["path",{d:"m6 8-4 4 4 4",key:"15zrgr"}],["path",{d:"m14.5 4-5 16",key:"e7oirm"}]]);/**
7 * @license lucide-react v0.453.0 - ISC
8 *
9 * This source code is licensed under the ISC license.
10 * See the LICENSE file in the root directory of this source tree.
11 */const W=v("Hash",[["line",{x1:"4",x2:"20",y1:"9",y2:"9",key:"4lhtct"}],["line",{x1:"4",x2:"20",y1:"15",y2:"15",key:"vyu0kd"}],["line",{x1:"10",x2:"8",y1:"3",y2:"21",key:"1ggp8o"}],["line",{x1:"16",x2:"14",y1:"3",y2:"21",key:"weycgp"}]]);/**
12 * @license lucide-react v0.453.0 - ISC
13 *
14 * This source code is licensed under the ISC license.
15 * See the LICENSE file in the root directory of this source tree.
16 */const S=v("Shield",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]]);/**
17 * @license lucide-react v0.453.0 - ISC
18 *
19 * This source code is licensed under the ISC license.
20 * See the LICENSE file in the root directory of this source tree.
21 */const K=v("Zap",[["path",{d:"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z",key:"1xq2db"}]]),C=[{title:"Get started",links:[{id:"introduction",title:"Introduction"},{id:"authentication",title:"Authentication"},{id:"rate-limits",title:"Rate limits"},{id:"errors",title:"Errors"}]},{title:"OAuth 2.1",links:[{id:"oauth-overview",title:"How it works"},{id:"oauth-apps",title:"Create a developer app"},{id:"oauth-account-tokens",title:"Tokens from your account"},{id:"oauth-code-flow",title:"Authorization code + PKCE"},{id:"oauth-refresh",title:"Refresh tokens"},{id:"oauth-scopes",title:"Scopes"},{id:"oauth-revoke",title:"Revoke & disconnect"},{id:"oauth-discovery",title:"Discovery & registration"},{id:"oauth-errors",title:"OAuth errors"}]},{title:"REST API",links:[{id:"endpoint-entity",title:"Get entity profile"},{id:"reading-a-profile",title:"Reading a profile"},{id:"endpoint-search",title:"Search carriers"},{id:"endpoint-me",title:"Token owner & quota"},{id:"endpoint-partner",title:"Account endpoints"},{id:"endpoint-methodology",title:"Methodology"},{id:"endpoint-health",title:"Health check"}]},{title:"MCP server",links:[{id:"mcp-overview",title:"Overview"},{id:"mcp-tools",title:"Tools"},{id:"mcp-oauth",title:"Connect with OAuth"},{id:"mcp-setup",title:"Add to a client"}]},{title:"Reference",links:[{id:"openapi",title:"OpenAPI spec"},{id:"support",title:"Support"}]}],$="curl https://longmile.io/api/entity/3600129",T=`curl https://longmile.io/api/entity/3600129 \\
22  -H "Authorization: Bearer $LONGMILE_TOKEN"`,X='curl "https://longmile.io/api/entity/search?q=Swift&limit=5"',Y='curl "https://longmile.io/api/entity/3600129?raw=1"',V="curl https://longmile.io/api/methodology",J="
22curl https://longmile.io/api/health",Z=`const res = await fetch("https://longmile.io/api/entity/3600129");
23const profile = await res.json();`,Q='const res = await fetch("https://longmile.io/api/entity/3600129", {\n  headers: { Authorization: `Bearer ${process.env.LONGMILE_TOKEN}` },\n});\nconst profile = await res.json();',ee=`import requests
24
25profile = requests.get(
26    "https://longmile.io/api/entity/3600129",
27    timeout=15,
28).json()`,te=`import os, requests
29
30r = requests.get(
31    "https://longmile.io/api/entity/3600129",
32    headers={"Authorization": f"Bearer {os.environ['LONGMILE_TOKEN']}"},
33    timeout=15,
34)
35profile = r.json()`,re=`{
36  "notice": "Informational only. … Not legal advice, not a consumer report, and not a recommendation to transact with any party.",
37  "as_of": "2026-09-04T11:00:00.000Z",
38  "sources": [
39    {
40      "source": "FMCSA Motus",
41      "status": "live",
42      "checked_at": "2026-09-04T11:00:00.000Z",
43      "detail": "3 insurance filing rows available; active BMC-91X found"
44    }
45  ],
46  "method": {
47    "scoring_edition": 13,
48    "current_scoring_edition": 13,
49    "score_generation": "v1",
50    "identity_thresholds": { "review": 40, "high": 100 },
51    "crash_window_months": 24
52  },
53  "profile": {
54    "legal_name": "ACME TRUCKING LLC",
55    "dba_name": null,
56    "usdot": "3600129",
57    "mc": "123456",
58    "entity_type": "carrier",
59    "registered_as": ["Carrier"],
60    "also_broker": false,
61    "also_carrier": false,
62    "officer": "JOHN DOE",
63    "phone": "2145550100",
64    "email": null,
65    "address": "123 Main St, Dallas, TX 75201"
66  },
67  "decision": {
68    "recommendation": "Cleared — no disqualifying findings in the records screened",
69    "score": 87,
70    "band": "Excellent",
71    "display_score": { "value": 87, "source": "legacy", "withheld": null },
72    "meets_federal_requirements": true,
73    "federal_disqualifiers": [],
74    "bookable": true,
75    "gated": false,
76    "blocked_only_by_unverifiable": false,
77    "unverified_checks": [],
78    "gate_reason": null,
79    "gates": [
80      { "key": "authority", "status": "pass", "detail": "Active authority on file.", "code": "authority_active" },
81      { "key": "insurance", "status": "pass", "detail": "BMC-91X in force.", "code": "insurance_on_file" }
82    ],
83    "hard_stops": [],
84    "review_flags": []
85  },
86  "risk": {
87    "score": 78,
88    "band": "Average",
89    "method_version": 2,
90    "statement": "Scores better than 62% of carriers with 1–6 power units.",
91    "confidence": "medium",
92    "measured_share": 0.71,
93    "percentile": 62,
94    "peer_group": "1–6 power units",
95    "signals": [
96      { "key": "inspection_oos", "state": "measured", "value": 74, "weight": 0.25, "detail": "Vehicle and driver OOS below the national average." }
97    ],
98    "patterns": []
99  },
100  "authority": {
101    "status": "A",
102    "active": true,
103    "out_of_service": false,
104    "out_of_service_date": null,
105    "state": "active",
106    "state_label": "Active interstate authority",
107    "granted": { "common": true, "contract": false, "broker": false },
108    "granted_on": "2019-07-02",
109    "carrier_operation": ["Interstate"],
110    "adverse": false
111  },
112  "insurance": {
113    "bmc91x": true,
114    "status": "Active",
115    "insurer": "PROGRESSIVE INSURANCE",
116    "coverage": "$1,000,000",
117    "required": "$750,000",
118    "adequate": true,
119    "state": "in_force",
120    "verification": "motus_rest",
121    "decisive": true,
122    "filing_date": "2021-03-15",
123    "filings": [
124      {
125        "form": "BMC-91X",
126        "covers": "Public liability",
127        "insurer": "PROGRESSIVE INSURANCE",
128        "status": "Active",
129        "policy_number": "MCP-88213",
130        "coverage": "1000000",
131        "effective_date": "2021-03-15",
132        "received_date": "2021-03-17",
133        "cancellation_date": null,
134        "in_force": true
135      }
136    ]
137  },
138  "safety": {
139    "rating": "Satisfactory",
140    "score": 90,
141    "band": "Excellent",
142    "oos_hint": "normal",
143    "inspections": {
144      "total": 19,
145      "verdict": "Pass",
146      "verdict_detail": "Both categories screen at or below the national average.",
147      "vehicle": {
148        "inspections": 12,
149        "oos_rate": 16.7,
150        "adjusted_oos_rate": 18.4,
151        "national_average": 22.26,
152        "delta_from_average": -5.56,
153        "comparison": "better",
154        "status": "pass",
155        "tier": "pass"
156      },
157      "driver": {
158        "inspections": 7,
159        "oos_rate": 14.3,
160        "adjusted_oos_rate": 9.1,
161        "national_average": 6.67,
162        "delta_from_average": 7.63,
163        "comparison": "worse",
164        "status": "watch",
165        "tier": "review"
166      },
167      "basic_alerts": ["Vehicle Maintenance"],
168      "basics": { "published": 2, "total": 7, "areas": [] }
169    },
170    "crashes": { "window_months": 24, "in_window": 0, "all_time": 1, "fatal_in_window": 0, "injury_in_window": 0 }
171  },
172  "operations": {
173    "fleet_size": 12,
174    "power_units": 12,
175    "drivers": 15,
176    "annual_mileage": 500000,
177    "years_active": 5,
178    "registered_on": "2019-06-01",
179    "mcs150_date": "2025-01-10",
180    "cargo_types": ["General Freight"],
181    "hauls_motor_vehicles": false,
182    "hazmat": false
183  },
184  "identity_risk": {
185    "verdict": "clean",
186    "verdict_label": "Clear",
187    "verdict_statement": "No shared-identity pattern found in the records screened.",
188    "score": 30,
189    "blocking": false,
190    "shared_contact_groups": 1,
191    "shared_contacts": [
192      { "contact_type": "officer", "matching": [{ "usdot": "2811004", "legal_name": "SOLIS LOGISTICS LLC", "operating_status": "A" }] }
193    ],
194    "warning_flags": [],
195    "findings": [
196      {
197        "code": "shared_officer_active",
198        "points": 30,
199        "summary": "Company officer also listed on 2 companies that are still operating.",
200        "related_usdots": ["2811004", "3140922"]
201      }
202    ],
203    "notes": ["Officer links to operating entities are capped and cannot reach the review threshold alone."],
204    "linked_entities": [
205      {
206        "usdot": "2811004",
207        "legal_name": "SOLIS LOGISTICS LLC",
208        "standing": "active",
209        "status_detail": "Active interstate authority",
210        "linked_by": ["officer"],
211        "ceased_at": null,
212        "registered_at": "2018-03-01",
213        "self_affiliate": false
214      }
215    ],
216    "federal_excluded": false,
217    "federal_exclusion_checked": true,
218    "screen_unavailable": false
219  },
220  "timeline": [
221    { "date": "2021-03-15", "kind": "insurance", "title": "BMC-91X · Public liability", "detail": "PROGRESSIVE INSURANCE · $1,000,000" },
222    { "date": "2019-06-01", "kind": "registration", "title": "USDOT number issued", "detail": "FMCSA registration for DOT 3600129." }
223  ]
224}`,se=`[
225  {
226    "usdot": "3600129",
227    "legal_name": "ACME TRUCKING LLC",
228    "dba_name": "",
229    "physical_address": "123 MAIN ST",
230    "city": "DALLAS",
231    "state": "TX",
232    "status": "A"
233  },
234  {
235    "usdot": "3600130",
236    "legal_name": "BLUE RIDGE HAULERS LLC",
237    "dba_name": "BLUE RIDGE",
238    "physical_address": "40 GAP RD",
239    "city": "ASHEVILLE",
240    "state": "NC",
241    "status": "A"
242  }
243]`,ne=`{
244  "status": "ok"
245}`,oe=`{
246  "access_token": "lm_at_0H7m…",
247  "token_type": "Bearer",
248  "expires_in": 3600,
249  "refresh_token": "lm_rt_Qy2c…",
250  "scope": "carrier:read mcp"
251}`,ie=`curl -X POST https://longmile.io/oauth/token \\
252  -u "$LONGMILE_CLIENT_ID:$LONGMILE_CLIENT_SECRET" \\
253  -d grant_type=refresh_token \\
254  -d refresh_token="$LONGMILE_REFRESH_TOKEN"`,ae=`curl -X POST https://longmile.io/oauth/token \\
255  -d grant_type=refresh_token \\
256  -d client_id="$LONGMILE_CLIENT_ID" \\
257  -d refresh_token="$LONGMILE_REFRESH_TOKEN"`,ce=`// longmile-token.js — always returns a valid access token.
258// Seed longmile-tokens.json once with the refresh token from
259// Account → Connections → Developer apps → Generate tokens:
260//   { "refresh_token": "lm_rt_…", "access_token": null, "expires_at": 0 }
261import fs from "node:fs/promises";
262
263const TOKEN_URL = "https://longmile.io/oauth/token";
264const STORE = "./longmile-tokens.json";
265
266export async function getAccessToken() {
267  const saved = JSON.parse(await fs.readFile(STORE, "utf8"));
268  // Reuse the current token until one minute before it expires.
269  if (saved.access_token && Date.now() < saved.expires_at - 60_000) {
270    return saved.access_token;
271  }
272
273  const res = await fetch(TOKEN_URL, {
274    method: "POST",
275    headers: {
276      "Content-Type": "application/x-www-form-urlencoded",
277      // Server-side app: authenticate with client ID + secret.
278      // Public app: drop this header and add client_id to the body instead.
279      Authorization:
280        "Basic " +
281        Buffer.from(
282          \`\${process.env.LONGMILE_CLIENT_ID}:\${process.env.LONGMILE_CLIENT_SECRET}\`,
283        ).toString("base64"),
284    },
285    body: new URLSearchParams({
286      grant_type: "refresh_token",
287      refresh_token: saved.refresh_token,
288    }),
289  });
290  if (!res.ok) throw new Error(\`LongMile refresh failed: \${res.status} \${await res.text()}\`);
291
292  const t = await res.json();
293  // The refresh token you just used is now dead. Save the new one BEFORE
294  // doing anything else, or the next refresh will fail with invali
294d_grant.
295  await fs.writeFile(STORE, JSON.stringify({
296    access_token: t.access_token,
297    refresh_token: t.refresh_token,
298    expires_at: Date.now() + t.expires_in * 1000,
299  }));
300  return t.access_token;
301}
302
303// Usage
304const token = await getAccessToken();
305const profile = await fetch("https://longmile.io/api/entity/3600129", {
306  headers: { Authorization: \`Bearer \${token}\` },
307}).then((r) => r.json());`,de=`# longmile_token.py — always returns a valid access token.
308# Seed longmile-tokens.json once with the refresh token from
309# Account → Connections → Developer apps → Generate tokens:
310#   {"refresh_token": "lm_rt_…", "access_token": null, "expires_at": 0}
311import json, os, time, requests
312
313TOKEN_URL = "https://longmile.io/oauth/token"
314STORE = "longmile-tokens.json"
315
316def get_access_token() -> str:
317    with open(STORE) as f:
318        saved = json.load(f)
319    # Reuse the current token until one minute before it expires.
320    if saved.get("access_token") and time.time() < saved["expires_at"] - 60:
321        return saved["access_token"]
322
323    r = requests.post(
324        TOKEN_URL,
325        # Server-side app: client ID + secret. Public app: remove auth= and
326        # add "client_id" to data instead.
327        auth=(os.environ["LONGMILE_CLIENT_ID"], os.environ["LONGMILE_CLIENT_SECRET"]),
328        data={"grant_type": "refresh_token", "refresh_token": saved["refresh_token"]},
329        timeout=15,
330    )
331    r.raise_for_status()
332    t = r.json()
333    # The refresh token you just used is now dead. Save the new one first.
334    with open(STORE, "w") as f:
335        json.dump({
336            "access_token": t["access_token"],
337            "refresh_token": t["refresh_token"],
338            "expires_at": time.time() + t["expires_in"],
339        }, f)
340    return t["access_token"]
341
342# Usage
343profile = requests.get(
344    "https://longmile.io/api/entity/3600129",
345    headers={"Authorization": f"Bearer {get_access_token()}"},
346    timeout=15,
347).json()`,le=`https://longmile.io/oauth/authorize
348  ?response_type=code
349  &client_id=lm_client_8oQ4dmoZ0_MLM3ZpEY41HA
350  &redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback
351  &scope=carrier%3Aread
352  &state=Zk3q9…            # random, check it on the way back
353  &code_challenge=E9Mel…   # BASE64URL(SHA256(code_verifier))
354  &code_challenge_method=S256`,he=`// Minimal Express integration: "Connect LongMile" for your own users.
355import crypto from "node:crypto";
356import express from "express";
357
358const LM = "https://longmile.io";
359const CLIENT_ID = process.env.LONGMILE_CLIENT_ID;
360const CLIENT_SECRET = process.env.LONGMILE_CLIENT_SECRET; // server-side apps only
361const REDIRECT_URI = "https://app.example.com/oauth/callback"; // registered on the app
362
363const app = express();
364app.use(yourSessionMiddleware); // somewhere to keep state + verifier per user
365
366// 1. Send the user to LongMile.
367app.get("/connect/longmile", (req, res) => {
368  const verifier = crypto.randomBytes(32).toString("base64url");
369  const challenge = crypto.createHash("sha256").update(verifier).digest("base64url");
370  const state = crypto.randomBytes(16).toString("base64url");
371  req.session.longmile = { verifier, state };
372
373  const url = new URL(\`\${LM}/oauth/authorize\`);
374  url.search = new URLSearchParams({
375    response_type: "code",
376    client_id: CLIENT_ID,
377    redirect_uri: REDIRECT_URI,
378    scope: "carrier:read",
379    state,
380    code_challenge: challenge,
381    code_challenge_method: "S256",
382  }).toString();
383  res.redirect(url.toString());
384});
385
386// 2. LongMile sends them back with ?code=… (or ?error=…).
387app.get("/oauth/callback", async (req, res) => {
388  const pending = req.session.longmile;
389  if (!pending || req.query.state !== pending.state) return res.status(400).send("Bad state");
390  if (req.query.error) return res.status(400).send(\`LongMile said: \${req.query.error_description}\`);
391
392  // 3. Exchange the code within 5 minutes. It works exactly once.
393  const tokenRes = await fetch(\`\${LM}/oauth/token\`, {
394    method: "POST",
395    headers: {
396      "Content-Type": "application/x-www-form-urlencoded",
397      Authorization: "Basic " + Buffer.from(\`\${CLIENT_ID}
397:\${CLIENT_SECRET}\`).toString("base64"),
398    },
399    body: new URLSearchParams({
400      grant_type: "authorization_code",
401      code: String(req.query.code),
402      redirect_uri: REDIRECT_URI,
403      code_verifier: pending.verifier,
404    }),
405  });
406  const tokens = await tokenRes.json();
407  if (!tokenRes.ok) return res.status(400).send(tokens.error_description);
408
409  // 4. Store tokens.refresh_token (encrypted) against this user of YOUR app.
410  await saveLongMileTokens(req.user.id, tokens);
411  res.redirect("/settings/integrations?connected=longmile");
412});`,ue=`curl -X POST https://longmile.io/oauth/token \\
413  -u "$LONGMILE_CLIENT_ID:$LONGMILE_CLIENT_SECRET" \\
414  -d grant_type=authorization_code \\
415  -d code="lm_code_…" \\
416  -d redirect_uri="https://app.example.com/oauth/callback" \\
417  -d code_verifier="$CODE_VERIFIER"`,pe=`curl -X POST https://longmile.io/oauth/revoke \\
418  -d token="lm_rt_…"      # an access OR a refresh token`,me=`{
419  "issuer": "https://longmile.io",
420  "authorization_endpoint": "https://longmile.io/oauth/authorize",
421  "token_endpoint": "https://longmile.io/oauth/token",
422  "registration_endpoint": "https://longmile.io/oauth/register",
423  "revocation_endpoint": "https://longmile.io/oauth/revoke",
424  "scopes_supported": ["openid", "profile", "email", "mcp", "carrier:read", "risk-acceptance:write"],
425  "response_types_supported": ["code"],
426  "response_modes_supported": ["query"],
427  "grant_types_supported": ["authorization_code", "refresh_token"],
428  "token_endpoint_auth_methods_supported": ["none", "client_secret_post", "client_secret_basic"],
429  "revocation_endpoint_auth_methods_supported": ["none", "client_secret_post", "client_secret_basic"],
430  "code_challenge_methods_supported": ["S256"],
431  "service_documentation": "https://longmile.io/docs"
432}`,xe=`curl -X POST https://longmile.io/oauth/register \\
433  -H "Content-Type: application/json" \\
434  -d '{
435    "client_name": "Acme TMS",
436    "redirect_uris": ["https://app.example.com/oauth/callback"],
437    "token_endpoint_auth_method": "client_secret_basic"
438  }'`,ge=`{
439  "client_id": "lm_client_V2m…",
440  "client_id_issued_at": 1790035200,
441  "redirect_uris": ["https://app.example.com/oauth/callback"],
442  "grant_types": ["authorization_code", "refresh_token"],
443  "response_types": ["code"],
444  "token_endpoint_auth_method": "client_secret_basic",
445  "scope": "mcp carrier:read",
446  "client_name": "Acme TMS",
447  "client_secret": "4qR…",
448  "client_secret_expires_at": 0,
449  "registration_access_token": "lm_reg_…",
450  "registration_client_uri": "https://longmile.io/oauth/register/lm_client_V2m…"
451}`,A=`curl https://longmile.io/api/me \\
452  -H "Authorization: Bearer $LONGMILE_TOKEN"`,je=`{
453  "account": {
454    "email": "[email protected]",
455    "name": "Dana Ruiz",
456    "role": "user",
457    "team_owner_email": null
458  },
459  "plan": { "name": "Starter", "slug": "starter" },
460  "quota": {
461    "plan": "Starter",
462    "used": 41,
463    "limit": 500,
464    "remaining": 459,
465    "unlimited": false,
466    "window": "monthly",
467    "period_start": "2026-09-01T00:00:00.000Z",
468    "resets_at": "2026-10-01T00:00:00.000Z"
469  },
470  "scopes": ["carrier:read", "mcp"]
471}`,fe="claude mcp add --transport http longmile https://longmile.io/mcp",ye=`{
472  "mcpServers": {
473    "longmile": {
474      "url": "https://longmile.io/mcp"
475    }
476  }
477}`;function be({text:s,label:i="Copy"}){const[n,u]=b.useState(!1);return e.jsxs("button",{onClick:async()=>{try{await navigator.clipboard.writeText(s),u(!0),setTimeout(()=>u(!1),1500)}catch{}},className:"inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",type:"button","aria-label":i,children:[n?e.jsx(P,{className:"h-3.5 w-3.5 text-emerald-500"}):e.jsx(B,{className:"h-3.5 w-3.5"}),e.jsx("span",{children:n?"Copied":i})]})}function we({method:s}){const i={GET:"bg-emerald-500/12 text-emerald-600 border-emerald-500/30 dark:text-emerald-400",POST:"bg-emerald-500/12 text-emerald-700 border-emerald-500/30 dark:text-emerald-400",DELETE:"bg-rose-500/12 text-rose-600 border-rose-500/30 dark:text-rose-400",PUT:"bg-amber-500/12 text-amber-600 border-amber-500/30 dark:text-amber-400"};return e.jsx("span",{className:f("inline-flex items-center rounded-md border px-1.5 py-0.5 font-mono text-[11px] font-bold tracking-wide",i[s]??"bg-muted text-muted-foreground border-border"),children:s})}function h({tabs:s,defaultLang:i,title:n}){var l;const[u,w]=b.useState(i??((l=s[0])==null?void 0:l.lang)),a=s.find(p=>p.lang===u)??s[0];return e.jsxs("div",{className:"overflow-hidden rounded-lg border border-border bg-zinc-950 shadow-sm",children:[e.jsxs("div",{className:"flex items-center justify-between gap-2 border-b border-white/10 bg-zinc-900/80 px-3 py-1.5",children:[e.jsxs("div",{className:"flex items-center gap-1 overflow-x-auto",children:[n&&e.jsx("span",{className:"mr-2 text-[11px] uppercase tracking-wider text-zinc-500",children:n}),s.map(p=>e.jsx("button",{type:"button",onClick:()=>w(p.lang),className:f("rounded px-2 py-1 text-xs font-medium transition-colors",p.lang===u?"bg-white/10 text-zinc-50":"text-zinc-400 hover:text-zinc-100"),children:p.label},p.lang))]}),e.jsx(be,{text:a.code})]}),e.jsx("pre",{className:"overflow-x-auto p-4 text-xs leading-relaxed text-zinc-100",children:e.jsx("code",{children:a.code})})]})}function c({name:s,type:i,required:n=!1,children:u}){return e.jsxs("div",{className:"border-b border-border/60 py-3 last:border-b-0",children:[e.jsxs("div",{className:"flex flex-wrap items-baseline gap-2",children:[e.jsx("code",{className:"rounded bg-muted px-1.5 py-0.5 font-mono text-xs font-semibold text-primary",children:s}),e.jsx("span",{className:"font-mono text-xs text-muted-foreground",children:i}),n&&e.jsx("span",{className:"rounded-full border border-rose-500/30 bg-rose-500/10 px-1.5 py-0 text-[10px] font-semibold uppercase tracking-wide text-rose-600 dar
477k:text-rose-400",children:"required"})]}),e.jsx("div",{className:"mt-1.5 text-sm leading-relaxed text-muted-foreground",children:u})]})}function y({code:s,name:i,children:n}){const u=s.startsWith("2")?"text-emerald-600 dark:text-emerald-400":s.startsWith("4")?"text-amber-600 dark:text-amber-400":s.startsWith("5")?"text-rose-600 dark:text-rose-400":"text-muted-foreground";return e.jsxs("div",{className:"grid grid-cols-[72px_120px_1fr] items-baseline gap-3 border-b border-border/60 py-2.5 last:border-b-0 text-sm",children:[e.jsx("code",{className:f("font-mono text-xs font-semibold",u),children:s}),e.jsx("span",{className:"font-medium text-foreground",children:i}),e.jsx("span",{className:"text-muted-foreground",children:n})]})}function x({n:s,field:i,children:n}){return e.jsxs("li",{className:"grid grid-cols-[2rem_minmax(0,1fr)] gap-4 py-6 sm:grid-cols-[3rem_minmax(0,1fr)] sm:gap-6",children:[e.jsx("span",{className:"font-mono text-lg font-bold tabular-nums text-primary sm:text-2xl",children:s}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("code",{className:"block break-words font-mono text-[13px] font-semibold text-foreground",children:i}),e.jsx("p",{className:"mt-2 max-w-2xl text-sm leading-relaxed text-muted-foreground [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[12px] [&_code]:text-foreground",children:n})]})]})}function R({children:s}){return e.jsx("ol",{className:"divide-y divide-border border-y border-border",children:s})}function d({id:s,eyebrow:i,title:n,children:u}){return e.jsxs("section",{id:s,className:"scroll-mt-24 space-y-5 border-b border-border/60 pb-12",children:[e.jsxs("header",{className:"space-y-2",children:[i&&e.jsx("p",{className:"text-[11px] font-semibold uppercase tracking-[0.14em] text-primary",children:i}),e.jsxs("h2",{className:"group flex items-center gap-2 text-2xl font-semibold tracking-tight text-foreground",children:[n,e.jsx("a",{href:`#${s}`,className:"opacity-0 transition-opacity group-hover:opacity-60","aria-label":`Link to ${n}`,children:e.jsx(W,{className:"h-4 w-4"})})]})]}),u]})}function o({children:s}){return e.jsx("h3",{className:"text-base font-semibold text-foreground",children:s})}function r({children:s}){return e.jsx("div",{className:"space-y-3 text-[15px] leading-relaxed text-muted-foreground",children:s})}function g({tone:s="info",children:i}){const n=s==="warning"?"border-amber-500/30 bg-amber-500/8 text-amber-900 dark:text-amber-200":s==="success"?"border-emerald-500/30 bg-emerald-500/8 text-emerald-900 dark:text-emerald-200":"border-primary/30 bg-primary/10 text-foreground";return e.jsxs("div",{className:f("flex items-start gap-2.5 rounded-lg border px-3 py-2.5 text-sm leading-relaxed",n),children:[e.jsx(S,{className:"mt-0.5 h-4 w-4 shrink-0 opacity-80"}),e.jsx("div",{children:i})]})}function j({method:s,path:i,auth:n}){const u={oauth:"OAuth token required",optional:"No auth or OAuth",public:"Public · no auth","token-or-session":"OAuth token or signed-in session",any:"No auth, OAuth or session"};return e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx(we,{method:s}),e.jsx("code",{className:"rounded-md border border-border bg-muted px-2 py-1 font-mono text-sm text-foreground",children:i}),e.jsx(q,{variant:n==="oauth"||n==="token-or-session"?"warning":"secondary",className:"text-[10px]",children:u[n]})]})}function t({children:s}){return e.jsx("code",{className:"rounded bg-muted px-1 py-0.5 font-mono text-[13px] text-foreground",children:s})}function m({head:s,rows:i}){return e.jsx("div",{className:"overflow-x-auto rounded-lg border border-border/60",children:e.jsxs("table",{className:"w-full min-w-[560px] text-sm",children:[e.jsx("thead",{className:"bg-muted/40 text-left text-xs uppercase tracking-wide text-muted-foreground",children:e.jsx("tr",{children:s.map(n=>e.jsx("th",{className:"px-4 py-2 font-medium",children:n},n))})}),e.jsx("tbody",{className:"divide-y divide-border/60 align-top",children:i.map((n,u)=>e.jsx("tr",{children:n.map((w,a)=>e.jsx("td",{className:f("px-4 py-2.5",a===0?"text-foreground":"text-muted-foreground"),children:w},a))},u))})]})})}function k({children:s}){return e.jsx("ol",{className:"space-y-3",children:s.map((i,n)=>e.jsxs("li",{className:"grid grid-cols-[1.75rem_minmax(0,1fr)] gap-3 text-[15px] leading-relaxed text-muted-foreground",children:[e.jsx("span",{className:"flex h-6 w-6 items-center justify-center rounded-full border border-primary/40 bg-primary/10 font-mono text-xs font-bold text-primary",children:n+1}),e.jsx("div",{className:"min-w-0 pt-0.5",children:i})]},n))})}function _e({items:s,active:i}){return e.jsxs("div",{className:"space-y-2",children:[e.jsx("p",{className:"text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground",children:"On this page"}),e.jsx("ul",{className:"space-y-1.5 border-l border-border/60 text-sm",children:s.map(n=>e.jsx("li",{children:e.jsx("a",{href:`#${n.id}`,className:f("-ml-px block border-l border-transparent pl-3 text-muted-foreground transition-colors hover:text-foreground",i===n.id&&"border-primary text-primary"),children:n.title})},n.id))})]})}function ut(){O({title:"Docs — Carrier Vetting Guide & API | LongMile",description:"How to use LongMile: carrier and broker lookups, the 100-point risk score, monitoring, and the LongMile API and MCP integration.",canonical:"https://longmile.io/docs"});const[s,i]=b.useState(!1),n=b.useMemo(()=>C.flatMap(a=>a.links.map(l=>l.id)),[]),[u,w]=b.useState(n[0]??"");return b.useEffect(()=>{const a=new IntersectionObserver(l=>{const p=l.filter(N=>N.isIntersecting).sort((N,L)=>(N.boundingClientRect.top??0)-(L.boundingClientRect.top??0));p[0]&&w(p[0].target.id)},{rootMargin:"-100px 0px -55% 0px",threshold:[0,1]});
477return n.forEach(l=>{const p=document.getElementById(l);p&&a.observe(p)}),()=>a.disconnect()},[n]),e.jsxs("div",{className:"landing-green min-h-screen bg-background",children:[e.jsx("header",{className:"sticky top-0 z-50 border-b border-border/60 bg-background/80 backdrop-blur-xl",children:e.jsxs("div",{className:"mx-auto flex h-14 max-w-[1400px] items-center justify-between px-4 sm:px-6",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{type:"button",className:"rounded-md p-1.5 text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden",onClick:()=>i(a=>!a),"aria-label":"Toggle navigation",children:s?e.jsx(I,{className:"h-4 w-4"}):e.jsx(F,{className:"h-4 w-4"})}),e.jsxs(_,{href:"/",className:"flex items-center gap-2.5 transition-opacity hover:opacity-80",children:[e.jsx("img",{src:"/logo.png",alt:"LongMile",className:"h-7 w-auto"}),e.jsx("span",{className:"hidden text-xs text-muted-foreground/70 sm:inline",children:"/ Docs"})]})]}),e.jsx("div",{className:"hidden flex-1 max-w-md px-6 md:flex",children:e.jsxs("label",{className:"relative w-full",children:[e.jsx(M,{className:"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground"}),e.jsx("input",{type:"search",placeholder:"Search documentation…",className:"h-9 w-full rounded-md border border-border/70 bg-muted/40 pl-8 pr-12 text-sm outline-none transition-colors placeholder:text-muted-foreground focus:border-primary/50 focus:bg-background"}),e.jsx("kbd",{className:"absolute right-2.5 top-1/2 -translate-y-1/2 rounded border border-border bg-background px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground",children:"⌘K"})]})}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("a",{href:"/api/openapi.json",target:"_blank",rel:"noopener noreferrer",className:"hidden items-center gap-1.5 rounded-full border border-border/60 px-3 py-1.5 text-xs text-muted-foreground transition-colors hover:border-border hover:text-foreground sm:inline-flex",children:[e.jsx(E,{className:"h-3.5 w-3.5"}),"OpenAPI"]}),e.jsx(_,{href:"/",className:"inline-flex items-center gap-1.5 rounded-full border border-primary/30 px-3 py-1.5 text-xs font-medium text-primary transition-colors hover:bg-primary/5",children:"Try the app"}),e.jsx(D,{compact:!0})]})]})}),e.jsxs("div",{className:"mx-auto grid max-w-[1400px] grid-cols-1 gap-0 px-4 sm:px-6 lg:grid-cols-[240px_minmax(0,1fr)] xl:grid-cols-[240px_minmax(0,1fr)_240px]",children:[e.jsx("aside",{className:f("fixed inset-y-14 left-0 z-40 w-64 overflow-y-auto border-r border-border/60 bg-background px-4 py-6 transition-transform lg:sticky lg:top-14 lg:z-0 lg:max-h-[calc(100vh-3.5rem)] lg:translate-x-0 lg:border-r-0 lg:px-0 lg:pr-6",s?"translate-x-0 shadow-2xl":"-translate-x-full lg:translate-x-0"),children:e.jsx("nav",{className:"space-y-7",children:C.map(a=>e.jsxs("div",{children:[e.jsx("p",{className:"mb-2 px-2 text-[11px] font-semibold uppercase tracking-[0.14em] text-foreground/80",children:a.title}),e.jsx("ul",{className:"space-y-0.5",children:a.links.map(l=>e.jsx("li",{children:e.jsx("a",{href:`#${l.id}`,onClick:()=>i(!1),className:f("block rounded-md px-2 py-1.5 text-sm transition-colors",u===l.id?"bg-primary/10 font-medium text-primary":"text-muted-foreground hover:bg-muted hover:text-foreground"),children:l.title})},l.id))})]},a.title))})}),s&&e.jsx("div",{className:"fixed inset-0 z-30 bg-background/60 backdrop-blur-sm lg:hidden",onClick:()=>i(!1)}),e.jsxs("main",{className:"min-w-0 px-0 py-10 lg:px-10 lg:py-12",children:[e.jsxs(d,{id:"introduction",eyebrow:"Developer documentation",title:"Introduction",children:[e.jsxs(r,{children:[e.jsxs("p",{children:["The ",e.jsx("strong",{className:"text-foreground",children:"LongMile Carrier Intelligence API"})," gives developers and AI agents programmatic access to enriched FMCSA carrier and broker data. Look up any USDOT, search by name, score carriers and brokers, validate bonds, and surface chameleon patterns — all with one consistent JSON response."]}),e.jsxs("p",{children:["Start with ",e.jsx("strong",{className:"text-foreground",children:"no auth"}),": REST profile lookups and MCP tool calls work up to the same free visitor quota as the website. Add ",e.jsx("strong",{className:"text-foreground",children:"OAuth 2.1"})," ","when you want account quota, connected-app controls, and more room for production traffic. Search and health endpoints stay open with no auth."]})]}),e.jsx("div",{className:"grid gap-3 sm:grid-cols-3",children:[{icon:K,title:"No-auth first",desc:"Fetch profiles or call MCP tools until the free visitor quota is used."},{icon:S,title:"OAuth when ready",desc:"Create an app in your account and generate tokens, or let users sign in and authorize. Every plan."},{icon:G,title:"MCP ready",desc:"Native MCP server for Claude, ChatGPT, Cursor, and other AI agents."}].map(({icon:a,title:l,desc:p})=>e.jsxs("div",{className:"rounded-lg border border-border/60 bg-card/60 p-4",children:[e.jsx(a,{className:"mb-2 h-4 w-4 text-primary"}),e.jsx("p",{className:"text-sm font-medium text-foreground",children:l}),e.jsx("p",{className:"mt-1 text-xs leading-relaxed text-muted-foreground",children:p})]},l))}),e.jsx(o,{children:"Base URL"}),e.jsx("div",{className:"rounded-md border border-border/60 bg-muted/40 px-3 py-2 font-mono text-sm text-foreground",children:"https://longmile.io"})]}),e.jsxs(d,{id:"authentication",eyebrow:"Get started",title:"Authentication",children:[e.jsx(r,{children:e.jsxs("p",{children:["LongMile has ",e.jsx("strong",{className:"text-foreground",children:"no static API keys"}),". Programmatic access is either anonymous or an ",e.jsx("strong",{className:"text-foreground",children:"OAuth 2.1 access token"})," that belongs to one LongMile user and draws on that user's lookup quota. Pick the path that fits what you are building:"]})}),e.jsx(m,{head:["You are building…","Use","What you need"],rows:[["A quick test, or low-volume lookups",e.
477jsxs(e.Fragment,{children:["No auth — just omit ",e.jsx(t,{children:"Authorization"}),"."]}),"Nothing. Draws on the free visitor quota for your IP."],["A script, cron job or backend acting as you",e.jsx("a",{href:"#oauth-account-tokens",className:"text-primary hover:underline",children:"Tokens from your account"}),e.jsxs(e.Fragment,{children:["A developer app, then ",e.jsx("strong",{className:"text-foreground",children:"Generate tokens"}),". No redirect, no browser flow."]})],["A product other LongMile users connect (TMS, CRM, portal)",e.jsx("a",{href:"#oauth-code-flow",className:"text-primary hover:underline",children:"Authorization code + PKCE"}),"A developer app with a redirect URI. Each user signs in and approves."],["An AI assistant (Claude, ChatGPT, Cursor…)",e.jsx("a",{href:"#mcp-oauth",className:"text-primary hover:underline",children:"MCP with OAuth"}),e.jsxs(e.Fragment,{children:["Nothing. Paste ",e.jsx(t,{children:"https://longmile.io/mcp"})," and use the client's sign-in action; it registers itself."]})]]}),e.jsx(o,{children:"Sending a token"}),e.jsxs(r,{children:[e.jsxs("p",{children:["Put the access token in the ",e.jsx(t,{children:"Authorization"})," header on every request:"," ",e.jsx(t,{children:"Authorization: Bearer lm_at_…"}),". Access tokens start with ",e.jsx(t,{children:"lm_at_"})," and last"," ",e.jsx("strong",{className:"text-foreground",children:"1 hour"}),"; refresh tokens start with ",e.jsx(t,{children:"lm_rt_"}),", last"," ",e.jsx("strong",{className:"text-foreground",children:"60 days"})," and are replaced every time you use one (see"," ",e.jsx("a",{href:"#oauth-refresh",className:"text-primary hover:underline",children:"Refresh tokens"}),"). Tokens are not accepted in query strings or cookies."]}),e.jsxs("p",{children:["A token that is present but invalid, expired or revoked is rejected with ",e.jsx(t,{children:"401"})," — it"," ",e.jsx("strong",{className:"text-foreground",children:"never"})," falls back to anonymous access. The response carries"," ",e.jsx(t,{children:'WWW-Authenticate: Bearer resource_metadata="https://longmile.io/.well-known/oauth-protected-resource", error="invalid_token"'})," ","so a client knows where to start again."]})]}),e.jsx(o,{children:"What each endpoint accepts"}),e.jsx(m,{head:["Endpoint","No auth","OAuth token","Notes"],rows:[[e.jsx(t,{children:"GET /api/entity/:usdot"}),"Yes","Yes","Lookup quota: visitor (no auth) or account (token)."],[e.jsx(t,{children:"GET /api/v1/carrier/:usdot"}),"Yes","Yes","Legacy alias of the endpoint above."],[e.jsx(t,{children:"POST /mcp"}),"Yes","Yes",e.jsxs(e.Fragment,{children:["Only ",e.jsx(t,{children:"tools/call"})," uses lookup quota."]})],[e.jsx(t,{children:"GET /api/me"}),"No","Required","Who the token belongs to and how much quota is left."],[e.jsx(t,{children:"POST /api/carrier/:usdot/share"}),"Yes","Yes","Also accepts a signed-in browser session."],[e.jsx(t,{children:"POST /api/carrier/:usdot/due-diligence"}),"No","Yes","Also accepts a signed-in browser session."],[e.jsx(t,{children:"/api/risk-acceptance/*"}),"No","Yes",e.jsxs(e.Fragment,{children:["Writes need the ",e.jsx(t,{children:"risk-acceptance:write"})," scope."]})],[e.jsxs(e.Fragment,{children:[e.jsx(t,{children:"GET /api/entity/search"}),", ",e.jsx(t,{children:"/api/methodology"}),", ",e.jsx(t,{children:"/api/health"})]}),"Yes","Ignored","Public. No quota."]]}),e.jsx(g,{tone:"success",children:e.jsxs("p",{children:["Every token can be seen and revoked by its owner under"," ",e.jsx("strong",{children:"Account → Connections → Connected applications"}),". Apps you create live under"," ",e.jsx("strong",{children:"Account → Connections → Developer apps"}),"."]})})]}),e.jsxs(d,{id:"rate-limits",eyebrow:"Get started",title:"Rate limits",children:[e.jsx(r,{children:e.jsx("p",{children:"Two independent limits apply. A request has to pass both."})}),e.jsx(o,{children:"1. Lookup quota — what a request costs"}),e.jsxs(r,{children:[e.jsxs("p",{children:["Profile lookups (",e.jsx(t,{children:"GET /api/entity/:usdot"}),", ",e.jsx(t,{children:"GET /api/v1/carrier/:usdot"}),"), share links (",e.jsx(t,{children:"POST /api/carrier/:usdot/share"}),") and MCP ",e.jsx(t,{children:"tools/call"})," requests are charged against a lookup allowance:"]}),e.jsxs("ul",{className:"list-disc space-y-1.5 pl-5",children:[e.jsxs("li",{children:[e.jsx("strong",{className:"text-foreground",children:"No auth:"})," the free visitor allowance for your IP address, the same one logged-out website visitors use. A request that fails (any 4xx/5xx) is refunded."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-foreground",children:"With a token:"})," the token owner's monthly plan allowance — for a team member, the team owner's shared pool. Only successful requests count, and looking up a USDOT your team already looked up this period is free on the REST endpoints. Each successful MCP"," ",e.jsx(t,{children:"tools/call"})," counts once. Admin accounts are uncapped."]}),e.jsxs("li",{children:["MCP handshake traffic (",e.jsx(t,{children:"initialize"}),", ",e.jsx(t,{children:"tools/list"}),", …) is never charged."]})]}),e.jsxs("p",{children:["Check what is left at any time with ",e.jsx("a",{href:"#endpoint-me",className:"text-primary hover:underline",children:e.jsx(t,{children:"GET /api/me"})}
477)," ","— it costs nothing."]})]}),e.jsx(o,{children:"2. Daily per-IP request limit — abuse protection"}),e.jsx(r,{children:e.jsxs("p",{children:["Separately, every request from one IP address is counted per UTC day, ",e.jsx("strong",{className:"text-foreground",children:"with or without a token"}),", in two buckets: ",e.jsx(t,{children:"/mcp"}),", and everything under ",e.jsx(t,{children:"/api/entity"}),", ",e.jsx(t,{children:"/api/entities"}),","," ",e.jsx(t,{children:"/api/carrier"}),", ",e.jsx(t,{children:"/api/share"})," and ",e.jsx(t,{children:"/api/v1"}),". The counter resets at 00:00 UTC. Responses in those buckets carry ",e.jsx(t,{children:"X-RateLimit-Limit"}),", ",e.jsx(t,{children:"X-RateLimit-Remaining"})," and"," ",e.jsx(t,{children:"X-RateLimit-Reset"})," (Unix seconds). Over the limit you get ",e.jsx(t,{children:"429"})," with a ",e.jsx(t,{children:"Retry-After"})," ","header. If a server-side integration needs more, email support with its outbound IP."]})}),e.jsx(g,{tone:"warning",children:e.jsxs("p",{children:["Out of lookup quota: no-auth REST requests get ",e.jsx(t,{children:"429"})," with ",e.jsx(t,{children:"reason"}),", a ",e.jsx(t,{children:"quota"})," object and a ",e.jsx(t,{children:"next_step"})," pointing at ",e.jsx(t,{children:"/signup"}),". A no-auth MCP ",e.jsx(t,{children:"tools/call"})," gets the same body with ",e.jsx(t,{children:"401"})," and a ",e.jsx(t,{children:"WWW-Authenticate"})," sign-in hint instead, so MCP clients open the LongMile sign-in. Token requests get ",e.jsx(t,{children:"429"})," with ",e.jsx(t,{children:"reason"})," and ",e.jsx(t,{children:"quota.used"}),", ",e.jsx(t,{children:"quota.limit"})," ","and ",e.jsx(t,{children:"quota.plan"}),"."]})})]}),e.jsxs(d,{id:"errors",eyebrow:"Get started",title:"Errors",children:[e.jsx(r,{children:e.jsxs("p",{children:["LongMile uses standard HTTP status codes with a JSON body. Authentication, quota and account endpoints describe the problem in ",e.jsx(t,{children:"message"}),"; the profile lookup endpoints use ",e.jsx(t,{children:"error"}),". Read whichever is present. The OAuth endpoints use the standard ",e.jsx(t,{children:"error"})," / ",e.jsx(t,{children:"error_description"})," pair — see"," ",e.jsx("a",{href:"#oauth-errors",className:"text-primary hover:underline",children:"OAuth errors"}),"."]})}),e.jsxs("div",{className:"rounded-lg border border-border/60 bg-card/40 p-4",children:[e.jsx(y,{code:"200",name:"OK",children:"Request succeeded."}),e.jsx(y,{code:"400",name:"Bad Request",children:"Invalid USDOT (must be 6–8 digits on lookups) or an invalid body."}),e.jsxs(y,{code:"401",name:"Unauthorized",children:["Bearer token missing where one is required, or invalid, expired or revoked — or an anonymous MCP tool call after the visitor quota is used up. Comes with a ",e.jsx("code",{className:"font-mono",children:"WWW-Authenticate"})," header."]}),e.jsxs(y,{code:"403",name:"Forbidden",children:["Account locked or pending approval, a token without the scope a write needs (",e.jsx("code",{className:"font-mono",children:'code: "insufficient_scope"'}),"), or an IP blocked by security controls."]}),e.jsx(y,{code:"404",name:"Not Found",children:"No entity matches the given USDOT."}),e.jsx(y,{code:"429",name:"Too Many Requests",children:"Lookup quota used up (REST, or any request with a token), or the daily per-IP limit reached."}),e.jsx(y,{code:"500",name:"Server Error",children:"Upstream FMCSA failure or an internal error. Retry with backoff."})]})]}),e.jsxs(d,{id:"oauth-overview",eyebrow:"OAuth 2.1",title:"How it works",children:[e.jsx(r,{children:e.jsxs("p",{children:["LongMile is a standards-based ",e.jsx("strong",{className:"text-foreground",children:"OAuth 2.1 authorization server"})," ","and the resource server for its own API and MCP endpoints. Any OAuth library that supports the authorization-code grant with PKCE works. Prefer a document to share with your team? Download the"," ",e.jsx("a",{href:"/longmile-oauth-integration-guide.pdf",target:"_blank",rel:"noopener noreferrer",className:"text-primary hover:underline",children:"OAuth integration guide (PDF)"}),". In short:"]})}),e.jsx(k,{children:[e.jsxs(e.Fragment,{children:["You create an ",e.jsx("strong",{className:"text-foreground",children:"app"})," (an OAuth client) in your account and get a"," ",e.jsx("strong",{className:"text-foreground",children:"client ID"})," — plus a ",e.jsx("strong",{className:"text-foreground",children:"client secret"})," if it runs on a server."]}),e.jsxs(e.Fragment,{children:["A LongMile user grants that app access — either you, by clicking ",e.jsx("strong",{className:"text-foreground",children:"Generate tokens"}),", or any user, by signing in and clicking ",e.jsx("strong",{className:"text-foreground",children:"Authorize"})," on LongMile's consent screen."]}),e.jsxs(e.Fragment,{children:["The app receives an ",e.jsx("strong",{className:"text-foreground",children:"access token"})," (1 hour) and a"," ",e.jsx("strong",{className:"text-foreground",children:"refresh token"})," (60 days, rotated on every use)."]}),e.jsxs(e.Fragment,{children:["The app calls the API with ",e.jsx(t,{children:"Authorization: Bearer <access token>"}),", refreshing when it expires. Every call acts as — and is billed to — the user who granted access."]})]}),e.jsx(o,{children:"Endpoints"}),e.jsx(m,{head:["Endpoint","Purpose"],rows:[[e.jsx(t,{children:"GET /oauth/authorize"}),"Sign-in and consent screen (authorization code + PKCE S256)."],[e.jsx(t,{children:"POST /oauth/token"}),e.jsx(e.Fragment,{children:"Exchange a code, or a refresh token, for tokens. Form-encoded or JSON body."})],[e.jsx(t,{children:"POST /oauth/revoke"}),"Revoke an access or refresh token (RFC 7009)."],[e.jsx(t,{children:"POST /oauth/register"}),"Dynamic Client Registration (RFC 7591) — used by MCP clients."],[e.jsx(t,{children:"GET /.well-known/oauth-authorization-server"}),"Authorization server metadata (RFC 8414)."],[e.jsx(t,{children:"GET /.well-known/oauth-protected-resource"}),"Protected resource metadata (RFC 9728)."]]}),e.jsx(o,{children:"Lifetimes"}),e.jsx(m,{head:["Artifact","Prefix","Lifetime"],rows:[["Client ID",e.jsx(t,{children:"lm_client_"}),"Until you delete the app"],["Client secret","—","Until you create a new one or delete the app"],["Consent screen request","—","10 minutes to click Authorize or Cancel"],["Authorization code",e.jsx(t,{children:"lm_code_"}),"5 minutes, single use"],["Access token",e.jsx(t,{children:"lm_at_"}),"1 hour"],["Refresh token",e.jsx(t,{children:"lm_rt_"}),"60 days from issue; single use — each refresh returns a new one"]]}),e.jsx(g,{children:e.jsx("p",{children:"LongMile stores only a SHA-256 hash of every secret, code and token. Nothing can be shown to you again after it is issued — if you lose a client secret, create a new one; if you lose a refresh token, generate tokens again."})})]}),e.jsxs(d,{id:"oauth-apps",eyebrow:"OAuth 2.1",title:"Create a developer app",children:[e.jsx(r,{children:e.jsx("p",{children:"Every integration starts with an app. Create one in your LongMile account — no approval, no email to supp
477ort:"})}),e.jsx(k,{children:[e.jsxs(e.Fragment,{children:["Sign in and open"," ",e.jsx(_,{href:"/account?section=connections",className:"text-primary hover:underline",children:"Account → Connections"}),"."]}),e.jsxs(e.Fragment,{children:["Under ",e.jsx("strong",{className:"text-foreground",children:"Developer apps"}),", click ",e.jsx("strong",{className:"text-foreground",children:"New app"}),"."]}),e.jsx(e.Fragment,{children:"Give it a name. Users see this name on the consent screen and in their Connected applications list."}),e.jsxs(e.Fragment,{children:["Choose the type:",e.jsxs("ul",{className:"mt-2 list-disc space-y-1 pl-5",children:[e.jsxs("li",{children:[e.jsx("strong",{className:"text-foreground",children:"Server-side app"})," — runs on a server you control. Gets a client ID ",e.jsx("em",{children:"and"})," a client secret."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-foreground",children:"Public app"})," — a browser, mobile, desktop or CLI app that cannot keep a secret. Gets a client ID only and relies on PKCE."]})]})]}),e.jsxs(e.Fragment,{children:["Optionally add ",e.jsx("strong",{className:"text-foreground",children:"redirect URIs"}),", one per line. You need them only if users will connect through the consent screen (",e.jsx("a",{href:"#oauth-code-flow",className:"text-primary hover:underline",children:"authorization code flow"}),"). Leave them empty if you will only use ",e.jsx("a",{href:"#oauth-account-tokens",className:"text-primary hover:underline",children:"tokens from your account"}),"."]}),e.jsxs(e.Fragment,{children:["Click ",e.jsx("strong",{className:"text-foreground",children:"Create app"}),". For a server-side app, ",e.jsx("strong",{className:"text-foreground",children:"copy the client secret now"})," — it is shown exactly once."]})]}),e.jsx(o,{children:"Redirect URI rules"}),e.jsx(r,{children:e.jsxs("ul",{className:"list-disc space-y-1.5 pl-5",children:[e.jsxs("li",{children:[e.jsx(t,{children:"https://"})," on any host, ",e.jsx(t,{children:"http://"})," only on ",e.jsx(t,{children:"localhost"}),", ",e.jsx(t,{children:"127.0.0.1"})," or ",e.jsx(t,{children:"[::1]"}),", or a custom scheme for native apps (",e.jsx(t,{children:"com.example.app:/callback"}),")."]}),e.jsxs("li",{children:["No ",e.jsx(t,{children:"#fragment"}),". ",e.jsx(t,{children:"javascript:"}),", ",e.jsx(t,{children:"data:"}),", ",e.jsx(t,{children:"vbscript:"})," and ",e.jsx(t,{children:"file:"})," are rejected."]}),e.jsxs("li",{children:["Matching is ",e.jsx("strong",{className:"text-foreground",children:"exact"}),", character for character — no wildcards, no prefix matching. Register every variant you use."]}),e.jsx("li",{children:"Up to 10 per app. You can edit them later without changing the client ID or secret."})]})}),e.jsx(o,{children:"Managing an app"}),e.jsx(m,{head:["Action","Effect"],rows:[[e.jsx("strong",{children:"Generate tokens"}),"Issues a new access + refresh token pair acting as you. Existing tokens keep working."],[e.jsx("strong",{children:"Edit redirect URIs"}),"Takes effect on the next authorization. Existing tokens keep working."],[e.jsx("strong",{children:"New secret"}),e.jsxs(e.Fragment,{children:["Server-side apps only. The old secret stops working ",e.jsx("strong",{children:"immediately"}),"; issued tokens stay valid, but the next refresh must use the new secret."]})],[e.jsx("strong",{children:"Delete"}),e.jsxs(e.Fragment,{children:["Removes the app and revokes ",e.jsx("strong",{children:"every"})," token issued to it, for every user who connected it. Cannot be undone."]})]]}),e.jsx(g,{tone:"warning",children:e.jsx("p",{children:"Keep the client secret and refresh tokens on your server — in an environment variable or a secrets manager. Never ship them in browser JavaScript, a mobile binary, or a public repository. Up to 20 apps per account."})})]}),e.jsxs(d,{id:"oauth-account-tokens",eyebrow:"OAuth 2.1",title:"Tokens from your account",children:[e.jsx(r,{children:e.jsx("p",{children:"The fastest way to call the API from your own code. You are both the app's owner and the user, so LongMile lets you grant the app access directly from your account instead of going through a redirect."})}),e.jsx(k,{children:[e.jsxs(e.Fragment,{children:["In ",e.jsx("strong",{className:"text-foreground",children:"Account → Connections → Developer apps"}),", find your app and click"," ",e.jsx("strong",{className:"text-foreground",children:"Generate tokens"}),"."]}),e.jsxs(e.Fragment,{children:["Choose what the tokens may do. ",e.jsx("strong",{className:"text-foreground",children:"Carrier & broker lookups"})," (",e.jsx(t,{children:"carrier:read"}),") and"," ",e.jsx("strong",{className:"text-foreground",children:"MCP tools"})," (",e.jsx(t,{children:"mcp"}),") are on by default. Tick"," ",e.jsx("strong",{className:"text-foreground",children:"Record & withdraw risk acceptances"})," (",e.jsx(t,{children:"risk-acceptance:write"}),") only if your integration writes to the risk-acceptance register."]}),e.jsxs(e.Fragment,{children:["Click ",e.jsx("strong",{className:"text-foreground",children:"Generate"})," and copy both tokens. They are shown once."]}),e.jsxs(e.Fragment,{children:["Call the API with the access token right away. Store the refresh token — and, for a server-side app, the client ID and secret — on your server, and ",e.jsx("a",{href:"#oauth-refresh",className:"text-primary hover:underline",children:"refresh"})," when the access token expires."]})]}),e.jsx(h,{tabs:[{lang:"bash",label:"cURL",code:T},{lang:"me",label:"Test the token",code:A}]}),e.jsx(r,{children:e.jsxs("p",{children:["The pair behaves exactly like one issued through the consent screen: it acts as you, draws on your (or your team's) lookup quota, appears under ",e.jsx("strong",{className:"text-foreground",children:"Connected applications"})," ","with the app's name, and stops working when you click ",e.jsx("strong",{className:"text-foreground",children:"Disconnect"})," there or delete the app. Generating again adds a new pair; it does not invalidate earlier ones."]})})]}),e.jsxs(d,{id:"oauth-code-flow",eyebrow:"OAuth 2.1",title:"Authorization code + PKCE",children:[e.jsx(r,{children:e.jsxs("p",{children:["Use this when ",e.jsx("em",{children:"other"})," LongMile users connect their accounts to your product. Each user signs in to LongMile (email and password, or Google), sees your app's name and the permissions it asks for, and clicks ",e.jsx("strong",{className:"text-foreground",children:"Authorize"}),". PKCE with ",e.jsx(t,{children:"S256"})," is"," ",e.jsx("strong",{className:"text-foreground",children:"required"})," for every client, public or server-side."]})}),e.jsx(o,{children:"Step 1 — Send the user to the authorize endpoint"}),e.jsx(r,{children:e.jsxs("p",{children:["Generate a random ",e.jsx(t,{children:"code_verifier"})," (43–128 characters) and a random ",e.jsx(t,{children:"state"}),", keep both in the user's session, and redirect the browser to:"]})}),e.jsx(h,{tabs:[{lang:"url",label:"GET /oauth/authorize",code:le}]}),e.jsxs("div",{className:"rounded-lg border border-border/60 bg-card/40 px-4",children:[e.jsxs(c,{name:"response_type",type:"string",required:!0,children:["Must be ",e.jsx(t,{children:"code"}),"."]}),e.jsx(c,{name:"client_id",type:"string",required:!0,children:"Your app's client ID."}),e.jsx(c,{name:"redirect_uri",type:"string",children:"Must exactly match one of the app's registered redirect URIs. May be omitted only when the app has exactly one."}),e.jsxs(c,{name:"code_challenge",type:"string",required:!0,children:[e.jsx(t,{children:"BASE64URL(SHA256(code_verifier))"}),", no padding."]}),e.jsxs(c,{name:"code_challenge_method",type:"string",children:[e.jsx(t,{children:"S256"})," (the default when omitted). ",e.jsx(t,{children:"plain"})," is rejected."]}),e.jsxs(c,{name:"scope",type:"string",children:["Space-separated ",e.jsx("a",{href:"#oauth-scopes",className:"text-primary hover:underline",children:"scopes"}),". Unknown values are dropped; if nothing valid remains, you get the default ",e.jsx(t,{children:"mcp carrier:read"}),"."]}),e.jsx(c,{name:"state",type:"string",children:"Returned unchanged on the redirect. Strongly recommended — compare it to protect against CSRF."}),e.jsxs(c,{name:"resource",type:"string",children:["Optional RFC 8707 resource indicator, e.g. ",e.jsx(t,{children:"https://longmile.io/mcp"}),". Recorded with the grant."]})]}),e.jsx(o,{children:"Step 2 — The user approves"}),e.jsxs(r,{children:[e.jsxs("p",{children:["A user who is not signed in is sent to the LongMile sign-in page and brought back afterwards. A user who is already signed in sees the consent screen immediately, with a ",e.jsx("em",{children:"Use a different account"})," ","link. The request must be approved within 10 minutes."]}),e.jsxs("p",{children:["LongMile then redirects the browser to your ",e.jsx(t,{children:"redirect_uri"}),":"]}),e.jsxs("ul",{className:"list-disc space-y-1.5 pl-5",children:[e.jsxs("li",{children:["Approved: ",e.jsx(t,{children:"?code=lm_code_…&state=…"})]}),e.jsxs("li",{children:["Cancelled: ",e.jsx(t,{children:"?error=access_de
477nied&error_description=The+user+denied+the+request.&state=…"})]})]})]}),e.jsx(o,{children:"Step 3 — Exchange the code for tokens"}),e.jsx(r,{children:e.jsxs("p",{children:["Within ",e.jsx("strong",{className:"text-foreground",children:"5 minutes"}),", from your server, POST to"," ",e.jsx(t,{children:"/oauth/token"}),". The code works once."]})}),e.jsxs("div",{className:"rounded-lg border border-border/60 bg-card/40 px-4",children:[e.jsx(c,{name:"grant_type",type:"string",required:!0,children:e.jsx(t,{children:"authorization_code"})}),e.jsxs(c,{name:"code",type:"string",required:!0,children:["The ",e.jsx(t,{children:"code"})," from the redirect."]}),e.jsx(c,{name:"code_verifier",type:"string",required:!0,children:"The verifier you generated in step 1."}),e.jsx(c,{name:"redirect_uri",type:"string",children:"If sent, must equal the one used in step 1."}),e.jsx(c,{name:"client_id",type:"string",required:!0,children:"In the body, or as the username of HTTP Basic auth."}),e.jsxs(c,{name:"client_secret",type:"string",children:["Server-side apps only (required for them). Send as HTTP Basic auth (",e.jsx(t,{children:"client_secret_basic"}),") or in the body (",e.jsx(t,{children:"client_secret_post"}),") — either works."]})]}),e.jsx(h,{tabs:[{lang:"bash",label:"cURL",code:ue}]}),e.jsx(o,{children:"Response"}),e.jsx(h,{tabs:[{lang:"json",label:"200 OK",code:oe}]}),e.jsx(r,{children:e.jsxs("p",{children:["Token responses are sent with ",e.jsx(t,{children:"Cache-Control: no-store"}),". The ",e.jsx(t,{children:"scope"})," field is what was actually granted — check it rather than assuming what you asked for."]})}),e.jsx(o,{children:"Full example"}),e.jsx(h,{tabs:[{lang:"js",label:"Node.js · Express",code:he}]})]}),e.jsxs(d,{id:"oauth-refresh",eyebrow:"OAuth 2.1",title:"Refresh tokens",children:[e.jsx(r,{children:e.jsxs("p",{children:["Access tokens expire after 1 hour. Get a new one by POSTing the refresh token to ",e.jsx(t,{children:"/oauth/token"})," ","with ",e.jsx(t,{children:"grant_type=refresh_token"}),", authenticating the same way as for the code exchange."]})}),e.jsx(h,{tabs:[{lang:"confidential",label:"Server-side app",code:ie},{lang:"public",label:"Public app",code:ae}]}),e.jsx(r,{children:e.jsxs("p",{children:["The response has the same shape as the code exchange, with the ",e.jsx("strong",{className:"text-foreground",children:"same scope"})," ","the original grant had."]})}),e.jsx(g,{tone:"warning",children:e.jsxs("p",{children:[e.jsx("strong",{children:"Refresh tokens rotate."})," Each refresh revokes the refresh token you sent"," ",e.jsx("em",{children:"and"})," the access token paired with it, and returns a new pair. Save the new refresh token before doing anything else; reusing an old one returns ",e.jsx("code",{className:"font-mono",children:"invalid_grant"}),". If several workers share one token, let a single process refresh it. A refresh token that is not used for 60 days expires — schedule a refresh at least that often, or generate tokens again."]})}),e.jsx(o,{children:"Drop-in token helper"}),e.jsx(h,{tabs:[{lang:"js",label:"Node.js",code:ce},{lang:"py",label:"Python",code:de}]})]}),e.jsxs(d,{id:"oauth-scopes",eyebrow:"OAuth 2.1",title:"Scopes",children:[e.jsx(m,{head:["Scope","Shown on the consent screen as","What it controls"],rows:[[e.jsx(t,{children:"carrier:read"}),"Look up carrier and broker intelligence profiles","Default. Profile lookups and share links."],[e.jsx(t,{children:"mcp"}),"Use LongMile's MCP tools (carrier & broker lookups)","Default. The MCP server's tools."],[e.jsx(t,{children:"risk-acceptance:write"}),"Record and withdraw documented risk acceptances in your workspace",e.jsxs(e.Fragment,{children:["Required to ",e.jsx(t,{children:"POST /api/risk-acceptance/carrier/:usdot"})," and ",e.jsx(t,{children:"POST /api/risk-acceptance/:id/revoke"}),". Never granted by default."]})],[e.jsx(t,{children:"profile"}),"Read your basic profile (name)","Informational."],[e.jsx(t,{children:"email"}),"Read your email address","Informational."],[e.jsx(t,{children:"openid"}),"Confirm your LongMile identity","Informational. LongMile does not issue ID tokens."]]}),e.jsxs(r,{children:[e.jsxs("p",{children:["Today ",e.jsx(t,{children:"risk-acceptance:write"})," is the only scope the API enforces; every valid token can use the read endpoints and MCP tools. Ask for the scopes that describe what your app does anyway — they are what the user sees and approves, and future enforcement will follow them. A token missing the write scope gets ",e.jsx(t,{children:"403"})," with ",e.jsx(t,{children:'"code": "insufficient_scope"'})," and"," ",e.jsx(t,{children:'"required_scope": "risk-acceptance:write"'}),"."]}),e.jsxs("p",{children:["There is no userinfo endpoint; to find out who a token belongs to, call"," ",e.jsx("a",{href:"#endpoint-me",className:"text-primary hover:underline",children:e.jsx(t,{children:"GET /api/me"})}),". Scopes cannot be changed on an existing grant — to add one, authorize again (or generate new tokens) with the wider scope."]})]})]}),e.jsxs(d,{id:"oauth-revoke",eyebrow:"OAuth 2.1",title:"Revoke & disconnect",children:[e.jsx(r,{children:e.jsxs("p",{children:["When a user disconnects your integration, revoke their grant. Send either the access token or the refresh token; LongMile revokes the whole grant (both tokens). The endpoint always answers"," ",e.jsx(t,{children:"200"})," with ",e.jsx(t,{children:"{}"}),", even for an unknown token (RFC 7009)."]})}),e.jsx(h,{tabs:[{lang:"bash",label:"cURL",code:pe}]}),e.jsx(m,{head:["Who","Where","What stops working"],rows:[["Your app",e.jsx(t,{children:"POST /oauth/revoke"}),"That one grant (access + refresh token)."],["A user",e.jsxs(e.Fragment,{children:["Account → Connections → Connected applications → ",e.jsx("strong",{children:"Disconnect"})]}),"Every grant that user gave your app."],["The app owner",e.jsxs(e.Fragment,{children:["Developer apps → ",e.jsx("strong",{children:"Delete"})]}),"Every grant, for every user, plus the app itself."],["The app owner",e.jsxs(e.Fragment,{children:["Developer apps → ",e.jsx("strong",{children:"New secret"})]}),"The old secret. Issued tokens keep working until their next refresh."]]}
477),e.jsx(r,{children:e.jsxs("p",{children:["Tokens also stop working if the user's LongMile account is locked or deleted. A revoked or expired token returns ",e.jsx(t,{children:"401"})," — send the user through the authorization flow again."]})})]}),e.jsxs(d,{id:"oauth-discovery",eyebrow:"OAuth 2.1",title:"Discovery & registration",children:[e.jsx(r,{children:e.jsxs("p",{children:["Clients that follow the MCP authorization spec need nothing but the server URL. A request with an invalid token — or with no token to an endpoint that requires one, such as ",e.jsx(t,{children:"/api/me"}),", or an anonymous MCP tool call once the visitor quota is used up — answers"," ",e.jsx(t,{children:"401"})," with a ",e.jsx(t,{children:"WWW-Authenticate"})," header pointing at the protected-resource metadata, which names this server as its authorization server. Both documents are public and CORS-enabled."]})}),e.jsx(m,{head:["URL","Returns"],rows:[[e.jsx(t,{children:"/.well-known/oauth-protected-resource"}),e.jsxs(e.Fragment,{children:["RFC 9728 metadata. ",e.jsx(t,{children:"resource"})," is ",e.jsx(t,{children:"https://longmile.io/mcp"}),". Also served at ",e.jsx(t,{children:"…/oauth-protected-resource/mcp"}),"."]})],[e.jsx(t,{children:"/.well-known/oauth-authorization-server"}),e.jsxs(e.Fragment,{children:["RFC 8414 metadata (below). Also served at ",e.jsx(t,{children:"…/oauth-authorization-server/mcp"})," and ",e.jsx(t,{children:"/.well-known/openid-configuration"}),"."]})]]}),e.jsx(h,{tabs:[{lang:"json",label:"/.well-known/oauth-authorization-server",code:me}]}),e.jsx(o,{children:"Dynamic Client Registration"}),e.jsx(r,{children:e.jsxs("p",{children:["MCP clients register themselves with ",e.jsx(t,{children:"POST /oauth/register"})," (RFC 7591). No authentication is needed. For your own integration, prefer a ",e.jsx("a",{href:"#oauth-apps",className:"text-primary hover:underline",children:"developer app"}),": an app registered this way belongs to no account, so it never appears under Developer apps and you cannot rotate its secret, edit it or generate tokens for it."]})}),e.jsxs("div",{className:"rounded-lg border border-border/60 bg-card/40 px-4",children:[e.jsxs(c,{name:"redirect_uris",type:"string[]",required:!0,children:["Non-empty. Same ",e.jsx("a",{href:"#oauth-apps",className:"text-primary hover:underline",children:"rules"})," as developer apps."]}),e.jsxs(c,{name:"token_endpoint_auth_method",type:"string",children:[e.jsx(t,{children:"none"})," (default — public client, no secret), ",e.jsx(t,{children:"client_secret_basic"})," or"," ",e.jsx(t,{children:"client_secret_post"})," (a ",e.jsx(t,{children:"client_secret"})," is returned once)."]}),e.jsx(c,{name:"client_name",type:"string",children:"Shown on the consent screen. Up to 255 characters."}),e.jsxs(c,{name:"grant_types",type:"string[]",children:["Only ",e.jsx(t,{children:"authorization_code"})," and ",e.jsx(t,{children:"refresh_token"}),". Defaults to both."]}),e.jsx(c,{name:"scope",type:"string",children:"Recorded on the client. The scope actually granted comes from the authorize request."}),e.jsx(c,{name:"client_uri · logo_uri · software_id · software_version",type:"string",children:"Optional, stored as given."})]}),e.jsx(h,{tabs:[{lang:"bash",label:"Request",code:xe},{lang:"json",label:"201 Created",code:ge}]}),e.jsx(r,{children:e.jsxs("p",{children:[e.jsx(t,{children:"client_secret"})," is present only when ",e.jsx(t,{children:"token_endpoint_auth_method"})," is not ",e.jsx(t,{children:"none"}),", and never expires (",e.jsx(t,{children:"client_secret_expires_at: 0"}),"). ",e.jsx(t,{children:"registration_access_token"})," and"," ",e.jsx(t,{children:"registration_client_uri"})," are returned for spec compliance; LongMile does not currently serve RFC 7592 client management at that URI."]})})]}),e.jsxs(d,{id:"oauth-errors",eyebrow:"OAuth 2.1",title:"OAuth errors",children:[e.jsx(o,{children:"On the authorize redirect"}),e.jsx(r,{children:e.jsxs("p",{children:["If ",e.jsx(t,{children:"client_id"})," is unknown or ",e.jsx(t,{children:"redirect_uri"})," does not match, LongMile shows an error page and"," ",e.jsx("strong",{className:"text-foreground",children:"does not redirect"})," — it cannot safely send the user anywhere. Every other problem is sent to your ",e.jsx(t,{children:"redirect_uri"})," as ",e.jsx(t,{children:"error"}),","," ",e.jsx(t,{children:"error_description"})," and your ",e.jsx(t,{children:"state"}),":"]})}),e.jsx(m,{head:["error","Cause"],rows:[[e.jsx(t,{children:"unsupported_response_type"}),e.jsxs(e.Fragment,{children:[e.jsx(t,{children:"response_type"})," was not ",e.jsx(t,{children:"code"}),"."]})],[e.jsx(t,{children:"invalid_request"}),e.jsxs(e.Fragment,{children:["No ",e.jsx(t,{children:"code_challenge"}),", or a method other than ",e.jsx(t,{children:"S256"}),"."]}
477)],[e.jsx(t,{children:"access_denied"}),"The user clicked Cancel, or their LongMile account is locked or pending approval."]]}),e.jsx(o,{children:"From the token endpoint"}),e.jsx(m,{head:["Status","error","Cause"],rows:[["400",e.jsx(t,{children:"invalid_request"}),e.jsxs(e.Fragment,{children:["Missing ",e.jsx(t,{children:"code"}),", ",e.jsx(t,{children:"code_verifier"})," or ",e.jsx(t,{children:"refresh_token"}),"."]})],["400",e.jsx(t,{children:"invalid_grant"}),"Code expired, already used, issued to another client, redirect_uri mismatch, PKCE check failed — or refresh token expired, revoked or already used."],["400",e.jsx(t,{children:"unsupported_grant_type"}),e.jsxs(e.Fragment,{children:["Anything other than ",e.jsx(t,{children:"authorization_code"})," or ",e.jsx(t,{children:"refresh_token"}),". (There is no ",e.jsx(t,{children:"client_credentials"})," grant — use tokens from your account.)"]})],["400",e.jsx(t,{children:"invalid_client"}),e.jsxs(e.Fragment,{children:["No ",e.jsx(t,{children:"client_id"})," sent."]})],["401",e.jsx(t,{children:"invalid_client"}),"Unknown client ID (for example, the app was deleted), or a missing or wrong client secret."]]}),e.jsx(o,{children:"From the API"}),e.jsx(r,{children:e.jsxs("p",{children:["A rejected token returns ",e.jsx(t,{children:"401"})," with a ",e.jsx(t,{children:"message"})," and a ",e.jsx(t,{children:"WWW-Authenticate"})," header carrying"," ",e.jsx(t,{children:'error="invalid_token"'}),". Refresh once; if that fails with ",e.jsx(t,{children:"invalid_grant"}),", the grant is gone — reconnect."]})})]}),e.jsxs(d,{id:"endpoint-entity",eyebrow:"REST API",title:"Get entity profile",children:[e.jsx(j,{method:"GET",path:"/api/entity/:usdot",auth:"optional"}),e.jsxs(r,{children:[e.jsx("p",{children:"Returns the screening profile for a USDOT entity, motor carrier or freight broker. No auth is required until the free visitor quota is used; add an OAuth token to use your account's quota. Works uniformly for both carriers and brokers — only the score and financial-responsibility blocks differ. Results are cached for fast repeated lookups."}),e.jsxs("p",{children:[e.jsx("strong",{children:"Two shapes."})," By default you get the ",e.jsx("em",{children:"compact"})," profile shown below: the screening decision, the eligibility gates, the evidence-weighted risk score, authority, insurance with the filing in force marked, safety, operations, the shared-identity screen, and the company's dated record on one timeline. Add"," ",e.jsx("code",{className:"rounded bg-muted px-1.5 py-0.5 font-mono text-xs font-semibold text-primary",children:"?raw=1"})," ","for the full internal profile — a superset with every source field, larger and less stable. Build against the compact shape unless you need a field it does not carry."]}),e.jsxs("p",{children:["The compact shape is ",e.jsx("strong",{children:"additive by contract"}),": new fields appear over time, existing ones keep their name and type. Parse it permissively."]})]}),e.jsxs(r,{children:[e.jsxs("p",{children:[e.jsx("strong",{children:"Reading the decision block."})," ",e.jsx("code",{className:"rounded bg-muted px-1.5 py-0.5 font-mono text-xs font-semibold text-primary",children:"recommendation"})," states whether the records screened met LongMile's published criteria — ",e.jsx("em",{children:"Cleared"}),", ",e.jsx("em",{children:"Review recommended"}),", or"," ",e.jsx("em",{children:"Not cleared"}),". It describes the records, not the company, and it is not an instruction to transact or to decline to transact."]}),e.jsxs("p",{children:["Branch on ",e.jsx("code",{className:"rounded bg-muted px-1.5 py-0.5 font-mono text-xs font-semibold text-primary",children:"meets_federal_requirements"}),": it is ",e.jsx("code",{className:"rounded bg-muted px-1.5 py-0.5 font-mono text-xs font-semibold text-primary",children:"false"})," when the federal record shows a condition federal law itself treats as disqualifying — no active operating authority, no insurance or bond on file, an out-of-service order, a federal exclusion, or an Unsatisfactory safety rating — with each one listed in ",e.jsx("code",{className:"rounded bg-muted px-1.5 py-0.5 font-mono text-xs font-semibold text-primary",children:"federal_disqualifiers"}),". No score answers that question at any value, so check this field before you read the number."," ",e.jsx("code",{className:"rounded bg-muted px-1.5 py-0.5 font-mono text-xs font-semibold text-primary",children:"bookable"})," carries the same boolean and is kept for existing integrations; prefer the clearer name in new code."]}),e.jsxs("p",{children:["Every response carries a ",e.jsx("code",{className:"rounded bg-muted px-1.5 py-0.5 font-mono text-xs font-semibold text-primary",children:"notice"})," field. Our"," ",e.jsx("a",{href:"/terms",children:"Terms"})," require you to keep it with any screening result you display or pass on."]})]}),e.jsx(o,{children:"Path parameters"}),e.jsx("div",{className:"rounded-lg border border-border/60 bg-card/40 px-4",children:e.jsxs(c,{name:"usdot",type:"string",required:!0,children:["6–8 digit USDOT number (e.g. ",e.jsx("code",{className:"rounded bg-muted px-1 font-mono",children:"3600129"}),"). Works for both carriers and brokers."]})}),e.jsx(o,{children:"Query parameters"}),e.jsxs("div",{className:"rounded-lg border border-border/60 bg-card/40 px-4",children:[e.jsxs(c,{name:"raw",type:"string",children:["Set to ",e.jsx("code",{className:"rounded bg-muted px-1 font-mono",children:"1"})," to receive the full internal profile instead of the compact one. Same lookup, same quota."]}),e.jsxs(c,{name:"view",type:"string",children:[e.jsx("code",{className:"rounded bg-muted px-1 font-mono",children:"carrier"})," (default) or"," ",e.jsx("code",{className:"rounded bg-muted px-1 font-mono",children:"broker"}),". A company registered as both is screened as a carrier unless you ask for its broker screen, which judges it on its broker authority and bond. Returns 404 when the company holds no broker authority. Same lookup, same quota."]})]}),e.jsx(o,{children:"Example request"}),e.jsx(h,{tabs:[{lang:"no-auth",label:"No auth",code:$},{lang:"bash",label:"OAuth",code:T},{lang:"js-no-auth",label:"JS no auth",code:Z},{lang:"js",label:"JS OAuth",code:Q},{lang:"py-no-auth",label:"Python no auth",code:ee},{lang:"py",label:"Python OAuth",code:te},{lang:"raw",label:"Raw shape",code:Y}]}),e.jsx(o,{children:"Example response"}),e.jsx(h,{tabs:[{lang:"json",label:"200 OK",code:re}]}
477),e.jsx("p",{className:"mt-3 text-xs text-muted-foreground/70",children:"Example payloads use fictitious carriers and illustrative values. Scores, bands, and flags shown in the docs do not describe any real company."})]}),e.jsxs(d,{id:"reading-a-profile",eyebrow:"REST API",title:"Reading a profile",children:[e.jsx(r,{children:e.jsx("p",{children:"Three fields carry the decision. The other six exist because the obvious reading of them is wrong, and an integration built on that reading says something untrue about a real company."})}),e.jsx(o,{children:"Build on these three"}),e.jsxs(R,{children:[e.jsxs(x,{n:"01",field:"decision.meets_federal_requirements",children:["The field to branch on. It is ",e.jsx("code",{children:"false"})," where the federal record shows a condition federal law itself treats as disqualifying: no active operating authority, no insurance or bond on file, an out-of-service order, a federal exclusion, or an Unsatisfactory safety rating. Each one is listed in"," ",e.jsx("code",{children:"federal_disqualifiers"}),". No score answers that question at any value, so read this before you read a number. ",e.jsx("code",{children:"bookable"})," carries the same boolean and is kept for existing integrations; prefer the clearer name in new code."]}),e.jsxs(x,{n:"02",field:"decision.display_score",children:["The number the LongMile screens show, already resolved against the verdict. Render this rather than"," ",e.jsx("code",{children:"decision.score"}),", so your UI cannot publish a figure our own verdict contradicts. A null"," ",e.jsx("code",{children:"value"})," with a ",e.jsx("code",{children:"withheld"})," reason is a real answer, not a missing field."]}),e.jsxs(x,{n:"03",field:"method.scoring_edition",children:["Which edition of the rules produced this answer. On a live response it always equals"," ",e.jsx("code",{children:"current_scoring_edition"}),". On a response you ",e.jsx("em",{children:"stored"}),", a difference means it was scored under rules we no longer stand behind. Re-fetch it."]})]}),e.jsx(o,{children:"These six are easy to misread"}),e.jsxs(R,{children:[e.jsxs(x,{n:"04",field:"decision.unverified_checks",children:["The requirements we could not ",e.jsx("em",{children:"check"}),", by name, such as “Federal exclusion” when the SAM.gov list was unavailable. A check we could not run does not bar the company: the score is shown and the verdict is held at Review until it is confirmed at the source. It is a gap in our evidence, not a finding about the business, so never render it as a failure."," ",e.jsx("code",{children:"blocked_only_by_unverifiable"})," is ",e.jsx("code",{children:"true"})," in exactly this case and keeps its original name for existing integrations."]}),e.jsxs(x,{n:"05",field:"insurance.state · insurance.decisive",children:[e.jsx("code",{children:"bmc91x: false"})," means two different things. ",e.jsx("code",{children:'state: "not_in_force"'})," with"," ",e.jsx("code",{children:"decisive: true"})," is a source that was allowed to say no actually saying no, which is a finding about the carrier. ",e.jsx("code",{children:'state: "unknown"'})," with ",e.jsx("code",{children:"decisive: false"})," is our own blind spot. Printing “No insurance” for the second puts an accusation on a carrier who may be perfectly covered."]}),e.jsxs(x,{n:"06",field:"insurance.filings[].in_force",children:["A docket lists every filing a company ever made, most of them superseded. At most one row is marked, and only where the verdict itself says a filing is on file. If no row is marked, do not describe any of them as current cover. The newest date is not the answer. Each row also carries"," ",e.jsx("code",{children:"status"})," (Active, Pending, Replaced or Cancelled, in FMCSA's words), ",e.jsx("code",{children:"received_date"})," ","and ",e.jsx("code",{children:"cancellation_date"})," where the source recorded them; a past cancellation date means the row is history even when no status word came with it."]}),e.jsxs(x,{n:"07",field:"safety.inspections.*.adjusted_oos_rate",children:["The rate the screen actually compared. A carrier with three inspections and one out-of-service reads 33%, five times the national average, on a sample far too small to mean it. Small samples are weighted toward the average before screening, so quoting ",e.jsx("code",{children:"oos_rate"})," beside a verdict of Pass makes the verdict look wrong. ",e.jsx("code",{children:"tier"})," is the five-way result;"," ",e.jsx("code",{children:"insufficient_data"})," means the sample was too small to screen, not that something was found."]}),e.jsxs(x,{n:"08",field:"identity_risk.findings[].points",children:["The shared-identity screen weighs each finding, and the weights are the whole meaning. A shared officer at a company that is"," ",e.jsx("strong",{className:"font-semibold text-foreground",children:"still operating"})," is capped context. One person owning several live businesses is a portfolio, not a chameleon, and it can never reach the review threshold on its own. A link to a company that"," ",e.jsx("strong",{className:"font-semibold text-foreground",children:"ceased"})," around this one's registration is the pattern the screen exists to catch, and is conclusive by itself. The two summaries read alike; only the points and ",e.jsx("code",{children:"linked_entities[].standing"})," tell them apart. Compare"," ",e.jsx("code",{children:"identity_risk.score"})," against ",e.jsx("code",{children:"method.identity_thresholds"}),"."]}),e.jsxs(x,{n:"09",field:"profile.registered_as · profile.also_broker",children:[e.jsx("code",{children:"entity_type"})," is the screen this response ran, not everything the company is."," ",e.jsx("code",{children:"registered_as"})," lists every role on its FMCSA registration (Carrier, Broker, Freight forwarder, Shipper), the way SAFER prints Entity Type. When ",e.jsx("code",{children:"also_broker"})," is"," ",e.jsx("code",{children:"true"}),", request ",e.jsx("code",{children:"?view=broker"})," before hauling for the company: its broker screen is judged on its broker authority, which can be active while its carrier authority is revoked, or the other way round."]})]}),e.jsx(g,{tone:"warning",children:e.jsxs("p",{children:["Every response carries a ",e.jsx("code",{className:"rounded bg-black/10 px-1 dark:bg-white/10",children:"notice"})," ","field. Our ",e.jsx("a",{href:"/terms",children:"Terms"})," require you to keep it with any screening result you display or pass on."]})})]}),e.jsxs(d,{id:"endpoint-search",eyebrow:"REST API",title:"Search carriers",children:[e.jsx(j,{method:"GET",path:"/api/entity/search",auth:"public"}),e.jsx(r,{children:e.jsx("p",{children:"Search for carriers and brokers by legal name or DBA name. Returns a ranked list of possible matches. Use this endpoint to resolve a name to a USDOT before calling the entity endpoint."})}),e.jsx(o,{children:"Query parameters"}),e.jsxs("div",{className:"rounded-lg border border-border/60 bg-card/40 px-4",children:[e.jsx(c,{name:"q",type:"string",required:!0,children:"Carrier name or partial name. Minimum 2 characters."}),e.jsx(c,{name:"limit",type:"number",children:"Maximum results to return (1–50). Defaults to 25."})]}),e.jsx(o,{children:"Example request"}),e.jsx(h,{tabs:[{lang:"bash",label:"cURL",code:X}]}),e.jsx(o,{children:"Example response"}),e.jsx(h,{tabs:[{lang:"json",label:"200 OK",code:se}]})]}),e.jsxs(d,{id:"endpoint-me",eyebrow:"REST API",title:"Token owner & quota",children:[e.jsx(j,{method:"GET",path:"/api/me",auth:"oauth"}),e.jsxs(r,{children:[e.jsx("p",{children:`Returns the LongMile account behind a token, its plan, how much of the lookup allowance is left this period and when it resets, and the scopes the token carries. Use it as your integration's "test c
477onnection" call. It costs no lookups.`}),e.jsxs("p",{children:[e.jsx(t,{children:"account.team_owner_email"})," is set when the allowance is billed to a team owner rather than the token owner. ",e.jsx(t,{children:"quota.limit"})," and ",e.jsx(t,{children:"quota.remaining"})," are ",e.jsx(t,{children:"null"})," when the account is uncapped (",e.jsx(t,{children:"quota.unlimited: true"}),")."]})]}),e.jsx(h,{tabs:[{lang:"bash",label:"cURL",code:A}]}),e.jsx(h,{tabs:[{lang:"json",label:"200 OK",code:je}]})]}),e.jsxs(d,{id:"endpoint-partner",eyebrow:"REST API",title:"Account endpoints",children:[e.jsx(r,{children:e.jsx("p",{children:"These act inside the token owner's account and workspace. They accept an OAuth token (from a server — they do not send CORS headers) or a signed-in LongMile browser session."})}),e.jsx(j,{method:"POST",path:"/api/carrier/:usdot/due-diligence",auth:"token-or-session"}),e.jsx(r,{children:e.jsxs("p",{children:["Generates a Carrier Verification Due Diligence report (PDF) for the carrier. Body:"," ",e.jsx(t,{children:'{"agreed": true}'})," is required (acknowledging the informational-use terms); optional"," ",e.jsx(t,{children:"mode"})," (",e.jsx(t,{children:'"download"'})," or ",e.jsx(t,{children:'"link"'}),") and ",e.jsx(t,{children:"company_profile_id"}),". The account needs a completed company profile (",e.jsx(t,{children:"412"})," otherwise); free plans include one report and then return"," ",e.jsx(t,{children:"402"})," with ",e.jsx(t,{children:'code: "upgrade_required"'}),". The response includes ",e.jsx(t,{children:"report_id"}),","," ",e.jsx(t,{children:"filename"}),", ",e.jsx(t,{children:"generated_at"}),", ",e.jsx(t,{children:"pdf_base64"})," (null when an existing report is reused, with"," ",e.jsx(t,{children:"cached: true"}),"), ",e.jsx(t,{children:"file_url"}),", ",e.jsx(t,{children:"share_url"})," and an ",e.jsx(t,{children:"evidence"})," block with hashes and a"," ",e.jsx(t,{children:"verify_url"}),"."]})}),e.jsx(j,{method:"POST",path:"/api/carrier/:usdot/share",auth:"any"}),e.jsx(r,{children:e.jsxs("p",{children:["Mints a link anyone can open without an account, showing this carrier's full profile. Optional body"," ",e.jsx(t,{children:'{"ttl_minutes": 1–60}'}),", default 10. Returns ",e.jsx(t,{children:"share_url"}),", ",e.jsx(t,{children:"usdot"}),","," ",e.jsx(t,{children:"expires_at"})," and ",e.jsx(t,{children:"ttl_minutes"}),". Costs one lookup, like a profile request. Also works with no auth, on the visitor quota."]})}),e.jsx(j,{method:"GET",path:"/api/risk-acceptance/*",auth:"token-or-session"}),e.jsx(r,{children:e.jsx("p",{children:"The workspace's documented risk-acceptance register, for CRMs that record screening exceptions:"})}),e.jsx(m,{head:["Endpoint","Scope needed","Does"],rows:[[e.jsx(t,{children:"GET /api/risk-acceptance/policy"}),"any","Whether the workspace has the feature on, and its maximum duration."],[e.jsx(t,{children:"GET /api/risk-acceptance/carrier/:usdot"}),"any","The carrier's active acceptance and what could be accepted."],[e.jsx(t,{children:"POST /api/risk-acceptance/carrier/:usdot"}),e.jsx(t,{children:"risk-acceptance:write"}),"Record an acceptance."],[e.jsx(t,{children:"GET /api/risk-acceptance/list"}),"any","Every acceptance in the workspace."],[e.jsx(t,{children:"POST /api/risk-acceptance/:id/revoke"}),e.jsx(t,{children:"risk-acceptance:write"}),"Withdraw an acceptance."],[e.jsx(t,{children:"GET /api/risk-acceptance/:id/events"}),"any","An acceptance's audit history."]]}),e.jsx(r,{children:e.jsx("p",{children:"Turning the feature on or changing the policy is not possible with a token — only the workspace owner can, in the LongMile app."})})]}),e.jsxs(d,{id:"endpoint-methodology",eyebrow:"REST API",title:"Methodology",children:[e.jsx(j,{method:"GET",path:"/api/methodology",auth:"public"}),e.jsxs(r,{children:[e.jsx("p",{children:"The machine-readable method behind every verdict: the eligibility gates, the scored factors and their weights, the peer groups, the shared-identity screen with its point weights and thresholds, the published national benchmarks, and the method's own stated limitations. A black-box score is one nobody can check; this is the document that lets you reproduce a result from public FMCSA data — or tell us we are wrong."}),e.jsxs("p",{children:[e.jsx("code",{className:"rounded bg-muted px-1.5 py-0.5 font-mono text-xs font-semibold text-primary",children:"scoring_edition"})," ","here is the same number a profile carries in"," ",e.jsx("code",{className:"rounded bg-muted px-1.5 py-0.5 font-mono text-xs font-semibold text-primary",children:"method.scoring_edition"}),", so a stored result can always be read against the rules that produced it. Cached for an hour; consumes no lookup quota."]})]}),e.jsx(o,{children:"Example request"}),e.jsx(h,{tabs:[{lang:"bash",label:"cURL",code:V}]})]}),e.jsxs(d,{id:"endpoint-health",eyebrow:"REST API",title:"Health check",children:[e.jsx(j,{method:"GET",path:"/api/health",auth:"public"}),e.jsx(r,{children:e.jsxs("p",{children:["A liveness check: returns ",e.jsx(t,{children:"200"})," with ",e.jsx(t,{children:'{"status": "ok"}'})," whenever the server is up. It does not check upstream data sources, needs no auth and is not rate limited."]})}),e.jsx(o,{children:"Example request"}),e.jsx(h,{tabs:[{lang:"bash",label:"cURL",code:J}]}),e.jsx(o,{children:"Example response"}),e.jsx(h,{tabs:[{lang:"json",label:"200 OK",code:ne}]})]}),e.jsx(d,{id:"mcp-overview",eyebrow:"MCP server",title:"Overview",children:e.jsxs(r,{children:[e.jsxs("p",{children:["The ",e.jsx("strong",{className:"text-foreground",children:"Model Context Protocol (MCP)"})," is a standard that lets AI assistants like Claude call external tools directly. LongMile ships an MCP server that exposes a small set of tools, so Claude, ChatGPT, Cursor and other MCP clients can look up carrier and broker data in natural language."]}),e.jsxs("p",{children:["The server is hosted at ",e.jsx(t,{children:"https://longmile.io/mcp"})," over Streamable HTTP (stateless — no MCP session id). It works without auth up to the free visitor quota. Connect with OAuth to use your account's quota and to see and revoke the connection from your account."]})]})}),e.jsxs(d,{id:"mcp-tools",eyebrow:"MCP server",title:"Tools",children:[e.jsx(r,{children:e.jsxs("p",{children:["Each tool maps to a focused action. Profiles come back as readable Markdown rather than JSON — the same facts as the REST payload, written for a model to quote without reshaping. Everyone gets"," ",e.jsx(t,{children:"search_entities"})," and ",e.jsx(t,{children:"get_entity_profile"}),"; the others are listed only for the internal LongMile accounts named below."]})}),[{name:"search_entities",desc:"Search for US motor carriers or brokers by name or partial name, against the same index the site searches. One match returns the full profile automatically; several return a list with each company's city and state, because names repeat across states and picking the wrong one screens the wrong business.",params:[{name:"query",type:"string",required:!0,desc:"Carrier or broker name (min 2 characters)."},{name:"limit",type:"number",required:!1,desc:"Max results (1–10). Defaults to 6."}]},{name:"get_entity_profile",desc:"Look up a US motor carrier or broker by USDOT. Returns the profile as readable Markdown — screening result, risk and safety scores, authority, the insurance filing in force, roadside inspections with the sample-adjusted rates the screen compared, BASIC coverage, crash history, the shared-identity screen with each finding's weight, the company's dated record on one timeline, and the rules edition it was all produced under.",params:[{name:"usdot",type:"string",required:!0,desc:"USDOT number (1–8 digits)."},{name:"view",type:"string",required:!1,desc:"carrier (default) or broker — the broker screen of a company that also brokers."}]},{name:"add_curated_report",desc:"LongMile admin accounts only — not listed for anyone else. Queues a paraphrased community report from a public source for human review; it cannot publish.",params:[{name:"usdot",type:"string",required:!0,desc:"USDOT of the company the source is about."},{name:"subject_type",type:"string",required:!0,desc:"carrier or broker."},{name:"categories",type:"string[]",required:!0,desc:"Incident categories."},{name:"body",type:"string",required:!0,desc:"Paraphrased summary of the complaint."},{name:"source_url",type:"string",required:!0,desc:"Public http(s) link to the source."},{name:"incident_date",type:"string",required:!1,desc:"ISO date, not in the future."}]},{name:"list_pending_curated_reports",desc:"LongMile admin accounts only. Lists curated reports queued but not yet reviewed.",params:[{name:"usdot",type:"string",required:!1,desc:"Only reports for this USDOT."}]},{name:"publish_blog_article",desc:"LongMile's blog account only — not listed for anyone else. Publishes a Markdown or MDX article to Sanity immediately. SEO title, meta description, slug, reading time, and rich Portable Text body are generated 
477when omitted.",params:[{name:"title",type:"string",required:!0,desc:"Article title."},{name:"content",type:"string",required:!0,desc:"Full Markdown or MDX article body."},{name:"coverImageUrl",type:"string",required:!1,desc:"Optional hero/preview image URL."}]},{name:"list_blog_articles",desc:"LongMile's blog account only. Lists recent Sanity blog articles so the assistant can avoid duplicate topics.",params:[{name:"limit",type:"number",required:!1,desc:"Max articles to return. Defaults to 10."}]}].map(a=>e.jsxs("div",{className:"rounded-lg border border-border/60 bg-card/40 p-4",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(H,{className:"h-4 w-4 text-primary"}),e.jsx("code",{className:"font-mono text-sm font-semibold text-primary",children:a.name})]}),e.jsx("p",{className:"mt-2 text-sm leading-relaxed text-muted-foreground",children:a.desc}),e.jsx("div",{className:"mt-3 rounded-md border border-border/60 px-3",children:a.params.map(l=>e.jsx(c,{name:l.name,type:l.type,required:l.required,children:l.desc},l.name))})]},a.name))]}),e.jsxs(d,{id:"mcp-oauth",eyebrow:"MCP server",title:"Connect with OAuth",children:[e.jsx(r,{children:e.jsxs("p",{children:["MCP clients use the same OAuth server as everything else, and do all of it for you: they read"," ",e.jsx(t,{children:"/.well-known/oauth-protected-resource"}),", register themselves with"," ",e.jsx("a",{href:"#oauth-discovery",className:"text-primary hover:underline",children:"Dynamic Client Registration"}),", open LongMile's sign-in and consent screen, and refresh tokens in the background. You never handle a client ID, secret or token."]})}),e.jsx(k,{children:[e.jsxs(e.Fragment,{children:["In your AI client, add a custom MCP server / connector with the URL ",e.jsx(t,{children:"https://longmile.io/mcp"}),"."]}),e.jsxs(e.Fragment,{children:["Choose the client's ",e.jsx("strong",{className:"text-foreground",children:"Connect"}),", ",e.jsx("strong",{className:"text-foreground",children:"Sign in"})," or"," ",e.jsx("strong",{className:"text-foreground",children:"Authenticate"})," action for the server."]}),e.jsxs(e.Fragment,{children:["Sign in to LongMile (email or Google) in the window that opens and click ",e.jsx("strong",{className:"text-foreground",children:"Authorize"}),"."]}),e.jsx(e.Fragment,{children:"Done. Tool calls now act as you and use your account's lookup quota."})]}),e.jsx(g,{tone:"warning",children:e.jsxs("p",{children:[e.jsx(t,{children:"/mcp"})," also answers requests that carry no token, on the free visitor quota. A client that only starts OAuth after it receives a ",e.jsx("code",{className:"font-mono",children:"401"})," will connect anonymously at first; when that quota is used up, LongMile answers the next tool call with ",e.jsx("code",{className:"font-mono",children:"401"})," and the sign-in hint, and the client opens the LongMile sign-in then. To sign in straight away, use the client's authenticate action. ",e.jsx("strong",{children:"Account → Connections → Connected applications"})," lists OAuth connections; an anonymous one is not listed."]})}),e.jsx(r,{children:e.jsxs("p",{children:["To disconnect, click ",e.jsx("strong",{className:"text-foreground",children:"Disconnect"})," next to the client under"," ",e.jsx("strong",{className:"text-foreground",children:"Account → Connections → Connected applications"}),". The client will need to sign in again."]})})]}),e.jsxs(d,{id:"mcp-setup",eyebrow:"MCP server",title:"Add to a client",children:[e.jsx(r,{children:e.jsxs("p",{children:["Most clients (Claude, ChatGPT, Cursor) let you add a remote MCP server by URL in their connector settings: paste ",e.jsx(t,{children:"https://longmile.io/mcp"}),". Clients configured with a JSON file take an entry like this:"]})}),e.jsx(h,{tabs:[{lang:"json",label:"mcp config",code:ye},{lang:"bash",label:"Claude Code",code:fe}]}),e.jsx(g,{tone:"success",children:e.jsxs("p",{children:["After adding the entry, restart the client if needed. The"," ",e.jsx("code",{className:"font-mono",children:"search_entities"})," and ",e.jsx("code",{className:"font-mono",children:"get_entity_profile"})," ","tools will be available to the assistant."]})})]}),e.jsxs(d,{id:"openapi",eyebrow:"Reference",title:"OpenAPI spec",children:[e.jsx(r,{children:e.jsx("p",{children:"Machine-readable OpenAPI 3.0 spec for the public REST endpoints, including the OAuth 2.1 authorization-code flow as a security scheme. Drop it into Postman, Insomnia, or any OpenAPI-compatible generator."})}),e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3 rounded-lg border border-border/60 bg-card/40 p-4",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium text-foreground",children:"longmile-openapi.json"}),e.jsx("p",{className:"mt-0.5 text-xs text-muted-foreground",children:"Covers entity lookup, search, token owner (/api/me), methodology, and health."})]}),e.jsx("a",{href:"/api/openapi.json",target:"_blank",rel:"noopener noreferrer",children:e.jsxs(U,{variant:"outline",size:"sm",className:"gap-2",children:[e.jsx(E,{className:"h-4 w-4"}),"View spec"]})})]})]}),e.jsxs(d,{id:"support",eyebrow:"Reference",title:"Support",children:[e.jsx(r,{children:e.jsxs("p",{children:["Questions, integration help, bug reports, billing, or custom lookup limits — reach out at"," ",e.jsx("a",{href:"mailto:[email protected]",className:"text-primary underline",children:"[email protected]"}),"."]})}),e.jsxs("div",{className:"flex items-center justify-between gap-4 border-t border-border/60 pt-6 text-xs text-muted-foreground",children:[e.jsx("span",{children:"LongMile Intelligence · API v1.0.0"}),e.jsxs(_,{href:"/",className:"inline-flex items-center gap-1.5 transition-colors hover:text-foreground",children:[e.jsx(z,{className:"h-3.5 w-3.5 rotate-180"}),"Back to app"]})]})]})]}),e.jsx("aside",{className:"hidden py-12 xl:block",children:e.jsx("div",{className:"sticky top-24",children:e.jsx(_e,{items:n.map(a=>{var l;return{id:a,title:((l=C.flatMap(p=>p.links).find(p=>p.id===a))==null?void 0:l.title)??a}}),active:u})})})]})]})}export{ut as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.