Skip to main content

Authentication

Secure your API access with tokens and scopes.

API Tokens

All API requests require a Bearer token:
Authorization: Bearer sk-ensemble-abc123...

Get API Token

Via CLI

conductor auth:token
Output:
API Token: sk-ensemble-abc123def456...
Expires: Never
Scopes: read:ensembles, execute:ensembles, write:ensembles

Via Dashboard

  1. Navigate to Settings → API Tokens
  2. Click “Create Token”
  3. Select scopes
  4. Copy token (shown only once)

Token Scopes

Control access with fine-grained scopes:

Read Scopes

  • read:ensembles - List and view ensembles
  • read:agents - List and view agents
  • read:executions - View execution history
  • read:components - View components
  • read:state - Read ensemble state

Write Scopes

  • write:ensembles - Create/update ensembles
  • write:agents - Create/update agents
  • write:components - Create/update components
  • write:state - Update ensemble state

Execute Scopes

  • execute:ensembles - Execute ensembles
  • execute:agents - Execute agents

Admin Scopes

  • admin - Full access to all resources

Scope Examples

Read-Only Token

{
  "scopes": [
    "read:ensembles",
    "read:agents",
    "read:executions"
  ]
}
Use case: Monitoring, analytics

Execution Token

{
  "scopes": [
    "execute:ensembles",
    "execute:agents",
    "read:executions"
  ]
}
Use case: Application integration

Full Access Token

{
  "scopes": ["admin"]
}
Use case: Development, CI/CD

Token Management

Create Token

curl -X POST https://api.ensemble.dev/v1/tokens \
  -H "Authorization: Bearer ${ADMIN_TOKEN}" \
  -d '{
    "name": "Production API Token",
    "scopes": ["execute:ensembles", "read:executions"],
    "expiresIn": 2592000
  }'
Response:
{
  "token": "sk-ensemble-abc123...",
  "id": "tok_abc123",
  "name": "Production API Token",
  "scopes": ["execute:ensembles", "read:executions"],
  "expiresAt": 1708000000000,
  "createdAt": 1705408000000
}

List Tokens

curl https://api.ensemble.dev/v1/tokens \
  -H "Authorization: Bearer ${ADMIN_TOKEN}"
Response:
{
  "tokens": [
    {
      "id": "tok_abc123",
      "name": "Production API Token",
      "scopes": [...],
      "lastUsed": 1705408000000,
      "createdAt": 1705315200000
    }
  ]
}

Revoke Token

curl -X DELETE https://api.ensemble.dev/v1/tokens/tok_abc123 \
  -H "Authorization: Bearer ${ADMIN_TOKEN}"

Environment Variables

Store tokens securely:

Local Development

# .env.local
CONDUCTOR_API_TOKEN=sk-ensemble-abc123...

Production (Wrangler)

wrangler secret put CONDUCTOR_API_TOKEN
# Enter token when prompted

Usage

const token = process.env.CONDUCTOR_API_TOKEN;

const response = await fetch('https://api.ensemble.dev/v1/ensembles/my-workflow/execute', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ inputs: {...} })
});

Security Best Practices

1. Never Commit Tokens
# .gitignore
.env
.env.local
.env.*.local
2. Use Minimal Scopes
// Good: Only what's needed
{
  "scopes": ["execute:ensembles"]
}

// Bad: Over-permissioned
{
  "scopes": ["admin"]
}
3. Rotate Tokens Regularly
# Create new token
conductor auth:token --name "Q1 2025"

# Update applications
# ...

# Revoke old token
conductor auth:revoke tok_old123
4. Use Short-Lived Tokens
# 30-day expiration
conductor auth:token --expires-in 30d
5. Monitor Token Usage
# Check last used
conductor tokens:list

# View activity
conductor tokens:activity tok_abc123

Error Handling

Missing Token

Request:
GET /ensembles
Response (401):
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Missing authorization header"
  }
}

Invalid Token

Request:
GET /ensembles
Authorization: Bearer invalid-token
Response (401):
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid API token"
  }
}

Insufficient Scope

Request:
POST /ensembles
Authorization: Bearer sk-ensemble-read-only...
Response (403):
{
  "error": {
    "code": "FORBIDDEN",
    "message": "Insufficient scope: requires write:ensembles",
    "requiredScope": "write:ensembles",
    "providedScopes": ["read:ensembles"]
  }
}

Expired Token

Response (401):
{
  "error": {
    "code": "TOKEN_EXPIRED",
    "message": "API token expired",
    "expiredAt": 1705315200000
  }
}

OAuth (Future)

OAuth 2.0 support coming soon:
// Future API
const oauth = new ConductorOAuth({
  clientId: 'your-client-id',
  redirectUri: 'https://yourapp.com/callback'
});

const authUrl = oauth.getAuthorizationUrl();
// Redirect user to authUrl

// In callback handler
const token = await oauth.exchangeCode(code);

Next Steps