Generic WhatsApp

Overview

CleverTap has now made its WhatsApp API and standard callback formats public to global communities of WhatsApp service providers. Now, BSPs & customers do not need to depend on CleverTap to build the native WhatsApp integration. Instead, BSPs or customers can use these APIs and build the integration independently.

📘

For CleverTap Customers

This feature is helpful to customers in the following ways:

  1. Customers can build middleware between CleverTap & BSP and create their own endpoint to start using WhatsApp functionality in CleverTap with the BSP of their choice.
  2. Customers can ask their BSPs to build the CleverTap integration. This will allow customers to use that provider with CleverTap without any development.

📘

For WhatsApp BSPs

Providers interested in pursuing the partnership must complete the integration steps listed in this document and write to [email protected] to get their name added to this list.

Partner Acceptance Criteria

Any WhatsApp partner can integrate with our open APIs, but to get added to the list of integrated partners, they need to share the following with CleverTap:

  • Production API endpoint for sending Template and Freeform notifications via single endpoint.
  • Production API credentials for two test accounts so the CleverTap team can test the integration.
  • Providers need to provide dashboard access where the CleverTap team can create the templates or share approved templates to test the campaign flow.
  • Providers need to add CleverTap's callback URL in the test accounts so that CleverTap can ensure that callbacks are received in the correct format.
  • Providers must publish a CleverTap integration guide in their user docs so that CleverTap can link it with the user document.
  • Partners must provide the contact details of their dedicated support and product team so that CleverTap can reach out to them in case of any issues.

Supported Business Service Providers

CleverTap supports WhatsApp integration with the following business service providers:

Integration steps

Customers need to get endpoint and authentication details from BSP.

📘

Custom Endpoint

  • For customers
    BSPs might have a dedicated endpoint (different from their standard endpoint) for CleverTap integration. Given this, you need to get the API details for CleverTap integration from your provider.

  • For BSPs:
    BSPs must provide API endpoints and tested credentials that work with the CleverTap platform.

To get started with generic integration, BSP integrating with CleverTap must share the API endpoint and authentication details with customers. BSPs must ensure that they provide the endpoint that can process the payload being sent by CleverTap. After the customers have API details handy, proceed as follows:

  1. Navigate to Settings > Channels > WhatsApp and select Other(Generic) from the Provider dropdown list.
Generic Provider Setup

Generic Provider Setup

  1. Enter the API endpoint and auth details to save the endpoint.

📘

Provider Verification

CleverTap sends a sample payload to validate the credentials before saving the provider details. BSPs need to provide a “200 OK“ response along with the response if API credentials are correct.

Expected API Response

{
    "status": "success"
}

Sample Credential Validation Payload

{
    "payloadVersion": 0.1,
    "wabaNumber": $$WABAnumber,
    "to": "+919870369294",
    "isTemplate": true,
    "template": {
      "namespace": "whatsapp:hsm:technology:generic:verify",
      "languageCode": "en"
    },
    "components": [
      {
        "type": "body",
        "body": {
          "text": "1 code: 2.Valid for 3 minutes.",
          "parameters": [
            {
              "type": "text",
              "text": "1"
            },
            {
              "type": "text",
              "text": "2"
            },
            {
              "type": "text",
              "text": "3"
            }
          ]
        }
      }
    ],
    "msgId": "0|0|0|0"
  }
  1. Customers need to copy both delivery report callbacks and IMO callback URLs and get these callbacks configured with the WhatsApp provider. Providers need to send the callbacks in the format specified later in this document. Any format, other than our expected callback, will not be accepted. This may result in campaign stats not being populated and incoming messages not showing up in the CleverTap dashboard.

  2. After the credentials are stored, customers can save the approved templates under the template section to start sending WhatsApp notifications in the approved template formats.

📘

Template Verification

CleverTap sends a sample payload according to the template being saved in the dashboard. BSPs need to validate the payload and return 200 OK response if all the details are as per approved templates.

  1. After the templates are saved, you can navigate to the Campaigns and start creating campaigns.

