Send WhatsApp Pay Messages

The WhatsApp Pay feature allows businesses to send an order payment link to the end customers on WhatsApp. Customers can pay the order amount using any of the UPI apps using the link or pay from WhatsApp natively.

The WhatsApp pay feature is built on existing messaging solutions such as Single Product Messages, Multi Product Messages, and Product Detail Messages that help in placing orders using the product catalog. Further, the feature is powered with payment solutions by partner payment gateways.

Prerequisites

The following are the prerequisites for sending catalog messages:

  1. An active kaleyra.io account, if you do not have one, you can contact a kaleyra representative to sign up for free to the platform. Before you get started, Create a Kaleyra Account and Create an API Key.
    To view the API Key and the SID, see View API Key and SID..
  2. An active WhatsApp for Business plan.
  3. Prerequisites specified for WhatsApp Pay.

Order_details API request message

An order details message is an interactive message that contains the list of items selected along with the items’ prices and the total bill amount to be paid and a payment link using which the customer can complete the order payment.

Base URL

https://api.kaleyra.io/v1/<SID>

Request format

The following is the request format to send an order details request message.

curl --location --request POST '<base_url>/messages' \
 --header 'api-key: <api_key>' \
--form 'to="<to>"' \
--form 'type="order_details"' \
--form 'channel="WhatsApp"' \
--form 'from="<from>"' \
--form 'body="message body"' \
--form 'footer="Footer"' \
--form 'action="{
    \"name\": \"Review_and_Pay\",
    \"parameters\": {
        \"reference_id\": \"<reference_id>\",
        \"type\": \"<goods type>\",
        \"payment_settings\": [
            {
                \"type\": \"payment_gateway\",
                \"payment_gateway\": {
                    \"type\": \"razorpay\",
                    \"configuration_name\": \"<configuration name>\",
                    \"razorpay\": {
                        \"receipt\": \"receipt-value\",
                        \"notes\": {
                            \"key1\": \"value1\"
                        }
                    }
                }
            }
        ],
        \"currency\": \"INR\",
        \"total_amount\": {
            \"value\": <value>,
            \"offset\": 100
        },
        \"order\": {
            \"status\": \"pending\",
            \"items\": [
                {
                    \"retailer_id\": \"<retailer_id>\",
                    \"name\": \"<item name>\",
                    \"amount\": {
                        \"value\": <value>,
                        \"offset\":100 
                    },
                    \"sale_amount\": {
                        \"value\": <value>,
                        \"offset\": 100
                    },
                    \"quantity\": 1
                }
            ],
            \"subtotal\": {
                \"value\": <value>,
                \"offset\": 100
            },
            \"tax\": {
                \"value\": 00,
                \"offset\": 100,
                \"description\": \"0%\"
            },
            \"shipping\": {
                \"value\": 0,
                \"offset\": 100,
                \"description\": \"<Discount message>\"
            },
            \"discount\": {
                \"value\": 0,
                \"offset\": 100,
                \"description\": \"0% discount\",
                \"discount_program_name\": \"optional_text\"
            }
        }
    }
}"' \
--form 'header="<header text>"' \
--form 'callback_url="<https://<callback URL>"'

Sample Request

The following is a sample API request to send an order details message.

