Channel transfer
Channel transfers enable you to move a conversation with a user to another integrated channel, while preserving the conversation history and unifying the user's identity within Zendesk. It allows businesses and end users to have conversations on whichever channel is better suited for the topic at hand. Changing channels can be used to:
- Avoid the user being stuck in their browser window.
- Move the conversation to a channel that is less costly (for example, move away from SMS).
- Provide a better user experience by leveraging rich messaging features on modern channels.
- Offer users the option to receive notifications on a more convenient channel.
User-initiated transfer
End users contacting your business using the messaging Web Widget can be provided the option to transfer their conversation to one of several supported social channels. This flow is handled end-to-end by Zendesk and does not require development effort to achieve. You can learn more about this use case in Enabling customers to continue conversations through social channels in Zendesk help.
The linking procedure is the same as when transferring with a link request code. The main difference is that in the user-initated case, the link request code is generated automatically by the Web Widget instead of requiring a bot to propose the transfer.
Business-initiated transfer
Channel transfers can also be initiated by your business. This can either be done automatically using the messaging public API, or by presenting the user with options to move their conversation to a different channel (leveraging a specially crafted "link request" code). The method used depends on the capabilities of the channel, as well as the desired degree of required user input.
Transfer via direct API call
Users can be explicitly linked to a channel using the public API without requiring their confirmation. One use case for this would be in order to send targeted outbound messages. For example, if your business already knows the user's phone number and you've received prior consent to message them on WhatsApp, then the Create Client API can be used to automatically connect WhatsApp to their conversation.
When adding a new client to a user, you will need to specify the following information:
- The
matchCriteria
which contains the integration you are attempting to link, and the criteria to use to find the user on that channel. For example, on WhatsApp you would supply the user's phone number. - The
target
conversation that you would like to connect the new client to. - The
confirmation
type, explained in more detail below.
Here's an example:
{
"matchCriteria": {
"type": "whatsapp",
"integrationId": "582dedf230e788746891281a",
"phoneNumber": "+1 212-555-2368"
},
"confirmation": {
"type": "immediate"
},
"target": {
"conversationId": "029c31f25a21b47effd7be90"
}
}
When the Create Client API is called, this will add a new client with status: "pending"
to the user.
After the request concludes, the link request will be processed in the background according to the specified confirmation type. If the confirmation type was specified as immediate
, the client will be immediately confirmed and upgraded to status: "active"
. See channel transfer events for an explanation of the possible outcomes when the link is confirmed.
If the confirmation type was specified as userActivity
, the link will only be confirmed after the user acknowledges the link by performing some activity in the channel such as sending a message or clicking a postback button. When specifying userActivity
as the confirmation type, a confirmation.message
must be supplied. This message will be sent to the user after the pending client is added.
If the user replies to the message, the link will be confirmed at this time, triggering the client:update
event with reason: "confirmed"
. Note that in this scenario the content of the message does not matter, as long as the user interacts with the conversation the link will be automatically confirmed.
If the confirmation type was specified as prompt
, then a platform-generated message will be sent to the user to ask for their consent to confirm or refuse the link. This scenario is explained in more detail in transfer via link request code, as link request codes implicitly use the prompt
confirmation type.
Transfer via link request code
If you want to offer the user the option to transfer their channel, without forcing the transfer using the API, then link request codes can be used for this purpose. A link request code is a one-time use token that the business can generate in order to send the user an invitation to link an additional channel to their conversation.
To explain the link request flow, we'll use an example use case showing how a conversation can be transferred from the Zendesk Web Widget to a third party messaging app. We'll use Facebook Messenger as the target channel for this example. Rather than the user choosing to initiate the transfer themselves, in this use case the business' bot will prompt the user to move to a different channel. The flow chart below summarizes this process:

Sue, our example user, is a customer of Acme Bank. She wants to switch to a credit card that offers more points. She browses to Acme Bank's website and asks a question using the Zendesk Web Widget embedded there. An automated bot flow helps Sue choose a credit card and opens an application for her.
Sue is informed that the process should take one day to complete. She is then invited to link the conversation to a 3rd party messaging channel of her choice so that she can receive a notification as soon as her application completes. Sue is then presented with a list of channels to choose from.

