Authentication

Learn how to authenticate your API requests using API keys, OAuth 2.0, or JWT tokens.

API Keys

API keys are the simplest way to authenticate. Include your API key in the Authorization header of every request.

Getting Your API Key

  1. Navigate to your API Keys settings
  2. Click "Create New Key"
  3. Give your key a descriptive name
  4. Copy and securely store your key (it won't be shown again)
curl https://api.example.com/v1/users \
  -H "Authorization: Bearer YOUR_API_KEY"
Security Note: Never commit API keys to version control or expose them in client-side code. Use environment variables and secure secret management.

API Key Permissions

API keys can be scoped to specific permissions. Available scopes include:

Scope Description Operations
users:read Read user data GET /users, GET /users/:id
users:write Create and update users POST /users, PUT /users/:id, PATCH /users/:id
users:delete Delete users DELETE /users/:id
projects:read Read project data GET /projects, GET /projects/:id
projects:write Create and update projects POST /projects, PUT /projects/:id
teams:admin Full team management All team endpoints

OAuth 2.0

For applications that need to access user data on behalf of users, use OAuth 2.0. We support the authorization code flow with PKCE for maximum security.

OAuth Flow

The OAuth 2.0 flow involves several steps:

  1. Register your application

    Create an OAuth application in your OAuth Apps settings. You'll receive a client ID and client secret.

  2. Redirect users to authorize
    let params = new URLSearchParams({
      client_id: 'YOUR_CLIENT_ID',
      redirect_uri: 'https://yourapp.com/callback',
      response_type: 'code',
      scope: 'users:read projects:write',
      state: 'random_state_value'
    })
    
    window.location.href = `https://api.example.com/oauth/authorize?${params}`
  3. Handle the callback

    After authorization, users are redirected back with an authorization code:

    // In your callback handler
    let code = new URL(window.location.href).searchParams.get('code')
    
    // Exchange code for access token
    let response = await fetch('https://api.example.com/oauth/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        client_id: 'YOUR_CLIENT_ID',
        client_secret: 'YOUR_CLIENT_SECRET',
        code: code,
        grant_type: 'authorization_code'
      })
    })
    
    let { access_token, refresh_token } = await response.json()
  4. Use the access token
    let response = await fetch('https://api.example.com/v1/users/me', {
      headers: {
        'Authorization': `Bearer ${access_token}`
      }
    })

Refreshing Tokens

Access tokens expire after 1 hour. Use refresh tokens to obtain new access tokens:

let response = await fetch('https://api.example.com/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET',
    refresh_token: refresh_token,
    grant_type: 'refresh_token'
  })
})

let { access_token } = await response.json()

JWT Tokens

For service-to-service authentication, you can use JWT tokens signed with RS256. This is useful for webhook validation and server-side integrations.

Creating a JWT

import jwt from 'jsonwebtoken'
import fs from 'fs'

let privateKey = fs.readFileSync('private-key.pem')

let token = jwt.sign(
  {
    iss: 'YOUR_CLIENT_ID',
    sub: 'user-id',
    aud: 'https://api.example.com',
    exp: Math.floor(Date.now() / 1000) + 3600
  },
  privateKey,
  { algorithm: 'RS256' }
)

Verifying a JWT

When you receive a JWT from our API (such as in webhook payloads), verify it using our public key:

import jwt from 'jsonwebtoken'

// Fetch our public key from /.well-known/jwks.json
let publicKey = await fetchPublicKey()

try {
  let decoded = jwt.verify(token, publicKey, {
    algorithms: ['RS256'],
    audience: 'https://yourapp.com',
    issuer: 'https://api.example.com'
  })
  console.log('Valid token:', decoded)
} catch (err) {
  console.error('Invalid token:', err)
}

Code Examples

JavaScript/TypeScript

import { ApiClient } from '@example/api-client'

let client = new ApiClient({
  apiKey: process.env.API_KEY
})

// List users
let users = await client.users.list({ limit: 10 })

// Create a user
let newUser = await client.users.create({
  email: 'user@example.com',
  name: 'John Doe'
})

Python

from example_api import ApiClient

client = ApiClient(api_key=os.environ['API_KEY'])

# List users
users = client.users.list(limit=10)

# Create a user
new_user = client.users.create(
    email='user@example.com',
    name='John Doe'
)

Ruby

require 'example_api'

client = ExampleApi::Client.new(api_key: ENV['API_KEY'])

# List users
users = client.users.list(limit: 10)

# Create a user
new_user = client.users.create(
  email: 'user@example.com',
  name: 'John Doe'
)

Best Practices

Security Recommendations

  • Rotate keys regularly - Set up a rotation schedule for API keys (every 90 days recommended). See key rotation guide.
  • Use separate keys per environment - Never use production keys in development. Create environment-specific keys.
  • Implement rate limiting - Protect your integration with client-side rate limiting.
  • Monitor API usage - Set up alerts for unusual activity in your usage dashboard.
  • Use HTTPS only - Never send credentials over unencrypted connections

Error Handling

Authentication errors return specific status codes. See our error reference for details:

Status Code Error Description
401 Unauthorized Invalid or missing API key
403 Forbidden Valid key but insufficient permissions
429 Rate Limited Too many requests, see rate limits

