Appearance
Admin panel API
Endpoints exposed by admin-backend/mono-backend (Express) behind the admin panel. Paths are written from their mount point; prepend /api in local development. See conventions.
Wallets
Mounted at /wallets (routes/WalletRoutes.js → controllers/WalletController.js).
| Method | Path | Purpose |
|---|---|---|
| GET | /wallets/:adminId | List all fiat wallets |
| GET | /wallets/all/:adminId | List all category wallets |
| GET | /wallets/bonus/:adminId | List all bonus wallets |
| PUT | /wallets/:walletId/deposit/:adminId | Manually credit a wallet |
| PUT | /wallets/:walletId/withdraw/:adminId | Manually debit a wallet |
| PUT | /wallets/:walletId/bonus/:adminId | Credit a bonus wallet |
| POST | /wallets/add/:adminId | Create a wallet |
Credit a wallet (manual top-up)
PUT /wallets/:walletId/deposit/:adminId — WalletController.deposit.
Request body:
| Field | Type | Notes |
|---|---|---|
amount | number | Required, positive |
method | string | fiat | correction | bonus — this is the operator's choice |
type | string | Sent as "Admin" by the panel; stored as the transaction's provider |
note | string | Optional internal note |
idempotency_key | string (UUID) | De-duplication |
Behaviour:
- The wallet is credited immediately and a
DepositTransactionis written withstatus: "success",confirmed_at: now, andcreatedByresolved fromadminId. - The stored
methodis not the same string you send. The controller maps it:correction → "correction",bonus → "bonus", anything else (i.e.fiat)→ "cash". Theproviderfield is set totype("Admin"). method === "bonus"sets an internalisBonusflag: the credit is excluded from deposit totals, the Customer.io deposit event, affiliate deposit events, referral FTD and dashboard deposit metrics.- A
fiatcredit is the only one that behaves as a real deposit — it feeds metrics, affiliate attribution and deposit-bonus evaluation. See Crediting and debiting a player.
Responses: 404 wallet not found; 200 with the wallet and transaction.
Permission (panel): players.list.detail.payment.deposit.create.
Debit a wallet (manual withdrawal)
PUT /wallets/:walletId/withdraw/:adminId — WalletController.withdrawal.
Request body: same shape as credit, but method is fiat | correction only.
Behaviour:
- Refuses with
400 Insufficient fundsif the wallet total is below the amount — you cannot go negative. - Refuses with
409if theidempotency_keywas already used, returning the existing transaction. - On success the wallet is debited and a
WithdrawalTransactionis written withstatus: "success",isApproved: true,approved.approved_by: adminId, andretry_count: 5. - Stored
method:correction → "correction", otherwise→ "cash".provideris set totype.addressDatais written with null bank fields (there is no payout destination).
Permission (panel): players.list.detail.payment.withdrawal.create.
Wallet transactions
Mounted at /wallet-transactions (routes/walletTransactionsRoutes.js). Read-only history and listing, all GET.
| Path | Purpose |
|---|---|
/wallet-transactions/:adminId | All transactions |
/wallet-transactions/deposit/:userId/:adminId | A player's deposits |
/wallet-transactions/general-deposits/:adminId | Deposits across players (filter by provider, e.g. manual_bank) |
/wallet-transactions/general-withdrawals/:adminId | Withdrawals across players |
/wallet-transactions/withdraw-bank-accounts/:adminId | Saved player bank accounts |
/wallet-transactions/:walletId/:adminId | One wallet's transactions |
/wallet-transactions/:walletId/all-transactions/:adminId | One wallet, all transaction types |
/wallet-transactions/:walletId/all-transactions-with-casino/:adminId | As above, including casino play |
Withdrawal review
Mounted at /withdrawal-transactions (routes/withdrawalTransactionRoutes.js → controllers/withdrawalTransaction.js). This is the queue for player-requested withdrawals.
| Method | Path | Purpose |
|---|---|---|
| GET | /withdrawal-transactions/unapproved/:adminId | The review queue (filtered) |
| GET | /withdrawal-transactions/:walletId/:adminId | One wallet's withdrawal history |
| GET | /withdrawal-transactions/by-user/:playerId/:adminId | A player's withdrawals (paginated, exportable) |
| POST | /withdrawal-transactions/approve/:transactionId/:adminId | Approve and pay out |
| PUT | /withdrawal-transactions/reject/:transactionId/:adminId | Reject and refund |
| PUT | /withdrawal-transactions/approve/retry/:transactionId/:adminId | Retry a failed payout |
:transactionId here is the readable tx_id, not the Mongo _id.
The review queue
GET /withdrawal-transactions/unapproved/:adminId returns withdrawals that are not settled (pending, plus recent error/rejected). Query filters:
| Filter | Values |
|---|---|
state | pending | success | error | rejected |
provider | Case-insensitive. AlphaPo matches that gateway; Admin (aliases by_admin, offline, manual) matches manual admin adjustments; gateway excludes manual adjustments |
paymentSystemName | Provider name (older alias for provider) |
paymentSystemId | External payment-system transaction id |
currency | Currency code, e.g. EUR |
isVerifiedPlayer | yes | no |
playerId / username | Target one player |
| date ranges | requestStartDate/requestEndDate, updatedStartDate/updatedEndDate, paidStartDate/paidEndDate |
Approve
POST /withdrawal-transactions/approve/:transactionId/:adminId. Optional body { note }; for manual bank withdrawals a bank_reference (the UTR / transfer reference) is required and is forwarded to the bank payment step. Only a pending transaction can be approved; anything else returns 400. On success isApproved and approved.* are set and the provider is asked to execute the payout.
Reject
PUT /withdrawal-transactions/reject/:transactionId/:adminId. Required body:
| Field | Purpose |
|---|---|
agentNote | Internal note, agents only |
playerMessage | The message shown to the player |
Only a pending transaction can be rejected. The handler sets status: "rejected", refunds the held amount to the wallet, records rejection.reject_reason_code: "ADMIN_REJECTED", creates a PlayerNotification with playerMessage, and fires a withdrawal_rejected event. Because the refund is automatic, do not credit the player again. See Reviewing withdrawals.
Retry
PUT /withdrawal-transactions/approve/retry/:transactionId/:adminId. Only a transaction in error status can be retried, and only within its retry_count (default 5); otherwise 400.
Manual bank
Mounted at /bank-payment (routes/bankPaymentRoutes.js → controllers/bankPaymentController.js). Manages the brand bank accounts players pay into, and receives the approve/reject/refund callbacks. Every route is permissioned.
| Method | Path | Permission |
|---|---|---|
| POST | /bank-payment/accounts | management.payment.manualBank.create |
| GET | /bank-payment/accounts | management.payment.manualBank.view |
| PUT | /bank-payment/accounts/:id | management.payment.manualBank.edit |
| PATCH | /bank-payment/accounts/:id/status | management.payment.manualBank.edit |
| DELETE | /bank-payment/accounts/:id | management.payment.manualBank.delete |
| POST | /bank-payment/callback | management.payment.manualBank.edit |
| POST | /bank-payment/withdrawal/callback | management.payment.manualBank.edit |
Create / update an account
Body for POST /bank-payment/accounts (create requires all core fields; update makes them optional):
| Field | Required on create | Notes |
|---|---|---|
bank_name | Yes | |
account_number | Yes | Stored encrypted |
account_holder_name | Yes | Stored encrypted |
ifsc_code | Yes | Stored encrypted |
currency | Yes | Uppercased |
deposit_min_amount / deposit_max_amount | No | max ≥ min enforced |
withdrawal_min_amount / withdrawal_max_amount | No | max ≥ min enforced |
instructions | No | Shown to the player |
order | No | Display order |
isActive | No |
DELETE is a soft delete (isDeleted: true, isActive: false) — the record is kept for the history of past transfers.
Deposit callback
POST /bank-payment/callback — processDepositCallback. Body: transaction_id, status, and depending on status: amount + bank_reference (for success), reason (for failed/refunded), and optional approved_by / rejected_by / refunded_by (auto-filled if omitted). This is what approve / reject / refund on a manual bank deposit resolve to.
Withdrawal callback
POST /bank-payment/withdrawal/callback — processWithdrawalCallback. Body: transaction_id, status, plus bank_reference (success) or reason (failed), and optional actor fields.
Payment settings
Mounted at /payment-settings (routes/paymentSettingRoutes.js). The brand-wide master switches — a singleton document keyed global.
| Method | Path | Permission |
|---|---|---|
| GET | /payment-settings/ | management.payment.view |
| PUT | /payment-settings/ | management.payment.edit |
PUT body (all optional, partial updates allowed):
deposit.cryptoEnabled boolean
deposit.localCurrencyEnabled boolean
deposit.manualBankEnabled boolean
withdraw.cryptoEnabled boolean
withdraw.localCurrencyEnabled booleanThese map to the player-app flags enableCryptoDeposit, enableLocalCurrencyDeposit, enableManualBankDeposit, allowCryptoWithdraw, enableLocalCurrencyWithdraw.
Payment methods catalogue
Mounted at /payment-methods (routes/paymentMethodRoutes.js). The catalogue of methods shown to players.
| Method | Path | Permission |
|---|---|---|
| GET | /payment-methods/ | management.payment.view |
| POST | /payment-methods/sync | management.payment.create |
| PATCH | /payment-methods/reorder | management.payment.edit |
| PATCH | /payment-methods/:id/status | management.payment.edit |
sync refreshes the canonical catalogue into the DB, preserving admin order and enabled state; :id/status toggles one entry with { isActive: boolean }.
Per-player payment blocks
A single player can be blocked from depositing or withdrawing via Player.restrictions.payment.deposit and .withdrawal (booleans, default false). There are also account-level PUT /players/:id/block/:adminId and /players/:id/unblock/:adminId endpoints.
Not fully traced
The exact endpoint that toggles restrictions.payment.deposit / .withdrawal specifically (versus the whole-account block above) was not pinned down in this pass. The field on the Player model is confirmed; the setter route is not. Confirm in the code before integrating.