Sue clicks the Facebook link which takes her into a Facebook Messenger conversation with Acme Bank. Behind the scenes, the link request code is communicated back to the messaging platform to establish the link between Sue's user object and the Facebook account which consumed the code.
At this point, the newly added client is in a "pending" status and cannot be used yet to send or receive messages. Sue is prompted to accept or refuse the link attempt from within Facebook Messenger.

Sue accepts the link attempt by clicking the "Yes" button. This then confirms the link within Zendesk, and the Facebook Messenger channel is now usable in Sue's conversation.

The following day, Sue's credit card application is approved. Acme Bank sends a notification to let Sue know, and she receives the message via a Facebook Messenger push notification on her phone. She can then continue the conversation with Acme Bank from there.
Channel transfer events
The following events are associated with channel transfers:
client:add
client:update
client:remove
user:merge
These four events reflect all possible outcomes:
- The link is sucessful, and a new client is added to the user.
- The link is successful, but the client
externalId
is already claimed by a different user. The conflict is resolved by either merging the users or transferring ownership of the client. - The link fails, or the user refuses the link prompt. The pending client is removed from the user as a result.
For the exact payload of each event, see Webhook Events, then expand events and select an appropriate trigger.
Event details
The following sections briefly describe how the channel transfer events work. See scenarios for more examples of when these events are triggered.
Client add event
The client:add
event is triggered when a new client is added to a user. This occurs as the first step of channel transfer when the link is initiated. In the example scenario above for transferring via a link request code, the client is added with status: "pending"
when Sue is redirected to Facebook and the link request code is consumed. At this point in the flow, the client is just a shell object with no usable properties yet. As the flow progresses, the client will be updated with more information.
Here is an example webhook payload:
{
"app": {
"id": "60bf823452c2a718162f986a"
},
"webhook": {
"id": "613a338323224aab69eb878c",
"version": "v2"
},
"events": [
{
"id": "6148a57e20698d4e576c4786",
"createdAt": "2021-09-20T15:15:10.239Z",
"type": "client:add",
"payload": {
"user": {
"id": "72bb4789027915bdeeb18f20"
},
"reason": "channelLinking",
"client": {
"integrationId": "5a99b74858247c608f64a348",
"type": "messenger",
"id": "67dc3cc53f09a870b773216b",
"status": "pending"
},
"source": {
"type": "web",
"integrationId": "60bf824952c2a718162f989c"
}
}
}
]
}
Client update event
During a channel transfer flow, the client:update
event is triggered when a meaningful change occurs on the client object:
- When a channel successfully matches the provided
externalId
,client:update
will be triggered withreason: "matched"
. This means that the client has been assigned itsexternalId
. - When the transfer is complete,
client:update
will be triggered withreason: "confirmed"
. This means that the client has transitioned fromstatus: "pending"
tostatus: "active"
. Typically, profile information gathered from the channel will also be available at this time and will be communicated in the webhook.
When transferring via a direct API call, if confirmation.type: "immediate"
is used, the client:update
with reason: "matched"
event is not triggered. Instead, the client will be immediately confirmed
.
Here is an example webhook payload:
{
"app": {
"id": "60bf823452c2a718162f986a"
},
"webhook": {
"id": "60c0eeee67951336472cc5c4",
"version": "v2"
},
"events": [
{
"id": "60c0ef415a04b5364b8a21cf",
"createdAt": "2021-06-09T16:41:37.794Z",
"type": "client:update",
"payload": {
"reason": "confirmed",
"user": {
"id": "3350271ca0e348bd1525d59d",
"externalId": "sue"
},
"conversation": {
"id": "59022bc666581de2ec5cba81",
"type": "personal"
},
"client": {
"integrationId": "60bfc8fa67951336472cc57a",
"type": "twilio",
"externalId": "+15140000000",
"id": "60bfcc075a04b5364b8a21cd",
"displayName": "+1 514-000-0000",
"status": "active",
"info": {
"city": "MONTREAL",
"country": "CA",
"phoneNumber": "+15140000000",
"state": "QC"
},
"raw": {
"FromZip": "",
"FromState": "QC",
"FromCity": "MONTREAL",
"FromCountry": "CA",
"From": "+15140000000"
},
"lastSeen": "2021-06-10T17:58:31.576Z",
"linkedAt": "2021-06-08T19:59:03.667Z"
}
}
}
]
}
Client remove event
The client:remove
event is triggered when a client is removed from a user. This can occur in the following scenarios:
- A client is explicitly removed by the business or the user (
reason: "api"
orreason: "sdk"
). - A channel transfer attempt fails (
reason: "linkFailed"
). - A client theft occurs, meaning a client was moved from one user to another (
reason: "theft"
). - The user refuses the link prompt (
reason: "linkCancelled"
).
Here is an example webhook payload:
{
"app": {
"id": "60bf823452c2a718162f986a"
},
"webhook": {
"id": "613a338323224aab69eb878c",
"version": "v2"
},
"events": [
{
"id": "6148a7ed5720554e5aae608f",
"createdAt": "2021-09-20T15:25:33.441Z",
"type": "client:remove",
"payload": {
"user": {
"id": "e58c581e263921222ec129f8"
},
"client": {
"integrationId": "60bfc8fa67951336472cc57a",
"type": "twilio",
"externalId": "+15140000000",
"id": "6148a7e920698d4e576c4791",
"displayName": "+1 514-000-0000",
"status": "pending"
},
"reason": "sdk",
"source": {
"type": "web",
"integrationId": "60bf824952c2a718162f989c"
}
}
}
]
}
User merge event
The user:merge
event is triggered when two users are merged into one. It contains the client that was removed and the client that was kept. Note that merging triggers neither the client:update
nor the client:remove
events.
In a channel transfer flow, a user merge can occur if the external account that gets linked is already claimed by an anonymous user. In the example scenario above for transferring via a link request code, suppose Sue already had a pre-existing conversation on Facebook Messenger. By linking her Web Widget session to the same Facebook account, this proves that these two users represent the same person (Sue). As a result, a merge is executed between the user that initiated the link from the Web Widget, and the user that owns the existing Facebook Messenger conversation.
For more information on user merges, see how merges work. In this case, the merge reason would be channelLinking
.
Here is an example webhook payload:
{
"app": {
"id": "60bf823452c2a718162f986a"
},
"webhook": {
"id": "613a338323224aab69eb878c",
"version": "v2"
},
"events": [
{
"id": "6148ae625720554e5aae60a7",
"createdAt": "2021-09-20T15:53:06.850Z",
"type": "user:merge",
"payload": {
"mergedUsers": {
"surviving": {
"id": "022d1469bf801373e43ea6fd"
},
"discarded": {
"id": "2e72fa9bc5440a9378fad4df"
}
},
"mergedConversations": {
"surviving": {
"id": "428c83b6cc5c58a5862f7e09",
"type": "personal"
},
"discarded": {
"id": "d8375e4387893d2080905054",
"type": "personal"
}
},
"mergedClients": {
"surviving": {
"integrationId": "61324f8fdd4e49165bf3a66f",
"type": "messenger",
"externalId": "1395558734359624",
"id": "6148ae465720554e5aae6092",
"displayName": "Sue",
"status": "active",
"info": {
"avatarUrl": "https://example.com/images/avatar.jpg"
},
"raw": {
"first_name": "Sue",
"last_name": "Allen",
"profile_pic": "https://example.com/images/profilepic.jpg",
"id": "1395558734359624"
},
"lastSeen": "2021-09-20T15:52:47.766Z",
"linkedAt": "2021-09-20T15:52:38.522Z",
"avatarUrl": "https://example.com/images/avatar.jpg"
},
"discarded": {
"integrationId": "61324f8fdd4e49165bf3a66f",
"type": "messenger",
"externalId": "1395558734359624",
"id": "6148ae5e5720554e5aae609c",
"displayName": "Sue",
"status": "pending"
}
},
"reason": "channelLinking"
}
}
]
}
Special case: client theft
In the user merge scenario above, Sue has proven that her Web Widget conversation and Facebook Messenger conversation are owned by the same person. Assuming that the Facebook Messenger conversation belongs to an anonymous user, a merge is safe to perform. However, if the Facebook Messenger conversation belonged to an identified user instead, then the merge would not be safe to perform. In this instance, the merge would result in a single user which is itself identified, meaning a JWT credential would be required in order to continue using the Web Widget. Since there is no JWT token provisioned to her in her browser, performing this merge would effectively lock Sue out of her Web Widget session.
To allow the link to succeed without locking Sue out of the Web Widget, a client theft is performed instead of a merge. When Sue confirms the link prompt in Facebook Messenger, the client will be removed from the identified user, triggering a client:remove
event with reason: "theft"
. At the same time, the client:update
event is triggered for the anonymous user with reason: "confirmed"
. From now on, the Facebook Messenger conversation will continue under ownership of the anonymous user.
Scenarios
This section describes a few scenarios to illustrate how the events are triggered. You can subscribe to these triggers when configuring your own webhook to better understand what is happening and when.
Basic channel linking
This scenario describes the simplest form of channel linking.
Sue is setting up a new bank account at Acme Bank and reaches out to them through their web page. This is the first time Sue has interacted with Acme Bank's web page. Acme Bank's web page uses the Zendesk Web Widget.
During her conversation, Sue notices the button to link Facebook Messenger to her conversation. She has never interacted with the Acme Bank's Facebook page before. She initiates the link with Facebook and confirms the link prompt.
Here's the workflow:
- When Sue starts the conversation on Acme Bank's web page, a new anonymous user is created in Zendesk messaging.
- When Sue clicks the Facebook icon, a
messenger
client is created withstatus: "pending"
and triggers theclient:add
event. - Facebook communicates the link request code back to Zendesk, and includes Sue's Facebook account id.
- The Facebook account id is stored under the client's
externalId
field and aclient:update
event withreason: "matched"
is triggered. - When Sue replies Yes, the client changes to
status:active
and aclient:update
event is triggered withreason: "confirmed"
.
Suppose Sue accidentally clicks the link when logged into her husband Chris' Facebook account by mistake. In this scenario, the workflow is:
- When Sue clicks the Facebook icon and is redirected to Messenger, a
messenger
client is created withstatus: "pending"
and triggers theclient:add
event. - Facebook communicates the link request code back to Zendesk, but includes Chris' Facebook account id instead of Sue's.
- The Facebook account id is stored under the client's
externalId
field and aclient:update
event withreason: "matched"
is triggered. - Sue notices that she is logged into the wrong Facebook account, and replies No to the link prompt.
- A
client:remove
event is triggered withreason: linkCancelled
.
Channel transfer between two anonymous users
In this scenario, we'll see what happens when an anonymous user tries to link a client which is already claimed by another anonymous user.
Let's resume where the basic channel linking flow left off. To recap, Sue had a conversation with Acme Bank and linked her Web Widget session to Facebook Messenger. Suppose Sue's prior conversation occurred on her laptop, and she now opens Acme Bank's website again from her mobile phone and starts a new conversation. Since Sue is not logged in on either browser, the two conversations belong to totally separate users in Zendesk messaging. Once again, Sue decides that she would like to receive replies over Facebook Messenger and completes the linking flow. The messaging platform recognizes that both Sue users share the same Facebook account and merges the users as a result.
Here's the workflow:
- When Sue sends her first message on her laptop, a new anonymous user is created in Zendesk messaging.
- Sue links this user to Facebook messenger.
- Later on, Sue sends a new message from the web browser on her mobile phone. A new anonymous user and conversation is created.
- Sue links this second user to Facebook messenger using the same Facebook account.
- Zendesk determines that two instances of an anonymous user share the same Facebook account, and therefore are likely to represent the same person. The two anonymous users are merged into one, triggering the
user:merge
event withreason: "channelLinking"
.
Channel transfer between two identified users
In this scenario, we'll examine what happens when two identified users try to use the same client (same externalId
).
Chris wants to book a hotel reservation and uses the hotel's Web Widget on their website. Ash, who works at the hotel, offers to help. Chris already has an account with this hotel, logs in to the Web Widget, and is therefore an identified user. Chris has also already linked WhatsApp. Due to a conflict in their schedule, two days later his wife Sue logs in to the hotel's Web Widget to update their reservation. Sue is also an identified user, separate from Chris. Ash gives Sue the option to link her WhatsApp to this conversation. Sue agrees, and provides the same phone number that Chris used. Ash initiates the link with WhatsApp using a custom Zendesk app that his company created. Sue has now linked this conversation and the WhatsApp channel is transferred from Chris to Sue.
The workflow is as follows:
- Chris, and later Sue, log in to the hotel's website to use the hotel's Web Widget.
- When Ash initiates the link with WhatsApp, a
whatsapp
client is created withstatus: "pending"
and triggers theclient:add
event. - Zendesk notices that the
externalId
that was linked (the phone number in this case) matches another user, Chris. - The confirmation type was specified as
immediate
, so the link is automatically confirmed. - The WhatsApp client is removed from Chris and triggers the
client:remove
event withreason: "theft"
for him. This also triggers theclient:update
event withreason: "confirmed"
for Sue.