Keep your personal number private
Your real phone number never touches Hashkey. Use a virtual number for full privacy.
The SMS verification hashkey is conceptually simple – just an 11-character string – but it routinely generates debugging sessions that devour an afternoon. This guide is for developers and QA engineers who want working OTP verification without spending a week digging through broadcast receiver logs.
Hashkey SMS verification confirms you control a phone number by sending a 6-digit OTP to that number during signup or login. With SMSPin you receive that code on a temporary virtual number online — no physical SIM card needed and your production workflows stay separate.
No paperwork, no carrier hassle — a real number ready to receive your Hashkey OTP code right now.
Your real phone number never touches Hashkey. Use a virtual number for full privacy.
Hashkey sends the SMS immediately. Your inbox refreshes in real time — no delays.
US, UK, Germany, India, Brazil, and more. Real, carrier-registered numbers.
Everything happens online. No monthly subscription to buy, no roaming, no second phone.
If the OTP never arrives in 20 minutes, your credits return automatically.
Top up with USDT, BTC, ETH and more via Cryptomus. No card required.
Four steps — from picking a number to a verified Hashkey account.
Generate the hash from your app's signing certificate — it's always an 11-character, base64-encoded string.
Put the hash in your SMS template in the exact format: <#> OTP 11-char-hash, no extra characters or line breaks.
Call startSmsRetriever() early in your activity's onCreate so Play services starts listening for the broadcast.
Verify the hash in any incoming SMS matches your expected value before extracting the OTP.
If the flow keeps failing, avoid the hashkey entirely: route OTPs to a virtual number from SMSPin and poll codes via API or dashboard.
SMSPin is provided for legitimate privacy and convenience use cases only. Please review Hashkey's terms before use.
Need a specific country code for your Hashkey verification? We've got you covered.
Every SMSPin number is a legitimate, carrier-registered mobile number — not a VoIP range. Hashkey accepts them reliably.
Sign up with email only. Your real number and identity stay private.
The moment Hashkey sends your OTP, it appears in your dashboard — pushed, not polled.
Keep the SMS short: <#> 123456 FA+9qCX9VZs. No branding, no extra sentences — every extra character risks gateway truncation.
Set a 30–60 second timeout with a resend button; cap resend attempts at 2–3 per session.
Don't request READ_SMS permission — the whole point of the Retriever API is avoiding it.
Test on a physical device with updated Play services; emulators without GApps can't run the API.
Option | Best for | Model |
One-time OTP (from $0.01) | Single verification, no code refund | Play only if the code arrives |
Rent a number (1 day–1 month) | Repeated re-verifications, stable profile | Flat rate by duration |
Physical SIM testing | High-volume production test fleets | Hardware + maintenance cost |
Pick a US number or UK number – both keep the standard 10-digit and 11-digit E.164 formats used by most test apps.
Order from one of the 20+ covered countries at SMSPin pricing and send your OTP to that exact number, full international format with country code.
Yes, receiving SMS on a temporary number is legal for personal or business verification in most jurisdictions. However, you must respect the target app’s terms. Never use it for account takeover, security workaround, or abusive behaviour. SMSPin is not affiliated with any app or website. Please follow each app’s terms and local regulations.
Code delays are typically caused by carrier spam filtering, app‑level blocking, or a mismatch between the SMS template and what your app expects. On SMSPin, notifications are routed in real time, and refunds are auto‑processed if no code arrives within the paid window.
A one‑time rental is used for a single verification sign-up and then discarded. A week‑long or monthly rental keeps the number stable, allowing repeated verifications without changing your profile. Use rentals for apps that require periodic SMS reconfirmation.
Do not use temporary numbers for anything requiring true identity ownership, such as bank 2FA or cryptocurrency wallet recovery. Also avoid using them to compromise account security on existing accounts or for any unlawful purpose. Legitimate testing and privacy‑conscious signups are fine.
First, verify the 11‑character hash matches the one generated from your app’s signing certificate. Then confirm your SMS template uses the exact <#> OTP HASH format with no extra characters. If the problem persists, test your OTP flow using an SMSPin number to isolate whether the fault is in your retriever setup or your backend.
It can. SMSPin receives OTPs server‑side and exposes them via API, so your app doesn’t need to intercept SMS messages on‑device at all. This removes the hashkey dependency entirely while still giving you reliable code delivery for testing or production.
Can I use the SMS Retriever API on iOS? No. The Android SMS Retriever API and its hashkey mechanism are Android‑specific. iOS uses its own OTP autofill mechanism, which doesn’t require a hashkey or <#> prefix.
If you've landed here, you have probably stared at an Android SMS Retriever error long enough to question every technical decision that led you here. The SMS verification hashkey is conceptually simple just an 11‑character string but it routinely generates debugging sessions that devour an afternoon. This guide is for developers and QA engineers who want working OTP verification without spending a week digging through broadcast receiver logs. We'll walk through what the hashkey actually does, why it breaks in the wild, how to fix it, and, if you're tired of fighting it, how to test verification flows without the hashkey at all.
An SMS verification hashkey is an 11‑character string derived from your Android app’s signing certificate.
It tells Google Play services which app can receive a specific verification message.
Most “hashkey not working” errors trace back to a mismatch between the hash in your SMS template and the hash your app generates on‑device.
If the hashkey path keeps causing friction, you can avoid it by routing OTPs through a service like SMSPin and polling codes via API.
An SMS verification hashkey is a cryptographic string that Google Play services uses to confirm your Android app is the legitimate recipient of an SMS containing a one‑time passcode.
The SMS Retriever API identifies your app through this hash key. That means verification codes land in your app, not a malicious app that also requested SMS permissions. It’s a guardrail, pure and simple.
Why it’s required:
Android‑only necessity. The hashkey is part of Google’s effort to stop apps from casually requesting broad SMS read permissions or intercepting OTPs meant for someone else.
Hashkey vs. hashcode. The terms are often thrown around interchangeably, but the formal name in the Android API is HashKey.
Where it lives: the hash appears as a base64‑encoded string inside your SMS message body, usually after a colon separator. Play Services validates it on-device.
Not on iOS. The SMS Retriever API and its hashkey mechanism are Android‑specific. iOS uses a different native OTP autofill approach that doesn’t need a hashkey.
If you’re only shipping an iOS app, you can skip the hashkey conversation entirely. This article is aimed at Android developers working with online SMS verification flows.
The hashkey itself is exactly 11 characters long. It comes from your app’s signing certificate, encoded in a modified base64 format.
When you see references to a “13‑character hashkey” in older docs, that usually includes the full SMS string format, not just the hash.
The FA+9qCX9VZs portion 11 characters is the actual hash key. The <#> prefix signals Google Play services to start listening for a retriever message.
Key components:
The 11‑character core. The hash value is always 11 characters after base64 encoding of your signing certificate. No exceptions.
The <#> prefix. This marker is what tells Play services the message contains a retriever hash. Without it, the API ignores the incoming SMS entirely.
The OTP itself. The hash is appended right after the OTP, in the same message body. They have to appear together.
Format matters more than you think. If your SMS template puts the hash on a separate line or inserts extra characters between the <#> and the code, parsing can silently break.
If your client‑side parser accidentally chops the hash, say, to 10 characters, you’ll get a mismatch error even though everything else looks fine. It’s maddening, but it’s also an easy fix once you spot it.
Your SMS verification hashkey does not expire on a timer. It’s tied to your app’s signing certificate, and it stays valid as long as that certificate signs your app.
The practical “expiration” moments happen when:
You switch from a debug keystore to a release keystore.
You rotate your release signing key.
You update your app with a new signing certificate for any reason.
When any of these events occur, the hash key changes. Your old hash key becomes invalid on devices running the new app version.
Renewal steps:
Generate a new hash using the AppSignatureHelper class or your build tooling.
Update every SMS message template that includes the old hash.
Ship the app update with the new signing certificate.
Test the old template against the new app; it should fail, confirming the hash changed.
The most common real‑world mismatch: a developer generates a new hash, updates the app, but forgets to update the SMS template on the server side. The app now expects hash B, but the SMS still carries hash A. The result is a “hashkey mismatch” error that looks inexplicable but has a boring, fixable cause.
The hashkey’s security strength comes from its tie to your app’s signing certificate. It isn’t transferable between apps; a malicious actor can’t reuse your hash key unless they have your signing key.
And that’s also the vulnerability. Anyone with access to your keystore can derive your hash key and build an app that impersonates yours to intercept SMS.
Production security rules:
Protect the keystore. Your .jks or .keystore file is the crown jewel. Store it in a secure, access‑controlled environment. Never commit it to source control.
Don’t reuse hashkeys across unrelated apps. Each app should have its own signing certificate and therefore its own hashkey. Sharing creates unnecessary blast radius.
Don’t log the full SMS body. The SMS message contains the hashkey plus the OTP. Logging message bodies in production leaks secrets that can be used for app impersonation.
Treat the hashkey as an identifier, not an authenticator. The hashkey confirms which app should receive the SMS. It does not authenticate the user. Pair it with the OTP and an expiration window.
Avoid publishing hashkeys in public documentation. A README or blog post that includes your production hashkey is a quick way to leak a security‑relevant artifact.
The OWASP Mobile Application Security Verification Standard (MASVS) offers broader guidance on mobile authentication security, including protecting signing keys and handling OTP flows safely.
Most “hashkey not working” reports aren’t Google‑side failures. They’re template mismatches, network delays, or receiver configuration problems.
Why it fails on Android:
Template drift. The SMS was sent with an old hash, a gateway truncated the hash, or a stray space broke the <#> block.
Receiver ordering. If you have multiple broadcast receivers in your manifest that catch SMS, the one you need may never fire first. Android can throttle or drop competing broadcasts.
Network delays. The SMS arrives late, the user gives up, and the retriever callback never fires in time.
Battery saver or Doze mode. On some devices, background receivers are delayed or suppressed entirely.
No Play services. Devices without Google Play Services (AOSP builds, certain Chinese ROMs, emulators without GApps can’t use the Retriever API at all.
Why iOS doesn’t have this problem:
iOS doesn’t use a hashkey. Apple’s autofill mechanism reads OTP codes from SMS using a different, simpler approach. If you’re porting an Android flow to iOS and hunting for a hashkey, you won’t find one, and you don’t need one.
Optimization here isn’t about making the hashkey itself faster. It’s about reducing the number of things that can go wrong between the SMS send and the OTP appearing in the user’s app.
Optimization checklist:
Keep the SMS short. The ideal format is <#> 123456 FA+9qCX9VZs. No branding, no extra sentences. Every additional character increases the risk that a gateway truncates or reformats the message.
Call startSmsRetriever early. Start the retriever listener in your activity’s onCreate, not after the user clicks a button. The earlier Play services starts listening, the more likely it is to catch the broadcast.
Set a timeout. If the OTP hasn’t arrived in 30–60 seconds, show a “resend code” button. Don’t leave the user staring at a blank screen.
Cap resend attempts. Allow 2–3 resends per session. More than that increases brute‑force risk and annoys users.
Don’t require SMS permissions. The whole point of the Retriever API is to avoid asking for READ_SMS permission. If you’re asking for it anyway, you’ve doubled your work for no benefit.
Parse the hash before extracting the OTP. Confirm the 11‑character hash matches your expected value before pulling out the code. This prevents parsing a random incoming SMS as a verification message.
The official Android SMS Retriever API documentation covers the recommended message format and retriever client setup.
Hash generated against the wrong signing certificate
You generated a debug hash but shipped a release build, or vice versa. The SMS template contains the debug hash, but the release app expects the release hash.
Fix: Generate the hash against the exact APK you’re shipping. Use the same keystore consistently across your build pipeline.
Copy‑paste error in the hash string
An 11‑character string is easy to mangle. You may have dropped a character, added a space, or URL‑decoded something by accident.
Fix: Re‑generate the hash and copy it programmatically, not by hand.
SMS template got reformatted
Some gateways strip the <#> prefix, add line breaks, or concatenate multi‑part SMS in ways that break the hash block.
Fix: Test with multiple gateways. If one consistently breaks the format, switch providers or add a fallback OTP delivery method.
Receiver not registered correctly.
Your BroadcastReceiver may lack the right intent filter for com.google.android.gms.auth.api.phone.SMS_RETRIEVED.
Verify your manifest matches the structure in the official SMS Retriever documentation. Ensure the receiver is exported and has the correct action string.
Testing on an emulator without Play services
The standard Android emulator without Google APIs cannot run the Retriever API.
Fix: Use an emulator image with Google Play services, or test on a physical device.
These are Android SMS Retriever error states, and each one points to a different root issue.
HashkeyInvalid: The hash string couldn’t be parsed. It’s not 11 characters, contains an illegal character, or isn’t properly base64‑encoded. Fix the template.
HashkeyMismatch: The hash in the SMS doesn’t match the hash derived from the app’s signing certificate. Check for a debug/release signature conflict.
HashkeyMissing: The SMS didn’t contain a hash at all, or the <#> prefix was missing. The retriever saw the message but couldn’t identify your app.
HashkeyUnavailable: Play services can’t process the hash. This often means the device doesn’t support the Retriever API, or Play services is missing or outdated.
When you see these errors, here’s what to do:
Re‑generate the hash from the exact APK you’re testing.
Confirm the SMS body uses the <#> OTP HASH format with no extra characters.
Test on a physical device with updated Play services.
If the problem persists on specific devices or ROMs, the hashkey path may not be reliable for your user base, and a polling‑based API approach may be a better fit.
When the hashkey flow fails silently no callback, no broadcast, no error, nothing works- go through this checklist in order.
Confirm you called SmsRetrieverClient.startSmsRetriever() after your activity is created, not before initialization.
Confirm your BroadcastReceiver has an intent filter for com.google.android.gms.auth.api.phone.SMS_RETRIEVED.
Confirm the SMS body starts with <#> followed by a space, then the OTP, then a space, then the 11‑character hash.
Confirm the device isn’t in battery saver mode or Doze, which can suppress broadcasts.
Test on a different physical device. If it works on phone A but not phone B, the problem is device- or carrier-specific, not your hash key.
Check the logs with adb logcat for SMS_RETRIEVED and Play services messages.
Verify your APK signature with apksigner and confirm it matches the hash you generated.
If the flow still fails after all of this, the most productive move may be to stop debugging the hash key and switch to a server‑side OTP polling approach.
SMS simply not arriving? When carriers block OTP delivery or messages get blackholed, SMSPin issues codes from dedicated routes, and you only pay when the OTP actually arrives. Head to SMSPin pricing to see country coverage.
If your goal is to test the verification logic itself, not the Android retriever plumbing, there’s a simpler path.
Use a virtual number service to receive the OTP, then manually enter the code in your app. This avoids the hashkey entirely and lets you test your backend flow, UI, rate limiting, and error handling without chasing broadcast events.
Quick start:
Pick a number from the SMSPin receive SMS availability list.
Request a one‑time order.
Send your OTP to the number.
Read the code from your SMSPin dashboard or API.
Enter the code manually in your test app.
This is especially useful for testing edge cases: malformed messages, delayed OTPs, resend loops, and expiry windows. You can stress your parser and your UX without waiting for carrier delivery or Play services behaviour.
For business users running repeated verification tests, virtual SMS verification infrastructure scales better than maintaining physical SIM cards or fighting hashkey lifecycle issues.
Need a number now to test? Start with an inexpensive number and issue your first test code in minutes. Get test numbers here.
If you’ve read this far because hashkey lifecycle headaches are eating your development time, there’s a robust alternative: remove the client‑side SMS interception layer entirely.
Instead of depending on the Android Retriever API and HashiKey matching, you route OTPs through a backend service that polls for codes via API. The user enters the code manually, as they already do with many services. No hashkey, no broadcast receiver, no Play services dependency.
SMSPin enables this approach directly:
Pay per code. Pricing starts at $0.01 per SMS received, with automatic refunds if no code arrives.
No physical SIMs. Numbers are issued instantly and ready to receive OTPs.
Country coverage. Choose from virtual phone numbers in the USA and the United Kingdom, among 20+ countries.
Rental options. For apps that require re‑verification over time, rent a number for a day, a week, or a month.
API‑first. Poll OTP status programmatically to eliminate manual dashboard checks.
This doesn’t mean you should abandon the Android Retriever API if it’s working for you. But if hashkey errors are consuming your development cycles, a server‑side polling architecture removes the entire problem class.
For production use cases, check SMSPin pricing and compare it against the cost of maintaining physical test SIMs, debugging hashkey mismatches, or losing users to flaky OTP delivery.
Run repeated verification tests without managing SIM cards. Rent a number for 24 hours to 30 days and receive codes on demand. Rent a number.
The SMS verification hashkey is an 11‑character string derived from your Android app’s signing certificate.
It’s Android‑only. iOS doesn’t use a hashkey for OTP verification.
Most hashkey failures are template mismatches, not Google‑side bugs.
The most reliable production setup is to keep your SMS format exactly as documented: <#> OTP HASH, no extras.
If the hashkey path keeps causing friction, a server‑side OTP polling approach using a service like SMSPin removes the entire problem class.
Compliance note: SMSPin.io is not affiliated with any app, website, or third-party platform. Please follow each platform’s terms and local regulations.
Get a virtual number in under 2 minutes. No monthly subscription, no hassle, no privacy compromise.
Last updated September 2, 2026