User profile¶
Purpose¶
The User Profile screen is the per-user 360° view: identity, balance, KYC, bets, deposits/withdrawals, notes, restrictions, and audit history. It also exposes the most consequential admin operations on the platform: balance edit, ban/unban, role changes, withdrawal block, KYC level override. Every destructive action requires OtpGuard (live MFA code) or SuperAdmin.
Audience¶
Customer support, compliance, fraud / risk, finance (for balance / vault adjustments). SuperAdmin-only operations are flagged below.
Path in admin-fe¶
| Route | Page | Component |
|---|---|---|
/users/[id] |
ebit-admin-fe/src/app/(dashboard)/users/[id]/page.tsx |
components/pages/userDetails/ |
The profile is a tabbed view (Overview / Balance / KYC / Notes / Restrictions / Audit). Tabs are wired into components/common/UserProfileOptions/.
Backing API endpoints¶
| Endpoint | Source |
|---|---|
GET /admin/user/:id |
apps/api/src/user/admin.user.controller.ts:248 |
GET /admin/user/:id/full/stats |
apps/api/src/user/admin.user.controller.ts:239 |
PATCH /admin/user/:id (basic edit) |
apps/api/src/user/admin.user.controller.ts:118 |
PATCH /admin/user/:id/avatar/reset |
apps/api/src/user/admin.user.controller.ts:130 |
PATCH /admin/user/:id/ban |
apps/api/src/user/admin.user.controller.ts:139 |
PATCH /admin/user/:id/unban |
apps/api/src/user/admin.user.controller.ts:152 |
PUT /admin/user/:id/balance (SuperAdmin + OTP) |
apps/api/src/user/admin.user.controller.ts:164 |
PUT /admin/user/:id/roles (SuperAdmin + OTP) |
apps/api/src/user/admin.user.controller.ts:180 |
PUT /admin/user/:id/permissions (SuperAdmin) |
apps/api/src/user/admin.user.controller.ts:194 |
GET /admin/registration-info/:userId |
apps/api/src/user/registration-info/admin-registration-info.controller.ts:22 |
GET /admin/user-notes (list) / POST / DELETE :id / PATCH |
apps/api/src/user/notes/admin.notes.controller.ts:32-60 |
GET /admin/withdrawals-block/:userId, POST, DELETE |
apps/api/src/user/withdrawals-block/admin.withdrawals-block.controller.ts:28-47 |
GET /admin/kyc/:userId/info, PATCH /admin/kyc/:userId, POST /admin/kyc/:userId/info |
apps/api/src/kyc/controller/admin.kyc.controller.ts:37-59 |
GET /admin/accounting/usd-balance/:userId |
apps/api/src/accounting/controllers/admin.accounting.controller.ts:47 |
GET /admin/accounting/balances?userId= |
apps/api/src/accounting/controllers/admin.accounting.controller.ts:56 |
GET /admin/accounting/vault-balances?userId= |
apps/api/src/accounting/controllers/admin.accounting.controller.ts:67 |
GET /admin/rakeback/find-one-claimable-usd?userId= |
apps/api/src/rakeback/admin.rakeback.controller.ts:33 |
POST /admin/user-limits/user-exclusion / DELETE / GET :id |
apps/api/src/users-limits/admin/admin.user-limits.controller.ts:32-50 |
POST /admin/user-limits/gambling-limits / GET :id |
apps/api/src/users-limits/admin/admin.user-limits.controller.ts:59-68 |
POST /admin/admin-tips (tip user) / POST /admin/admin-tips/deduct-balance |
apps/api/src/admin-tips/admin.admin-tips.controller.ts:28-38 |
Frontend wiring: ebit-admin-fe/src/queries/users/index.ts (most of it). Notes: same file. KYC: same file (useGetUserKycQuery, useUpdateUserKycMutation). Limits: queries/users/index.ts useGetUserAccountingBalancesQuery, useGetUserVaultBalancesQuery.
Key actions¶
| Action | Required role / permission | API call | DB tables touched | Audit-logged? |
|---|---|---|---|---|
| View profile | user.view |
GET /admin/user/:id |
User + relations |
yes |
| Edit basic fields (email, username, language) | user.edit |
PATCH /admin/user/:id |
User |
yes |
| Reset avatar | user.edit |
PATCH /admin/user/:id/avatar/reset |
User.avatar |
yes |
| Ban | user.ban |
PATCH /admin/user/:id/ban |
User.bannedAt, BanLog, sessions revoked |
yes |
| Unban | user.ban |
PATCH /admin/user/:id/unban |
User.bannedAt = null, BanLog |
yes |
| Edit balance | SuperAdmin + OTP |
PUT /admin/user/:id/balance |
Account, Transaction |
yes |
| Edit roles | SuperAdmin + OTP |
PUT /admin/user/:id/roles |
UserRole |
yes |
| Edit permissions | SuperAdmin |
PUT /admin/user/:id/permissions |
UserPermission |
yes |
| View KYC | kyc.view |
GET /admin/kyc/:userId/info |
UserKyc, last 5 Sumsub KycRequest |
yes |
| Override KYC level | kyc.edit |
POST /admin/kyc/:userId/info |
UserKyc, KycRequest (admin-source) |
yes |
| Block withdrawals | withdrawals.block |
POST /admin/withdrawals-block/:userId |
WithdrawalsBlock |
yes |
| Unblock withdrawals | withdrawals.unblock |
DELETE /admin/withdrawals-block/:userId |
WithdrawalsBlock (deleted) |
yes |
| Set self-exclusion | user-limit.edit |
POST /admin/user-limits/user-exclusion |
UserExclusion |
yes |
| Set gambling limits | user-limit.edit |
POST /admin/user-limits/gambling-limits |
UserGamblingLimit |
yes |
| Add note | user-note.edit |
POST /admin/user-notes |
UserNote |
yes |
| Delete note | user-note.delete |
DELETE /admin/user-notes/:id |
UserNote |
yes |
| View user audit history | admin-users.view-admin-audit |
GET /admin/user/admin-audit?userId=... |
AdminActionLog |
n/a (read) |
| Tip user (admin-funded) | admin-tips.tip |
POST /admin/admin-tips |
AdminTip, Transaction |
yes |
| Deduct balance (admin-funded) | admin-tips.deduct-balance |
POST /admin/admin-tips/deduct-balance |
AdminTip (negative), Transaction |
yes |
AdminActionLog rows: every action above writes a row capturing method, url, userId, requestBody, response, durationMs, ipAddress, userAgent per api.prisma:1479-1497.
Filters and views¶
The profile itself does not paginate, but its sub-tables do. Notable sub-view filters:
- Notes tab —
riskLevel(LOW / MEDIUM / HIGH),sortBy,sortOrder. SeeuseGetUserNotesQueryinqueries/users/index.ts:498. - Bets tab — date range, game type, status (won/lost/cancelled). Calls
POST /admin/betswith auserIdpredicate. See bets-history.md. - Deposits / Withdrawals tab — status, date range. See deposits-history.md, withdrawals.md.
- Audit tab — date range,
method(GET vs mutation), HTTP status. Endpoint is/admin/user/admin-audit.
Common workflows¶
- Player disputes a missing bonus. CS opens the profile, checks Notes tab for prior bonuses, opens the Promo tab (filters list of
UserPromoCodefor the user), confirms they received it. If yes, replies with link to bet history. If no, calls promo-codes.md to grant manually. - Compliance flags a high-risk player. Compliance opens the profile, sets a
risk=HIGHnote ("multiple chargebacks reported"), blocks withdrawals viaPOST /admin/withdrawals-block/:userId, and bumpsKYC level requiredviaPOST /admin/kyc/:userId/info. Player can keep depositing but can't cash out. - Banning for fraud. Risk opens profile, clicks Ban, fills
banReason. Backend writesUser.bannedAt, revokes sessions (BullMQ session-update queue triggers; seeapps/api/src/auth/session/session.queue-producer.ts), broadcastsUserBannedevent. Player gets logged out within 60 s. - Manual balance correction (post-incident). SuperAdmin only. Open profile → Balance tab → Adjust. UI prompts for current OTP. Backend writes
Accountadjustment +Transactionrow of typeADMIN_ADJUSTMENT. Logged inAdminActionLog. Engineering posts the rationale in incident channel. Seehandover/oncall-runbook.md. - KYC level override. Compliance reviews Sumsub artifacts in
/admin/kyc/:userId/info, thenPOST /admin/kyc/:userId/infowithlevel=2, verificationPending=false. Player can immediately initiate higher-tier withdrawal. - Tip / refund a player. Operator with
admin-tips.tipopens profile, clicks Tip, enters amount and reason. Server pulls from operator's tip-pool account; player's balance increments. Recorded inAdminTip. See tips-history.md.
Edge cases / gotchas¶
- Balance edit requires fresh OTP.
OtpGuardre-validates on every balance / role mutation. If you've been logged in > 5 minutes, expect to be re-prompted. - Unban does NOT restore sessions. It clears
bannedAt, but the player must re-log in. Sessions were terminated by the BullMQ session updater on ban. - Withdrawals block is independent of bans. A banned user is implicitly blocked from withdrawals (auth fails). A withdrawals-block on a non-banned user lets them keep playing but freezes cashouts. Be specific in operations comms.
PUT /admin/user/:id/permissionsdoes not require OTP but does requireSuperAdmin. This is a known asymmetry; seeadmin.user.controller.ts:194-204.PATCH /admin/user/:idallows nullable email assignment. That can break login. The DTOUpdateUserByAdminDtovalidates this; do not bypass via curl.- Avatar reset writes a new random URL. It does not delete the old asset from S3 — orphaned assets accumulate. Tracked in repo issues.
- Multi-account detection isn't on this screen. It lives on the registration-info endpoint (
/admin/registration-info/:userId) which surfacesUser.RegistrationInfo(IP/device/email-domain). Operator must manually cross-reference. - Banning a streamer doesn't revoke affiliate rewards in-flight. Pending affiliate payouts may still process. See affiliates.md.
Cross-links¶
- List screen: users-management.md
- KYC-only deep dive: kyc-limits.md
- Withdrawal blocks at scale: withdrawals.md
- Tip / deduct flow detail: tips-history.md
- Audit-trail detail: admin-logs.md
- Incident response (manual balance edits, escalations):
handover/oncall-runbook.md - Per-user RTP / wagering analytics:
flows/→ bet flow
Sequence — manual ban (with session revocation)¶
sequenceDiagram
actor admin
participant admin-fe
participant api
participant queue as BullMQ (session-update)
participant pg as Postgres
participant rt as rt service
admin->>admin-fe: click Ban (reason="duplicate-account")
admin-fe->>api: PATCH /admin/user/123/ban { banReason: "..." }
api->>api: PermissionGuard('user.ban')
api->>pg: UPDATE User SET bannedAt=now() WHERE id=123
api->>pg: INSERT BanLog (admin, reason, target=123)
api->>queue: enqueue session-revoke for user 123
api-->>admin-fe: 200 OK with updated UserDto
queue->>pg: DELETE Session WHERE userId=123
queue->>rt: emit UserBanned for socket force-disconnect
rt-->>admin-fe: (no-op for admin)
admin-fe-->>admin: profile re-renders with banned banner