Authentication

Learn how to authenticate your API requests using API keys, OAuth 2.0, or JWT tokens.

API Keys

API keys are the simplest way to authenticate. Include your API key in the Authorization header of every request.

Getting Your API Key

  1. Navigate to your API Keys settings
  2. Click "Create New Key"
  3. Give your key a descriptive name
  4. Copy and securely store your key (it won't be shown again)
curl https://api.example.com/v1/users \
  -H "Authorization: Bearer YOUR_API_KEY"
Security Note: Never commit API keys to version control or expose them in client-side code. Use environment variables and secure secret management.

API Key Permissions

API keys can be scoped to specific permissions. Available scopes include:

Scope Description Operations
users:read Read user data GET /users, GET /users/:id
users:write Create and update users POST /users, PUT /users/:id, PATCH /users/:id
users:delete Delete users DELETE /users/:id
projects:read Read project data GET /projects, GET /projects/:id
projects:write Create and update projects POST /projects, PUT /projects/:id
teams:admin Full team management All team endpoints

OAuth 2.0

For applications that need to access user data on behalf of users, use OAuth 2.0. We support the authorization code flow with PKCE for maximum security.

OAuth Flow

The OAuth 2.0 flow involves several steps:

  1. Register your application

    Create an OAuth application in your OAuth Apps settings. You'll receive a client ID and client secret.

  2. Redirect users to authorize
    let params = new URLSearchParams({
      client_id: 'YOUR_CLIENT_ID',
      redirect_uri: 'https://yourapp.com/callback',
      response_type: 'code',
      scope: 'users:read projects:write',
      state: 'random_state_value'
    })
    
    window.location.href = `https://api.example.com/oauth/authorize?${params}`
  3. Handle the callback

    After authorization, users are redirected back with an authorization code:

    // In your callback handler
    let code = new URL(window.location.href).searchParams.get('code')
    
    // Exchange code for access token
    let response = await fetch('https://api.example.com/oauth/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        client_id: 'YOUR_CLIENT_ID',
        client_secret: 'YOUR_CLIENT_SECRET',
        code: code,
        grant_type: 'authorization_code'
      })
    })
    
    let { access_token, refresh_token } = await response.json()
  4. Use the access token
    let response = await fetch('https://api.example.com/v1/users/me', {
      headers: {
        'Authorization': `Bearer ${access_token}`
      }
    })

Refreshing Tokens

Access tokens expire after 1 hour. Use refresh tokens to obtain new access tokens:

let response = await fetch('https://api.example.com/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET',
    refresh_token: refresh_token,
    grant_type: 'refresh_token'
  })
})

let { access_token } = await response.json()

JWT Tokens

For service-to-service authentication, you can use JWT tokens signed with RS256. This is useful for webhook validation and server-side integrations.

Creating a JWT

import jwt from 'jsonwebtoken'
import fs from 'fs'

let privateKey = fs.readFileSync('private-key.pem')

let token = jwt.sign(
  {
    iss: 'YOUR_CLIENT_ID',
    sub: 'user-id',
    aud: 'https://api.example.com',
    exp: Math.floor(Date.now() / 1000) + 3600
  },
  privateKey,
  { algorithm: 'RS256' }
)

Verifying a JWT

When you receive a JWT from our API (such as in webhook payloads), verify it using our public key:

import jwt from 'jsonwebtoken'

// Fetch our public key from /.well-known/jwks.json
let publicKey = await fetchPublicKey()

try {
  let decoded = jwt.verify(token, publicKey, {
    algorithms: ['RS256'],
    audience: 'https://yourapp.com',
    issuer: 'https://api.example.com'
  })
  console.log('Valid token:', decoded)
} catch (err) {
  console.error('Invalid token:', err)
}

Code Examples

JavaScript/TypeScript

import { ApiClient } from '@example/api-client'

let client = new ApiClient({
  apiKey: process.env.API_KEY
})

// List users
let users = await client.users.list({ limit: 10 })

// Create a user
let newUser = await client.users.create({
  email: 'user@example.com',
  name: 'John Doe'
})

Python

from example_api import ApiClient

client = ApiClient(api_key=os.environ['API_KEY'])

# List users
users = client.users.list(limit=10)

# Create a user
new_user = client.users.create(
    email='user@example.com',
    name='John Doe'
)

Ruby

require 'example_api'

client = ExampleApi::Client.new(api_key: ENV['API_KEY'])

# List users
users = client.users.list(limit: 10)

# Create a user
new_user = client.users.create(
  email: 'user@example.com',
  name: 'John Doe'
)

Best Practices

Security Recommendations

  • Rotate keys regularly - Set up a rotation schedule for API keys (every 90 days recommended). See key rotation guide.
  • Use separate keys per environment - Never use production keys in development. Create environment-specific keys.
  • Implement rate limiting - Protect your integration with client-side rate limiting.
  • Monitor API usage - Set up alerts for unusual activity in your usage dashboard.
  • Use HTTPS only - Never send credentials over unencrypted connections

Error Handling

Authentication errors return specific status codes. See our error reference for details:

Status Code Error Description
401 Unauthorized Invalid or missing API key
403 Forbidden Valid key but insufficient permissions
429 Rate Limited Too many requests, see rate limits