curl ----request POST 'https://api.kaleyra.io/v1/HXIN17787xxxxxIN/messages' \ 
--header 'api-key: xxxxxe49xxxx13ce3f53613dad5xxxxxx' \ 
--form 'to="xx99865xxxxx"' \ 
--form 'type="order_details"' \
--form 'channel="WhatsApp"' \ 
--form 'from="+9180452xxxxx"' \
--form 'body="They`re good for your eye health too"' \ 
--form 'footer="Footer"' \ 
--form 'action="{ 
    \"name\": \"Review_and_Pay\", 
    \"parameters\": { 
        \"reference_id\": \"Hello_2abcdxyabxyaab\", 
        \"type\": \"physical-goods\", 
        \"payment_settings\": [ 
            { 
               \"type\": \"payment_gateway\", 
                \"payment_gateway\": { 
                 \"type\": \"razorpay\", 
                  \"configuration_name\": \"razor_pay_config\", 
                   \"razorpay\": { 
                    \"receipt\": \"receipt-value\", 
                     \"notes\": { 
                            \"key1\": \"value1\" 
                        } 
                    } 
                } 
            } 

        ], 
        \"currency\": \"INR\", 
        \"total_amount\": { 
        \"value\": 1000, 
        \"offset\": 100 
        }, 
        \"order\": { 
        \"status\": \"pending\", 
        \"expiration\":  
          {  
            \"timestamp\": \"1721198395\",  
             \"description\": \"cancellation-explanation\" 
           }, 
            \"items\": [ 
                { 
                  \"retailer_id\": \"6f5gcuihud\", 
                   \"name\": \"Bushel of carrots\", 
                    \"amount\": { 
                     \"value\": 100, 
                      \"offset\":100  
                    }, 
                    \"sale_amount\": { 
                     \"value\": 100, 
                      \"offset\": 100 
                    }, 
                    \"quantity\": 10 
                } 
            ], 
            \"subtotal\": { 
               \"value\": 1000, 
                \"offset\": 100 
            }, 
            \"tax\": { 
                \"value\": 100, 
                \"offset\": 100, 
                \"description\": \"10% tax on your order\" 
            }, 
            \"shipping\": { 
             \"value\": 100, 
              \"offset\": 100, 
               \"description\": \"10% off on your order\" 
            }, 
            \"discount\": { 
               \"value\": 200, 
                \"offset\": 100, 
                \"description\": \"20% discount\", 
                \"discount_program_name\": \"optional_text\" 
            } 
        } 
    } 

}"' \ 
--form 'header="Bushel of carrots"' \ 
--form 'callback_profile_id="IN_b86b078e-046b-4565-892c-XXXXXXXXXXXX"'

Parameters description table

The following table shows the list of parameters for order details API request.

ParameterData TypeDescriptionExamplesMandatory?
sidStringAccount SID (Security Identifier).HXXXXXXX071USYes
api_keyStringThe API Key generated by kaleyra.io.Ac4XXXXX21fYes
to_numberStringA valid WhatsApp number of the recipient. Ensure that the country code is prefixed to the number. (E164 format).

Note: You can add multiple numbers, separate each number using the comma (,).
919886517012Yes
typeStringMessage format for the message. The type must be 'order_details'.order_detailsYes
channel_nameStringOnly the WhatsApp channel is supported for the WhatsApp catalog template.WhatsAppYes
from_numberStringThe number registered with WhatsApp business from which the message is to be sent. Ensure that the country code is prefixed to the number. (E164 format).918045210677Yes
callback_urlStringSpecifies the callback URL to receive notifications regarding the WhatsApp message status (sent, delivered, read, and failed). For more information related to callback profiles, see the Callback profiles page.
The URL can be accessed publicly.
Click here for an example.
https://webhook.site/e5xxxx3f-cxx4-4xx4-axx8-2exxxxxxxx95No
headerObjectHeader content is displayed on top of a message. If a header is not provided, the API uses an image of the first available product as the header.Message headerNo
footerObjectAn object with the footer of the message. The object contains the following field:

text string: Required, if footer is present. The footer content. Emojis, markdown, and links are supported. Maximum length is 60 characters
Message footerNo
bodyObjectAn object with the message body. The object contains the following field:

