Skip to content
OmniChannels.ai

OTP Autofill for SMS: Android, iOS and Web Formats

How OTP autofill works on Android, iOS and the web: SMS Retriever hash, domain-bound codes, WebOTP, one message format for all three and WhatsApp autofill.

Updated 9 min read

By OmniChannels.ai Editorial Team · Reviewed by BroadNet Messaging Solutions Team

Integration

Short answer

OTP autofill is built from three separate mechanisms. Android apps use the SMS Retriever API, which needs an 11-character app hash in a message of at most 140 bytes, or the SMS User Consent API, which needs no hash. iOS offers codes from the SMS itself, and domain-bound codes end with a line such as @example.com #123456. Websites use autocomplete="one-time-code" and the WebOTP API, which read the same last line.

On this page
  1. How does OTP autofill work on each platform?
  2. How does the Android SMS Retriever API work?
  3. How does iOS and iPadOS autofill a code?
  4. How does OTP autofill work on the web?
  5. Can one SMS work on Android, iOS and the web?
  6. What about Arabic and Unicode text?
  7. How do WhatsApp authentication templates handle autofill?
  8. Worked example: a ride-hailing app login
  9. What are the common mistakes?
  10. How OmniChannels.ai helps

Key takeaways

  • Android has two APIs: SMS Retriever (message of at most 140 bytes with a one-time code and an 11-character app hash) and SMS User Consent (the user approves sharing one message).
  • On iOS, set the field to the one-time-code content type; domain-bound codes end with a last line of @domain, a space and #code, supported from iOS 14.
  • On the web, add autocomplete="one-time-code" to the input and call navigator.credentials.get with an sms transport for Chrome on Android; Safari uses the attribute.
  • One message can satisfy all three if the app hash sits above a final line of @domain #code, but test it on real devices and operators.
  • WhatsApp authentication templates offer copy-code, one-tap and zero-tap buttons; zero-tap is Android only.

This guide is for developers and product owners who want a verification code to fill itself in on Android, iPhone and the web. It explains what each platform requires from the SMS, how to write one message that serves all three, how WhatsApp authentication templates handle autofill, and what usually breaks.

How does OTP autofill work on each platform?

Each platform reads the SMS differently, so there is no single switch. Android needs either an app hash or the user's consent, iOS detects codes itself, and browsers read a formatted last line.

Platform Mechanism What the SMS needs User action
Android app SMS Retriever API At most 140 bytes, a one-time code and an 11-character app hash None
Android app SMS User Consent API A message containing a one-time code Approves sharing one message
iOS and iPadOS app Security code AutoFill A code iOS can detect; domain-bound format recommended Taps the suggestion
Web (Safari) autocomplete="one-time-code" Domain-bound last line Taps the suggestion
Web (Chrome on Android) WebOTP API Last line @host #code Confirms the prompt

All of them only help if your SMS arrives. If it does not, see OTP not received.

How does the Android SMS Retriever API work?

Your app asks Google Play services to listen for one message, and the message carries a hash that identifies your app. No extra app permission is needed.

Google's documentation lists three requirements for the verification message. It must:

  1. be no longer than 140 bytes;
  2. contain a one-time code that your app sends back to your server to finish verification;
  3. include an 11-character hash string that identifies your app.

Everything else in the message is up to you. The flow is: the app starts the API while it requests the SMS, your server sends the message, Play services directs it to your app, and the app extracts the code and returns it to your server for validation. Your server should also check that the code is valid and has not expired.

How do I get the 11-character hash?

Google says the hash is the first 11 characters of a base64-encoded hash. It is computed from your app's signing certificate, using the keytool steps in Google's documentation. If Google Play manages your app signing, import that certificate for the calculation. If you sign directly, use your production keystore and certificate alias.

Because the hash depends on the signing certificate, a build signed with a different key will have a different hash. Plan for that when you test debug builds against production messages, and confirm the hash for each signing setup you ship.

What if I would rather not manage a hash?

Use the SMS User Consent API. Google's documentation says the user is asked before a message is shared, and the whole message then becomes available to your app, which parses out the code. Per Google's page, the message must contain a 4-10 character alphanumeric string with at least one number, the listener covers five minutes, and you may optionally restrict to a sender. Start the API before the SMS arrives: messages received earlier are not forwarded.

