Authentication: API Keys
Scoped API keys for programmatic access to Sibyl.
Overview
API keys provide:
- Long-lived credentials for automation
- Scoped permissions (read, write, MCP)
- Organization-bound access
- Revocation support
Key Format: sk_live_<random> or sk_test_<random>
Key Scopes
| Scope | Description |
|---|---|
mcp | Access to /mcp MCP endpoints |
api:read | Read operations on /api/* (GET, HEAD, OPTIONS) |
api:write | All operations on /api/* (implies read) |
Beyond these access scopes, a key can also be bound to specific projects and memory spaces. Those bindings further restrict which projects and memory scopes the key can read or write, independent of the access scope. A key with no project or memory-space binding inherits the creating user's full access within the organization.
Creating API Keys
Via CLI
sibyl auth api-key create --name "CI/CD Pipeline" --scopes mcp,api:readVia REST API
POST /api/auth/api-keysRequest:
{
"name": "CI/CD Pipeline",
"scopes": ["mcp", "api:read"],
"expires_days": 365,
"live": true
}Response:
{
"id": "key_uuid",
"name": "CI/CD Pipeline",
"prefix": "sk_live_abc123...",
"scopes": ["mcp", "api:read"],
"created_at": "2024-12-30T10:00:00Z",
"expires_at": "2025-12-31T23:59:59Z",
"api_key": "sk_live_abc123def456ghi789..."
}Important: The full key is only returned once on creation. Store it securely.
Using API Keys
Authorization Header
curl -X GET "https://api.example.com/api/entities" \
-H "Authorization: Bearer sk_live_abc123def456ghi789..."MCP Endpoint
curl -X POST "https://api.example.com/mcp" \
-H "Authorization: Bearer sk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"method": "tools/call",
"params": {
"name": "search",
"arguments": {"query": "OAuth patterns"}
}
}'Key Storage
API keys are stored securely:
- Prefix stored in plaintext - For lookup (
sk_live_abc123...) - Full key hashed with PBKDF2 - For verification
- Algorithm: SHA-256
- Iterations: 210,000
- Salt: 16 random bytes
Key Management
List Keys
GET /api/auth/api-keysResponse:
{
"keys": [
{
"id": "key_uuid",
"name": "CI/CD Pipeline",
"prefix": "sk_live_abc123...",
"scopes": ["mcp", "api:read"],
"created_at": "2024-12-30T10:00:00Z",
"expires_at": "2025-12-31T23:59:59Z",
"last_used_at": "2024-12-30T15:30:00Z",
"revoked_at": null
}
]
}Revoke Key
POST /api/auth/api-keys/{api_key_id}/revokeResponse:
{
"success": true,
"id": "key_uuid"
}Revoked keys immediately stop working.
Scope Enforcement
REST API Scopes
| HTTP Method | Required Scope |
|---|---|
| GET, HEAD, OPTIONS | api:read OR api:write |
| POST, PUT, PATCH, DELETE | api:write |
MCP Scope
The mcp scope is required for /mcp endpoint access:
{
"scopes": ["mcp"]
}Without mcp scope, MCP requests return 403.
Combined Scopes
For full access:
{
"scopes": ["mcp", "api:write"]
}Authentication Flow
- Extract token from Authorization header
- Check if token starts with
sk_ - Look up key by prefix in database
- Verify key hasn't been revoked
- Check key hasn't expired
- Verify full key hash matches
- Update last_used_at timestamp
- Return user and organization context
Organization Binding
API keys are bound to:
- User who created them
- Organization active at creation time
All operations use the bound organization context.
Expiration
Keys can have optional expiration:
{
"expires_at": "2025-12-31T23:59:59Z"
}Expired keys return 401:
{
"detail": "API key expired"
}Error Responses
| Status | Cause |
|---|---|
| 401 | Invalid, expired, or revoked key |
| 403 | Insufficient scope for operation |
Invalid Key:
{
"detail": "Invalid API key"
}Insufficient Scope:
{
"detail": "Insufficient API key scope"
}Best Practices
Scope Minimization
Grant only required scopes:
// Read-only automation
{"scopes": ["api:read"]}
// MCP agent only
{"scopes": ["mcp"]}
// Full access (use sparingly)
{"scopes": ["mcp", "api:write"]}Key Rotation
- Create new key with same scopes
- Update integrations to use new key
- Verify new key works
- Revoke old key
Naming Pattern
Use descriptive names:
"CI/CD - GitHub Actions"
"Agent - Claude Code"
"Monitoring - Datadog"Expiration Policy
Set expiration for temporary access:
{
"name": "Contractor Access",
"expires_at": "2025-03-31T23:59:59Z"
}Environment Variables
For CI/CD and automation, use environment variables:
# GitHub Actions
- name: Query Sibyl
env:
SIBYL_API_KEY: ${{ secrets.SIBYL_API_KEY }}
run: |
curl -H "Authorization: Bearer $SIBYL_API_KEY" \
https://api.example.com/api/entitiesRelated
- auth-jwt.md - JWT session authentication
- index.md - API overview
