{"openapi":"3.0.3","info":{"title":"🔐 Etera Auth API","description":"# 🔐 Etera Auth API - Client Integration\n\nA modern authentication service for mobile and web applications.\n\n## 🚀 Quick Start\n\n### 📱 Phone Authentication (3-Step Process)\n1. **Send OTP**: POST /api/auth/phone-number/send-otp with phoneNumber & countryCode (calling code)\n2. **Verify & Login**: POST /api/auth/phone-number/verify with phoneNumber, countryCode & code\n3. **Update Profile** (for new users): PATCH /api/auth/user\n\n### 📧 Email Authentication (3-Step Process)\n1. **Send OTP**: POST /api/auth/email/send-otp\n2. **Verify & Login**: POST /api/auth/email/verify-otp\n3. **Update Profile** (for new users): PATCH /api/auth/user\n\n### 🌐 Mobile OAuth (2-Step Process)\n- **Google**: POST /api/auth/mobile/google with Google ID token\n- **Apple**: POST /api/auth/mobile/apple with Apple identity token\n- **Update Profile** (if needed): PATCH /api/auth/user\n\n### 🌐 Web OAuth (Redirect Flow)\n- **Google**: GET /api/auth/sign-in/social/google → redirects to Google\n- **Callback**: GET /api/auth/callback/google → handles return\n- **Update Profile** (if needed): PATCH /api/auth/user\n\n## 👤 After Authentication\n\n### **Update User Profile** (Required for new users)\n    PATCH /api/auth/user (or /api/auth/me - preferred)\n    Authorization: Bearer your_jwt_token\n    Body: firstName, lastName, language\n\n### **Get Current User**\n    GET /api/auth/user (or /api/auth/me - preferred)\n    Authorization: Bearer your_jwt_token\n\n## 🔑 Authentication\n\nAll authenticated requests require a JWT token in the Authorization header:\n    Authorization: Bearer your_jwt_token_here\n\n## 📞 Phone Number Format\n\nFor phone authentication, use:\n- **phoneNumber**: Just the number without country code (e.g., \"589595029\")\n- **countryCode**: Country calling code with + prefix (e.g., \"+971\" for UAE, \"+1\" for US)\n\n**Examples:**\n- UAE: phoneNumber: \"589595029\", countryCode: \"+971\"\n- US: phoneNumber: \"1234567890\", countryCode: \"+1\"\n- UK: phoneNumber: \"7123456789\", countryCode: \"+44\"\n\n## 📱 Complete Mobile Integration Flow\n\n### Phone Authentication Example\n    Step 1: Send OTP\n    POST /api/auth/phone-number/send-otp\n    Body: phoneNumber, countryCode\n\n    Step 2: Verify OTP and get user data\n    POST /api/auth/phone-number/verify\n    Body: phoneNumber, countryCode, code\n\n    Step 3: Update profile for new users\n    PATCH /api/auth/user\n    Headers: Authorization Bearer TOKEN\n    Body: firstName, lastName\n\n### Google OAuth Example\n    Get Google ID token from mobile SDK\n    POST /api/auth/mobile/google\n    Body: id_token from Google SDK\n\n    Update profile if needed for new users\n    PATCH /api/auth/user\n    Headers: Authorization Bearer TOKEN\n    Body: firstName, lastName\n\n## 🛡️ Rate Limits\n- **Phone/Email OTP**: 5 requests per hour per number/email\n- **Authentication**: 20 requests per minute per IP\n- **Profile Updates**: 10 requests per minute per user\n- **General**: 100 requests per minute per IP\n\n## ⚠️ Error Handling\n\nAll errors return consistent JSON format:\n    error: code, message, timestamp, requestId\n\n## 🏗️ Microservice Integration & Token Verification\n\n### **For Other Microservices in Your Ecosystem**\n\nIf you have other microservices that need to verify user authentication, use these internal endpoints:\n\n#### **Full Token Verification** (Complete user data)\n    POST /api/internal/verify-token\n    Content-Type: application/json\n    Body: token JWT_TOKEN_HERE\n\n    Response: isValid, user object, authMethod, tokenInfo\n\n#### **Quick Token Validation** (Minimal user data for high-frequency calls)\n    POST /api/internal/validate-token\n    Content-Type: application/json\n    Body: token JWT_TOKEN_HERE\n\n    Response: isValid, userId, email\n\n### **Auth Guard Implementation Examples**\n\n#### Express.js Middleware\n- Import: AuthGuardClient from './auth-guard'\n- Setup: new AuthGuardClient('https://auth.etera.dev')\n- Usage: app.use('/api/protected/*', createExpressAuthGuard(authClient))\n- Route: app.get('/dashboard', handler) where handler returns user data\n\n#### Elysia/Bun Middleware\n- Import: AuthGuardClient, createElysiaAuthGuard from './auth-guard'\n- Setup: new AuthGuardClient('https://auth.etera.dev')\n- Usage: app.onBeforeHandle(authGuard).get('/profile', handler)\n\n#### Next.js API Routes\n- Import: withAuth, AuthGuardClient from './auth-guard'\n- Setup: new AuthGuardClient('https://auth.etera.dev')\n- Usage: export default withAuth(handler, authClient)\n\n### **Environment Configuration for Microservices**\n    AUTH_SERVICE_URL=https://auth.etera.dev\n    AUTH_TIMEOUT=5000\n    USE_QUICK_VALIDATION=false\n\n### **Performance Tips**\n- Use **quick validation** for high-frequency endpoints (metrics, health checks)\n- Implement **token caching** (5-minute TTL recommended)\n- Use **circuit breakers** for resilience\n- Set appropriate **timeouts** (5 seconds recommended)\n- **Rate limits**: Internal endpoints have higher limits (1000 req/min vs 100 req/min for client APIs)\n\n### **Session Management**\n- JWT tokens are **stateless** - no server-side session storage needed\n- Tokens contain user info and expire automatically\n- No need to call logout endpoints from microservices\n- Fresh user data is fetched on each verification call\n\n### **Security Considerations**\n- Internal endpoints should be accessible only within your **private network**\n- Use **HTTPS** in production environments\n- Implement **network-level security** (VPC, firewall rules)\n- Consider **service mesh** for advanced security (mTLS, policy enforcement)\n\n📚 **Full Integration Guide**: See /docs/MICROSERVICE_AUTH_INTEGRATION.md in the repository\n\n## 🎯 Integration Steps\n\n1. Choose your authentication method (Phone OTP, Email OTP, or OAuth)\n2. Follow the 2-3 step process above\n3. Store the returned JWT token\n4. Update user profile if needed (especially for new users)\n5. Use the token for authenticated requests\n6. **For microservices**: Implement auth guards using internal verification endpoints\n7. Test using the \"Try it out\" feature below!\n\nReady to integrate? Start with the endpoints below! 🚀","version":"1.0.0","contact":{"name":"Etera Auth Support","email":"support@etera-auth.com"}},"tags":[{"name":"Phone Auth","description":"📱 Phone number based authentication with OTP"},{"name":"Email Auth","description":"📧 Email based authentication with OTP"},{"name":"Mobile OAuth","description":"🌐 Mobile app OAuth integration (Google, Apple)"},{"name":"OAuth","description":"🌐 Web OAuth flows"},{"name":"User Management","description":"👤 User profile and account management (both /user and /me endpoints)"},{"name":"─────────────────","description":"📋 System & Monitoring"},{"name":"Microservice Integration","description":"🏗️ Token verification for other microservices (Internal endpoints)"},{"name":"Internal","description":"🔧 Internal utilities and debugging (Admin only)"},{"name":"Health","description":"💚 Service health and status"}],"servers":[{"url":"https://auth.etera.dev","description":"Production server"},{"url":"http://localhost:3000","description":"Development server"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token obtained from authentication endpoints"}},"schemas":{"User":{"type":"object","properties":{"id":{"type":"string","description":"Unique user identifier","example":"user_123456"},"email":{"type":"string","format":"email","description":"User email address","example":"user@example.com"},"name":{"type":"string","description":"User full name","example":"John Doe"},"firstName":{"type":"string","description":"User first name","example":"John"},"lastName":{"type":"string","description":"User last name","example":"Doe"},"image":{"type":"string","nullable":true,"description":"User profile image URL","example":"https://example.com/avatar.jpg"},"phoneNumber":{"type":"string","nullable":true,"description":"User phone number with country code","example":"+1234567890"},"phoneNumberVerified":{"type":"boolean","description":"Phone verification status","example":true},"emailVerified":{"type":"boolean","description":"Email verification status","example":true},"language":{"type":"string","description":"User preferred language code","example":"en"},"deviceTokens":{"type":"array","items":{"type":"string"},"description":"Device tokens for push notifications","example":["fGzQ9k4FmkE:APA91bH...","eXjP8l2AmcN:APA91bK..."]},"createdAt":{"type":"string","format":"date-time","description":"Account creation timestamp","example":"2023-01-01T00:00:00.000Z"},"updatedAt":{"type":"string","format":"date-time","description":"Last update timestamp","example":"2023-01-01T12:00:00.000Z"}},"required":["id","email","emailVerified","phoneNumberVerified","createdAt","updatedAt"]},"SessionResponse":{"type":"object","properties":{"token":{"type":"string","description":"JWT session token for authentication","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."},"expiresAt":{"type":"string","format":"date-time","description":"Token expiration timestamp","example":"2023-01-08T00:00:00.000Z"}},"required":["token","expiresAt"]},"AuthResponse":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"session":{"$ref":"#/components/schemas/SessionResponse"},"isNewUser":{"type":"boolean","description":"Whether this is a new user registration","example":true}},"required":["user","session","isNewUser"]},"ErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code","example":"INVALID_INPUT"},"message":{"type":"string","description":"Human-readable error message","example":"The provided email address is invalid"},"details":{"type":"object","description":"Additional error context","example":{"field":"email","received":"invalid-email"}},"timestamp":{"type":"string","format":"date-time","description":"Error occurrence timestamp","example":"2023-01-01T12:00:00.000Z"},"requestId":{"type":"string","description":"Unique request identifier for tracing","example":"req_123456789"}},"required":["code","message","timestamp"]}},"required":["error"]},"SuccessResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Operation completed successfully"},"data":{"type":"object","description":"Response data"}},"required":["success"]}}},"paths":{"/metrics":{"get":{"operationId":"getMetrics","responses":{"200":{}}}},"/":{"get":{"operationId":"getIndex","tags":["General"],"summary":"API Welcome","description":"Returns welcome message and API information","responses":{"200":{}}}},"/email/send-otp":{"post":{"operationId":"postEmailSend-otp","tags":["Email Auth"],"summary":"Send OTP to email","description":"Sends a 4-digit OTP to the provided email for sign-in or sign-up.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","example":"test@example.com"}},"required":["email"]}}}},"responses":{"200":{"description":"OTP sent successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"OTP sent successfully"},"expiresIn":{"type":"number","example":300,"description":"OTP expiration time in seconds"},"isNewUser":{"type":"boolean","example":true,"description":"Whether this email belongs to a new user"},"onboardingComplete":{"type":"boolean","example":false,"description":"Whether the user has completed onboarding (only included when isNewUser is false)"},"otp":{"type":"string","example":"1234","description":"OTP code (only included in development mode for testing purposes)"}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/email/verify-otp":{"post":{"operationId":"postEmailVerify-otp","tags":["Email Auth"],"summary":"Verify email OTP","description":"Verifies the 4-digit OTP sent to the email and signs in or signs up the user.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","example":"test@example.com"},"otp":{"type":"string","example":"1234"}},"required":["email","otp"]}}}},"responses":{"200":{"description":"OTP verified and user signed in/up successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"400":{"description":"Invalid or expired OTP","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/sign-up/email":{"post":{"operationId":"postSign-upEmail","tags":["Email Auth"],"summary":"Sign up with email (extras)","description":"Create a new user account with email and password.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","example":"moeid@etera.tech"},"password":{"type":"string","minLength":8,"example":"12345678"},"name":{"type":"string","example":"John Doe"}},"required":["email","password"]}}}},"responses":{"200":{"description":"User created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"session":{"$ref":"#/components/schemas/Session"}}}}}},"400":{"description":"Invalid input or user already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/sign-in/email":{"post":{"operationId":"postSign-inEmail","tags":["Email Auth"],"summary":"Sign in with email (extras)","description":"Authenticate a user with their email and password.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","example":"moeid@etera.tech"},"password":{"type":"string","example":"12345678"},"rememberMe":{"type":"boolean","default":true}},"required":["email","password"]}}}},"responses":{"200":{"description":"Authentication successful","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"session":{"$ref":"#/components/schemas/Session"}}}}}},"401":{"description":"Invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/forget-password":{"post":{"operationId":"postForget-password","tags":["Email Auth"],"summary":"Request password reset (extras)","description":"Sends a password reset link to the user's email.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","example":"moeid@etera.tech"}},"required":["email"]}}}},"responses":{"200":{"description":"Password reset email sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/reset-password":{"post":{"operationId":"postReset-password","tags":["Email Auth"],"summary":"Reset password (extras)","description":"Sets a new password using the reset token.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","description":"The reset token from the email"},"password":{"type":"string","minLength":8,"example":"newstrongpassword"}},"required":["token","password"]}}}},"responses":{"200":{"description":"Password reset successful","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Password reset successfully"}}}}}},"400":{"description":"Invalid or expired token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/setup-email/send-otp":{"post":{"operationId":"postSetup-emailSend-otp","responses":{"200":{}}}},"/setup-email/verify-otp":{"post":{"operationId":"postSetup-emailVerify-otp","responses":{"200":{}}}},"/email/change/send-otp":{"post":{"operationId":"postEmailChangeSend-otp","tags":["Email Auth"],"summary":"Send OTP to new email for change","description":"Sends OTP to a new email address for email change verification. Requires Authorization header.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Email address for the user","example":"moeid@etera.tech"}},"required":["email"]}}}},"responses":{"200":{"description":"OTP sent successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"OTP sent to new email address"},"expiresIn":{"type":"number","example":300,"description":"OTP expiration time in seconds"},"otp":{"type":"string","example":"1234","description":"OTP code (only included in development mode for testing purposes)"}}}}}},"400":{"description":"Invalid email or authorization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/email/change":{"post":{"operationId":"postEmailChange","tags":["Email Auth"],"summary":"Change email address","description":"Changes the user's email address after verifying the OTP. Requires Authorization header.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Email address for the user","example":"moeid@etera.tech"},"code":{"type":"string","description":"The OTP code received via email","example":"123456"}},"required":["email","code"]}}}},"responses":{"200":{"description":"Email changed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Email changed successfully"},"user":{"$ref":"#/components/schemas/User"},"session":{"type":"object","properties":{"token":{"type":"string"},"expiresAt":{"type":"string"}}}}}}}},"400":{"description":"Invalid OTP, email, or authorization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/phone-number/send-otp":{"post":{"operationId":"postPhone-numberSend-otp","tags":["Phone Auth"],"summary":"Send OTP to phone number","description":"Sends a one-time password (OTP) to the provided phone number for verification","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phoneNumber":{"type":"string","description":"Phone number without country code (e.g., 589595029)","example":"589595029"},"countryCode":{"type":"string","description":"Country calling code with + prefix (e.g., +971, +1, +44)","example":"+971"}},"required":["phoneNumber","countryCode"]}}}},"responses":{"200":{"description":"OTP sent successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"isNewUser":{"type":"boolean","example":true,"description":"Whether this phone number belongs to a new user"},"formattedPhoneNumber":{"type":"string","example":"+971***95029","description":"Masked phone number for privacy"},"onboardingComplete":{"type":"boolean","example":false,"description":"Whether the user has completed onboarding (only included when isNewUser is false)"},"otp":{"type":"string","example":"1234","description":"OTP code (only included in development mode for testing purposes)"}}}}}},"400":{"description":"Invalid phone number or rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/phone-number/verify":{"post":{"operationId":"postPhone-numberVerify","tags":["Phone Auth"],"summary":"Verify phone OTP","description":"Verifies the OTP sent to the phone number and completes authentication. Optionally sets user's preferred language.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phoneNumber":{"type":"string","description":"Phone number without country code (e.g., 589595029)","example":"589595029"},"countryCode":{"type":"string","description":"Country calling code with + prefix (e.g., +971, +1, +44)","example":"+971"},"code":{"type":"string","description":"The OTP code received via SMS","example":"123456"},"language":{"type":"string","description":"Optional: User's preferred language (e.g., 'en', 'ar', 'fr')","example":"en"}},"required":["phoneNumber","countryCode","code"]}}}},"responses":{"200":{"description":"OTP verified successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"400":{"description":"Invalid OTP or phone number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/phone-number/change":{"post":{"operationId":"postPhone-numberChange","tags":["Phone Auth"],"summary":"Change phone number","description":"Changes the user's phone number after verifying the OTP. Requires Authorization header. Optionally sets user's preferred language.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phoneNumber":{"type":"string","description":"New phone number without country code (e.g., 589595029)","example":"589595029"},"countryCode":{"type":"string","description":"Country calling code with + prefix (e.g., +971, +1, +44)","example":"+971"},"code":{"type":"string","description":"The OTP code received via SMS","example":"123456"},"language":{"type":"string","description":"Optional: User's preferred language (e.g., 'en', 'ar', 'fr')","example":"en"}},"required":["phoneNumber","countryCode","code"]}}}},"responses":{"200":{"description":"Phone number changed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"400":{"description":"Invalid OTP, phone number, or authorization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/phone-number/change/send-otp":{"post":{"operationId":"postPhone-numberChangeSend-otp","tags":["Phone Auth"],"summary":"Send OTP to new phone number for change","description":"Sends OTP to a new phone number for phone number change verification. Requires Authorization header.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phoneNumber":{"type":"string","description":"New phone number without country code (e.g., 589595029)","example":"589595029"},"countryCode":{"type":"string","description":"Country calling code with + prefix (e.g., +971, +1, +44)","example":"+971"}},"required":["phoneNumber","countryCode"]}}}},"responses":{"200":{"description":"OTP sent successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"OTP sent to new phone number"},"formattedPhoneNumber":{"type":"string","example":"+971***95029","description":"Masked phone number for privacy"},"otp":{"type":"string","example":"1234","description":"OTP code (only included in development mode for testing purposes)"}}}}}},"400":{"description":"Invalid phone number or authorization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/phone-number/sign-in":{"post":{"operationId":"postPhone-numberSign-in","tags":["Phone Auth"],"summary":"Sign in with phone","description":"Sign in using phone number and OTP","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phoneNumber":{"type":"string","description":"Phone number with country code","example":"+1234567890"},"code":{"type":"string","description":"The OTP code received via SMS","example":"123456"},"rememberMe":{"type":"boolean","description":"Whether to keep the session active for longer","default":true}},"required":["phoneNumber","code"]}}}},"responses":{"200":{"description":"Successfully signed in","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"session":{"$ref":"#/components/schemas/Session"}}}}}},"401":{"description":"Invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/phone-number/reset-password":{"post":{"operationId":"postPhone-numberReset-password","tags":["Phone Auth"],"summary":"Reset password with phone","description":"Reset password using phone number and OTP verification","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phoneNumber":{"type":"string","description":"Phone number with country code","example":"+1234567890"},"code":{"type":"string","description":"The OTP code received via SMS","example":"123456"},"newPassword":{"type":"string","description":"New password to set","example":"newSecurePassword123"}},"required":["phoneNumber","code","newPassword"]}}}},"responses":{"200":{"description":"Password reset successful","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Password reset successfully"}}}}}},"400":{"description":"Invalid request or OTP","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/mobile/google":{"post":{"operationId":"postMobileGoogle","tags":["Mobile OAuth"],"summary":"Mobile Google OAuth authentication","description":"Mobile OAuth authentication using Google ID tokens. Supports platform-specific Google client IDs for iOS, Android, and Web. Requires a valid Google ID token from the mobile app.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"authCode":{"type":"string","description":"Authorization code from Google OAuth flow (recommended)"},"access_token":{"type":"string","description":"Google access token (direct method)"},"refresh_token":{"type":"string","description":"Google refresh token (optional, for token refresh)"},"idToken":{"type":"string","description":"Google ID token (camelCase)"},"id_token":{"type":"string","description":"Google ID token (snake_case)"},"platform":{"type":"string","enum":["ios","android","web"],"description":"Platform for Google client ID selection (ios, android, or web). Determines which Google OAuth client ID to use for token validation."},"deviceId":{"type":"string","description":"Device identifier for tracking"},"firstName":{"type":"string","description":"User's first name (optional)","nullable":true},"lastName":{"type":"string","description":"User's last name (optional)","nullable":true},"email":{"type":"string","description":"User's email (optional)","nullable":true}},"anyOf":[{"required":["idToken"]},{"required":["id_token"]}]}}}},"responses":{"200":{"description":"Successful authentication","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"400":{"description":"Invalid request or token verification failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/sign-in/google":{"get":{"operationId":"getSign-inGoogle","tags":["OAuth"],"summary":"Initiate Google OAuth","description":"Redirects users to Google's OAuth consent screen for authentication using Better Auth's social provider. After successful authentication, users will be redirected back to your application.","responses":{"302":{"description":"Redirect to Google OAuth consent screen"},"400":{"description":"OAuth configuration error"}}}},"/callback/google":{"get":{"operationId":"getCallbackGoogle","tags":["OAuth"],"summary":"Google OAuth callback","description":"Handles Google OAuth callback after user authorizes your application using Better Auth's social provider. This endpoint automatically creates or signs in the user and establishes a session.","responses":{"302":{"description":"Redirect after successful OAuth authentication"},"400":{"description":"OAuth error or user denied authorization"}}}},"/mobile/apple":{"post":{"operationId":"postMobileApple","tags":["Mobile OAuth"],"summary":"Mobile Apple OAuth","description":"Authenticate with Apple ID token","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"identityToken":{"type":"string"},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"email":{"type":"string","nullable":true}},"required":["identityToken"]}}}},"responses":{"200":{}}}},"/me":{"get":{"operationId":"getMe","tags":["User Management"],"summary":"Get current user session","description":"Returns the current user's profile data with session information","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User session data retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"patchMe","tags":["User Management"],"summary":"Update current user profile","description":"Update user profile fields for the currently authenticated user. Preferred endpoint over /user.","security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"firstName":{"type":"string","example":"John"},"lastName":{"type":"string","example":"Doe"},"name":{"type":"string","example":"John Doe"},"image":{"type":"string","example":"https://example.com/avatar.jpg"},"deviceTokens":{"type":"array","items":{"type":"string"},"description":"Device tokens for push notifications","example":["fGzQ9k4FmkE:APA91bH...","eXjP8l2AmcN:APA91bK..."]}}}}}},"responses":{"200":{"description":"Profile updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"session":{"type":"object","properties":{"token":{"type":"string","description":"New JWT token with updated user data"},"expiresAt":{"type":"string","format":"date-time","description":"Token expiration time"}}}}}}}},"400":{"description":"No updatable fields provided","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/refresh-token":{"post":{"operationId":"postRefresh-token","tags":["User Management"],"summary":"Refresh authentication token","description":"Refreshes the current user's JWT token with the latest user data from the database. Returns a new session token.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Token refreshed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"token":{"type":"string","description":"New JWT token with latest user data"},"expiresAt":{"type":"string","format":"date-time","description":"Token expiration time"}}}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/user":{"patch":{"operationId":"patchUser","responses":{"200":{}}},"get":{"operationId":"getUser","tags":["User Management"],"summary":"Get current user data","description":"Returns the current user's profile data (legacy endpoint, prefer /me)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User data retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/user-profile":{"delete":{"operationId":"deleteUser-profile","tags":["User Management"],"summary":"Delete user account","description":"Permanently deletes the current user's account and all associated data. This action is irreversible and will trigger a USER_DELETED Kafka event.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User account deleted successfully","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","example":true,"description":"Whether the deletion was successful"},"message":{"type":"string","example":"User profile deleted successfully","description":"Confirmation message (only present on success)"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Not authenticated"}}}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"User not found"}}}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Failed to delete user profile"}}}}}}}}},"/device-tokens":{"post":{"operationId":"postDevice-tokens","tags":["Device Tokens"],"summary":"Add device token","description":"Add a device token for push notifications. Tokens are automatically deduplicated.","security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"deviceToken":{"type":"string","example":"fGzQ9k4FmkE:APA91bH...","description":"FCM token for Android, APNs token for iOS, or web push token"},"platform":{"type":"string","enum":["ios","android","web"],"example":"ios","description":"Platform type (optional, for metadata)"}},"required":["deviceToken"]}}}},"responses":{"200":{"description":"Device token added successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Device token added successfully"},"deviceTokens":{"type":"array","items":{"type":"string"},"description":"Updated list of all device tokens"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteDevice-tokens","tags":["Device Tokens"],"summary":"Remove device token","description":"Remove a specific device token from the user's account","security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"deviceToken":{"type":"string","example":"fGzQ9k4FmkE:APA91bH...","description":"The device token to remove"}},"required":["deviceToken"]}}}},"responses":{"200":{"description":"Device token removed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Device token removed successfully"},"deviceTokens":{"type":"array","items":{"type":"string"},"description":"Updated list of remaining device tokens"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"operationId":"getDevice-tokens","tags":["Device Tokens"],"summary":"Get all device tokens","description":"Retrieve all device tokens associated with the current user","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Device tokens retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"deviceTokens":{"type":"array","items":{"type":"string"},"description":"List of all device tokens for this user"},"count":{"type":"number","example":2,"description":"Total number of device tokens"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/device-tokens/all":{"delete":{"operationId":"deleteDevice-tokensAll","tags":["Device Tokens"],"summary":"Clear all device tokens","description":"Remove all device tokens from the current user's account. Useful for logout or account cleanup.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"All device tokens cleared successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"All device tokens cleared successfully"},"deviceTokens":{"type":"array","items":{"type":"string"},"example":[],"description":"Empty array after clearing"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/logout":{"delete":{"operationId":"deleteLogout","tags":["Authentication"],"summary":"Logout current user","description":"Invalidates all sessions, clears device tokens, blocklists the JWT, and publishes a logout event.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Logged out successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Logged out successfully"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/phone/countries":{"get":{"operationId":"getPhoneCountries","tags":["Phone Auth"],"summary":"Get supported countries","description":"Returns a list of supported countries for phone number validation","responses":{"200":{"description":"List of supported countries","content":{"application/json":{"schema":{"type":"object","properties":{"countries":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"ISO2 country code"},"name":{"type":"string","description":"Country name"},"dialCode":{"type":"string","description":"Country dial code"}}}}}}}}}}}},"/languages":{"get":{"operationId":"getLanguages","tags":["User"],"summary":"Get supported languages","description":"Returns lists of supported languages in different formats for UI components","responses":{"200":{"description":"Lists of supported languages","content":{"application/json":{"schema":{"type":"object","properties":{"popular":{"type":"array","description":"Most commonly used languages with flags","items":{"type":"object","properties":{"code":{"type":"string","example":"en"},"label":{"type":"string","example":"🇺🇸 English"},"direction":{"type":"string","enum":["ltr","rtl"],"example":"ltr"}}}},"all":{"type":"array","description":"All supported languages with flags","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"},"direction":{"type":"string","enum":["ltr","rtl"]}}}},"rtl":{"type":"array","description":"Right-to-left languages","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"},"direction":{"type":"string","enum":["rtl"]}}}},"supported":{"type":"array","description":"All supported language codes","items":{"type":"string"}},"default":{"type":"string","description":"Default language code","example":"en"}}}}}}}}},"/check-user":{"post":{"operationId":"postCheck-user","tags":["User"],"summary":"Check if user exists by email or phone number","description":"Returns { exists: true } if a user exists with the given email or phone number, otherwise { exists: false }.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","example":"test@example.com"},"phoneNumber":{"type":"string","example":"+1234567890"}},"oneOf":[{"required":["email"]},{"required":["phoneNumber"]}]}}}},"responses":{"200":{"description":"User existence result","content":{"application/json":{"schema":{"type":"object","properties":{"exists":{"type":"boolean","example":true}}}}}},"422":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/session/logout":{"delete":{"operationId":"deleteSessionLogout","tags":["Authentication"],"summary":"Logout current user","description":"Invalidates all sessions, clears device tokens, blocklists the JWT, and publishes a logout event.","security":[{"bearerAuth":[]}],"responses":{"200":{}}},"post":{"operationId":"postSessionLogout","tags":["Authentication"],"summary":"Logout current user","description":"Invalidates all sessions, clears device tokens, blocklists the JWT, and publishes a logout event.","security":[{"bearerAuth":[]}],"responses":{"200":{}}}},"/health":{"get":{"operationId":"getHealth","tags":["Health"],"summary":"Basic health check","description":"Returns basic service health status for client applications","responses":{"200":{}}}},"/internal/verify-token":{"post":{"operationId":"postInternalVerify-token","tags":["Microservice Integration"],"summary":"Verify JWT token","description":"Verifies a JWT token and returns user information. Used by other microservices for authentication.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","description":"JWT token to verify","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}},"required":["token"]}}}},"responses":{"200":{"description":"Token verification result"},"400":{"description":"Invalid request"}}}},"/internal/validate-token":{"post":{"operationId":"postInternalValidate-token","tags":["Microservice Integration"],"summary":"Quick token validation","description":"Lightweight token validation for high-frequency checks. Returns minimal user information.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","description":"JWT token to validate"}},"required":["token"]}}}},"responses":{"200":{"description":"Token validation result"}}}},"/internal/debug/user/{userId}":{"get":{"operationId":"getInternalDebugUserByUserId","tags":["Internal"],"summary":"Debug user data","description":"Internal debug endpoint to inspect user data and onboarding calculations","parameters":[{"schema":{"type":"string"},"in":"path","name":"userId","required":true}],"responses":{"200":{}}}},"/internal/system/info":{"get":{"operationId":"getInternalSystemInfo","tags":["Internal"],"summary":"System information","description":"Internal endpoint providing system configuration and feature flags","responses":{"200":{}}}},"/internal/test/sms":{"post":{"operationId":"postInternalTestSms","tags":["Internal"],"summary":"Test SMS sending","description":"Internal endpoint to test SMS functionality via configured provider (Twilio or Infobip)","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phoneNumber":{"type":"string","example":"+971589595029"},"message":{"type":"string","example":"Test message"}},"required":["phoneNumber"]}}}},"responses":{"200":{}}}},"/internal/sync-onboarding":{"post":{"operationId":"postInternalSync-onboarding","tags":["Internal"],"summary":"Sync onboarding status","description":"Manually recalculate and update the onboarding completion status for the current user based on their profile data.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Onboarding status synced successfully"},"401":{"description":"Not authenticated"}}}},"/internal/health/":{"get":{"operationId":"getInternalHealth","tags":["Health"],"summary":"Basic health check","description":"Returns basic service health status","responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"timestamp":{"type":"string","format":"date-time"}}}}}}}}},"/internal/health/liveness":{"get":{"operationId":"getInternalHealthLiveness","tags":["Health"],"summary":"Kubernetes liveness probe","description":"Kubernetes liveness probe endpoint - returns 200 if service is running","responses":{"200":{"description":"Service is alive","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"alive"},"timestamp":{"type":"string","format":"date-time"}}}}}}}}},"/internal/health/readiness":{"get":{"operationId":"getInternalHealthReadiness","tags":["Health"],"summary":"Kubernetes readiness probe","description":"Kubernetes readiness probe endpoint - checks if service is ready to accept traffic","responses":{"200":{"description":"Service readiness status","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ready","not-ready"]},"checks":{"type":"object","properties":{"database":{"type":"object","properties":{"status":{"type":"string","enum":["healthy","unhealthy"]},"error":{"type":"string"}}},"redis":{"type":"object","properties":{"status":{"type":"string","enum":["healthy","unhealthy"]},"error":{"type":"string"}}}}},"timestamp":{"type":"string","format":"date-time"}}}}}}}}},"/internal/health/detailed":{"get":{"operationId":"getInternalHealthDetailed","tags":["Health"],"summary":"Detailed health check","description":"Returns comprehensive service health including database, Redis, memory usage, and uptime","responses":{"200":{"description":"Detailed health information","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["healthy","degraded"]},"checks":{"type":"object","properties":{"database":{"type":"object","properties":{"status":{"type":"string","enum":["healthy","unhealthy"]},"error":{"type":"string"}}},"redis":{"type":"object","properties":{"status":{"type":"string","enum":["healthy","unhealthy"]},"error":{"type":"string"}}},"memory":{"type":"object","properties":{"rss":{"type":"number","description":"Resident Set Size (bytes)"},"heapTotal":{"type":"number","description":"Total heap size (bytes)"},"heapUsed":{"type":"number","description":"Used heap size (bytes)"},"external":{"type":"number","description":"External memory usage (bytes)"},"arrayBuffers":{"type":"number","description":"ArrayBuffer memory usage (bytes)"}}},"uptime":{"type":"number","description":"Process uptime in seconds"}}},"timestamp":{"type":"string","format":"date-time"}}}}}}}}},"/internal/metrics/":{"get":{"operationId":"getInternalMetrics","tags":["Monitoring"],"summary":"Prometheus metrics endpoint","description":"Returns metrics in Prometheus format for scraping","responses":{"200":{"description":"Metrics in Prometheus format","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/internal/metrics/health-metrics":{"get":{"operationId":"getInternalMetricsHealth-metrics","responses":{"200":{}}}},"/internal/metrics/events":{"get":{"operationId":"getInternalMetricsEvents","responses":{"200":{}}}},"/internal/metrics/performance":{"get":{"operationId":"getInternalMetricsPerformance","responses":{"200":{}}}},"/internal/metrics/user-events":{"get":{"operationId":"getInternalMetricsUser-events","tags":["Monitoring"],"summary":"Get user authentication events","description":"Returns recent user signup/login events for monitoring","responses":{"200":{}}}}}}