← Docs

API keys

A real, standalone credential for server-to-server access — distinct from a user's own OAuth access token, and distinct from an Application's own client_secret. Requires Plan.api_keys (Growth and above) — checked at creation, and again on every authenticated request (a downgrade fails a previously-issued key closed, not open).

POST /api/v1/api-keys/
{ "name": "CI pipeline", "scope": "global:applications.view global:audit.view" }

The response includes the raw key — onhx_... — shown exactly once. Only its hash and a short prefix (shown everywhere else, e.g. onhx_a1B2c3D4…, enough to recognize a key without being able to reconstruct it) are ever stored.

Scope is narrowed, never widened

scope is a space-delimited list of real Permission.key values — the same vocabulary covered on Roles & permissions, never a separate one. What you request is narrowed at creation to the intersection of what you asked for and what you, the creating user, actually hold via a real Role — never wider, and fixed for that key's lifetime: there is no PATCH to widen an existing key later. Create a new one instead if you need more.

Authenticating with a key

A distinct scheme, not Bearer (that scheme belongs to an OAuth access token — see Sign-in methods):

Authorization: ApiKey onhx_...

A request authenticated this way resolves to the key's own creator as request.user (so ownership/membership checks keep working unmodified), but every Permission check is additionally narrowed to the key's own scope — the key can never do more than it was granted, even if its creator's Role gains broader Permissions afterward.

Rate limiting is real and per-key

Plan.api_rate_limit_per_minute caps requests per key per 60-second window (null means unlimited — Enterprise). Exceeding it returns 429. The counter is atomic (Redis INCR, TTL set only on the window's first request) — a steady stream of requests can never keep pushing the window forward and silently disable the limit.

Revoking

DELETE /api/v1/api-keys/{id}/ soft-deletes the key — the exact same raw key fails 401 immediately afterward. Both creation and revocation are real, logged Audit log events (api_key_created/api_key_revoked).