Skip to content

Authentication.

How Agent API keys work, security model, and authentication flow.

Jul 21, 2026

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:

  1. Extract the key from the X-Agent-Key header
  2. Detect the key role from the prefix (rl_admin_, rl_agent_, rl_member_)
  3. Lookup candidate keys by prefix (indexed database query, fast)
  4. Verify the raw key against the stored bcrypt hash
  5. Check the key is active and not expired
  6. Check the key's owner account is active
  7. 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-10 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 platform hashes the rest with bcrypt. Even with database access, nobody can reconstruct the key.

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

The dashboard shows the full key once, at creation. After you close the dialog, only the prefix remains visible. There is no way to retrieve the full key. If you lose it, create a new one.

Bcrypt hashing

The platform stores keys as bcrypt hashes, the same algorithm it uses for passwords. This means:

  • Database leaks don't expose keys, and 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 platform enforces the limit per key in 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 their requests:

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 a key goes over the limit, 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 have an expiration date. Authentication rejects expired keys. Use this for temporary integrations or scheduled key rotation.

Owner activity check

Even a valid key fails when its owner account is inactive. When an admin disables a partner account, every key that partner owns stops working at once.

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 is no longer active
AUTH_KEY_EXPIRED 401 Key has passed its expiration date
AUTH_OWNER_INACTIVE 403 The owner account is inactive
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: grant only what the integration needs
Set expiration dates Limit blast radius of compromised keys
Rotate keys on a schedule 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 travels in a request header. Unencrypted connections expose it

Cookies on this site.

Google Analytics runs only if you allow it. Cookie policy