Skip to main content
View as Markdown

API Keys

A session cookie suits a browser and nothing else. When a script, a CI job or a service has to call your instance, it needs a credential it can hold — and self-service API keys are that credential: a signed-in user mints their own key, presents it in the x-api-key header, and revokes it when the job is done.

Keys are off by default. One boolean turns them on.

app.yaml
auth:
  strategies:
    - type: emailAndPassword
  apiKeys: true
Property Description
apiKeys Boolean. true mounts the /api/auth/api-key/* endpoints and makes x-api-key a valid credential on every auth-gated route. Omitted (default) is off.

When the option is absent the surface does not acknowledge its own existence: /api/auth/api-key/* answers 404, and an x-api-key header is simply ignored.

Authenticating with a key

Present the key in the x-api-key header. It works on every auth-gated /api/* route, from any client — no cookie, no sign-in round-trip.

>_ terminal
curl -H "x-api-key: $SOVRIUM_API_KEY" \
  http://localhost:3000/api/tables/notes/records

Minting, listing and revoking

Four endpoints, mounted under /api/auth/api-key/. Each is scoped to the calling session: a user sees and manages their own keys and nobody else's.

Method Path Body / query Purpose
POST /api/auth/api-key/create { "name": "CI deploy" } Mint a key. The response carries the plaintext value once.
GET /api/auth/api-key/list List the caller's keys (metadata only).
GET /api/auth/api-key/get ?id=<keyId> Read one of the caller's keys back by id.
POST /api/auth/api-key/delete { "keyId": "<keyId>" } Revoke a key.

Mint one from an authenticated browser session:

>_ terminal
curl -X POST http://localhost:3000/api/auth/api-key/create \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{ "name": "Nightly backup job" }'
app.json
{
  "id": "aK9tZ...",
  "name": "Nightly backup job",
  "key": "sk_live_9f2c...",
  "createdAt": "2026-08-26T09:14:22.000Z"
}

key is the only thing you have to keep. Store it wherever the job reads its secrets from.

Revocation is a deletion, not a flag: the row is removed, and the identical request that succeeded a moment earlier answers 401.

Give each key the name of the job that will carry it. Names are how you tell, six months later, which key belongs to the CI pipeline and which to the reporting script you decommissioned.

What a key is allowed to do

A key carries the role of the user who minted it — resolved live, on every request, not frozen at creation.

  • A member's key can do exactly what that member can do. It can never do more.
  • Promote or demote the owner and their existing keys follow immediately. Demoting a user narrows every key they hold, without invalidating any of them.
  • A caller cannot widen their own key. Supplying a permissions payload to create does not produce an escalated key — the grant is a function of who asked, never of what they asked for.

This is why a key needs no permission configuration of its own: it is a second way to present an identity you already have, not a new identity.

Banning suspends a key; unbanning restores it

Banning a user stops their keys authenticating, immediately and on every route. The key row survives untouched, so lifting the ban restores the same keys — a ban suspends a credential rather than destroying it, which matters because Better Auth clears a temporary ban on its own once it expires.

Sovrium sets no expiry, so a key stays valid until it is revoked or its owner is banned. Treat one as you would a password: scope it to a job, store it in a secret manager, and revoke it when the job ends.

The console page

Signed-in operators manage their own keys at /_admin/api-keys — mint, copy once, revoke — beside the other pages about their own account in the admin dashboard.

The console sits behind the usual admin-only /_admin/* gate, but the API does not: any signed-in user can mint and use keys through /api/auth/api-key/*. A member who never sees the dashboard can still hold a key and authenticate a script with it.

Not the same thing as a Connection

Two features in Sovrium involve something called an API key, and they point in opposite directions.

API keys (this page) Connections
Direction Inbound — credentials Sovrium issues Outbound — credentials Sovrium presents
Who is authenticated A caller, to your instance Your instance, to a third-party service
Where it is declared auth.apiKeys app.connections[]
Where it is managed /_admin/api-keys /_admin/connections

A Connection of type: apiKey holds somebody else's key so an automation can call their API. This page is about the keys your own instance issues so somebody can call yours. Turning one on says nothing about the other.

Last updated August 28, 2026

This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.

Built with Sovrium