In Zendesk messaging, most messages are generated automatically as actions are taken by an end user, agent or AI Agent. The agent might craft a message by hand using the composer in the Agent Workspace UI, or the end user might send a message using a third-party messaging channel such as WhatsApp. However, for more advanced use cases, messages can also be sent programmatically using the messaging platform APIs. In this guide, we'll explain the various ways your software can send messages on behalf of users or on behalf of your business.

Replying to an existing conversation

Most conversations are initiated by an end user reaching out to your business. For example, the user might start a conversation by sending a message to your Facebook page, or by launching one of the messaging SDKs embedded in your website or mobile app. When receiving messages over webhooks, the conversation id is included as part of the payload. This id can then be stored and reused to send messages back to the user.

Regardless of how the conversation id was obtained, the Post Message API will allow you to send a message in a specific conversation:

const apiInstance = new SunshineConversationsClient.MessagesApi()const data = new SunshineConversationsClient.MessagePost()
data.author = {  type: "business"}data.content = {  type: "text",  text: "Hello world"}
await apiInstance.postMessage("{appId}", "{conversationId}", data)

In the code above, content contains the message payload, while the author object is used to identify the "sender" of the message. To learn more about the various parameters of this API, see Post Message.

Initiating a new conversation

Conversations can be initiated by the business in order to proactively reach out to a user who may not have contacted you before. Before sending a message, you will need to create a conversation for the interaction, and link a channel to determine how the message should be delivered to the user. The notification API can also be used to perform all of these operations with a single API call. Learn more about this use case in the outbound messaging guide.

Users on multiple channels

In most cases, a conversation will only be linked to a single channel, making it obvious to know where messages will go when the business replies. In general, you should not need to worry about which channel(s) messages will be sent to. The messaging platform will make a best effort to ensure the user will see the message. This section explains in detail how channels are selected for delivery, but you may also skip ahead to sending typing activities if this information is not important for your integration.

Automatic message delivery

There are a number of ways in which a conversation can be linked to more than one channel at a time. For example, if a user on the Web Widget opts to continue their conversation on a social messaging channel, the result will be a conversation with two or more channels connected. The linked channels for a given conversation are stored on the participant object, specifically in the clientAssociations array. The first entry in the clientAssociations array is the user's "preferred channel" (explained below). You can retrieve the list of participants for a conversation using the List Participants API.

Business messages are delivered according to an order of priority determined by the user's most recent activity on each channel. The user's "preferred channel" will be the channel where they were most recently active. In the previous example of a user on the Web Widget continuing their conversation on WhatsApp, the preferred channel might change as time goes on:

  1. When the user and conversation are initially created by the Web Widget, there is only one client linked. The preferred channel at this point is the user's sdk client, specifically their web device.
  2. When the user completes the flow to link their conversation to WhatsApp, a new client of type whatsapp is added, and whatsapp now becomes the preferred channel.
  3. As the user continues sending and receiving messages over WhatsApp, it remains the preferred channel.
  4. Some time later, the user might go back to the Web Widget and send another message in the conversation. If this happens, the preferred channel will be updated once again to sdk. Note that just viewing a conversation will not update the preferred channel - the user must perform a write operation on the conversation.

The following decision tree is used to determine where business messages should go. The goal of this flow is to give the best chance of the user seeing the message, while avoiding sending push notifications when it's not necessary:

  1. First, send the message to the user's preferred channel.
    • Depending on the capabilities of the channel, this may or may not trigger a push notification for the user.
    • Social messaging channels all support sending a push notification to the end user when a new message is received, so these channels are considered to be "push capable".
    • Zendesk's iOS and Android SDKs are also considered to be "push capable". You can configure push credentials for your mobile app in the Admin Center.
    • The Web Widget and Unity SDKs do not support configuring push notifications, so these channels are considered to be "not push capable".
  2. If the preferred channel was push capable, then no further delivery is attempted.
  3. If the preferred channel was not push capable, a 5 minute time window is allowed for the user to read the message.
  4. If the message is still unread after 5 minutes, delivery will be re-attempted using the second-best channel. This means the second most recently used channel that is push capable.

Here's a visual representation of the automatic message delivery logic:

Built-in message delivery logic

Targeting a specific channel

To bypass the automatic delivery logic, you may target a specific channel when sending a message instead. The Post Message API accepts a destination object which can be used to target a channel with either an integrationType or integrationId. The conversation must be linked to a client matching the supplied destination criteria, otherwise an error is returned.

const apiInstance = new SunshineConversationsClient.MessagesApi()const data = new SunshineConversationsClient.MessagePost()
data.author = {  type: "business"}data.content = {  type: "text",  text: "Hello world"}
data.destination = new SunshineConversationsClient.Destination()data.destination.integrationType = "whatsapp" // the targeted channel
await apiInstance.postMessage("{appId}", "{conversationId}", data)

Note: For the Zendesk Web Widget and SDKs, the conversation history is used directly as the source of truth for messages. All messages will be visible by the user, regardless of where they were "delivered". Targeting a social channel for delivery will not prevent the message from being displayed on any linked SDKs, but it will suppress the dispatching of push notifications.

Silent Messages

In some cases the conversation with a user might proceed through an external channel, outside of Zendesk messaging. You can backfill messages into a conversation without triggering any further message delivery by marking the message as "silent". To send a silent message, provide a destination object with integrationType of none.

data.destination = new SunshineConversationsClient.Destination()data.destination.integrationType = "none" // a silent message

Sending typing activities

User experience in a conversation can be improved by letting the user know that "typing" is in progress and that a message will soon be on its way. The Post Activity API can be used to signal these events to messaging channels that support this feature.

const apiInstance = new SunshineConversationsClient.ActivitiesApi()const data = new SunshineConversationsClient.ActivityPost()
data.author = {  type: "business"}data.type = "typing:start"
await apiInstance.postActivity("{appId}", "{conversationId}", data)

When you call this function with typing:start, a typing activity indicator will be displayed on the supported channel. On most platforms, the indicator will automatically time out. It can also be canceled by sending typing:stop. Alternatively, the indicator will be stopped the moment you send another message on the channel.

Next steps