How does iOS and iPadOS autofill a code?

iOS detects an SMS that contains a one-time code and offers it in fields that declare themselves as code fields. Apple documents two steps for you.

  1. Set the field's content type. In UIKit, set textContentType to .oneTimeCode. In SwiftUI, use the textContentType modifier with the same value. For a website, set autocomplete="one-time-code" on the input.
  2. Optionally bind the code to your domain. From iOS 14 and macOS Big Sur, Apple lets you format the SMS so AutoFill suggests the code only when the Safari site or the app's associated domain matches. This makes it harder to trick someone into entering a code on a phishing site.

The format, as Apple documents it: any text first, then a last line with your domain prefixed by @, a space, and the code prefixed by #.

Your Example code is 123456.

@example.com #123456

For an app, add the bound domain to the app's associated domains, or AutoFill will not suggest it. Standard codes without the binding are still detected, but Apple recommends the domain-bound format.

How does OTP autofill work on the web?

Add the attribute first, then the JavaScript for browsers that support it.

<input type="text" inputmode="numeric"
       autocomplete="one-time-code" name="otp" />

Chrome's documentation recommends the attribute for cross-browser compatibility, and says Safari uses it to suggest the code from a correctly formatted SMS even though it lacks the WebOTP JavaScript API.

For Chrome on Android, call WebOTP with the sms transport. The API needs a secure origin (HTTPS), and an AbortController lets you cancel the pending request if the user types the code by hand.

if ("OTPCredential" in window) {
  const ac = new AbortController();
  const form = document.querySelector("form");
  const input = document.querySelector("input[autocomplete=one-time-code]");

  form.addEventListener("submit", () => ac.abort());

  navigator.credentials
    .get({ otp: { transport: ["sms"] }, signal: ac.signal })
    .then((otp) => {
      input.value = otp.code;
      form.submit();
    })
    .catch(() => {}); // user dismissed, or no matching SMS
}

The SMS must follow the origin-bound format in the WICG specification and Chrome's guide: a code of four to ten alphanumeric characters with at least one number before the last line, then a last line that starts with @, the host, a single space and # plus the code. The host must contain no whitespace.

Can one SMS work on Android, iOS and the web?

Often yes, because the rules are compatible. Put the human-readable text first, the Android hash on a line above the end, and the domain-bound line last. This layout is built from the documented rules, not a vendor guarantee, so test it.

Your Example code is 483921. Do not share it.
Ab3dE+9xYz1
@example.com #483921

Why this order: Apple and the web specification both want the domain and code on the last line. Google requires the 11-character hash somewhere in the message but, in the page we checked, does not fix its position. The example above is 78 bytes in plain Latin characters, comfortably inside the 140-byte limit.

Checklist before you ship:

  1. Code is a single token, clearly separated from other digits in the text.
  2. Domain on the last line matches the site and the app's associated domain.
  3. Total length is within 140 bytes for the Retriever variant.
  4. Hash matches the signing key of the build under test.
  5. Tested on a handset from each platform, on the operators you actually send through.

What about Arabic and Unicode text?

Keep the code and the two machine-readable parts plain. Google's limit is in bytes, and a Unicode SMS fits far fewer characters than a GSM-7 one: 70 against 160 in a single segment (see SMS character limits and Unicode). An Arabic sentence therefore consumes the budget quickly, and one non-GSM character switches the whole message to Unicode.

Safe approach: keep the code in Latin digits, keep @domain #code in ASCII, and keep the Arabic wording short. We found no official documentation describing autofill of codes written in other digit systems, so we leave that out and recommend testing before use.

How do WhatsApp authentication templates handle autofill?

Meta's authentication templates come with three button styles, each with a fixed body text of the form <code> is your verification code.

Button What it does Notes
Copy code User taps to copy the code, then pastes it into your app Works without app changes
One-tap autofill On Android, a button opens your app and delivers the code Needs package_name and signature_hash in supported_apps
Zero-tap No button; the WhatsApp client broadcasts the code to your app Android only; you must accept Meta's zero-tap terms

