CLEMA protocol

Phone Number

An account has exactly one phone number. It is how the account signs in and how people who have the number find it through their address books. The number is private: no profile, search result or presence event carries it, and OSCAR clients never see it. It is not the account's identity either: changing it keeps the ID, the contacts and the conversations.

An account made before sign-in by phone has no number until it binds one the way a number is changed. phone.get returns "phone": null for it, and a client should ask for a number: without one, the account cannot be signed in to again once its sessions end.

Get the Number

{"id":"phone-1","type":"phone.get"}
{"id":"phone-1","type":"phone.get","ok":true,"data":{"phone":{"number":"+79161234567","verified_at":"2026-09-24T10:00:00Z"}}}
Field Type Description
number string The number in E.164 form
verified_at string RFC 3339 time the number was confirmed

Change the Number

Proving a new number takes the same two steps as signing in: phone.code sends a code to it, and phone.set binds it with that code.

{"id":"phone-2","type":"phone.code","data":{"phone":"+79161234567","app_hash":"FA+9qCX9VSu","language":"ru"}}
{"id":"phone-2","type":"phone.code","ok":true,"data":{"verification_id":"v_91d0...","code_length":6,"expires_in":300,"resend_in":60,"sent_to":"sms"}}

The fields, limits and errors are those of auth.code; the code always goes by SMS. Once the number is changed, Mandarin tells the account.

{"id":"phone-3","type":"phone.set","data":{"verification_id":"v_91d0...","code":"123456"}}
{"id":"phone-3","type":"phone.set","ok":true,"data":{"phone":{"number":"+79161234567","verified_at":"2026-09-24T10:05:00Z"}}}

The previous number is released at once and no longer signs in to the account; sessions stay as they are. Confirming the number the account already has changes nothing and returns the record as it is. A number that belongs to another account returns conflict — only once the code has proved it is the caller's, so the answer reveals nothing to anyone who does not hold the number. Codes are bound to their purpose: a verification from auth.code does not work with phone.set, nor one from phone.code with auth.phone or for another account than the one that asked for it.

A number cannot be removed, only replaced: it is the only way to sign in again once the account's sessions end.

Updates

The account owner's other connections receive phone.updated after a change:

{"type":"phone.updated","data":{"phone":{"number":"+79161234567","verified_at":"2026-09-24T10:05:00Z"}}}

Like transport.updated, it carries the whole record and is not a durable event: a client reads the number with phone.get after authenticating.