Authentication & API keys
How Orion authenticates requests — API keys for data, bearer tokens for account management, and the key lifecycle.
Orion uses two credential types:
- API keys authenticate every data request, sent in the
x-orion-api-keyheader. This is what you use to read positions, activities, and protocol data. - Bearer tokens (JWT) authenticate organization-management requests — reading your org, listing keys, revoking keys. You get them by logging in.
Data endpoints never accept a bearer token, and management endpoints never accept an API key.
API keys
Send your key on every data request:
curl https://query.orionhq.run/v1/users/GABC7XYZ.../positions \
-H "x-orion-api-key: sk-orion-...your-key..."A key value is shown exactly once, at creation. Orion stores only a hash — listing keys returns their metadata and prefix, never the full secret. If you lose a key, revoke it and mint a new one.
Create a key
POST /v1/org/api-keys creates an organization and its first key. It is
authorized by an x-orion-admin-token header rather than a bearer token.
curl -X POST https://query.orionhq.run/v1/org/api-keys \
-H "x-orion-admin-token: ...admin-token..." \
-H "content-type: application/json" \
-d '{
"organization_name": "Acme Analytics",
"organization_slug": "acme",
"key_name": "production",
"scopes": ["data:read"]
}'201 Created:
{
"organization_id": "org_a1b2c3",
"organization_slug": "acme",
"organization_reused": false,
"api_key_id": "key_9f8e7d",
"key_prefix": "sk-orion-9f8e",
"scopes": ["data:read"],
"key": "sk-orion-...full-secret-shown-once...",
"expires_at": null
}Store key immediately — it is the only time the full value appears.
| Field | Type | Notes |
|---|---|---|
organization_name | string | Required. Display name for the organization. |
organization_slug | string | Required. URL-safe identifier; reused if the org already exists. |
key_name | string | Required. Label for the key, shown in listings. |
scopes | string[] | Optional. Defaults to ["data:read"]. |
expires_at | string | null | Optional RFC 3339 expiry. Omit or null for no expiry. |
List keys
GET /v1/org/api-keys returns every key on the organization — prefixes and
metadata only, never raw values.
curl https://query.orionhq.run/v1/org/api-keys \
-H "authorization: Bearer <access_token>"[
{
"id": "key_9f8e7d",
"name": "production",
"prefix": "sk-orion-9f8e",
"scopes": ["data:read"],
"revoked": false,
"created_at": "2026-07-20T14:03:00Z",
"last_used_at": "2026-07-23T09:12:44Z",
"expires_at": null
}
]Revoke a key
DELETE /v1/org/api-keys/{id} revokes a key by its id. A revoked key is refused
on every subsequent request.
curl -X DELETE https://query.orionhq.run/v1/org/api-keys/key_9f8e7d \
-H "authorization: Bearer <access_token>"Returns 204 No Content on success, or 404 if the id is unknown or belongs to
another organization.
Bearer tokens
Management endpoints (GET /v1/org, listing and revoking keys) authenticate with
a JWT access token in the authorization header.
Log in
POST /v1/auth/login exchanges credentials for a token pair:
curl -X POST https://query.orionhq.run/v1/auth/login \
-H "content-type: application/json" \
-d '{ "email": "you@acme.com", "password": "..." }'{
"access_token": "eyJhbGciOi...",
"refresh_token": "eyJhbGciOi...",
"expires_in": 3600
}expires_in is the access token lifetime in seconds. Send the access token as
authorization: Bearer <access_token> on management requests.
Refresh
When the access token expires, exchange the refresh token for a fresh one with
POST /v1/auth/refresh — no need to log in again:
curl -X POST https://query.orionhq.run/v1/auth/refresh \
-H "content-type: application/json" \
-d '{ "refresh_token": "eyJhbGciOi..." }'{
"access_token": "eyJhbGciOi...",
"expires_in": 3600
}Errors
Authentication failures return the standard error envelope:
{
"error": {
"code": "unauthorized",
"message": "Missing or invalid API key"
},
"meta": { "...": "response metadata" }
}| Status | When |
|---|---|
401 | Missing, invalid, revoked, or expired credential. |
404 | Revoking a key id that doesn’t belong to your organization. |
429 | Rate limit for your organization exceeded. |