If the end-customers reply to notifications sent from CleverTap, incoming messages appear under the Conversation section of the dashboard. Businesses can reply to incoming messages by selecting the chat.

API Endpoint

BSPs are requested to create an endpoint where CleverTap can send the notification payload for campaigns and free form messages. Customers should ask BSPs to share the API endpoint that can accept the CleverTap Payload to save the provider in CleverTap Dashboard.

API Authentication

The CleverTap's REST API supports basic authentication or custom headers for authentication. Partners must share these credentials with customers and customers will save these credentials with an endpoint.

Send message API Specifications

CleverTap has categorized outbound message payloads into 2 categories.

  • Templates messages
  • Freeform messages

CleverTap sends either of the payloads depending on the type of message sent by the dashboard user. CleverTap sends key isTemplate to help BSPs understand whether this is templatized message or a freeform message.

📘

Single Endpoint

CleverTap does not support different endpoints for different types of messages, so BSPs need to provide just one endpoint to process freeform and template messages.

API Payload for Template Message

CleverTap sends this payload for sending the template message notification.
The outgoing template message payload can be broken down into the following three sections:

  • User & notification information
  • Template information that includes template name, namespace, template language, etc.
  • Content information

📘

Payload Essentials

The message payload is encoded into UTF-8 (Unicode Transformation Format) before being sent to the provider's endpoint.

Values with $$ ($$To,$$BusinessWabaNumber) are dynamic values and change for each request.

CleverTap does not support templates with contacts currently, so contact objects are not sent with payload.

CleverTap does not support Name, Address, or URL key in location object.

msg_id is a unique parameter for each message sent from CleverTap and must be present in callbacks sent by BSP to CleverTap.

{
    "payloadVersion": $$payloadVersion,
    "to": "$$to", // suppport numbers with + and without +(eg. 91 and +91)
    "wabaNumber": "$$businessWabaNumber",
    "isTemplate": true,
    "msgId": "i|campaignID|batchID|j|campaign_variant", // upto 60 characters
    "template": {
        "namespace": "$$templateNamespace",
        "languageCode": "$$BSPsr-language-and-locale-code"
    },
    "components": [
        //There will be either just one header object or no header object present in the payload depending on the template being used. 
        { // optional media header
            "type": "header",
            "header": {
                "type": "text",
                "text": {
                    "text": "$headertext",
                    "parameters": [
                        {
                            "type": "text",
                            "text": "$$PlaceholderText1"
                        }
                    ]
                }
            }
        },
        { // optional media header
            "type": "header",
            "header": {
                "type": "video",
                "video": {
                    "mediaURL": "$$mediaUrl"
                }
            }
        },
      
          { // optional media header
            "type": "header",
            "header": {
                "type": "image",
                "image": {
                    "mediaURL": "$$mediaUrl"
                }
            }
        },
        { // optional document header
            "type": "header",
            "header": {
                "type": "file",
                "file": {
                    "mediaURL": "$$mediaUrl",
                    "filename": "$$filename"
                }
            }
        },
        { // optional location header
            "type": "header",
            "header": {
                "type": "location",
                "location": {
                    "longitude": "$$longitude",
                    "latitude": "$$longitude"
                }
            }
        },
        {
            "type": "body",
            "body": {
                "text": "$$Body",
                "parameters": [
                    {
                        "type": "text",
                        "text": "$$PlaceholderText1"
                    },
                    {
                        "type": "text",
                        "text": "$$PlaceholderText2"
                    }
                ]
            }
        },
        {// optional.. will be present in payload if templates have a footer text
            "type": "footer",
            "footer": "$$footertext"
        },
        {// Dynamic URL CTA (optional)
            "type": "button",
            "index": $$buttonPosition,
            "subType": "dynamicUrl",
            "buttonText": "$$cta_text",
            "parameters": [
                {
                    "type": "text",
                    "text": "$$suffixURL"
                }
            ]
        },
        {//static URL CTA (optional)
            "type": "button",
            "index": $$Buttonposition,
            "buttonText": "cta_text",
            "subType": "staticUrl",
            "parameters": [
                {
                    "type": "text",
                    "text": "$$ButtonURL"
                }
            ]
        },
        {// quick reply buttons (optional)
            "type": "button",
            "index": $$buttonPosition,
            "subType": "quickReply",
            "buttonText": "$$cta_text",
            "parameters": [
                {
                    "type": "payload",
                    "payload": "$$buttonPayload"
                }
            ]
        },
        {// call phone CTA (optional)
            "type": "button",
            "index": $$buttonPosition,
            "subType": "callPhone",
            "buttonText": "$$cta_text",
            "parameters": [
                {
                    "type": "text",
                    "text": "$$phonenumber"
                }
            ]
        }
    ]
}

