Message overrides
Message overrides (also referred to as passthrough) are a way to leverage channel-specific features that are not natively supported by the messaging platform API. For example, the Apple Messages for Business channel supports a variety of specific message types such as list picker and time picker which are not supported in any other channels. Message overrides allow the integrator to craft a raw payload to send to the third-party channel's API, and the Zendesk messaging platform will "pass it through", automatically including any required authentication.
Specifying overrides
Post message API
To send a message override using the Post Message API, you must first craft a valid message of any type, and then specify an additional override
parameter for the channels you would like to use passthrough. Under each channel name key, the payload
field should contain the exact structure expected by the third-party channel's API. If the message is dispatched to a channel for which an override has been supplied, then the override will be used instead of the message's content
. For any channel that does not have a specified override, the message's content
will be used as usual.
For example, to send an override for whatsapp
you would specify override.whatsapp.payload
as follows:
{
"author": {
"type": "business"
},
"content": {
"type": "text",
"text": "Hello there"
},
"override": {
"whatsapp": {
"payload": {
"type": "text",
"text": {
"body": "Goodbye"
}
}
}
}
}
The above example depicts a simple override example, where the text message will render as "Hello there" on every channel except WhatsApp, where it would instead render the text "Goodbye". Note that all fields under payload
in the above example (type
, text
, body
) are WhatsApp-specific fields, exactly as the WhatsApp API would accept them.
Notification API
Overrides can also be specified when using the notification API. Reusing the same whatsapp
example from above, the call to the notification API would look like this (note that the notification API is using v1
API naming conventions):
curl https://{subdomain}.zendesk.com/sc/v1.1/apps/{app_id}/notifications \
-X POST \
--user '{key_id}:{secret}' \
-H 'content-type: application/json' \
-d '{
"destination": {
"integrationId": "5e1f438bf99fb467810c0ab9",
"destinationId": "+15145555333"
},
"author": {
"role": "appMaker"
},
"message": {
"type": "text",
"text": "Hello there"
},
"override": {
"whatsapp": {
"payload": {
"type": "text",
"text": {
"body": "Goodbye"
}
}
}
}
}'
Reserved fields
In order to ensure the message is correctly delivered, Zendesk will automatically set some fields in the payload for you. For example, many messaging channels require the destination user's id to be specified in the request body. To avoid the possibility of an incorrect recipient being supplied, Zendesk will reserve that field for its own usage, and inject the proper value when delivering the message. If you supply any of the reserved fields in your override payload, they will be ignored and replaced according to the table below:
Channel type | Reserved key | Reserved behaviour |
---|---|---|
apple | destinationId | The unique identifier of the user. Zendesk will populate this value based on the target user |
apple | id | A unique identifier (in UUID format) for the message. Zendesk will automatically generate and populate this value |
apple | sourceId | The unique identifier of the calling business. Zendesk will populate this value based on the configured integration |
apple | v | Specifies the version of the message schema to use. Zendesk will automatically set this to 1 |
recipient | The identity of the user on Instagram. Zendesk will populate this value based on the target user | |
line | to | The user's id on LINE. Zendesk will populate this value based on the target user |
messenger | recipient | The identity of the user on Facebook. Zendesk will populate this value based on the target user |
messaging_product | Zendesk will automatically set this to whatsapp | |
template.namespace | This field is no longer supported in WhatsApp's cloud API. Zendesk will remove this field from the payload | |
to | The username of the user on WhatsApp. Zendesk will populate this value based on the target user |
Capabilities check
When sending message overrides to the apple
channel, the capabilities of the end-user's device and OS version can affect their ability to receive certain message types. Certain devices or versions offer unique features such as time pickers or list pickers, while others may not have support for rendering these types of messages.
The withCapabilities
optional parameter can be used to specify when your override should apply, based on the capabilities of the end-user's device. If the user does not have the desired capabilities, the message's override payload will be ignored, and the content
field will be sent to the user instead.
Here is an example of a time picker message with a fallback for devices that do not have proper support for the message type:
{
"author": {
"type": "business"
},
"content": {
"type": "text",
"text": "Time Picker fallback"
},
"override": {
"apple": {
"withCapabilities": ["TIME"],
"payload": {
"interactiveData": {
"bid": "com.apple.messages.MSMessageExtensionBalloonPlugin:0000000000:com.apple.icloud.apps.messages.business.extension",
"data": {
"event": {
"identifier": "reservation-id",
"timeslots": [
{
"duration": 3600,
"identifier": "7-am",
"startTime": "2018-10-24T07:00+0000"
},
{
"duration": 3600,
"identifier": "8-am",
"startTime": "2018-10-24T08:00+0000"
}
],
"timezoneOffset": 400,
"title": "Available Reservations"
},
"mspVersion": "1.0",
"requestIdentifier": "appointment-selector-1"
},
"receivedMessage": {
"style": "icon",
"title": "Make a reservation"
},
"replyMessage": {
"style": "icon",
"title": "Your reservation"
}
},
"type": "interactive"
}
}
}
}
In this example:
- If the user’s device supports time pickers, they will receive a specific reservation message with selectable time slots.
- If it doesn't, they will see the fallback message: "Time Picker fallback".
Examples
We've crafted some example override payloads to help you get started. You can refer to the JSON samples below for guidance on how to send various third-party-specific message types.
Apple Messages for Business
Rich links
A rich link is automatically built for text messages containing a single URL inside. However, you can manually define rich links using overrides. Rich links can contain an image or video preview. For the image, you must provide the image in base64 encoding. You can obtain this through a library or 3rd party website.
Example of a rich link with an image preview:
{
"author": {
"type": "business"
},
"content": {
"type": "text",
"text": "Hello there"
},
"override": {
"apple": {
"payload": {
"body": "https://zendesk.com/",
"type": "richLink",
"richLinkData": {
"url": "https://zendesk.com/",
"title": "Title",
"assets": {
"image": {
"data": "<put base64 encoded image here>",
"mimeType": "image/jpg"
}
}
}
}
}
}
}
Example of a rich link with a video preview:
{
"author": {
"type": "business"
},
"content": {
"type": "text",
"text": "Hello there"
},
"override": {
"apple": {
"payload": {
"body": "https://zendesk.com/",
"type": "richLink",
"richLinkData": {
"url": "https://zendesk.com/",
"title": "Title",
"assets": {
"video": {
"url": "https://images.apple.com/media/us/homepod/2018/98e6cc1a_37ec_4e69_b717_9e58c71e1937/films/expand/homepod-expand-tpl-cc-us-20180306_1280x720h.mp4",
"mimeType": "video/mp4"
}
}
}
}
}
}
}
Attachments
For some message types, it might be necessary to send attachments. In such cases, you should refer to the Apple documentation for instructions on how to upload those resources to Apple, and pass the object reference to Zendesk using an override.
Example:
{
"author": {
"type": "business"
},
"content": {
"type": "text",
"text": "Hello there"
},
"override": {
"apple": {
"payload": {
"attachments": [
{
"decryption-key": "00260450bb05cd4597a8ae706199f685c0f6fafee4b6c3e1375771c472f4149e29",
"file-size": "10159",
"mime-type": "image/png",
"mmcs-owner": "M39303332653438322d396338342d313165372d613330332d633735336231626666366661.C01USN00",
"mmcs-signature-base64": "AXG58GIo7xWZz7gRXZH2liWuzVah",
"mmcs-url": "https://p97-content.icloud.com",
"name": "icon.png"
}
],
"body": "goodbye",
"type": "text"
}
}
}
}
List picker
A sample list picker payload:
{
"author": {
"type": "business"
},
"content": {
"type": "text",
"text": "Hello there"
},
"override": {
"apple": {
"payload": {
"interactiveData": {
"bid": "com.apple.messages.MSMessageExtensionBalloonPlugin:0000000000:com.apple.icloud.apps.messages.business.extension",
"data": {
"images": [],
"listPicker": {
"multipleSelection": true,
"sections": [
{
"items": [
{
"identifier": "taco-appetizer",
"order": 0,
"style": "default",
"subtitle": "Great tacos",
"title": "Tacos"
},
{
"identifier": "burrito-appetizer",
"order": 1,
"style": "default",
"subtitle": "Good burritos",
"title": "Burritos"
}
],
"order": 0,
"title": "Appetizers"
},
{
"items": [
{
"identifier": "taco-meal",
"order": 0,
"style": "default",
"subtitle": "Great tacos",
"title": "Tacos"
},
{
"identifier": "burrito-meal",
"order": 1,
"style": "default",
"subtitle": "Good burritos",
"title": "Burritos"
}
],
"order": 1,
"title": "Meals"
}
]
},
"mspVersion": "1.0",
"requestIdentifier": "food-picker-1"
},
"receivedMessage": {
"style": "small",
"subtitle": "Let's eat!",
"title": "FoodPicker"
},
"replyMessage": {
"style": "large",
"subtitle": "Nutrition",
"title": "Selected food"
}
},
"type": "interactive"
}
}
}
}
Time picker
A sample time picker payload:
{
"author": {
"type": "business"
},
"content": {
"type": "text",
"text": "Hello there"
},
"override": {
"apple": {
"payload": {
"interactiveData": {
"bid": "com.apple.messages.MSMessageExtensionBalloonPlugin:0000000000:com.apple.icloud.apps.messages.business.extension",
"data": {
"event": {
"identifier": "reservation-id",
"timeslots": [
{
"duration": 3600,
"identifier": "7-am",
"startTime": "2018-10-24T07:00+0000"
},
{
"duration": 3600,
"identifier": "8-am",
"startTime": "2018-10-24T08:00+0000"
}
],
"timezoneOffset": 400,
"title": "Available Reservations"
},
"mspVersion": "1.0",
"requestIdentifier": "appointment-selector-1"
},
"receivedMessage": {
"style": "icon",
"title": "Make a reservation"
},
"replyMessage": {
"style": "icon",
"title": "Your reservation"
}
},
"type": "interactive"
}
}
}
}
Authentication message
A sample authentication message payload:
{
"author": {
"type": "business"
},
"content": {
"type": "text",
"text": "Hello there"
},
"override": {
"apple": {
"payload": {
"type": "interactive",
"v": 1,
"locale": "en_us",
"interactiveData": {
"bid": "com.apple.messages.MSMessageExtensionBalloonPlugin:0000000000:com.apple.icloud.apps.messages.business.extension",
"data": {
"version": "2.0",
"requestIdentifier": "1a50ecd2-a0bb-467c-a77c-1932033e8309",
"authenticate": {
"oauth2": {
"responseType": "code",
"scope": ["r_liteprofile"],
"redirectURI": "https://example.com/auth/linkedin/callback"
}
}
},
"receivedMessage": {
"title": "Lets Sign In"
},
"replyMessage": {
"title": "Sign In Completed"
}
}
}
}
}
}
Business flow
- Businesses must supply authentication details about the OAuth provider, including authentication endpoint URLs, client identifier, and business information to their AMB account.
- Create an integration with an
authenticationMessageSecret
value. This value will be used by Zendesk to sign the state. The state is a value required by Apple to perform the authentication flow. Securely store this value as it cannot be retrieved by the API. - In order to perform the authentication flow, the business needs to expose an endpoint that will be the same route as the
redirectURI
. - After the end-user authenticates, the endpoint created by the business will be called and they will be responsible for validating the state value. This can be done by decoding the state value using the
authenticationMessageSecret
. The state value is a JWT containing theconversationId
. Using the List Participants API, you can query which user is participating in the conversation. You can then merge the user on Apple with an existing authenticated user in Zendesk using the v1 merge users api. This will merge a user with an existing one in your Zendesk Agent Workspace. - Redirect the end-user to the following URL scheme after the auth flow is complete in order to close the webview. See Finishing the New Authentication Flow for more details.
// URL Schema
message-auth://?
Examples:
messages-auth://?status=success
messages-auth://?status=failure&error_code=400
- The authentication webhook event is sent when closing the window with the status as success or failure. Zendesk messaging will publish the webhook via
passthrough:apple:interactive
containing the status of the authentication.
Custom extension
A sample custom extension (e.g. iMessage App) payload:
{
"author": {
"type": "business"
},
"content": {
"type": "text",
"text": "Hello there"
},
"override": {
"apple": {
"payload": {
"attachments": [
{
"decryption-key": "00260450bb05cd4597a8ae706199f685c0f6fafee4b6c3e1375771c472f4149e29",
"file-size": "10159",
"mime-type": "image/png",
"mmcs-owner": "M39303332653438322d396338342d313165372d613330332d633735336231626666366661.C01USN00",
"mmcs-signature-base64": "AXG58GIo7xWZz7gRXZH2liWuzVah",
"mmcs-url": "https://p97-content.icloud.com",
"name": "icon.png"
}
],
"interactiveData": {
"URL": "?options=tacos,burritos",
"appId": 34554,
"appName": "Your App Name",
"bid": "com.apple.messages.MSMessageExtensionBalloonPlugin:ABCD5678:com.yourDomain.MessagesExtension",
"receivedMessage": {
"imageSubtitle": "Image Subtitle",
"imageTitle": "Image Title",
"secondarySubtitle": "Trailing Caption",
"subtitle": "Subcaption",
"tertiarySubtitle": "Trailing Subcaption",
"title": "Caption"
},
"useLiveLayout": false
},
"type": "interactive"
}
}
}
}
Apple Pay payment requests
Prerequisites for sending ApplePay payment requests include:
- Apple Pay Merchant ID including:
- signed certificate (
developer.apple.com
) - a verified Merchant Domain
- signed certificate (
- Apple Pay Merchant ID configured on the Messages for Business account (
register.apple.com
) - Valid
MerchantSession
object, obtained from Apple's API
Override should be constructed according to the Apple specification here.
Responses are sent to the function-specific URL(s) specified in the request (in the interactiveData.data.payment.endpoints
object).
App Clips
An App Clip is a small, lightweight version of a full application. It allows users to quickly access and use specific functionalities of an app without needing to download and install the entire application.
Prerequisites for sending App Clips include:
- A valid URL has App Clips elements on its web page. See details here.
Ensure the version
property is a string with at least one decimal precision. Otherwise, the Apple Messages for Business API will return an error.
If the App Clip is not available in the US App Store, add an optional storeRegion
link property. See the construct payload API for more details.
{
"author": {
"type": "business"
},
"content": {
"type": "text",
"text": "Hello there"
},
"override": {
"apple": {
"payload": {
"type": "link",
"link": {
"url": "https://zendesk.com/",
"storeRegion": "US" // optional
},
"version": "1.0"
}
}
}
}
LINE
Flex message
A sample flex message payload:
{
"author": {
"type": "business"
},
"content": {
"type": "text",
"text": "Hello there"
},
"override": {
"line": {
"payload": {
"messages": [
{
"type": "flex",
"altText": "This is a Flex Message",
"contents": {
"type": "bubble",
"body": {
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "Left"
},
{
"type": "text",
"text": "Right"
}
]
}
}
}
]
}
}
}
}
Facebook Messenger
Receipt
The example below shows how a receipt message can be sent using an override:
{
"author": {
"type": "business"
},
"content": {
"type": "text",
"text": "Hello there"
},
"override": {
"messenger": {
"payload": {
"message": {
"attachment": {
"type": "template",
"payload": {
"template_type": "receipt",
"recipient_name": "John",
"order_number": "111111111111",
"currency": "USD",
"payment_method": "Visa 2345",
"order_url": "http://petersapparel.parseapp.com/order?order_id=123456",
"timestamp": "1428444852",
"address": {
"street_1": "1 Apple Highway",
"street_2": "",
"city": "Mangle city",
"postal_code": "94025",
"state": "CA",
"country": "US"
},
"summary": {
"subtotal": 75.0,
"shipping_cost": 4.95,
"total_tax": 6.19,
"total_cost": 56.14
},
"adjustments": [
{
"name": "New Customer Discount",
"amount": 20
},
{
"name": "$10 Off Coupon",
"amount": 10
}
],
"elements": [
{
"title": "Fall Coat",
"subtitle": "Soft and Luxurious",
"quantity": 2,
"price": 50,
"currency": "USD",
"image_url": "https://smooch.io/static_assets/images/shoplifter/double-breasted-coat.jpg"
}
]
}
}
}
}
}
}
}
Template messages
Here is an example of how to send an approved template message on WhatsApp:
{
"author": {
"type": "business"
},
"content": {
"type": "text",
"text": "Hello there"
},
"override": {
"whatsapp": {
"payload": {
"type": "template",
"template": {
"name": "goodbye_template",
"language": {
"policy": "deterministic",
"code": "en_US"
}
}
}
}
}
}
Message template reconstruction
Message template reconstruction enables the conversation history stored by Zendesk to reflect the message as seen by the user, so that all the software connected to the conversation (e.g. Agent Workspace or a bot platform) have the full context of what was sent.
When sending a message template on WhatsApp, you can optionally use message template reconstruction to avoid having to specify your own fallback content
field when sending the message. Instead, Zendesk will parse the specified override payload and leverage WhatsApp's Business Management API to connect to your WhatsApp Business Manager and read the contents of the template. The message's content
field would then be "reconstructed" from the template details, and will be automatically populated for you.
To leverage message template reconstruction, rather than specifying an override
property, you would instead use the content
field in conjunction with the schema
property. Specify a schema
of whatsapp
, and replace the message content
with your WhatsApp template payload, exactly as it would have appeared under override.whatsapp.payload
. Here is an example:
{
"author": {
"type": "business"
},
"schema": "whatsapp",
"content": {
"type": "template",
"template": {
"name": "goodbye_template",
"language": {
"policy": "deterministic",
"code": "en_US"
}
}
}
}
Zendesk will then attempt to reconstruct a valid message according to the following sequence, replacing any template variables with the values provided in the API request body:
- If the template contains header text, it will be inserted into the
text
property of the reconstructed message - If the template contains a header image, the
type
of the message will be set toimage
, and the image URL will be set as the message'smediaUrl
- If the template contains a file in the header, the
type
of the message will be set tofile
, and the document URL will be set as the message'smediaUrl
- If the template contains body text, it will be inserted into the
text
property of the reconstructed message, separated from the header text (if it exists) by two newline characters - If the template contains footer text, it will be inserted into the
text
property of the reconstructed message, separated from preceding text (if it exists) by two newline characters - For each button in the template:
- Reply buttons will append an action of type
reply
to the message'sactions
array - Phone number buttons will append an action of type
link
to the message'sactions
array - URL buttons will append an action of type
link
to the message'sactions
array
- Reply buttons will append an action of type
The reconstructed text
of the message will use one of the available translations of the message template; the selected language will be determined according to the following order of precedence:
- The
language.code
specified in the message override payload - The fallback language (
hsmFallbackLanguage
) configured via the Integration API - The first variation of
en_*
found
Full reconstruction example:
Given the following template configuration:
Component type | Content |
---|---|
Header image | {{some_image}} |
Body | Your order {{1}} for a total of {{2}} is confirmed. |
Footer | Thank you for shopping with us! |
Button | Thanks! |
Button | New order |
and the following override payload:
{
"author": {
"type": "business"
},
"schema": "whatsapp",
"content": {
"type": "template",
"template": {
"name": "template_example",
"language": {
"policy": "deterministic",
"code": "en_US"
},
"components": [
{
"type": "header",
"parameters": [
{
"type": "image",
"image": {
"link": "https://example.com/image.png"
}
}
]
},
{
"type": "body",
"parameters": [
{
"type": "text",
"text": "#1234"
},
{
"type": "text",
"text": "$22.47"
}
]
},
{
"type": "button",
"sub_type": "quick_reply",
"index": "0",
"parameters": [
{
"type": "payload",
"payload": "THANK_YOU"
}
]
},
{
"type": "button",
"sub_type": "quick_reply",
"index": "1",
"parameters": [
{
"type": "payload",
"payload": "PLACE_NEW_ORDER"
}
]
}
]
}
}
}
Zendesk would reconstruct the following message content
:
{
"author": {
"type": "business"
},
"content": {
"type": "image",
"mediaUrl": "https://example.com/image.png",
"text": "Your order #1234 for a total of $22.47 is confirmed.\n\nThank you for shopping with us!",
"actions": [
{
"type": "reply",
"text": "Thanks!",
"payload": "THANK_YOU"
},
{
"type": "reply",
"text": "New order",
"payload": "PLACE_NEW_ORDER"
}
]
}
}
List Message
List Messages are messages that include a menu of up to 10 options for users to choose from. For example, you can use List Messages for:
- A restaurant online menu
- Appointment reservation available times
- Available online courses
To create a list message, use the interactive
object of type list
. Options are listed inside an action
object. The action
object must contain at least one section
field. You can have a maximum of ten section
fields. Each section
field must have at least one rows
object that describes the option.
Example:
{
"author": {
"type": "business"
},
"content": {
"type": "text",
"text": "Hello there"
},
"override": {
"whatsapp": {
"payload": {
"type": "interactive",
"interactive": {
"type": "list",
"body": {
"text": "What would you like for dinner?"
},
"action": {
"button": "Make your selection",
"sections": [
{
"title": "Main course",
"rows": [
{
"id": "tacos",
"title": "Tacos",
"description": "Pork"
},
{
"id": "guacamole",
"title": "Guacamole",
"description": "Vegetarian"
}
]
},
{
"title": "Drink",
"rows": [
{
"id": "tea",
"title": "Tea",
"description": "Oolong"
},
{
"id": "soda",
"title": "Soda",
"description": "Stewart's Key Lime"
}
]
}
]
}
}
}
}
}
}
The user's selection is returned as text, including a quotedMessage
reference to the original message sent. Since the override
payload is not persisted alongside the message, the WhatsApp syntax used in the initial request is not available.
When the user makes a selection, the resulting conversation:message
webhook should look similar to this:
{
"app": {
"id": "5698edbf2a43bd081be982f1"
},
"webhook": {
"id": "5e554d2cac66fb73a3c01869",
"version": "v2"
},
"events": [
{
"id": "0ca7d56ba7b2e081e479fe9e",
"type": "conversation:message",
"createdAt": "2020-02-25T18:06:37.547Z",
"payload": {
"conversation": {
"id": "13f32c6cf7d0d38e6e0eba93",
"type": "personal",
"brandId": "360004166971",
"activeSwitchboardIntegration": {
"id": "62261ff6aa72f900ef123e1d",
"name": "zd-agentWorkspace",
"integrationId": "62261ff41b74b670c3f64c2f",
"integrationType": "zd:agentWorkspace"
}
},
"message": {
"id": "5e55622dac66fb73a3c01877",
"received": "2020-02-25T18:06:37.547Z",
"content": {
"type": "text",
"text": "Tacos - Pork",
"payload": "tacos"
},
"author": {
"type": "user",
"userId": "cbec0073faeda4dfcd21d0f1",
"displayName": "WhatsApp User",
"user": {
"id": "cbec0073faeda4dfcd21d0f1"
}
},
"source": {
"type": "whatsapp",
"integrationId": "5d83a25d9916b64a83ed25e8",
"originalMessageId": "ABGGFDhgAzJ_AhDcs60XVOVFceznj0Re54wL",
"originalMessageTimestamp": "2020-02-25T18:06:37.000Z"
},
"quotedMessage": {
"type": "message",
"message": {
"id": "6143599e72ad5800cb150832",
"received": "2020-02-25T18:05:00.531Z",
"author": {
"avatarUrl": "https://www.gravatar.com/avatar/00000000000000000000000000000000.png?s=200&d=mm",
"type": "business"
},
"content": {
"type": "text",
"text": "Hello there"
},
"source": {
"type": "api:conversations"
}
}
}
}
}
}
]
}