Skip to content

Depot users

Registers the management routes.

This page is generated from the Depot route table. Every operation carries the permission it needs and the anchor an agent can link to.

POSTNo permission neededshared facehosteditmspremote

Source: [object Object]

acceptInvite spends an invitation and sets the account’s first password.

Required, sent as application/json.

FieldTypeDescription
passwordrequiredstring
tokenrequiredstring
StatusBodyDescription
204NoneSuccess, with no body.
400NoneThe request was refused.
Terminal window
# This operation is public. It takes no credentials.
curl -X POST 'https://depot.example.com/api/v1/console/invites/accept' \
-H 'Content-Type: application/json' \
--data '{
"password": "string",
"token": "string"
}'

GETclient:readshared facehosteditmspremoteIdempotent

Source: [object Object]

getLoginPolicy returns the client-wide rules and the caller’s own address.

StatusBodyDescription
200application/json, LoginPolicyResponseSuccess.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold client:read.

200 response body

FieldTypeDescription
callerIprequiredstring
ipAllowlistrequiredstring
timeZonerequiredstring
weekarray of array of WindowView or null
week[][].endMinuterequiredinteger
week[][].startMinuterequiredinteger
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X GET 'https://depot.example.com/api/v1/console/login-policy' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

PUTclient:manageshared facehosteditmspremoteIdempotent

Source: [object Object]

saveLoginPolicy replaces the client-wide rules.

Saving rules that exclude the caller from where they are standing right now returns 409 naming their address, unless the body carries an explicit acknowledgeLockout.

This mirrors refuseIfLastOwner and rests on the same argument. There is no way back that does not involve somebody else: the rules can only be edited from inside the console, and the console is what the rules just closed. Under an hours rule that is a wait; under a network rule it is a support call, and on a hosted fleet it is a support call to us. So the accident is refused and the deliberate act is allowed, and the difference between them is a field somebody had to set.

It also refuses, on the same acknowledgement, rules that would shut out EVERY live owner: the caller may be an administrator who is not an owner, and rules that pass them can still leave nobody able to manage the organisation. Each owner is evaluated against where they last signed in from (their newest session’s address) and the time now; an owner with no recorded address counts as excluded by any allowlist, because the guard cannot show they are inside it. It refuses only when ALL of them are out: refusing a rule because it might exclude SOMEBODY would refuse every useful rule there is.

Break-glass (breakglass.go) is the way back for an owner who acknowledged the warning and was wrong; the guard is what stops it being needed by accident.

Every save is audited against the policy, and every owner is told by email. A rule that changes who can reach the console is the change an owner most needs to hear about from somebody other than the person who made it.

Required, sent as application/json.

FieldTypeDescription
acknowledgeLockoutrequiredboolean
ipAllowlistrequiredstring
timeZonerequiredstring
weekarray of array of WindowView or null
week[][].endMinuterequiredinteger
week[][].startMinuterequiredinteger
StatusBodyDescription
204NoneSuccess, with no body.
400application/problem+json, ProblemDetailsThe request body is not acceptable.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold client:manage.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X PUT 'https://depot.example.com/api/v1/console/login-policy' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-b 'outpost_console=<console-session>' \
--data '{
"acknowledgeLockout": false,
"ipAllowlist": "string",
"timeZone": "string",
"week": [
[
{
"endMinute": 0,
"startMinute": 0
}
]
]
}'

GETticket:readshared facehosteditmspremoteIdempotent

Source: [object Object]

listOperators lists the active console accounts a desk operator can pick as an assignee: a macro’s owner, a queue’s members, an incident’s commander.

Invited and disabled accounts are left out because neither can work what they are handed. Unpaged on purpose: a picker needs the whole list, and it is one query with no per-row lookups.

StatusBodyDescription
200application/json, objectSuccess.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold ticket:read.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X GET 'https://depot.example.com/api/v1/console/operators' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

GETrole:manageshared facehosteditmspremoteIdempotent

Source: [object Object]

listPermissions serves the permission vocabulary, grouped by area.

StatusBodyDescription
200application/json, objectSuccess.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold role:manage.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X GET 'https://depot.example.com/api/v1/console/permissions' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

