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..."
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..."
}
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.
- Redirect the user to
https://auth.example.com/oauth/authorizewithclient_id,redirect_uri,scope,state, and a PKCEcode_challenge. - Verify that the
stateparameter on return matches the value you stored. - Exchange the
codefor tokens at/oauth/token, including yourcode_verifier. - Store the refresh token server-side. Never expose it to the browser.
| Parameter | Required | Description |
|---|---|---|
client_id | Yes | Public identifier for your app. |
redirect_uri | Yes | Must exactly match one registered URI. |
scope | Yes | Space-separated list of requested scopes. |
state | Yes | Random string for CSRF protection. |
code_challenge | Yes | S256 hash of your PKCE verifier. |
Scopes and permissions
Scopes limit what a token can do. Request the smallest set your feature needs.
| Scope | Grants |
|---|---|
read:projects | List and view projects. |
write:projects | Create, update, and archive projects. |
read:users | View member profiles within your organization. |
admin:billing | View 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.