Meta's documentation also notes that on iOS 26 and later, keyboard suggestions provide autofill with no integration, and that users on platforms without zero-tap receive a copy-code button. You can set an expiry with code_expiration_minutes, and the preview API accepts values from 1 to 90 minutes. Compare the channels in WhatsApp OTP vs SMS OTP.

Worked example: a ride-hailing app login

"Qafila Rides" (synthetic) has an Android app, an iOS app and a mobile site at qafila.example.

  1. The Android app starts the SMS Retriever when the user taps "Send code".
  2. The backend generates a six-digit code, valid for a few minutes, and sends the three-line message above through the SMS API.
  3. Android hands the message to the app. iOS offers the code above the keyboard. On the mobile site, Safari suggests it and Chrome fills it via WebOTP.
  4. The server validates the code once, rejects reuse and expired codes, and rate-limits requests.
  5. If the SMS is not confirmed within a short timeout, the backend offers a WhatsApp authentication template as a second path.

What are the common mistakes?

  • Code not on its own. Extra numbers (phone, price, date) beside the code confuse detection. Use one code per message.
  • Wrong hash. The hash follows the signing certificate. A hash from a debug key will not match a production build.
  • Domain mismatch. @example.com must match the site or associated domain, otherwise iOS and WebOTP will not offer the code.
  • Broken last line. Anything after #code, a missing space, or whitespace inside the domain breaks the format.
  • Message too long or Unicode-heavy, which exceeds the 140-byte Retriever limit.
  • Listening too late. The User Consent API ignores messages received before it started.
  • Using autofill as security. Autofill is convenience. Still validate server-side, expire codes and limit attempts, and watch for SMS pumping fraud.
  • Scope. This guide covers documented behaviour at the time of writing. Platform rules change, so check the linked sources before release.

For message wording and expiry choices, see our OTP and authentication messages guide.

How OmniChannels.ai helps

OmniChannels.ai sends OTP through SMS and WhatsApp over BroadNet's direct operator connectivity, so you can control the exact message text your autofill format depends on. WhatsApp templates cover copy-code, one-tap and zero-tap authentication. Channel fallback, for example WhatsApp then SMS, is configured per use case, and delivery statuses arrive in one webhook format. See the SMS API docs, the WhatsApp API docs and the OTP solution.

Frequently asked questions

What is the SMS Retriever API message format?

Google's documentation says the message must be no longer than 140 bytes, contain a one-time code that your app sends back to your server, and include an 11-character hash that identifies your app. Beyond that, the contents are your choice. The hash lets Google Play services route the message to the right app without extra permissions.

Do I need an app hash for the SMS User Consent API?

Google's overview of the SMS User Consent API does not describe an app hash. Instead, the user sees a prompt and chooses whether to share the message with your app. Your app must start listening before the SMS arrives, and it then extracts the code from the message text itself.

What is a domain-bound SMS code on iOS?

It is an SMS whose last line binds the code to your domain: the domain prefixed with @, a space, and the code prefixed with #. Apple documents it for iOS 14 and later. AutoFill suggests the code only when the website in Safari, or an associated domain of the app, matches the domain in the message.

How does WebOTP read the SMS?

The page calls navigator.credentials.get with an otp option and the sms transport. When a message arrives whose last line is @host #code and the host matches the page origin, the browser asks the user to confirm and returns the code. It needs HTTPS, and Chrome on Android is the documented browser.

Why does my OTP not autofill?

The usual causes are a code that is not clearly identifiable, a wrong or missing app hash on Android, a last line that does not follow the @domain #code format, a domain that does not match the page, or text that pushes the message over the byte limit. Check each against the format rules, then test with a real handset.

Does WhatsApp support OTP autofill?

Yes. Meta's authentication templates support a copy-code button, a one-tap autofill button and a zero-tap option. One-tap and zero-tap need your Android package name and signing hash. Zero-tap is supported only on Android, where users on other platforms receive a copy-code button instead.

Sources

  1. SMS Retriever API: perform SMS verification — Google for Developers
  2. SMS User Consent API: request a one-time code — Google for Developers
  3. Enabling AutoFill for domain-bound SMS codes — Apple Developer
  4. Verify phone numbers on the web with the WebOTP API — Chrome for Developers
  5. WebOTP API — WICG specification
  6. Authentication templates — WhatsApp Business Platform, Meta for Developers