Mobile User

Phone Management

Set an initial phone number or replace the current one with OTP verification.

Flow Summary

The current phone module has two flows:

Set Phone Flow

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

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

Replace Phone Flow

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

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

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

Step 4. Verify (new)
POST /api/v1/auth/reset-phone/new-phone/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 Phone (For Users Without A Verified Phone)

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

1. Send OTP

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

Request

{
  "phone_code": "855",
  "country_code": "KH",
  "phone_number": "012345678"
}

Success response

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

Current rules

  • If the user already has a stored phone value, the submitted phone must match that stored phone exactly after normalization.
  • If the user has no phone yet, the service validates the phone format and checks that the phone number is not already used by another user.
  • The OTP session is stored in Redis for 5 minutes.
  • The response reuses the phone-reset OTP message key.

2. Verify OTP And Save Phone

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

Request

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

Success response

{
  "status_code": 200,
  "message": "Phone number updated successfully",
  "data": {
    "success": true,
    "message": "Phone number set and verified successfully."
  }
}

Notes

  • The OTP session purpose must be set_phone.
  • On success, the service updates phone, phone_code, country_code, and sets is_phone_verified = true.
  • The OTP session is deleted after a successful verification.

Reset Phone (Replace An Existing Verified Phone)

This is a 4-step flow.

1. Send OTP To Current Phone

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

Request

{
  "phone_code": "855",
  "country_code": "KH",
  "phone_number": "012345678"
}

Success response

{
  "status_code": 200,
  "message": "Phone reset initiated successfully",
  "data": {
    "current_phone_session_id": "f2b4a8f8-0f5f-4a8d-9b20-8f2a1f4f34c2",
    "phone": "85512345678",
    "expires_at": 300
  }
}

Notes

  • phone_code and phone_number are required. country_code is optional here.
  • The submitted current phone must match the logged-in user’s registered phone.
  • The service rate-limits this step to once per minute per user. If hit, it returns a failure payload with retry_after.
  • The OTP session is stored in Redis for 5 minutes.

2. Verify Current Phone OTP

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

Request

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

Success response

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

Notes

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

3. Send OTP To New Phone

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

Request

{
  "phone_code": "855",
  "country_code": "KH",
  "new_phone_number": "098765432",
  "new_phone_session_id": "12dc8cb8-cdf5-4f1a-b5ec-c0fd7184bfe2"
}

Success response

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

Notes

  • country_code is required on this step.
  • The new_phone_session_id must come from step 2 and must belong to the current user.
  • The new phone number is validated and must not already exist.
  • The OTP for the new phone uses the same new_phone_session_id value as the OTP session UUID.
  • Resending is limited to once per minute for the same new_phone_session_id. When blocked, the response includes retry_after.

4. Verify New Phone OTP And Update

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

Request

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

Success response

{
  "status_code": 200,
  "message": "OTP verified successfully",
  "data": {
    "success": true,
    "message": "Phone number updated successfully."
  }
}

Notes

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

Common Failure Cases

  • 400: invalid phone 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 phone number already exists

Integration Notes

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