Troubleshoot contact onboarding
Use this guide when someone shared their contact with the client bot but the
Mini App still reports contact_required, when bot permission looks wrong, or
after an API or database outage. It covers authentication facts only.
Notification preferences are a separate client setting; see
Client booking notifications.
How facts are applied is explained in Channel authentication.
Prerequisites
- A read-only SQL session on the database, as an operator.
- Access to the API's logs and the client bot's token, for
getWebhookInfo.
Check for pending facts
Facts that were recorded but not applied to an account yet:
SELECT 'contact' AS "fact", c."source_app_id" AS "app",
c."source_version", c."applied_version", c."updated_at",
p."supabase_user_id" IS NOT NULL AS "signed_in"
FROM "channel_auth"."channel_contacts" AS c
JOIN "channel_auth"."provider_identities" AS i ON i."id" = c."provider_identity_id"
JOIN "channel_auth"."principals" AS p ON p."id" = i."principal_id"
WHERE c."applied_version" < c."source_version"
UNION ALL
SELECT 'permission', m."app_id", m."source_version", m."applied_version",
m."updated_at", p."supabase_user_id" IS NOT NULL
FROM "channel_auth"."channel_permissions" AS m
JOIN "channel_auth"."provider_identities" AS i ON i."id" = m."provider_identity_id"
JOIN "channel_auth"."principals" AS p ON p."id" = i."principal_id"
WHERE m."applied_version" < m."source_version"
ORDER BY "updated_at";
| What you see | What it means |
|---|---|
| No rows | Every recorded fact is applied |
signed_in is false | The person has never signed in. Their first sign-in applies the fact |
signed_in is true, app is the client app, the person has only used the professional app | They have no client account yet. Their first client Mini App sign-in creates it and applies the fact |
app is no longer in CHANNEL_AUTH_TELEGRAM_APPS_JSON | The fact has nowhere to go and stays pending |
signed_in is true, recent rows | Application failed. Telegram redelivers the update, which applies it; check the API logs |
signed_in is true, rows older than a day | Telegram stopped redelivering. The person's next update or sign-in applies the fact |
The API logs a failed webhook as telegram update failed with the error name,
and a failed application during sign-in as pending facts could not be applied. Logs never include the phone or the fact.
A shared contact did not arrive
Work through these in order:
- The bot did not receive it. Run
getWebhookInfofor the client bot, as in Move Telegram webhooks to channel authentication. Alast_error_messagepoints at the webhook URL, the secret, or a500while applying. An emptyurlmeans no webhook is set, for example because the local preview bot (apps/client_tg_app/bot.py) polls this bot. That script receives the contact and replies, but the API never sees it; stop it and set the webhook. - The update was not a verified contact. Someone else's contact card, a forwarded contact, a group chat, and a phone that is not a valid international number are ignored. Ask the person to use the Mini App's contact request, which shares their own contact from a private chat.
- The person has not signed in to the client Mini App yet. A contact shared before the first client sign-in is kept and applied right after that sign-in.
- Application failed. Use the pending check above. While a fact is pending,
the account stays
contact_required, and the session stays usable.
Channel authentication never fills in a contact from a Mini App request, a display name, or editable user metadata. Only a bot update proves a contact.
After an outage
Fix the cause first: the API must reach the database. There is nothing to replay. Telegram redelivers every update the API did not acknowledge, for up to a day, and each redelivered update applies whatever is still pending for that person. Facts that outlive Telegram's retries are applied at the person's next update or sign-in.