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

08.03 Permissions and the API

API permissions are SimpleRisk user permissions. The API key inherits the user's role, direct grants, admin flag, and team membership. To restrict an integration's capabilities, restrict the user's permissions; to broaden them, broaden the user's. There's no separate API permission model.

Why this matters

Most production integration mistakes trace to a permission misunderstanding. The integration submits a risk and gets a 403; the developer thinks the API is broken; the actual cause is the integration user lacks submit_risks. The integration reads risks and gets back an empty list; the developer thinks something's wrong; the actual cause is team-membership filtering. Understanding how permissions interact with API requests prevents most of these wrong-track debugging sessions.

The model itself is simple: API permissions are user permissions. The key inherits everything the user has — role, direct grants, admin flag, team memberships. There's no separate "API can do X but not Y" layer. Restricting the integration means restricting the user account behind the key; broadening it means broadening the user.

This article covers how this plays out in practice. For the broader permission model, see The Permission Model.

How API requests resolve permissions

When a request hits an authenticated endpoint:

  1. The API key is extracted and the user identified (see Authentication and API Keys).
  2. The user's effective permissions are loaded — the union of role-derived permissions, direct per-user grants, and admin override.
  3. The user's team memberships are loaded.
  4. The endpoint handler executes the operation as that user. Permission checks (check_permission(...)) and team filters apply normally.

The same code path runs whether the user came in via the web UI or the API. There's no separate "API permission" branch.

What this means in practice

The integration user's role is the integration's role

If you create a user slack-bot with the role "Risk Submitter," the API integration acting as that user can do exactly what a Risk Submitter can do — submit risks, view risks, but not modify configuration or other users. Want the integration to be able to close risks too? Either add the close-risks permission to the Risk Submitter role (affecting every Risk Submitter, not just the integration) or grant the permission directly to the slack-bot user.

The pattern: create one SimpleRisk user per integration, assign the role that matches the integration's needs, add per-user permission grants for any integration-specific capabilities the role doesn't cover.

Team membership filters the integration's view

A user on the Engineering team only sees Engineering's records. The API enforces this — GET /api/v2/risks returns risks visible to the user, not all risks in the database.

If your integration needs to see risks across multiple teams (e.g., a centralized dashboard), the integration user needs team memberships covering those teams. For "see everything" integrations, either:

  • Add the user to every team, OR
  • Set the user's admin flag to 1 (which bypasses team filtering entirely, but is a much broader grant).

The admin-flag approach is convenient but dangerous — the integration can do anything in SimpleRisk, including modify other users' records and change critical configuration. Prefer adding the user to specific teams when possible.

Permission-denied responses

When the user lacks permission for an operation:

  • 403 Forbidden — the standard response for permission denial.
  • The response body includes a status_message indicating the denial.
  • The audit log records the attempt (object-level access denials are logged at warning level per the SimpleRisk log conventions).

The 403 isn't an authentication failure — the key is valid; the user just can't do the requested operation. Check the user's permissions in SimpleRisk's User Management UI.

Admin override

A user with admin = 1 bypasses all permission checks. The API key for an admin user can do anything: submit risks, modify users, deactivate Extras, change settings, generate other users' API keys.

This is appropriate for human-operator accounts; it's almost never appropriate for integration accounts. Compromise of an admin key compromises the entire SimpleRisk install. Use admin keys exclusively for break-glass scenarios; create dedicated, least-privileged users for routine integrations.

Per-user direct grants

The User Management UI lets you grant specific permissions to a user beyond their role's default. This is useful for integration users that need a slightly-different permission set than any defined role:

  • Pick a role that's mostly right.
  • Grant the additional permissions directly to the user.
  • The user's effective permission set is the union.

For integrations that recur, the grant pattern often repeats — at which point it's worth defining a new role specifically for that integration pattern.

Designing permissions for integrations

Pattern A: Read-only reporting integration

Goal: a script that reads risk data for an external dashboard.

  • Role: a custom "API Reader" role with view permissions for the relevant entities (view_risks, view_compliance, etc.) and no write permissions.
  • Teams: every team the dashboard should display data for.
  • Admin flag: 0.
  • Verify: the user can GET /api/v2/risks (returns expected data) but POST /api/v2/risks/submit returns 403.

Pattern B: Risk submitter integration

Goal: a script that imports risks from an external source weekly.

  • Role: a custom "API Submitter" role with submit_risks and the necessary related permissions.
  • Teams: the team(s) the imported risks should be assigned to (so they're visible to the right users via team-segregation).
  • Admin flag: 0.
  • Verify: the user can POST /api/v2/risks/submit (creates a risk) and the created risk appears for users on the same team(s).

Pattern C: Risk-state-update integration

Goal: a CI pipeline that closes risks when corresponding fixes ship.

  • Role: a custom "API CI" role with modify_risks and close_risks (and the related team-scoped equivalents).
  • Teams: the team the risks the CI manages belong to.
  • Admin flag: 0.
  • Verify: the user can PATCH /api/v2/risks/{id} to update the relevant risks but can't modify other team's risks (403).

Pattern D: Admin operations integration

Goal: a script that bulk-creates users from an HR feed.

  • Role: includes manage_users and related admin permissions.
  • Teams: not relevant for user management.
  • Admin flag: 0 if manage_users alone is sufficient; 1 only if you genuinely need admin override.
  • Verify: the user can POST /api/v2/users to create users, and the created users get the right initial role/team.

Common pitfalls

A handful of patterns recur with API permissions.

  • Generating keys against admin users. The compromise blast radius is enormous. Use dedicated, least-privileged integration users.

  • Forgetting that team membership filters the integration's view. "Why does my integration see only some of the risks?" — usually the integration user isn't on the team that owns the missing risks.

  • Granting admin = 1 to fix permission issues. Diagnose the actual missing permission and grant that specifically; don't paper over with admin override.

  • Not creating a per-integration user. Sharing one user across multiple integrations means rotating the key affects all integrations and audit attribution is ambiguous.

  • Granting permissions ad-hoc to individual users instead of defining roles. Three integrations with the same permission profile should use the same role; otherwise role-management drift over time produces inconsistent integration behavior.

  • Not testing the integration's permissions before going live. "It works for the developer's admin account; deploy to production" → first 403 in production is the integration first proving it lacks the actual permission.

  • Treating 403 as a transient error. It's not; the user genuinely lacks the permission. Check permissions; don't add retry logic.

  • Forgetting that some endpoints require the user be on a specific team. A risk endpoint may filter by team; a configuration endpoint may require admin. The endpoint's permission check is endpoint-specific.

  • Confusing read permissions with team filtering. A user with view_risks permission and membership in the relevant team can see those risks; either alone isn't enough for cross-team visibility.

  • Granting permissions for hypothetical future operations. "Just give it manage_users in case." No — give it what it needs now; expand when needed. Over-granting is a liability.

Related