Postman Collection

https://www.getpostman.com/collections/5c34a2e3ffd06b7c6492

Payload key-value description

Key nameDescriptionSample values
payloadVersionA version of the payload being sent.0.1
toTargeted user’s phone number.9199XXXXXXXX/ +9199XXXXXXXX
wabaNumberBusiness’s WABA number (from number).9189XXXXXXXX
isTemplateUsed to highlight whether the payload being sent is for templates or freeform.true/false (Boolean)
msgIdUnique identifier for a message sent and expected to be present in DLR status callback payloads.25596363|1639561857|20220227|25596363
template.namespaceTemplate name/namespace.Namespace/name
template.languageCodeTemplate language code as defined by Facebook in
Message Templates - WhatsApp Business On-Premises API - Documentation - Facebook for Developers.
en(uk)
components.typeType of component object.header/body/buttons
Components.header.typeType of header object.text/audio/image/video/file/location
Components.header.text.textHeader text.“This is header text”
Components.header.text.parameters .textPersonalization variables sent as an array of objects."john"
Components.header.audio.mediaURLMedia URL of the audio file.www.example.com/audio.mp3
Components.header.file.filenameTitle of the file.“sample_document.pdf”
Components.header.video.mediaURL | Type= headerMedia URL of the video file.www.example.com/video.mp4
Components.header.location.lattitudeLatitude of the location shared.2.457476548
Components.parameters.location.longitudeLongitude of the location shared.1.565867657
Components.bodyMessage body.“This is message body”
Components.body.parametersta".textPersonalization variables sent as an array of objects"john"
Components.index |type = buttonsPosition of the buttons.0/1/2
Components.subtype |type = buttonsType of button.static_url/ Quickreply/call_phone/dynamic_url
Components.parameters.text |type = buttonsURL suffix for dynamic URL CTA./home
Components.parameters.payload |type = buttonsQuick reply button identifier. Same value needs to be returned with incoming message callback URL if someone clicks on the button.“25596363|click“
Components.buttonTextButton text.“visit home page“
Components.footerFooter text.“This is footer”

Payload Samples for Template Message

{
"payloadVersion":0.1,
"to": "919999999999",
"wabaNumber": "91999999XXXX",
"isTemplate": true,
"msgId": "25596363|1639561857|20220227|25596363",
"template": {
             "namespace": "Namespace:sample",
             "languageCode":"en(uk)"
                         
                         },
"components": [

{
 "type": "body",
 "body": {
 "text":"This is sample body",
  "parameters": [
               {
                  "type": "text",
                   "text": "Sample"
               }
           ]}},
  {
  "type": "footer",
  "footertext":"this is footer"
        
       }
]}
{
    "payloadVersion": 0.1,
    "to": "919999999999",
    "wabaNumber": "919999999999",
    "isTemplate": true,
    "msgId": "i|campaignID|batchID|j",
    "template": {
        "namespace": "Namespace:sample",
        "languageCode": "en"
    },
    "components": [
        {
            "type": "header",
            "header": {
                "type": "image",
                "image": {
                    "mediaURL": "httpa://www.example.com/image.png"
                }
            }
        },
        {
            "type": "body",
            "body": {
                "text": "This is sample body",
                "parameters": [
                    {
                        "type": "text",
                        "text": "sample"
                    }
                ]
            }
        },
        {
            "type": "button",
            "index": 0,
            "subType": "dynamicUrl",
            "buttonText": "Sample CTA TEXT",
            "parameters": [
                {
                    "type": "text",
                    "text": "https://www.example.com"
                }
            ]
        }
    ]
}

