<input autocomplete="one-time-code"> fields. Whether any of those affordances fires is decided by two things you own: the shape of your message template and the autocomplete hints on your input field. Orbit Verify generates the code and delivers the message exactly as your template renders it; nothing is appended or reformatted on your behalf. This guide shows the body shape each platform reads, the client-side wiring for web, iOS, and Android, and how to prove on a real device that the suggestion appeared.
If you have not wired the send/check loop yet, run Verify in 30 minutes first; this guide starts where it ends, with a working /send and /check.
1. Why autofill lifts OTP conversion
Three OS-level affordances read your SMS body:
All three consume the same rendered components: the code digits and the single-line suffixes described next. Autofill removes the app-switch, read, and retype loop, which is where wrong-digit entries and abandoned verifications concentrate. A user who taps a suggestion submits the exact code seconds after delivery; a user who retypes it is your
VALIDATION_ERROR and EXPIRED_TOKEN traffic.
2. Template the message body for autofill
Your SMS template lives under Verify → Configuration → Templates (walkthrough in Verify configuration console). Whatever you write there is what the device receives verbatim, so the template has to carry the platform suffixes itself. Three rules make one body satisfy every OS:- The code is the last digit run in the body. Platform scanners pick the final numeric run that looks like a code. Any number after it (an order id, a year, a support line) becomes the suggested “code” instead.
- End the body with an
@your-domainline. iOS uses it to bind the suggestion to your domain for web-to-app handoff, and Chrome’s Web OTP API uses it to offer the code to your focused web page. - End the body with your Android app hash line. The SMS Retriever delivers a message to your app only when the last line is that app’s 11-character hash (computation in section 5).
- Line 1 renders your app name and the 6-digit code.
{{code}}is the last digit run; keep every later line free of digit runs (the hash’s isolated digits never parse as a code). - Line 2 is the domain token. Replace
verify.example.comwith a domain you control and, for the iOS handoff, one you have registered as an associated domain (section 4). - Line 3 is the Android app hash, one per Android app package and signing key. If you ship two Android apps that both consume the code, you cannot put two hashes on one body; use the SMS User Consent API for the second app (section 5).
3. Web
Mark the field and keep it in a form:<input> must sit inside a <form>, and it must be focused when the message arrives (on macOS the code arrives via iPhone message forwarding; on iOS via the SMS itself). Render the form up front and focus the field when the user taps “send code”; a field swapped into the DOM after the send loses the suggestion.
On Chrome for Android you can also pull the code programmatically with the Web OTP API, which reads the @domain line from section 2:
maxlength and pattern permissive of paste, and on a failed check surface attempts_remaining from the 422 response exactly as Verify without our SDK describes. If the chip never appears on a device where it should, work the matrix in section 7 before blaming the template.
4. iOS native
Set the content type on the field that receives the code:@domain line adds the domain-bound form. For the web-to-app handoff (the user starts verification on your mobile site and finishes in your app, or you want the suggestion to show your domain so users trust it), the domain in the SMS must be one your app claims via the Associated Domains capability (applinks:verify.example.com in the Signing & Capabilities tab, backed by the apple-app-site-association file on that domain). With the entitlement and the @domain line both in place, the banner shows the domain and a tap can hand the code from your web page to your app; without either, you get the unbound banner only.
5. Android native
The SMS Retriever API gives your app the matching message with no SMS permission and no user prompt, in exchange for the app-hash line in the body. Start the listener when the code entry screen appears:IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)) while the verification screen is visible and unregister it on destroy. The Retriever watches for 5 minutes per startSmsRetriever() call and only delivers messages whose last line is your app’s hash.
The app hash is 11 characters, derived from your package name and signing certificate: SHA-512 of "<package name> <base64 signing certificate>", base64-encoded, first 11 characters. The same computation over your release key:
SmsRetriever.getClient(this).startSmsUserConsent() delivers the next OTP-shaped message regardless of its suffix, and your receiver launches the intent in EXTRA_CONSENT_INTENT, which shows a system sheet with the full message text the user approves once. It works with any template, at the cost of that one-tap consent prompt. Pick the Retriever when you control the template (the usual Orbit case); pick User Consent for a second Android app that shares one sender, since one body carries one hash.
6. Wire it to Orbit Verify
The whole loop with autofill shapes:- Edit the template under Verify → Configuration → Templates (configuration console guide) to the body from section 2, with your domain and hash.
- Send through the normal SMS factor:
POST /api/v1/verify/sendwithchannel: "sms", exactly as in Verify in 30 minutes.
- Pin a test send and read the rendered body. Send once with your sandbox key (
dv_test_sk_…, magic numbers) and open that send’s delivery log row: the rendered body should show the real code digits plus both suffix lines, untouched. This catches template-engine mistakes (a swallowed@line, a trailing space merging the hash into the previous line) before you spend a live send. - Send one live verification to a test device and confirm the device receives exactly that body. The rendered body a phone receives is your template, verbatim, with
{{code}}replaced:
- Check as usual. The client (web field, iOS banner tap, or Android receiver) extracts
482910; your backend validates it withPOST /api/v1/verify/check. Autofill changes nothing about the check call.
7. Testing matrix
To confirm a fill came from the suggestion rather than manual typing, timestamp the client event where the field goes from empty to complete in a single change (autofill and paste fill in one step; typing does not) and compare it with the delivered timestamp on that send’s delivery log row: an autofill fill lands one or two seconds after delivery, manual entry many seconds later. Sandbox sends are simulated and never reach a real device, so banner behavior is only testable on live sends; use the sandbox strictly for the rendered-body check above.
Next steps
- Verify in 30 minutes — the send/check loop this guide builds on.
- Verify factor suite — TOTP, push, and passkey factors alongside SMS.
- SMS channel — sender IDs, segments, and delivery semantics for the SMS factor.
- Verification lifecycle — statuses a verification moves through from send to approval.