Govern
Documentation

Version Latest

API keys and service accounts

Create scoped API keys for scripts and CI in Neural Inverse Govern, understand how a key's scopes and its owner limit it, and use service accounts for non-human principals.

API keys are long-lived credentials for scripts, CI and integrations. Service accounts are non-human principals that keys can belong to, so automation does not depend on a person's account.

For applications that call models, use a gateway key instead; it can call the AI Gateway and nothing else. See Gateway keys and workload identity.

Create an API key

The console page is Access > API keys (Access keys on the hosted service). Through the API:

curl -sS -X POST "$KEYSTONE_URL/api/v1/organizations/$ORG_ID/api-keys" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"name": "CI pipeline", "scopes": ["audit:read"], "expires_at": "2027-01-31T00:00:00Z"}'
FieldMeaning
nameA name for the key
scopesPermissions in the resource:verb grammar, with resource:* and *:* wildcards
expires_atRFC 3339 expiry (optional)
  • The plaintext key is returned once, in the nia_<prefix>.<secret> format. Govern stores only an HMAC-SHA-256 digest keyed with the deployment's NI_AUTH_API_KEY_PEPPER.
  • The creator is always the caller. A created_by_user_id naming someone else is refused.
  • The scopes must be within the creator's own effective permissions in the organisation; a key created by another key is bounded by that key and cannot outlive it. A refused scope is audited as api_key.scope_denied.
  • Root sessions cannot create keys.
  • Clients send the key as Authorization: Bearer <key>.
RoutePermission
POST /api/v1/organizations/{id}/api-keysapikeys:create
GET /api/v1/organizations/{id}/api-keysapikeys:read (metadata only, never the secret)
GET /api/v1/organizations/{id}/api-keys/scope-reportapikeys:read
DELETE /api/v1/organizations/{id}/api-keys/{key_id}apikeys:delete (revokes the key, 204)

What a key can do

A key holds a permission only when both of these hold it, checked at every request:

  1. the key's own scopes, and
  2. the principal it acts for: its service account when it is linked to one, otherwise the person who created it, with that principal's current groups, project memberships and bindings.

Deny policies and ceilings that cover the principal also cover the key. So a person who is demoted loses, in their keys, what they lost themselves; a decision refused for this reason reports decided_by: api_key_owner.

A key whose owner is disabled, no longer a member, suspended or deleted holds nothing: authentication refuses it with 401 api_key_owner_inactive. Revoked and expired keys are refused at authentication, which is never cached.

Some actions refuse API keys outright and need a signed-in person, for example SSO test sign-ins and the first-party apps' audit client events.

Service accounts

Service accounts live in the directory. Each has an owner node, an owner user, a slug unique in the organisation, a display name, labels and a disabled state. Credentials (API keys and OAuth clients) stay in their own systems and are linked to the service account; each credential links to at most one service account.

RoutePurpose
GET, POST /api/v1/directory/service-accountsList and create
GET, PATCH, DELETE /api/v1/directory/service-accounts/{id}Read, change, delete
POST /api/v1/directory/service-accounts/{id}:disable, :enableDisable or enable
GET, POST /api/v1/directory/service-accounts/{id}/credentialsList and link credentials
DELETE /api/v1/directory/service-accounts/{id}/credentials/{credentialID}Unlink a credential

Reading needs directory.serviceaccounts:read; changing needs directory.serviceaccounts:manage, a high-risk permission because service accounts hold credentials. Service accounts can be members of nodes and groups and receive role bindings like people. Directory service accounts have no console page yet.

The older /api/v1/enterprise/service-accounts routes are partial and superseded: their tokens are accepted nowhere except their own validate route. Use directory service accounts.

Good practice

  • Give each integration its own key with the narrowest scopes it needs and an expiry.
  • Link keys used by automation to a service account, so they do not stop working, or keep a person's privileges, when that person leaves.
  • Revoke keys you no longer use. SCIM deprovisioning sets a leaver's keys inactive automatically; see Directory sync with SCIM.

Last reviewed 17 October 2026View as Markdown

API keys and service accounts · Docs · Neural Inverse