switching clears the token & responses
No token
30

Removing a phone credential

Profile & credentials

A signed-in user unlinks their phone. STEP-UP action (counterpart to adding a phone, scenario 29). Refused if it would leave the account with zero credentials.

  1. DELETE /v1/me/phone → unlink the phone (step-up: currentPassword or otpCode)
DELETE/v1/me/phoneauthUnlink phone (step-up)

Step-up: a password-holding user supplies currentPassword; a password-less user an otpCode (from POST /v1/me/mfa/otp/request). TWO separate guards, both returning 409. (1) last_credential — refused if the phone is the user's ONLY credential (e.g. a phone-first account from scenario 3); an email counts only if it is VERIFIED, since login refuses an unverified one. (2) PRIMARY-CHANNEL — refused, and ENFORCED rather than merely advised, while login_mode = 'phone'. That case is a HARD lockout the last-credential guard cannot catch: it asks “is any credential left?” (yes — email + password), not “can the login this account will actually be asked to complete still be completed?”. With the number gone, every new-device challenge would still be planned on the phone channel and could neither be delivered nor satisfied. Switch the channel first (scenario 31), then remove. Unlinking also DROPS any phone_otp MFA method bound to the number — leaving it enrolled would advertise a second factor whose channel no longer existed, moving the same lockout one hop later; ensure another factor remains or re-enrol (scenario 25).

⛓ Needs an out-of-band value?
Request body (JSON)
ℹ︎Field guide — what each value means & where it comes from2 fields

Unlinks the caller's verified phone. Requires Auth (the tester sends accessToken + x-gateway-context). Step-up re-auth: supply AT LEAST ONE factor — a password-holding user supplies currentPassword, a password-less (phone-only) user supplies otpCode. The body is .strict(). TWO guards then apply, both 409: last_credential (a phone-only user cannot remove their only credential — an email counts only if VERIFIED, since login refuses an unverified one), and the PRIMARY-CHANNEL guard, which refuses while login_mode = 'phone' because the account's new-device challenge would then target a number it no longer holds. Switch the channel first (scenario 31). Unlinking also drops any phone_otp MFA method bound to the number.

currentPasswordbodystringconditionalyou choose
The user types their current account password (1-1024 chars) as step-up re-authentication. Supplied by password-holding users; at least one of currentPassword or otpCode is required. A wrong value returns 401 invalid_credentials.
otpCodebodystringconditionalout-of-band
Numeric phone OTP the user retrieves from the SMS sent by POST /v1/me/mfa/otp/request (§3.26) and pastes here. Used by password-less (phone-only) accounts as the step-up factor. Conditional: at least one of currentPassword or otpCode must be supplied.
Bearer token
DELETE https://api.kerja.team/v1/me/phone
↩︎Response guide — what comes back & what each value meansno body

200 OK with the standard { data } envelope, where data is an empty object ({}). The phone is unlinked (phone and phoneVerifiedAt cleared, user.upserted enqueued, phone_unlinked audited) but the success body carries no fields. No notable response headers (no Set-Cookie, no Cache-Control: no-store).