Wallet
Panel Guide
End Users

Panel — End Users

Who calls these endpoints?Merchant panel
Base path
/api/panel/v1/wallet
Identity
Panel session (JWT) — Authorization: Bearer <panel access token>. The token comes from panel login; an s2s signing key does NOT open these endpoints.
Authorization
RBAC permission: wallet:enduser:read · wallet:enduser:manage · wallet:enduser:kyc-override · wallet:enduser:kyc-approveFour-eyes: the requester and the approver cannot be the same person — this is enforced by identity comparison, not by a role gate.
Cross-surface
PARTIAL: 12 of the end-user and corporate-account endpoints also exist in the s2s API (GET/POST end-users, GET end-users/:id, /balances, /limits, GET /kyc-level, POST end-users/:id/freeze, corporate-accounts …). The remaining 8 are panel-only: end-users/:id/close, /unfreeze, /wallets, /kyc-level/history, /kyc-level/override-requests, /merchant-category, GET corporate-accounts/:id, PATCH corporate-accounts/:id.

Where you view customer records and manage their lifecycle.

Search and detail

GET end-users                   # filter by programId, accountType, status; cursor pagination
GET end-users/:id               # customer detail
GET end-users/:id/wallets       # their wallets
GET end-users/:id/balances      # their balances
GET end-users/:id/limits        # limit status
GET end-users/:id/deposit-instructions

PII is masked in listings. Use the masked value when verifying a customer on a support call; there is no need to display the full number on screen.

Freeze, unfreeze, close

POST end-users/:id/freeze       # { reason }
POST end-users/:id/unfreeze     # { reason }
POST end-users/:id/close        # { reason }
ActionEffect
FreezeThe user cannot transact; the balance stays put, reversible
UnfreezeLifts the freeze
CloseTerminates the account — irreversible
⚠️

Before closing, check the balance and open operations. Closing an account that holds a balance or has an operation in BANK_PROCESSING / AWAITING_RECON leaves the ownership of that money ambiguous.

The reason field is written to the audit record. Write the real reason rather than "test" or "n/a" — this record is your only support in an audit or a customer dispute.

KYC level

GET  end-users/:id/kyc-level
GET  end-users/:id/kyc-level/history
POST end-users/:id/kyc-level/override-requests   # { kycLevel, sourceRef, reason, effectiveAt?, expectedCurrentLevel? }

Changing the level from the panel is a four-eyes request: you open it with the wallet:enduser:kyc-override permission, and the change is applied when another person holding wallet:enduser:kyc-approve approves it. The requester is taken from your session; you cannot approve your own request. Approval flow: Four-eyes requests.

sourceRef (the basis for the decision) and a reason are required. For an upgrade the evidence is verified at approval; if you pass expectedCurrentLevel and the level changed in the meantime, the approval is rejected. The history endpoint shows when, by whom and on what basis the level changed.

⚠️

To raise a level, enter in sourceRef the id of an approved KYC session belonging to this end user. The level cannot exceed the level the session attests; four-eyes approval does not replace this requirement. To lower a level, a reference to the decision record is sufficient.

Details: KYC Levels.

Crypto balances

GET crypto/balances?swapAccountId=<id>

Permission: wallet:enduser:read

An admin view of the crypto asset balances of a swap wallet account.

The panel is read-only here. Quoting, executing a swap and transferring are not in the panel — those are customer money movements and happen through the portal surface. Executed swaps already appear in the normal operation list.

Corporate accounts and the employee roster

POST  corporate-accounts
GET   corporate-accounts
GET   corporate-accounts/:id
PATCH corporate-accounts/:id
POST  corporate-accounts/:id/employees/import
POST  corporate-accounts/:id/employees/offboard
GET   end-users?corporateAccountId=<id>          # roster listing

Corporate account management uses this section's permissions (wallet:enduser:*); there is no separate permission family.

⚠️

Bulk import is all-or-nothing, and offboarding an employee does not touch money — their balance stays with them. Full behaviour of both: Corporate Accounts & Employee Management.

Spend policy

PUT end-users/:id/merchant-category

Assigns a category to a merchant; category-based spend rules read this field. The full rule engine: Spend Policy.

Support scenarios

ScenarioWhere to look
"My money never arrived"Operations → status; AWAITING_RECON means pending
"My transfer was rejected"The user's limit status and KYC level
"I can't sign in"User status (frozen?), session/device records
"My balance is short"Operation list + fee fields (feeTotal, taxTotal)
"Suspicious transaction"Operation detail → fraudDecision; freeze if needed

Things to watch

TopicWhy it matters
Freeze is reversible, close is notWhen in doubt, freeze first
Do not close accounts with a balanceMoney ownership becomes ambiguous
Make reason realIt is your only evidence in an audit
Level equals authorityRaising it takes effect immediately