API Payload for Freeform Message

CleverTap will send this payload for sending the Freeform message notification.
Freeform message API payload has the user information about the targetted users, media URLs, and text.

📘

Payload Essentials

  • Values with $$ (for example $$To,$$BusinessWabaNumber) are dynamic values and change for each request.

    • Dynamic Reply Button, interactive lists, and product catalog, contacts, and locations are not currently supported in the CleverTap platform. This is why these objects are not mentioned in the payload.

    • CleverTap does not support Name, Address, or URL key in location object.

    • msg_id is a unique parameter for each message sent from CleverTap and must be present in callbacks sent by BSP to CleverTap.

{
    "payloadVersion": $$payloadversion,
    "to": "$$To",
    "wabaNumber": "$$BusinessWabaNumber",
    "msgId": "i|campaignID|batchID|j",
    "type": "text/audio/file/image/location/video",
    "isTemplate": false,
    //Optional 
    "audio": {
        "mediaURL": "$$mediaUrl"
    },
    //OR
    "file": {
        "mediaURL": "$$mediaUrl",
        "caption": "$$body",
        "filename": "$$filename"
    },
    //OR
    "image": {
        "mediaURL": "$$mediaUrl",
        "caption": "$$body"
    },
    //OR
    "text": {
        "body": "$$Body"
    },
    //OR
    "video": {
        "mediaURL": "$$mediaUrl",
        "caption": "$$body"
    }
}

Payload Description

Key nameDescriptionSample values
toTargeted user’s phone number9199XXXXXXXX
"payloadversion"Version of the payload being sent to CleverTap0.1
wabaNumberBusiness’s WABA number9189XXXXXXXX
isTemplateUsed to highlight the whether the payload being sent is for templates or freeformtrue/false
msgIdUnique identifier for message being sent and expected to be present in callback payloads25596363|1639561857|20220227|25596363
audio.mediaURLMedia URL of the filewww.example.com/audio.mp3
file.mediaURLMedia URL of the filewww.example.com/document.pdf
file.captionMessage body“This is message body”
file.filenameTitle of the file“sample_document.pdf”
image.mediaURLMedia URL of the filewww.example.com/image.png
image.captionMessage body“This is message body”
video.mediaURLMedia URL of the filewww.example.com/video.mp4
video.captionMessage body“This is message body”

Payload Samples For Freeform Message Template

{
"payloadVersion":0.1,
"to": "919999999999",
“msg_id”:"25596363|1639561857|20220227|25596363",
"wabaNumber": "91999999XXXX",
"type": "audio",
"isTemplate": false,
"audio": {
        "mediaURL": "www.example.com/audio.mp3"
        }
        }
{
"payloadVersion":0.1,
"to": "919999999999",
"wabaNumber": "91999999XXXX",
“msg_id”:"25596363|1639561857|20220227|25596363",
"type": "image",
"isTemplate": false,
"image": {"mediaURL": "www.example.com/image.png","caption": "This is body"} }
{
"payloadVersion":0.1,
"to": "919999999999",
"wabaNumber": "91999999XXXX",
“msg_id”:"25596363|1639561857|20220227|25596363",
"type": "file",
"isTemplate": false,
"file": {
          "mediaURL": "www.example.com/document.pdf",
           "caption": "This is body",
          "filename": "sample.pdf"
          } }

Expected API Response

CleverTap expects partners to return any of the following responses depending on whether the request was successful. CleverTap will read the response body if the API request status code is 200 OK, so notification processing-related errors and codes can be sent in the response body. Read below for expected error codes:

📘

Error Object

The error object for the following error code needs to be specified only if there are errors in the send message API request:

  • 2000 → Invalid credentials
  • 2001 → Invalid template parameters
  • 2002 → Invalid phone number
  • 2003 → Phone number not subscribed
  • 2004 → Other

HTTPS Code: 200 Successful API call

{ 
  "status": "success | failure",
}

HTTPS Code: 200

