SMS Zendesk Omni-Channel Templates: technical deep dive

Katrin Geske
Katrin Geske
  • Updated

babelforce offers a full omni-channel integration for SMS communication in Zendesk. For the operational description and use cases please read this article.

In this article, we want to deep dive on the details of this integration. Two so called "Taskrouter templates" are used to achieve the omni-channel integration which combines a number of different processes to one.

  1. First, we will guide you through the high level process.
  2. Next, we will explain the actions and http requsts used to achieve the integration.
  3. In the end, we will spend some time on the taskrouter script 

1. The high level process

There are two events which this template covers

  • SMS sent
  • SMS received

Each event triggers a specific process. We describe the process step-by-step. You may skip the description and review the graphs instead.

Let's start with SMS sent.

Whenever a SMS is sent, either by an agent or an automation there are two possible process paths.

    • Was the SMS sent by clicking on the "Sent message" button on an existing ticket and the via babelconnect?
      • Yes, in this case we append the ticket to the existing ticket following these processes:
        • The current Zendesk ticket is updated with the message of the text
        • the ticket will be put on pending
        • the agent will be assigned
        • The ticket id of the ticket the SMS was appended to is saved on the message conversation
      • No, in this case a new ticket is created, following these processes:
        • End user is looked up
        • a new ticket is created in Zendesk
        • End user and agent are assigned to ticket
        • Ticket is put on pending
        • The ticket id of the ticket newly created is saved on the message conversation

The second event is the reception of a SMS which we will describe next. It is the path describing whether a SMS is appended to an existing ticket or a new ticket is created.

    • For every received SMS the message conversation is looked up
    • Does a conversation for this SMS exist?
      • No - this means that the phone number never send a message nor was a message sent to this number before. In this case a new ticket is created following this procecess:
        • End user is looked up
        • a new ticket is created in Zendesk
        • End user and agent are assigned to ticket
        • Ticket is put on pending
        • The ticket id of the ticket newly created is saved on the message conversation
      • Yes - in this case there has been previous contact with this sender
        • Is the idle time of the conversation greater than the defined maximum, e.g. 8000 seconds?
        • Yes - in this case a new ticket will be opened following the already known process:
          • End user is looked up
          • a new ticket is created in Zendesk
          • End user and agent are assigned to ticket
          • Ticket is put on pending
          • The ticket id of the ticket newly created is saved on the message conversation
        • No - If the idle time was not reached, we might want to update an already existing ticket. But we have to check if a valid ticket id is saved on the ticket
          • Does the conversation contain a valid ticket id?
            • Yes - in this case we append the ticket to the existing ticket following these processes:
              • The current Zendesk ticket is updated with the message of the text
              • the ticket will be put on pending
              • the agent will be assigned
              • The ticket id of the ticket the SMS was appended to is saved on the message conversation
            • No - in this case a new ticket will be opened following the already known process:
              • End user is looked up
              • a new ticket is created in Zendesk
              • End user and agent are assigned to ticket
              • Ticket is put on pending
              • The ticket id of the ticket newly created is saved on the message conversation

2. The endpoints used for this integration

Before we deep dive the endpoints it is worth mentioning that on every "received" and "sent" event, a number of relevant data points is send to the taskrouter. If you review the last part of this article you will notice a table with a list of values. These are the data points (key values) sent to the taskrouter which will be used to send the right data to the endpoints. Keep this in mind for later.

For now, this is the list of actions and endpoints utilized in the template.

Actions are predefined endpoints, often internal endpoints or abstractions, the endpoints are simple http requests. We will start with the actions

Action Provider Parameters Description
ticket.update
babelforce Action Proxy (internal)
integration_id,
comment,
ticket_id,
status,
assignee_id,
add_tags,
additional_fields,
custom_fields,
public
This endpoint updates a Zendesk ticket. It uses the babelforce Action Proxy to execute the tasks. The parameters are partially pre-filled, some are dynamic, others are passed on to the taskrouter.
enduser.lookup
babelforce Action Proxy (internal)
task.body.integrationId,
task.body.consumerNumber
This endpoint looks up the end user based on the phone number of the customer. 
ticket.create
babelforce Action Proxy (internal)
public,
integration_id,
subject,
comment,
ticket_id,
status,
assignee_id,
add_tags,
additional_fields,
custom_fields
We have similar parameters as for a ticket update to create a new ticket in Zendesk. 
js: zendesk_fields

