opsdocsConsole

Ops platform / HTTP API

Members, roles and tokens

Who can do what in a project: members, roles, environment rules, tokens, notification settings and the audit log.

Updated 13 Oct 2026View as Markdown

Who can do what in a project: members, roles, environment rules, tokens, notification settings and the audit log.

GET /api/v1/me

Who the caller is: memberships with their permissions, the environment rules that apply, and the project a token is pinned to, if any.

StatusMeansBody
200DoneMeResponse
401No token, or a token that is not valid

DELETE /api/v1/me/mfa

Drop the caller's own enrolment.

StatusMeansBody
204Done, with no body
401No token, or a token that is not valid
409It conflicts with what is there

POST /api/v1/me/mfa/confirm

Prove one code from the staged seed and the enrolment is in force. A wrong code and a replayed code are the same 401, deliberately.

Body: MfaConfirmBody

StatusMeansBody
204Done, with no body
401No token, or a token that is not valid
409Nothing is waiting to be confirmed
503The secrets store is sealed

POST /api/v1/me/mfa/enrol

Start an enrolment: a fresh seed, staged but not yet in force. The account is only protected once a code from it verifies, so this call alone locks nobody out and can be repeated.

StatusMeansBody
200DoneMfaEnrolResponse
401No token, or a token that is not valid
409This account already has an authenticator enrolled
503The secrets store is sealed

GET /api/v1/me/sessions

The caller's own live sessions: every browser and CLI they are signed in on.

StatusMeansBody
200Donearray of SessionResponse
401No token, or a token that is not valid

DELETE /api/v1/me/sessions/{session_id}

Sign one of the caller's own sessions out.

ParameterInTypeAbout
session_idpathstring (uuid)
StatusMeansBody
204Done, with no body
401No token, or a token that is not valid
404Not found

GET /api/v1/notifications

The caller's feed, newest first.

ParameterInTypeAbout
unread (optional)queryboolean or null
project (optional)querystring or nullA project slug, matched against the notice's own project param.
limit (optional)queryinteger or nullDefault 50, maximum 200.
cursor (optional)querystring or null
StatusMeansBody
200Donearray of NotificationResponse
401No token, or a token that is not valid

POST /api/v1/notifications/read-all

Mark the whole feed read. Idempotent, and never an error on an empty feed.

StatusMeansBody
204Done, with no body
401No token, or a token that is not valid

GET /api/v1/notifications/unread-count

How many notifications the caller has not read.

StatusMeansBody
200DoneUnreadCountResponse
401No token, or a token that is not valid

POST /api/v1/notifications/{id}/read

Mark one notice read.

ParameterInTypeAbout
idpathstring (uuid)
StatusMeansBody
204Done, with no body
401No token, or a token that is not valid
404Not found

GET /api/v1/permissions

The 28 permissions, in console order with the groups contiguous, so the Roles page renders the toggles without sorting anything.

StatusMeansBody
200Donearray of PermissionResponse
401No token, or a token that is not valid

GET /api/v1/projects/{slug}/audit

One project's audit log: the same trail with the project pinned, so a project page can show its own refusals.

ParameterInTypeAbout
slugpathstring
actor (optional)querystring or null
project (optional)querystring or nullIgnored on the project route: the path pins the project there.
environment (optional)querystring or null
action (optional)querystring or null
outcome (optional)querystring or nullallowed | denied. Absent = both.
since (optional)querystring or null
limit (optional)queryinteger or nullDefault 100, maximum 500.
cursor (optional)queryinteger or null
StatusMeansBody
200Donearray of AuditResponse
403Your role does not allow this
404No such project

GET /api/v1/projects/{slug}/environment-policies

Every policy in the project, for its Environment access panel.

ParameterInTypeAbout
slugpathstring
StatusMeansBody
200Donearray of EnvironmentPolicyResponse
401No token, or a token that is not valid
403Your role does not allow this

PUT /api/v1/projects/{slug}/environment-policies/{environment}/{role_id}

Write a policy.

ParameterInTypeAbout
slugpathstring
environmentpathstring
role_idpathstring (uuid)

Body: WritePolicyBody

StatusMeansBody
200DoneEnvironmentPolicyResponse
401No token, or a token that is not valid
403Your role does not allow this
404Not found
409The fixed role
422Refused, and the error says why

DELETE /api/v1/projects/{slug}/environment-policies/{environment}/{role_id}

Remove a policy, which gives the role its full set back in that environment.

ParameterInTypeAbout
slugpathstring
environmentpathstring
role_idpathstring (uuid)
StatusMeansBody
204Done, with no body
401No token, or a token that is not valid
403Your role does not allow this
404Not found