text string: Required, if the body is present. This is the content of the message.
Emojis and markdown are supported. Maximum length of the body text is 1024 characters.
They`re good for your eye health tooYes
actionObjectAn action that you want the end customer to perform after reading the message.
This action object contains the following fields:
name string: Required.
Must be "review_and_pay".

Parameters object-See Parameters Object description table for information.
See Parameters Object for informationYes

Parameters object description table

The following table shows the list of parameters for the 'Parameters' object.

ParameterData TypeDescriptionExampleMandatory?
reference_idStringUnique ID given by the business.
It is case sensitive and cannot be an empty string. It can only contain English letters, numbers, underscores, dashes, or dots, and should not exceed 35 characters.

If there is a need to send multiple order_details messages for the same order, add a sequence number in the reference_id (for example, "BM345A-12") to ensure reference_id uniqueness.
Hello_2abcdxyabxyaabYes
typeObjectThe type of goods in this order.
The supported options are digital-goods and physical-goods.
digital-goodsYes
beneficiariesArrayRequired for shipped physical-goods.
Beneficiary information is not shown to users but is needed for legal and compliance reasons.
beneficiary1No

Payment Settings object description table

The following table shows the list of parameters for Payment Settings object.

ParameterDescriptionData TypeExampleMandatory?
typeMust be set to 'payment_gateway'Stringpayment_gatewayYes
payment_gatewayThis object contains the following fields:
1)type: Required.
Unique identifier for an item in the order. Must set this to "razorpay" or "payu", if you have linked your Razorpay or PayU payment gateway to accept payments
2) configuration_name: Required.
The name of the pre-configured payment configuration to use for this order and must not exceed 60 characters. This value must match with a payment configuration set up on the WhatsApp Business Manager as shown here.

Note: When the configuration_name is invalid, the customer will be unable to pay for their order.

3) razorpay/payu object: Optional.
For merchants/partners that want to use notes, receipt(for Razorpay) and UDF fields(for PayU), they can now pass these values in Order Details message and we would use these to create transactoin/order at respective PGs.
Object{
"type": "razorpay",
"configuration_name": "razor_pay_config",
"razorpay": {
"receipt": "receipt-value",
"notes": {
"key1": "value1"
}
Yes

Razorpay Notes and PayU UDF field description table

The following table shows the list of parameters for Razorpay Notes and PayU UDF fields.

ParameterData TypeDescriptionExampleMandatory?
notesObjectOnly supported for Razorpay payment gateway
The object can be key value pairs with maximum 15 keys and each value limits to 256 characters.
“Key1”:
”value1”, “key2”:
”value2”
No
receiptStringOnly supported for Razorpay payment gateway.
Receipt number that corresponds to this order, set for your internal reference. Maximum length of 40 characters supported with minimum length greater than 0 characters.
receipt-valueNo
udf1-4StringOnly supported for PayU payment gateway.
User-defined fields (udf) are used to store any information corresponding to a particular order.
Each UDF field has a maximum character limit of 255.
value1, value2, value3,
value4
No

Order object parameters description table

The following table shows the list of parameters for Order Object.

ParameterData TypeDescriptionExampleMandatory?
statusstringThe only supported value in the order_details message is 'pending' status.pendingYes
typestringThe only supported value is 'quick_pay'.
When this field value is provided, the "Review and Pay" button is hidden and only the "Pay Now" button is shown in the order details bubble.
quick_payNo
itemsObjectThis is an object with the list of items for the order that contains the following fields:

1. retailer_id: string
Optional. Content ID for an item in the order from the catalog.
2. name: string
Required. The item’s name to be displayed. Cannot exceed 60 characters.
3. image: object
Optional. Custom image for the item to be displayed to the user. See item image object for information.
Using this image field will limit the items array to a maximum of 10 items and this cannot be used with retailer_id or catalog_id.
4. amount: amount object with value and offset;
Required. The price per item.
5. sale_amount: amount object
Optional. The discounted price per item. This should be less than the original amount. If included, this field is used to calculate the subtotal amount.
6. quantity: integer
Required. The number of items in this order, this field cannot have a decimal value.
7. country_of_origin: string
Required, if the catalog_id is not present. The country of origin of the product.
8. importer_name: string
Required, if the catalog_id is not present. Name of the importer company.
9. importer_adress: string
Required, if the catalog_id is not present. Address of importer company.
"items":
{
"retailer_id": "6f5gcuihud",
"name": "Bushel of carrots",
"amount":
{
"value": 12.34,
"offset":100
},
"sale_amount": {
"value": 12.34,
"offset": 100
},
"quantity": 1
}
],
"subtotal": {
"value": 12.34,
"offset":100
},
"tax": {
"value": 00,
"offset": 100,
"description": "0%"
},
"shipping": {
"value": 0,
"offset": 100,
"description": "0% off on your order"
},
"discount": {
"value": 0,
"offset": 100,
"description": "0% discount", "discount_program_name": "optional_text"
}
}
}
}
Yes
subtotalObjectThe value must be equal to sum of order.amount.value * order.amount.quantity.
The following fields are part of the subtotal object:

1. offset: integer
Required. This value must be 100 for INR.
Offset is a positive integer used to multiply with the price amount to make it a whole number.
For example, Rs.12.34 has a value 1234 where 100 is the offset value.
"subtotal": {
"value": 12.34,
"offset": 100
}
Yes
taxObjectThe tax information for this order which contains the following fields:

1. offset: integer
Required. Must be 100 for INR
2. value: integer
Required. Positive integer representing the amount value multiplied by offset. For example, ₹12.34 has value 1234
3)description: string
Optional. Max character limit is 60 characters.
"tax": {
"value": 00,
"offset": 100,
"description": "0%"
}
Yes
shippingObjectThe shipping cost of the order. The object contains the following fields:
1)offset: integer
Required. Must be 100 for INR
2)value: integer
Required. Positive integer representing the amount value multiplied by offset. For example, ₹12.34 has value 1234
3)description: string
Optional. Max character limit is 60 characters.
"shipping": {
"value": 0,
"offset": 100,
"description": "0% off on your order"
}
No
discountObjectThe discount for the order. The object contains the following fields:
1)offset: integer
Required. Must be 100 for INR
2)value: integer
Required. Positive integer representing the amount value multiplied by offset. For example, ₹12.34 has value 1234
3)description: string
Optional. Max character limit is 60 characters
4)discount_program_name: string
Optional. Text used for defining the orders with incentive. If an order is incentivized, the merchant needs to define this information. Max character limit is 60 characters.
"discount": {
"value": 0,
"offset": 100,
"description": "0% discount",
"discount_program_name": "optional_text"
}
No
catalog_idObjectThe unique identifier of the Facebook catalog being used by the business.
If you do not provide this field, make sure you provide the following fields inside the items object:
country_of_origin, importer_name, and importer_address.
the-catalog_idNo
expirationObjectThe expiration time for that order. Business must define the following fields inside this object:

1. timestamp: string – UNIX timestamp, after which the order expires.
The minimum validity interval is 300 seconds.
The specified time interval is calculated from the time the order message is delivered.

2. description: string – Text explanation for expiration. Max character limit is 120 characters.
"expiration":
{"timestamp": "1721198395 ",
"description": "cancellation-explanation"
No

Item Image object parameters description table

The following table shows the list of parameters for Item Image Object.

ParameterData TypeDescriptionExampleMandatory?
linkStringA link to the item image that is shown to the end customer.
Must be an image/jpeg or image/png and 8-bit, RGB or RGBA. Follows same requirements as image in media.
@/C:/Users/company_A/Downloads/videoplayback.mp4Yes

Sample Success Response

The following is a sample success message with the status 202 Accepted.

{
    "id": "5d34d9e5-d36c-4ad2-b6d5-f5a4abc460bc",
    "type": "order_details",
    "body": "They`re good for your eye health too",
    "createdDateTime": "2024-04-25 12:20:51+00:00",
    "totalCount": 1,
    "data": [
        {
            "message_id": "5d34d9e5-d36c-4ad2-b6d5-f5a4abc460bc:0",
            "recipient": "xxxx8651xxxx"
        }
    ],
    "error": {}
}

