Files
nick-doc/03 - API Reference/Admin API.md

263 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Admin API
tags: [api, admin, reference]
---
# Admin API
> **Last updated:** 2026-05-30 — break-glass endpoints added, scanner/status auth fixed, reload/probe routes now implemented, confirmation threshold history implemented, resolver role added
There is no single `/api/admin` namespace — admin-only endpoints are scattered across the service routers. This page catalogs them in one place. All require `Bearer JWT` with `req.user.role === 'admin'` unless explicitly noted otherwise. The two enforcement patterns are:
- Middleware: `authorizeRoles('admin')` after `authenticateToken` (used by the dispute, data-cleanup, blog routers).
- Inline check inside the handler: `if (req.user.role !== 'admin') return 403` (used by user, points, payment routes).
> [!note] Resolver role
> The `resolver` role was added (commit `fce8a19`). Resolvers have access to the dispute-triage endpoints (`assign`, `status`, `resolve`, `statistics`) only. All other admin endpoints remain `admin`-only.
## User management
See full descriptions in [[User API]].
> **Path note:** The frontend uses `/api/users/admin/*` (plural — legacy `userRoutes`). The singular `/api/user/admin/*` group (new `userController`) **is also mounted** (`app.ts`). Since backend `14c231e` (v2.8.50) the plural group delegates `toggle-status` and `dependencies` to the new controller so every frontend call routes. Prefer `/api/users/admin/*` for user-management calls.
| Endpoint | Action |
| --- | --- |
| `POST /api/users/admin/create` | Create user with role/status |
| `DELETE /api/users/admin/:userId` | Soft delete user — sets `status='deleted'` (admins cannot delete each other) |
| `PATCH /api/users/admin/:userId/status` | Activate / suspend |
| `PATCH /api/users/admin/:userId/toggle-status` | Flip active flag |
| `PATCH /api/users/admin/:userId/role` | Change role |
| `GET /api/users/admin/list` | Paginated directory + stats |
| `GET /api/users/admin/:userId/dependencies` | Pre-delete dependency check |
| `GET /api/users/admin/stats` | Aggregate user analytics |
| `GET /api/users/admin/:userId` | Full user detail (admin view) |
| `PUT /api/users/admin/:userId` | Mass update user |
| `PUT /api/users/admin/update/:email` | Mass update by email |
| `PATCH /api/users/admin/:userId/password` | Force password reset (wipes refresh tokens) |
| `POST /api/users/admin/:userId/resend-verification` | Resend verification email (legacy route — uses 8-digit codes) |
> **Verification code length:** The endpoint `POST /api/users/admin/:userId/resend-verification` is served by the legacy userRoutes and generates **8-digit** codes. The new userController generates 6-digit codes and is reached via a different path. Both coexist; the legacy route takes precedence for this path.
**✅ FIXED (frontend `d7a2a86` / `6fe1328`, v2.8.5051):** the old PUT-verb and status-value mismatches are gone — `updateUserStatus` sends `PATCH` with `{ isActive: boolean }` (the field the legacy plural route reads).
**Soft delete + email release (backend `378f8f6`, v2.8.51):** `DELETE /api/users/admin/:userId` soft-deletes (sets `status='deleted'`) **and releases the email** (renamed to `deleted_<legacyId>_<email>`) so the address can be reused. Create/register also lazily free emails still held by accounts deleted before this fix. Soft-deleted users are excluded from the admin list and all stats (backend `14c231e`).
## Listing / marketplace moderation
See [[Marketplace API]]. Admins can use most marketplace endpoints with elevated privileges (e.g. delete any purchase request, override offer status). Specific admin-only actions:
| Endpoint | Action |
| --- | --- |
| `PUT /api/marketplace/offers/:id/status` | Direct status mutation including admin overrides |
| `POST /api/marketplace/purchase-requests/:id/release-payment` | Force escrow release |
| `PATCH /api/marketplace/purchase-requests/:id/status` (any → any) | Override request state machine |
Template approval is implicit: admins use the same template CRUD endpoints with override privileges.
## Dispute mediation
See [[Dispute API]].
| Endpoint | Action |
| --- | --- |
| `POST /api/disputes/:id/assign` | Assign moderator |
| `PATCH /api/disputes/:id/status` | Update status |
| `POST /api/disputes/:id/resolve` | Final decision (buyer / seller / split) |
| `GET /api/disputes/statistics` | Admin dashboard data |
## Manual payment operations
See [[Payment API]].
| Endpoint | Action |
| --- | --- |
| `POST /api/payment/payments/cleanup-pending` | Delete stale pending payments |
| `POST /api/payment/payments/:id/fetch-tx` | Re-query chain for missing tx hash |
| `POST /api/payment/payments/auto-fetch-missing` | Batch tx-hash backfill |
| `POST /api/payment/:id/release` | Build escrow-release tx |
| `POST /api/payment/:id/release/confirm` | Confirm release tx hash |
| `POST /api/payment/:id/refund` | Build refund tx |
| `POST /api/payment/:id/refund/confirm` | Confirm refund tx hash |
| `POST /api/payment/shkeeper/payout` | Create payout task |
| `GET /api/payment/shkeeper/webhook-stats` | Webhook telemetry |
| `POST /api/payment/decentralized/admin-payout` | Direct admin-wallet payout |
**⚠️ Path correction:** Release/refund routes do **not** include a `/shkeeper/` segment. The correct paths are `/api/payment/:id/release`, `/api/payment/:id/release/confirm`, etc. (Previously documented incorrectly as `/api/payment/shkeeper/:id/…`.)
## Derived destinations & sweep
Frontend page: `/dashboard/admin/derived-destinations`. Backend registers 7 endpoints under `/api/payment/derived-destinations/*` with admin auth.
| Endpoint | Action |
| --- | --- |
| `GET /api/payment/derived-destinations` | List all derived destination addresses |
| `POST /api/payment/derived-destinations/sweep/trigger` | Trigger a sweep across all destinations |
| `POST /api/payment/derived-destinations/sweep/trigger/:id` | Trigger sweep for a single destination |
| `GET /api/payment/derived-destinations/sweep/cron/status` | Get sweep cron job status |
| `POST /api/payment/derived-destinations/sweep/cron/start` | Start the sweep cron job |
| `POST /api/payment/derived-destinations/sweep/cron/stop` | Stop the sweep cron job |
| `GET /api/payment/derived-destinations/sweep/history` | Sweep history log |
> Frontend action functions: `getDerivedDestinations`, `triggerSweep`, `triggerSingleSweep`, `getSweepCronStatus`, `startSweepCron`, `stopSweepCron`.
## Points (admin)
See [[Points API]].
| Endpoint | Action |
| --- | --- |
| `POST /api/points/admin/add` | Manually grant / deduct points for a user |
## Data cleanup
Router: [`backend/src/services/admin/dataCleanupRoutes.ts`](../../backend/src/services/admin/dataCleanupRoutes.ts). Mounted under `/api/admin/cleanup/*`. The router applies `authenticateToken` + `authorizeRoles('admin')` to every endpoint.
### GET /api/admin/cleanup/stats
**Description:** Per-collection document counts and sizes.
**Response 200:** `{ success, data: { collections: [{ name, count, sizeBytes }] } }`
### GET /api/admin/cleanup/collections
**Description:** List collections that can be cleaned and the supported flags.
**Response 200:** `{ success, data: { collections, options } }`
### POST /api/admin/cleanup/clean
**Description:** Bulk delete records. Defaults to `dryRun: true` and `keepAdmins: true`.
**Request body:**
```ts
{
collections?: string[]; // default ["all"]
dryRun?: boolean; // default true
keepAdmins?: boolean; // default true
olderThanDays?: number; // optional age filter
confirm?: "DELETE_ALL_DATA"; // required for actual deletion
}
```
**Response 200:** `{ success, data: { deletedCounts, dryRun } }`
### DELETE /api/admin/cleanup/user/:userId
**Description:** Cascade-delete all data for a specific user (GDPR). Requires `?confirm=DELETE_USER_DATA` for real execution.
**Query params:** `dryRun=true|false`, `confirm=DELETE_USER_DATA`
### POST /api/admin/cleanup/temp
**Description:** Purge temporary data older than N hours (verification codes, file temp uploads).
**Request body:** `{ olderThanHours?: number }` (default 24)
### POST /api/admin/cleanup/seed-templates
**Description:** Re-runs the request templates seeder (production safe; idempotent).
### POST /api/admin/cleanup/seed-all
**Description:** Seeds users, addresses, and templates in dependency order. Used to bootstrap a fresh staging environment.
## Scanner / monitoring
### GET /api/admin/scanner/status
**Description:** Returns the current state of the AMN Pay Scanner. Proxies to `AMN_SCANNER_URL/scanner/status`.
**Auth required:** Bearer JWT (`admin`) — `authenticateToken` + `authorizeRoles('admin')` were added in commit `1d881c5`. The previously documented unauthenticated access gap (ISSUE-006) is closed.
### POST /api/admin/scanner/webhooks/retry
**Description:** Trigger a retry of failed/pending scanner webhooks.
**Auth required:** Bearer JWT (`admin`)
**Request body:** `{ intentId?: string }` — omit to retry all pending.
## Settings
### AML settings
> **⚠️ RUNTIME-ONLY PERSISTENCE:** `PATCH /api/admin/settings/aml` updates `process.env` at runtime only. Changes are **lost on server restart**. There is no frontend page for these endpoints.
| Endpoint | Auth | Action |
| --- | --- | --- |
| `GET /api/admin/settings/aml` | admin | Read current AML settings |
| `PATCH /api/admin/settings/aml` | admin | Update AML settings (runtime only — not persisted to disk or DB) |
**AML providers available:**
- **Chainalysis** — cloud API provider (requires `CHAINALYSIS_API_KEY`). Enabled via `AML_PROVIDER=chainalysis`.
- **OFAC SDN local** — downloads the US Treasury SDN XML list once per 24 hours and checks addresses locally. No API key required. Enabled via `AML_PROVIDER=ofac`. Added in commit `31343d1` (Task #10). List is fetched from `OFAC_SDN_URL` (defaults to `https://www.treasury.gov/ofac/downloads/sdn.xml`).
The active provider is selected at startup via `AML_PROVIDER`. `PATCH /api/admin/settings/aml` can switch the provider at runtime but the change is not persisted.
### Confirmation thresholds
Frontend page exists. Endpoints require admin auth.
| Endpoint | Action |
| --- | --- |
| `GET /api/admin/settings/confirmation-thresholds` | Get current confirmation thresholds for all chains |
| `PATCH /api/admin/settings/confirmation-thresholds/:chainId` | Update threshold for a specific chain |
| `GET /api/admin/settings/confirmation-thresholds/history` | Last 50 threshold change events (populated with `changedBy` user email/name) |
> **History route:** `GET /api/admin/settings/confirmation-thresholds/history` is now implemented (commit `27fb15a`). It reads from the `ConfigSettingHistory` collection, keyed as `confirmation_threshold:<chainId>`.
### Break-glass (Trezor bypass)
Three endpoints manage the break-glass mode, which disables the Trezor safekeeping requirement for escrow release/refund for up to 1 hour. All changes fire a Telegram alert.
| Endpoint | Action |
| --- | --- |
| `GET /api/admin/settings/break-glass` | Read current break-glass status (active, expiresAt, activatedBy) |
| `POST /api/admin/settings/break-glass` | Activate break-glass for 1 hour |
| `DELETE /api/admin/settings/break-glass` | Cancel break-glass before it expires |
> [!warning] In-memory state
> Break-glass state is stored in-memory only (`breakGlassRoutes.ts`). A server restart always clears it, which is intentional. The `isBreakGlassActive()` helper is exported and consumed by the Trezor safekeeping middleware.
## Payments awaiting confirmation
Frontend page exists.
| Endpoint | Auth | Action |
| --- | --- | --- |
| `GET /api/admin/payments/awaiting-confirmation` | admin | List payments pending blockchain confirmation |
## RN network registry
Frontend page exists.
| Endpoint | Auth | Action |
| --- | --- | --- |
| `GET /api/admin/rn/networks` | admin | List all registered RN networks |
| `POST /api/admin/rn/networks/reload` | admin | Reload chain + token registries from disk (no restart needed) |
| `POST /api/admin/rn/networks/probe/:chainId` | admin | On-demand on-chain probe: RPC reachability, proxy bytecode, dummy-call validity |
> All three routes are implemented (commit `5681abf`). Previous docs listed reload and probe as not implemented.
## Blog admin
Backend registers 5 blog admin endpoints, all guarded by `authorizeRoles('admin')`. Frontend has action functions calling each.
| Endpoint | Action |
| --- | --- |
| `GET /api/blog/admin/posts` | List all blog posts (admin view, includes drafts) |
| `POST /api/blog/posts` | Create a new blog post |
| `GET /api/blog/admin/posts/:id` | Get a single blog post (admin view) |
| `PUT /api/blog/posts/:id` | Update a blog post |
| `DELETE /api/blog/posts/:id` | Delete a blog post |
## Analytics
There is no dedicated analytics router. Admin dashboards stitch together:
- `GET /api/users/admin/stats` (user metrics)
- `GET /api/payment/stats` (payment aggregates — note: `'completed'` status is excluded from `successfulPayments` count)
- `GET /api/disputes/statistics` (dispute KPIs)
- `GET /api/admin/cleanup/stats` (collection sizes)
- `GET /api/payment/shkeeper/webhook-stats` (provider health)
- `GET /api/payment/shkeeper/wallet-monitor/status` (chain monitor)
## Related
- [[Admin Console Architecture]]
- [[Authorization Model]]
- [[Error Codes]]