Manage the back-office admin accounts themselves: list, invite, update roles/cities,
and revoke access. Paths are relative to the /api/v1 prefix; these are back-office
endpoints requiring a super-admin token.
Requires a super-admin bearer token (see Admin auth). Super-admin only.
See Common errors.
The role is super_admin or admin. A plain admin is restricted to the cities in
its cities list; a super_admin has no city restriction (any city_ids sent for a
super-admin are cleared).
| Method | URI | Access | Description |
|---|---|---|---|
GET |
/admin/users |
Super admin | List every admin account |
POST |
/admin/users |
Super admin | Create an admin account |
PATCH |
/admin/users/{adminId} |
Super admin | Update name, role, cities, or password |
DELETE |
/admin/users/{adminId} |
Super admin | Deactivate an admin (revokes access) |
List and single responses present each admin the same way:
{
"id": 5,
"name": "Ops Caracas",
"email": "ops@aguita.app",
"role": "admin",
"is_active": true,
"cities": [ { "id": 1, "name": "Caracas" } ],
"last_activity_at": "2026-06-29T12:00:00+00:00",
"created_at": "2026-06-01T09:00:00+00:00"
}
cities is empty for super-admins. last_activity_at is the admin's most recent
activity, or null if they have never authenticated.
Lists all admins (newest first). Not paginated.
Response 200
{ "items": [ { "id": 5, "name": "Ops Caracas", "role": "admin", "is_active": true } ] }
Creates an admin. If password is omitted, a temporary one is generated and returned
once in temporary_password so it can be shared with the invitee. city_ids are
applied only when the new admin is a plain admin.
Request body
| Field | Type | Required | Rules |
|---|---|---|---|
name |
string | yes | ≤ 120 chars |
email |
string | yes | valid email, ≤ 160 chars, unique among admins |
role |
string | no | admin (default) or super_admin |
password |
string | no | 8–72 chars; if omitted, one is generated |
is_active |
boolean | no | defaults to true |
city_ids |
integer[] | no | each must exist in cities; ignored for super-admins |
Response 201
{
"admin": { "id": 9, "name": "Ops Valencia", "role": "admin", "is_active": true },
"temporary_password": "Ab3xK9mQ2pLz"
}
temporary_password is present only when no password was supplied.
Updates an admin. Promoting to super_admin clears the admin's cities; for a plain
admin, sending city_ids replaces the city list.
Request body (all optional)
| Field | Type | Required | Rules |
|---|---|---|---|
name |
string | no | ≤ 120 chars |
email |
string | no | valid email, ≤ 160 chars, unique among admins (ignoring self) |
role |
string | no | admin or super_admin |
password |
string | no | 8–72 chars |
is_active |
boolean | no | — |
city_ids |
integer[] | no | each must exist in cities |
Response 200
{ "admin": { "id": 5, "name": "Ops Caracas", "role": "super_admin", "is_active": true } }
Errors
| Status | Body | When |
|---|---|---|
404 |
{ "error": "AdminNotFound" } |
No admin with that id |
422 |
{ "error": "CannotDemoteSelf", "message": "…" } |
You tried to remove your own super-admin role |
422 |
{ "error": "LastSuperAdmin", "message": "…" } |
Would demote/deactivate the last active super-admin |
Deactivates the admin. This is not a hard delete.
Response 200
{ "ok": true, "deactivated": true }
Errors
| Status | Body | When |
|---|---|---|
404 |
{ "error": "AdminNotFound" } |
No admin with that id |
422 |
{ "error": "CannotRemoveSelf", "message": "…" } |
You tried to deactivate your own account |
422 |
{ "error": "LastSuperAdmin", "message": "…" } |
Would deactivate the last active super-admin |