Authentication.
How Agent API keys work, security model, and authentication flow.
The Agent API uses long-lived API keys instead of login tokens. Each key is self-contained—it carries the identity, role, and permissions needed to authenticate a request.
How It Works
Every request must include the key in the X-Agent-Key header:
curl -X GET https://your-domain.com/api/agent/v1/health \
-H "X-Agent-Key: rl_agent_a8f3k2m1x9v4b7n2..."
The authentication flow:
- Extract the key from the
X-Agent-Keyheader - Detect the key role from the prefix (
rl_admin_,rl_agent_,rl_member_) - Lookup candidate keys by prefix (indexed database query—fast)
- Verify the raw key against the stored bcrypt hash
- Check the key is active and not expired
- Check the key's owner account is active
- Bind the key and owner to the request for downstream use
Key Format
Keys follow a predictable format:
rl_agent_a8f3k2m1x9v4b7n2p5q8r1t6w3y0z4d7f0h3j6l9m2o5r8u1w4z7c0e3...
└──────┘ └──────┘ └─────────────────────────────────────────────────────┘
role prefix random secret
| Component | Length | Description |
|---|---|---|
| Role prefix | 9-11 chars | rl_admin_, rl_agent_, rl_member_ |
| Key prefix | 8 chars | Unique identifier for database lookup |
| Random secret | 40 chars | Cryptographically random, bcrypt-hashed |
The prefix is the only part stored in plaintext. It enables fast lookups without scanning the entire table. The rest is hashed with bcrypt—even with database access, the key cannot be reconstructed.
Key Roles
Each key belongs to one owner type:
| Prefix | Role | Owner |
|---|---|---|
rl_admin_ |
Admin | Administrator account |
rl_agent_ |
Partner | Partner (business) account |
rl_member_ |
Member | Customer account |
All three key types are available.
Security Model
One-Time Display
When a key is created, the full key is displayed exactly once. After the dialog is closed, only the prefix is visible. There is no way to retrieve the full key—if lost, create a new one.
Bcrypt Hashing
Keys are stored as bcrypt hashes (the same algorithm used for passwords). This means:
- Database leaks don't expose keys
- Even administrators cannot see the full key
- Each key verification takes ~100ms (bcrypt's built-in load factor)
Rate Limiting
Each key has a configurable rate limit (requests per minute, default: 60). The limit is enforced per-key using a 1-minute fixed window — two keys owned by the same partner have separate limits.
Every response includes rate limit headers so clients can pace proactively:
X-RateLimit-Limit: 60 ← max requests per window
X-RateLimit-Remaining: 42 ← requests left in current window
X-RateLimit-Reset: 1741260000 ← Unix timestamp when window resets
When the limit is exceeded, the API returns HTTP 429 with an additional Retry-After header:
{
"error": true,
"code": "RATE_LIMITED",
"message": "Too many requests. Please slow down.",
"retry_strategy": "backoff",
"details": {
"limit": 60,
"window_seconds": 60,
"retry_after_seconds": 23
}
}
Expiration
Keys can optionally have an expiration date. Expired keys are rejected at authentication. This is useful for:
- Temporary integrations
- Time-limited API access
- Rotating keys on a schedule
Owner Activity Check
Even with a valid key, if the owner account is deactivated (e.g., partner account disabled by admin), the key is rejected. This ensures key revocation at the account level is immediate.
Authentication Errors
All authentication errors follow the standard error envelope:
| Error Code | HTTP Status | Meaning |
|---|---|---|
AUTH_MISSING_KEY |
401 | No X-Agent-Key header provided |
AUTH_INVALID_KEY |
401 | Key format is wrong, prefix not found, or hash doesn't match |
AUTH_KEY_REVOKED |
401 | Key exists but has been deactivated |
AUTH_KEY_EXPIRED |
401 | Key has passed its expiration date |
AUTH_OWNER_INACTIVE |
403 | The owner account is deactivated |
AUTH_ANONYMOUS_MEMBER |
403 | Anonymous members cannot use agent keys |
Example error response:
{
"error": true,
"code": "AUTH_KEY_REVOKED",
"message": "This agent key has been revoked.",
"retry_strategy": "no_retry"
}
See Error Handling for the full error envelope format.
Best Practices
| Practice | Why It Matters |
|---|---|
| Store keys in environment variables | Never hardcode keys in source code |
| Use the minimum required scopes | Least-privilege principle—only grant what's needed |
| Set expiration dates | Limit blast radius of compromised keys |
| Rotate keys periodically | Create a new key, update your integration, revoke the old one |
| Monitor the audit log | Watch for unexpected patterns in Activity Logs |
| Use HTTPS | The key is sent as a header—always use encrypted connections |
Related Topics
- Enabling & Managing Keys — Create and revoke keys in the dashboard
- Scopes & Permissions — What each key is allowed to do
- Error Handling — Standard error response format