Telegram Gateway OTP
This service verifies a phone number through Telegram's Verification Codes chat. A recipient explicitly chooses Telegram; no bot invitation, project membership or pre-linked chat is needed. Notification bots remain a separate opt-in channel.
Readiness and launch boundary
The implementation has an isolated managed TEST/FAKE profile and an initial first-party HailMe LIVE pilot. Deployment, credential setup and opening admission are separate operator actions. The initial LIVE profile pins the approved Gateway account token and owner phone by SHA-256 fingerprints, permits that owner number only, and closes further sends if any nonzero provider cost is observed. A cabinet setting cannot expand those pins. General customer LIVE traffic, paid billing and US/Canada activation are not enabled by this release.
TEST accepts only `test:` synthetic subjects and cannot construct a real Gateway transport. It reuses TEST success-fee reservations, project budgets and affiliated-project exemptions. LIVE initially requires an effective VERIFY fee exemption and does not borrow the TEST wallet. An exemption affects the platform fee; it does not waive a provider's charges. Observed Gateway credit costs/refunds are reported as delivery facts separately.
The existing HailMe 1.3.3 SMS contract and mobile user/session database are unchanged. A mobile Telegram choice requires its own client/backend integration and acceptance before a wider rollout. A commercial verification cannot sign into the platform owner cabinet or mint a mobile session.
Backend API
Use `verify:write` and `verify:read`, scoped to one project/environment. Existing notification or mobile phone-OTP keys do not gain Verify rights. No LIVE key can have `verify:test-code:read`. Never embed these credentials in a mobile SDK or browser.
Create: POST `/v1/verify/verifications`, with a persisted UUID `Idempotency-Key`.
{"channel":"TELEGRAM","recipient":"+59800000000","purpose":"signup","telegramConsent":true,"language":"es"}
The phone above is a format example, not a test destination. In managed TEST use `test:customer-1` and omit Telegram consent. The service enforces the credential's environment; adding a phone to a TEST request never enables a real send. `telegramConsent:true` is the trusted backend's attestation that the recipient explicitly chose Telegram; it must come from the application's channel choice, not from a default hidden fallback.
- GET `/v1/verify/verifications/{id}` reads current status.
- POST `/{id}/checks` with `{"code":"123456"}` checks locally; persist a new operation key for each deliberate check.
- POST `/{id}/resend` with `{}` rotates the code after cooldown, with no extra platform success fee and no TTL/attempt reset.
- POST `/{id}/cancel` with `{}` closes a pending verification.
- GET `/{id}/test-code` is available only to separately scoped TEST observers.
The prefix for these relative paths is `/v1/verify/verifications`. Keep the original create key and exact request after an uncertain client response. A replay cannot send again. A known UNKNOWN result is an instruction to inspect the same verification; it must not trigger a fresh request or SMS fallback.
The published JVM SDK `0.1.0-dev.3` does not contain typed Verify methods. Use these backend HTTP endpoints until a separately signed SDK release adds them. The existing notification SDK remains compatible; no Maven version is republished by this change.
State, limits and billing
States include SENDING, PENDING, UNKNOWN, VERIFIED, LOCKED, CANCELLED, EXPIRED and FAILED. A durable claim commits before network I/O. SENDING returns HTTP202; delivery UNKNOWN/FAILED returns HTTP503 with a stable verification receipt. Other admission errors use the ordinary error envelope. A signed delivery callback can reconcile SENDING/UNKNOWN into PENDING, but only a correct local code check produces VERIFIED. Old/out-of-order reports cannot reopen terminal challenges or downgrade newer delivery facts.
The code lasts 300 seconds. There are five check attempts, two resends, a 30-second cooldown, five send claims per recipient/hour, and deployment-specific daily/monthly send caps. Resends count toward send caps and never extend the original expiry. A resend with less than 30 seconds left is rejected. There are at most 1000 retained managed challenges per pilot scope; reaching this storage limit stops new admission. Closing the deployment's admission flag keeps reads, checks, cancel and expiry available.
TEST reserves the immutable sampled success price once and settles it once on VERIFIED; failure, cancel, lock and expiry release the hold. Replays do not add fees. The initial LIVE fee snapshot is zero under the effective exemption. Charging commercial LIVE customers remains closed until real wallet/payment accounting is ready.
Credentials and callbacks
An owner enables Verify in Options & channels to reveal its available controls. In an approved LIVE scope, the Verify page contains a separate Telegram Gateway connection form. Tokens are encrypted, versioned, never returned after write and never written to browser storage. The protected connection also stores its Gateway owner phone. Updating a token requires an operator-approved pin update; previous encrypted versions stay available for callbacks belonging to already-issued challenges.
Callback URLs use the official host and `/hooks/telegram-gateway/{configurationId}/{credentialVersion}`. Verify the HMAC-SHA256 of timestamp + newline + raw body using SHA256(token), enforce a five-minute timestamp window, then match pinned scope, credential version, phone fingerprint, attempt payload and request ID. Keep provider input bounded. Code/body/phone/token are never diagnostic log fields; `code_entered` in a provider report is discarded.
Gateway status responses and callback reports can return international phone digits without the leading `+`. After authenticating the raw callback bytes, the adapter adds only that missing prefix before matching the original E.164 recipient. It never removes punctuation, whitespace, leading zeroes or repairs another number. Client recipient input still requires strict E.164.
The platform calls `sendVerificationMessage` directly once per durable claim. It does not call `checkSendAbility` in the background: that availability check may incur a charge. It performs no automatic provider retry and no automatic channel fallback after an ambiguous outcome. Balance funding and wallet connections are human-controlled, separate actions.
Operator rollout
V013 adds five prefixed tables only. Stop runtime schema leases for explicit migration; normal startup never mutates schema. Rehearse V012→V013 on a disposable copy and retain an encrypted backup. A rollback binary must understand V013 even when Gateway admission is off. Preserve the existing mobile SMS/push/OTP configuration and keys. Stage uses only FAKE and needs no Gateway token.
Set `PLATFORM_MANAGED_VERIFY_PROFILE` to the exact approved project and `mode:FAKE` on stage. For LIVE use only HailMe, initially `admissionEnabled:false`, plus verified `ownerPhoneSha256` and `credentialSha256` pins. Move the escrowed token through an approved secret-transfer path, configure the connection, and obtain the exact real-recipient send permission before opening the owner control test. Do not infer permission from a healthy callback URL or a successful signup.
Primary references: Gateway API, verification tutorial, Gateway terms.
HailMe phone sign-in integration
The private first-party backend port is /v1/hailme/phone-otp/telegram/challenges, with GET by ID, POST /checks and a separate FAKE-only /test-code inbox. It reuses durable Verify challenges, one provider claim, signed callbacks, budgets and affiliation snapshots. Its reserved phone-auth purpose cannot be created, checked, resent or exposed by generic Verify credentials or cabinet controls. The existing hailme-phone-otp:read/write service credential stays on the backend; code observation requires a separate TEST-only scope.
The B2C /auth/phone/send-code body accepts optional otpChannel: TELEGRAM. Omitting it keeps SMS for every released mobile version. Telegram is explicit, never selected from a country, inferred contact number or silent fallback. The number being verified is the HailMe account phone; a different messenger number cannot authenticate that account. The existing /auth/phone/verify-code JSON, JWT and registration-session contract are retained.
One Redis owner per phone retains the original channel, expiry and operation UUID. An unresolved Telegram send cannot turn into an SMS request. Create/check acknowledgement loss replays the original operation. A signed late delivery receipt can resolve UNKNOWN without another physical send. Five guesses and the five-minute phone cooldown remain; channel changes do not reset them. Existing SMS codes and in-flight platform owners continue to work. Closing new Telegram admission leaves its existing read/check/replay paths available.
Activation uses PLATFORM_HAILME_TELEGRAM_OTP_ENABLED on the platform and HAILME_TELEGRAM_OTP_ENABLED on B2C, after local PostgreSQL18 TEST/FAKE acceptance. Both are off by default. The platform also requires matching phone and Gateway profiles; their mode, project and environment must match. The existing Gateway operation ID, credential pins and consumed day/month counters are retained. The original SMS profile, keys and limits are unchanged.
LIVE remains restricted to the actual corporate Gateway owner, with maximum provider spend zero. General paid recipients, checkSendAbility probes, top-ups and payments are not enabled. Funding and a reviewed broader-recipient policy are separate gates. An approved free owner control proves that one path, not universal delivery or availability for other HailMe users. The current mobile screen has no new channel selector; that mobile release remains a separate task.
The existing read-only phone-OTP operations snapshot includes nested telegram status, retained send counters, unresolved operations and nonzero-cost attempts. The optional monitor switch PLATFORM_MONITOR_TELEGRAM_PHONE_OTP_ENABLED checks this state without sending a code. Keep the SMS monitor active throughout. Roll back new admission flags first; preserve code capable of checking in-flight Telegram challenges until they drain. Never restore an old DB or deploy a binary that cannot read the retained owners.
Sources: Gateway API and free own-number rule, Telegram opt-in and FAQ.
The dedicated `PLATFORM_HAILME_TELEGRAM_OTP_ENABLED` admission does not reopen the generic Gateway pilot. Its `PLATFORM_MANAGED_VERIFY_PROFILE.admissionEnabled` remains false; shared limits and consumed counters still apply.