Spec0docs
Platform

Access control

Roles, permissions, and the rules Spec0 uses to decide whether a request from your dashboard, CLI, or CI is allowed. What we accept, what we reject, and why.

Every action you take in Spec0 — opening a page in the dashboard, publishing a spec from CI, calling a public API endpoint with a personal token — runs through the same access-control engine. This page is the contract: who can do what, under what conditions, and what to expect when a request is rejected.

The five-step decision

When you make a request, Spec0 runs through these steps in order. The first step that produces a verdict wins — later steps don't run.

#StepWhen it allowsWhen it denies
1Platform super-adminCaller has platform:super_admin (Spec0 staff only).Otherwise → fall through to step 2.
2Same organisationResource you're acting on belongs to your org.Cross-org access → deny, regardless of role.
3Org admin overrideYou have organisation:admin or organisation:manage AND it's your org.Falls through to step 4 if you're not an org admin.
4Resource-specific ruleThe rule for this resource type (API, team, subscription, mock server, etc.) accepts the action.Resource rule rejects → deny.
5Permission checkYou hold the <resource>:<level> permission needed for the action (e.g. api:manage to edit an API).Missing permission → deny.

Cross-organisation requests are structurally impossible — they fail at step 2, and the database-layer query filter (see ADR-0010) means even a buggy backend couldn't accidentally return another org's data.

Roles

Every user in your org carries one or more roles. Roles are how you grant permissions in bulk — instead of assigning api:view, team:view, subscription:view etc. one by one, you assign Org Member and the corresponding permissions come with it.

RoleScopeWhat they can do
Org OwnerWhole orgEverything in the org. The original signup user; can transfer ownership. Holds every :admin permission.
Org AdminWhole orgManage members, teams, APIs, subscribers, mock servers. Cannot transfer ownership or delete the org.
Org MemberWhole orgRead-only across the org. View APIs, teams, subscribers, mock servers. Cannot create or edit.
Team AdminOne teamManage their team's members, APIs, subscribers, and mock servers. Mint service-account tokens for the team. Cannot touch other teams.
Team MemberOne teamEdit APIs and mock servers owned by their team. Read-only on subscriptions and team membership.
Subscription AdminSubscriptionApprove, modify, or revoke specific subscriptions. Used by teams that mostly consume APIs without owning them.
Platform UserPlatform-wideRead-only across published APIs and subscriptions. Used for cross-org integrations.

Spec0 staff also carry Platform Admin (backoffice console access) or Platform Super-Admin (every permission). These never apply to customer accounts.

Permissions

A permission is <resource>:<level>. Levels are hierarchical: admin includes manage includes view.

PermissionGranted byWhat it lets you do
organisation:viewEvery org roleSee your org's profile, members count, billing tier.
organisation:manageOrg Admin, Org OwnerEdit org profile, invite/remove members, change plans.
organisation:adminOrg OwnerTransfer ownership, delete the org.
team:viewOrg Member, Team MemberSee team rosters and ownership.
team:manageOrg Admin, Team AdminAdd/remove team members, mint service-account tokens, edit team metadata.
team:adminOrg Owner, Team AdminDelete a team, transfer team ownership.
api:viewOrg Member, Team MemberRead API metadata, fetch published specs, see version history.
api:manageOrg Admin, Team Member, Team AdminPublish new spec versions, edit API metadata, configure governance.
api:adminOrg OwnerDelete an API, transfer API ownership.
subscription:viewOrg Member, Team MemberList subscribers, see subscription details.
subscription:manageOrg Admin, Team AdminApprove, reject, or modify subscriptions on your team's APIs.
subscription:adminOrg Owner, Subscription AdminForce-delete a subscription, override approval gates.
mock_server:viewOrg Member, Team MemberSee mock servers and their variants.
mock_server:manageMost rolesCreate, edit, delete mock servers and response variants.
mock_server:adminOrg Admin, Org Owner, Team AdminReset mock-server state, rotate keys.

