Authentication Templates

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:

  • Preset authentication message template fixed text:
    • <VERIFICATION_CODE> is your verification code.
    • Security disclaimer (optional): For your security, do not share this code.
    • Expiration warning (optional): This code expires in <NUM_MINUTES> minutes.
  • Button: Either a copy code or one-tap autofill button.

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.

Buttons

Authentication templates must include either a copy code or one-tap autofill button. Buttons behave differently when tapped by a user:

  • A copy code button copies the one-time password or code to the user's clipboard. The user can then manually switch to your app and paste the password or code into your app's interface.
  • A one-tap autofill button automatically loads and passes your app the one-time password or code.

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.

Best Practices

  • Confirm the user's WhatsApp phone number before sending the one-time password or code to that number.
  • Make it clear to your user that the password or code will be delivered to their WhatsApp phone number, especially if you offer multiple ways for the user to receive password or code delivery. See Getting Opt-In for additional tips.
  • When the user pastes the password or code into your app, or your app receives it as part of the one-tap autofill button flow, make it clear to the user that your app has captured it.

Template Creation

Use the WhatsApp Business Account > Message Templates endpoint to create authentication templates. Alternatively, you can manually create them using the WhatsApp Manager.

Components

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:

  • a single body component
  • a single footer component
  • a single OTP Button component

Request Syntax

POST /<WHATSAPP_BUSINESS_ACCOUNT_ID>/message_templates

Post Body

{
  "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.

Properties

PlaceholderDescriptionExample Value

<ADD_SECURITY_RECOMMENDATION>

Boolean

Optional.


Set to true if you want the template to include the string, For your security, do not share this code. Set to false to exclude the string.

true

<AUTOFILL_TEXT>

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, Autofill for English (US).

Autofill

<CATEGORY>

String

Required.


Template category. Set this to AUTHENTICATION.

AUTHENTICATION

<CODE_EXPIRATION_MINUTES>

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.

5

<LANGUAGE>

String

Required.


Template language and locale code.

en_US

<NAME>

String

Required.


Template name.


Maximum 512 characters.

verification_code

<OTP_TYPE>

String

Indicates button type. Set to COPY_CODE if you want the template to use a copy code button, or ONE_TAP to have it use a one-tap autofill button.


See Buttons above.

ONE_TAP

<PACKAGE_NAME>

String

One-tap buttons only.


Your Android app's package name.

com.example.myapplication

<SIGNATURE_HASH>

String

One-tap buttons only.


Your app signing key hash. See App Signing Key Hash below.

K8a%2FAINcGX7

<TEXT>

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, Copy Code for English (US).


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.

Copy Code

Example Request (Copy Code)

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"
                }
            ]
        }
    ]
}'

Example Request (One-tap Autofill)

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"
              }
          ]
      }
  ]
}'

Example Response

Upon success, the API will respond with a JSON object describing the newly created template.

{
    "id": "594425479261596",
    "status": "PENDING",
    "category": "AUTHENTICATION"
}

Sending Authentication Template Messages

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.

Template Previews

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.

Request Syntax

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

Query String Parameters

PlaceholderDescriptionExample Value

<LANGUAGE>

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.

en_US,es_ES

<ADD_SECURITY_RECOMMENDATION>

Boolean

Optional.


Set to true if you want the security recommendation body string included in the response.


If omitted, the security recommendation string will not be included.

true

<CODE_EXPIRATION_MINUTES>

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 1, maximum 90.

10

<BUTTON_TYPES>

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 OTP.

OTP

Example Request

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...'

Example Response

{
  "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"
    }
  ]
}

Bulk Management

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.

Request Syntax

POST /<WHATSAPP_BUSINESS_ACCOUNT_ID>/upsert_message_templates

Post Body

{
  "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
        }
      ]
    }
  ]
}

Properties

All template creation properties are supported, with these exceptions:

  • The 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"].
  • The text property is not supported.
  • The autofill_text property is not supported.

Example Copy Code Request

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"
        }
      ]
    }
  ]
}'

Example One-Tap Autofill Request

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"
        }
      ]
    }
  ]
}'

Example Response

{
  "data": [
    {
      "id": "954638012257287",
      "status": "APPROVED",
      "language": "en_US"
    },
    {
      "id": "969725527415202",
      "status": "APPROVED",
      "language": "es_ES"
    },
    {
      "id": "969725530748535",
      "status": "APPROVED",
      "language": "fr"
    }
  ]
}

App Signing Key Hash

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

Handshake

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.

Eligibility Checks

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.

  • The handshake was initiated no more than 10 minutes ago.
  • 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. See Components and Public Class.
  • 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. See Components
  • The message includes the one-tap autofill button text.
  • Your app has defined an activity to receive the password or code. See Activity below.

Android Notifications

Android notifications indicating receipt of a WhatsApp authentication template message will only appear on the user's Android device if:

  • The user is logged into the WhatsApp app or WhatsApp Business app with the phone number (account) that the message was sent to.
  • The user is logged into your app.
  • Android OS is KitKat (4.4, API 19) or above.
  • Show notifications is enabled (Settings > Notifications) in the WhatsApp app or WhatsApp Business app.
  • Device level notification is enabled for the WhatsApp app or WhatsApp Business app.
  • Prior message threads in the WhatsApp app or WhatsApp Business app between the user and your business are not muted.

Client Implementation

Implement the following activity and class in your app.

Activity

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.

Public Class

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");
       }
   }
}

Initiating the Handshake

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);
}

Errors Signals

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.

KeyDescription

incompatible_os_version

Incompatible Android version


This can happen when you initiate the handshake (send the com.whatsapp.otp.OTP_REQUESTED intent) but the device is running a version of Android older than v19.

incorrect_signature_hash

Emulator only

Incorrect signature hash


This can happen when you initiate the handshake (send the com.whatsapp.otp.OTP_REQUESTED intent) and our app receives an authentication template message that uses a one-tap autofill button, but the package name in the message does not produce the message's signature hash.

missing_handshake_or_disorder

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

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.

whatsapp_message_notification_disabled

Emulator only

Message notification disabled in WA settings


This can happen when you initiate the handshake (send the com.whatsapp.otp.OTP_REQUESTED intent) but the user has disabled notifications in the WhatsApp app or WhatsApp Business app (within our app settings).

whatsapp_notification_disabled

Emulator only

WA notification disabled in device level


This can happen when you initiate the handshake (send the com.whatsapp.otp.OTP_REQUESTED intent) but the user has disabled app notifications for our apps (device level settings).

Integration

The error signals are delivered via broadcasted intent so you must implement BroadcastReceiver to listen for error signals.

In manifest.xml

<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>

In receiver class

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());
   }
 }
}

Sample App

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.

See Also