"Try again later" is not "invalid": handling a phone check that cannot answer right now

Build your integration in the free sandbox before preparing for production.

Your measurement choice is reused on this site and can be turned off. Account association requires separate consent.

"Try again later" is not "invalid": handling a phone check that cannot answer right now

A public question describes Google Play Console showing "We can't verify your phone number right now. Try again later." We cannot diagnose that platform, we do not represent the person who asked, and nothing here fixes Google Play. The message does raise an adjacent decision for your own signup flow. When your phone check cannot answer, does the user see a rejection, a dead end, or a bounded retry with a clear next step? This article uses only the checked four-branch synthetic example to separate the provider_failure path from a genuine invalid result. It proposes caller-owned failure handling and gives exact fixture outputs you can assert against.

curl https://gridzen.ai/console/api/quickstart/trial \
  -H 'Content-Type: application/json' \
  -d '{"task": "signup_phone", "scenario": "success"}'

Integration guide

The problem and the decision

"Try again later" tells the user that the verifying system did not reach a conclusion. It does not say the number is wrong. If your signup code treats every non-pass as a failure, a temporary outage becomes a permanent rejection. The user may also retry endlessly with no feedback. You need to decide three things. First, which state a temporary failure maps to. Second, how many retries you allow and over what interval. Third, where the case goes when retries run out. The fixture does not make these decisions for you. It only names a next step. Your application owns the retry budget, the user message and the review queue. This is proposed application design, not behavior the example implements.

What the fixture actually returns

The downloadable example runs four synthetic branches: normal, unknown, provider_failure and otp. It needs Python 3.10 or later, no credentials and no network calls. Every branch returns the fields mode, branch, otp_sent, live_line_type_available, state, valid, line_type and next. For provider_failure, the expected output is mode synthetic_fixture, branch provider_failure, otp_sent false, live_line_type_available false, state unknown, valid null, line_type null and next bounded_retry_then_review. Compare that with unknown, which returns the same state unknown and null valid but next review_or_existing_otp. Only normal returns valid true, line_type mobile and next continue_customer_policy. The otp branch returns state unsupported and next customer_otp_provider, and no code is sent. Any other branch name raises UNSUPPORTED_FIXTURE. Note that valid null is not valid false. No branch in this fixture means "invalid number", and none should be read as a rejection.

A hypothetical worked scenario

Imagine a hypothetical small B2B signup form. A user enters a number, and your backend's phone check times out. In the sandbox you represent this by running the provider_failure branch. Your proposed handler reads state unknown and next bounded_retry_then_review. It does not mark the account rejected. It stores the attempt with a counter and shows the user a neutral message: the check is unavailable, and the signup is not refused. It retries on a schedule you define, for example a small fixed number of attempts. If those attempts still return unknown, the handler moves the case to human review instead of looping. If the user is otherwise allowed to continue under your policy, any action that depends on the check stays held. The counts and timings are your choices for illustration. They are not fixture behavior or a recommended production value.

Limits you should state plainly

The example is synthetic and is never a live provider adapter. A valid true result in the normal branch is canned data. It does not prove ownership, SMS reachability or identity. The current free production Basic Lookup route does not request line type, ownership, reachability or OTP delivery. That is why live_line_type_available is false in every branch. Separate account enrollment applies to that route, and it has a technical rate limit of 60 requests per minute. A burst of user retries could therefore make your own failures worse. The otp branch recommends using your existing OTP provider and sends nothing. The four-branch download is not the same as the anonymous three-scenario HTTP trial, so do not treat one as evidence for the other.

Failure handling to build around the fixture

Keep "unavailable" and "invalid" as distinct stored states, even if your user interface words them similarly. Cap retries per signup and per time window, and stay under the rate limit. Log each attempt with its branch outcome so reviewers can see why a case arrived. Never let a retry loop silently become an automatic rejection or an automatic approval. Handle unexpected input explicitly: in the fixture, an unsupported branch raises an error rather than guessing. Your adapter should do the same. The included engineering tests cover simulated transitions, bindings, duplicate callbacks, restart and timeouts. They do not prove production success.

Acceptance checklist

Run all four branches and assert the exact outputs above. Confirm that provider_failure never produces a rejected account. Confirm that unknown routes to review or your existing OTP step. Confirm that otp_sent stays false and that your code does not wait for a code the fixture never sent. Check that retries stop at your defined bound and then create a review item. Check that the user sees a non-accusatory message. Assert live_line_type_available false, and make sure no logic depends on line_type in live use. Record that sandbox passing is not live business permission. If this matches your task, the next step is to run the synthetic tutorial and then optionally save a sandbox request. Live route availability requires separate confirmation.

Run the checked synthetic example

Download and extract https://gridzen.ai/downloads/verification-delivery.zip, enter verification_delivery, then run with Python 3.10+. This local example makes no network calls. Use a fresh database for an independent identity run.

python3 delivery.py phone --branch unknown
python3 delivery.py phone --branch provider_failure

Sources and scope

https://gridzen.ai/guides/signup-phone-check/ https://gridzen.ai/guides/current-capabilities/ Based on the checked source snapshot and local fixture; proposed application rules are design advice, not proven production behavior. Written with AI assistance and reviewed independently.

Separately try three sandbox outcomes Create an account to continue integrating

A download does not prove a successful run. Local examples and the online sandbox are tested separately.

Connect your app

Run three synthetic outcomes · Get integration help

Connect a free sandbox account

Basic number validity does not prove possession or reachability and does not send OTP. Your app owns the admission decision.