Skip to content
English
  • There are no suggestions because the search field is empty.

08.02 Authentication and API Keys

API requests authenticate with a per-user API key passed as the X-API-KEY header. Each user has at most one active key; keys are 64 random characters; the plaintext is shown once at generation. The master api setting must be on, and the API Extra must be active. Rotation invalidates the previous key immediately.

Why this matters

Authentication is the gate every API request passes through. Get it wrong and either nothing works (every request returns 401) or everything works for the wrong identity (the integration acts as the wrong user, with the wrong permissions, attributing the wrong activity in the audit log). Getting it right is mostly mechanical: generate the key, store it securely, pass it on every request.

This article covers the request-time mechanics. For the lifecycle of generating, rotating, and revoking keys, see API Key Management. For the broader API context, see API Overview.

Prerequisites

For API requests to authenticate successfully, three things must be true:

  1. The API Extra is active. Settings cog → Settings Hub → Extras chip → API Extra tile → Activate. The Extra turns on API authentication and the key-management screens.
  2. The master api setting is on. Settings cog → Settings Hub → Security tile → check Enabled under the API section. Without this, the API returns errors regardless of key validity.
  3. The user has a generated API key. Settings cog → Settings Hub → User Management → [user] → Generate API Key (admin-side) or My Account → API Keys (user self-service).

Verify all three before troubleshooting authentication failures.

The authentication flow

Each API request is authenticated independently — there's no session establishment for API calls. The flow:

  1. The client sends an HTTP request to /api/v2/{endpoint} with the X-API-KEY: <64-char-key> header.
  2. SimpleRisk extracts the key from the header.
  3. SimpleRisk hashes the key it received and looks for a match among the stored key hashes.
  4. On match, the corresponding user_id is loaded; the user's permissions and team membership are loaded into the request context.
  5. The endpoint handler runs as that user; permission and team filters apply normally.
  6. On no match, the request returns 401 Unauthorized.

Because each request is independently authenticated, the API has no notion of "session timeout" — keys remain valid until rotated or revoked. This is by design (integrations need long-lived credentials) but means a leaked key can be used until someone notices and rotates.

Passing the key

The API accepts the key in three forms, in this preference order. Only the header is always available; the two URL-based forms are gated by a security setting that ships off (see each form below).

Recommended: X-API-KEY header

curl -H "X-API-KEY: {key}" \
     -H "Accept: application/json" \
     https://your-simplerisk.example.com/api/v2/risks

This is the recommended pattern. Headers don't end up in URL query-string logs (web server access logs, proxy logs, browser history if anyone accidentally pastes the URL into a browser).

Alternative: key URL query parameter

curl https://your-simplerisk.example.com/api/v2/risks?key={key}

Works but exposes the key to URL logging surfaces. Avoid in production. On a fresh install this form is rejected outright: the Allow API Key in URL or POST Body setting under Settings → Security is off by default, and only an administrator who turns it on accepts the key anywhere but the header. Installs that upgrade from an earlier release keep the URL key working so older integrations don't break — but the right move is to migrate those integrations to the header and leave the setting off.

Alternative: key POST body field

curl -X POST -d 'key={key}&subject=My+Risk' \
     https://your-simplerisk.example.com/api/v2/risks/submit

Works for POST endpoints. The body isn't logged in standard access logs (the URL is); slightly safer than the query parameter, less safe than the header. The same Allow API Key in URL or POST Body setting gates this form too — with it off (the default), a key in the POST body is rejected exactly like one in the query string.

Use the header. The other forms exist for compatibility, and on a default install they're switched off.

Key generation

Keys are 64 random characters. Generate via:

  • Admin path: Settings cog → Settings Hub → User Management → [user] → API KeyGenerate API Key. Recommended for integration users.
  • User self-service: My Account → API Keys → Generate API Key. For users managing their own keys.

The generated key is shown once on the page. Copy it immediately; the database stores only the hash, so once you leave the page there's no "show me the key again" path. If lost, generate a new one (which invalidates any previous key).

For the full generation workflow including the per-user-creation pattern for integration accounts, see API Key Management.

Key rotation

Generating a new key for a user invalidates the previous key immediately. To rotate:

  1. Generate the new key.
  2. Update the secrets manager / configuration with the new key.
  3. Update the integration to read the new key.
  4. Confirm the integration is working with the new key.

Because rotation is immediate, there's no "old and new keys both work for a transition window." If your integration reads the key from a configuration file at startup, restart the integration after updating the file; if it reads at every request from a secrets manager, propagation is near-instantaneous.

Key revocation

To revoke a key without generating a new one:

  1. Open the user's record (admin or self-service).
  2. Click Remove API Key.
  3. The stored key is deleted; the user no longer has an active key.
  4. Subsequent requests with that key return 401.

For terminated integrations: revoke the key and disable the user (so a forgotten key can't be reactivated by re-enabling the user). For lost keys (the user lost the credential): generate a new one, which implicitly invalidates the old one.

Storage of keys

SimpleRisk stores the key's hash, not the key. The hash is salted with a random value generated for your install at install time. Because only the hash is kept:

  • The plaintext can't be retrieved from the database.
  • The same key generated by two different SimpleRisk installs would produce different hashes (different api_salt), so installs aren't accidentally interchangeable.
  • The hashing algorithm makes brute-force lookup impractical even with database access.

For the integration side, store the plaintext in a secrets manager (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, 1Password, etc.). Don't store it in:

  • Source-controlled config files.
  • Environment variables that bleed into application logs.
  • Plaintext files on shared file systems.
  • Chat threads, email, or ticket comments.

Common errors

401 Unauthorized

Possible causes:

  • The X-API-KEY header isn't being sent (check curl arguments, HTTP client configuration).
  • The key is wrong (typo, truncated paste, wrong key for the wrong environment).
  • The key has been rotated (a newer key exists; the old one is invalid).
  • The key has been revoked.

403 Forbidden

The key is valid, but the user the key belongs to doesn't have permission for the requested operation. Check the user's permissions and team membership; not an authentication issue.

Connection timeout / refused

Network-level: SimpleRisk is unreachable from the client. Check the URL, the network path, and whether SimpleRisk is running.

{"status_code": 503, "status_message": "API is disabled"}

The master api toggle is off. Enable it in the SimpleRisk admin settings.

{"status_code": 503, "status_message": "API extra not installed"}

The API Extra is not active. Activate it in Settings cog → Settings Hub → Extras chip → API Extra tile.

Common pitfalls

A handful of patterns recur with API authentication.

  • Hardcoding the key in source. Use environment variables or a secrets manager.

  • Sharing a key across multiple integrations. Rotation affects them all; revocation breaks both; audit-trail attribution is ambiguous. One user per integration; one key per user.

  • Generating a key against an admin account. The key inherits admin permissions — anything goes. Use a dedicated, least-privileged integration user.

  • Forgetting that key changes don't propagate to existing requests. A key revoked mid-request doesn't kill in-flight requests; subsequent requests fail.

  • Using the same key in dev, staging, and production. They'd presumably be different installs with different api_salt values, so the same plaintext key would hash differently. But operationally: separate keys per environment.

  • Putting the key in URLs that get logged. Use the header. Always.

  • Treating 401 as "API is broken." It usually means the key is wrong or expired. Verify the key.

  • Treating 403 as "API is broken." It usually means the user lacks permission for the operation. Check the user's permissions in SimpleRisk.

  • Skipping the Accept: application/json header. The API generally returns JSON regardless, but the explicit header prevents content negotiation surprises.

  • Building production integrations against the unversioned /api/ URL. Always use /api/v2/ explicitly.

Related