GETrole:manageshared facehosteditmspremoteIdempotent

Source: [object Object]

listRoles serves the role catalog: every role this client has, with what each one can do, who holds it, and whether the caller may edit it.

Two queries for the whole page, not one per role. The grants come back as a map keyed by role code from a single join, so adding a role to the catalog costs nothing here.

StatusBodyDescription
200application/json, objectSuccess.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold role:manage.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X GET 'https://depot.example.com/api/v1/console/roles' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

POSTrole:manageshared facehosteditmspremoteNot fully resolved

What this page does not know
  • response-status

These are parts of the route the extractor could not read from the source. Everything else on this page came straight from it.

Source: [object Object]

createRole makes a custom role, or clones one with fromRole.

The code is slugged from the name and never changes afterwards.

Required, sent as application/json.

FieldTypeDescription
descriptionstring or null
fromRolestring or null
namerequiredstring
permissionsarray of string or null
StatusBodyDescription
200application/json, RoleViewSuccess.
400application/problem+json, ProblemDetailsThe request body is not acceptable.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold role:manage.

200 response body

FieldTypeDescription
clippedPermissionsrequiredarray of string
codestring or null
descriptionrequiredstring
idrequiredstring
isSystemrequiredboolean
memberCountrequiredinteger
namerequiredstring
permissionsrequiredarray of string
rowVersionrequiredinteger

int64.

Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
curl -X POST 'https://depot.example.com/api/v1/console/roles' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-b 'outpost_console=<console-session>' \
--data '{
"description": "string",
"fromRole": "string",
"name": "string",
"permissions": [
"string"
]
}'

PATCHrole:manageshared facehosteditmspremoteNot fully resolved

What this page does not know
  • response-status

These are parts of the route the extractor could not read from the source. Everything else on this page came straight from it.

Source: [object Object]

updateRole changes a role’s name or description.

The code never changes.

NameInTypeRequiredDescription
roleCodepathstringYes

Required, sent as application/json.

FieldTypeDescription
descriptionstring or null
namestring or null
StatusBodyDescription
200application/json, RoleViewSuccess.
400application/problem+json, ProblemDetailsThe request body is not acceptable.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold role:manage.
404application/problem+json, ProblemDetailsNo resource matches that id.

200 response body

FieldTypeDescription
clippedPermissionsrequiredarray of string
codestring or null
descriptionrequiredstring
idrequiredstring
isSystemrequiredboolean
memberCountrequiredinteger
namerequiredstring
permissionsrequiredarray of string
rowVersionrequiredinteger

int64.

Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
curl -X PATCH 'https://depot.example.com/api/v1/console/roles/<roleCode>' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-b 'outpost_console=<console-session>' \
--data '{
"description": "string",
"name": "string"
}'

DELETErole:manageshared facehosteditmspremoteIdempotent

Source: [object Object]

deleteRole removes a custom role nobody holds.

NameInTypeRequiredDescription
roleCodepathstringYes
StatusBodyDescription
204NoneSuccess, with no body.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold role:manage.
404application/problem+json, ProblemDetailsNo resource matches that id.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X DELETE 'https://depot.example.com/api/v1/console/roles/<roleCode>' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

GET /api/v1/console/roles/{roleCode}/holders

Section titled “GET /api/v1/console/roles/{roleCode}/holders”

GETrole:manageshared facehosteditmspremoteIdempotent

Source: [object Object]

listRoleHolders lists the people who hold a role, of any account status.

NameInTypeRequiredDescription
roleCodepathstringYes
StatusBodyDescription
200application/json, objectSuccess.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold role:manage.
404application/problem+json, ProblemDetailsNo resource matches that id.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X GET 'https://depot.example.com/api/v1/console/roles/<roleCode>/holders' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

PUT /api/v1/console/roles/{roleCode}/permissions

Section titled “PUT /api/v1/console/roles/{roleCode}/permissions”

PUTrole:manageshared facehosteditmspremoteIdempotentNot fully resolved

What this page does not know
  • response-status