GET /api/v1/projects/{slug}/members

The project's members with their roles.

ParameterInTypeAbout
slugpathstring
StatusMeansBody
200Donearray of MemberResponse
401No token, or a token that is not valid
403Your role does not allow this

POST /api/v1/projects/{slug}/members

Add a member. One membership per operator per project, so adding someone who is already a member changes their role rather than failing.

ParameterInTypeAbout
slugpathstring

Body: WriteMembershipBody

StatusMeansBody
204Done, with no body
401No token, or a token that is not valid
403Your role does not allow this
404Not found
409It conflicts with what is there

GET /api/v1/projects/{slug}/members/candidates

The people who can be added as members of the project.

ParameterInTypeAbout
slugpathstring
StatusMeansBody
200Donearray of CandidateResponse
401No token, or a token that is not valid
403Your role does not allow this

PUT /api/v1/projects/{slug}/members/{operator_id}

Change one member's role. The path names the operator; a mismatched operator_id in the body is ignored, because the path is what was authorized.

ParameterInTypeAbout
slugpathstring
operator_idpathstring (uuid)

Body: WriteMembershipBody

StatusMeansBody
204Done, with no body
401No token, or a token that is not valid
403Your role does not allow this
404Not found
409It conflicts with what is there

DELETE /api/v1/projects/{slug}/members/{operator_id}

Take a project membership away.

ParameterInTypeAbout
slugpathstring
operator_idpathstring (uuid)
StatusMeansBody
204Done, with no body
401No token, or a token that is not valid
403Your role does not allow this
404Not found

GET /api/v1/projects/{slug}/notification-settings

The caller's notification settings for this project, one for each kind of notice.

ParameterInTypeAbout
slugpathstring
StatusMeansBody
200Donearray of NotificationSettingResponse
403Your role does not allow this
404No such project

PUT /api/v1/projects/{slug}/notification-settings

Replace the caller's notification settings for this project.

ParameterInTypeAbout
slugpathstring

Body: WriteNotificationSettingsBody

StatusMeansBody
204Done, with no body
403Your role does not allow this
404No such project
422Unknown notification kind

GET /api/v1/projects/{slug}/roles

The roles every project has, plus the ones this project defined.

ParameterInTypeAbout
slugpathstring
StatusMeansBody
200Donearray of RoleResponse
401No token, or a token that is not valid
403Your role does not allow this

POST /api/v1/projects/{slug}/roles

Create a role this project owns. The path decides the project; a project in the body is ignored, because the permission that let this call through is project.members in THIS project.

ParameterInTypeAbout
slugpathstring

Body: WriteRoleBody

StatusMeansBody
201CreatedRoleResponse
401No token, or a token that is not valid
403Your role does not allow this
404Not found
409It conflicts with what is there
422Refused, and the error says why

PUT /api/v1/projects/{slug}/roles/{id}

Change a role this project defined: its name and its permissions.

ParameterInTypeAbout
slugpathstring
idpathstring (uuid)

Body: WriteRoleBody

StatusMeansBody
200DoneRoleResponse
401No token, or a token that is not valid
403Your role does not allow this
404Not found
409It conflicts with what is there
422Refused, and the error says why

DELETE /api/v1/projects/{slug}/roles/{id}

Remove a role this project defined.

ParameterInTypeAbout
slugpathstring
idpathstring (uuid)
StatusMeansBody
204Done, with no body
401No token, or a token that is not valid
403Your role does not allow this
404Not found
409That role still has members

GET /api/v1/projects/{slug}/tokens

Every token pinned to this project, for its settings page.

ParameterInTypeAbout
slugpathstring
StatusMeansBody
200Donearray of TokenResponse
401No token, or a token that is not valid
403Your role does not allow this

GET /api/v1/tokens

The caller's own tokens, and every token when the caller manages people.

StatusMeansBody
200Donearray of TokenResponse
401No token, or a token that is not valid

POST /api/v1/tokens

Mint a token for the caller. The secret is in the answer and nowhere else.

Body: MintTokenBody

StatusMeansBody
201CreatedTokenMintedResponse
401No token, or a token that is not valid
403A machine operator cannot mint tokens
404Not found
422A token cannot carry more than the caller does

DELETE /api/v1/tokens/{id}

Revoke a token, instantly. An operator revokes their own; one who manages operators revokes anyone's.

ParameterInTypeAbout
idpathstring (uuid)
StatusMeansBody
204Done, with no body
401No token, or a token that is not valid
404Not found

Types

MeResponse