Sample Error Response

The following is a sample error response.

{
    "code": "E413",
    "message": "Invalid/incorrect inputs",
    "data": [],
    "error": {
        "body": "For the type order_details, body field is mandatory and cannot be empty!"
    }
}

Order status API

An order status API request sends the latest order status to the end customer with a reference to the original order_details message.

Base URL

https://api.kaleyra.io/v1/<SID>

Request format

The following is the request format to send an order status update message.

curl --location --request POST '<base_url>/messages' \
--header 'api-key: A6292e49b8d313ce3f53613dad588dda9' \
--form 'to="<to number>"' \
--form 'type="order_status"' \
--form 'channel="WhatsApp"' \
--form 'from="<from number>"' \
--form 'body="<body text>"' \
--form 'footer="Footer"' \
--form 'action="{
      \"name\": \"review_order\",
      \"parameters\": {
        \"reference_id\": \"<reference id>\",
        \"order\": {
          \"status\": \"<status\",
          \"description\": \"<status description>\"
        }
      }
    }"' \
--form 'callback_url="<callback url>"'

Sample API request

The following is a sample API request to send an order status update message.

curl location --request POST 'https://api.kaleyra.io/v1/xxxx177871xxxxxx/messages' \
--header 'api-key: xxxxxe49xxxx13ce3f53613dad58xxxxx' \
--form 'to="+9199865xxxxx"' \
--form 'type="order_status"' \
--form 'channel="WhatsApp"' \
--form 'from="+xxxx45210xxx"' \
--form 'body="Hello user"' \
--form 'footer="Footer"' \
--form 'action="{
      \"name\": \"review_order\",
      \"parameters\": {
        \"reference_id\": \"Hello_2hyabfabkeabzahi\",
        \"order\": {
          \"status\": \"partially_shipped\",
          \"description\": \"Order status update\"
        }
      }
    }"' \
--form 'callback_url="https://webhook.site/e79a769e-e523-4662-a3cc-a4ae09a9045d"'

