API: System
Controllers: admin-admins.controller.ts, admin-roles.controller.ts, admin-approvals.controller.ts, admin-audit.controller.ts, admin-stats.controller.ts.
Admins
Section titled “Admins”Mount admin/admins.
| Method | Path | Perm |
|---|---|---|
| GET | /admin/admins/me | (any authenticated admin) |
| GET | /admin/admins | admins:read |
| GET | /admin/admins/:id | admins:read |
| POST | /admin/admins | admins:write |
| PUT | /admin/admins/:id | admins:write |
| DELETE | /admin/admins/:id | admins:write |
GET /admin/admins/me
Section titled “GET /admin/admins/me”Returns the current admin’s profile including resolved permissions array. Called by the admin UI on every navigation to populate the permission-aware sidebar.
Create
Section titled “Create”{ "email": "newuser@twistcue.local", "name": "New User", "roleId": "0192...", "password": "temporary-please-reset"}Mount admin/roles.
| Method | Path | Perm |
|---|---|---|
| GET | /admin/roles | roles:read |
| GET | /admin/roles/:id | roles:read |
| POST | /admin/roles | roles:manage |
| PUT | /admin/roles/:id | roles:manage |
| DELETE | /admin/roles/:id | roles:manage |
Create
Section titled “Create”{ "name": "compliance", "description": "PII compliance officer", "permissions": ["users:read", "users:anonymize", "audit:read"]}- Role name must be unique; cannot collide with the 6 system role names.
permissions: array of valid permission strings; refused if any is unknown.- Role changes may go through the approvals queue depending on config.
Approvals
Section titled “Approvals”Mount admin/approvals.
| Method | Path | Perm |
|---|---|---|
| GET | /admin/approvals | approvals:read |
| GET | /admin/approvals/:id | approvals:read |
| POST | /admin/approvals/:id/approve | approvals:decide + the underlying resource perm |
| POST | /admin/approvals/:id/reject | approvals:decide + the underlying resource perm |
Query: status (pending|approved|rejected), actionType, requestedBy, cursor, limit.
Approval shape
Section titled “Approval shape”{ "id": "...", "actionType": "refund.execute", "payload": { "purchaseId": "...", "amountCents": 8000, "reason": "..." }, "requestedBy": "adminId-1", "approvedBy": null, "status": "pending", "resolvedAt": null, "createdAt": "..."}Approve
Section titled “Approve”{ "note": "Reviewed refund policy; approved." }Response: the executed underlying action (e.g. the refund row with status='executed').
Error 403: approver_must_differ if requested_by == req.admin.sub.
Reject
Section titled “Reject”{ "note": "Insufficient documentation; requestor to resubmit with support ticket URL." }Audit log
Section titled “Audit log”Mount admin/audit-logs.
| Method | Path | Perm |
|---|---|---|
| GET | /admin/audit-logs | audit:read |
Query: adminId, action (glob), refType, refId, dateFrom, dateTo, cursor, limit.
Entry shape
Section titled “Entry shape”{ "id": "...", "adminId": "...", "adminEmail": "finance@twistcue.local", "action": "refund.execute", "refType": "refund", "refId": "...", "before": { /* prior state */ }, "after": { /* new state */ }, "ip": "1.2.3.4", "userAgent": "Mozilla/5.0...", "createdAt": "..."}Stats (legacy)
Section titled “Stats (legacy)”Mount admin/stats.
| Method | Path | Perm |
|---|---|---|
| GET | /admin/stats | analytics:read |
Superseded by /admin/analytics/overview but kept for backward compatibility with the P1 dashboard.