Backend (Convex)
API — auth
Generated catalog of Convex functions in convex/auth/. Regenerated by `bun run docs:api`.
Auto-generated from source. Do not edit by hand — run
bun run docs:api.
query (14)
| Function | Description | Args |
|---|---|---|
dashboardGetUserDetail | Get composed user detail for dashboard screens. Keeps getUserProfile narrow for edit forms and single-table reads. | { userId: v.string() } |
dashboardGetUserStats | Get user statistics Note: Better Auth manages users table - stats come from custom tables | { dateFrom: v.optional(v.string()), dateTo: v.optional(v.string()), } |
dashboardListUsersWithAggregates | Dashboard user listing with per-user aggregates. Same filtering/pagination as getAllUsers, but enriches each row with: - totalStampsCollected (sum of currentStamps + completedCycles * campaign.totalStamps) - totalSpentCents (sum of orders.totalAmount * 100, parsed) - totalProductInteractions (count of productInteractions rows) Designed to power the user-management table without N+1 client fetches. Page size capped at 50 to keep per-page enrichment bounded. | { limit: v.optional(v.number()), cursor: v.optional(v.string()), role: v.optional( v.union( v.literal("apps"), v.literal("dashboard"), v.literal("admin"), v.… |
dashboardSearchUsersByBeautyProfile | Search users by their linked data (beauty profile, etc.) | { skinType: v.optional(v.string()), limit: v.optional(v.number()), } |
getAllUserIds | Get all user IDs for bulk operations (e.g. notifications). Returns only userId strings — lightweight for large user bases. Requires dashboard access. | { role: v.optional( v.union( v.literal("apps"), v.literal("dashboard"), v.literal("admin"), v.literal("super_admin"), ), ), status: v.optional( v.union( v.li… |
getAllUsers | Get all users with pagination and filters Requires dashboard access | { limit: v.optional(v.number()), cursor: v.optional(v.string()), role: v.optional( v.union( v.literal("apps"), v.literal("dashboard"), v.literal("admin"), v.… |
getUserById | Get user by profile ID (public query for dashboard) Requires dashboard access | { id: v.id("userProfiles") } |
getUserProfile | Get user profile by userId (public query for dashboard) Requires dashboard access (dashboard, admin, super_admin roles) | { userId: v.string() } |
getUserProfileByUserId | Get current user's profile (public query for dashboard frontend) Returns profile data directly (not wrapped in { success, data }). Used by dashboard to fetch the current user's profile after Better Auth login. Auth is enforced server-side via safeGetAuthUser — no userId arg needed. | {} |
mobileGetCurrentUser | Get current user profile with all fields Returns Better Auth user data merged with custom profile data (30+ fields) | {} |
mobileGetShopifyCustomerStatus | Get Shopify customer status for current user | {} |
mobileGetUserBeautyProfile | Get user's beauty profile (linked to Better Auth user) | {} |
mobileIsProfileComplete | Check if user profile is complete Returns detailed completion status for onboarding tracking | {} |
searchUsers | Search users across all fields (name, email, phone, beauty profile) Requires dashboard access | { query: v.string(), limit: v.optional(v.number()), includeBeautyProfile: v.optional(v.boolean()), } |
mutation (13)
| Function | Description | Args |
|---|---|---|
dashboardBanUser | Ban user (dashboard admin function) Sets accountStatus to 'suspended' and isActive to false | { userId: v.string(), reason: v.optional(v.string()), } |
dashboardBulkUpdateUsers | Bulk update users (dashboard admin function) Update multiple users at once (e.g., ban all, change roles) | { userIds: v.array(v.string()), action: v.union( v.literal("ban"), v.literal("unban"), v.literal("delete"), v.literal("restore"), v.literal("changeRole"), ),… |
dashboardCompleteHardDelete | Complete Hard Delete User (Dashboard Admin Function) PERMANENTLY removes user from ALL tables including Better Auth tables. This frees up the phone number/email for reuse. Tables deleted from: - userProfiles (app profile) - beautyProfiles (skin profile) - Better Auth: user, session, account tables Use cases: - Testing: Need to recreate user with same phone number - GDPR: Complete data erasure request - Cleanup: Remove test accounts WARNING: This is IRREVERSIBLE. Use soft delete for normal operations. | { userId: v.string(), // Better Auth user ID (from userProfiles.userId) } |
dashboardDeleteUser | Delete user (dashboard admin function) Soft delete: sets accountStatus to 'deleted' and isActive to false | { userId: v.string(), hardDelete: v.optional(v.boolean()), // Only super_admin can hard delete } |
dashboardDeleteUserBeautyProfile | Delete user's beauty profile (admin action) | { userId: v.string(), } |
dashboardRestoreUser | Restore soft-deleted user (dashboard admin function) Sets accountStatus back to 'active' and isActive to true | { userId: v.string(), } |
dashboardUnbanUser | Unban user (dashboard admin function) Sets accountStatus to 'active' and isActive to true | { userId: v.string(), } |
dashboardUpdateUser | Update user profile (dashboard admin function) Admins can update any user's profile fields | { userId: v.string(), updates: v.object({ firstName: v.optional(v.string()), lastName: v.optional(v.string()), fullName: v.optional(v.string()), email: v.opt… |
dashboardUpdateUserRole | Update user role (dashboard admin function) Super admins can change any role, admins can only change to apps/dashboard | { userId: v.string(), role: v.union( v.literal("apps"), v.literal("dashboard"), v.literal("admin"), v.literal("super_admin"), ), } |
mobileGetOrCreateProfile | Get or create profile for current user Returns full profile with all fields Profile fields accepted: - firstName, lastName, fullName: Name fields - email: Optional email - dateOfBirth: YYYY-MM-DD format - gender: male|female|non-binary|other|prefer-not-to-say - country: 2-letter country code (MY, US, etc.) - occupation: User's occupation - skincareExperience: beginner|intermediate|advanced - brandFamiliarity: new_to_brand|tried_before|regular_user If profile exists and args are provided, updates those fields. If profile doesn't exist, creates with provided values. Sets profileCompleted=true when required fields are all present. | { // Core profile fields (onboarding) firstName: v.optional(v.string()), lastName: v.optional(v.string()), fullName: v.optional(v.string()), email: v.optiona… |
mobileMarkOnboardingOffered | Mark onboarding as offered for the current user (persistent, server-side). Called when the beauty quiz prompt is shown — returning users skip onboarding on any device once this is set. Idempotent. | {} |
mobileUpdateProfile | — | { // Name fields firstName: v.optional(v.string()), lastName: v.optional(v.string()), fullName: v.optional(v.string()), // Better Auth compatibility: name … |
requestAccountDeletion | Request account deletion (mobile self-service) Soft delete: sets accountStatus to 'deleted' and isActive to false The mobile app auto-signs out when mobileGetCurrentUser returns { deleted: true } | {} |
internalQuery (4)
| Function | Description | Args |
|---|---|---|
getAccountStatusByEmail | Pre-auth account-status lookup by email — mirror of getAccountStatusByPhone. Used by the email sign-in HTTP route to 403 suspended/deleted accounts before Better Auth issues a session (the dashboard signs in via email). | { email: v.string() } |
getAccountStatusByPhone | Get account status by phone number (internal) Used by OTP send/verify flows to block deleted or suspended accounts before consuming iSMS credits or creating sessions. Returns: { accountStatus, isActive } | null (null = no profile = new user) | { phoneNumber: v.string() } |
getCurrentAuthUser | Get current authenticated user (internal) Used by actions that need to verify auth before making external API calls. Actions can call this via ctx.runQuery to get the authenticated user's ID. Returns: { userId: string } | null | {} |
getUserRoleInternal | Get user role by userId (internal) Used by public actions that need to check authorization after verifying auth. Actions can't access ctx.db directly, so they call this via ctx.runQuery. Returns null for banned/suspended/deleted profiles as well as missing ones, so every action-side role gate built on this query also enforces the active-profile check (mirrors requireDashboardAccess/requireAdminAccess). Returns: { role: string } | null | { userId: v.string() } |
internalMutation (5)
| Function | Description | Args |
|---|---|---|
checkOtpRateLimit | Check OTP send rate limit Called by HTTP action before sending OTP via iSMS | { phoneNumber: v.string(), } |
checkOtpVerifyRateLimit | Check OTP verify rate limit Called by HTTP action before forwarding a verify request to Better Auth. Complements the per-code attempt cap in auth.ts verifyOTP. | { phoneNumber: v.string() } |
checkSignInEmailRateLimit | Check email sign-in rate limit Called by HTTP action before forwarding to Better Auth | { key: v.string() } |
internalCompleteHardDelete | Internal Complete Hard Delete (for CLI/testing - no auth required) Use with: bunx convex run "auth/mutations:internalCompleteHardDelete" '{"userId": "..."}' | { userId: v.string(), } |
patchUserLastLogin | Patch userProfiles.lastLoginAt with the timestamp of a fresh Better Auth session. Called from the session.create.after hook so the dashboard listing query can read the denormalized field instead of looking up sessions per page user. Idempotent: only writes when the new timestamp is strictly greater than the stored one (avoids unnecessary patches on stale events). | { userId: v.string(), loginAt: v.number(), } |