Parameters description table

The following table shows the parameter descriptions for order status API request.

ParameterData TypeDescriptionExampleMandatory?
sid StringAccount SID (Security identifier)HXXXXXXX071USYes
api_keyString The API key generated by kaleyra.ioAc4XXXXX21fYes
to_number StringA valid WhatsApp number of the recipient. Ensure that the country code is prefixed to the number. (E164 format).
Note: You can add multiple numbers, separate each number using the comma (,).
xx988xxx70xxYes
typeStringMessage format for the message. The type must be 'order_details'.order_statusYes
from_number StringThe number registered with WhatsApp business from which the message is to be sent. Ensure that the country code is prefixed to the number. (E164 format).xx80452xxxxxYes
body StringMessage bodyHello UserYes
footer StringAn object with the footer of the message. The object contains the following fields:
text string.
Required if footer is present. The footer content. Emojis, markdown, and links are supported. Maximum length is 60 characters.
Footer text No
reference_idStringThe ID sent by the business in the order_details messageHello_2hyabfabkeabzahiYes
order ObjectThis object contains the following fields:

1. status: string
Required. The represents the new order status.
The status values can be:
processing, partially_shipped, shipped, completed, canceled.
2. description: string
Optional. This is the text that shows the status related information in the order_details.
This is useful while sending cancellation.
Maximum character limit is 120 characters.
"order": {
"status": "partially_shipped",
"description": "Order status update"
Yes
actionObjectAn action object you want the user to perform after reading the message. This action object contains the following fields:

1. name:string
Required. Must be "review_and_pay".

2. parameters object
See, [Parameters_object_description](Parameters description table) table for information.
'action="
{

"name": "review_order",

` "parameters": {

`"reference_id": "Hello_2hyabfabkeabzahi",

"order": {

"status": "partially_shipped",

"description": "Order status update" }}
Yes
Callback_
profile_id
StringThe callback ID created for WhatsApp channel to receive updates about the message delivery status.

Note: You can also use callback_url to get updates about the message delivery status to the specified URL.
xx_b86b078e-046b-4565-892c-XXXXXXXXXXXXNo

Sample success response

The following is a sample success response for order status API request.

{
    "id": "cbc7725a-8199-4e32-8af0-25e7ed261c52",
    "type": "order_status",
    "body": "Hello user",
    "createdDateTime": "2024-05-02 13:39:15+00:00",
    "totalCount": 1,
    "data": [
        {
            "message_id": "cbc7725a-8199-4e32-8af0-25e7ed261c52:0",
            "recipient": "xx99865xxxxx"
        }
    ],
    "error": {}
}

Sample error response

The following shows an error response for order status API request.

{
    "code": "E413",
    "message": "Invalid/incorrect inputs",
    "data": [],
    "error": {
        "type": "type field is mandatory"
    }
}

Payment status API

A payment status API request fetches the status about the order payment made by the customer for the businesses.

📘

Note:

The message status webhooks also retrieve the payment status updates. This can be shared with the business via callbacks.

Base URL

https://api.kaleyra.io/v1/<SID>

API request format

The following is the GET request format to obtain the customer payment status.

curl --location --request GET 'https://api.kaleyra.io/v1/<SID/ <phone_no>/<payment_configuration>/reference_id'

where the 'payment configuration' and the 'reference ID' are the same values sent for the order details message and the phone number is the customer WhatsApp phone number for which the payment status is fetched.

Sample API request

The following is a sample API that uses 'GET' request to fetch payment update.

curl --location --request GET 'https://api.kaleyra.io/v1/v1/xxxx17787121xxxx/whatsapp/payment/status?number=%2xx18045210677&reference_id=Hiabqidaq_1abcdeabyzqabq&payment_config_id=razor_pay_config' \ 

--header 'api-key: A6292e49b8d313ce3f53613dad588dda9' 

Sample success response

The following sample success response is shown for the payment update GET request.

{
    "code": "WA200",
    "message": "Request Processed Successfully",
    "data": {
        "payments": [
            {
                "amount": {
                    "offset": 100,
                    "value": 100
                },
                "currency": "INR",
                "reference_id": "Hiabqidaq_1abcdeabyzqabq",
                "status": "CAPTURED",
                "transactions": [
                    {
                        "amount": {
                            "offset": 100,
                            "value": 100
                        },
                        "created_timestamp": 1708424916,
                        "currency": "INR",
                        "id": "order_Nd6JAsbEscE2gY",
                        "status": "success",
                        "type": "razorpay",
                        "updated_timestamp": 1708424916
                    }
                ]
            }
        ]
    },
    "error": {}
}

Payment Refund API

A business can refund the order payment money back to the customer under specific business conditions.

Base URL

https://api.kaleyra.io/v1/<SID>

Request format

The following is the request format to refund the order payment money to the customer.

curl --location --request POST '<base_url>/whatsapp/payment/refund' \
--header 'api-key: xxxxxe49b8dxxxxe3f53613dad5xxxxxx' \
--header 'Content-Type: application/json' \
--data '{
    "reference_id": "<reference id>",
    "speed": "instant",
    "payment_config_id": "razor_pay_config",
    "currency": "INR",
    "value": "<value>",
    "offset": "<value>",
    "number": "<from no>"
}' 

