Sync (Data Transfer)

Sync enables the synchronization of data such as tickets, threads, and contacts between Zoho Desk and external services. Desk offers the below methods to perform two-way data sync.

 

Pull Request from Desk

The pull URL helps to fetch data from the external service to desk periodically. This ensures Zoho Desk to stay updated with the latest information from the external service. Desk periodically invokes a request to the endpoint specified in the sync pull property of resource.json. Pull requests are made every 4 minutes from the desk. You can send the new / updated data of tickets and threads that need to be updated in desk in the specified SYNC_OBJECT format.

*Tip : If you want to keep track of the syncing status of the data you transferred (e.g number of tickets remaining to be sync),use channelState property to store the tracking data during pull, push & import requests.

Data Sync Format for Pull Request 

Zoho Desk accepts data to be synced in the SYNC_OBJECT format during pull requests from Desk. SYNC_OBJECT contains SYNC_TICKET_OBJECT and SYNC_THREAD_OBJECT.

 
SYNC_OBJECT


{
    "channelState":"{\"my_pending_data\":\"298092,289782,2767\"}",
    "data":{
        "tickets": [
            #SYNC_TICKET_OBJECT,
            #SYNC_TICKET_OBJECT,
            #SYNC_TICKET_OBJECT,
            ... max 1000
        ],
        "threads": [
            #SYNC_THREAD_OBJECT,
            #SYNC_THREAD_OBJECT,
            #SYNC_THREAD_OBJECT,
            ... max 1000
        ]
    }
}
    
  

 

FieldTypeDescription
channelStatestringValue to be stored in the extension's channelState configParam. Can be used to store the state of the channel sync progress.
Refer Channel State
dataJSONObject
SYNC_DATA_OBJECT
Contains the data to be imported from the external service, and the data to be converted to desk compatible format. Supported properties are
  • Tickets
  • Threads
 

Channel State

Channel State represents the channelState non-secure configParam which is automatically added for the channel type of extensions during installation. Channel State can be used as the current status of the channel's syncing process such as the last fetched item, remaining API limit in the external service, pending or next set of entities to be fetched, and so on.      

The value of the channelState param can be any string which will be updated in the extension's channelState configParam during the pull & push requests. If you don't want to update the channelState of the extension in ZohoDesk, then exclude the channelState key while sending the sync request response.
 

 
SYNC_DATA_OBJECT

Data format to be specified in the sync response's data property.

FieldTypeDescription
ticketsJSONArray (1000)
SYNC_TICKET_OBJECT
Array of ticket properties to be imported.
threadsJSONArray (1000)
SYNC_THREAD_OBJECT
Array of thread properties to be imported.

 

Create or Update Tickets in Desk 

Channel integration allows you to create and update tickets for external resources. To import tickets to the desk, you need to provide the ticket details in the SYNC_TICKET_OBJECT format within the data.tickets property of the pull-request from desk and push-data to desk response.

 
SYNC_TICKET_OBJECT

Extra object holds extra information about the ticket/thread for the extension.


{
  "data":{
    "tickets":[
      {
        "extId":"whatsapp:+919994411345",
        "subject":"How to reset the configuration?",
        "createdTime":"2023-04-10T13:34:26.000Z",
        "actor": #SYNC_ACTOR_OBJECT,
        "extra": #SYNC_EXTRA_OBJECT
      },
      ...
    ],
    "threads":[]
    }
}
  

Now,let's take a look at the params supported in the SYNC_TICKET_OBJECT

FieldTypeDescription
extIdstring
required
Unique ID of the ticket in the external service.
Refer External ID
actorJSONObject
SYNC_ACTOR_OBJECT
required
Details about the author.
subjectstring (255)
required
Subject of the ticket.
extraJSONObject
SYNC_TICKET_OBJECT
Extra information about the ticket for the extension.
createdTimeTimestamp ISO FormatCreated time of the ticket.

Note: An important usage of the syncing via channel integration is, some of the read-only system-computed fields such as createdTime, modifiedTime and direction of the thread can be overridden with the original value in the external service.

emailstringEMail ID of the ticket.
phonestringPhone number of the ticket.
descriptionstringDescription of the ticket.
statusstringStatus of the ticket.
categorystringTicket category.
subCategorystringTicket sub category.
resolutionstringResolution of the ticket.
dueDateTimestamp ISO FormatDue date for resolving the ticket.
prioritystringPriority of the ticket.
classificationstringClassification of the ticket.
customFieldsJSONObjectCustom fields in the ticket.
assigneeIdlongID of the agent to whom the ticket is assigned.
teamIdlongID of the team assigned to resolve the ticket.
productIdstringProduct to which the ticket is mapped.

 

 
SYNC_ACTOR_OBJECT

