Skip to content

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

ScopeDescription
mcpAccess to /mcp MCP endpoints
api:readRead operations on /api/* (GET, HEAD, OPTIONS)
api:writeAll 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

bash
sibyl auth api-key create --name "CI/CD Pipeline" --scopes mcp,api:read

Via REST API

http
POST /api/auth/api-keys

Request:

json
{
  "name": "CI/CD Pipeline",
  "scopes": ["mcp", "api:read"],
  "expires_days": 365,
  "live": true
}

Response:

json
{
  "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

bash
curl -X GET "https://api.example.com/api/entities" \
  -H "Authorization: Bearer sk_live_abc123def456ghi789..."

MCP Endpoint

bash
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:

  1. Prefix stored in plaintext - For lookup (sk_live_abc123...)
  2. Full key hashed with PBKDF2 - For verification
    • Algorithm: SHA-256
    • Iterations: 210,000
    • Salt: 16 random bytes

Key Management

List Keys

http
GET /api/auth/api-keys

Response:

json
{
  "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

http
POST /api/auth/api-keys/{api_key_id}/revoke

Response:

json
{
  "success": true,
  "id": "key_uuid"
}

Revoked keys immediately stop working.

Scope Enforcement

REST API Scopes

HTTP MethodRequired Scope
GET, HEAD, OPTIONSapi:read OR api:write
POST, PUT, PATCH, DELETEapi:write

MCP Scope

The mcp scope is required for /mcp endpoint access:

json
{
  "scopes": ["mcp"]
}

Without mcp scope, MCP requests return 403.

Combined Scopes

For full access:

json
{
  "scopes": ["mcp", "api:write"]
}

Authentication Flow

  1. Extract token from Authorization header
  2. Check if token starts with sk_
  3. Look up key by prefix in database
  4. Verify key hasn't been revoked
  5. Check key hasn't expired
  6. Verify full key hash matches
  7. Update last_used_at timestamp
  8. 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:

json
{
  "expires_at": "2025-12-31T23:59:59Z"
}

Expired keys return 401:

json
{
  "detail": "API key expired"
}

Error Responses

StatusCause
401Invalid, expired, or revoked key
403Insufficient scope for operation

Invalid Key:

json
{
  "detail": "Invalid API key"
}

Insufficient Scope:

json
{
  "detail": "Insufficient API key scope"
}

Best Practices

Scope Minimization

Grant only required scopes:

json
// Read-only automation
{"scopes": ["api:read"]}

// MCP agent only
{"scopes": ["mcp"]}

// Full access (use sparingly)
{"scopes": ["mcp", "api:write"]}

Key Rotation

  1. Create new key with same scopes
  2. Update integrations to use new key
  3. Verify new key works
  4. Revoke old key

Naming Pattern

Use descriptive names:

"CI/CD - GitHub Actions"
"Agent - Claude Code"
"Monitoring - Datadog"

Expiration Policy

Set expiration for temporary access:

json
{
  "name": "Contractor Access",
  "expires_at": "2025-03-31T23:59:59Z"
}

Environment Variables

For CI/CD and automation, use environment variables:

bash
# 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/entities

Released under the Apache-2.0 License.