JavaScript 
"code": "() => {return {'additional_fields':
task.body.additional_fields==null?{}:
task.body.additional_fields,'custom_fields':
task.body.custom_fields==null?{}:
task.body.custom_fields,'tags':
task.body.tags==null?[]:task.body.tags};}"
,
This script checks whether any additional fields, custom fields or tags have been defined and maps them on specific variables (e.g. custom_fields, etc.) 
js: ticket_id

JavaScript 
"()=>{var i=context?.sessions?.item['ivr.ticketId'];
if(i!==null&&(''+i).trim().length>0&&Number.isInteger(Number(i)))
{return 'update_ticket'}return 'create_new_ticket'}"
This JS checks whether a ticket id is given on the variable ivr.ticketId which was retrieved from the call session. If it is given, 'update_ticket' is returned. Otherwise, a new ticket is created. Also, it transforms the id to an integer
js: idle_time

JavaScript 
"()=>{if(context.conversation.item.idle<{{task.body.idleTime}})
{return 'conversation_session'}return 'create_new_ticket'}"
Checks if idle time retrieved from the conversation is smaller than the defined maximum idle time. If this is true, the conversation is retrieved from babelforce. Otherwise, a 'create_new_ticket' is initiated.

 

Endpoint (http)

Provider Parameters Description
PUT
{{env.babelforce.url}}/api/v2/
conversations/
{{task.body.conversationSession}}/
session
babelforce backend service
env.babelforce.url,
task.body.conversationSession,
ivr.ticketId

 

This endpoint adds variables to the session of the conversation. The environment parameter is automatically set. The conversation session is passed on to the taskrouter. The iv.ticketId represents the id of the Zendesk ticket on which the update was performed.
GET
{{env.babelforce.url}}/api/v2/conversations/
{{{task.body.conversationSession}}}
babelforce backend service
env.babelforce.url,
task.body.conversationSession
This endpoint retrieves the conversation. The parameters returned include the Idle time which is important in our flow as it defines whether to update an existing ticket or to create a new one
{{env.babelforce.url}}/api/v2/conversations/
{{task.body.conversationSession}}/session

babelforce backend service
env.babelforce.url,
task.body.conversationSession
This endpoint retrieves the conversation session. After confirming that the idle time is within range, the conversation session gives insight in whether the ivr.ticketId session is set.

 

3. Explaining the Templates

The templates put the process in code. You will recognize the actions and http calls described in the previous chapter. 

We will first explain the basic structure of a template and then explain conditional logic which is also applied here. In the end, we will walk you briefly through the the two templates.

Structure of a template

Below, we stripped away most "noise" from a template and only left the most relevant parts of it as well as a couple examples. 

Let's start with "required_fields". These are fields filled dynamically, whenever executing the template. For instance, in our case, the sender number would be an example. The required fields are later used in actions and http's to fill in parameters, passwords or parts of a URL. Anything that might be changing with every call. When using required fields in actions, you need to follow this schema: task.body.nameOfRequiredField. Looking at the example below, the field body.requiredField1 would be referenced as task.body.requiredField1.

After the required fields, the actual task follows. Define the task type - we suggest a name that describes what the task tries to achieve.

You can also send along further parameters in the body.

Next, actions follow. Here, all kind of actions are possible - we already introduced a number of actions above. They can be pre-defined actions (all actions that talk to babelforce integrations such as zendesk ticket updates, etc.), you can call any http endpoint or write your own javascript.

The response of the requests is saved on variables and can be used in later actions. For instance, "var": "saveResponseFromAction" saves the response for the ticket.update action. 

