Mobile User

Email Management

Set an initial email address or replace the current one with OTP verification.

Flow Summary

The current email module has two flows:

Set Email Flow

Step 1. Send OTP
POST /api/v1/auth/set-email/otp

Step 2. Verify and save
POST /api/v1/auth/set-email/verification

Replace Email Flow

Step 1. Send OTP (current)
POST /api/v1/auth/reset-email/current-email/otp

Step 2. Verify (current)
POST /api/v1/auth/reset-email/current-email/verification

Step 3. Send OTP (new)
POST /api/v1/auth/reset-email/new-email/otp

Step 4. Verify (new)
POST /api/v1/auth/reset-email/new-email/verification

In development environments, OTP is currently hardcoded to 123456.

All routes below should be treated as protected routes and sent with:

Authorization: Bearer <access_token>

Set Email (For Users Without A Verified Email)

Use this when the user registered without a verified email, or has a stored email that is still unverified.

1. Send OTP

POST /api/v1/auth/set-email/otp

Request

{
  "email": "user@example.com"
}

Success response

{
  "status_code": 200,
  "message": "OTP sent successfully",
  "data": {
    "set_email_session_id": "9b7f1b4d-7c75-4d14-bec8-0d03b0f809d6",
    "expires_at": 300
  }
}

Current rules

  • If the user already has a stored email value, the submitted email must match that stored value after lowercase/trim normalization.
  • If the user has no email yet, the service checks that the new email is not already used by another user.
  • The OTP session is stored in Redis for 5 minutes.
  • The handler reuses the email-reset OTP message key for this step.

2. Verify OTP And Save Email

POST /api/v1/auth/set-email/verification

Request

{
  "set_email_session_id": "9b7f1b4d-7c75-4d14-bec8-0d03b0f809d6",
  "otp_code": "123456"
}

Success response

{
  "status_code": 200,
  "message": "Email reset successfully",
  "data": {
    "success": true,
    "message": "Email address set and verified successfully."
  }
}

Notes

  • The OTP session purpose must be set_email.
  • On success, the service updates email and sets is_email_verified = true.
  • The OTP session is deleted after a successful verification.
  • The handler reuses the email-reset success message key even though this is the set-email flow.

Reset Email (Replace An Existing Email)

This is a 4-step flow.

1. Send OTP To Current Email

POST /api/v1/auth/reset-email/current-email/otp

Request

{
  "email": "current@example.com"
}

Success response

{
  "status_code": 200,
  "message": "Email reset initiated successfully",
  "data": {
    "current_email_session_id": "f2b4a8f8-0f5f-4a8d-9b20-8f2a1f4f34c2",
    "email": "current@example.com",
    "expires_at": 300
  }
}

Notes

  • The submitted current email must match the logged-in user’s registered email after lowercase/trim normalization.
  • The OTP session is stored in Redis for 5 minutes.
  • This step sends OTP through the email service, not SMS.

2. Verify Current Email OTP

POST /api/v1/auth/reset-email/current-email/verification

Request

{
  "current_email_session_id": "f2b4a8f8-0f5f-4a8d-9b20-8f2a1f4f34c2",
  "otp_code": "123456"
}

Success response

{
  "status_code": 200,
  "message": "Current email verified successfully",
  "data": {
    "success": true,
    "message": "Current email verified successfully. You can now proceed to change email address.",
    "new_email_session_id": "12dc8cb8-cdf5-4f1a-b5ec-c0fd7184bfe2"
  }
}

Notes

  • This consumes the OTP session from step 1.
  • The service creates a new_email_session_id that unlocks step 3.
  • That intermediate reset session is stored in Redis for 10 minutes.

3. Send OTP To New Email

POST /api/v1/auth/reset-email/new-email/otp

Request

{
  "new_email": "new@example.com",
  "new_email_session_id": "12dc8cb8-cdf5-4f1a-b5ec-c0fd7184bfe2"
}

Success response

{
  "status_code": 200,
  "message": "OTP sent successfully",
  "data": {
    "new_email_session_id": "12dc8cb8-cdf5-4f1a-b5ec-c0fd7184bfe2",
    "expires_at": 300
  }
}

Notes

  • The new_email_session_id must come from step 2 and must belong to the current user.
  • The new email must not already exist.
  • The OTP for the new email uses the same new_email_session_id value as the OTP session UUID.
  • Resending is limited to once per minute for the same new_email_session_id. When blocked, the response includes retry_after.
  • This step sends OTP through the email service.

4. Verify New Email OTP And Update

POST /api/v1/auth/reset-email/new-email/verification

Request

{
  "new_email_session_id": "12dc8cb8-cdf5-4f1a-b5ec-c0fd7184bfe2",
  "otp_code": "123456"
}

Success response

{
  "status_code": 200,
  "message": "Email reset successfully",
  "data": {
    "success": true,
    "message": "Email address updated successfully."
  }
}

Notes

  • The OTP session purpose must be reset_email.
  • If the user has a cardholder_id, the service updates the cardholder email on UQPay before saving the local email.
  • The service updates email in the local database, then deletes the OTP session.

Common Failure Cases

  • 400: invalid email format, invalid OTP, expired session, or wrong session purpose
  • 403: resend requested too quickly, or reset session does not belong to the current user
  • 404: user not found
  • 409: new email already exists

Integration Notes

  1. Use set-email only for users who still need to verify or attach an email.
  2. Use reset-email when replacing an already registered email.
  3. The OTP steps here are route-specific and do not use /connect/token.
Copyright © 2026