API reference
Compass exposes a small REST API. Authentication is Supabase JWT (same token used by the web app). Every endpoint is rate-limited per IP via the in-edge limiter, then per-user once authenticated.
Base URL + versioning
Production base: https://x3compass.com/api (Cloudflare Pages Functions). No version prefix today — breaking changes will introduce/api/v2/ and the unversioned routes will be aliased to v1 for ≥ 6 months.
Authentication
Every authed endpoint expects a Supabase JWT in the Authorization header:
HTTPAuthorization: Bearer <SUPABASE_JWT> Content-Type: application/json
Get the JWT by signing in via Supabase Auth — see Integrations → Supabase. The JWT is also stored as an HTTP-only cookie after sign-in for browser flows.
Service-role keys are never accepted on customer endpoints. Only internal Pages Functions (e.g. the Stripe webhook) use service-role credentials — and those are server-side environment vars, not handed to clients.
Rate limits
/api/ask— 30 req / min / IP (authed)/api/ask-demo— 5 req / 6 h / IP (unauth, public)- Stripe + webhook endpoints — unlimited (HMAC-verified)
- Everything else — 60 req / min / IP
Exceeding a limit returns HTTP 429 with Retry-After in seconds.
POST /api/ask
Ask Compass an FMCSA compliance question. Authenticated; uses your carrier's context where relevant.
RequestPOST /api/ask Authorization: Bearer <jwt> { "messages": [ { "role": "user", "content": "Random rate for 2026 — what % must I hit?" } ], "model": "claude-sonnet-4-6" // optional, default }
Response 200{ "ok": true, "content": "Per 49 CFR § 382.305(b)(2), the minimum annual random testing rate ...", "model": "claude-sonnet-4-6", "usage": { "input_tokens": 145, "output_tokens": 312 }, "cited_sections": ["382.305(b)(2)", "382.305"], "unverified_citations": [], "citation_quality_score": 1.0 }
The cited_sectionsarray is every 49 CFR section in the response. Each is round-tripped against the live eCFR registry before the response returns — sections that don't resolve are listed in unverified_citations and drop the citation_quality_score below 1.0.
POST /api/ask-demo
Unauthenticated demo endpoint, same shape as /api/ask but rate-limited 5 req / 6h / IP and capped at 800 chars per prompt. The homepage Ask Compass widget uses this.
GET /api/health
Public health check. Pings Supabase + Stripe with 5-second timeouts. Used by the uptime + journey-probe crons.
Response 200{ "ok": true, "status": "operational", "checked_at": "2026-05-17T17:47:02.165Z", "total_ms": 482, "services": { "supabase": { "ok": true, "ms": 482 }, "stripe": { "ok": true, "ms": 326 } } }
Drivers + vehicles CRUD
Standard RESTful endpoints for the carrier-data tables. All require auth + are RLS-isolated by carrier_id at the database tier — even if a bug skipped a check at the API layer, the DB would still return zero rows.
EndpointsGET /api/drivers # list drivers for current carrier POST /api/drivers # create driver GET /api/drivers/{id} # get one PATCH /api/drivers/{id} # partial update DELETE /api/drivers/{id} # soft-delete (status=terminated) GET /api/vehicles # list vehicles POST /api/vehicles # create GET /api/vehicles/{id} PATCH /api/vehicles/{id} DELETE /api/vehicles/{id} GET /api/inspections # list inspections, paginated POST /api/inspections # log an inspection GET /api/inspections/{id} PATCH /api/inspections/{id} # mark OOS, attach photo evidence, etc.
Full schemas: see Supabase auto-generated REST docs at https://lsxtcluavinibdqlooil.supabase.co/rest/v1/?apikey={anon_key} once authenticated.
POST /api/audit/build
Builds a ZIP audit packet for the current carrier. Returns a signed URL to download. ZIP includes 10 tables + manifest + a README mapping each file to the CFR section it satisfies.
Response{ "ok": true, "url": "https://r2.x3compass.com/audits/{carrier_id}/{timestamp}.zip?sig=...", "expires_at": "2026-05-18T17:47:02Z", "size_bytes": 4823091 }
Webhooks
Compass receives webhooks from Stripe (subscription state) and Checkr (background-check report updates). Both verify HMAC signatures before any state change. Customer-facing outbound webhooks are on the roadmap — eventual events:
driver.cdl_expiring— fires 60 days, 30 days, 7 days before expiryinspection.oos_logged— fires when an OOS inspection is loggedaudit.export_ready— fires when an audit ZIP completes
Error format
Every error returns JSON:
Error 4xx/5xx{ "ok": false, "error": "Human-readable message", "detail": "Optional longer detail", "code": "RATE_LIMITED" // optional, machine-readable }
Common codes: UNAUTHORIZED, FORBIDDEN, RATE_LIMITED, NOT_FOUND,VALIDATION_ERROR, UPSTREAM_ERROR.