Files
nick-doc/03 - API Reference/Admin API.md
Siavash Sameni dceaf82934 audit: 2026-05-30 full-codebase audit — report, issues, docs, runbooks
Full-codebase-audit 2026-05-30 outputs:
- Audit report: 09 - Audits/Full Codebase Audit - 2026-05-30.md
- 81 issue files ISSUE-055..135 (decisions + 1 skipped no-brainer).
- Scanner docs from scratch (was zero): architecture, data model, API ref, payment
  flow, operations runbook + repo README.
- Doc-sync updates across API reference, data models, flows, design system.
- Secret Rotation Runbook (08 - Operations) for the exposed credentials.
- Reusable workflow guide (07 - Development) + .claude/workflows/full-codebase-audit.js.

Issues remain status:open intentionally — the code fixes are uncommitted-then-committed
working-tree changes per repo and aren't "resolved" until merged/deployed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 18:48:04 +04:00

13 KiB

title, tags
title tags
Admin API
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 and backend both use /api/users/admin/* (plural). The singular /api/user/admin/* paths for create/delete/status/role/list are unreachable — they are not mounted in the backend. Use /api/users/admin/* for all 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.

⚠️ KNOWN BUG — HTTP verb mismatch (status/role updates): The frontend Redux actions for updateUserStatus and updateUserRole send PUT requests, but the backend registers these handlers under PATCH. These calls will receive 404 Method Not Found responses until the frontend is corrected to use PATCH.

⚠️ KNOWN BUG — Status value mismatch: The frontend sends 'inactive' and 'pending' as status values when updating user status. The backend only accepts 'active', 'suspended', or 'deleted'. Sending 'inactive' or 'pending' will be rejected or silently ignored.

Hard vs. soft delete note: The legacy route DELETE /users/admin/:id performs a hard delete (findByIdAndDelete). The current route DELETE /api/users/admin/:userId performs a soft delete (sets status='deleted'). Always use the current /api/users/admin/:userId route to preserve data integrity.

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/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)