Skip to content

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 tabriskLevel (LOW / MEDIUM / HIGH), sortBy, sortOrder. See useGetUserNotesQuery in queries/users/index.ts:498.
  • Bets tab — date range, game type, status (won/lost/cancelled). Calls POST /admin/bets with a userId predicate. 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

  1. Player disputes a missing bonus. CS opens the profile, checks Notes tab for prior bonuses, opens the Promo tab (filters list of UserPromoCode for the user), confirms they received it. If yes, replies with link to bet history. If no, calls promo-codes.md to grant manually.
  2. Compliance flags a high-risk player. Compliance opens the profile, sets a risk=HIGH note ("multiple chargebacks reported"), blocks withdrawals via POST /admin/withdrawals-block/:userId, and bumps KYC level required via POST /admin/kyc/:userId/info. Player can keep depositing but can't cash out.
  3. Banning for fraud. Risk opens profile, clicks Ban, fills banReason. Backend writes User.bannedAt, revokes sessions (BullMQ session-update queue triggers; see apps/api/src/auth/session/session.queue-producer.ts), broadcasts UserBanned event. Player gets logged out within 60 s.
  4. Manual balance correction (post-incident). SuperAdmin only. Open profile → Balance tab → Adjust. UI prompts for current OTP. Backend writes Account adjustment + Transaction row of type ADMIN_ADJUSTMENT. Logged in AdminActionLog. Engineering posts the rationale in incident channel. See handover/oncall-runbook.md.
  5. KYC level override. Compliance reviews Sumsub artifacts in /admin/kyc/:userId/info, then POST /admin/kyc/:userId/info with level=2, verificationPending=false. Player can immediately initiate higher-tier withdrawal.
  6. Tip / refund a player. Operator with admin-tips.tip opens profile, clicks Tip, enters amount and reason. Server pulls from operator's tip-pool account; player's balance increments. Recorded in AdminTip. See tips-history.md.

Edge cases / gotchas

  • Balance edit requires fresh OTP. OtpGuard re-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/permissions does not require OTP but does require SuperAdmin. This is a known asymmetry; see admin.user.controller.ts:194-204.
  • PATCH /admin/user/:id allows nullable email assignment. That can break login. The DTO UpdateUserByAdminDto validates 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 surfaces User.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.

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