These are parts of the route the extractor could not read from the source. Everything else on this page came straight from it.

Source: [object Object]

setRolePermissions replaces what a role grants.

The floor codes are always kept. The change reaches every holder on their next request.

NameInTypeRequiredDescription
roleCodepathstringYes

Required, sent as application/json.

FieldTypeDescription
permissionsrequiredarray of string
StatusBodyDescription
200application/json, RoleViewSuccess.
400application/problem+json, ProblemDetailsThe request body is not acceptable.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold role:manage.
404application/problem+json, ProblemDetailsNo resource matches that id.

200 response body

FieldTypeDescription
clippedPermissionsrequiredarray of string
codestring or null
descriptionrequiredstring
idrequiredstring
isSystemrequiredboolean
memberCountrequiredinteger
namerequiredstring
permissionsrequiredarray of string
rowVersionrequiredinteger

int64.

Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X PUT 'https://depot.example.com/api/v1/console/roles/<roleCode>/permissions' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-b 'outpost_console=<console-session>' \
--data '{
"permissions": [
"string"
]
}'

POST /api/v1/console/roles/{roleCode}/restore-defaults

Section titled “POST /api/v1/console/roles/{roleCode}/restore-defaults”

POSTrole:manageshared facehosteditmspremoteNot fully resolved

What this page does not know
  • response-status

These are parts of the route the extractor could not read from the source. Everything else on this page came straight from it.

Source: [object Object]

restoreRoleDefaults puts a built-in role’s permissions back the way the product ships them.

NameInTypeRequiredDescription
roleCodepathstringYes
StatusBodyDescription
200application/json, RoleViewSuccess.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold role:manage.
404application/problem+json, ProblemDetailsNo resource matches that id.

200 response body

FieldTypeDescription
clippedPermissionsrequiredarray of string
codestring or null
descriptionrequiredstring
idrequiredstring
isSystemrequiredboolean
memberCountrequiredinteger
namerequiredstring
permissionsrequiredarray of string
rowVersionrequiredinteger

int64.

Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
curl -X POST 'https://depot.example.com/api/v1/console/roles/<roleCode>/restore-defaults' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

GETclient:readshared facehosteditmspremoteIdempotent

Source: [object Object]

getSecurityPolicy answers the GET.

StatusBodyDescription
200application/json, SecurityPolicyResponseSuccess.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold client:read.

200 response body

FieldTypeDescription
effectivePasswordrequiredPasswordView
effectivePassword.breachScreenboolean or null
effectivePassword.historyCountrequiredinteger
effectivePassword.maxAgeDaysrequiredinteger
effectivePassword.minLengthrequiredinteger
effectivePassword.requireDigitrequiredboolean
effectivePassword.requireSymbolrequiredboolean
effectivePassword.requireUpperrequiredboolean
frameworkrequiredstring
frameworksrequiredarray of FrameworkView
frameworks[].coderequiredstring
frameworks[].labelrequiredstring
frameworks[].lockoutrequiredLockoutView
frameworks[].lockout.minutesrequiredinteger
frameworks[].lockout.thresholdrequiredinteger
frameworks[].mfaRequirementrequiredstring
frameworks[].noterequiredstring
frameworks[].passwordrequiredPasswordView
frameworks[].password.breachScreenboolean or null
frameworks[].password.historyCountrequiredinteger
frameworks[].password.maxAgeDaysrequiredinteger
frameworks[].password.minLengthrequiredinteger
frameworks[].password.requireDigitrequiredboolean
frameworks[].password.requireSymbolrequiredboolean
frameworks[].password.requireUpperrequiredboolean
frameworks[].sessionrequiredSessionView
frameworks[].session.absoluteHoursrequiredinteger
frameworks[].session.idleMinutesrequiredinteger
lockoutrequiredLockoutView
lockout.minutesrequiredinteger
lockout.thresholdrequiredinteger
mfarequiredMfaView
mfa.acceptedFactorsrequiredarray of string
mfa.enforcedFromstring (date-time) or null
mfa.graceDaysrequiredinteger
mfa.requiredSincestring (date-time) or null
mfa.requirementrequiredstring
mfa.supportedFactorsrequiredarray of string
passwordrequiredPasswordView
password.breachScreenboolean or null
password.historyCountrequiredinteger
password.maxAgeDaysrequiredinteger
password.minLengthrequiredinteger
password.requireDigitrequiredboolean
password.requireSymbolrequiredboolean
password.requireUpperrequiredboolean
passwordlessrequiredPasswordlessView