FieldTypeAbout
actorstringThe audit actor string: the operator's email, or api-token.
display_namestring
kindstringperson | machine
machineboolean
membershipsarray of MeMembership
mfabooleanWhether this operator has a TOTP authenticator enrolled.
operator_idstring (uuid)
policiesarray of MePolicy
suspendedboolean
token_id (optional)string (uuid) or null
token_project (optional)string or nullA value means the token acts in this project and nowhere else.
token_scopes (optional)array of string or nullnull = a session bearer, which is never narrowed. A value means a minted token's snapshot of its role at mint time.
viastringconsole | token | api-token

MfaConfirmBody

FieldTypeAbout
codestring

MfaEnrolResponse

FieldTypeAbout
secretstringBase32, for manual entry. Shown once and never again.
uristringThe otpauth:// URI the authenticator app scans.

SessionResponse

FieldTypeAbout
created_atstring
currentbooleanThe session this very request arrived on.
expires_atstring
idstring (uuid)
ipstring
kindstringconsole | cli
user_agentstring

NotificationResponse

FieldTypeAbout
created_atstring
idstring (uuid)
kindstringbuild.failed | deployment.rolled | deployment.refused | deployment.auto_rollback | probe.down | quota.high | package.published
link (optional)string or nullThe console path the notice opens, relative.
paramsanyPII free: slugs, names of things, numbers.
readboolean

UnreadCountResponse

The badge in the operator menu: global, never per project.

FieldTypeAbout
unreadinteger

PermissionResponse

FieldTypeAbout
groupstring
hintstring
keystring
labelstring

AuditResponse

FieldTypeAbout
actionstringThe permission that was asked for, or the verb of a self service write.
actorstringThe operator email, or api-token / github-app for a machine caller.
atstring
environment (optional)string or null
idinteger
ipstring
outcomestringallowed | denied
project (optional)string or null
reasonstringThe deny reason of a refusal, empty when allowed.
targetstring
viastringconsole | token | api-token | system

EnvironmentPolicyResponse

FieldTypeAbout
allowedarray of string
environmentstring
removedarray of stringWhat this policy takes away from the role, for the console's muted line.
role_idstring (uuid)
role_namestring
updated_atstring

WritePolicyBody

FieldTypeAbout
allowedarray of string

MemberResponse

FieldTypeAbout
display_namestring
emailstring
kindstringperson | machine
operator_idstring (uuid)
project (optional)string or null
project_name (optional)string or null
role_idstring (uuid)
role_namestring
role_slugstring
sincestring

WriteMembershipBody

FieldTypeAbout
operator_idstring (uuid)
role_idstring (uuid)

CandidateResponse

One operator the Add member form may offer: identity only, no memberships, no sessions, no MFA state, kind person | machine.

FieldTypeAbout
display_namestring
emailstring
idstring (uuid)
kindstring

NotificationSettingResponse

One kind's two channels, with the label the section draws.

FieldTypeAbout
emailboolean
in_appboolean
kindstring
label (optional)stringIgnored on the way in: the label belongs to the API, not to the form.

WriteNotificationSettingsBody

FieldTypeAbout
settingsarray of NotificationSettingResponse

RoleResponse

FieldTypeAbout
descriptionstring
display_namestring
fixedboolean
idstring (uuid)
membersinteger
permissionsarray of stringThe console keys on fixed, not on this.
project (optional)string or null
slugstring

WriteRoleBody

FieldTypeAbout
description (optional)string
display_namestring
permissionsarray of string
project (optional)string or nullIgnored on PUT and on the project scoped routes, where the path decides: a role never moves.
slugstring

TokenResponse

FieldTypeAbout
created_atstring
expires_at (optional)string or null
idstring (uuid)
kindstringapi | cli | mcp
last_used (optional)string or null
namestring
operatorstringThe operator's email, or the machine's handle.
operator_idstring (uuid)
project (optional)string or nullnull = the token acts wherever its operator may.
revokedboolean
role_idstring (uuid)
role_namestring
scopesarray of string

MintTokenBody

FieldTypeAbout
expires_in_days (optional)integer or nullDays until expiry. Omitted = OPS_TOKEN_TTL_DAYS, 0 = no expiry.
kind (optional)string or nullapi (default) | cli | mcp
namestring
project (optional)string or nullnull = the token acts wherever its operator may; a slug pins it.
role_idstring (uuid)

TokenMintedResponse

FieldTypeAbout
secretstringThe raw bearer, opst_…. Shown ONCE: it is never stored and cannot be read back, so a lost secret is a new token.
tokenTokenResponse

MeMembership

FieldTypeAbout
fixedboolean
permissionsarray of string
project (optional)string or null
role_idstring (uuid)
role_namestring
role_slugstring

MePolicy

FieldTypeAbout
allowedarray of string
environmentstring
projectstring
role_idstring (uuid)