Staffs
Staffs
Staff endpoints manage human Operator Dashboard accounts.
These endpoints require a staff-session bearer token. Operator API keys cannot call them even if the API key has broad scopes.
Auth paths
POST /api/operator/auth/staff-login
POST /api/operator/auth/staff-change-password
POST /api/operator/auth/logout
staff-login returns a bearer token with prefix pmost. and a 12-hour expiry.
staff-login does not require an existing bearer token. Its JSON body is:
| Field | Type | Requirement | Notes |
|---|---|---|---|
email | string | Required | Valid email address. |
password | string | Required | Non-empty password. |
Login is limited to 5 failed attempts per normalized email and source identifier in a 15-minute window. A blocked attempt returns 429 RATE_LIMIT_EXCEEDED; wait for the window instead of retrying unchanged credentials.
staff-change-password is available only to staff sessions. newPassword is required and must contain at least 8 characters. oldPassword is conditionally required when must_change_password is false; it is optional for a forced first change. Successful change returns a replacement staff token and expiry because all previous sessions are revoked.
logout requires a valid bearer token and has no body. It revokes the current staff session when the bearer token is a staff token. An API-key call returns ok: true without revoking the key.
Staff management paths
GET /api/operator/staff
POST /api/operator/staff
PATCH /api/operator/staff/{membership_id}
POST /api/operator/staff/{membership_id}/reset-password
Required scopes:
staffs:readfor liststaffs:writefor create, patch, and reset password
All staff management paths also require auth_type = staff.
List query params
Both list query parameters are optional:
| Parameter | Type | Requirement | Notes |
|---|---|---|---|
search | string | Optional | Case-insensitive match against email or display name. |
status | string | Optional | active or disabled; an omitted or unrecognized value returns all. |
Staff fields
membership_idadmin_user_idemaildisplay_namerolestatusaccount_statusscopesmust_change_passwordlast_login_atcreated_atupdated_at
Create body
{
"email": "ops@example.com",
"display_name": "Ops User",
"scopes": ["users:read", "trades:read"]
}
| Field | Type | Requirement | Notes |
|---|---|---|---|
email | string | Required | Valid, globally unused email. |
scopes | string | Required | Non-empty assignable scope list; * is forbidden. |
display_name | string | Optional | Stored display name. |
Rules:
- email must be valid and globally unused
scopesmust be non-empty*is not assignable in staff create/update- write scopes normalize to include the matching read scope when available
- staff can assign only scopes they already hold, unless they have
*
Patch body
All fields are optional, but at least one update is required.
{
"display_name": "Ops Lead",
"status": "disabled",
"scopes": ["users:read", "trades:read", "webhooks:read"]
}
A staff member cannot update their own access status or scopes. The implemented error is SELF_ACCESS_UPDATE_FORBIDDEN. A staff member also cannot reset their own password through the management endpoint; that returns SELF_PASSWORD_RESET_FORBIDDEN.
Patch path membership_id is required and must be a positive integer. display_name, status, and scopes are individually optional, but at least one must be supplied. Reset-password also requires the positive membership_id path parameter and has no body. Validation failures include INVALID_STAFF_ID, INVALID_STATUS, NO_UPDATES, and CANNOT_ASSIGN_SCOPE. Access or password mutations revoke active sessions for the target staff account; changing only the display name does not.
Password reset response
Create and reset responses include onboarding metadata:
{
"ok": true,
"item": {},
"onboarding": {
"email_sent": false,
"email_error": "provider error",
"temp_password": "returned-only-when-email-fails"
}
}
If email sends successfully, temp_password is null.