Sample request format

The following sample API request refunds the order payment money to the customer.

curl --location --request POST 'https://api.kaleyra.io/v1/whatsapp/payment/refund' \
--header 'api-key: xxxxxe49b8d313ce3f53613dad5xxxxxx' \
--header 'Content-Type: application/json' \
--data '{
    "reference_id": "Hiabqidaq_1abcdeabyzqabq",
    "speed": "instant",
    "payment_config_id": "razor_pay_config",
    "currency": "INR",
    "value": "100",
    "offset": "100",
    "number": "+xxxx45210xxx"
}' 

Parameter description table

The following table shows the parameter description for Refund API.
Refer to the Order Details parameters description tables for the remaining parameters used in the API request.

ParameterData TypeDescriptionExampleMandatory?
sidStringAccount SID (Security Identifier).HXXXXXXX071USYes
api_keyStringThe API Key generated by kaleyra.io.Ac4XXXXX21fYes
reference_id StringUnique ID given by the business for every transaction.

It is case sensitive and cannot be an empty string. It can only contain English letters, numbers, underscores, dashes, or dots, and should not exceed 35 characters.

If there is a need to send multiple order_details messages for the same order, add a sequence number in the reference_id (for example, "BM345A-12") to ensure reference_id uniqueness.
Hello_2abcdxyabxyaab
SpeedStringThe time at which the refund should be done.
For Instant speed, the refund is done immediately.
For normal speed, the refund may be done with a time delay.
Instant, NormalYes
Payment_
config_id
StringThe name of the pre-configured payment configuration to use for this order and must not exceed 60 characters. This value must match with a payment configuration set up on the WhatsApp Business Manager
Note: When the configuration_name is invalid, the customer will be unable to pay for their order.
xxxxxo_2abcdxyabxyaabYes
currencyStringName of the currency used in the payment transaction.INRYes
valueIntegerThe refund amount to be paid to the customer.100Yes
offsetIntegerThe offset value is to be calibrated with the currency value.100Yes
numberIntegerThe customer number to which the refund has to be made.xx99xxx17xx6Yes

Sample success message

The following is a sample success message for the Refund API request.

{
  "id": "refund-id",
  "status": "pending",
  "speed_processed": "normal"}

Sample error message

The following is a sample error message for the Refund API request.

{
    "code": "WA400",
    "message": "valid refund speed required, accepted values are normal/instant",
    "data": [],
    "error": {
        "speed": "valid refund speed required, accepted values are normal/instant"
    }
}

What’s Next