--- title: User API tags: [api, user, reference] --- # User API Two routers are mounted for users: - `/api/user/*` - the new controller pattern in [`backend/src/services/user/userControllerRoutes.ts`](../../backend/src/services/user/userControllerRoutes.ts) wired to `userController`. - `/api/users/*` - the legacy router in [`backend/src/services/user/userRoutes.ts`](../../backend/src/services/user/userRoutes.ts) (kept for backward compatibility, primarily used by the admin console). Address-book CRUD lives on its own service: [`/api/addresses/*`](../../backend/src/services/address/addressRoutes.ts). All endpoints require `Bearer JWT` unless noted. Source of truth model: [[User]]. ## Profile (current user) ### GET /api/user/profile **Description:** Returns the caller's profile. **Auth required:** Bearer JWT **Response 200:** ```json { "success": true, "data": { /* User without password / verification tokens */ } } ``` **Source:** `userController.getCurrentUserProfile` ### PUT /api/user/profile **Description:** Updates the caller's profile. **Auth required:** Bearer JWT **Request body (whitelisted fields):** ```ts { firstName?: string; lastName?: string; name?: string; // alias for profile.name phone?: string; bio?: string; website?: string; photoURL?: string; // also mirrored to profile.avatar isPublic?: boolean; address?: { street?: string; city?: string; state?: string; country?: string; postalCode?: string; }; preferences?: { language?: "en" | "fa" | "ar"; currency?: "USD" | "EUR" | "IRR" | "AED"; notifications?: { email?: boolean; sms?: boolean; push?: boolean }; }; } ``` **Response 200:** Updated user. ### GET /api/users/profile **Description:** Legacy equivalent of the above. Returns the full sanitized user document. **Auth required:** Bearer JWT ### GET /api/users/profile/:userId **Description:** Public profile by id. If `profile.isPublic === false` and caller is not the owner or admin, only `firstName`, `lastName`, `avatar`, `role` are returned. **Auth required:** Bearer JWT ## Avatar upload Avatar upload is handled by the [[File API]]: `POST /api/files/upload/avatar` (multipart `avatar`) returns a URL that the caller then writes to `profile.avatar` via `PUT /api/user/profile`. ## Wallet address ### GET /api/user/wallet-address **Description:** Returns the caller's stored EVM wallet address (or `null`). **Auth required:** Bearer JWT **Response 200:** `{ "success": true, "data": { "walletAddress": "0x..." | null } }` ### PATCH /api/user/wallet-address **Description:** Verifies an EIP-191 signed message and stores `profile.walletAddress`. The server uses `ethers.verifyMessage(message, signature)` and rejects if the recovered address does not match. **Auth required:** Bearer JWT **Request body:** ```ts { walletAddress: string; // 0x-prefixed 40-hex signature: string; // signed `message` message: string; // human-readable challenge text } ``` **Response 200:** `{ "success": true, "data": { "walletAddress": "0x..." } }` **Errors:** - `400` missing fields, malformed address, signature mismatch - `404` user not found The legacy alias `PATCH /api/users/wallet-address` performs the same logic. ## Contacts and search ### GET /api/users/contacts **Description:** Returns the users the caller is allowed to chat with based on role: - `buyer` → sees `seller` + `admin` - `seller` → sees `buyer` + `admin` - `admin` → sees everyone **Auth required:** Bearer JWT **Response 200:** `{ "success": true, "data": { "contacts": [...], "count": N } }` ### GET /api/users/search?q=&role= **Description:** Case-insensitive search across `firstName`/`lastName`/`email`. Returns up to 20 active users. **Auth required:** Bearer JWT **Errors:** `400` if `q.length < 2`. ### GET /api/users?role=...&isActive=true&search=...&page=1&limit=50 **Description:** Paginated user directory (legacy, no admin gate). See pagination conventions in [[API Overview]]. **Auth required:** Bearer JWT ## Admin: user management These are duplicated across the two routers. The newer controller variants live under `/api/user/admin/*`; the legacy bodies live under `/api/users/admin/*`. All require `req.user.role === 'admin'` (the legacy routes check inline; the controller routes only check `authenticateToken` and the controller enforces the role). ### POST /api/user/admin/create **Description:** Admin creates a user with a chosen role and verification state. **Auth required:** Bearer JWT (admin) **Request body:** ```ts { email: string; password: string; firstName: string; lastName: string; role?: "buyer" | "seller" | "admin"; // default "buyer" isActive?: boolean; // default true isVerified?: boolean; // default false profile?: { /* free-form */ }; } ``` **Response 201:** `{ success, data: { user } }` **Errors:** `400` missing fields, `403` non-admin, `409` email exists. ### DELETE /api/user/admin/:userId **Description:** Hard-delete a user. Prevents self-deletion and deleting other admins. **Auth required:** Bearer JWT (admin) **Response 200:** `{ success, data: { deletedUserId } }` **Errors:** `400` self-delete, `403` admin-on-admin, `404` not found. ### PATCH /api/user/admin/:userId/status **Description:** Activate / suspend a user. **Auth required:** Bearer JWT (admin) **Request body:** `{ isActive: boolean; reason?: string }` **Response 200:** `{ success, data: { user: { _id, isActive, statusUpdatedAt } } }` ### PATCH /api/user/admin/:userId/toggle-status **Description:** Flip active/suspended without explicit body. **Auth required:** Bearer JWT (admin) ### PATCH /api/user/admin/:userId/role **Description:** Change a user's role. **Auth required:** Bearer JWT (admin) **Request body:** `{ role: "buyer" | "seller" | "admin"; reason?: string }` **Errors:** `400` invalid role. ### GET /api/user/admin/list **Description:** Paginated admin user list with filters. **Auth required:** Bearer JWT (admin) **Query params:** `role`, `isActive`, `isVerified`, `search`, `page`, `limit`, `sortBy`, `sortOrder` **Response 200:** `{ success, data: { users, pagination, stats: { totalUsers, activeUsers, verifiedUsers, buyers, sellers, admins } } }` ### GET /api/user/admin/:userId/dependencies **Description:** Returns a count of related entities (purchase requests, offers, payments) before a destructive admin operation. **Auth required:** Bearer JWT (admin) ### GET /api/users/admin/stats **Description:** Aggregated user stats — total/active/verified counts, role distribution, activity buckets (24h / 7d / 30d). **Auth required:** Bearer JWT (admin) ### GET /api/users/admin/:userId **Description:** Fetch a single user by id (admin view, includes preferences and last-login). **Auth required:** Bearer JWT (admin) ### PUT /api/users/admin/:userId **Description:** Mass update a user document (admin override). **Auth required:** Bearer JWT (admin) ### PUT /api/users/admin/update/:email **Description:** Same as above but keyed on email. **Auth required:** Bearer JWT (admin) ### PATCH /api/users/admin/:userId/password **Description:** Admin forces a new password. Wipes `refreshTokens` so all sessions are invalidated. **Auth required:** Bearer JWT (admin) **Request body:** `{ newPassword: string; reason?: string }` ### POST /api/users/admin/:userId/resend-verification **Description:** Regenerate the 8-digit email verification code and re-send the verification email. **Auth required:** Bearer JWT (admin) **Errors:** `400` user already verified. ## Address book Source: [`backend/src/services/address/addressRoutes.ts`](../../backend/src/services/address/addressRoutes.ts), model: [[Address]]. ### GET /api/addresses **Description:** List the caller's addresses. **Auth required:** Bearer JWT **Response 200:** `{ success: true, data: [Address, ...] }` ### POST /api/addresses **Description:** Create a new address. First address auto-becomes primary. **Auth required:** Bearer JWT **Request body:** ```ts { fullName: string; phone: string; street: string; city: string; state?: string; country: string; postalCode?: string; isPrimary?: boolean; notes?: string; } ``` ### PUT /api/addresses/:addressId **Description:** Update an address. **Auth required:** Bearer JWT **Errors:** `404` not owned by user. ### DELETE /api/addresses/:addressId **Description:** Delete an address. If it was the primary, another address is promoted. **Auth required:** Bearer JWT ### PATCH /api/addresses/:addressId/primary **Description:** Promote an address to primary; demotes the previous primary. **Auth required:** Bearer JWT See [[Address]] for the schema, and [[Marketplace API]] for how purchase requests reference an address.