Advanced integration
Advanced integration
Clickable links delegate
When a user taps a URL, phone number, or email address sent to a conversation, the SDK handles the event automatically by launching the default app capable of completing the action:
- the default browser for a URL
- the default phone app for a phone number
- the default email app for an email address
Note: Only URLs such as https://www.zendesk.com/ are made clickable in the UI. Other URIs such as zendesk://myapp
will not be clickable.
You can customize the behavior and change what happens when the user clicks a URL by setting a MessagingDelegate
. The shouldHandleUrl
function will be called any time the user clicks a URL.
To prevent the SDK from opening the default browser, you should return false when this happens. In that case, you must handle the completion of the action yourself. The function receives two parameters:
url
: the URL that was clickedurlSource
: an enumeration that describes where in the UI theurl
was clicked, such as a text message, a carousel item, and so on.
You can find a demo app showcasing this feature in our Zendesk SDK Demo app github.
The following snippets show how you can set a MessagingDelegate
in Kotlin and Java:
Kotlin
Messaging.setDelegate(object : MessagingDelegate() {
override fun shouldHandleUrl(url: String, urlSource: UrlSource): Boolean {
// Your custom action...
// Return false to prevent the SDK from handling the URL automatically
// Return true to allow the SDK to handle the URL automatically, even
// if you have done something custom
return false
}
})
Java
Messaging.setDelegate(new MessagingDelegate() {
@Override
public boolean shouldHandleUrl(@NotNull String url, @NotNull UrlSource urlSource) {
// Your custom action...
// Return false to prevent the SDK from handling the URL automatically
// Return true to allow the SDK to handle the URL automatically, even
// if you have done something custom
return false;
}
});
Events
The Zendesk SDK for Android provides an event listener ZendeskEventListener
where you can listen for any ZendeskEvent
emitted from the SDK.
Available Events
ZendeskEvent
is a sealed class with a subclass for each event that can currently be emitted. It has the following events:
- UnreadMessageCountChanged
- AuthenticationFailed
- FieldValidationFailed
- ConnectionStatusChanged
- SendMessageFailed
- ConversationAdded
UnreadMessageCountChanged
The number of unread messages has changed.
Name | Type | Comment |
---|---|---|
currentUnreadCount | Int | The current number of unread messages. |
AuthenticationFailed
Invoked when an authentication error has occurred on any of the API calls.
Name | Type | Comment |
---|---|---|
error | Error | Details about the error that occurred. |
FieldValidationFailed
Invoked when validation checks fails for conversation fields.
Name | Type | Comment |
---|---|---|
errors | List | Details about errors that occurred. |
ConnectionStatusChanged
Invoked when The SDK [ConnectionStatus] has changed due to an action or another event.
Name | Type | Comment |
---|---|---|
connectionStatus | ConnectionStatus | A representation of the current connection status of the SDK. |
SendMessageFailed
Invoked when a message fails to be sent.
Name | Type | Comment |
---|---|---|
cause | Throwable | Details about the error that occurred. |
ConversationAdded
Invoked when a conversation has been added.
Name | Type | Comment |
---|---|---|
conversationId | String | the id of the conversation that was added. |
EventListener
The EventListener
can be created then set to the current Zendesk instance using zendesk.addEventListener
.
We recommend that you ensure that the Zendesk instance you are attaching the listener to is valid and initialised completely in order avoid missing Events.
One reliable way to ensure this is to call zendesk.addEventListener
within the successCallback
of your Zendesk.initialize
call.
This will guarantee that the listener is added to the most current instance of the SDK.
You can find an example of this implementation within our demo apps:
Kotlin
Here is a code sample showing how the event listener can be used, added and removed:
// To create and use the event listener:
val zendeskEventListener: ZendeskEventListener = ZendeskEventListener {
zendeskEvent -> when (zendeskEvent) {
is ZendeskEvent.UnreadMessageCountChanged ->{
// Your custom action...
}
is ZendeskEvent.AuthenticationFailed -> {
// Your custom action...
}
is ZendeskEvent.FieldValidationFailed -> {
// Your custom action...
}
else -> {
// Default branch for forward compatibility with Zendesk SDK and its `ZendeskEvent` expansion
}
}
}
// To add the event listener to your Zendesk instance:
// (safe for concurrent use)
zendesk.addEventListener(zendeskEventListener)
// To remove the event listener from your Zendesk instance:
// (safe for concurrent use)
zendesk.removeEventListener(zendeskEventListener)
Java
Below is a code sample showing how the event listener can be used, added and removed.
This includes the ZendeskEventListenerAdapter
, an implementation of the ZendeskEventListener
designed to provide better support for Java:
// To create and use the event listener:
ZendeskEventListener zendeskEventListener =
new ZendeskEventListener() {
@Override
public void onEvent(@NonNull ZendeskEvent zendeskEvent) {
if (zendeskEvent instanceof UnreadMessageCountChanged) {
// Your custom action...
} else if (zendeskEvent instanceof AuthenticationFailed){
// Your custom action...
} else if {zendeskEvent instanceof FieldValidationFailed} {
// Your custom action...
}
}
};
// Or using the ZendeskEventListenerAdapter:
ZendeskEventListenerAdapter listenerAdapter = new ZendeskEventListenerAdapter() {
@Override
public void onUnreadMessageCountChanged(@NonNull UnreadMessageCountChanged event) {
super.onUnreadMessageCountChanged(event);
// Your custom action
}
};
// To add the event listener to your Zendesk instance:
zendesk.addEventListener(zendeskEventListener);
// To remove the event listener from your Zendesk instance:
zendesk.removeEventListener(zendeskEventListener);
EventFlow
Below is a code sample which shows the usage of the zendesk.eventFlow
API:
lifecycleScope.launch {
zendesk.eventFlow.collect { zendeskEvent ->
when (zendeskEvent) {
is ZendeskEvent.UnreadMessageCountChanged -> // Your custom action
else -> Unit
}
}
}
Authentication
The Zendesk SDK allows authentication of end users so that their identity can be verified by agents using Zendesk. A detailed article on the steps to set up authentication for your account is here. The steps mentioned in this article should be completed before beginning the steps below.
You can find a demo app demonstrating the capability of user authentication on our Demo app repository.
LoginUser
To authenticate a user call the loginUser
API with your own JWT
.
You can create your own JWT
following our Creating a JWT token section.
The JWT
can contain the following fields:
Name | Type | Comment |
---|---|---|
external_id | String | The external id of the user. Required. The maximum length is 255 characters. |
name | String | The name of the user. Optional. |
String | The email of the user. Optional. |
Kotlin
Zendesk.instance.loginUser(jwt = "your_jwt_here",
successCallback = { user -> },
failureCallback = { error -> }
)
Java
Zendesk.getInstance().loginUser("your_jwt_here", new SuccessCallback<ZendeskUser>() {
@Override
public void onSuccess(ZendeskUser value) {
}
}, new FailureCallback<Throwable>() {
@Override
public void onFailure(@NonNull Throwable error) {
}
});
LogoutUser
To unauthenticate a user call the logoutUser
API.
This is primarily for authenticated users but calling logoutUser
for an unauthenticated user will clear all of their data, including their conversation history. Please note that there is no way for us to recover this data, so only use this for testing purposes. The next time the unauthenticated user enters the conversation screen a new user and conversation will be created for them.
Kotlin
Zendesk.instance.logoutUser(
successCallback = { },
failureCallback = { error -> }
)
Java
Zendesk.getInstance().logoutUser(new SuccessCallback<Unit>() {
@Override
public void onSuccess(Unit value) {
}
}, new FailureCallback<Throwable>() {
@Override
public void onFailure(@NonNull Throwable error) {
}
});
Note: This will unsubscribe you from Push notifications. You will no longer receive these notifications for any of your ongoing conversations. If you log back in and get resubscribed, you will received new notifications but not the ones you missed while you were logged out.
Authentication Errors
All authentication errors can be observed through Events.
The typical issues encountered here are receiving an HTTP 401 error or creating a JWT
with an expiration timestamp, which leads to an expiration error. In this case a new JWT
should be generated and a call made to loginUser
.
Authentication lifecycle and authentication expiration
Once loginUser
is successful, the user remains authenticated until the token expires or an error occurs.
You can create your JWT
with an expiration timestamp using the optional exp
property. See Creating a JWT token.
When the token expires, the server will return an ZendeskJwtExpiredException
error indicating "The JWT has expired". This error can be captured through the AuthenticationFailed
event listener. Following this, the user will lose their access and must re-authenticate using loginUser
. If the now unauthenticated user attempts to start a conversation, they will be directed to the conversation screen with an error. The conversation content will not be visible.
As the SDK doesn't renew the token itself, you will have to handle the re-authentication process.
Using ZendeskEvent.AuthenticationFailed to reauthenticate the user
You can reauthenticate a user based on the specific error. For example if you wanted to reauthenticate a user once their JWT
has expired, you can search for that error in your implementation using the AuthenticationFailed
event. Once you are notified that the user's JWT
has expired you can generate a new JWT
with a new expiration timestamp and log them back into the SDK.
Kotlin
Below is a code sample using callbacks showing how to reauthenticate a user after their JWT has expired.
is ZendeskEvent.AuthenticationFailed -> {
if (event.error is ZendeskJwtExpiredException) {
lifecycleScope.launch {
zendesk.loginUser(newJwtToken, successCallback = {
// Handle login success
}, failureCallback = {
// Handle login error
})
}
}
}
Java
Below is a code sample using callbacks showing how to reauthenticate a user after their JWT has expired.
if (zendeskEvent instanceof ZendeskEvent.AuthenticationFailed) {
ZendeskEvent.AuthenticationFailed authFailedEvent = (ZendeskEvent.AuthenticationFailed) zendeskEvent;
if (authFailedEvent.getError() instanceof ZendeskJwtExpiredException) {
zendeskInstance.loginUser(newJwtToken, successCallback -> {
// Handle login success
}, throwable -> {
// Handle login error
});
}
}
Authentication merges
When an anonymous user in the Zendesk SDK is identified using the loginUser
API, and this user already exists in Zendesk (for example, they were previously active on another device), the data from the anonymous user and the existing user will be merged. This functionality enables a user to initiate a conversation with your business anonymously and then log in.
For single-conversation, Once logged in, they will see that their previous anonymous conversation has been combined with conversations from their logged-in account, allowing them to continue seamlessly as an authenticated user.
For multi-conversations, Once logged in, they will see that their previous anonymous conversations in the conversation list screen with conversations from their logged-in account, allowing them to continue seamlessly as an authenticated user.
Preventing unwanted authentication merges
If you are using authentication, it's important to authenticate your users prior to displaying the messaging screens. Delaying authentication may result in users engaging in anonymous conversations, complicating their ability to access prior conversations and potentially causing them to create new tickets for existing issues (duplicating tickets).
It's also important to check the JWT
is not about to expire as you authenticate your users prior to displaying the messaging screens, as you will need handle the error to reauthenticate the user if the JWT
expires while the user is interacting with a conversation.
For more information and code samples, see Authentication in the Zendesk mobile SDKs.
Visitor Path
The Visitor Path lets agents see what screen the end user had landed on, for better conversation context.
You can find a demo app showcasing this feature in our Zendesk SDK Demo app github.
Page View Event
The PageView
object encapsulates information related to a user’s interactions and passes it to the Page View Event API. These session-based page view events can be seen in Agent Workspace by support agents using Zendesk.
Kotlin
Below is a code sample showing how to send a Page View Event.
The API accepts a PageView
object as a parameter. Pass the location of the screen that the end user is on to url
and the name of the screen to pageTitle
.
// Create a `PageView` object
val pageView = PageView(url = url, pageTitle = pageTitle)
Zendesk.instance.sendPageView(pageView,
successCallback = { user -> },
failureCallback = { error -> }
)
Java
Below is a code sample showing how to send a Page View Event.
The API accepts a PageView
object as a parameter. Pass the location of the screen that the end user is on to url
and the name of the screen to pageTitle
.
PageView pageView = new PageView(url, pageTitle);
Zendesk.getInstance().sendPageViewEvent(pageView, new SuccessCallback<Unit>() {
@Override
public void onSuccess(Unit value) {
}
}, new FailureCallback<Throwable>() {
@Override
public void onFailure(@NonNull Throwable error) {
}
});
Proactive messaging
Proactive messaging can deliver targeted local push notifications to your users through your mobile SDK channel when triggering pre-defined conditions. See Creating proactive messages for mobile SDK channels for information on creating a proactive messaging campaign for your channel. The steps mentioned in the article should be completed before performing the steps below.
Local push notifications
Proactive messaging uses Android local notifications to deliver messages to users that meet your pre-defined conditions.
Zendesk SDK for Android will request notification permissions from the user only when they open the conversation screen (if they haven't been prompted previously) so a proactive message can be displayed.
Make sure your project requests notification permissions from the user before they open the conversation screen, if you want Proactive messages to display before this.
Relationship with the Page View event
A proactive messaging campaign is evaluated when triggered by the Page View Event integration. It is essential that for each screen the user visits, the page view event is correctly updated to reflect this.
For example, suppose you have a campaign that triggers when the user is on a particular product screen for 30 seconds. If the user navigates to a different screen after 25 seconds, and there's no subsequent page view event sent, the user will not receive a proactive message notification.
Customization
The style of the Zendesk SDK can be set through Admin Center which currently includes the primary color, the message color and the action color.
There is additional customization support available for setting text colors through the SDK using the following API. Please note this is a temporary API which will be available until the same feature is supported in Admin Center. This API will then be deprecated.
When initializing the Zendesk SDK, different text colors can now be passed as parameters to compliment the available style colors, with support for both light and dark mode.
Kotlin
val colors = UserColors(onPrimary = Color.Black,
onMessage = Color.Black,
onAction = Color.Black)
val factory = DefaultMessagingFactory(userLightColors = colors,
userDarkColors = colors)
Zendesk.initialize(
context = this,
channelKey = "channel_key",
messagingFactory = factory,
)
Java
UserColors colors = new UserColors(Color.BLACK, Color.BLACK, Color.BLACK);
DefaultMessagingFactory factory = new DefaultMessagingFactory(colors, colors);
Zendesk.initialize(this, "channel_key", factory);
Messaging Metadata
To learn more about Messaging Metadata, see Introduction to Messaging Metadata.
Note: The SDK must be initialized before using any of these methods. See Initialize the SDK for more details.
You can find a demo app showcasing this feature in our Zendesk SDK Demo app github.
Conversation Fields
Set Conversation Fields
Allows values for conversation fields to be set in the SDK to add contextual data about the conversation.
Zendesk.instance.messaging.setConversationFields(fields: Map<String, Any>)
Conversation fields must first be created as custom ticket fields and configured to allow their values to be set by end users in Admin Center. To use conversation fields, see Using Messaging Metadata with the Zendesk Web Widgets and SDKs.
The values stored are persisted, and will be applied to all conversations going forward. To remove conversation fields stored in the SDK, use the ClearConversationFields
API.
Note: Conversation fields are not immediately associated with a conversation when the API is called. Calling the API will store the conversation fields, but those fields will only be applied to a conversation when end users either start a new conversation or send a new message in an existing conversation.
System ticket fields, such as the Priority field, are not supported.
Parameters
fields: Map<String, Any>
: It is a collection of key-value pairs.
Type | Description |
---|---|
String | id of custom ticket field |
Any | value of custom ticket field |
Note: Supported types for Any
are string, number and boolean.
Example
For example, if the id
of your custom field for flight number is 4422761977114
and you want to set its value to FA2590
, then you would call:
Kotlin
val fields = mapOf("4422761977114" to "FA2590")
Zendesk.instance.messaging.setConversationFields(fields)
Java
Map<String,Object> fields = new HashMap<String, Object>();
fields.put("4422761977114", "FA2590");
Zendesk.getInstance().messaging.setConversationFields(fields);
Clear Conversation Fields
You can clear conversation fields from the SDK storage when the client side context changes.
To do this, use the clearConversationFields
API. This removes all stored conversation fields from the SDK storage.
Note: This API does not affect conversation fields already applied to the conversation.
Example
Kotlin
Zendesk.instance.messaging.clearConversationFields()
Java
Zendesk.getInstance().messaging.clearConversationFields();
Conversation Tags
Set Conversation Tags
Allows custom conversation tags to be set in the SDK to add contextual data about the conversation.
Zendesk.instance.messaging.setConversationTags(tags: List<String>)
To use conversation tags, refer to Using Messaging Metadata with the Zendesk Web Widgets and SDKs.
Note: Conversation tags are not immediately associated with a conversation when the API is called. It will only be applied to a conversation when end users either start a new conversation or send a new message in an existing conversation.
Parameters
List<String>
A list of strings
Example
For example, to apply promo_code
and discount
tags to a conversation about an order, then you would call:
Kotlin
val tags = listOf("promo_code", "discount")
Zendesk.instance.messaging.setConversationTags(tags)
Java
List<String> tags = new ArrayList<String>();
tags.add("promo_code")
tags.add("discount")
Zendesk.getInstance().messaging.setConversationTags(tags)
Clear Conversation Tags
Allows you to clear conversation tags from SDK storage when the client side context changes.
To do this, use the clearConversationTags
API. This removes all stored conversation tags from the SDK storage.
Note: This API does not affect conversation tags already applied to the conversation.
Example
Kotlin
Zendesk.instance.messaging.clearConversationTags()
Java
Zendesk.getInstance().messaging.clearConversationTags();
Postback Buttons in Messaging
Introduction
Postback buttons are a great way to enhance your conversations, since they can trigger server-side logic when a user clicks on them. When you send the postback button, you can attach a payload and when the user clicks on it, it will trigger webhooks listening to the postback trigger. The payload associated with the action clicked by the user will be included in the webhook body. This allows you to respond to the press of a button from your backend. The server-side logic can use the payload to run different code based on the context of the conversation. These features are very useful when building a bot. You can also use them to trigger other server-side actions with the click of a button in the conversation, such as an “add-to-cart” action.
Sending postback buttons
Postback buttons will soon be leveraged by the Zendesk FlowBuilder in carousel messages, so you will be able to create carousel buttons for your Zendesk Bot that will save the user's response and proceed the flow accordingly. You can also send postback buttons by using the Sunshine Conversations API. (Note that a Zendesk Suite Professional plan or above is required to send the postback buttons via the Sunshine Conversations API.)
You can use postback buttons together with other button types in the same message, as seen below, with the exception of reply buttons. (Reply buttons can not be used together with postback buttons or any other button type supported in Messaging.)
const apiInstance = new SunshineConversationsApi.MessagesApi();
const data = new SunshineConversationsApi.MessagePost();
data.author = {
type: 'business'
};
data.content = {
type: 'text',
text: 'Press one of the buttons below',
actions: [
{
type: 'postback',
text: 'Postback Button Label',
payload: 'PAYLOAD_HERE'
},
{
type: 'link',
text: 'Link Button Label',
uri: 'http://url.button.com'
}
]
};
apiInstance.postMessage(appId, conversationId, data)
.then(response => /* success */)
.catch(error => /* failure */);
Setting up your conversation integrations
When you send postback buttons via the Sunshine Conversations API, in order for the buttons to trigger webhooks, you will need to set up “conversation integrations” and subscribe for postback events. Here is more information on how you can set up conversation integrations.
Echo Postbacks
The ‘echoPostback’ allows you to add a message to the conversation history when a postback button is clicked. The content of the message will match the button label. When ‘echoPostback’ is enabled, the message will be silently added to the conversation as a user message, without a corresponding ‘conversation:message’ webhook being triggered. To enable ‘echoPostback’ a request must be made to update the app via the Sunshine Conversations API.
Invalidate the SDK
Invalidating the Zendesk SDK refers to the process of stopping its current instance. Upon instance invalidation, Zendesk SDK nullifies the invalidated instance and references to the internal storages are lost. This ensures that unused data does not accumulate over time, freeing up system resources. When the end user logs out, the Zendesk SDK removes all user details from local storages and terminates the real-time connection. Invalidating the Zendesk SDK instance means that no messages nor notifications will be received.
Switching between Instances
The Zendesk SDK enables switching between different instances at runtime, which is useful for running multiple instances within a single application.
Push notifications for switching between instances
No additional setup is required to switch between instances; however, we have introduced a new public API for push notifications, which validates incoming push notifications for the currently active instance.
For handling push notifications for switching instances, the following API will need to be added to your custom implementation of the FirebaseMessagingService
:`
PushNotifications.validatePushIntegration(context: Context, messageData: Map<String, String>)
This API will need to be called in onMessageReceived
method when a push notification is going to be displayed.
Kotlin
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import zendesk.messaging.android.push.PushNotifications
import zendesk.messaging.android.push.PushResponsibility.MESSAGING_SHOULD_DISPLAY
import zendesk.messaging.android.push.PushResponsibility.MESSAGING_SHOULD_NOT_DISPLAY
import zendesk.messaging.android.push.PushResponsibility.NOT_FROM_MESSAGING
/**
* A sample integration of [FirebaseMessagingService] that interacts with Messaging SDK.
*/
class SampleMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
val responsibility = PushNotifications.shouldBeDisplayed(remoteMessage.data)
when (responsibility) {
MESSAGING_SHOULD_DISPLAY -> {
// The push needs to be validated that it belongs to the current SDK instance.
if (PushNotifications.validatePushIntegration(this, remoteMessage.data)) {
// This push has been validated and it belongs to Messaging so the SDK is able to display it to the end user
PushNotifications.displayNotification(
context = this,
messageData = remoteMessage.data,
)
} else {
// This push does not belong to the current SDK instance and should not be displayed.
}
}
}
MESSAGING_SHOULD_NOT_DISPLAY -> {
// This push belongs to Messaging but it should not be displayed to the end user
}
NOT_FROM_MESSAGING -> {
// This push does not belong to Messaging
}
}
}
}
Java
import androidx.annotation.NonNull;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import zendesk.messaging.android.push.PushNotifications;
import zendesk.messaging.android.push.PushResponsibility;
public class SampleMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
PushResponsibility responsibility = PushNotifications.shouldBeDisplayed(remoteMessage.getData());
switch (responsibility) {
case MESSAGING_SHOULD_DISPLAY:
// The push needs to be validated that it belongs to the current SDK instance.
if (PushNotifications.validatePushIntegration(this, remoteMessage.getData())) {
// This push has been validated and it belongs to Messaging so the SDK is able to display it to the end user
PushNotifications.displayNotification(this, remoteMessage.getData());
} else {
// This push does not belong to the current SDK instance and should not be displayed.
}
break;
case MESSAGING_SHOULD_NOT_DISPLAY:
// This push belongs to Messaging but it should not be displayed to the end user
break;
case NOT_FROM_MESSAGING:
// This push does not belong to Messaging
break;
}
}
}