{ 
  "status": "failure",
   "error" : {
              "code" : 2000,
              "message" : "Invalid Credentials"
              }
}

HTTPS Code: 500

{
"status": "failure",
"error" : {
"message" : "Unable to process the request"
          }
}

HTTPS Code: 429

{ "message" : "Too many requests to process"}

HTTPS Code: 403

{ "message" : "Bad Request"}

WhatsApp Callbacks

CleverTap automatically creates unique callback URLs for each customer. These callback URLs are available under the Provider setup section. BSPs are expected to add these callbacks at their end and forward the incoming messages to CleverTap’s callback URL in the specified format. CleverTap creates separate callback URLs for message status and incoming messages. Both these callbacks are needed to be configured at the BSP's end.

Callback Authentication

CleverTap creates unique callback URLs for each customer and does not need any authentication. CleverTap accepts the callbacks if the callbacks are sent in the expected format.

Incoming Message Callbacks

BSPs need to add CleverTap's incoming message callback URL at their end and forward the incoming message in the following format.

{
    "payloadVersion": "$$payloadversion",
    "from": "$$phone",
    "wabaNumber": "$$BusinessWabaNumber",
    "timestamp": "$$receivedTS",
    "type": "text/location/image/video/voice/file/button",
    //Optional(Needs to be sent if user replies to previously sent message)
    "context": {
        "id": "$$msg_ID"
    },
    //Optional (will be needed if message received is text)
    "text": {
        "body": "$$incomingMessage"
    },
    //Optional (will be needed if message received is loaction)
    "location": {
        "address": "$$address",
        "latitude": "$$latitude",
        "longitude": "$$latitude",
        "name": "$$addressTitle",
        "url": "$$addressUrls"
    },
    //Optional (will be needed if message received is image)
    "image": {
        "mediaURL": "$$mediaUrl",
        "mimeType": "$$mediaMimeType", // image/jpeg
        "caption": "$$incomingmessage"
    },
    //Optional (will be needed if message received is file)
    "file": {
        "mediaURL": "$$mediaUrl",
        "mimeType": "$$mediaMimeType",
        "caption": "$$incomingmessage"
    },
    //Optional (will be needed if message received is audio)
    "audio": {
        "mediaURL": "$$mediaUrl",
        "mimeType": "$$mediaMimeType"
    },
    //Optional (will be needed if message received is video)
    "video": {
        "mediaURL": "$$mediaUrl",
        "mimeType": "$$mediaMimeType",
        "caption": "$$incomingmessage"
    },
    //Optional (will be needed if the message received is video)
    "button": {
        "payload": "$$buttonPayload", // same as outgoing  message
        "text": "$$buttonText"
    }
}

Payload Description

Key nameDescriptionSample values
fromSource number for incoming message“9199XXXXXXXX”
payloadversionversion of the callback payload being sent to CleverTap“0.1“
wabaNumberDestination business WABA number to which message was sent“9199XXXXXXXX”
timestampTimestamp when the message was sent“1647441774”
typeType of incoming message“text/audio/video/image”
context.idMsg_id of the message to which the user has responded25596363|1639561857|20220227|25596363
text.bodyText received from end user“Hey, This is message”
location.lattitudeLatitude of the location shared2.457476548
location.LongitudeLongitude of the location shared1.565867657
location.nameName of the location sharedJoe’s House
location.addressAddress of the location shared102, Parker street, USA
location.urlAddress URLwww.example.com
image.mediaURLMedia File URLwww.example.com/image.png
image.mimetypeMedia file mime type.png/.jpeg
image.captionImage caption“This is caption”
file.mediaURLMedia File URLwww.example.com/file.pdf
file.mimetypeMedia file mime type.pdf
file.captionImage caption“This is caption”
audio.mediaURLMedia File URLwww.example.com/audio.mp3
video.mimetypeMedia file mime type.mp3
video.mediaURLMedia File URLwww.example.com/image.png
video.mimetypeMedia file mime type.mp4
video.captionImage caption“This is caption”

Sample Payload

