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:
- The API Extra is active.
Configure → Extras → API Extra → Activate. The Extra registersapi_authenticate()and the API key tables. - The master
apisetting is on.Configure → Settings → API → Enabled. Without this, the API returns errors regardless of key validity. - The user has a generated API key.
Configure → User Management → [user] → Generate API Key(admin-side) orMy 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:
- The client sends an HTTP request to
/api/v2/with theX-API-KEY: <64-char-key>header. - SimpleRisk extracts the key from the header.
api_authenticate()hashes the key and looks it up in theapi_keystable.- On match, the corresponding
user_idis loaded; the user's permissions and team membership are loaded into the request context. - The endpoint handler runs as that user; permission and team filters apply normally.
- 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:
Recommended: X-API-KEY header
curl -H "X-API-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=
Works but exposes the key to URL logging surfaces. Avoid in production.
Alternative: key POST body field
curl -X POST -d '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.
Use the header. The other forms exist for compatibility.
Key generation
Keys are 64 random characters. Generate via:
- Admin path:
Configure → User Management → [user] → API Key → Generate 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:
- Generate the new key.
- Update the secrets manager / configuration with the new key.
- Update the integration to read the new key.
- 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:
- Open the user's record (admin or self-service).
- Click Remove API Key.
- The
api_keysrow is deleted; the user no longer has an active key. - 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 hash, not the plaintext. The hashing uses generateHash() with the per-install api_salt setting (a per-install random value created at install time). Because only the hash is stored:
- 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-KEYheader 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 Configure → Extras → API Extra.
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_saltvalues, 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/jsonheader. 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
- API Overview
- Permissions and the API
- API Key Management
- The Permission Model
- Using the API for Integrations
Reference
- Authentication method: Per-user API key.
- Recommended header:
X-API-KEY: <64-char-key>. - Alternative forms:
key=URL query parameter;key=POST body field (less preferred). - Implementing files:
simplerisk/extras/api/index.php(api_authenticate(),set_api_key(),get_user_api_key(),remove_api_key());simplerisk/api/v2/index.php(the v2 router that calls the auth function). - Database tables:
api_keys(user_idPK,api_key_hashVARCHAR(256)). config_settingskeys:api(master API toggle);api_salt(per-install hashing salt).- Key generation:
generate_token(64)produces the plaintext;generateHash($token)produces the stored hash. Plaintext shown once at generation. - External dependencies: None beyond the API Extra and the master
apisetting.