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 in the backend of your software. It allows businesses and end-users to have conversations on whichever channel is better suited for the topic at hand and to benefit from a single, continuous, cross-channel conversation thread. Changing channels can be used to:
By leveraging the auth code and link request APIs, links can be generated and sent to users to direct them to the channel of your choice.
The guides below cover how to achieve this transfer in either direction:
The scenario below demonstrates how a customer Sue can begin a conversation with her bank on Facebook, and continue the same conversation on the bank’s own website. The flow chart below summarizes what we’re about to cover:
Sue is looking to set up a new bank account at Acme Bank. She first reaches out on their Facebook page. Acme Bank’s Facebook page is integrated with Sunshine Conversations, so Sue’s conversation shows up as a new conversation in Acme Bank’s helpdesk software.
The conversation is assigned to Ash, a customer service representative. He offers to guide her through the application process. Ash opens the help desk menu and selects “Send user to banking app”. The help desk inserts a message with an action of type link into the conversation which Sue can use to access the Acme Bank app.
Sue is now in the Acme banking app, but she hasn’t yet logged in. She does however see the history of messages that were sent between her and Ash over Facebook replicated here.
On Ash’s end, the conversation continues within the same help desk context as before and the help desk indicates that Sue is now talking to him via the Acme banking app. Ash directs Sue to log in.
The banking app validates Sue's Acme Bank account credentials. In doing so, Acme Bank issues a JWT for Sue and includes it in the dashboard login response. In the browser, the dashboard logs Sue in to Sunshine Conversations using this JWT.
Smooch.login('sue_purb@lunamail.com', '{jwt}').then(
function() {
// Your code after login is complete
},
function(err) {
// Something went wrong during login. Your JWT might be invalid
}
);
Smooch.login("sue_purb@lunamail.com", jwt:"{jwt}") { ( error:Error? , userInfo:[AnyHashable : Any]?) in
if (error == nil) {
// Your code after login is complete
} else {
// Something went wrong during login. Your JWT might be invalid
}
}
Smooch.login("sue_purb@lunamail.com", "{jwt}", new SmoochCallback() {
@Override
public void run(Response response) {
if (response.getError() == null) {
// Your code after login is complete
} else {
// Something went wrong during login. Your JWT might be invalid
}
}
});
Ash sees that Sue has logged in and helps her complete the application to set up her bank account.
userId
and an Acme Bank account that she can log in to, the channel transfer process is simpler. The flow chart below shows the difference:For example, suppose a few years later Sue is shopping for a mortgage. She again contacts Acme Bank over Facebook. This time a different agent named Bishop responds with some rates. Sue finds the rates attractive and wishes to proceed. Bishop once again sends her a link to the Acme banking app.
The help desk has seen Sue before and it recognizes that she has multiple channels connected: Facebook Messenger, Web, and possibly iOS or Android. It also sees that Sue has a valid userId that matches an existing account on record at Acme Bank. Because of this, no auth code is required, the help desk only needs to send Sue to the dashboard login page:
https://acme-bank.com/login
Sue visits the link and logs in. She can now seamlessly continue her conversation with Bishop from the banking app.
When supplying an expired or otherwise invalid auth code at init time, the init flow will return an error and leave the SDK in an uninitialized state. Your code should account for this possibility and gracefully handle the error. You may also choose to display an error to the user in this case. If you prefer to ignore the error and continue execution anyways, your code should call init again without an auth code in order to trigger a new initialization process as the previously logged in user.
In this example we’ll show how a conversation can be transferred from a Sunshine Conversations SDK to a third party messaging app, in this case Facebook Messenger. The flow chart below summarizes this process:
Sue wants to switch to a credit card that offers more points. She browses to her bank’s website and asks a question.
An agent named Ash helps Sue choose a credit card and opens an application for her. Ash tells Sue that the process should take one day to complete.
Ash then invites Sue 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. In the help desk software, Ash selects a menu option “Transfer to messaging app”. Sue is then presented with a list of channels to choose from.
The help desk software queries the Sunshine Conversations integrations API for a list of available messaging app integrations. Acme Bank has previously configured Facebook and Telegram integrations, so they appear in the response payload. Using the integrationIds returned, the dashboard calls the Sunshine Conversations link request API to generate channel transfer links for Sue to use.
{
"linkRequests": [
{
"integrationId": "59dbd737f294ea1fa2734829",
"type": "messenger",
"code": "lr_K4DBnWEiWv6LR-CP0j_5FFcl",
"url": "https://m.me/123456781234567?ref=lr_K4DBnWEiWv6LR-CP0j_5FFcl"
},
{
"integrationId": "59fa19888b1dd03638d2dd54",
"type": "telegram",
"code": "lr_Xidl9tTWt6YVXP8SFD3hdUiD",
"url": "https://telegram.me/your_telegram_bot?start=lr_Xidl9tTWt6YVXP8SFD3hdUiD"
}
]
}
Note: Link requests are one time use. See more details here.
{
"text": "Choose your preferred channel:",
"role": "appMaker",
"type": "text",
"actions": [
{
"type": "link",
"text": "Facebook Messenger",
"uri": "https://m.me/123456781234567?ref=lr_K4DBnWEiWv6LR-CP0j_5FFcl"
},
{
"type": "link",
"text": "Telegram",
"uri": "https://telegram.me/your_telegram_bot?start=lr_Xidl9tTWt6YVXP8SFD3hdUiD"
}
]
}
Sue clicks the Facebook link which takes her into a Facebook Messenger conversation with Acme Bank.
Acme Bank receives a client:add webhook event from Sunshine Conversations. The webhook payload includes Sue's basic profile information from Facebook:
{
"trigger": "client:add",
"app": {
"_id": "575040549a38df8fb4eb1e51"
},
"version": "v1.1",
"appUser": {
"_id": "de13bee15b51033b34162411",
"email": "steve.bob@email.com",
"surname": "Steve",
"givenName": "Bob",
"signedUpAt": "2018-04-02T14:45:46.505Z",
"properties": { "favoriteFood": "prizza" }
},
"clients": [
{
"_id": "5c93cb748f63db54ff3b51dd",
"status": "active",
"integrationId": "5a99b74858247c608f64a348",
"externalId": "1128837457239041",
"id": "bea02e14-86f4-4711-8ecc-ab717ebf750a",
"displayName": "Sue Purb",
"info": {
"gender": "female",
"timezone": -5,
"locale": "en_US",
"avatarUrl": "https://scontent.xx.fbcdn.net/example.png"
},
"lastSeen": "2019-01-14T19:11:17.783Z",
"linkedAt": "2019-01-14T19:11:17.783Z",
"avatarUrl": "https://scontent.xx.fbcdn.net/example.png",
"primary": true,
"platform": "messenger",
"active": true,
"blocked": false
}
]
}
In response to this webhook, the help desk software notifies Ash that Sue has linked a new Facebook Messenger client.
Ash sends Sue another message to let her know she can continue the conversation over Facebook Messenger. Sue sees this message in Facebook Messenger while the conversation log continues to be replicated in her browser chat window.
The following day, Sue’s application completes. Acme Bank sends a notification to let Sue know, and she receives the message via a Facebook Messenger push notification on her phone, and she continues the conversation with Acme Bank from there.
The following events are associated with channel transfers:
client:add
client:update
client:remove
user:merge
These four events reflect all possible outcomes:
The following sections describe these events and scenarios in more detail.
Not all channels support link requests. For complete information, see channel capabilities.
Before talking about the v2 events, it is important to quickly review user types as they play an important role in which event is triggered. For detailed information about users, see Introduction to users.
There are two basic types of users in Sunshine Conversations.
A user can be only one type at any given time.
The following sections briefly describe how the channel transfer events work. See Scenarios for more examples of when these events are triggered.
The client:add
event is triggered when a new client is added to a user. This occurs when initiating a channel transfer request or when logging in through an SDK integration (iOS, Web, or Android) that isn’t already linked to the user.
For example, Sue visits her bank’s Facebook page to ask questions regarding the latest interest rates. The bank representative sends Sue a link to their chat widget on their website to continue the conversation. The chat widget is created using the Sunshine Conversations SDK. This is the first time Sue uses the bank’s chat widget. When Sue clicks the link and “lands” on the chat widget, the client:add
event is triggered.
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": "authCode",
"client": {
"type": "sdk",
"id": "6148a57e20698d4e576c4783",
"status": "active",
"lastSeen": "2021-09-20T15:15:10.207Z",
"linkedAt": "2021-09-20T15:15:10.207Z"
},
"source": {
"type": "web",
"integrationId": "60bf824952c2a718162f989c"
}
}
}
]
}
For more information, see Webhook Events, then expand events and select client:add.
The client:update
event is triggered when the client’s status is updated. This occurs when a channel successfully matches the provided externalId, when a channel transfer is complete, or when a user blocks or unsubscribes from a conversation.
The client:update
event can also be triggered during the course of a transfer. For example, with reason:matched
, the status remains pending
before and after an event (see above paragraph.) However, the client:update
event can still be triggered when something client-related is updated as part of the transfer process.
For example, Sue is using the hotel’s chat widget (created with the Sunshine Conversations SDK) on their web page and wants to link SMS to the conversation she is having with a hotel representative. Sue clicks the button to link SMS in the chat and enters her phone number. This creates a Twilio client with status:pending
and triggers the client:add
event. A text message is sent to Sue to confirm/deny (Yes/No) triggering a client:update
event with reason:matched
. Sue clicks Yes which triggers a client:update
event with reason:confirmed
indicating that her phone number is successfully linked.
Note that if confirmation.type: immediate
was used instead (meaning, no text sent to Sue), the client:update
with reason:matched
event is not triggered.
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"
}
}
}
]
}
The client:remove
event is triggered when a client is removed from a user. This occurs when you manually remove a client (using the API), when a channel transfer fails, when a client theft occurs (a client is moved from one user to another), or when the user refuses the link prompt.
For example, suppose Sue is interested in her bank’s car loans and is chatting with Ash, the loan representative, through the bank’s chat widget on their website. The chat widget is created using the Sunshine Conversations SDK. Ash invites Sue to link the conversation to a third-party messaging channel of her choice so that she can review the conversation whenever she wants. Sue clicks the button for SMS which creates a client with status:pending
and triggers the client:add
event. Sue accidentally enters the wrong phone number and cancels the request. This triggers a client:remove
event.
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"
}
}
}
]
}
For more information, see Webhook Events, then expand events and select client:remove.
When transferring a client, a client:remove
event is triggered when removing a client from a user and a client:update
event is triggered when the client is transferred to the new user.
For example, Sue is inquiring about a car and creates an account on the dealer’s website. She uses the dealer’s web chat to talk to a salesperson and during the conversation asks to link this conversation to her Facebook Messenger account. The salesperson generates a link request and redirects Sue to the Messenger app. A few days later, Sue has more questions and starts a conversation with the dealer using their Android app. Sue does not log in and is considered an anonymous user. Sue again wants to link this conversation to her Facebook Messenger account through the same Facebook account as earlier. Sunshine Conversations determines that this Facebook Messenger client is already linked to an identified user. In this example, an anonymous user is trying to link to a client already associated with an identified user and a merge is not allowed. Instead, the Facebook Messenger client is transferred to the anonymous user. Removing the client from the identified Sue triggers the client:remove
event. Once she approves the link, the client:update
event is triggered.
The user:merge
event is triggered when two users are merged. 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.
For example, Sue is using a store website’s web chat to chat with a sales representative regarding an order. She doesn’t want to remain at her computer for the duration of the session and chooses to continue her conversation on Facebook Messenger. A few days later, Sue returns to the store’s website using a different computer and starts a new conversation with a sales representative. She again chooses to continue her conversation on Facebook Messenger using the same account as before. Sunshine Conversations determines that both Facebook Messenger conversations belong to Sue, merges them, and triggers the user:merge
event. In both chats, Sue is considered an anonymous user.
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"
}
}
]
}
For more information on user merges, see How merges work.
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.
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 a chat widget created with the Sunshine Conversations SDK.
Ash, a customer service representative, offers to guide Sue through the application process and gives Sue the option to link her phone number to this conversation. She agrees, clicks the SMS icon in their current conversation, and enters her phone number. This phone number does not yet exist in Acme Bank’s Helpdesk software. Sue receives a text message on her phone to accept or deny this link request and clicks Yes. Sue has now linked this conversation to her phone number.
Here’s the workflow:
status:pending
and triggers the client:add
event.client:update
with reason:matched
is triggered.status:active
and a client:update
event is triggered with reason:confirmed
.Suppose Sue accidentally enters a wrong phone number (a number that doesn’t actually belong to any mobile phone.) Twilio now responds with no matching user found. In this scenario, the workflow is:
status:pending
and triggers the client:add
event.client:remove
with reason:linkFailed
is triggered.client:remove
webhook event, Ash sees in the logs that Twilio cannot be linked and asks Sue to re-enter her phone number.In the above examples, Twilio confirms whether the phone number is valid. However, not all channels have this feature of confirming the externalId. In these cases, Sunshine Conversations assumes the confirmation is valid and does not trigger a client:update
with reason:match
as in the first workflow above.
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 chat widget on their website which is created with the Sunshine Conversations SDK. Ash, who works at the hotel, offers to help. Chris already has an account with this hotel, logs in to the chat widget, and is therefore an identified user. Chris has also already linked Twilio. Due to a conflict in their schedule, two days later his wife Sue logs in to the hotel’s chat widget to update their reservation. Sue is also an identified user, separate from Chris. Ash gives Sue the option to link her phone number to this conversation. Sue agrees, clicks the SMS icon in the chat window, and enters the same phone number that Chris used. She receives a text message on her phone to accept or deny this link request and clicks Yes. Sue has now linked this conversation and the Twilio channel is transferred from Chris to Sue.
The workflow is as follows:
status:pending
and triggers the client:add
event.client:matched
event.client:remove
event with reason:theft
for him. This also triggers the client:update
with reason:confirmed
event for Sue.In this scenario, we’ll see what happens when an anonymous user tries to use the same client (same externalId) as another anonymous user.
Let’s use the Example business-initiated flow. To recap, Alice sends a message to your business’ configured Twilio phone number. Later, Alice starts a conversation with your business through your iOS application. During Alice’s conversation in iOS, she would like to receive further updates over SMS. The create client API is used to attach the phone number to Alice’s iOS conversation. Sunshine Conversations determines that both Alices share the same phone number and merges the users. Now, the conversation history is shared between iOS and SMS and Alice can reply from either channel.
Here’s the workflow:
user:merge
event with reason:channelLinking
.