Actions can be executed based on conditions. As you can see in our SMS example, we have a number of decision points, e.g: is a ticketID given or idleTime less than a certain factor. For conditions, we need a javascript (how this works in detail we'll explain with the concrete example). Within an action or http, you can add "condition". Only if, e.g., the condtion "context.code_block==='whatever_js_returned'", is true, the action is executed.

After completing the action, you have the options to adjust "selection" settings. These are only important if you have agents involved in your template. In our case we do not so we leave those aside.

Finally, you decide when to execute the task. In most cases "now" is what you need. Of course, if you want to dynamically execute a template (e.g. at a specific time or a few minutes after receiving the SMS), no problem. 

{
"required_fields": [
"body.requiredField1",
"body.requiredFild2"
],
"task": {
"type": "type.name",
"body": {},
"actions": {
"on_scheduled": [
{
"js": {
"code": "you may enter any kind of javascript that you need and as many as you want"
"var": "responseOfJS"
}
},
{
"action": {
"condition": "context.code_block==='whatever_js_returned'",
"params": {
"comment": "add any action, such as ticket update in Zendesk and add parameters, e.g. {{task.body.requiredField2}}, you like",
"ticket_id": "{{task.body.requiredField1}}"
},
"type": "ticket.update",
"integration_id": "{addTheIntegrationIdHere}",
"var": "saveResponseFromAction"
}
},
{
"http": {
"url": "someURL",
"method": "PUT",
"params": {some parameters},
"header": {some header},
"body": {some body}
}
}
]
},
"selection_settings": {
"enable": false
},
"scheduled_at": "now"
}
}

The SMS Sent template

Below, you see the full template for the SMS Sent automation. You can also view the template in the Task section of babelforce manager.

{
"required_fields": [
"body.conversationSession",
"body.integrationId",
"body.ticketComment",
"body.fromNumber",
"body.consumerNumber",
"body.ticketSubject",
"body.requesterId",
"body.zendeskTicketId",
"body.agentSourceId",
"body.agentName"
],
"task": {
"type": "sms.automation",
"body": {
"additional_fields": {},
"custom_fields": {},
"tags": []
},
"actions": {
"on_scheduled": [
{
"js": {
"code": "() => {return {'additional_fields':task.body.additional_fields==null?{}:task.body.additional_fields,'custom_fields':task.body.custom_fields==null?{}:task.body.custom_fields,'tags':task.body.tags==null?[]:task.body.tags};}",
"var": "zendesk_fields"
}
},

{
"js": {
"var": "code_block",
"code": "()=>{var ticketId=Number(task?.body?.zendeskTicketId);if(Number.isInteger(ticketId)){return 'update_ticket'}return 'create_new_ticket'}"
}
},
{
"action": {
"condition": "context.code_block==='update_ticket'",
"params": {
"comment": "Agent {{task.body.agentName}} reply: \n {{task.body.ticketComment}} \n ----- \n from number +{{task.body.fromNumber}} \n to number +{{task.body.consumerNumber}}",
"ticket_id": "{{task.body.zendeskTicketId}}",
"public": false,
"status": "pending",
"assignee_id": "{{task.body.agentSourceId}}",
"add_tags": "{{context.zendesk_fields.tags}}",
"additional_fields": "{{context.zendesk_fields.additional_fields}}",
"custom_fields": "{{context.zendesk_fields.custom_fields}}"
},
"type": "ticket.update",
"integration_id": "{{task.body.integrationId}}",
"var": "ticketUpdate"
}
},
{
"http": {
"condition": "context.code_block==='update_ticket'",
"url": "{{env.babelforce.url}}/api/v2/conversations/{{task.body.conversationSession}}/session",
"method": "PUT",
"header": {
"Authorization": "Bearer {{task.token.access_token}}",
"Content-Type": "application/json"
},
"body": {
"ivr.ticketId": "{{{task.body.zendeskTicketId}}}"
}
}
},
{
"action": {
"condition": "context.code_block==='create_new_ticket'",
"type": "enduser.lookup",
"integration_id": "{{task.body.integrationId}}",
"var": "lookupEnduser",
"context": {
"consumer": {
"number": "{{task.body.consumerNumber}}"
}
},
"params": {
"create_if_none_found": true,
"sort_by": "Updated at"
}
}
},
{
"action": {
"condition": "context.code_block==='create_new_ticket'",
"params": {
"public": false,
"subject": "{{task.body.ticketSubject}}",
"comment": "Agent {{task.body.agentName}} sent new message: \n {{task.body.ticketComment}} \n ----- \n from number +{{task.body.fromNumber}} \n to number +{{task.body.consumerNumber}}",
"requester_id": "{{task.body.requesterId}}",
"tags": "{{context.zendesk_fields.tags}}",
"additional_fields": "{{context.zendesk_fields.additional_fields}}",
"custom_fields": "{{context.zendesk_fields.custom_fields}}",
"status": "pending",
"assignee_id": "{{task.body.agentSourceId}}"
},
"type": "ticket.create",
"integration_id": "{{task.body.integrationId}}",
"var": "ticketCreate"
}
},
{
"http": {
"condition": "context.code_block==='create_new_ticket'",
"url": "{{env.babelforce.url}}/api/v2/conversations/{{task.body.conversationSession}}/session",
"method": "PUT",
"header": {
"Authorization": "Bearer {{task.token.access_token}}",
"Content-Type": "application/json"
},
"body": {
"ivr.ticketId": "{{{context.ticketCreate.data.session.[integration.zendesk_v2.ticket.id]}}}"
}
}
}
]
},
"selection_settings": {
"enable": false
},
"scheduled_at": "now"
}
}

The SMS Received template

Below, you see the full template for the SMS Received automation. You can also view the template in the Task section of babelforce manager.

{
"required_fields": [
"body.conversationSession",
"body.integrationId",
"body.ticketComment",
"body.fromNumber",
"body.toNumber",
"body.ticketSubject",
"body.idleTime",
"body.requesterId"
],
"task": {
"type": "sms.automation.additional.fields",
"body": {
"additional_fields": {},
"custom_fields": {},
"tags": []
},
"actions": {
"on_scheduled": [
{
"js": {
"code": "() => {return {'additional_fields':task.body.additional_fields==null?{}:task.body.additional_fields,'custom_fields':task.body.custom_fields==null?{}:task.body.custom_fields,'tags':task.body.tags==null?[]:task.body.tags};}",
"var": "zendesk_fields"
}
},
{
"http": {
"url": "{{env.babelforce.url}}/api/v2/conversations/{{{task.body.conversationSession}}}",
"method": "GET",
"header": {
"Authorization": "Bearer {{{task.token.access_token}}}",
"Content-Type": "application/json"
},
"var": "conversation"
}
},
{
"js": {
"var": "code_block",
"code": "()=>{if(context.conversation.item.idle<{{task.body.idleTime}}){return 'conversation_session'}return 'create_new_ticket'}"
}
},
{
"http": {
"condition": "context.code_block==='conversation_session'",
"url": "{{env.babelforce.url}}/api/v2/conversations/{{task.body.conversationSession}}/session",
"method": "GET",
"header": {
"Authorization": "Bearer {{task.token.access_token}}",
"Content-Type": "application/json"
},
"var": "sessions"
}
},
{
"js": {
"var": "code_block",
"code": "()=>{var i=context?.sessions?.item['ivr.ticketId'];if(i!==null&&(''+i).trim().length>0&&Number.isInteger(Number(i))){return 'update_ticket'}return 'create_new_ticket'}"
}
},
{
"action": {
"condition": "context.code_block==='update_ticket'",
"params": {
"comment": "Customer replied to message: \n {{task.body.ticketComment}} \n ----- \n from +{{task.body.fromNumber}} \n to +{{task.body.toNumber}}",
"ticket_id": "{{context.sessions.item.[ivr.ticketId]}}",
"public": false,
"status": "open",
"add_tags": "{{context.zendesk_fields.tags}}",
"additional_fields": "{{context.zendesk_fields.additional_fields}}",
"custom_fields": "{{context.zendesk_fields.custom_fields}}"
},
"type": "ticket.update",
"integration_id": "{{task.body.integrationId}}",
"var": "ticketUpdate"
}
},
{
"action": {
"condition": "context.code_block==='create_new_ticket'",
"type": "enduser.lookup",
"integration_id": "{{task.body.integrationId}}",
"var": "lookupEnduser",
"params": {
"create_if_none_found": true,
"sort_by": "Updated at"
}
}
},
{
"action": {
"condition": "context.code_block==='create_new_ticket'",
"params": {
"public": false,
"subject": "{{task.body.ticketSubject}}",
"comment": "Customer sent new message: \n {{task.body.ticketComment}} \n ----- \n from +{{task.body.fromNumber}} \n to +{{task.body.toNumber}}",
"requester_id": "{{task.body.requesterId}}",
"tags": "{{context.zendesk_fields.tags}}",
"additional_fields": "{{context.zendesk_fields.additional_fields}}",
"custom_fields": "{{context.zendesk_fields.custom_fields}}"
},
"type": "ticket.create",
"integration_id": "{{task.body.integrationId}}",
"var": "ticketCreate"
}
},
{
"http": {
"condition": "context.code_block==='create_new_ticket'",
"url": "{{env.babelforce.url}}/api/v2/conversations/{{task.body.conversationSession}}/session",
"method": "PUT",
"header": {
"Authorization": "Bearer {{task.token.access_token}}",
"Content-Type": "application/json"
},
"body": {
"ivr.ticketId": "{{{context.ticketCreate.data.session.[integration.zendesk_v2.ticket.id]}}}"
}
}
}
]
},
"selection_settings": {
"enable": false
},
"scheduled_at": "now"
}
}

Was this article helpful?

/

Comments

0 comments

Please sign in to leave a comment.