> ## Documentation Index
> Fetch the complete documentation index at: https://veriqa.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Phone number

> How Veriqa gets the user's phone number in each channel: the mechanism, whether the number is proven to be the user's, what the platform limits, and which part is Veriqa's work and which is yours.

Veriqa takes a phone number **only from the channel**: the messenger hands it over, the user never
types it. The sign-in page has no phone field, and a number typed into the chat as a message is not
taken. Your application receives it as the standard `phone_number` claim in E.164 format, next to
`phone_number_verified`.

## When Veriqa asks for the number

Veriqa asks only when your application needs the number and the channel has not already given it.
The need is the requirement level of the `phone_number` claim:

| Level | Where it comes from |
| - | - |
| `Required` | `RequirePhone` or `Level: Required` in [`Veriqa:ScopesClaims`](/docs/reference/configuration#veriqascopesclaims), `RequirePhone` or `ClaimLevels` of the [client entry](/docs/reference/configuration#clients), or `essential: true` on `phone_number` in the request's `claims` parameter |
| `Optional` | `Level: Optional`, or `phone_number` in the request's `claims` parameter without `essential` |
| `IfAvailable` (default) | nothing is asked: the number is issued only if the channel gave it by itself |

A requirement counts only when the sign-in asks for the `phone` scope. With a `Required` number the
sign-in window offers only the channels able to provide one; if none is left, `/connect/authorize`
answers `400 no_channel_for_required_phone` ([troubleshooting](/docs/guides/troubleshooting)).

The request comes once the user has confirmed the sign-in — in the chat or with "Yes" on the
confirmation page in the browser. The bot sends it to the chat the user signed in from: a short text,
a button that shares the number and a second button whose meaning depends on the level. The texts
are shown in the user's language.

| Level | Second button | No answer |
| - | - | - |
| `Optional` | **Skip** — the sign-in goes on without the number | the step is skipped when the window `ClaimCompletion.PhoneOptionalWindow` runs out |
| `Required` | **Cancel sign-in** — the sign-in fails and your application receives `error=access_denied` | the transaction waits until it expires: no platform tells the bot that the user declined to share |

The window is 60 seconds by default. Change it with `Veriqa:ClaimCompletion:PhoneOptionalWindow`, or
for one application with the `ClaimCompletionPhoneOptionalWindow` member of its client entry (see
[keys in the declaration catalog](/docs/reference/configuration#keys-in-the-declaration-catalog)). The
window never runs past the transaction: the step is skipped at least 5 seconds before the
transaction expires.

When the shared number completes the sign-in, the chat shows the usual receipt of a confirmed
sign-in; **Cancel sign-in** shows the receipt of a declined one.

## What your application receives

* `phone_number` — in E.164: `+` and digits. A number that does not convert to E.164 counts as
  absent: it is not issued, and the step waits as if the user had not answered.
* `phone_number_verified` — a JSON boolean: `true` only when the channel proves that the number
  belongs to the user who sent it, otherwise `false`.
* Both claims go out only under a granted `phone` scope.

A contact that fails the channel's ownership check — somebody else's contact card, a forwarded
contact, a missing or wrong signature — is not taken, and the bot asks again. A request that cannot
be delivered to the chat even after retries changes nothing: the step waits as it does with no
answer.

## Channel by channel

"Who" is whose code gets the number: Veriqa's channel adapter, or your own integration with the
platform. Channels whose adapter is not shipped yet show what the platform's API allows.

| Channel | How the number is obtained | Proven to be the user's | Who | Official sources |
| - | - | - | - | - |
| Telegram | the `request_contact` button of a reply keyboard; private chats only | yes — the contact's `user_id` is the sender, and the message is not forwarded | Veriqa | [Bot API: KeyboardButton](https://core.telegram.org/bots/api#keyboardbutton), [Log In With Telegram](https://core.telegram.org/bots/telegram-login) |
| MAX | the `request_contact` button; the number comes in the contact's `vcf_info`. **Webhook mode only** | yes — the contact's `hash` matches HMAC-SHA256 of `vcf_info` keyed with the bot token | Veriqa | [keyboard](https://dev.max.ru/docs-api/use-cases/sending-messages/keyboard), [attachments](https://dev.max.ru/docs-api/use-cases/sending-messages/another-attachments) |
| WhatsApp | automatically — the sender's number (`from` / `wa_id`) of every message; without it, the `request_contact_info` interactive message | yes — a number from the message itself, or a contact sent from the request (`origin = contact_request`) | Veriqa | [Business-scoped user IDs](https://developers.facebook.com/documentation/business-messaging/whatsapp/business-scoped-user-ids/) |
| BiP | automatically — the subscriber's MSISDN, if Turkcell allows the service open numbers (`receiver.type = 2`) | yes — the number is the account | Veriqa, once the adapter ships | [sending](https://bip.com/tr/gelistiriciler/kesfet-api/mesaj-gonderme/servisler/kullanici-bazli-mesaj-gonderimi/), [subscribers](https://bip.com/tr/gelistiriciler/kesfet-api/abone-islemleri/) |
| Viber | the `share-phone` button (API 3 and later) | no | Veriqa, once the adapter ships | [keyboards](https://developers.viber.com/docs/tools/keyboards/) |
| Messenger | the `user_phone_number` quick reply — the number from the user's profile | no | Veriqa, once the adapter ships | [quick replies](https://developers.facebook.com/docs/messenger-platform/send-messages/quick-replies) |
| Instagram Direct | the `user_phone_number` quick reply | no | Veriqa, once the adapter ships | [quick replies](https://developers.facebook.com/documentation/business-messaging/instagram-messaging/features/quick-replies) |
| Slack | the `profile.phone` profile field, free text | no; a value that is not E.164 is not issued | Veriqa, once the adapter ships | [users.info](https://docs.slack.dev/reference/methods/users.info) |
| Microsoft Teams | Microsoft Graph `mobilePhone`, the `User.Read.All` permission with admin consent | no | Veriqa, once the adapter ships | [Graph user](https://learn.microsoft.com/en-us/graph/api/resources/user) |
| LINE | only LINE Login or LIFF with the `phone` scope (LINE Profile+, companies in Japan, on application) | — | you | [LINE Profile+](https://developers.line.biz/en/docs/partner-docs/line-profile-plus/) |
| KakaoTalk | Kakao Login or Kakao Sync, the `phone_number` consent item, a Biz App and review | — | you | [Kakao Login](https://developers.kakao.com/docs/en/kakaologin/utilize) |
| WeChat | the mini program `getPhoneNumber`, a verified business entity, paid | — | you | [getPhoneNumber](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html) |
| Zalo | the mini app `getPhoneNumber` plus a server-side token exchange | — | you | [getPhoneNumber](https://docs.zaloplatforms.com/docs/MA/api/user/user-information/getPhoneNumber) |
| Discord | none | — | — | [User](https://docs.discord.com/developers/resources/user) |
| Threema | none | — | — | [Threema Gateway](https://gateway.threema.ch/en/developer/api) |
| Email | none | — | — | — |

### Telegram

The bot sends a reply keyboard: the share button on top, the second button below it; the keyboard
hides after a press. Veriqa takes only the user's own contact: the `user_id` of the contact must be
the sender, and a forwarded contact or a card from the address book is rejected.

Telegram's website login ([Log In With Telegram](https://core.telegram.org/bots/telegram-login)) can
return the phone number as well — that is your own integration with Telegram, outside Veriqa.

<Warning>
  If you route the bot's updates yourself — one bot shared by your code and Veriqa, and your code
  decides which update goes to Veriqa with `OwnsInboundEvent` (see
  [phone number on request](/docs/guides/custom-channel-adapter#phone-number-on-request)) —
  the **Skip** and **Cancel sign-in** buttons reach your bot, not Veriqa: in Telegram they are plain
  text buttons with no marker to recognise. The shared contact itself is recognised. An `Optional`
  number is then skipped by the window, and a `Required` one waits until the transaction expires.
</Warning>

### MAX

The bot sends the share button and a callback button with the second action. The number is taken
from `vcf_info` only when the contact's `hash` signature matches — computed with the bot token of the
tenant that received the contact.

The phone number is requested **only in webhook mode**. With `UpdateMode: Polling` the MAX adapter
declares no phone number and asks nothing: the signature is read from the raw webhook body, which
polling does not have, so every contact would be rejected. A `Required` number then leaves MAX out
of the sign-in window.

### WhatsApp

Today the number arrives with every message: WhatsApp identifies the user by the phone number, so
the token carries `phone_number` with `phone_number_verified: true`, and nothing is asked.

The request is for users WhatsApp addresses without a number (business-scoped user IDs): the
`request_contact_info` interactive message, followed by a separate message with the second button —
the request carries no buttons of its own. A contact card the user sends by hand
(`origin = other`) is rejected. The shipped Meta Cloud API provider sends the request; a WhatsApp
provider of your own (`IWhatsAppProvider`) sends it only if it implements `SendContactRequestAsync` —
otherwise the request is not delivered and the step waits as it does with no answer.

### LINE, KakaoTalk, WeChat and Zalo — your integration

These platforms give the phone number only to their own login or mini-app, which you register and
the platform approves — not to a bot. Veriqa does not get the number there. If your application
needs it from these users, obtain it in your own code through the platform's login and keep it on
your side.

### Discord and Threema

Neither platform gives a bot the user's phone number.

## Limitations

* The window of an `Optional` number is kept in the memory of the instance that sent the request.
  If that instance stops before the window runs out, the step is not skipped, and the transaction
  waits until it expires.
* The record that ties a chat to its waiting request is in-process by default. With more than one
  replica, move it to Redis together with the other channel stores
  ([channel stores](/docs/guides/storage#channel-stores-multiple-replicas)) — otherwise a number that
  arrives at another replica finds no request.
* When one chat has two sign-ins waiting for a number, the number goes to the one whose request came
  last; the other waits for its window or until it expires.
* The buttons of the request stay in the chat after the step ends without a press — the window of
  an `Optional` number runs out, the sign-in expires. Veriqa does not take them down: in Telegram
  the keyboard stays under the input field until the user presses one of its buttons. A number
  shared from it afterwards finds no waiting request and is not taken, and the bot does not answer
  it; a press of the second button is then an ordinary message to the bot.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.