API Key Endpoint Reference

Models, permissions, and request and response contracts for API key management.

AuthOS release 0.8.2 API v1 Latest-only documentation
Updated Jul 15, 2026
On this page

Data Models

API Key Model

{
  "id": "uuid",
  "service_id": "uuid",
  "name": "Production Backend Server",
  "prefix": "sk_a1b2c3",
  "key_hash": "sha256-hash",
  "permissions": ["read:users", "read:subscriptions", "read:analytics"],
  "last_used_at": "datetime",
  "expires_at": "datetime",
  "created_at": "datetime",
  "created_by": "uuid"
}

API Key Response (without key)

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "service_id": "service-uuid",
  "name": "Production Backend Server",
  "prefix": "sk_a1b2c3",
  "permissions": ["read:users", "read:subscriptions", "read:analytics"],
  "last_used_at": "2025-01-20T14:22:00Z",
  "expires_at": "2025-04-15T10:30:00Z",
  "created_at": "2025-01-15T10:30:00Z",
  "created_by": "user-uuid"
}

API Key Create Response (with full key)

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "service_id": "service-uuid",
  "name": "Production Backend Server",
  "prefix": "sk_a1b2c3",
  "permissions": ["read:users", "read:subscriptions", "read:analytics"],
  "expires_at": "2025-04-15T10:30:00Z",
  "created_at": "2025-01-15T10:30:00Z",
  "created_by": "user-uuid",
  "key": "sk_a1b2c3_d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2"
}

Valid Permissions

API keys support the following granular permissions:

Permission Description
read:users List and retrieve user information for users who have authenticated with the service
write:users Create and manage users
delete:users Delete users
read:subscriptions List and retrieve subscription information
write:subscriptions Create and update subscriptions
delete:subscriptions Delete subscriptions
read:analytics Access service analytics and metrics
read:service View service configuration
write:service Modify service configuration

Permission Best Practices:

  • Principle of Least Privilege: Grant only the permissions required for the specific use case
  • Read-Only Keys: Use read-only permissions (read:*) for analytics dashboards and monitoring
  • Separate Keys: Create different keys for different backend services
  • Time-Limited: Set expiration dates for keys used in CI/CD or temporary integrations

Endpoints Summary

Method Path Description Permissions
POST /api/organizations/:org_slug/services/:service_slug/api-keys Create API key Owner/Admin
GET /api/organizations/:org_slug/services/:service_slug/api-keys List API keys Owner/Admin/Member
GET /api/organizations/:org_slug/services/:service_slug/api-keys/:api_key_id Get API key details Owner/Admin/Member
DELETE /api/organizations/:org_slug/services/:service_slug/api-keys/:api_key_id Delete API key Owner/Admin

API Key Operations

POST /api/organizations/:org_slug/services/:service_slug/api-keys

Create a new API key for a service.

Permissions: Owner or Admin

Headers:

Header Value
Authorization Bearer {jwt}

Path Parameters:

Parameter Type Description
org_slug string Organization slug
service_slug string Service slug

Request Body:

Field Type Required Description
name string Yes Descriptive name for the API key (e.g., “Production Backend Server”)
permissions string[] Yes Array of permissions (at least one required)
expires_in_days integer No Number of days until expiration (e.g., 90 for 3 months)

Example Request:

curl -X POST https://sso.example.com/api/organizations/acme-corp/services/main-app/api-keys \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Backend Server",
    "permissions": ["read:users", "read:subscriptions", "read:analytics"],
    "expires_in_days": 90
  }'

Example Response (201 Created):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "service_id": "service-uuid",
  "name": "Production Backend Server",
  "prefix": "sk_a1b2c3",
  "permissions": ["read:users", "read:subscriptions", "read:analytics"],
  "expires_at": "2025-04-15T10:30:00Z",
  "created_at": "2025-01-15T10:30:00Z",
  "created_by": "user-uuid",
  "key": "sk_a1b2c3_d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2"
}

Error Responses:

  • 400 Bad Request: Invalid permissions, empty name, or no permissions provided
  • 401 Unauthorized: Invalid or missing JWT
  • 403 Forbidden: User is not an owner or admin, or organization not active
  • 404 Not Found: Organization or service not found
  • 500 Internal Server Error: Database error

Important Notes:

  • Store the key securely: The key field contains the full API key and is only returned once
  • Cannot be retrieved: If you lose the key, you must create a new one
  • Key format: sk_{prefix}_{random_component} (e.g., sk_a1b2c3_d4e5f6g7...)
  • SHA256 hashed: Only the hash is stored in the database
  • Constant-time verification: Intended to reduce timing leakage; validate the deployed implementation
  • Audit logged: Key creation is logged with permissions and expiration
  • Organization must be active: Inactive organizations cannot create API keys

Security Best Practices:

  • Store keys in environment variables or secret managers (never in code)
  • Use different keys for development, staging, and production
  • Set expiration dates for temporary or CI/CD keys
  • Rotate keys regularly (e.g., every 90 days)
  • Delete unused keys immediately
  • Monitor last_used_at to detect unused keys

Example: Store Key Securely

# Environment variable (recommended)
export SSO_API_KEY="sk_a1b2c3_d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2"

# Use in application
curl -X GET https://sso.example.com/api/service/users \
  -H "X-Api-Key: $SSO_API_KEY"

GET /api/organizations/:org_slug/services/:service_slug/api-keys

List all API keys for a service.

Permissions: Owner, Admin, or Member

Headers:

Header Value
Authorization Bearer {jwt}

Path Parameters:

Parameter Type Description
org_slug string Organization slug
service_slug string Service slug

Query Parameters:

Parameter Type Required Description
limit integer No Maximum number of keys to return (default: 50, max: 100)
offset integer No Number of keys to skip for pagination (default: 0)

Example Request:

curl -X GET "https://sso.example.com/api/organizations/acme-corp/services/main-app/api-keys?limit=50&offset=0" \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Example Response (200 OK):

{
  "api_keys": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "service_id": "service-uuid",
      "name": "Production Backend Server",
      "prefix": "sk_a1b2c3",
      "permissions": ["read:users", "read:subscriptions"],
      "last_used_at": "2025-01-20T14:22:00Z",
      "expires_at": "2025-04-15T10:30:00Z",
      "created_at": "2025-01-15T10:30:00Z",
      "created_by": "user-uuid"
    },
    {
      "id": "another-key-uuid",
      "service_id": "service-uuid",
      "name": "Analytics Dashboard",
      "prefix": "sk_d4e5f6",
      "permissions": ["read:analytics", "read:service"],
      "last_used_at": "2025-01-21T09:15:00Z",
      "expires_at": null,
      "created_at": "2025-01-16T11:00:00Z",
      "created_by": "another-user-uuid"
    },
    {
      "id": "expired-key-uuid",
      "service_id": "service-uuid",
      "name": "Deprecated Integration",
      "prefix": "sk_g7h8i9",
      "permissions": ["read:users"],
      "last_used_at": "2024-12-01T10:00:00Z",
      "expires_at": "2025-01-01T00:00:00Z",
      "created_at": "2024-10-01T10:00:00Z",
      "created_by": "user-uuid"
    }
  ],
  "total": 3
}

Error Responses:

  • 401 Unauthorized: Invalid or missing JWT
  • 403 Forbidden: User is not a member, or organization not active
  • 404 Not Found: Organization or service not found
  • 500 Internal Server Error: Database error

Notes:

  • All members can view API keys (but not the actual key value)
  • last_used_at shows when the key was last used (null if never used)
  • expires_at is null for keys with no expiration
  • Expired keys are still returned (check expires_at on client side)
  • Use last_used_at to identify inactive keys for cleanup
  • Pagination supported via limit and offset

Example: Identify Unused Keys

// Filter keys not used in the last 90 days
const ninetyDaysAgo = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000);
const unusedKeys = apiKeys.filter(key =>
  !key.last_used_at || new Date(key.last_used_at) < ninetyDaysAgo
);

GET /api/organizations/:org_slug/services/:service_slug/api-keys/:api_key_id

Get details for a specific API key.

Permissions: Owner, Admin, or Member

Headers:

Header Value
Authorization Bearer {jwt}

Path Parameters:

Parameter Type Description
org_slug string Organization slug
service_slug string Service slug
api_key_id string API key ID (UUID)

Example Request:

curl -X GET https://sso.example.com/api/organizations/acme-corp/services/main-app/api-keys/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Example Response (200 OK):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "service_id": "service-uuid",
  "name": "Production Backend Server",
  "prefix": "sk_a1b2c3",
  "permissions": ["read:users", "read:subscriptions", "read:analytics"],
  "last_used_at": "2025-01-20T14:22:00Z",
  "expires_at": "2025-04-15T10:30:00Z",
  "created_at": "2025-01-15T10:30:00Z",
  "created_by": "user-uuid"
}

Error Responses:

  • 401 Unauthorized: Invalid or missing JWT
  • 403 Forbidden: User is not a member, or organization not active
  • 404 Not Found: Organization, service, or API key not found
  • 500 Internal Server Error: Database error

Notes:

  • Full key value is never returned (only available during creation)
  • Use this endpoint to check key details and permissions
  • Verify expires_at to detect expired keys
  • Check last_used_at to monitor key activity

DELETE /api/organizations/:org_slug/services/:service_slug/api-keys/:api_key_id

Delete an API key immediately.

Permissions: Owner or Admin

Headers:

Header Value
Authorization Bearer {jwt}

Path Parameters:

Parameter Type Description
org_slug string Organization slug
service_slug string Service slug
api_key_id string API key ID (UUID)

Example Request:

curl -X DELETE https://sso.example.com/api/organizations/acme-corp/services/main-app/api-keys/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Example Response (204 No Content):

No response body.

Error Responses:

  • 401 Unauthorized: Invalid or missing JWT
  • 403 Forbidden: User is not an owner or admin, or organization not active
  • 404 Not Found: Organization, service, or API key not found
  • 500 Internal Server Error: Database error

Important Warnings:

  • Immediate revocation: Services using this key will lose access immediately
  • Cannot be undone: Deleted keys cannot be recovered
  • No grace period: Active requests using the key will fail instantly
  • Audit logged: Deletion is logged with key name and actor

Best Practices:

  • Notify relevant teams before deleting keys used in production
  • Update services to use new keys before deleting old ones
  • Consider setting expiration dates instead of manual deletion for temporary keys
  • Delete keys immediately if compromised

Example: Key Rotation Workflow

# 1. Create new API key
NEW_KEY=$(curl -X POST .../api-keys -d '{"name":"New Key","permissions":[...]}' | jq -r '.key')

# 2. Update service configuration with new key
export SSO_API_KEY="$NEW_KEY"

# 3. Test new key
curl -X GET .../api/service/users -H "X-Api-Key: $SSO_API_KEY"

# 4. Delete old key
curl -X DELETE .../api-keys/old-key-uuid

# 5. Store new key securely
echo "$NEW_KEY" | vault write secret/sso-api-key value=-