Skip to main content

API Key Management

FieldValue
Document IDASCEND-SEC-001
Version1.0.0
Last UpdatedDecember 19, 2025
AuthorAscend Engineering Team
PublisherOW-KAI Technologies Inc.
ClassificationEnterprise Client Documentation
ComplianceSOC 2 CC6.1/CC6.2, PCI-DSS 7.1/8.3, HIPAA 164.312, NIST 800-53 AC-2/SI-4

Reading Time: 10 minutes | Skill Level: Intermediate

Overview

ASCEND provides enterprise-grade API key management for SDK authentication. Keys use SHA-256 hashing with salt, constant-time comparison, rate limiting, and complete audit trails.

Key Architecture

+---------------------------------------------------------------------------------+
| API KEY SECURITY ARCHITECTURE |
+---------------------------------------------------------------------------------+
| |
| KEY GENERATION |
| +-------------------------------------------------------------------------+ |
| | | |
| | 1. Generate 256-bit random key (secrets.token_urlsafe(32)) | |
| | 2. Add role prefix (owkai_admin_, owkai_user_) | |
| | 3. Generate 128-bit salt | |
| | 4. SHA-256 hash (key + salt) → key_hash | |
| | 5. Store: key_hash, salt, key_prefix (first 32 chars) | |
| | | |
| | NEVER stored: full plaintext key (shown once on generation) | |
| | | |
| +-------------------------------------------------------------------------+ |
| |
| KEY VERIFICATION |
| +-------------------------------------------------------------------------+ |
| | | |
| | 1. Extract prefix from provided key | |
| | 2. Lookup by prefix (may return multiple candidates) | |
| | 3. For each candidate: SHA-256(key + salt) == stored hash? | |
| | 4. Constant-time comparison (secrets.compare_digest) | |
| | 5. Check: is_active, expires_at, rate_limit | |
| | | |
| +-------------------------------------------------------------------------+ |
| |
+---------------------------------------------------------------------------------+

Generating API Keys

REST API

curl -X POST "https://pilot.owkai.app/api/keys/generate" \
-H "Authorization: Bearer <jwt_token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Production SDK Key",
"description": "Main production API key for agent actions",
"expires_in_days": 90,
"permissions": [
{"category": "agent", "actions": ["read", "write"]},
{"category": "action", "actions": ["submit", "read"]}
],
"rate_limit": {
"max_requests": 1000,
"window_seconds": 3600
}
}'

Response:

{
"success": true,
"api_key": "owkai_admin_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3",
"key_id": 123,
"key_prefix": "owkai_admin_a1b2c3d4e5f6g7h8",
"name": "Production SDK Key",
"expires_at": "2026-03-15T10:30:00Z",
"created_at": "2025-12-15T10:30:00Z",
"warning": "⚠️ Save this key now - you will NOT see it again!"
}

Permission Shorthand

# Use string shorthand for permissions
curl -X POST "https://pilot.owkai.app/api/keys/generate" \
-H "Authorization: Bearer <jwt_token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Read-Only Key",
"permissions": ["agent:read", "action:read", "analytics:read"]
}'

Using API Keys

Authorization Header

curl "https://pilot.owkai.app/api/v1/actions/submit" \
-H "Authorization: Bearer owkai_admin_a1b2c3d4..."

X-API-Key Header

curl "https://pilot.owkai.app/api/v1/actions/submit" \
-H "X-API-Key: owkai_admin_a1b2c3d4..."

SDK Integration

from ascend import AscendClient

# Initialize with API key
client = AscendClient(api_key="owkai_admin_a1b2c3d4...")

# Submit action
result = client.evaluate_action(
action_type="database.query",
resource="customers"
)

Listing API Keys

curl "https://pilot.owkai.app/api/keys/list?include_revoked=false&page=1" \
-H "Authorization: Bearer <jwt_token>"

Response:

{
"success": true,
"keys": [
{
"id": 123,
"name": "Production SDK Key",
"key_prefix": "owkai_admin_a1b2c3d4",
"is_active": true,
"expires_at": "2026-03-15T10:30:00Z",
"last_used_at": "2025-12-15T10:25:00Z",
"usage_count": 15420,
"created_at": "2025-12-15T10:30:00Z"
}
],
"total_count": 1,
"page": 1,
"page_size": 20
}

Revoking API Keys

curl -X DELETE "https://pilot.owkai.app/api/keys/123/revoke?reason=Security%20rotation" \
-H "Authorization: Bearer <jwt_token>"

Response:

{
"success": true,
"message": "API key revoked successfully",
"key_id": 123,
"revoked_at": "2025-12-15T10:35:00Z"
}