passwordlessView is whether a passkey alone may sign somebody in.

passwordless.allowedrequiredboolean
policyVersionrequiredinteger
reauthRequiredboolean or null
rememberDevicerequiredRememberView
rememberDevice.daysrequiredinteger
rememberDevice.enabledrequiredboolean
rowVersionrequiredinteger

int64.

sessionrequiredSessionView
session.absoluteHoursrequiredinteger
session.idleMinutesrequiredinteger
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X GET 'https://depot.example.com/api/v1/console/security-policy' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

PUTclient:manageshared facehosteditmspremoteIdempotent

Source: [object Object]

saveSecurityPolicy answers the PUT.

A TIGHTENING SAVE SIGNS EVERYBODY OUT, THE CALLER INCLUDED

Section titled “A TIGHTENING SAVE SIGNS EVERYBODY OUT, THE CALLER INCLUDED”

When the save asks more of a password or a second factor than the old policy did (console.Tightens), PolicyVersion is bumped in the same statement and the middleware refuses every session issued under the old version. Everybody signs in again, and the sign-in is where a password that no longer meets the policy is caught (it is the only moment the plaintext exists) and where a missing second factor is caught. The caller is not exempt: an exemption would be the one session in the tenant that never met the rule its holder just set. The response says reauthRequired so the console can explain the sign-out rather than let it look like a fault.

Required, sent as application/json.

FieldTypeDescription
frameworkrequiredstring
lockoutLockoutView
lockout.minutesrequiredinteger
lockout.thresholdrequiredinteger
mfaobject or null
mfa.acceptedFactorsrequiredarray of string
mfa.graceDaysrequiredinteger
mfa.requirementrequiredstring
passwordPasswordView
password.breachScreenboolean or null
password.historyCountrequiredinteger
password.maxAgeDaysrequiredinteger
password.minLengthrequiredinteger
password.requireDigitrequiredboolean
password.requireSymbolrequiredboolean
password.requireUpperrequiredboolean
passwordlessPasswordlessView

passwordlessView is whether a passkey alone may sign somebody in.

passwordless.allowedrequiredboolean
rememberDeviceRememberView
rememberDevice.daysrequiredinteger
rememberDevice.enabledrequiredboolean
rowVersioninteger or null

int64.

sessionSessionView
session.absoluteHoursrequiredinteger
session.idleMinutesrequiredinteger
StatusBodyDescription
200application/json, SecurityPolicyResponseSuccess.
400application/problem+json, ProblemDetailsThe request body is not acceptable.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold client:manage.

200 response body

FieldTypeDescription
effectivePasswordrequiredPasswordView
effectivePassword.breachScreenboolean or null
effectivePassword.historyCountrequiredinteger
effectivePassword.maxAgeDaysrequiredinteger
effectivePassword.minLengthrequiredinteger
effectivePassword.requireDigitrequiredboolean
effectivePassword.requireSymbolrequiredboolean
effectivePassword.requireUpperrequiredboolean
frameworkrequiredstring
frameworksrequiredarray of FrameworkView
frameworks[].coderequiredstring
frameworks[].labelrequiredstring
frameworks[].lockoutrequiredLockoutView
frameworks[].lockout.minutesrequiredinteger
frameworks[].lockout.thresholdrequiredinteger
frameworks[].mfaRequirementrequiredstring
frameworks[].noterequiredstring
frameworks[].passwordrequiredPasswordView
frameworks[].password.breachScreenboolean or null
frameworks[].password.historyCountrequiredinteger
frameworks[].password.maxAgeDaysrequiredinteger
frameworks[].password.minLengthrequiredinteger
frameworks[].password.requireDigitrequiredboolean
frameworks[].password.requireSymbolrequiredboolean
frameworks[].password.requireUpperrequiredboolean
frameworks[].sessionrequiredSessionView
frameworks[].session.absoluteHoursrequiredinteger
frameworks[].session.idleMinutesrequiredinteger
lockoutrequiredLockoutView
lockout.minutesrequiredinteger
lockout.thresholdrequiredinteger
mfarequiredMfaView
mfa.acceptedFactorsrequiredarray of string
mfa.enforcedFromstring (date-time) or null
mfa.graceDaysrequiredinteger
mfa.requiredSincestring (date-time) or null
mfa.requirementrequiredstring
mfa.supportedFactorsrequiredarray of string
passwordrequiredPasswordView
password.breachScreenboolean or null
password.historyCountrequiredinteger
password.maxAgeDaysrequiredinteger
password.minLengthrequiredinteger
password.requireDigitrequiredboolean
password.requireSymbolrequiredboolean
password.requireUpperrequiredboolean
passwordlessrequiredPasswordlessView