Actor object defines the properties of the author of the resource (i.e by whom this resource was made) on the external service.



{
    "name"        :"John Snow",
    "displayName" :"John Snow @johnstark",
    "email"       :"john@gmail.com",
    "phone"       :"+918987654321",
    "extId"       :"39jdiwkndw3ninj",
    "photoURL"    :"https://example.com/profile/39jninj/photo.jpg"
}
  

 

FieldTypeDescription
extIdstring
required
Unique ID of the person in the external service. Used for contacts profiles sync.
namestring
required
Name of the person in the external service.
displayNamestringName to be displayed on the contact's detail page. If not provided, the value of the name will be considered as displayName by default.
emailstringEmail ID of the author on external service. If provided, this profile will be added under the contact who have the same Email Id.
phonestringPhone number of the author on external service. If provided, this profile will be added under the contact who have the same Phone Number.
photoURLstring
URL
URL of the author's photo on external service.
 
 
SYNC_EXTRA_OBJECT

 {

    "key"            :"Post-{{ticket.id}}-Comment-{{thread.id}}-Details",
    "queriableValue" :"Post-{{ticket.id}}-Details"
    "value"          :{
                        "likes":3809,
                        "comments":453
                      }
}
FieldTypeDescription
keystring
required
Value for the key property can be a templated string with supported placeholders.Key for which the given value has to be stored in the DB storage. Refer Extra key
valueJSONObject
required
Specifies the value that needs to be stored for the given template key.
queriableValuestring
required
Specifies a common lookup group of the given key-value pair which will be useful for lookup from the database.

 

 
External ID (extId)

External ID (extId) is a unique Identifier for the tickets created via channel integrated app.The creation and updation of channel tickets are based on extId. The extId property is crucial for identifying the corresponding desk entity associated with the external resource during synchronization. When syncing, if no entity is found in the desk with the given extId, a new entity will be created. Otherwise, the existing entity with the matching extId will be updated. The extId should only contain the following characters: A-Z, a-z, 0-9, @, $, &, +, :, ., { }, ( ), #, -, _, +.

Use Case: Convert each Facebook Post to a ticket in desk.

Let's consider the case of a Facebook post, where each post has a unique ID. As a developer, you can set this unique ID as the extId. When a data from the Facebook service is passed with an extId, let's say "298393",to create a ticket in Desk, the following process occurs:

If any of the tickets in the desk has same extId, then the ticket will be updated with the provided data; otherwise, a new ticket will be created with the given extId
This mechanism ensures that the tickets in Desk are synchronized with the corresponding Facebook posts using the extId as the identifier.

 
Extra key

This key helps to store the additional data (i.e., the data not supported by default) relative to the external service entity.The key itself is a unique identifier.

For Example,

When a like count of a post has to be stored in the extension's db storage so that an app can re-access them, the external resource response's extra.key can be specified as facebook_comment_{{thread.id}}_data.

When the external resource is processed and generated as a desk thread with threadId 2980928, the key will be replaced as facebook_comment_2980928_data and the value given in the extra.value will be stored for the key facebook_comment_2980928_data in that app's DB storage. Later the app can lookup the DB storage from the widget with the key facebook_comment_2980928_data to get the value of the likes count.

Note:Extra Object Supports 2 merge Fields {{ticket.id}} and {{thread.id}} for replacing ticket and thread Ids respectively.

Create or Update Threads in Desk

Channel integration allows you to add threads or replies  to the Zoho Desk tickets from the external service. To import threads, you need to provide the thread details in the SYNC_THREAD_OBJECT format within the data.threads property during the pull-request from Desk and push-data to Desk responses.

 
SYNC_THREAD_OBJECT

 {
  "data":{
    "tickets":[],
    "threads":[
        {
            "extId":"SMa8974b1b935d957ffd9",
            "extParentId":"+123456789",
            "createdTime":"2023-04-10T11:54:03.000Z",
            "content":"What surprised",
            "direction":"in",
            "from":"+00032882",
            "to":["+00000273637"],
            "canReply":true,
            "extra": #SYNC_EXTRA_OBJECT,
            "actor": #SYNC_ACTOR_OBJECT
      },
      ...
    ]
   
  }
}

Now, let's take a look at the params supported in the SYNC_THREAD_OBJECT

FieldTypeDescription
extIdstring
required
Unique ID of the ticket in the external service.
Refer External ID
extParentIdstring
required
Parent Entity's ID of the thread in the external service.Threads having the same parentId will be grouped under the same ticket whose extId is equal to the parentId.
Refer External Parent ID
actorJSONObject
SYNC_ACTOR_OBJECT required
Details about the author.
contentstring (255)
required
Content of the thread
directionstringSpecifies the direction of the thread. Supported Values are
  • in (incoming thread)
  • out (outgoing thread)