The platform also defines user:* and system:* permissions — those are reserved for Spec0 staff.

How team boundaries work

A few resources are team-scoped — APIs, mock servers, subscriptions. Even if you have api:manage, you can only modify APIs that your team owns. This means:

  • A Team Member of Team A who has api:manage can publish new versions of Team A's APIs but cannot publish to Team B's APIs (denied at step 4: resource-specific rule).
  • A user with no team assignment cannot perform any non-VIEW action on team-scoped resources, even if they have permissions.
  • An Org Admin or Org Owner overrides this at step 3 — they can act on any team's resources within the org, no team membership required. This is by design and matches how org admins work in tools like GitHub or Linear.

Org-scoped resources (the org profile itself, member invitations, billing) don't have team boundaries — only org isolation and permission checks apply.

Accepted vs. rejected — examples

These walk through the five-step decision against real situations.

A team admin mints a service-account token for their own team.

  1. Not super-admin → continue.
  2. Same org ✓ → continue.
  3. Has team:manage from Team Admin role, but not organisation:admin → continue.
  4. Resource = team, action = manage. Resource rule: same-team check. userTeamId == resourceTeamId ✓ → continue.
  5. Has team:manage permission ✓ → allowed.

The same team admin tries to mint a SAT for a different team.

  1. Not super-admin → continue.
  2. Same org ✓ → continue.
  3. Not an org admin → continue.
  4. Resource rule: same-team check. userTeamId ≠ resourceTeamIddenied at step 4.

An org admin mints a SAT for any team in their org.

  1. Not super-admin → continue.
  2. Same org ✓ → continue.
  3. Has organisation:manage AND same org → allowed (skips step 4 entirely).

An org member of Org A views an API published by Org B (e.g. via a leaked URL).

  1. Not super-admin → continue.
  2. userOrgId ≠ resourceOrgIddenied at step 2. The Hibernate org filter additionally guarantees the read returns nothing even before authz runs.

A user with no team assignment tries to edit one of their org's APIs.

  1. Not super-admin → continue.
  2. Same org ✓ → continue.
  3. Not an org admin → continue.
  4. Resource rule: API is team-scoped, action is manage. User has no userTeamIddenied at step 4.

A CI service-account token (SAT) tries to publish a spec.

  1. Not super-admin → continue.
  2. Same org ✓ → continue.
  3. SAT carries the team's permissions, not org-admin → continue.
  4. SAT's userTeamId is the team it was minted for; spec's API belongs to the same team ✓ → continue.
  5. Token's scopes include write:specs ✓ → allowed. (See Public API tokens for how scopes map to permissions.)

What you'll see when something is rejected

The HTTP response is always 403 Forbidden with this shape:

{
  "timestamp": "2026-05-02T10:42:00Z",
  "status": 403,
  "error": "Access Denied",
  "message": "Access denied",
  "reason": "User is not in same team as the API resource"
}

The reason field tells you which step rejected the request — useful for debugging. Common reasons:

reasonWhat to do
User org ... ≠ Resource org ...You're trying to act on another org's resource. Check the URL or IDs.
User is not in same team as the ... resourceYou're not on the team that owns the resource. Ask a team admin to add you, or have an org admin perform the action.
User has no team assignment - cannot access team-scoped ...You're an Org Member with no team. Ask an Org Admin to add you to a team.
User lacks required permission: ...Your role doesn't include the permission. Check the permissions table above.
... team ID is null - cannot perform ... operation on Team without team contextThe endpoint expected a team-scoped path but the URL didn't include one. Check the API reference.

Where to go next

  • Teams — the multi-tenant model the access control runs against.
  • APIs — what api:* permissions actually let you do.
  • Subscriptionssubscription:* and the approval flow.
  • Tokens — PAT vs SAT vs org API key, and how token scopes interact with the permissions on this page.

On this page