13 KiB
title, tags
| title | tags | |||
|---|---|---|---|---|
| Admin API |
|
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')afterauthenticateToken(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
resolverrole was added (commitfce8a19). Resolvers have access to the dispute-triage endpoints (assign,status,resolve,statistics) only. All other admin endpoints remainadmin-only.
User management
See full descriptions in User API.
Path note: The frontend uses
/api/users/admin/*(plural — legacyuserRoutes). The singular/api/user/admin/*group (newuserController) is also mounted (app.ts). Since backend14c231e(v2.8.50) the plural group delegatestoggle-statusanddependenciesto 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-verificationis 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.50–51): 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. 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:
{
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/amlupdatesprocess.envat 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 viaAML_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 commit31343d1(Task #10). List is fetched fromOFAC_SDN_URL(defaults tohttps://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/historyis now implemented (commit27fb15a). It reads from theConfigSettingHistorycollection, keyed asconfirmation_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. TheisBreakGlassActive()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 fromsuccessfulPaymentscount)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)