passwordlessView is whether a passkey alone may sign somebody in.

passwordless.allowedrequiredboolean
policyVersionrequiredinteger
reauthRequiredboolean or null
rememberDevicerequiredRememberView
rememberDevice.daysrequiredinteger
rememberDevice.enabledrequiredboolean
rowVersionrequiredinteger

int64.

sessionrequiredSessionView
session.absoluteHoursrequiredinteger
session.idleMinutesrequiredinteger
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X PUT 'https://depot.example.com/api/v1/console/security-policy' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-b 'outpost_console=<console-session>' \
--data '{
"framework": "string",
"lockout": {
"minutes": 0,
"threshold": 0
},
"mfa": {
"acceptedFactors": [
"string"
],
"graceDays": 0,
"requirement": "string"
},
"password": {
"breachScreen": false,
"historyCount": 0,
"maxAgeDays": 0,
"minLength": 0,
"requireDigit": false,
"requireSymbol": false,
"requireUpper": false
},
"passwordless": {
"allowed": false
},
"rememberDevice": {
"days": 0,
"enabled": false
},
"rowVersion": 0,
"session": {
"absoluteHours": 0,
"idleMinutes": 0
}
}'

GETuser:manageshared facehosteditmspremoteIdempotent

Source: [object Object]

listUsers is the team page: one narrowed, ordered, paged slice of the roster.

PAGING IS A FIX HERE AND NOT ONLY A FEATURE. Each row costs two extra queries, the account’s roles and whether it has confirmed a second factor, and before this they ran for every user on the tenant on every load. They now run for the PAGE, so opening the team page stopped costing more as the team grew.

The response GAINS page and pageSize and keeps items and total, so it is a superset of what it sent before and every existing caller reads items unaffected. total was the row count and is now the count BEFORE paging, which is the number a pager needs and the same number whenever one page holds everybody, which today is every deployment.

NameInTypeRequiredDescription
dirquerystringNo
pagequerystringNo
pageSizequerystringNo
sortquerystringNo
statusquerystringNo
StatusBodyDescription
200application/json, objectSuccess.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold user:manage.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X GET 'https://depot.example.com/api/v1/console/users?dir=<dir>&page=<page>&pageSize=<pageSize>&sort=<sort>&status=<status>' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

POSTuser:manageshared facehosteditmspremote

Source: [object Object]

inviteUser creates an account and issues a single-use invitation.

The account starts “invited” with no credential, so it cannot sign in until the recipient accepts and chooses a password. The link is returned to the caller rather than emailed, because Depot has no outbound mail yet; when it does, this is the one line that changes.

Required, sent as application/json.

FieldTypeDescription
displayNamerequiredstring
emailrequiredstring
namedUserIdstring or null
roleIdsrequiredarray of string
StatusBodyDescription
201application/json, objectCreated.
400application/problem+json, ProblemDetailsThe request body is not acceptable.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold user:manage.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
curl -X POST 'https://depot.example.com/api/v1/console/users' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-b 'outpost_console=<console-session>' \
--data '{
"displayName": "string",
"email": "string",
"namedUserId": "string",
"roleIds": [
"string"
]
}'

