Skip to main content
Documentation · Reference

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:

HTTP
Authorization: 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.

Request
POST /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.

Endpoints
GET /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 expiry
  • inspection.oos_logged — fires when an OOS inspection is logged
  • audit.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.