Docs
Dashboard

Admin API

The Admin API powers GullyPay internal risk, compliance, and platform-operations tooling: risk monitoring, fraud detection, KYC/KYB review, transaction monitoring, merchant management, chargeback management, and revenue analytics.

Internal / admin-scoped

Every endpoint below is mounted under /admin and protected by both the JWT auth guard and a role guard requiring the platform admin role. These routes are not available to merchant or developer API keys — a normal authenticated user receives 403 Forbidden. This is an internal operations surface.

Authentication

All requests require a bearer JWT belonging to a user with the admin platform role.

Authorization header
Authorization: Bearer <admin_jwt>

Both guards apply to the entire controller:

  • JwtAuthGuard — authenticates the caller.
  • RolesGuard + @Roles('admin') — enforces the admin role.

Endpoints

MethodPathDescription
GET/admin/dashboardPlatform risk metrics (rates, blocked count, revenue MTD)
GET/admin/risk-alertsLive risk/fraud alerts derived from real rows
GET/admin/merchantsList merchants (optionally by status)
PATCH/admin/merchants/:id/statusSet a merchant account status
PATCH/admin/merchants/:id/kycRecord a KYC/KYB review decision
GET/admin/transactionsRecent platform transactions
GET/admin/audit-logsPlatform-wide audit trail
GET/admin/fraud-assessmentsPer-transaction fraud/risk assessments
GET/admin/chargebacksChargebacks / disputes with money + merchant context
PATCH/admin/chargebacks/:idUpdate a chargeback status (contest / accept / resolve)
GET/admin/usersList all users with wallet counts (optional search)
GET/admin/users/:idOne user's wallets, activity, and linked merchants
PATCH/admin/users/:id/statusLock / unlock / suspend an account
PATCH/admin/wallets/:id/statusFreeze / unfreeze a wallet

Dashboard

GET /admin/dashboard

Returns platform-wide risk metrics. Rates are decimal fractions (e.g. 0.0123 = 1.23%) and are null when there is no source data in the window (for example, no authorized transactions yet).

Query parameters

ParamValuesDefaultDescription
window24h, 7d, 30d30dRate-computation window
Response
{
"merchants": 128,
"users": 340,
"transactions": 5120,
"windowDays": 30,
"authRate": 0.9821,
"disputeRate": 0.0042,
"refundRate": 0.0113,
"fraudRate": 0.0075,
"blocked24h": 2,
"platformRevenueMtd": 184200,
"currency": "USD"
}
Amounts are integer cents

platformRevenueMtd is an integer amount in the smallest currency unit (cents) — sum of real fee_ledger_entries for the current month.

Fraud detection

GET /admin/fraud-assessments

Lists per-transaction fraud/risk assessments from the real fraud_assessments table, newest first. Returns an empty array when nothing has been scored.

Query parameters

ParamDefaultDescription
limit50Max rows to return
offset0Pagination offset
Response
[
{
  "id": "a1b2c3d4-...",
  "transactionId": "f5e6d7c8-...",
  "merchantId": "9a8b7c6d-...",
  "merchantName": "Acme Coffee",
  "riskScore": 82,
  "decision": "block",
  "reasons": [{ "code": "velocity", "detail": "10 attempts / 1m" }],
  "createdAt": "2026-07-18T09:12:04.000Z"
}
]

decision is one of allow, review, or block. riskScore is 0–100.

Chargeback management

Chargebacks are modelled as rows in the disputes table (a chargeback is a dispute). The list endpoint joins the underlying transaction so each row carries real amount, currency, and merchant context.

List chargebacks

GET /admin/chargebacks

Query parameters

ParamDefaultDescription
limit50Max rows to return
status—Optional filter by status
Response
[
{
  "id": "d1e2f3a4-...",
  "transactionId": "f5e6d7c8-...",
  "type": "chargeback",
  "status": "open",
  "reason": "fraudulent",
  "evidenceDueBy": "2026-07-25T00:00:00.000Z",
  "resolvedAt": null,
  "createdAt": "2026-07-18T09:12:04.000Z",
  "amount": 4999,
  "currency": "USD",
  "merchantId": "9a8b7c6d-...",
  "merchantName": "Acme Coffee"
}
]

Update a chargeback

PATCH /admin/chargebacks/:id

Updates a chargeback's status. Terminal statuses (won, lost, accepted) stamp resolvedAt. The transition is audit-logged with the reviewing admin under the action admin.chargeback.status_changed.

Body

FieldTypeDescription
statusstringOne of open, under_review, won, lost, accepted
notesstring?Optional reviewer note (stored in the audit log)
Contest a chargeback
curl -X PATCH https://gullypay.xyz/api/admin/chargebacks/d1e2f3a4 \
-H "Authorization: Bearer <admin_jwt>" \
-H "Content-Type: application/json" \
-d '{"status": "under_review", "notes": "Submitting evidence"}'
Response
{ "success": true, "status": "under_review" }

Merchant management

Update merchant status

PATCH /admin/merchants/:id/status

Sets a merchant account status (e.g. active, suspended, blocked). The change is audit-logged under admin.merchant.status_changed, which is a real signal feeding blocked24h and the risk alerts feed.

Body

FieldTypeDescription
statusstringOne of pending, active, suspended, deactivated, blocked, banned, rejected
reasonstring?Optional reason

KYC / KYB review

PATCH /admin/merchants/:id/kyc

Records a KYC/KYB decision. Approval activates the merchant; rejection sets status rejected. Audit-logged under admin.merchant.kyc_reviewed.

Body

FieldTypeDescription
decisionstringapproved or rejected
notesstring?Optional reviewer note
Response
{ "success": true, "decision": "approved", "status": "active" }

Users & wallets

Every account is a user with a personal wallet; becoming a merchant is an opt-in upgrade. Admins therefore need visibility and compliance controls over all users, including wallet-only ones.

GET /admin/users?search=&limit=50&offset=0 lists users (newest first) with a real per-user wallet count. search matches email / first / last name (case-insensitive).

GET /admin/users/:id returns one user's profile, their wallets (chain, address, status — never the encrypted key), recent ledger activity across those wallets, and any merchants they can access via the org/team chain. Real rows only; empty arrays when there is nothing.

Compliance actions

PATCH /admin/users/:id/status with { "status": "locked" | "suspended" | "active" }. Locking or suspending revokes all of the user's sessions, and login + token refresh fail-close on a non-active status — so the account is locked out immediately.

PATCH /admin/wallets/:id/status with { "status": "frozen" | "active" }. A frozen wallet can still be read (balances, address, history) but cannot move funds — send and swap fail-close on status = "frozen".

Both actions are audit-logged (admin.user.status_changed, admin.wallet.status_changed).

Risk monitoring

GET /admin/risk-alerts returns a real-time, derived alert feed (blocked merchants, open disputes, large refunds, failed-payment spikes, high-value pending payouts, and blocked fraud assessments). Each alert has a severity of low, medium, or high. Returns [] when nothing qualifies.

GET /admin/transactions?limit=50 returns recent platform transactions for transaction monitoring.

Audit log

GET /admin/audit-logs?limit=100 returns the platform-wide audit trail (newest first), including the admin actions above (admin.merchant.status_changed, admin.merchant.kyc_reviewed, admin.chargeback.status_changed, admin.user.status_changed, admin.wallet.status_changed).

Errors

StatusMeaning
401 UnauthorizedMissing or invalid JWT
403 ForbiddenAuthenticated user lacks the admin role
404 Not FoundReferenced merchant or chargeback does not exist