PATCHuser:manageshared facehosteditmspremote

Source: [object Object]

patchUser changes an account’s status.

Disabling also ends its sessions, in the store, in the same transaction.

NameInTypeRequiredDescription
userIdpathstringYes

Required, sent as application/json.

FieldTypeDescription
statusrequiredstring
StatusBodyDescription
204NoneSuccess, with no body.
400application/problem+json, ProblemDetailsThe request body is not acceptable.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold user:manage.
404application/problem+json, ProblemDetailsNo resource matches that id.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
curl -X PATCH 'https://depot.example.com/api/v1/console/users/<userId>' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-b 'outpost_console=<console-session>' \
--data '{
"status": "string"
}'

DELETEuser:manageshared facehosteditmspremoteIdempotent

Source: [object Object]

Delete user

NameInTypeRequiredDescription
userIdpathstringYes
StatusBodyDescription
204NoneSuccess, with no body.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold user:manage.
404application/problem+json, ProblemDetailsNo resource matches that id.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X DELETE 'https://depot.example.com/api/v1/console/users/<userId>' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

POST /api/v1/console/users/{userId}/invite

Section titled “POST /api/v1/console/users/{userId}/invite”

POSTuser:manageshared facehosteditmspremote

Source: [object Object]

reinviteUser issues a fresh invitation for an account that has not accepted yet, for the ordinary case of a link that expired or never arrived.

NameInTypeRequiredDescription
userIdpathstringYes
StatusBodyDescription
200application/json, objectSuccess.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold user:manage.
404application/problem+json, ProblemDetailsNo resource matches that id.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
curl -X POST 'https://depot.example.com/api/v1/console/users/<userId>/invite' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

GET /api/v1/console/users/{userId}/login-policy

Section titled “GET /api/v1/console/users/{userId}/login-policy”

GETuser:manageshared facehosteditmspremoteIdempotent

Source: [object Object]

getUserLoginPolicy returns one user’s additional restrictions.

It exists because the team page’s per-person panel is the SAME editor component the organisation tab uses, and an editor with no read opens holding its defaults. Pressing Save then replaces whatever that person actually had with them, silently, and the person finds out when they cannot sign in. A route that answers 405 to a GET is not a neutral omission here; it is a data loss the console cannot defend against on its own.

The rules ride the row GetConsoleUser already returns, so this costs one lookup and no new query. TimeZone is the CLIENT’s, echoed so the editor can say which zone the hours are read in; it is read only on this route, and saveUserLoginPolicy ignores it.

NameInTypeRequiredDescription
userIdpathstringYes
StatusBodyDescription
200application/json, LoginPolicyResponseSuccess.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold user:manage.
404application/problem+json, ProblemDetailsNo resource matches that id.

200 response body

FieldTypeDescription
callerIprequiredstring
ipAllowlistrequiredstring
timeZonerequiredstring
weekarray of array of WindowView or null
week[][].endMinuterequiredinteger
week[][].startMinuterequiredinteger
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X GET 'https://depot.example.com/api/v1/console/users/<userId>/login-policy' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

PUT /api/v1/console/users/{userId}/login-policy

Section titled “PUT /api/v1/console/users/{userId}/login-policy”

PUTuser:manageshared facehosteditmspremoteIdempotent

Source: [object Object]

saveUserLoginPolicy writes one user’s additional restrictions.

THERE IS DELIBERATELY NO LOCKOUT GUARD HERE, and it is not an oversight.

An administrator setting rules on somebody else’s account is doing exactly what this feature is for - pinning a contractor to one network, or a part-timer to their shift - and refusing it because the SUBJECT would be excluded from where they happen to be sitting would refuse the ordinary case and, worse, would leak where that person currently is to whoever edits their account. The subject is not locked out of anything an administrator cannot undo from this same screen, which is the property the client-wide guard does not have.

An administrator who narrows their OWN account through this route can lock themselves out. That is accepted for the same reason: another owner can clear it, the route is guarded by user:manage, and adding a self-check here would mean the guard fires on one row in the list and not the others, which reads as a bug rather than as a warning.

