# One-tap autofill authentication templates | Developer Documentation

<div id="bkmrk-one-tap-autofill-aut">## One-tap autofill authentication templates

<div><span>Updated: Feb 6, 2026</span></div><div><div>**Upcoming deprecation:** Starting **April 15, 2026**, the `PendingIntent`-based handshake method for authentication templates will be deprecated. If you are currently using `PendingIntent` to initiate handshakes or verify app identity, the [OTP Android SDK](/books/meta-whatsapp/page/zero-tap-authentication-templates-developer-documentation) is the preferred way to migrate.</div></div><div>One-tap autofill authentication templates allow you to send a one-time password or code along with an one-tap autofill button to your users. When a WhatsApp user taps the autofill button, the WhatsApp client triggers an activity which opens your app and delivers it the password or code.</div><div><div>![](https://support2.chatarchitect.com/uploads/images/gallery/2026-04/embedded-image-jpwqx5h3.png)</div></div><div>One-tap autofill button authentication templates consist of:</div><span>Preset text: *&lt;VERIFICATION\_CODE&gt; is your verification code.*</span><span>An optional security disclaimer: *For your security, do not share this code.*</span><span>An optional expiration warning (optional): *This code expires in &lt;NUM\_MINUTES&gt; minutes.*</span><span>A one-tap autofill button.</span><div>**Note**: The OTP Android SDK features a simplified workflow for implementing one-tap and zero-tap authentication templates. You can learn how to use it below.</div><div id="bkmrk-limitations"><div id="bkmrk-limitations-1"><div>### Limitations

</div></div></div><div>One-tap autofill buttons are only supported on Android. If you send an authentication template to a WhatsApp user who is using a non-Android device, the WhatsApp client will display a copy code button instead.</div><div>URLs, media, and emojis are not supported.</div><div id="bkmrk-app-signing-key-hash"><div id="bkmrk-app-signing-key-hash-1"><div>### App signing key hash

</div></div></div><div>You must include your app signing key hash in your post body.</div><div>To calculate your hash, follow Google’s instructions for [computing your app’s hash string<span>⁠</span>](https://l.facebook.com/l.php?u=https%3A%2F%2Fdevelopers.google.com%2Fidentity%2Fsms-retriever%2Fverify%23computing_your_apps_hash_string&h=AT6nbQ7OdYH3kxxs9CPLkbMFkYJdmIlnOVCV5-cSWm9YOPWX3wj8IaCbUQZ3PCvPJL58yRVUoc31Rq8zLrppd4YbzVcqH55nVkPlZx-pER5EHim51KZ9h-yFe3NvmGzhr8kOekdIiMptgwbNBo1BKw).</div><div>Alternatively, if you follow Google’s instructions and download your app signing key certificate (step 1), you can use your certificate with the [sms\_retriever\_hash\_v9.sh<span>⁠</span>](https://l.facebook.com/l.php?u=http%3A%2F%2Ftinyurl.com%2F43bkdrdt&h=AT6nbQ7OdYH3kxxs9CPLkbMFkYJdmIlnOVCV5-cSWm9YOPWX3wj8IaCbUQZ3PCvPJL58yRVUoc31Rq8zLrppd4YbzVcqH55nVkPlZx-pER5EHim51KZ9h-yFe3NvmGzhr8kOekdIiMptgwbNBo1BKw) shell script to compute the hash. For example:</div><div>```
<span>.</span><span>/sms_retriever_hash_v9.sh --package "com.example.myapplication" --keystore ~/</span><span>.</span><span>android</span><span>/</span><span>debug</span><span>.</span><span>keystore</span>
```

</div><div id="bkmrk-supported-apps"><div id="bkmrk-supported-apps-1"><div>### Supported apps

</div></div></div><div>The `supported_apps` array allows you define pairs of app package names and signing key hashes for up to 5 apps. This can be useful if you have different app builds and want each of them to be able to initiate the handshake:</div><div>Alternatively, if you are using Graph API version 20.0 or older and have only a single app, you can define the app’s package name and signing key hash as `buttons` object properties, but this is not recommended as we will stop supporting this method starting with version 21.0:</div><div id="bkmrk-handshake"><div id="bkmrk-handshake-1"><div>### Handshake

</div></div></div><div>You must signal to the WhatsApp client to expect imminent delivery of a password or code. You can do this by initiating a “handshake”.</div><div>A handshake is an Android intent and public class that you implement but that the WhatsApp client can start.</div><div>When a user in your app requests a one-time password or verification code and chooses for it to be delivered to their WhatsApp number, first perform the handshake, then call our API to send the authentication template message. When the WhatsApp client receives the message, it will perform an eligibility check, and if there are no errors, start the intent and display the message to the user. Finally, when the user taps the message’s one-tap autofill button, we automatically load your app and pass it the password or code.</div><div><div>![](https://support2.chatarchitect.com/uploads/images/gallery/2026-04/embedded-image-hawyahss.png)</div></div><div>If you do not perform a handshake before sending the message, or the message fails an eligibility check, the delivered message will display a copy code button instead of a one-tap button.</div><div id="bkmrk-eligibility-check"><div id="bkmrk-eligibility-check-1"><div>#### Eligibility check

</div></div></div><div>The WhatsApp client performs the following checks when it receives an authentication template message. If any check fails, the one-tap autofill button will be replaced with a copy code button.</div><span>The handshake was initiated no more than 10 minutes ago (or no more than the number of minutes indicated by the template’s `code_expiration_minutes` property, if present).</span><span>The package name in the message (defined in the `package_name` property in the `components` array upon template creation) matches the package name set on the intent. The match is determined through the `getCreatorPackage` method called in the `PendingIntent` object provided by your application.</span><span>None of the other apps that you included in the template’s list of `supported_apps` initiated a handshake in the last 10 minutes (or the number of minutes indicated by the template’s `code_expiration_minutes` property, if present).</span><span>The app signing key hash in the message (defined in the `signature_hash` property in the components array upon template creation) matches your installed app’s signing key hash.</span><span>The message includes the one-tap autofill button text.</span><span>Your app has defined an activity to receive the password or code.</span><div id="bkmrk-android-notification"><div id="bkmrk-android-notification-1"><div>#### Android notifications

</div></div></div><div>Android notifications indicating receipt of a WhatsApp authentication template message will only appear on the user’s Android device if:</div><span>The user is logged into the WhatsApp app or WhatsApp Business app with the phone number (account) that the message was sent to.</span><span>The user is logged into your app.</span><span>Android OS is KitKat (4.4, API 19) or above.</span><span>**Show notifications** is enabled (**Settings** &gt; **Notifications**) in the WhatsApp app or WhatsApp Business app.</span><span>Device level notification is enabled for the WhatsApp app or WhatsApp Business app.</span><span>Prior message threads in the WhatsApp app or WhatsApp Business app between the user and your business are not muted.</span><div id="bkmrk-using-the-sdk"><div id="bkmrk-using-the-sdk-1"><div>#### Using the SDK

</div></div></div><div>The OTP Android SDK can be used to perform handshakes, as well as other functions in both one-tap and zero-tap authentication templates.</div><div>To access SDK functionality, add the following configuration to your Gradle file:</div><div>```
<span>dependencies </span><span>{</span><span>…</span><span>
    implementation </span><span>'com.whatsapp.otp:whatsapp-otp-android-sdk:1.0.0'</span><span>…</span><span>}</span>
```

</div><div>To your repositories, add `mavenCentral()`:</div><div>```
<span>repositories </span><span>{</span><span>…</span><span>
    mavenCentral</span><span>()</span><span>…</span><span>}</span>
```

</div><div id="bkmrk-activity"><div id="bkmrk-activity-1"><div>#### Activity

</div></div></div><div>Declare an activity and intent filter that can receive the one-time password or code. The intent filter must have the action name `com.whatsapp.otp.OTP_RETRIEVED`.</div><div>```
<span><activity</span><span>android:name</span><span>=</span><span>".ReceiveCodeActivity"</span><span>android:enabled</span><span>=</span><span>"true"</span><span>android:exported</span><span>=</span><span>"true"</span><span>android:launchMode</span><span>=</span><span>"standard"</span><span>></span><span><intent-filter></span><span><action</span><span>android:name</span><span>=</span><span>"com.whatsapp.otp.OTP_RETRIEVED"</span><span>/></span><span></intent-filter></span><span></activity></span>
```

</div><div>This is the activity that the WhatsApp app or WhatsApp Business app will start once the authentication template message is received and it passes all [eligibility checks](#bkmrk-eligibility-check-1).</div><div id="bkmrk-activity-class"><div id="bkmrk-activity-class-1"><div>#### Activity class

</div></div></div><div id="bkmrk-using-the-sdk-%28prefe"><div id="bkmrk-using-the-sdk-%28prefe-1"><div>##### Using the SDK (Preferred)

</div></div></div><div>Define the activity public class and instantiate a `WhatsAppOtpIncomingIntentHandler` object to handle the intent. The `.processOtpCode()` method validates the handshake ID against the expected value you stored during handshake initiation and handles errors.</div><div>```
<span>public</span><span>class</span><span>ReceiveCodeActivity</span><span>extends</span><span>AppCompatActivity</span><span>{</span><span>@Override</span><span>protected</span><span>void</span><span> onCreate</span><span>(</span><span>Bundle</span><span> savedInstanceState</span><span>)</span><span>{</span><span>super</span><span>.</span><span>onCreate</span><span>(</span><span>savedInstanceState</span><span>);</span><span>WhatsAppOtpIncomingIntentHandler</span><span> incomingIntentHandler </span><span>=</span><span>new</span><span>WhatsAppOtpIncomingIntentHandler</span><span>();</span><span>// Retrieve the expected handshake ID that was stored during handshake initiation</span><span>String</span><span> expectedHandshakeId </span><span>=</span><span> retrieveStoredHandshakeId</span><span>();</span><span>

          incomingIntentHandler</span><span>.</span><span>processOtpCode</span><span>(</span><span>
                                 getIntent</span><span>(),</span><span>
                                 expectedHandshakeId</span><span>,</span><span>(</span><span>code</span><span>)</span><span>-></span><span>{</span><span>// The handshake ID has been validated by the SDK</span><span>
                                   validateCode</span><span>(</span><span>code</span><span>);</span><span>},</span><span>// call your function to handle errors</span><span>(</span><span>error</span><span>,</span><span> exception</span><span>)</span><span>-></span><span> handleError</span><span>(</span><span>error</span><span>,</span><span> exception</span><span>));</span><span>}</span>
```

</div><div id="bkmrk-without-the-sdk"><div id="bkmrk-without-the-sdk-1"><div>##### Without the SDK

</div></div></div><div>Define the activity public class that can accept the code once it has been passed to your app. The activity should validate the `request_id` (handshake ID) to ensure the OTP code is coming from a legitimate handshake initiated by your app.</div><div>```
<span>public</span><span>class</span><span>ReceiveCodeActivity</span><span>extends</span><span>AppCompatActivity</span><span>{</span><span>@Override</span><span>protected</span><span>void</span><span> onCreate</span><span>(</span><span>Bundle</span><span> savedInstanceState</span><span>)</span><span>{</span><span>super</span><span>.</span><span>onCreate</span><span>(</span><span>savedInstanceState</span><span>);</span><span>Intent</span><span> intent </span><span>=</span><span> getIntent</span><span>();</span><span>// Extract the handshake ID from the intent</span><span>String</span><span> incomingRequestId </span><span>=</span><span> intent</span><span>.</span><span>getStringExtra</span><span>(</span><span>"request_id"</span><span>);</span><span>// Retrieve the previously stored handshake ID</span><span>String</span><span> storedRequestId </span><span>=</span><span> retrieveStoredRequestId</span><span>();</span><span>// Validate the handshake ID matches</span><span>if</span><span>(</span><span>storedRequestId </span><span>!=</span><span>null</span><span>&&</span><span> storedRequestId</span><span>.</span><span>equals</span><span>(</span><span>incomingRequestId</span><span>))</span><span>{</span><span>// use OTP code</span><span>String</span><span> otpCode </span><span>=</span><span> intent</span><span>.</span><span>getStringExtra</span><span>(</span><span>"code"</span><span>);</span><span>// ...</span><span>}</span><span>}</span><span>}</span>
```

</div><div id="bkmrk-initiating-the-hands"><div id="bkmrk-initiating-the-hands-1"><div>#### Initiating the handshake

</div></div></div><div id="bkmrk-using-the-sdk-%28prefe-3"><div id="bkmrk-using-the-sdk-%28prefe-4"><div>##### Using the SDK (Preferred)

</div></div></div><div>Performing a handshake can be done by instantiating the `WhatsAppOtpHandler` object and passing in your context to the `.sendOtpIntentToWhatsApp()` method. The method returns a UUID (handshake ID) that must be stored and used to validate the incoming OTP code later:</div><div>```
<span>WhatsAppOtpHandler</span><span> whatsAppOtpHandler </span><span>=</span><span>new</span><span>WhatsAppOtpHandler</span><span>();</span><span>
UUID handshakeId </span><span>=</span><span> whatsAppOtpHandler</span><span>.</span><span>sendOtpIntentToWhatsApp</span><span>(</span><span>context</span><span>);</span><span>// Store handshakeId to validate the received OTP code later</span>
```

</div><div id="bkmrk-without-the-sdk-3"><div id="bkmrk-without-the-sdk-4"><div>##### Without the SDK

</div></div></div><div>This example demonstrates one way to initiate a handshake with the WhatsApp client. The handshake includes a `request_id` (UUID) that must be stored and validated when receiving the OTP code.</div><div>```
<span>private</span><span>String</span><span> currentRequestId</span><span>;</span><span>public</span><span>void</span><span> sendOtpIntentToWhatsApp</span><span>()</span><span>{</span><span>// Generate a unique handshake ID</span><span>
   currentRequestId </span><span>=</span><span> UUID</span><span>.</span><span>randomUUID</span><span>().</span><span>toString</span><span>();</span><span>// Store this ID for later validation when receiving the OTP</span><span>
   storeRequestId</span><span>(</span><span>currentRequestId</span><span>);</span><span>// Send OTP_REQUESTED intent to both WA and WA Business App</span><span>
   sendOtpIntentToWhatsApp</span><span>(</span><span>"com.whatsapp"</span><span>,</span><span> currentRequestId</span><span>);</span><span>
   sendOtpIntentToWhatsApp</span><span>(</span><span>"com.whatsapp.w4b"</span><span>,</span><span> currentRequestId</span><span>);</span><span>}</span><span>private</span><span>void</span><span> sendOtpIntentToWhatsApp</span><span>(</span><span>String</span><span> packageName</span><span>,</span><span>String</span><span> requestId</span><span>)</span><span>{</span><span>/**
  * Starting with Build.VERSION_CODES.S, it will be required to explicitly
  * specify the mutability of  PendingIntents on creation with either
  * (@link #FLAG_IMMUTABLE} or FLAG_MUTABLE
  */</span><span>int</span><span> flags </span><span>=</span><span>Build</span><span>.</span><span>VERSION</span><span>.</span><span>SDK_INT </span><span>>=</span><span>Build</span><span>.</span><span>VERSION_CODES</span><span>.</span><span>S </span><span>?</span><span> FLAG_IMMUTABLE </span><span>:</span><span>0</span><span>;</span><span>PendingIntent</span><span> pi </span><span>=</span><span>PendingIntent</span><span>.</span><span>getActivity</span><span>(</span><span>
      getApplicationContext</span><span>(),</span><span>0</span><span>,</span><span>new</span><span>Intent</span><span>(),</span><span>
      flags</span><span>);</span><span>// Send OTP_REQUESTED intent to WhatsApp</span><span>Intent</span><span> intentToWhatsApp </span><span>=</span><span>new</span><span>Intent</span><span>();</span><span>
  intentToWhatsApp</span><span>.</span><span>setPackage</span><span>(</span><span>packageName</span><span>);</span><span>
  intentToWhatsApp</span><span>.</span><span>setAction</span><span>(</span><span>"com.whatsapp.otp.OTP_REQUESTED"</span><span>);</span><span>// WA will use this to verify the identity of the caller app.</span><span>Bundle</span><span> extras </span><span>=</span><span> intentToWhatsApp</span><span>.</span><span>getExtras</span><span>();</span><span>if</span><span>(</span><span>extras </span><span>==</span><span>null</span><span>)</span><span>{</span><span>
     extras </span><span>=</span><span>new</span><span>Bundle</span><span>();</span><span>}</span><span>
  extras</span><span>.</span><span>putParcelable</span><span>(</span><span>"_ci_"</span><span>,</span><span> pi</span><span>);</span><span>// Add the handshake ID for secure validation</span><span>
  intentToWhatsApp</span><span>.</span><span>putExtra</span><span>(</span><span>"request_id"</span><span>,</span><span> requestId</span><span>);</span><span>
  intentToWhatsApp</span><span>.</span><span>putExtras</span><span>(</span><span>extras</span><span>);</span><span>
  getApplicationContext</span><span>().</span><span>sendBroadcast</span><span>(</span><span>intentToWhatsApp</span><span>);</span><span>}</span>
```

</div><div id="bkmrk-checking-if-whatsapp"><div id="bkmrk-checking-if-whatsapp-1"><div>#### Checking if WhatsApp is installed on Android

</div></div></div><div>You can check WhatsApp installation before offering WhatsApp as an option if you expect both WhatsApp and your app to be on the same device.</div><div>First, you need to add the following to your `AndroidManifest.xml` file:</div><div><div>```
<span><span><</span><span>queries</span><span>></span></span><br></br><span><span><</span><span>package</span><span>android:name</span><span>=</span><span>"com.whatsapp"</span><span>/</span><span>></span></span><br></br><span><span><</span><span>package</span><span>android:name</span><span>=</span><span>"com.whatsapp.w4b"</span><span>/</span><span>></span></span><br></br><span><span></</span><span>queries</span><span>></span></span><br></br>
```

</div></div><div id="bkmrk-using-the-sdk-%28prefe-6"><div id="bkmrk-using-the-sdk-%28prefe-7"><div>##### Using the SDK (Preferred)

</div></div></div><div>Instantiate the `WhatsAppOtpHandler` object:</div><div>```
<span>WhatsAppOtpHandler</span><span> whatsAppOtpHandler </span><span>=</span><span>new</span><span>WhatsAppOtpHandler</span><span>();</span>
```

</div><div>Check if the WhatsApp client is installed by passing the `isWhatsAppInstalled` method as the clause in an `If` statement:</div><div>```
<span>If</span><span>(</span><span>whatsAppOtpHandler</span><span>.</span><span>isWhatsAppInstalled</span><span>(</span><span>context</span><span>))</span><span>{</span><span>// ... do something</span><span>}</span>
```

</div><div id="bkmrk-without-the-sdk-6"><div id="bkmrk-without-the-sdk-7"><div>##### Without the SDK

</div></div></div><div>```
<span>if</span><span>(</span><span>this</span><span>.</span><span>isWhatsAppInstalled</span><span>(</span><span>context</span><span>))</span><span>{</span><span>// ... do something</span><span>}</span><span>public</span><span>boolean</span><span> isWhatsAppInstalled</span><span>(</span><span>final</span><span>@NonNull</span><span>Context</span><span> context</span><span>){</span><span>return</span><span> isWhatsAppInstalled</span><span>(</span><span>context</span><span>,</span><span>"com.whatsapp"</span><span>)</span><span>||</span><span>
           isWhatsAppInstalled</span><span>(</span><span>context</span><span>,</span><span>"com.whatsapp.w4b"</span><span>);</span><span>}</span><span>public</span><span>boolean</span><span> isWhatsAppInstalled</span><span>(</span><span>final</span><span>@NonNull</span><span>Context</span><span> context</span><span>,</span><span>final</span><span>@NonNull</span><span>String</span><span> type</span><span>){</span><span>final</span><span>Intent</span><span> intent </span><span>=</span><span>new</span><span>Intent</span><span>();</span><span>
    intent</span><span>.</span><span>setPackage</span><span>(</span><span>type</span><span>);</span><span>
    intent</span><span>.</span><span>setAction</span><span>(</span><span>"com.whatsapp.otp.OTP_REQUESTED"</span><span>);</span><span>PackageManager</span><span> packageManager </span><span>=</span><span> context</span><span>.</span><span>getPackageManager</span><span>();</span><span>List</span><span><</span><span>ResolveInfo</span><span>></span><span> receivers </span><span>=</span><span> packageManager</span><span>.</span><span>queryBroadcastReceivers</span><span>(</span><span>intent</span><span>,</span><span>0</span><span>);</span><span>return</span><span>!</span><span>receivers</span><span>.</span><span>isEmpty</span><span>();</span><span>}</span><span>}</span>
```

</div><div id="bkmrk-checking-if-whatsapp-3"><div id="bkmrk-checking-if-whatsapp-4"><div>#### Checking if WhatsApp is installed on iOS

</div></div></div><div>Use the following code in your iOS application to check if WhatsApp is installed</div><div>```
<span>let</span><span> schemeURL </span><span>=</span><span> URL</span><span>(</span><span>string</span><span>:</span><span>"whatsapp://otp"</span><span>)!</span><span>let</span><span> isWhatsAppInstalled </span><span>=</span><span>UIApplication</span><span>.</span><span>shared</span><span>.</span><span>canOpenURL</span><span>(</span><span>schemeURL</span><span>)</span>
```

</div><div id="bkmrk-error-signals"><div id="bkmrk-error-signals-1"><div>#### Error signals

</div></div></div><div>See [Error Signals](/books/meta-whatsapp/page/error-signals-developer-documentation) that can help with debugging.</div><div id="bkmrk-handshake-id-error-c"><div id="bkmrk-handshake-id-error-c-1"><div>#### Handshake ID error codes

</div></div></div><div>The following error codes may be returned when using the SDK with handshake ID validation:</div><div><table><thead><tr><th><span>Error Code</span></th><th><span>Description</span></th></tr></thead><tbody><tr><td><div>`HANDSHAKE_ID_MISSING`</div></td><td><div>The handshake ID was not included in the intent from WhatsApp</div></td></tr><tr><td><div>`HANDSHAKE_ID_INVALID_FORMAT`</div></td><td><div>The handshake ID is not a valid UUID format</div></td></tr><tr><td><div>`HANDSHAKE_ID_MISMATCH`</div></td><td><div>The handshake ID in the intent does not match the expected value</div></td></tr></tbody></table>

</div><div id="bkmrk-sample-app"><div id="bkmrk-sample-app-1"><div>#### Sample app

</div></div></div><div>See our [WhatsApp One-Time Password (OTP) Sample App<span>⁠</span>](https://l.facebook.com/l.php?u=https%3A%2F%2Fgithub.com%2FWhatsApp%2FWhatsApp-OTP-Sample-App&h=AT6nbQ7OdYH3kxxs9CPLkbMFkYJdmIlnOVCV5-cSWm9YOPWX3wj8IaCbUQZ3PCvPJL58yRVUoc31Rq8zLrppd4YbzVcqH55nVkPlZx-pER5EHim51KZ9h-yFe3NvmGzhr8kOekdIiMptgwbNBo1BKw) for Android on Github. The sample app demonstrates how to send and receive OTP passwords and codes via the API, how to integrate the one-tap autofill and copy code buttons, how to create a template, and how to spin up a sample server.</div></div>