{
              "payloadVersion":"0.1",
              "from": "919999999999",
              "wabaNumber": "9199999998888",
              "timestamp": "1647441774",
              "type": "file",
              "context": {
              "id": "25596363|1639561857|20220227|25596363"
              },
"document": {
            "mediaURL": "www.example.com/document.pdf",
             "caption": "This is body",
             "mimeType": ".pdf"
          } 
}
{
"payloadversion":"0.1",
"from": "919999999999",
"wabaNumber": "9199999998888",
"timestamp": "1647441774",
"type": "location",
"context": {
              "id": "25596363|1639561857|20220227|25596363"
              },
"location": {
            "address": "107, baker street",
            "latitude": "2.457476548",
            "longitude": "2.457476548",
            "name": "Johns office", //optional
            "url": "www.johnsoffice.com" // optional
            }
}

Message Status Callbacks

BSPs need to add CleverTap's message status callback URL at their end and forward the message statuses in the following format. CleverTap uses these callbacks to populate the campaign statistics.

{
    "payloadVersion": "$$paypoadVersion",
    "statuses": [
        {
            "msgId": "$$customMessageID",
            "status": "sent/delivered/read/failed",
            "timestamp": "$$timestamp", //Event timestamp
            "error": {
                "code": "$$errorcode",
                "title": "$$errordescription"
            }
        },
        {
            //Support multiple status callbacks in a single request
        }
    ]
}

Payload Description

Key nameDescriptionSample values
payloadversionVersion of the payload being sent to CleverTap0.1
statuses[X].statusNotification status“sent/delivered/read/failed”
statuses[X].timestampTimestamp of the event“1647441774”
statuses[X].msgIdUnique identifier for message being sent and expected to be present in callback payloads25596363|1639561857|20220227|25596363
statuses[X].error.codeFailure Error code1001
statuses[X].error.titleDescription of errorMessage specified does not match with any template.

Sample Payload

{
"payloadVersion":"0.1",
"statuses": [{
               "msgId": "25596363|1639561857|20220227|25596363", // same as outbound message ID
               "status": "delivered",
               "timestamp": "1647441774", //Event timestamp
               }]
               }
{"paytloadVersion":"0.1",
"statuses": [{
               "msgId": "25596363|1639561857|20220227|25596363",
               "status": "failed",
               "timestamp": "1647441774", //Event timestamp
               "error": {
               "code": 1004,
               "title": "Invalid phone number."
               }}]

}

Callback Error Codes

🚧

Callback Error Codes

Listed below are the expected callback error codes:

  • 1000 → Invalid credentials
  • 1001 → Invalid template parameters
  • 1002 → Invalid phone number
  • 1003 → The phone number is no longer active
  • 1004 → Too many send requests to phone numbers
  • 1005 → The phone number is temporarily unavailable or not in the provider network
  • 1006 → Phone number is blacklisted
  • 1007 → User device can’t receive the message
  • 1008 → This message is sent outside of the WhatsApp chat window
  • 1009 → Other

Creating/Uploading User Base

You can upload a CSV file to upload a set of internal users by going to Settings > CSV uploads. The following is the sample CSV format for uploading user data.

IdentityNameEmailPhoneGenderMSG-WhatsAppUpload Name
[email protected]John Doe+9199*420MTRUESample Upload

Accepted Message Formats

CleverTap currently has limitations on the types of WhatsApp messages supported on the dashboard. We are working on adding support for the new message types as soon as possible but for the time being, only the following message types are supported.

Freeform Messages
This message type supports the following format:

  • Simple text
  • Audio
  • Video
  • Images
  • Document

Template messages
The elements of the Template message include a header, footer, simple text, and buttons.
The header section supports the following formats:

  • Text
  • Image
  • Videos
  • Locations
  • Audios
  • Documents

FAQs

Q. My WhatsApp provider has shared the API credentials with me. Can I save the provider in the CleverTap dashboard and start creating WhatsApp campaigns?

Ans: Ensure that your WhatsApp service provider is there in the list of integrated partners. If yes, ensure that you are using the custom API endpoint that your partner has created for CleverTap integration.