NameInTypeRequiredDescription
userIdpathstringYes

Required, sent as application/json.

FieldTypeDescription
acknowledgeLockoutrequiredboolean
ipAllowlistrequiredstring
timeZonerequiredstring
weekarray of array of WindowView or null
week[][].endMinuterequiredinteger
week[][].startMinuterequiredinteger
StatusBodyDescription
204NoneSuccess, with no body.
400application/problem+json, ProblemDetailsThe request body is not acceptable.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold user:manage.
404application/problem+json, ProblemDetailsNo resource matches that id.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X PUT 'https://depot.example.com/api/v1/console/users/<userId>/login-policy' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-b 'outpost_console=<console-session>' \
--data '{
"acknowledgeLockout": false,
"ipAllowlist": "string",
"timeZone": "string",
"week": [
[
{
"endMinute": 0,
"startMinute": 0
}
]
]
}'

GET /api/v1/console/users/{userId}/passkeys

Section titled “GET /api/v1/console/users/{userId}/passkeys”

GETuser:manageshared facehosteditmspremoteIdempotent

Source: [object Object]

List user passkeys

NameInTypeRequiredDescription
userIdpathstringYes
StatusBodyDescription
200application/json, objectSuccess.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold user:manage.
404application/problem+json, ProblemDetailsNo resource matches that id.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X GET 'https://depot.example.com/api/v1/console/users/<userId>/passkeys' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

DELETE /api/v1/console/users/{userId}/passkeys/{passkeyId}

Section titled “DELETE /api/v1/console/users/{userId}/passkeys/{passkeyId}”

DELETEuser:manageshared facehosteditmspremoteIdempotent

Source: [object Object]

removeUserPasskey is an owner taking a passkey off somebody’s account: a lost laptop, a departed contractor’s key.

Owners only, on top of user:manage, because it changes how somebody else signs in.

The person’s sessions end and their remembered browsers are forgotten: the usual reason to do this is that the passkey is in the wrong hands, and a session it opened is in the same hands. An owner removing their own from this page keeps their own session; the account page is the ordinary route for that and asks for nothing more.

NameInTypeRequiredDescription
passkeyIdpathstringYes
userIdpathstringYes
StatusBodyDescription
204NoneSuccess, with no body.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold user:manage.
404application/problem+json, ProblemDetailsNo resource matches that id.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X DELETE 'https://depot.example.com/api/v1/console/users/<userId>/passkeys/<passkeyId>' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

PUT /api/v1/console/users/{userId}/roles/{roleCode}

Section titled “PUT /api/v1/console/users/{userId}/roles/{roleCode}”

PUTrole:manageshared facehosteditmspremoteIdempotent

Source: [object Object]

Grant role

NameInTypeRequiredDescription
roleCodepathstringYes
userIdpathstringYes
StatusBodyDescription
204NoneSuccess, with no body.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold role:manage.
404application/problem+json, ProblemDetailsNo resource matches that id.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X PUT 'https://depot.example.com/api/v1/console/users/<userId>/roles/<roleCode>' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

DELETE /api/v1/console/users/{userId}/roles/{roleCode}

Section titled “DELETE /api/v1/console/users/{userId}/roles/{roleCode}”

DELETErole:manageshared facehosteditmspremoteIdempotent

Source: [object Object]

Revoke role

NameInTypeRequiredDescription
roleCodepathstringYes
userIdpathstringYes
StatusBodyDescription
204NoneSuccess, with no body.
401application/problem+json, ProblemDetailsNo signed-in identity.
403application/problem+json, ProblemDetailsThe identity does not hold role:manage.
404application/problem+json, ProblemDetailsNo resource matches that id.
Terminal window
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X DELETE 'https://depot.example.com/api/v1/console/users/<userId>/roles/<roleCode>' \
-H 'X-CSRF-Token: <csrf-token>' \
-H 'Accept: application/json' \
-b 'outpost_console=<console-session>'

Content is licensed CC BY 4.0. Code samples are MIT. Outpost and the Outpost mark are trademarks of Outpost Business Solutions, PBC and are not covered by either license.