API & MCP Authentication
Auth

Authenticate your API calls

Two schemes: Bearer for web/mobile (Supabase session), X-API-Key for scripts/partners/Zapier. Pick by context, never both at once.

Available schemes

Authorization: Bearer Web, mobile

The client grabs the JWT from the Supabase session (supabase.auth.getSession()) and sends it as Authorization: Bearer <jwt>. Offline HS256 validation, no Supabase round-trip. The workspace is resolved via X-Workspace-Id or app_metadata.active_workspace_id.

curl https://api.freelance-os.fr/v1/me \
  -H "Authorization: Bearer $JWT"
X-API-Key Partners, scripts, Zapier

Generate a key from /settings/api-keys in Kernel. Format fos_sk_live_<token>, scoped to a single workspace (no X-Workspace-Id needed). The plaintext shows once, store it immediately.

curl https://api.freelance-os.fr/v1/me \
  -H "X-API-Key: fos_sk_live_..."

Scopes

Keys carry a list of scopes. For public API calls the scope check is currently minimal (read:* implied). We tighten route-level enforcement as the write surface grows.

ScopeCovers
read:*All GET endpoints
write:*POST / PATCH / DELETE on top of GET
*Full access including admin surfaces
read:bookings (granular)Reserved for future segmented keys

Limits & best practices

  • Rate limit: 120 req/min sliding window per key or IP. 429 when exceeded, headers X-RateLimit-Limit / Remaining / Reset.
  • Management gate: the /v1/api-keys endpoints require a JWT. A key cannot create or revoke another key, by design.
  • Expiration: optional. Bot keys can live forever; human / Zapier keys get a recommended 30/90/365-day window.
  • Revocation: immediate on DELETE /v1/api-keys/<id>. No cache, no delay.
  • Storage: token_hash = sha256(plaintext) at rest, plaintext never persisted. On leak, revoke + regenerate.
  • Mobile: native Supabase login, store the JWT in the secure keychain, refresh like web.

Common errors

StatusCauseFix
401Missing or malformed headerCheck Authorization: Bearer ... or X-API-Key: fos_sk_live_...
401Expired JWT or revoked keyRe-auth (Supabase) or regen a key
403User not a member of the workspaceCheck X-Workspace-Id; join via an invite
403/v1/api-keys with X-API-KeyUse a user JWT, not a key
429Rate limit exceededExponential backoff, watch X-RateLimit-Reset
5xxTransient errorRetry with idempotency-key (TODO) or backoff
Try it live (Scalar) → Generate a key in Kernel