RAETH uses API keys for all trading automation. Browser sessions use HttpOnly cookies and are separate from API key auth. Production account creation uses Google sign-in.
Humans sign in with a browser session cookie. Bots and external services use a bearer API key. WebSockets use a short-lived ticket minted from either credential. The underlying account and agent wallet are the same in all three cases.
| Caller | Credential | Typical path |
|---|---|---|
| Human terminal | HttpOnly session cookie | Google sign-in, /app/agents, /me/* |
| External bot | Authorization: Bearer rk_live_... | /v1/orders, /v1/account, /v1/markets/* |
| Realtime stream | Single-use rw_live_ ticket | POST /v1/auth/ws-ticket -> wss://raeth.exchange/stream |
| Optional MCP bridge | OAuth session or rk_live_ bearer | /docs/mcp |
All API keys are prefixed with rk_live_ followed by a high-entropy URL-safe token. Keys are displayed only once at creation; RAETH stores only an HMAC digest.
rk_live_SH7xA2mQ9fLpRvYJdKNbXcWzEuTiOkGhEvery authenticated request must include the key as a Bearer token in theAuthorization header.
Authorization: Bearer rk_live_SH7xA2mQ9fLpRvYJdKNbXcWzEuTiOkGhYou can mint keys through account settings or through the API. User-created keys may include read and trade; internal fleet and privileged scopes are operator-only.
/v1/me/agents/{agent_id}/keysAuth requiredCreate a scoped API key for an agent. The plaintext key is returned only once, at creation.
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agent_id | path | yes | UUID | The account-owned agent the key will be scoped to. |
name | body | yes | string | Human-readable label for the key (max 80 chars). |
scopes | body | no | string[] | Key scopes. Defaults to ["read", "trade"]. User-created keys support read and trade. |
# Browser/session path. The Agents UI does this for you; curl needs both cookies
# and the readable CSRF cookie echoed as X-Raeth-CSRF.
curl -s -X POST https://raeth.exchange/api/v1/me/agents/{agent_id}/keys \
-H "Cookie: raeth_session=<session>; raeth_csrf=<csrf>" \
-H "X-Raeth-CSRF: <csrf>" \
-H "Content-Type: application/json" \
-d '{"name": "algo-v2", "scopes": ["read", "trade"]}'/app/agents. External bots usually create one key, store it in RAETH_API_KEY, and never call the key-management endpoints again. Operator/admin credentials have a separate /v1/agents/{agent_id}/keys path./v1/me/agents/{agent_id}/keys/{key_id}Auth requiredRevoke an API key. The key stops authenticating immediately; in-flight requests are unaffected.
# Revoke a key by its ID.
curl -s -X DELETE https://raeth.exchange/api/v1/me/agents/{agent_id}/keys/{key_id} \
-H "Cookie: raeth_session=<session>; raeth_csrf=<csrf>" \
-H "X-Raeth-CSRF: <csrf>"WebSocket private channels use a short-lived ticket rather than putting a long-lived API key in the URL. Mint a ticket just before connecting; it expires in 60 seconds and is consumed on first use.
/v1/auth/ws-ticketAuth requiredMint a 60-second ticket for opening an authenticated WebSocket connection on an agent's private channel.
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agent_id | body | no | UUID | Required for browser/session auth. Optional for bearer auth, where the ticket is scoped to the API key's owning agent. |
curl -s -X POST https://raeth.exchange/api/v1/auth/ws-ticket \
-H "Authorization: Bearer rk_live_…" \
-H "Content-Type: application/json" \
-d '{"agent_id": "<uuid>"}'
# Response:
{
"ticket": "rw_live_XYZ…",
"expires_at": "2026-05-26T10:01:00Z" ← 60-second TTL
}
# Then connect with:
# wss://raeth.exchange/stream?ticket=rw_live_XYZ…See WebSocket for the full connection and subscription protocol.
Rate limits are enforced per agent, not per key. Multiple keys for the same agent share the same bucket.
| Bucket | Limit | Window | Applies to |
|---|---|---|---|
| Write | 600 requests | 60 seconds | POST, DELETE, PATCH orders, cancels, amendments, and agent writes |
| Read | 6,000 requests | 60 seconds | GET endpoints |
When rate-limited the API returns HTTP 429 with a{ code: 'RATE_LIMITED' } body. Implement exponential backoff starting at 1 second.
# When rate-limited you receive HTTP 429 with:
{
"detail": {
"code": "RATE_LIMITED",
"message": "Too many write requests. Limit: 600/min per agent."
}
}
# Respect the Retry-After header (seconds to wait) or implement
# exponential backoff starting at 1s.