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
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"
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.
| Scope | Covers |
|---|---|
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-keysendpoints 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
| Status | Cause | Fix |
|---|---|---|
| 401 | Missing or malformed header | Check Authorization: Bearer ... or X-API-Key: fos_sk_live_... |
| 401 | Expired JWT or revoked key | Re-auth (Supabase) or regen a key |
| 403 | User not a member of the workspace | Check X-Workspace-Id; join via an invite |
| 403 | /v1/api-keys with X-API-Key | Use a user JWT, not a key |
| 429 | Rate limit exceeded | Exponential backoff, watch X-RateLimit-Reset |
| 5xx | Transient error | Retry with idempotency-key (TODO) or backoff |