Skip to main content
POST
Create API Key
Mint a new axon_sk_* API key for an organization. The server generates the plaintext, HMAC-hashes it with the AXON_API_KEY_HASH_PEPPER (the same hash function verifyDatabaseApiKey uses on the read side), stores only the hash in api_keys, and returns the plaintext exactly once.
This route replaces the dashboard’s previous client-side hashing path, which was broken because the dashboard and the verifier used different hash functions. All new keys — dashboard-issued or CLI-issued — now flow through POST /v1/api-keys so they are verifiable on the read side.

Authentication

This is an operator-surface route. Accepted callers:
  • A Supabase JWT with owner or admin membership on organization_id
  • An org-wide tenant API key with write scope on the same organization
  • A wallet-bound tenant API key with write scope, but only when minting another key for that same wallet
  • The static operator key

Request Body

Response

Success (201 Created)

The plaintext field is returned exactly once and is never logged. Store it immediately in a secret manager (or in ~/.axon/operator.json for @axon402/cli auth login). Lost the key? Rotate: create a new one and deactivate the old one.

Response Fields

Errors

400 VALIDATION_ERROR

Returned when:
  • organization_id is missing or not a valid UUID
  • name is missing or empty
  • scopes is present but empty
  • wallet_id is present but belongs to a different organization

403 REJECTED_POLICY

The caller is authenticated but cannot mint the requested key. Common causes:
  • wallet-bound key trying to mint an org-wide key
  • wallet-bound key trying to mint a key for a different wallet
  • caller lacks write authority on organization_id

404 NOT_FOUND

wallet_id was supplied but does not exist (or the caller cannot see it).

Hash function

The server stores HMAC-SHA256(plaintext, AXON_API_KEY_HASH_PEPPER) — never the plaintext itself. The pepper lives in Cloud Run’s Secret Manager and is not accessible from the database. A DB leak alone is not enough to reverse or brute-force a stored key. verifyDatabaseApiKey (the function that runs on every authenticated request) uses the same HMAC function, which is why all new keys must be issued through this server-side route — client-side hashing with a different function would produce unverifiable keys (the bug that motivated this route’s creation, see CHANGELOG 2026-04-08).

See also

  • @axon402/cli — the primary consumer of org-wide tenant keys
  • GET /v1/me — introspect which org + scopes a key resolves to
  • Authentication — overall auth model for the Decision API