What are API Keys?
API keys are authentication credentials that grant programmatic access to the ActumX API. Each key:
- Uniquely identifies your requests
- Is associated with your user account
- Can be named for easy identification
- Can be revoked at any time
- Tracks usage via
lastUsedAt timestamp
API keys use bearer token authentication via the Authorization header.
API Key Structure
An API key has the following properties:
interface ApiKey {
id: string; // Unique key ID (prefixed with "key_")
userId: string; // Your user ID
name: string; // Human-readable name (2-80 characters)
keyPrefix: string; // First 14 chars of the key (for identification)
keyHash: string; // Hashed key (for secure storage)
revokedAt: string | null; // Revocation timestamp (null if active)
lastUsedAt: string | null; // Last usage timestamp
createdAt: string; // ISO 8601 creation timestamp
}
Database Schema
API keys are stored in the api_keys table:
CREATE TABLE api_keys (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
key_prefix TEXT NOT NULL,
key_hash TEXT NOT NULL UNIQUE,
revoked_at TEXT,
last_used_at TEXT,
created_at TEXT NOT NULL
);
CREATE INDEX idx_api_keys_user_id ON api_keys(user_id);
Source Reference: /home/daytona/workspace/source/api/src/db/schema.ts:5-16
How API Keys Work
Key Generation
When you create an API key:
- A cryptographically secure random key is generated
- The first 14 characters are stored as
keyPrefix for identification
- The full key is hashed using SHA-256 and stored as
keyHash
- The raw key is returned only once
import { hashSecret, newApiKey } from "../../lib/crypto";
// Generate new API key (source: service.ts:39-42)
const rawKey = newApiKey();
const keyPrefix = rawKey.slice(0, 14);
const keyHash = hashSecret(rawKey);
The raw API key is shown only once during creation. Store it securely - ActumX cannot recover lost keys.
API keys follow this format:
actumx_1234567890abcdefghijklmnopqrstuvwxyz
^^^^^^ ^^^^^^^^^
prefix random
- Prefix:
actumx_ + 14 random characters (stored for display)
- Full Length: Approximately 40-50 characters
- Encoding: Alphanumeric characters
Authentication Flow
- Client Request: Include API key in the
Authorization header
curl https://api.actumx.com/api/v1/agents \
-H "Authorization: Bearer actumx_abc123xyz..."
- Server Validation: ActumX hashes the provided key and looks up the hash
// Validate API key (conceptual)
const providedHash = hashSecret(providedKey);
const apiKey = await db.query.apiKeys.findFirst({
where: eq(apiKeys.keyHash, providedHash)
});
if (!apiKey || apiKey.revokedAt) {
return 401; // Unauthorized
}
-
Request Processing: If valid and not revoked, the request proceeds
-
Usage Tracking: The
lastUsedAt timestamp is updated
// Update last used timestamp (source: x402/service.ts:425-427)
await db
.update(apiKeys)
.set({ lastUsedAt: timestamp })
.where(eq(apiKeys.id, apiKey.id));
API Key Lifecycle
1. Creation
Create an API key via the API or dashboard:
curl -X POST https://api.actumx.com/api/v1/api-keys \
-H "Cookie: your_session_cookie" \
-H "Content-Type: application/json" \
-d '{
"name": "Production Key"
}'
Response:
{
"apiKeyId": "key_abc123",
"apiKey": "actumx_full_key_here",
"keyPrefix": "actumx_abc123",
"warning": "Store this key now. It is shown only once."
}
API key creation requires authentication via session cookie (web dashboard) or existing API key.
2. Usage
Use your API key to authenticate all requests:
# List agents
curl https://api.actumx.com/api/v1/agents \
-H "Authorization: Bearer actumx_your_key"
# Create x402 transaction
curl https://api.actumx.com/api/v1/x402/quote \
-H "Authorization: Bearer actumx_your_key"
# Top up credits
curl -X POST https://api.actumx.com/api/v1/billing/top-up \
-H "Cookie: your_session" \
-d '{"amountCents": 1000}'
3. Monitoring
List all your API keys to monitor usage:
curl https://api.actumx.com/api/v1/api-keys \
-H "Cookie: your_session_cookie"
Response:
{
"keys": [
{
"id": "key_abc123",
"name": "Production Key",
"keyPrefix": "actumx_abc123",
"revokedAt": null,
"lastUsedAt": "2026-03-03T22:30:00Z",
"createdAt": "2026-03-01T10:00:00Z"
},
{
"id": "key_xyz789",
"name": "Staging Key",
"keyPrefix": "actumx_xyz789",
"revokedAt": "2026-03-02T15:00:00Z",
"lastUsedAt": "2026-03-02T14:30:00Z",
"createdAt": "2026-02-15T09:00:00Z"
}
]
}
4. Revocation
Revoke a key if it’s compromised or no longer needed:
curl -X POST https://api.actumx.com/api/v1/api-keys/key_abc123/revoke \
-H "Cookie: your_session_cookie"
Response:
Revoked keys:
- Cannot authenticate new requests
- Remain visible in your key list (with
revokedAt timestamp)
- Cannot be un-revoked (create a new key instead)
Revoking a key immediately invalidates it. Any applications using that key will lose access.
Security Best Practices
Key Storage
DO:
- Store keys in environment variables
- Use secret management services (AWS Secrets Manager, HashiCorp Vault)
- Encrypt keys at rest in your application
DON’T:
- Commit keys to version control
- Hardcode keys in source code
- Share keys via email or chat
- Log keys in application logs
Key Rotation
- Create a new API key
- Update your application to use the new key
- Verify the new key works
- Revoke the old key
# Step 1: Create new key
curl -X POST https://api.actumx.com/api/v1/api-keys \
-d '{"name": "Production Key v2"}'
# Step 2: Update app environment
export ACTUMX_API_KEY="actumx_new_key"
# Step 3: Test
curl https://api.actumx.com/api/v1/agents \
-H "Authorization: Bearer $ACTUMX_API_KEY"
# Step 4: Revoke old key
curl -X POST https://api.actumx.com/api/v1/api-keys/key_old/revoke
Rotate API keys every 90 days or immediately if compromised.
Key Naming
Use descriptive names to track key usage:
Production Server
Staging Environment
CI/CD Pipeline
Local Development - John
Mobile App - iOS
This helps you identify which key to revoke if issues arise.
Least Privilege
Create separate keys for different environments:
# Production key (limited scope)
curl -X POST https://api.actumx.com/api/v1/api-keys \
-d '{"name": "Production - Read Only"}'
# Development key (full access)
curl -X POST https://api.actumx.com/api/v1/api-keys \
-d '{"name": "Development - Full Access"}'
ActumX currently doesn’t support key-level permissions, but using separate keys per environment aids in tracking and revocation.
Implementation Details
Service Layer
The API key service handles all key operations:
Key Functions:
list(request): Retrieve all API keys for authenticated user
create(request, payload): Generate new API key
revoke(request, id): Revoke an existing key
Source Reference: /home/daytona/workspace/source/api/src/modules/api-keys/service.ts
Validation
Key creation validates the name:
// Model validation (source: model.ts:4-6)
const createApiKeyBody = t.Object({
name: t.String({ minLength: 2, maxLength: 80 }),
});
Cryptographic Functions
ActumX uses custom crypto utilities:
// Generate random API key
export function newApiKey(): string {
// Returns cryptographically secure random string
// Format: "actumx_" + random characters
}
// Hash key for storage
export function hashSecret(secret: string): string {
// Returns SHA-256 hash of the key
}
Source Reference: /home/daytona/workspace/source/api/src/lib/crypto.ts
Context Service
The ApiKeyContextService extracts and validates keys from requests:
// Get authenticated API key from request
const apiKey = await ApiKeyContextService.getAuthenticatedApiKey(request);
if (!apiKey) {
return { statusCode: 401, body: { error: "missing_or_invalid_api_key" } };
}
Source Reference: /home/daytona/workspace/source/api/src/services/api-key-context.service.ts
Usage Tracking
API keys track two timestamps:
- createdAt: When the key was created (never changes)
- lastUsedAt: Most recent API request using this key
// Track usage (source: x402/service.ts:425-427)
await db
.update(apiKeys)
.set({ lastUsedAt: timestamp })
.where(eq(apiKeys.id, apiKey.id));
This helps you:
- Identify unused keys (revoke them)
- Detect unexpected usage patterns
- Audit key activity
Error Handling
Common API key errors:
| Error | HTTP Status | Cause | Solution |
|---|
unauthorized | 401 | Missing/invalid key | Provide valid API key in Authorization header |
missing_or_invalid_api_key | 401 | Key not found or revoked | Check key prefix and revocation status |
| Name validation error | 400 | Invalid name length | Use 2-80 characters |
Example Error Response
{
"error": "unauthorized"
}
Integration Examples
Node.js
const axios = require('axios');
const actumx = axios.create({
baseURL: 'https://api.actumx.com/api/v1',
headers: {
'Authorization': `Bearer ${process.env.ACTUMX_API_KEY}`
}
});
// List agents
const { data } = await actumx.get('/agents');
console.log(data.agents);
Python
import os
import requests
class ActumXClient:
def __init__(self):
self.api_key = os.environ['ACTUMX_API_KEY']
self.base_url = 'https://api.actumx.com/api/v1'
self.headers = {'Authorization': f'Bearer {self.api_key}'}
def list_agents(self):
response = requests.get(
f'{self.base_url}/agents',
headers=self.headers
)
return response.json()['agents']
client = ActumXClient()
agents = client.list_agents()
cURL
#!/bin/bash
API_KEY="actumx_your_key_here"
BASE_URL="https://api.actumx.com/api/v1"
# List agents
curl "$BASE_URL/agents" \
-H "Authorization: Bearer $API_KEY"
# Create agent
curl -X POST "$BASE_URL/agents" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "My Agent"}'
Best Practices Summary
- Never commit keys to version control
- Use environment variables for key storage
- Name keys descriptively (e.g., “Production API”, “Staging Bot”)
- Rotate keys every 90 days
- Revoke compromised keys immediately
- Create separate keys per environment
- Monitor
lastUsedAt to identify unused keys
- Store keys in secret management systems
Next Steps