Docs
Docs / Guides / Authentication

Authentication

Every request to the API must identify the caller. This guide explains the supported methods, when to use each one, and how to send credentials correctly.

API keys

API keys are long-lived secrets tied to a single project. They are the simplest way to authenticate server-to-server integrations. Create one from the dashboard under Settings → Developer → Keys.

Send the key in the Authorization header:

curl https://api.example.com/v1/projects \
  -H "Authorization: Bearer sk_live_4f9c2a7e1b8d03..."
Keep keys secret Never embed an API key in client-side JavaScript, mobile apps, or public repositories. Anyone holding the key can act as your project. If a key leaks, revoke it immediately and generate a new one.

Bearer tokens

Short-lived access tokens are issued in exchange for a valid API key or a refresh token. They expire after one hour and are the preferred option for user-facing apps.

POST /v1/auth/token
Content-Type: application/json

{
  "grant_type": "api_key",
  "api_key": "sk_live_4f9c2a7e1b8d03..."
}

// 200 OK
{
  "access_token": "at_eyJhbGciOiJIUzI1NiJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "rt_8c1d0e4f7a..."
}

Use the returned access_token on subsequent requests:

GET /v1/projects
Authorization: Bearer at_eyJhbGciOiJIUzI1NiJ9...

Refreshing a token

When an access token expires, exchange the refresh token for a new pair. Refresh tokens are single-use: each refresh returns a new refresh token, and the old one stops working.

POST /v1/auth/token
Content-Type: application/json

{
  "grant_type": "refresh_token",
  "refresh_token": "rt_8c1d0e4f7a..."
}
Tip Refresh proactively, about 5 minutes before expires_in elapses, rather than waiting for a 401. This avoids a failed request in the middle of a user action.

OAuth 2.0 flow

If your application acts on behalf of other users, use the authorization code flow with PKCE. The user signs in on our domain, grants your app specific scopes, and is redirected back to your redirect_uri with a one-time code.

  1. Redirect the user to https://auth.example.com/oauth/authorize with client_id, redirect_uri, scope, state, and a PKCE code_challenge.
  2. Verify that the state parameter on return matches the value you stored.
  3. Exchange the code for tokens at /oauth/token, including your code_verifier.
  4. Store the refresh token server-side. Never expose it to the browser.
ParameterRequiredDescription
client_idYesPublic identifier for your app.
redirect_uriYesMust exactly match one registered URI.
scopeYesSpace-separated list of requested scopes.
stateYesRandom string for CSRF protection.
code_challengeYesS256 hash of your PKCE verifier.

Scopes and permissions

Scopes limit what a token can do. Request the smallest set your feature needs.

ScopeGrants
read:projectsList and view projects.
write:projectsCreate, update, and archive projects.
read:usersView member profiles within your organization.
admin:billingView and change billing details. Requires owner role.

Troubleshooting

401 Unauthorized

The header is missing, malformed, or the token has expired. Confirm the header reads exactly Authorization: Bearer <token> with a single space and no quotes.

403 Forbidden

The credential is valid but lacks the required scope or role. Check the scopes granted to the token against the endpoint's reference page.

429 Too Many Requests

Authentication endpoints have a stricter limit than the rest of the API. Back off and retry after the number of seconds in the Retry-After header. See Rate limits.