Authentication templates with one-time password buttons will be available starting May 1, 2023. Starting May 29, 2023, all newly created authentication templates must include a one-time password button.
This functionality will not be available to businesses based in India until later this year.
If your mobile app offers users the option to receive one-time passwords or verification codes via the WhatsApp app or WhatsApp Business app, you must use an authentication template with a one-time password button (OTP) to deliver the password or code.
Authentication templates with OTP buttons consist of the following:
URLs, media, and emojis are not supported. Because authentication templates with OTP buttons only consist of preset text and buttons, their risk of being paused is significantly minimized.
See additional guidelines for when it's appropriate to use an authentication template.
Authentication templates must include either a copy code or one-tap autofill button. Buttons behave differently when tapped by a user:
One-tap buttons are the preferred solution as they offer the best user experience. However, one-tap buttons are currently only supported on Android, require changes to your app's code in order to perform a "handshake", and your app's signing key hash. See Handshake and App Signing Key Hash below.
Use the WhatsApp Business Account > Message Templates endpoint to create authentication templates. Alternatively, you can manually create them using the WhatsApp Manager.
The components
property value must be an array of objects that describes each component that makes up the template. Authentication templates must have the following components:
POST /<WHATSAPP_BUSINESS_ACCOUNT_ID>/message_templates
{ "name": "<NAME>", "language": "<LANGUAGE>", "category": "AUTHENTICATION", "components": [ { "type": "BODY", "add_security_recommendation": <ADD_SECURITY_RECOMMENDATION> // Optional property }, { "type": "FOOTER", "code_expiration_minutes": <CODE_EXPIRATION_MINUTES> // Optional property }, { "type": "BUTTONS", "buttons": [ { "type": "OTP", "otp_type": "<OTP_TYPE>", "text": "<TEXT>", // Optional property "autofill_text": "<AUTOFILL_TEXT>", // Optional property, one-tap buttons only "package_name": "<PACKAGE_NAME>", // One-tap buttons only "signature_hash": "<SIGNATURE_HASH>" // One-tap buttons only } ] } ] }
Note that in your template creation request the button type
is designated as OTP
, but upon creation the button type
will be set to URL
. You can confirm this by performing a GET request on a newly created authentication template and analyzing its components.
Placeholder | Description | Example Value |
---|---|---|
Boolean | Optional. Set to |
|
String | Optional. One-tap buttons only. One-tap button text. Maximum 25 characters. If omitted, the autofill text will default to a pre-set value, localized to the template's language. For example, |
|
String | Required. Template category. Set this to |
|
Integer | Optional. Indicates number of minutes the password or code is valid. If omitted, the code expiration warning will not be displayed in the delivered message. Minimum 1, maximum 90. |
|
String | Required. Template language and locale code. |
|
String | Required. Template name. Maximum 512 characters. |
|
String | Indicates button type. Set to See Buttons above. |
|
String | One-tap buttons only. Your Android app's package name. |
|
String | One-tap buttons only. Your app signing key hash. See App Signing Key Hash below. |
|
String | Optional. Copy code button text. If omitted, the text will default to a pre-set value localized to the template's language. For example, If your template is using a one-tap autofill button and you supply this value, the authentication template message will display a copy code button with this text if we are unable to validate your handshake. Maximum 25 characters. |
|
This example creates a template named "authentication_code_copy_code_button" categorized as AUTHENTICATION
with all optional text strings enabled and a copy code button.
curl 'https://graph.facebook.com/v18.0
/102290129340398/message_templates' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer EAAJB...' \
-d '{
"name": "authentication_code_copy_code_button",
"language": "en_US",
"category": "AUTHENTICATION",
"components": [
{
"type": "BODY",
"add_security_recommendation": true
},
{
"type": "FOOTER",
"code_expiration_minutes": 10
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "OTP",
"otp_type": "COPY_CODE",
"text": "Copy Code"
}
]
}
]
}'
This example creates a template named "authentication_code_autofill_button" categorized as AUTHENTICATION
with all optional text strings enabled and a one-tap autofill button.
curl 'https://graph.facebook.com/v18.0
/102290129340398/message_templates' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer EAAJB...' \
-d '
{
"name": "authentication_code_autofill_button",
"language": "en_US",
"category": "AUTHENTICATION",
"components": [
{
"type": "BODY",
"add_security_recommendation": true
},
{
"type": "FOOTER",
"code_expiration_minutes": 10
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "OTP",
"otp_type": "ONE_TAP",
"text": "Copy Code",
"autofill_text": "Autofill",
"package_name": "com.example.luckyshrub",
"signature_hash": "K8a%2FAINcGX7"
}
]
}
]
}'
Upon success, the API will respond with a JSON object describing the newly created template.
{ "id": "594425479261596", "status": "PENDING", "category": "AUTHENTICATION" }
Use Cloud API or On-Premises API (whichever you are already using) to send authentication template messages.
Note that if you are sending an authentication template that has a one-tap autofill button, you must first initiate a handshake between your app and WhatsApp Messenger or WhatsApp Business. See Handshake below.
You can generate previews of authentication template text in various languages that include or exclude the security recommendation string and code expiration string using the WhatsApp Business Account > Message Template Previews endpoint.
GET /<WHATSAPP_BUSINESS_ACCOUNT_ID>/message_template_previews ?category=AUTHENTICATION, &language=<LANGUAGE>, // Optional &add_security_recommendation=<ADD_SECURITY_RECOMMENDATION>, // Optional &code_expiration_minutes=<CODE_EXPIRATION_MINUTES>, // Optional &button_types=<BUTTON_TYPES> // Optional
Placeholder | Description | Example Value |
---|---|---|
Comma-separated list | Optional. Comma-separated list of language and locale codes of language versions you want returned. If omitted, versions of all supported languages will be returned. |
|
Boolean | Optional. Set to If omitted, the security recommendation string will not be included. |
|
Int64 | Optional. Set to an integer if you want the code expiration footer string included in the response. If omitted, the code expiration footer string will not be included. Value indicates number of minutes until code expires. Minimum |
|
Comma-separated list of strings | Required. Comma-separated list of strings indicating button type. If included, the response will include the button text for each button in the response. For authentication templates, this value must be |
|
curl 'https://graph.facebook.com/v17.0/102290129340398/message_template_previews?category=AUTHENTICATION&languages=en_US,es_ES&add_security_recommendation=true&code_expiration_minutes=10&button_types=OTP' \ -H 'Authorization: Bearer EAAJB...'
{ "data": [ { "body": "*{{1}}* is your verification code. For your security, do not share this code.", "buttons": [ { "autofill_text": "Autofill", "text": "Copy code" } ], "footer": "This code expires in 10 minutes.", "language": "en_US" }, { "body": "Tu código de verificación es *{{1}}*. Por tu seguridad, no lo compartas.", "buttons": [ { "autofill_text": "Autocompletar", "text": "Copiar código" } ], "footer": "Este código caduca en 10 minutos.", "language": "es_ES" } ] }
Use the WhatsApp Business Account > Upsert Message Templates endpoint to bulk update or create authentication templates in multiple languages that include or exclude the optional security and expiration warnings.
If a template already exists with a matching name and language, the template will be updated with the contents of the request, otherwise, a new template will be created.
POST /<WHATSAPP_BUSINESS_ACCOUNT_ID>/upsert_message_templates
{ "name": "<NAME>", "languages": [<LANGUAGES>], "category": "AUTHENTICATION", "components": [ { "type": "BODY", "add_security_recommendation": <ADD_SECURITY_RECOMMENDATION> // Optional }, { "type": "FOOTER", "code_expiration_minutes": <CODE_EXPIRATION_MINUTES> // Optional }, { "type": "BUTTONS", "buttons": [ { "type": "OTP", "otp_type": "<OTP_TYPE>", "package_name": "<PACKAGE_NAME>", // One-tap buttons only "signature_hash": "SIGNATURE_HASH>", // One-tap buttons only } ] } ] }
All template creation properties are supported, with these exceptions:
language
property is not supported. Instead, use languages
and set its value to an array of language and locale code strings. For example: ["en_US","es_ES","fr"]
.text
property is not supported.autofill_text
property is not supported.This example creates three authentication templates in English, Spanish, and French, with copy code buttons. Each template is named "authentication_code_copy_code_button" and includes the security recommendation and expiration time.
curl 'https://graph.facebook.com/v17.0/102290129340398/upsert_message_templates' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer EAAJB...' \ -d ' { "name": "authentication_code_copy_code_button", "languages": ["en_US","es_ES","fr"], "category": "AUTHENTICATION", "components": [ { "type": "BODY", "add_security_recommendation": true }, { "type": "FOOTER", "code_expiration_minutes": 10 }, { "type": "BUTTONS", "buttons": [ { "type": "OTP", "otp_type": "COPY_CODE" } ] } ] }'
This example (1) updates an existing template with the name "authentication_code_autofill_button" and language "en_US", and (2) creates two new authentication templates in Spanish and French with one-tap autofill buttons. Both newly created templates are named "authentication_code_autofill_button" and include the security recommendation and expiration time.
curl 'https://graph.facebook.com/v17.0/102290129340398/upsert_message_templates' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer EAAJB...' \ -d ' { "name": "authentication_code_autofill_button", "languages": ["en_US","es_ES","fr"], "category": "AUTHENTICATION", "components": [ { "type": "BODY", "add_security_recommendation": true }, { "type": "FOOTER", "code_expiration_minutes": 15 }, { "type": "BUTTONS", "buttons": [ { "type": "OTP", "otp_type": "ONE_TAP", "package_name": "com.example.luckyshrub", "signature_hash": "K8a%2FAINcGX7" } ] } ] }'
{ "data": [ { "id": "954638012257287", "status": "APPROVED", "language": "en_US" }, { "id": "969725527415202", "status": "APPROVED", "language": "es_ES" }, { "id": "969725530748535", "status": "APPROVED", "language": "fr" } ] }
If you are creating an authentication template that uses a one-tap autofill button, you must include your app signing key hash in the components array.
To calculate your hash, follow Google's instructions for computing your app's hash string.
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 shell script to compute the hash. For example:
./sms_retriever_hash_v9.sh --package "com.example.myapplication" --keystore ~/.android/debug.keystore
If any of your authentication templates use a one-tap autofill button, your app must be able to initiate a "handshake".
A handshake is an Android intent and public class that you implement but that we can start from the WhatsApp app or WhatsApp Business app.
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 app or WhatsApp Business app 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.
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.
The WhatsApp app or WhatsApp Business app 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.
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. See Components and Public Class.signature_hash
property in the components array upon template creation) matches your installed app's signing key hash. See ComponentsAndroid notifications indicating receipt of a WhatsApp authentication template message will only appear on the user's Android device if:
Implement the following activity and class in your app.
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
.
<activity android:name=".ReceiveCodeActivity" android:enabled="true" android:exported="true" android:launchMode="standard"> <intent-filter> <action android:name="com.whatsapp.otp.OTP_RETRIEVED" /> </intent-filter> </activity>
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.
Define the activity public class that can accept the code once it has been passed to your app.
public class ReceiveCodeActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = getIntent(); // retrieve PendingIntent from extras bundle PendingIntent pendingIntent = intent.getParcelableExtra("_ci_"); // verify source of the pendingIntent String pendingIntentCreatorPackage = pendingIntent.getCreatorPackage(); // check if creatorPackage is "com.whatsapp" -> WA consumer app Or // "com.whatsapp.w4b" -> WA business app if ("com.whatsapp".equals(creatorPackage) || "com.whatsapp.w4b".equals(creatorPackage)) { // use OTP code String otpCode = intent.getStringExtra("code"); } } }
This example demonstrates one way to initiate a handshake with the WhatsApp app or WhatsApp Business app.
public void sendOtpIntentToWhatsApp() { // Send OTP_REQUESTED intent to both WA and WA Business App sendOtpIntentToWhatsApp("com.whatsapp"); sendOtpIntentToWhatsApp("com.whatsapp.w4b"); } private void sendOtpIntentToWhatsApp(String packageName) { /** * 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 */ int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? FLAG_IMMUTABLE : 0; PendingIntent pi = PendingIntent.getActivity( getApplicationContext(), 0, new Intent(), flags); // Send OTP_REQUESTED intent to WhatsApp Intent intentToWhatsApp = new Intent(); intentToWhatsApp.setPackage(packageName); intentToWhatsApp.setAction("com.whatsapp.otp.OTP_REQUESTED"); // WA will use this to verify the identity of the caller app. Bundle extras = intentToWhatsApp.getExtras(); if (extras == null) { extras = new Bundle(); } extras.putParcelable("_ci_", pi); intentToWhatsApp.putExtras(extras); getApplicationContext().sendBroadcast(intentToWhatsApp); }
If your message fails any of our eligibility checks, the one-tap autofill button will be replaced with a copy code button. In addition, there may be device, WhatsApp app, or WhatsApp Business app settings that prevent message notifications. To help with debugging, our apps surface some error information via the com.whatsapp.OTP_ERROR
intent. In these situations you will receive an error key and message instead of the user's one-time passwords or verification code.
Note that some of these error signals will only surface if you are running WhatsApp in the Android emulator.
Key | Description |
---|---|
| Incompatible Android version This can happen when you initiate the handshake (send the |
Emulator only | Incorrect signature hash This can happen when you initiate the handshake (send the |
| Missing handshake / Order of operations This can happen when our app receives an authentication template message with a one-tap autofill button but the handshake was not initiated. |
| OTP request expired This can happen when an authentication template that uses a one-tap autofill button is delivered to the user but more than 10 minutes have passed since you initiated the handshake. In this situation, we display the copy code button instead. |
Emulator only | Message notification disabled in WA settings This can happen when you initiate the handshake (send the |
Emulator only | WA notification disabled in device level This can happen when you initiate the handshake (send the |
The error signals are delivered via broadcasted intent so you must implement BroadcastReceiver
to listen for error signals.
<receiver android:name=".app.otp.OtpErrorReceiver" android:enabled="true" android:exported="true" > <intent-filter> <action android:name="com.whatsapp.otp.OTP_ERROR"/> </intent-filter> </receiver>
public class OtpErrorReceiver extends BroadcastReceiver { public static final String OTP_ERROR_KEY = "error"; public static final String OTP_ERROR_MESSAGE_KEY = "error_message"; @Override public void onReceive(Context context, Intent intent) { try { PendingIntent pendingIntent = intent.getParcelableExtra("_ci_"); if (pendingIntent != null) { String packageName = pendingIntent.getCreatorPackage(); if (packageName.equalsIgnoreCase("com.whatsapp") || packageName.equalsIgnoreCase("com.whatsapp.w4b")) { String otpErrorKey = intent.getStringExtra(OTP_ERROR_KEY); String otpErrorMessage = intent.getStringExtra(OTP_ERROR_MESSAGE_KEY); // Handle errors } } } catch (BadParcelableException e) { Log.e("OtpErrorReceiver", e.getLocalizedMessage()); } } }
See our WhatsApp One-Time Password (OTP) Sample App 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.