extraJSONObject
SYNC_EXTRA_OBJECT
Extra information about the ticket for the extension.
attachmentUrlsJSONArray
String
Urls of the attachments to be added to the thread. Refer Adding External Attachments
createdTimeTimestamp ISO FormatCreated time of the ticket.
modifiedTimeTimestamp ISO FormatModified time of the thread.
canReplyBooleanSpecifies whether replies can be added to this thread.
contentTypestringContent-Type of the thread. Supported Values are
  • text/plain
  • text/html
fromstringFrom address of the thread.
Default : Channel Name
toJSONArray
string
Direct recipients of the thread.
ccJSONArray
string
cc'ed recipients of the thread.
bccJSONArray
string
bcc'ed recipients of the thread
 
 
External Parent ID (extParentId)

The External Parent ID (extParentId) constraint allows you to group multiple threads under a single ticket. It contains the parent ID of the external resource in the external service. For a thread in external resource, the extParentId plays a crucial role in identifying the correct ticket in Desk where the reply should be added.

When an extParentId is provided for a thread resource, the system looks for a ticket  that has the matching extId in Desk. If a ticket has the matching extParentId, the thread will be added to that specific ticket; otherwise, the thread will not be added. 

This constraint ensures that threads are associated with the correct tickets in Desk based on the extParentId, allowing for organized and structured communication within the ticketing system

For Example:
Let's consider a Facebook post comment, the extParentId for each comment will be the ID of the Facebook post comment. By setting the extId of the ticket as the unique ID of the Facebook post, comments with the same extParentId (which matches the ID of the post) will be grouped under the same ticket.

This means that all comments associated with a specific Facebook post will be organized and grouped together within the same ticket in Zoho Desk. The extParentId serves as a reference to link the comments to the correct ticket based on the ID of the Facebook post. This grouping ensures that all comments related to a particular post are easily accessible and managed within the corresponding ticket in Zoho Desk.

 

Push Request

When an agent replies toa ticket or thread (created by channel integration) from the desk , the reply is pushed to the Resources.channel.sync.push (Url specified in the push key in resource.json) endpoint of the extension in PUSH_REPLY_PAYLOAD  format which needs to be delivered to the external service so that the reply will be processed further. The push endpoint given in the resource.json is responsible for handling the desk reply, delivering it to the external service, and submitting the status & response of the processed reply to the desk.

 
PUSH_REPLY_PAYLOAD

   {
  "configParams": {
    "myConfigParam1": "My value for configParam",
    "myConfigParam2": "My value for configParam2",
    "myConfigParam3": "My value for configParam3"
  },
"resource": {
      "summary": "reply",
      "attachments": [],
      "visibility": "public",
      "author": 
	  {
	      "name": "John Snow",
	      "email": "john.snow@example.com",
	      "type": "AGENT",
	      "photoURL": "https://desk.zoho.com/api/v1/agents/387829/photo?orgId=28732"
	    }
      "channel": "CUSTOMCHANNEL",
      "replyToExtId": null,
      "extParentId": "1687767277870_1455839043203",
      "content": "reply",
      "hasAttach": false,
      "responderId": "12346000000139001",
      "createdTime": "2023-06-26T08:15:35.387Z",
      "attachmentCount": "0",
      "id": "12346000020202035",
      "contentType": "text/plain",
      "ticketId": "12346000020200007"
    }
}
  
FieldTypeDescription
configParamsJSONObjectAll the non-secure config params defined in the extension.
resourceJSONObject
DESK_REPLY_THREAD_OBJECT
Details of the Agent's reply to be sent to the external service.
 
DESK_REPLY_THREAD_OBJECT
FieldTypeDescription
extParentIdstringExternal Id of the thread's parent ticket. (i.e) parent entity's ID of the thread in the external service.
replyToExtIdstringHelps to add reply to the message in the external service. External Id of the thread to which the reply has to be added.
ticketIdlongID of the ticket in desk
visibilitystringDefines the visibility of the thread , i.e., public or private
responderIdlongThe ID of the Agent who replied
idlongID of the thread.
authorlongDetails about the author with the following properties.
  • name
  • email
  • type
  • photoURL
contentstringContent of the thread.
contentTypestringSummary of the thread.
hasAttachBooleanSpecifies whether the thread has attachments.
attachmentsJSONArrayRepresents attachments in the thread. Each attachment object contains properties.
  • id
  • name
  • size
  • href
attachmentCountintegerCount of the attachments
createdTimeTimestamp ISO FormatCreated time of the thread in Desk.

 

