Admin-specific API endpoints require the admin role and use assertAdmin() from @zooly/util-srv for authentication. This page covers the user management endpoint. For offer admin endpoints (bouncer queue, approve, reject), see Offers API Reference.
Lists accounts with optional search, pagination, and brand limits (blocked status).
Endpoint: GET /api/admin/users
Auth: Admin role required
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
search | string | — | Filter by display name or slug (case-insensitive) |
limit | number | 50 | Max accounts to return (max: 100) |
offset | number | 0 | Pagination offset |
Response (200):
| Field | Type | Description |
|---|---|---|
accounts | Account[] | Matching accounts |
total | number | Total count for pagination |
brandLimits | Record<string, { isBlocked, blockedReason }> | Blocked status per account |
Implementation: apps/zooly-app/app/api/admin/users/route.ts
Blocks or unblocks a user account. Blocked users cannot create or accept offers.
Endpoint: POST /api/admin/users
Auth: Admin role required
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
accountId | string | Yes | Account to block/unblock |
action | "block" or "unblock" | Yes | Action to perform |
reason | string | When blocking | Reason for block (stored in brand_limits) |
Response (200): { success: true, accountId, action }
Errors:
400 — missing accountId or action403 — not an adminDB access:
| Function | Location | Description |
|---|---|---|
blockAccount(accountId, reason, adminAccountId) | packages/db/src/access/brandLimits.ts | Upserts brand_limits with isBlocked = true |
unblockAccount(accountId, adminAccountId) | packages/db/src/access/brandLimits.ts | Sets isBlocked = false |
Implementation: apps/zooly-app/app/api/admin/users/route.ts
Resolves a disputed offer. See Dispute Resolution for full details.
Endpoint: POST /api/offers/admin/resolve-dispute
Implementation: apps/zooly-app/app/api/offers/admin/resolve-dispute/route.ts