Usage Statistics

curl "https://pilot.owkai.app/api/keys/123/usage?limit=100" \
-H "Authorization: Bearer <jwt_token>"

Response:

{
"success": true,
"key_id": 123,
"key_prefix": "owkai_admin_a1b2c3d4",
"statistics": {
"total_requests": 15420,
"recent_requests": 100,
"success_rate": 98.5,
"last_used_at": "2025-12-15T10:25:00Z"
},
"recent_activity": [
{
"timestamp": "2025-12-15T10:25:00Z",
"endpoint": "/api/v1/actions/submit",
"method": "POST",
"status": 200,
"ip_address": "192.168.1.100",
"response_time_ms": 45
}
]
}

Rate Limiting

Default Limits

TierMax RequestsWindow
Standard1,0001 hour
Premium10,0001 hour
EnterpriseCustomCustom

Rate Limit Response

When rate limit is exceeded:

{
"detail": "Rate limit exceeded",
"retry_after": 3600
}

Headers:

HTTP/1.1 429 Too Many Requests
Retry-After: 3600

Configure Custom Limits

curl -X POST "https://pilot.owkai.app/api/keys/generate" \
-H "Authorization: Bearer <jwt_token>" \
-d '{
"name": "High-Volume Key",
"rate_limit": {
"max_requests": 10000,
"window_seconds": 3600
}
}'

Permissions

Permission Categories

CategoryActionsDescription
agentread, write, deleteAgent management
actionsubmit, read, approve, denyAction governance
analyticsreadView analytics
auditread, exportAudit log access
configread, writeConfiguration

Permission Examples

{
"permissions": [
{"category": "agent", "actions": ["read"]},
{"category": "action", "actions": ["submit", "read"]},
{"category": "analytics", "actions": ["read"]}
]
}

Read-Only Key

{
"permissions": ["agent:read", "action:read", "analytics:read"]
}

Full Access Key

{
"permissions": [
{"category": "agent", "actions": ["read", "write", "delete"]},
{"category": "action", "actions": ["submit", "read", "approve", "deny"]},
{"category": "analytics", "actions": ["read"]},
{"category": "audit", "actions": ["read", "export"]},
{"category": "config", "actions": ["read", "write"]}
]
}

Security Features

Constant-Time Comparison

# Source: dependencies_api_keys.py:107
# Prevents timing attacks
if secrets.compare_digest(candidate_hash, candidate.key_hash):
matched_key = candidate

RLS Context Setting

# Source: dependencies_api_keys.py:141
# Set organization context for row-level security
db.execute(
text("SET LOCAL app.current_organization_id = :org_id"),
{"org_id": str(api_key_org_id)}
)

Audit Logging

All API key operations are logged:

{
"event_type": "api_key_generated",
"actor_id": "admin@company.com",
"resource_type": "api_key",
"resource_id": 123,
"outcome": "success",
"metadata": {
"key_prefix": "owkai_admin_a1b2c3d4",
"key_name": "Production SDK Key",
"expires_at": "2026-03-15T10:30:00Z",
"permissions_count": 3
}
}

Data Model

# Source: models_api_keys.py:23
class ApiKey(Base):
"""Enterprise API key storage with cryptographic security."""

id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"))
organization_id = Column(Integer, ForeignKey("organizations.id"))

# Cryptographic storage (NEVER stores plaintext)
key_hash = Column(String(64)) # SHA-256 hash
key_prefix = Column(String(40)) # First 32 chars for lookup
salt = Column(String(32)) # Random salt

# Metadata
name = Column(String(255))
description = Column(Text)
is_active = Column(Boolean)
expires_at = Column(DateTime)

# Usage tracking
last_used_at = Column(DateTime)
usage_count = Column(BigInteger)

Best Practices

1. Rotate Keys Regularly

# Set expiration for automatic rotation reminders
{
"expires_in_days": 90
}

2. Use Minimal Permissions

# Only grant necessary permissions
{
"permissions": ["action:submit", "action:read"]
}

3. Monitor Usage

# Check for unusual patterns
usage = client.keys.get_usage(key_id)
if usage.success_rate < 90:
investigate_errors()

4. Revoke Unused Keys

# Revoke keys not used in 30 days
for key in client.keys.list():
if key.last_used_at < thirty_days_ago:
client.keys.revoke(key.id, reason="Unused key cleanup")

5. Store Keys Securely

# Use environment variables or secrets manager
import os
api_key = os.environ.get("ASCEND_API_KEY")

# Never commit keys to source control
# Never log full key values

Next Steps


Document Version: 1.0.0 | Last Updated: December 2025