The progress of an agent reply from the desk for a ticket or thread created via channel integration are,

  • Agent reply is added to the desk with status PENDING.

  • Reply is pushed to the push_endpoint of the app.

  • Status is updated based on the acknowledgement message (response received) from the url.

 

Note:Desk expects the acknowledgement message in the PUSH_REPLY_RESPONSE_OBJECT Format.

 
PUSH_REPLY_RESPONSE_OBJECT

 

FieldTypeDescription
extIdstringUnique ID of the thread added in the external service.
extraJSONObject
SYNC_EXTRA_OBJECT
Extra information about the thread for the extension.
canReplyBooleanSpecifies whether replies can be added to this thread.
fromstringFrom Address of the thread.
Default : Channel Name
tostringDirect recipients of the thread

 

Sample Deluge code for PUSH_REPLY_RESPONSE_OBJECT


result = Map();
result.put("statusCode","200");
extIdMap = Map();
extIdMap.put("extId",responseMessageId);
extIdMap.put("canReply", true);
result.put("message",extIdMap);
return result;
  
 

Push Data to Desk

Using the following API, you can push tickets and threads to the desk from external service without the 4-minute delay that happens while syncing the data, if the external service you want to sync has webhook support. The API expects the payload in the SYNC_OBJECT format.

Request URL:

https://desk.zoho.com/api/v1/channels/{{installationId}}/import

Request Method:

POST

Content-Type:

application/json

RequestBody:JSONObject in the SYNC_OBJECT format.

Note: This is usually used when the service that you are integrating has webhook support.

This import API can be called in 2 ways.

  • Via Functions in sigma (Zoho infrastructure)
  • Via third party (External Infrastructure)

Via Functions in Sigma  

For calling import API from functions in sigma, you can use deluge invokeurl. Follow the steps given below:

You need two functions to achieve this.

1. A trigger function to subscribe to the external webhook when extension is installed , and to create sigma url as shown in the example.

  

deskDomain = "https://" + data.get("service_domain");
orgId = data.get("integ_scope_id");
securityContext = data.get("encapiKey");
encapiKey = data.get("encapiKey");
zapp_uuid = data.get("zapp_uuid");
appVersion = data.get("version");
appId = data.get("app_install_id");
encapiKey = zoho.encryption.urlEncode(encapiKey);
installationId = data.get("service_app_id");
sigmaDomain = data.get("sigma_domain");
sigmaExecutionDomain = data.get("sigma_execution_domain");
info "firstInfo:  "+sigmaExecutionDomain;
securityContext = zoho.encryption.urlEncode(securityContext);
webhookResponseBody = data.get("payload");

webhook_url = "https://" + sigmaExecutionDomain + "/workspace/invokefunction?sigma_function_uuid=d93317a8-1d8d-4008-9f58-42306363ae4d&sigma_function_version=2&integ_scope_id=" + orgId +"&app_install_id=" + appId + "&custom_response=true&auth_type=apikey&encapiKey=" + encapiKey;

//Copy the 2nd function url from the functions tab and generate the url as given.

//You can call this webhook_url from your external service

2. A function used to handle the logic: to push data to desk , you can call import API from the function as given in the example.



installationId = data.get("service_app_id"); 
pushToDesk = Map();
pushToDesk = {"data":{"threads":{{"extParentId":threadId,"extId":messageId.replaceAll("%","-"),"actor":actor,"content":bodyContent,"direction":"in","attachmentIds":uploadedUrlList,"canReply":true}}}};
//Convert your data in SYNC_RESPONSE_OBJECT format
info pushToDesk;
//Import API to push data to desk
import_ticket = invokeurl
[
  url :"https://desk.zoho.com/api/v1/channels/"+installationId+"/import"
  type :POST
  parameters:pushToDesk + " "
  connection:"pushdataconnection"
 ]; 
 info import_ticket;
}
return Map();
    
  

Via third party  

To call the desk's "channels/import" API from a third party, the orgId and securityContext params are mandatory. You can obtain these details during any of the extension event callbacks & store them so that you can reuse them during the channels/import API call.
 

*Tip : Call the channels/import API through the Desk Invoke API (proxy) so that the authentications will be handled automatically (Using connections).

For Example:

You may have subscribed to events such as onConfigParamAdd, onDeskAuthorize or onTPAAuthorise in the Platform Event Callbacks, which will make a request to the callback URL mentioned in the manifest with the securityContext & orgId during the respective events. During this event, you may subscribe to another service. The given orgId and securityContext can be stored or can be added in the subscription URL (e.g queryParams ) so that whenever the external service requests you with respect to the subscription, you can obtain the orgId and the securityContext from the URL and use them to call the desk "channels/import" API.