Admin & Internal Tooling
What it is
Three modules serving three different audiences: admin (the admin dashboard's general-purpose API — user lookups, quickstats, support), tooling (one-off operational actions an admin might run — reset a PIN, reprocess a stuck payment — described by a self-service metadata registry rather than hardcoded dashboard UI), and internal (system-to-system endpoints triggered by Cloud Scheduler/Cloud Tasks/Pub/Sub, never called by a human).
Core concepts
- The tooling registry is metadata-driven, not a generic dispatcher.
TOOLING_REGISTRY(tooling.registry.ts) is a static array of ~60ToolInfoentries — each describing a real endpoint'smethod,endpointpath, formfields(with type, validation, help text),category, and adestructiveflag. The admin dashboard reads this viaGET /tooling/registryto render a form per tool without per-tool custom frontend code — but it still calls each tool's real endpoint directly; the registry only describes the shape, it doesn't route the call. destructive: trueis advisory, not enforced — it's metadata for the dashboard to show a confirmation prompt (e.g. beforeDELETE /tooling/gift-purchase/:idor a bulk payment reprocess). The backend doesn't re-check this flag; if you add a new destructive tool, you're relying on the dashboard reading the registry correctly, not a server-side safety net.- All three modules are separately gated:
admin/toolingrequireadminMiddleware(a human admin's JWT with the admin role);internalendpoints have no user-auth middleware at all — they're protected by network/header-level trust (internal API tokens, Cloud Tasks headers) since there's no human session to check.
How it works
Tooling registry-described actions
GET /tooling/registry (all tools), GET /tooling/registry/by-category, GET /tooling/registry/:toolId — dashboard discovery endpoints. The actual tools span categories like users (reset PIN, wallet lookups), gift-purchases (create/update/delete/mark-paid), payment-tracking (reprocess-confirmed, retry-wallet-transfer — manual recovery for stuck payments, complementing the automatic reconciliation cron covered in Payments & Payouts), and one-off fixes (backfill-partybanks, reverse-wallet-transfers).
Admin dashboard API
Broader, more conventional admin surface: GET /admin/all-users, GET /admin/users/:id, .../transactions, .../events, .../activities, .../quickstats for user-level support/investigation; GET /admin/gift-store/quickstats, .../drink-store/quickstats for platform-level dashboards; support-category management; ban-list operations (ban-list.service.ts — see the double ban-check in the Auth guide).
Admin Actions (GET /admin/actions, .../by-category, .../:actionType) is the same self-describing-registry pattern as the tooling registry above, applied to a smaller set of admin overrides — configuration lives in src/shared/data/admin-application-forms.data.ts. The two registries are separate systems that happen to share a design, not one shared implementation; don't assume adding to one exposes in the other. Current actions: Manual KYC Update (POST /admin/kyc/manual-verify) and Manual BVN Update (POST /admin/bvn/manual-verify) let an admin force-complete a stuck verification when the automated IdentityPass/AWS Rekognition flow fails or a user can't complete it themselves — this bypasses the normal verification pipeline entirely, sets every KYC flag to verified in one transaction, clears any soft-delete on the KYC record, and fires the same push+email notifications a real verification would. Resend Guest RSVP (POST /event/:eventId/guests/resend-rsvp) is also exposed here for support use, distinct from the guest-facing version (see RSVP & Guests).
App Config (GET /app/config, GET /app/config/:flagName, under internal) is a general-purpose feature-flag system, resolved in priority order: Firebase Realtime Database (appConfig/{flagName}, editable without a deploy) → environment variable fallback → hardcoded default. Flags are cached for 5 minutes. Only whitelisted flags are exposed through the API — a flag can exist in code and gate server-side behavior without ever being exposed to clients. This is the same Firebase-config pattern used for Paystack's platform-charge percentage (Payments & Payouts) and broadcast per-channel pricing (Broadcast) — a recurring convention for "change this in production without a deploy," not unique to any one feature.
Internal (system-to-system)
Not admin-facing at all — these are the actual HTTP targets Cloud Scheduler/Cloud Tasks/Pub/Sub workers hit: RSVP batch processing (rsvp/process-batch, .../process-all-batches), reminder processing (events/process-reminder, .../schedule-pending-reminders — see Events & Event Types), broadcast delivery (broadcast/deliver, .../resend-failed/:broadcastId — see Broadcast), the weekly digest trigger, memory-sharing hourly processing, and infra health checks (database/health-check, database/pool-metrics, debug/cloud-tasks).
Key business rules & edge cases
internalendpoints trust the network, not a user — don't add a new one without confirming it has some form of caller verification (internal token header, Cloud Tasks signature); there's noauthMiddlewarefallback protecting these by default.- Adding a new tooling action means registering it in
TOOLING_REGISTRYand wiring the real route — the registry entry alone does nothing; forgetting the registry entry means the tool works but is invisible to the dashboard. - Several tooling actions are manual recovery paths for the same failure modes automatic crons already handle (e.g.
retry-wallet-transferalongside the payments reconciliation cron) — check whether the automated path already resolves an issue before reaching for the manual tool.
Related endpoints
Tooling: GET /v1/tooling/registry, PUT /v1/tooling/reset-pin/{userId}, POST /v1/tooling/payment-tracking/reprocess-confirmed.
Admin: GET /v1/admin/all-users, GET /v1/admin/users/{id}, GET /v1/admin/actions, POST /v1/admin/kyc/manual-verify, POST /v1/admin/bvn/manual-verify.
Internal: POST /v1/internal/rsvp/process-batch, GET /v1/internal/health, GET /v1/internal/app/config, GET /v1/internal/app/config/{flagName}.
None are in the generated OpenAPI spec.
Related code
src/modules/tooling/tooling.registry.ts— theToolInfo/TOOLING_REGISTRYmetadatasrc/modules/tooling/tooling.routes.ts,tooling.controller.tssrc/modules/admin/routes.ts,controller.ts,service.ts,ban-list.service.tssrc/shared/data/admin-application-forms.data.ts— Admin Actions registrysrc/shared/utils/feature-flags.util.ts— App Config priority resolutionsrc/modules/internal/internal.routes.ts,internal.controller.ts