Zoho Desk Platform
Introduction

ZOHO MARKETPLACE - AN INTRODUCTION

At Zoho Desk, we believe in delivering good user experiences and making already good experiences better. In that vein, stepping into Zoho Marketplace is an effort towards achieving the latter goal.

Zoho Marketplace is an online store where users can find extensions that deliver business value by enhancing the core functionality of the Zoho product they use. It is similar to Google's Play Store, where you find apps for installation and use, according to your need. Until now, one could create extensions only for Zoho CRM, Creator, Connect, Cliq, Recruit, SalesIQ, Mail, and Reports. Going forward, they can create extensions for Zoho Desk too, and make a positive impact in the customer service space.

As a developer, you can create extensions that combine Zoho Desk's functionalities with those of third-party tools to make work easier and more effective for the end-users of Zoho Desk. You do not require extensive expertise or experience in programming to create extensions. Functional knowledge of HTML, CSS, and JavaScript can help you to a great extent.

After you create and submit your extension for review, we test its functionalities and provide you with feedback for improving its effectiveness, if required. When the extension is ready for use, we host it on Marketplace for end-users to discover and use.

To keep yourself updated about changes to Zoho Desk Extension development capabilities, such as APIs and SDKs, follow the Zoho Desk Extension Developers forum.

Quick Start

Installing the Node

$ node -v v10.x or Above

As the first step to creating an extension, you must install the node.js runtime environment. You can download node.js from here (versions above 10.X are supported). After installing node.js, you can verify its version using the following command:

Installing the ZET CLI

Next, you must install the ZET (Zoho Extension Toolkit) Command Line Interface (CLI) tool, which enables you to build, test, and package extensions for Zoho products.

sudo npm install -g zoho-extension-toolkit

If you use a Mac/Unix system for development, run the following command to install ZET:

npm install -g zoho-extension-toolkit

If you use a Microsoft Windows system, run the following command:

The -g command option ensures that the installation is global. With a global installation, you can call commands and work on your extensions and CLI from anywhere within your machine.

zet -v 0.23.6 or Above

After ZET is installed, help information regarding the zet command would appear. You can then verify the version of the tool, using the following command:

D: \zet\projects\demoproject>zet -help Usage: index [options] [command] Options: -v, —version Show the version number -h, —help output usage information Commands: Init, Creates a new project template directory run Starts a local server with current directory as context Validate Validates the current app with validation rules Pack Packs the project to upload into marketplace

Running ZET will display all the commands supported, as shown in the right panel.














init

zet init

This command creates a new project for the extension.

D:\zet\projects>zet init ? Select the Zoho service for your widget and hit enter key (Use arrow keys) > Zoho Desk Zoho CRM ZES

Executing this command displays the list of Zoho Services available. Choose Zoho Desk and press the Enter key.




D:\zet\projects>zet init ? Select the Zoho service for your widget and hit enter key Zoho Desk ? Project Name demoproject

After choosing Zoho Desk, provide a name for the new project.



D:\zet\projects>D:\zet\projects>zet init ? Select the Zoho service for your widget and hit enter key Zoho Desk ? Project Name demoproject Initializing project at: D:\zet\projects\demoprojects Installing NPM dependencies… Project Initialized D:\zet\projects\demoprojects Run the following commands: Cd demoproject Zet run D:\zet\projects>

After you enter the name, a project template directory with all the necessary folders, dependency node packages, and files is created.









The image below shows the default folder structure of an extension project.

run

This command runs the http server hosting the extension.

D:\zet\projects>D:\zet\projects>zet run

1. To start the server and test the extension, run the following command:

This command makes the http server accessible through the 5000 port of your local machine. Make sure that the 5000 port is not occupied, before you start the server.

2. To verify if the server started successfully, open the following URL in your browser: http://localhost:5000/plugin-manifest.json

validate

D:\zet\projects>zet validate

This command validates the extension by checking if it follows the guidelines defined in this section. To perform this validation, run the following command:

After you execute this command, the result of the validation appears. Check the result and make any changes, if required. After this step, you can proceed to package the extension files for upload.

Guidelines

During validation, the details in the plugin-manifest.json file are checked to verify if the following conditions are met:

pack

zet pack

The project directory contains the source code and other node modules necessary for locally testing the extension. However, the final zip file you upload to Marketplace must contain only the files and folders essential for running the extension. ZET makes this packaging process easy through the following command:

After you execute this command, ZET creates a zip file containing all files in the app folder and the plugin-manifest.json file of your extension. This zip file is stored in the dist folder of the project.

Building Your First Extension

Let’s get started by creating an extension. First, open the terminal/command prompt window, and navigate to the directory under which you want to create your extension.

Then, perform the following steps:

  1. Run the init command.
  2. Under the list of services, choose Zoho Desk.
  3. Enter a name for the extension project.
  4. Press the Enter key.

The extension project is created with the necessary directories and files.

Testing the Extension

To test your extension, perform the following steps:

  1. Depending on the computer you use, open Terminal or Command Prompt and navigate to your project folder.
  2. Execute the zet run command.
  3. If you use Mozilla Firefox, open https://127.0.0.1:5000/plugin-manifest.json in a new tab, and click Advanced ---> Accept the Risk and Continue.
  4. If you use Google Chrome, open https://127.0.0.1:5000/plugin-manifest.json in a new tab and click Advanced ---> Proceed to Unsafe. If the Proceed to Unsafe option does not appear, enable the chrome://flags/#allow-insecure-localhost Chrome flag and restart the browser.  
  5. Next, login to your Zoho Desk account. If you do not have a Zoho Desk account, sign up here.
  6. On the upper-right side of the Zoho Desk screen, click the Setup icon.
    The Setup page appears.
  7. On this page, under the Developer Space section, click Build Extensions.
  8. On the Build Extensions page, click Enable Developer Mode.
    This action refreshes the page and activates the Developer Mode.

  9. dev-mode-image
  10. Now the extension is installed in your Zoho Desk portal. For example, if one of the locations of the extension is Ticket Right Panel (desk.ticket.detail.rightpanel), then the widget will load in this location.
    You can now test your extension to verify if it behaves the way you intended.

  11. ticket-right-panel-ex
  12. After testing the extension, you can disable the Developer Mode by clicking the Disable Developer Mode button on the Build Extensions page.
  13. Note: Only users with the Support Administrator user profile can build extensions.

Validating the Extension

To validate your extension and verify if it follows the guidelines mentioned earlier, perform the following steps:

  1. Open the terminal/command prompt and navigate to your project folder.
  2. Execute the zet validate command.
    The results of the validation process appear. Make changes, if required.

Packaging the Extension

To package your extension as a zip file that contains only the relevant files, perform the following steps:

  1. Open the terminal/command prompt and navigate to your project folder.
  2. Execute the zet pack command.
    The zip file of your extension is created in the dist folder of your project.

Uploading the Extension

Uploading the Extension to the Marketplace

After packaging your extension, perform the following steps to upload the zip file of your extension for review:

  1. Go to the Sigma website.
  2. Click the New Extension button.



    The upload process flow begins.

Based on the visibility option you choose, your extension can be of two types:

Private:

If you want to use the extension only within your organization or help desk portal, choose Private in the upload form. After you complete the upload process, you will be provided with an installation URL, using which you can install the extension in your portal.

Public:

If you want to let other end-users of Zoho Desk find your extension in Marketplace and install it in their portal, choose Public in the upload form.

In public extension, you can make it available for free or for a price. In Zoho Desk, payment for extensions is charged either per installation or per user. End-users can opt for a trial of the extension to explore its functionalities. If they are satisfied with the extension and decide to purchase it, they can choose the payment type that suits them.

The form through which you submit more details of your extension appears. Follow the wizard through and upload your extension.

  1. In the General information form, provide the following details:
    • Release Audience: Visibility option for the extension: Public or Private
    • Developer Name/Org: Name of the organization or individual developer who created the extension
    • Contact Number: Phone number to contact the extension developer
    • Developer Website: Website of the extension developer
    • Help URL: Link to the extension's help documentation

    After entering these details, click the Next Step button. The App information form appears.

  2. In the App information form, enter the following details:
    • App Title: Name of the extension
    • Tagline: Short, catchy phrase that describes the function of the extension
    • Category: Business function/operation related to the extension
    • App Description: Brief description of the extension
    • App Summary: Development summary of the extension and other important notes
    • Logo: Logo of the extension/product/developer
    • Banner: Banner image that should appear on the extension detail page
    • Screenshots: Screenshots of the extension

    After entering these details, click the Next Step button. The Uploads form appears.

  3. In the Uploads form, configure the following settings:
    • Zoho Service: Zoho product related to the extension
    • Upload Zip File: Final, packaged zip file of the extension

    After performing these steps, click the Next Step button. The Validation page appears.

  4. On the Validation page, verify the details you entered, and perform one of these two steps::
    • If all the information is accurate, click the Save button. The extension will be submitted for review.
    • If any inaccurate information is present, click the Back button and perform the required corrections in the relevant form. After making the necessary correction(s), click the Save button in the Validation page.

    After you upload your extension on the Sigma site, the Zoho Desk team reviews it for functionality and usability, and provides you with feedback if any enhancements are required. The extension is made available on Marketplace after it passes the review and functions as intended.

  5. For more details, refer to the Application Submission process section.

Updating the Extension

Updating the Extension to the Zoho Desk

If you come up with new features, enhancements to existing features, or bug fixes, you can update your extension to improve its functionality and performance. Each update increments the version of the extension.

To upload the updated version of your extension, perform the following steps:

  1. Go to the Sigma website and click your extension.
  2. On the upper-right corner of the extension detail page, click the Edit button. The extension information form with previously filled data appears. Edit the details, if required, and click Next.
  3. In the final form, upload the zip file of the updated extension and submit it for review.

The review process of the updated extension begins.

After the review is completed, one of these two events happens:

When you release a new version of your extension, the Update option for it will be visible in the user's Zoho Desk portals after a period of 24 hours. If the extension must be updated immediately after the new version is released, users can find the Update option on the detail page of the extension.

Installing the Extension

Installing the Extension in Zoho Desk

End-users of Zoho Desk can view and install the public extensions you create, from two locations:

If the Zoho Desk administrator installs an extension from the Setup page, the extension is made available to users in the current portal. On the other hand, if the administrator tries to install the extension from the Marketplace site, a list of all the available portals appears, letting the admin choose the one in which the extension must be installed.

Configuration

Below are the different configurations you can set for your extension.

Plugin Manifest

Configuring Connectors for Third-Party Services

To configure a connector for a third-party service, perform the following steps:

  1. Create a third-party connection by performing the steps under the Connect with a custom service section in this help article.
  2. On the Connection Summary page that appears after you create the connection, click the JSON tab. Copy the code snippet under the tab.
  3. Open the plugin-manifest.json file of your extension and paste the code snippet under the connectors key.
    Your extension is now connected to the third-party service you want.
  4. Note: To configure your extension to fetch data from the third-party service, include the connectionLinkName key in the request API.

Locations

This key, which must be configured in the plugin-manifest file, defines the location(s) for your extension's widgets. Keep in mind that a single extension can be rendered in multiple locations.

Location Name
Top Band (Modules section) desk.topband
Bottom Band desk.bottomband
Desk Telephony Extension desk.extension.telephony
Background desk.background
Desk Extension Preference desk.extension.preference
Ticket Detail Right Panel desk.ticket.detail.rightpanel
Ticket Detail Sub-tab desk.ticket.detail.subtab
Ticket Detail Left tab desk.ticket.detail.lefttab
Ticket Detail More Actions desk.ticket.detail.moreaction
Ticket Detail Thread More Actions desk.ticket.thread.moreaction
Ticket Form Right Panel desk.ticket.form.rightpanel
Contact Detail Right Panel desk.contact.detail.rightpanel
Contact Detail Sub-tab desk.contact.detail.subtab
Contact Detail Left tab desk.contact.detail.lefttab
Contact Form Right Panel desk.contact.form.rightpanel
Account Detail Right Panel desk.account.detail.rightpanel
Account Detail Sub-tab desk.account.detail.subtab
Account Detail Left tab desk.account.detail.lefttab
Account Form Right Panel desk.account.form.rightpanel

Product Level

Top Band - desk.topband

This location refers to the top band from which users navigate between Zoho Desk modules. The extensions you installed in this location will be shown up under the more icon. When user picks an extension from the more icon, it will utilise the entire screen.


Image



Bottom Band - desk.bottomband

This location refers to the space next to the Keyboard Shortcuts icon on the bottom-right side of the Zoho Desk page. When a user clicks the Marketplace icon on this location, the extensions configured here are listed. Clicking an extension on this list opens the corresponding widget on the left of the Marketplace icon.


desk.bottomband-image


Desk Telephony Extension - desk.extension.telephony

This location refers to another space next to the Keyboard Shortcuts icon on the bottom-right side of the Zoho Desk page. This location is meant exclusively for telephony-based extensions. Please note that there can be only one active telephony extension in a Zoho Desk portal.


telephony-image


Background - desk.background

This location is neither visible to end-users nor does it appear in the DOM elements. Extensions installed here will not appear anywhere, but the HTML code for them can be defined as required. The main purpose of this location is to aid inter-widget communication, which refers to the communication between two widgets when they are active. In some cases, one of the widgets might not be active. In such a scenario, data can be pushed from one widget to another using the widget configured in this location.


Desk Extension Preference - desk.extension.preference

This location refers to the Configuration tab that resides next to the General Settings tab on the extension detail page. The main purpose of this location is to get the configuration from the end-user. Extensions configured in this location are visible to users with the Support Administrator profile even if the extension visibility setting is not exclusively enabled for the profile. This location is enabled only if all other configuration is completed.


extension-pref-Image

Ticket Detail page

Right Panel - desk.ticket.detail.rightpanel

This location refers to a collapsible panel on the right side of the ticket detail page. The extension widget loads on this panel when the user clicks the Marketplace icon.




Sub-Tab - desk.ticket.detail.subtab

This location refers to the sub-tab (More icon) in the ticket detail page. Clicking the icon lists the extensions installed in this location. When a user clicks an extension from this list, the extension loads in the entire sub-tab section.






Left Tab - desk.ticket.detail.lefttab

This location refers to the black, vertical strip on the left side of the ticket detail page. The Marketplace logo appears here. Clicking the logo lists the extensions installed in this location.




More Actions - desk.ticket.detail.moreaction

This location refers to the more actions icon at the top-right side of the ticket detail page. Clicking this icon displays a menu in which the extensions are listed below the other standard ticket-related actions, such as Edit, Share, Follow, Delete, and so on.






Thread More Actions - desk.ticket.thread.moreaction

This location refers to the more actions icon at the top-right side of an individual thread. Clicking this icon displays a menu in which the extensions are listed below the other standard thread-related actions, such as Reply, Forward, Print, and so on.






Ticket Form Page

desk.ticket.form.rightpanel

This location refers to the Marketplace icon at the top-right side of the Add Ticket form. Clicking this icon displays a collapsible panel that lists all the extensions configured in this location. 






Contact Detail page

Right Panel - desk.contact.detail.rightpanel

This location refers to a collapsible panel on the right side of the contact detail page. The extension widget loads on this panel when the user clicks the Marketplace icon.




Sub-Tab - desk.contact.detail.subtab

This location refers to the sub-tab (More icon) in the contact detail page. Clicking the icon lists the extensions installed in this location. When a user clicks an extension from this list, the extension loads in the entire sub-tab section.






Left Tab - desk.contact.detail.lefttab

This location refers to the black, vertical strip on the left side of the contact detail page. The Marketplace logo appears here. Clicking the logo lists the extensions installed in this location.




Contact Form Page

Contact Form Right Panel - desk.contact.form.rightpanel

This location refers to the Marketplace icon at the top-right side of the Add Contact form. Clicking the icon displays a collapsible panel that lists all the extensions configured in this location.


contact-form-right-panel-image

Account Detail page

Right Panel - desk.account.detail.rightpanel

This location refers to a collapsible panel on the right side of the account detail page. The extension widget loads on this panel when the user clicks the Marketplace icon.




Sub-Tab - desk.account.detail.subtab

This location refers to the sub-tab (More icon) in the account detail page. Clicking the icon lists the extensions installed in this location. When a user clicks an extension from this list, the extension loads in the entire sub-tab section.






Left Tab - desk.account.detail.lefttab

This location refers to the black, vertical strip on the left side of the account detail page. The Marketplace logo appears here. Clicking the logo lists the extensions installed in this location.




Account Form Page

Account Form Right Panel - desk.account.form.rightpanel

This location refers to the Marketplace icon at the top-right side of the Add Account form. Clicking the icon displays a collapsible panel that lists all the extensions configured in this location.


account-form-right-panel-image

Features

Data APIs

Zoho Desk provides a set of APIs that facilitate interaction between your extension and your help desk portal. The APIs available are listed below:

Ticket Object

This object provides you with access to the various properties of a ticket. It is accessible only from the desk.ticket.detail.rightpanel, desk.ticket.detail.subtab and desk.ticket.detail.lefttab widget locations.

Property
Request
Response
departmentId
ticket.departmentId
{'status':'success','ticket.departmentId':'12345678901234567'}
email
ticket.email
{'status':'success','ticket.email':'youremail@domainname.com'}
subject
ticket.subject
{'status':'success','ticket.subject':'Faulty phone charging port'}
description
ticket.description
{'status':'success','ticket.description':'Product replacement'}
status
ticket.status
{'status':'success','ticket.status':'Open'}
dueDate
ticket.dueDate
{'status':'success','ticket.dueDate':'21/02/2018 12:00 PM'}
threadCount
ticket.threadCount
{'status':'success','ticket.threadCount':'1'}
isSpam
ticket.isSpam
{'status':'success','ticket.isSpam':'false'}
createdTime
ticket.createdTime
{'status':'success','ticket.createdTime':'29/01/2018 11:55 PM'}
modifiedTime
ticket.modifiedTime
{'status':'success','ticket.modifiedTime':'31/01/2018 11:27 AM'}
assignee
ticket.assignee
{'status':'success','ticket.assignee': { id : '34567844', name : 'User Name', teamid : '3456789', team : 'My Team' }
owner
ticket.owner
{'status':'success','ticket.owner':'Agent Name'}
id
ticket.id
{'status':'success','ticket.id':'12345678901234567'}
accountName
ticket.accountName
{'status':'success','ticket.accountName':'Account Name'}
phone
ticket.phone
{'status':'success','ticket.phone':'9876543210'}
productName
ticket.productName
{'status':'success','ticket.productName':'Product Name'}
commentCount
ticket.commentCount
{'status':'success','ticket.commentCount':'1'}
priority
ticket.priority
{'status':'success','ticket.priority':'High'}
channel
ticket.channel
{'status':'success','ticket.channel':'Phone'}
classification
ticket.classification
{'status':'success','ticket.classification':'Problem'}
category
ticket.category
{'status':'success','ticket.category':'General'}
subCategory
ticket.subCategory
{'status':'success','ticket.subCategory':'Sub General'}
contactName
ticket.contactName
{'status':'success','ticket.contactName':'Contact Name'}
customField
ticket.cf
{'status':'success','ticket.cf':{"cf_counter": "12","cf_deal_id": "2288589490093"}}
contactId
ticket.contactId
{'status':'success','ticket.contactId':'271540000000358001'}
number
ticket.number
{'status':'success','ticket.number':'104'}


Request:

ZOHODESK.get(“ticket”."<property>").then(function(response) { //Response - requested detail }).catch(function(err){ //Error Handling })


Response:

{ "ticket"."<property>": "<Requested Value>", "status": "success" }

Contact Object

This object provides you with access to the various properties of a contact. It is accessible only from the contact detail page locations, and global widget locations.

Property
Request
Response
accountId
contact.accountId
{'status':'success','contact.accountId':'12345678901234567'}
cf
contact.cf
{'status':'success','contact.cf': {cf_longitude: "0"}}
city
contact.city
{'status':'success','contact.city':'Chennai'}
country
contact.country
{'status':'success','contact.country':'India'}
createdTime
contact.createdTime
{'status':'success','contact.createdTime':'2018-05-17T08:32:19.000Z'}
customerHappiness
contact.customerHappiness
{'status':'success','contact.customerHappiness':{badPercentage: "0", okPercentage: "0", goodPercentage: "0"}}
description
ticket.description
{'status':'success','contact.description':'Customer Support Lead'}
email
contact.email
{'status':'success','contact.email':'support@zohodesk.com'}
facebook
contact.facebook
{'status':'success','contact.facebook':'29/01/2018 11:55 PM'}
modifiedTime
contact.modifiedTime
{'status':'success','contact.modifiedTime':'31/01/2018 11:27 AM'}
firstName
contact.firstName
{'status':'success','contact.firstName':'Lawrence'}
id
contact.id
{'status':'success','contact.id':'12345678901234567'}
isAnonymous
contact.isAnonymous
{'status':'success','contact.isAnonymous':false}
isDeleted
contact.isDeleted
{'status':'success','contact.isDeleted':false}
isEndUser
contact.isEndUser
{'status':'success','contact.isEndUser':false}
isFollowing
contact.isFollowing
{'status':'success','contact.isFollowing':false}
isSpam
contact.isSpam
{'status':'success','contact.isSpam':false}
isTrashed
contact.isTrashed
{'status':'success','contact.isTrashed':false}
lastName
contact.lastName
{'status':'success','contact.lastName':'Lawrence'}
layoutDetails
contact.layoutDetails
{'status':'success','contact.layoutDetails':{id: "271540000000074005", layoutName: "marketplacedemo"}}
layoutId
contact.layoutId
{'status':'success','contact.layoutId':'271540000000074005'}
link
contact.link
{'status':'success','contact.link':'https://desk.zoho.com/support/marketplacedemo/ShowHomePage.do#Contacts/dv/271540000000144009'}
mobile
contact.mobile
{'status':'success','contact.mobile': '044 - 67447070'}
modifiedTime
contact.modifiedTime
{'status':'success','contact.modifiedTime':'2020-03-12T10:44:38.000Z'}
ownerId
contact.v
{'status':'success','contact.ownerId':'271540000000103011'}
phone
contact.phone
{'status':'success','contact.phone':'044 - 67447070'}
photoURL
contact.photoURL
{'status':'success','contact.photoURL':'271540000000103011'}
secondaryEmail
contact.secondaryEmail
{'status':'success','contact.secondaryEmail':'support@zohodesk.com'}
state
contact.state
{'status':'success','contact.state':'Tamil Nadu'}
street
contact.street
{'status':'success','contact.street':'GST Road'}
title
contact.title
{'status':'success','contact.title':'271540000000103011'}
twitter
contact.twitter
{'status':'success','contact.twitter':'271540000000103011'}
type
contact.type
{'status':'success','contact.type':'Paid User'}
zip
contact.zip
{'status':'success','contact.zip':'603202'}


Request:

ZOHODESK.get("contact.<property>").then(function(response) { //Response - requested detail }).catch(function(err){ //Error Handling })


Response:

{ "contact.<property>": "<Requested Value>", "status": "success" }

Account Object

This object provides you with access to the various properties of a account. It is accessible only from the account detail page locations, and global widget locations.

account
Property
Request
Response
annualrevenue
account.annualrevenue
{'status':'success','account.annualrevenue':'1200000.0'}
cf
account.cf
{'status':'success','account.cf': {cf_longitude: "0"}}
city
account.city
{'status':'success','account.city':'Pleasanton'}
code
account.code
{'status':'success','account.code':'94588'}
country
account.country
{'status':'success','account.country':'United States'}
createdTime
account.createdTime
{'status':'success','account.createdTime':'s2018-04-19T06:22:28.000Z'}
description
account.description
{'status':'success','account.description':'Customer Support Account'}
email
account.email
{'status':'success','account.email':'support@zohodesk.com'}
fax
account.fax
{'status':'success','account.fax':'044 67447172'}
id
account.id
{'status':'success','account.id':'271540000000083214'}
industry
account.industry
{'status':'success','account.industry':'Large Enterprise'}
isDeleted
account.isDeleted
{'status':'success','account.isDeleted':false}
isFollowing
account.isFollowing
{'status':'success','account.isFollowing':false}
isTrashed
account.isTrashed
{'status':'success','account.isTrashed':false}
lastName
account.lastName
{'status':'success','account.lastName':'Lawrence'}
layoutDetails
account.layoutDetails
{'status':'success','account.layoutDetails':{id: "271540000000074009", layoutName: "marketplacedemo"}}
layoutId
account.layoutId
{'status':'success','contact.layoutId':'271540000000074009'}
link
account.link
{'status':'success','account.link':'https://desk.zoho.com/support/marketplacedemo/ShowHomePage.do#Accounts/dv/271540000000083214'}
modifiedTime
account.modifiedTime
{'status':'success','account.modifiedTime':'2020-03-12T10:44:38.000Z'}
ownerId
account.ownerId
{'status':'success','account.ownerId':'271540000000103011'}
phone
account.phone
{'status':'success','account.phone':'1 888 900 9646'}
state
account.state
{'status':'success','account.state':'CA'}
street
account.street
{'status':'success','account.street':'Hacienda Drive'}
website
account.website
{'status':'success','account.website':'https://www.zoho.com/'}


Request:

ZOHODESK.get("account.<property>").then(function(response) { //Response - requested detail }).catch(function(err){ //Error Handling })


Response:

{ "account.<property>": "<Requested Value>", "status": "success" }

Current Call Object

Listed below are the properties related to the user object:

Property
Request
Response
contactId
currentCall.contactId
{'status':'success','currentCall.contactId':'271230063478992'}
number
currentCall.number
{'status':'success','currentCall.number':'1 888 900 9646'}


Request:

ZOHODESK.get("currentCall.<property>").then(function(response){ // response requested-details }).catch(function(err){ // Error Handling })


Response:

{ "currentCall.<property>": "<Requested Value>", "status": "success" }

User Object

Listed below are the properties related to the user object:

Property
Request
Response
id
user.id
{'status':'success','user.id': '3456786789'}
fullName
user.fullName
{'status':'success','user.fullName':'User FullName'}
email
user.email
{'status':'success','user.email' : "emailID@domainname.com"}
tzoffset
user.tzoffset
{'status':'success','user.tzoffset' : '330'}
timeZone
user.timeZone
{'status':'success','user.timeZone' : 'Asia/Kolkata'}
portals
user.portals
{'status':'success','user.portals': { "myportal" : "3456789" }}


Request:

ZOHODESK.get("user.<property>").then(function(response){ // response requested-details }).catch(function(err){ // Error Handling })


Response:

{ "user.<property>": "<Requested Value>", "status": "success" }

Portal Object

Listed below are the properties related to the portal object:

Property
Request
Response
plan
portal.plan
{'status':'success','portal.plan':'Enterprise Edition'}
id
portal.id
{'status':'success','portal.id':'54516290'}
name
portal.name
{'status':'success','portal.name'."portalname"}
customDomainName
portal.customDomainName
{'status':'success','portal.customDomainName'."https://customdomainname.com/"}


Request:

ZOHODESK.get("portal.<property>").then(function(response){ // response requested-detail }).catch(function(err){ // Error Handling })


Response:

{ "portal.<property>": "<Requested Value>", "status": "success" }

Department Object

Listed below are the properties related to the department object:

Property
Request
Response
list
department.list
{'status':'success','department.list':[{"name": "department name", "depid": "1234567890123", "agents": ["1234567890"], "isActive": true, "isPrivate": false }'}
info
department.info
{'status':'success','department.info'."[{"name": "department name", "depid": "1234567890123", "agents": ["1234567890"], "isActive": true, "isPrivate": false }"}
id
department.id
{'status':'success','department.id'."123456789012345678"}

Request:

ZOHODESK.get("department.<property>").then(function(response){ // response requested-detail }).catch(function(err){ // Error Handling })


Response:

{ "department.<property>":"<Requested Value>", "status":"success" }

Set Wrapper

Data Storage APIs

Extension APIs 

These APIs fetch or set information related to the extension.

Get extension config variable: Fetch the installation parameters of the extension. In production mode, only those parameters that have the value of the secure key set to false are returned. In development mode, all parameters are returned, irrespective of the value of the secure key.

ZOHODESK.get("extension.config").then(function(response){ // response returns the installation parameters of the extension }).catch(function(err){ // error handling })

Request:



{ "extension.config": [ { "name": "apikey", "mandatory": true, "type": "text", "value": "apikey", "userdefined": true, "secure": true, "default": "TestData" } ], "status": "success" }

Response:














Set value for extension config variable: Dynamically sets the value for a config variable defined in the plugin manifest file. New config variables cannot be defined using this API. 

ZOHODESK.set('extension.config', {name : 'test', value : "value"}).then(function(res){ //response returns the value saved }).catch(function(err){ })

Request:


{ extension.config: { "data": [ { "userDefined": false, "name": "test", "options": "[]", "secure": false, "mandatory": false, "value": "value", "varId": "12345678901234567" } ] }, status: "success" }

Response:













Get extension configured departments: This API fetches the departments configured for the extension on installation.

ZOHODESK.get("extension.departments").then(function(response){ // response returns the department configured of the extension }).catch(function(err){ // error handling })

Request:



{ "extension.departments": [{ "name": "departmentName", "id": "1234567" }], "status": "success" }

Response:














Get extension configured profiles: This API fetches the profiles configured for the extension on installation.

ZOHODESK.get("extension.profiles").then(function(response){ // response returns the profiles configured of the extension }).catch(function(err){ // error handling })

Request:



{ "extension.profiles": [ { "name": "Support Manager", "id": "1234567890" }, { "name": "Newbie Agent", "id": "1234567890" }, { "name": "Administrator", "id": "1234567890" }, { "name": "Standard", "id": "1234567890" } ], "status": "success" }

Response:




















Get extension configured users: This API fetches the users configured for the extension on installation.

ZOHODESK.get("extension.users").then(function(response){ // response returns the users configured of the extension }).catch(function(err){ // error handling })

Request:



{ "extension.users": [ "1234567890", "0987654321" ], "status": "success" }

Response:














Request API

Resource API

This API gets the resource details of the extension. Make sure the resourceName key is given in resources.json file.
Refer resourceName for more details.
Note : Response will be given against the resourceName given in the extension.

ZOHODESK.get("extension.resource", {resourceName: "field1", resourceType: "fields"}).then(function(response){ //response returns the value saved }).catch(function(err){ // error handling })

Request:


{ "status": "success", "extension.resource": { "resourceName": "field1", "id": "4000000020060", "resourceType": "fields", "apiName": "cf_counter" } }

Response:













My Custom Permission API

This API gets the permission details of logged in user.
Note : Response will be given if the permissions are configured in the extension.

ZOHODESK.get("extension.permissions").then(function(response){ //response returns the value saved }).catch(function(err){ // error handling })

Request:


{ "status": "success", "extension.permissions": [ { "apiName": "createResolution", "isEnabled": true }, { "apiName": "editResolution", "isEnabled": false }, { "apiName": "deleteResolution", "isEnabled": false }] }

Response:













Invoke APIs

Below are some extra APIs that you can use in your extension. 

ROUTE_TO

This command navigates between the multiple subtabs on the ticket detail page. Listed below are the different routes supported:

  1. ticket.suggestedArticles
  2. ticket.timeline
  3. ticket.attachments
  4. ticket.history
  5. ticket.resolution
  6. ticket.approval
  7. ticket.task
  8. ticket.timeEntry
  9. ticket.conversation
  10. ticket.thread
ZOHODESK.invoke('ROUTE_TO','ticket.attachments')

Code format:


Record Based Navigation

This API navigates to the detail view or edit form of the record whose ID is specified in the request:

Param
Description
entity
Name of the entity. Values allowed are: ticket, contact, account, call, task, and event.
page
Page to open. Values allowed are add and edit.
id
ID of the record to navigate to. This parameter is not needed for the add action.


ZOHODESK.invoke('ROUTE_TO',{entity: 'ticket', page:'edit', id: '271540000009314019'})

Code format:


This API closes the modal that is currently opened.  

ZOHODESK.invoke('MODAL_CLOSE')

Code format:


RESIZE

This API resizes the widget.



ZOHODESK.invoke('RESIZE')

Code format: (without width and height specified)


ZOHODESK.invoke('RESIZE', {width: '40%', height: '50%'})

Code format:


Event APIs

The following APIs help you incorporate custom functionalities in your extension when certain events occur in your help desk portal.

Ticket Events

You can configure extensions to receive information when an event, such as adding a comment to a ticket or opening a different ticket occurs on the ticket detail page.

On adding a Comment

When a user adds a comment to a ticket from UI, the ticket_comment.add event is broadcast along with comment details.

This event can be invoked from the following locations:

App.instance.on("ticket_comment.add", function(data){ //data gives the comment detail })

Code format:



{ comment: "<div>Hello</div>", commentId: "90108000000847123", displayName: "Agent Name", displayTime: "09 Jun 2021 06:34 PM", isPublicComment: true }

The response/data sent to the extension will be in the following format.

Field Field type Description
commentId String ID of the comment added
isPublicComment Boolean Key that specifies if the comment is public or private
displayTime String Time when the comment was added
displayName String Name of the user who added the comment
comment String Content of the comment

On opening the Ticket Replyeditor

When a user opens the reply editor of a ticket, the ticket_replyEditor.opened event is broadcast along with details of the reply editor.

This event can be invoked from the following locations:

App.instance.on("ticket_replyEditor.opened", function(data){ //data gives the comment detail })

Code format:





{ mode: "replyAll", threadId: "90108000000495471", ticketId: "20400443020642124" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
mode String Mode of the reply editor: reply or replyAll
threadId String ID of the thread opened
ticketId String ID of the ticket

On changing the values in Custom Fields

When a user changes the value in a custom field on the left panel of the ticket detail page, the ticket_customFields.changed event is broadcast along with the new value in the field.

This event can be invoked from the following locations:

App.instance.on("ticket_${customFields}.changed", function(data){ //data gives the comment detail })
Eg:- App.instance.on("ticket_cf_single_line_1.changed", function(data){ })

Code format:


{ ticket.Custom Field 1: { oldValue: "new valu", newValue: "new value" } }

The response/data sent to the extension will be in the following format.

Field Field Type Description
oldValue String Previous value in the field
newValue String New value in the field


On Opening a Topic in the User Community

When a user opens a forum topic in the Community module, the community.topic.loaded event is broadcast along with the ID of the topic.

This event can be invoked from the following locations:

App.instance.on("community.topic.loaded", function(data){ //data gives the Article Id })

Code format:


{ topicId: "50138000520495421" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
topicId String ID of the forum topic

On Marking a Task as Completed

When a user marks a task as complete, the task_Completed event is broadcast.

This event can be invoked from the following locations:

App.instance.on("task_Completed", function(data){ //data gives the completed status })

Code format:


{ task_Completed: true }

The response/data sent to the extension will be in the following format.

Field Field Type Description
task_Completed Boolean Key that specifies if the task is completed or not

On Changing the Owner of Ticket

When a user changes the owner of a ticket, the ticket_owner.changed event is broadcast along with the details of the new owner.

This event can be invoked from the following locations:

App.instance.on("ticket_owner.changed", function(data){ //data gives the new owner details })

Code format:


{ ticket.owner: { type: "teamAgent", agent: { id: "90108000000190123", firstName: "NewAgent", lastName: "", fullName: "NewAgent", email: "NewAgent@something.com" }, team: { name: "newteam", id: "901080000012345", description: "description of team", departmentId: "90108000003700987" } } }

The response/data sent to the extension will be in the following format.

Field Field Type Description
type string Type of ticket owner. Values allowed are: Unassigned, agent, team, and teamAgent
agent Object Details of the agent. This key is returned only if the owner is an individual agent or an agent from a team
Field Field Type Description
id String ID of the agent
firstName String First name of the agent
lastName String Last name of the agent
fullName String Full name of the agent
email String Email ID of the agent
team Object Details of the team. This key is returned only if the owner is a team or an agent from a team
Field Field Type Description
id String ID of the team
name String Name of the team
description String Description of the team
departmentId String ID of the department to which the team belongs

On Moving to a Different Page

When a user moves from one page to another on their Zoho Desk portal, the pageChange event is broadcast along with details of the current view and previous view.

This event can be invoked from the following locations:

App.instance.on("pageChange", function(data){ //data gives the current view details and previous view details })

Code format:


{ previousView: { module: "ticket", page: "list" }, currentView: { module: "ticket", page: "detail" } }

The response/data sent to the extension will be in the following format.

Field Field Type Description
previousView Object Details of the previous page
Field Field Type Description
module String Module to which the page belongs
page String Type of the page: list or detail
currentView Object Details of the current page
Field Field Type Description
module String Module to which the page belongs
page String Type of the page: list or detail

On Changing or Applying a Response Template in the Ticket Reply Editor

When a user applies or changes a response template on the ticket reply editor, the ticket_replyEditor_template.loaded event is broadcast along with the content of the template and ID of the thread.

This event can be invoked from the following locations:

App.instance.on("ticket_replyEditor_template.loaded", function(data){ //data gives the template details })

Code format:


{ content: "Html Content of the template", id: "98787662312400000" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
content String Content of the template
id String ID of the template

On Moving from One Ticket to Another

When the user clicks a different ticket in the All Tickets view on the left panel, the ticket_Shift event is broadcast.  

This event can be invoked from the following locations:

App.instance.on("ticket_Shift", function(data){ //data provides status of ticket change as true }

Code format:


{ ticket_shifted: true }

The response/data sent to the extension will be in the following format.

Field Field Type Description
ticket_shifted Boolean Key that specifies if the ticket was loaded from the left panel on the ticket detail page

On Changing the Due Date of a Ticket

When a user changes the due date of a ticket, the ticket_dueDate.changed event is broadcast along with the details of the updated due date.

This event can be invoked from the following locations:




App.instance.on('ticket_dueDate.changed', function(data){ //data gives the duedate changed })

Code format:


{ ticket.dueDate :"08/15/2018 12:00 PM" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
ticket.dueDate String Updated due date and time for resolving the ticket

On Changing the Value in a Field

${module}Form.${fieldName}.changed - Details of the old and updated values in the field

This event can be invoked from the following locations:

App.instance.on('${formModulePage}_${fieldName}.changed', function(data){ })
Eg:- App.instance.on('ticketForm_email.changed', function(data){ //data gives the email value available in the field })

When a user changes the value in a field in the add ticket/contact/account form, the

  • ticketForm_{{fieldName}}.changed,
  • contactForm_{{fieldName}}.changed,
  • accountForm_{{fieldName}}.changed

event is broadcast along with the new value in the field.

{ ticketForm.email: { oldValue: "email@gmail.co", newValue: "email@gmail.com" } }

The response/data sent to the extension will be in the following format.

Field Field Type Description
${module}Form.${fieldName}.changed Object Values of the specified field:
Field Field Type Description
oldValue String Previous value in the field
newValue String New value in the field

On Changing the Priority of a Ticket

When a user changes the priority of a ticket, the ticket_priority.changed event is broadcast along with the details of the updated priority value.

This event can be invoked from the following locations:

App.instance.on('ticket_priority.changed', function(data){ //data gives the priority changed })
{ ticket.priority :"Low" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
ticket.priority String Priority of the ticket

On Changing the Classification of a Ticket

When a user changes the classification of a ticket, the ticket_classification.changed event is broadcast along with the details of the updated classification value.

This event can be invoked from the following locations:

App.instance.on('ticket_classification.changed', function(data){ //data gives the classification changed })
{ ticket.classification: "Feature" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
ticket.classification String Classification of the ticket

On Changing the Product Associated with a Ticket

When a user changes the product associated with a ticket, the ticket_productName.changed event is broadcast along with the details of the newly associated product.

This event can be invoked from the following locations:

App.instance.on('ticket_productName.changed', function(data){ //data gives the productname changed })
{ ticket.productName: "Finance" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
ticket.productName String Name of the product newly associated with the ticket

On Changing the Phone Number in a Ticket

When a user changes the phone number in a ticket, the ticket_phone.changed event is broadcast along with the new phone number.

This event can be invoked from the following locations:

App.instance.on('ticket_phone.changed', function(data){ //data gives the phone changed }) 
{ ticket.phone: "1 888 900 9646" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
ticket.phone String New phone number updated in the ticket

On Changing the Status of a Ticket

When a user changes the status of a ticket, the ticket_status.changed event is broadcast along with the details of the updated status value.

This event can be invoked from the following locations:

App.instance.on('ticket_status.changed', function(data){ //data gives the status changed }) 
{ ticket.status: "On Hold" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
ticket.status String Status of the ticket

On Opening Remote Assist

When a user initiates a Remote Assist session on the ticket detail view, the ticket_assist.click event is broadcast along with the status, owner, due date, and ID of the ticket.

This event can be invoked from the following locations:

App.instance.on('ticket_assist.click', function(data){ //data gives the spam status changed }) 
{ status: "Open", owner: "newTeam", dueDate: "No Due Date set", ticketId: "26811000000760569" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
status String Status of the ticket
owner String Agent name if ticket was assigned to an agent / Team name if ticket was assigned to a team or agent of the team / Unassigned
dueDate String Due date of the ticket / No Due Date Set
ticketId String ID of the ticket

On Changing the Spam Status of a Ticket

When user changes spam status of a ticket from UI, the ticket_isSpam.changed event will broadcast with details of the updated status.

This event can be invoked from the following locations:

App.instance.on('ticket_isSpam.changed', function(data){ //data gives the spam status changed }) 
{ ticket.isSpam: "true" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
ticket.isSpam String Key that specifies if the ticket is spam or not spam

Call Events

You can also configure extensions to receive information when a call-related event occurs in your help desk portal.

On Receiving a Call

When an agent receives a call, the call.ringing event is broadcast along with the phone number of the contact. Contact ID is included if the contact already exists in your portal.

This event can be invoked from the following locations:

App.instance.on('call.ringing', function(data){ //data gives the call information }) 
{ caseId: "901080000005819", contactId: "901080000004554", callType: "outbound", number: "9837650000", entityName: "new contact", callState: "ringing", ctinumber: "", caseNumber: "193", isCallAssociated: true, ztiCallId: "901080000007820" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
caseId String ID of the ticket
contactId String ID of the contact
callType String SType of the call: outbound or inbound
number String Phone number of the Contact
entityName String Entity name
callState String State of the call
( "ringing", "proceedanswer", "completed" )
ctinumber String CTI number of the contact
caseNumber String Ticket number
isCallAssociated Boolean Key that specifies if the call is associated with a ticket or not
ztiCallId String ZTI ID of the call

On Answering a Call

When an agent answers a call, the call.answered event is broadcast along with the phone number of the contact. Contact ID is included if the contact already exists in your portal.

This event can be invoked from the following locations:

App.instance.on('call.answered', function(data){ //data gives the call information }) 
{ caseId: "901080000005816", activityId: "901080000007003", contactId: "901080000004554", callType: "outbound", number: "", entityName: "new contact", callState: "proceedanswer", ctinumber: "", caseNumber: "193", isCallAssociated: true, ztiCallId: "901080000007821" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
caseId String ID of the ticket
contactId String ID of the contact
callType String Type of the call: outbound or inbound
number String Phone number of the Contact
entityName String Entity name
callState String State of the call
( "ringing", "proceedanswer", "completed" )
ctinumber String Key that specifies if the call is associated with a ticket or not
caseNumber String Ticket number
isCallAssociated Boolean Whether the call is associated
ztiCallId String ZTI ID of the call

On Completing a Call

When an agent completes the conversation with the customer and disconnects the call, the call.completed event is broadcast along with the phone number of the contact. Contact ID is included if the contact already exists in your portal.

This event can be invoked from the following locations:

App.instance.on('call.completed', function(data){ //data gives the call information }) 
{ caseId: "901080000005819", activityId: "901080000007820", contactId: "901080000004554", callType: "outbound", number: "", entityName: "new contact", callState: "completed", ctinumber: "", caseNumber: "193", isCallAssociated: true, ztiCallId: "901080000007820" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
caseId String ID of the ticket
contactId String ID of the contact
callType String Type of the call: outbound or inbound
number String Phone number of the Contact
entityName String Entity name
callState String State of the call
( "ringing", "proceedanswer", "completed" )
ctinumber String CTI number of the contact
caseNumber String Ticket number
isCallAssociated Boolean Key that specifies if the call is associated with a ticket or not
ztiCallId String ZTI ID of the call

On Making a Call

When a user makes a call from the ticket/contact/account/activity detail view or from the call reminder pop-up or from the dial pad, the click2Call event is broadcast along with details of the location from where the call is made and the ID of the resource (ticket/contact/account/activity) associated.

This event can be invoked from the following locations:

App.instance.on('click2Call', function(data){ //data gives the call information }) 
{ context: "ticket.detail.page", contactId:"123456789", caseId: "1234565234", activityId: "1241351321" }

The response/data sent to the extension will be in the following format.

Field Field Type Description
context String Name of the module from which the call was made ( ticket/contact/activity .detail.view, dial.pad, reminder.popUp )
caseId String ID of the ticket associated with the call
contactId String ID of the Contact
activityId String ID of the Activity

Hook APIs

Event Hooks help developers to introduce their own middlewares in the execution flow of certain UI actions.

Like events, developers need to subscribe to these event hooks to control the execution of UI action by allowing or terminating the flow.

For instance, let us consider that you are creating an extension for a bug-tracking software. The extension can record Zoho Desk tickets as issues in the bug-tracking software.

Now say a support agent tries to close a Zoho Desk ticket that has been recorded as an issue in the bug-tracking software. If the issue is not resolved in the third-party tool, but the ticket is closed in Zoho Desk, it would lead to a mismatch of status, causing confusion.

To prevent such a scenario, you can configure the extension to subscribe to the Zoho Desk hook defined for the close ticket event. With this hook in place, you can further configure your extension such that when an agent tries to close the ticket, the extension checks if the corresponding issue in the third-party tool is resolved. If it is resolved, the extension can give a go-ahead to close the ticket in Zoho Desk, else the ticket close event will not be executed and a relevant message will be displayed.


Zoho Desk currently supports hooks for the following events:


Extensions can pause the outcome of events by passing either boolean values or promises in the event handler.

If the boolean value TRUE is passed, the event will be executed

If the boolean value FALSE is passed, the event will be terminated.

Similarly, if the extension resolves the promise, the event will be executed, and if the extension rejects the promise, the event will be terminated. The reason for terminating the event can be displayed as an error message to end-users.

Extensions with subscriptions to hooks must respond within 30 seconds of the event getting triggered. If this time is exceeded, control provided to the extension will be lost and the event will be executed.

Multiple extensions can subscribe to a single hook. In such a scenario, the event will be executed only if all extensions subscribed to the event pass TRUE or resolve the promise. If even one extension passes FALSE or rejects the promise, the event will be terminated.



App.instance.on('ticket.reOpen', function(){  //return promise or boolean })

Hook to Use When a Ticket is Reopened

The ticket.reopen hook helps monitor the "reopen ticket" action. With this hook in place, if the custom logic returns the value true or if the promise is resolved by the extension, the ticket is reopened. If the custom logic returns false or if the promise is rejected by the extension, the ticket is not reopened.

This hook can be invoked from the following locations:



App.instance.on('ticket.close', function(){          })

Hook to Use When a Ticket is Closed

The ticket.close hook helps monitor the "close ticket" action. With this hook in place, if the custom logic returns the value true or if the promise is resolved by the extension, the ticket is closed. If the custom logic returns false or if the promise is rejected by the extension, the ticket is not closed.

This hook can be invoked from the following locations:



App.instance.on('ticket.status', function(){          })

Hook to Use When a Ticket Status is Changed

The ticket.status hook helps monitor the "status change" action. With this hook in place, if the custom logic returns the value true or if the promise is resolved by the extension, the ticket status is changed. If the custom logic returns false or if the promise is rejected by the extension, the ticket status is not changed.

This hook can be invoked from the following locations:

{ oldValue: "Open", newValue: "Closed" }

Response:

Field Field Type Description
oldValue String Previous value in the status field
newValue String New value in the status field

App.instance.on('ticket.comment', function(data){       })

Hook to Use When a Ticket Comment is Made


The ticket.comment hook helps monitor the "comment on ticket" action. With this hook in place, if the custom logic returns the value true or if the promise is resolved by the extension, the comment is added to the ticket. Additionally, the ID of the ticket, content of the comment, and visibility of the comment are returned by the hook. If the custom logic returns false or if the promise is rejected by the extension, the comment is not added to the ticket.

This hook can be invoked from the following locations:

{ ticketId: "26811000000760569", content: "<div>Hello</div>", isPublic: false }

Response:

Field Field Type Description
ticketId String ID of the ticket
content String Content of the comment
isPublic Boolean Key that specifies if the comment is public or private


App.instance.on('ticket.comment.edit', function(data){                   })

Hook to Use When a Ticket Comment is Edited


The ticket.comment.edit hook helps monitor the "edit ticket comment" action. With this hook in place, if the custom logic returns the value true or if the promise is resolved by the extension, the comment becomes editable. Additionally, the ID of the ticket, content of the comment, and visibility of the comment are returned by the hook. If the custom logic returns false or if the promise is rejected by the extension, the comment does not become editable.

This hook can be invoked from the following locations:

{ commentId: "90108000000849057", oldContent: "<div>Hello</div>", newContent: "<div>New Hello</div>", ticketId: "26811000000760569" } 

Response:

Field Field Type Description
ticketId String ID of the ticket
commentId String ID of the comment
oldContent String Previous content of the comment
newContent String Updated content of the comment


App.instance.on('ticket.reply', function(data){          })

Hook to Use When a Ticket Response is Sent


The ticket.reply hook helps monitor the "send ticket response" action. With this hook in place, if the custom logic returns the value true or if the promise is resolved by the extension, the response is sent. Additionally, the "from" address, "to" and "cc" addresses, ID and subject of the ticket, ID of the thread, and content of the response are returned by the hook. If the custom logic returns false or if the promise is rejected by the extension, the response is not sent.

This hook can be invoked from the following locations:

{ toAddress: "support@zohodesk.com", ticketId: "26811000000760569", ticketSubject: "[## 159 ##] sd", threadId: "", fromAdddress: "\"mpdemo\"", ccAddress: "support@mpdemo.zohodesk.com", content: "%3Cdivticket.replyetyle%3D'font-size%3A13.0px%3Bfont-family%3A%20Arial%20%2C%20Helvetica%20%2C%20Verdana%20%2C%sans-serif%3B'%3ETest%20Reply%3C%2Fdiv%3E" }

Response:

Field Field Type Description
ticketId String ID of the ticket
fromAdddress String Email ID from which the response is sent
toAddress String Email ID(s) to which the response is sent, separated by commas
ccAddress String Email ID(s) CCed in the response, separated by commas
ticketSubject String Subject of the ticket and ticket number
threadId String ID of the thread
content String Content of the response


App.instance.on('ticket.replyAndClose', function(data){            })

Hook to Use When a Ticket Response is Sent and the Ticket is Closed


The ticket.replyAndClose hook helps monitor the "send response and close ticket" action. With this hook in place, if the custom logic returns the value true or if the promise is resolved by the extension, the response is sent and the ticket is closed. Additionally, the "from" address, "to" and "cc" addresses, ID and subject of the ticket, ID of the thread, and content of the response are returned by the hook. If the custom logic returns false or if the promise is rejected by the extension, the response is not sent and the ticket is not closed.

This hook can be invoked from the following locations:

{ toAddress: "support@zohodesk.com", ticketId: "26811000000760569", ticketSubject: "[## 159 ##] sd", threadId: "26811000000783105", fromAdddress: "\"mpdemo\"", ccAddress: "support@mpdemo.zohodesk.com", content: "%3Cdivticket.replyasndcloseetyle%3D'font-size%3A13.0px%3Bfont-family%3A%20Arial%20%2C%20Helvetica%20%2C%20Verdana%20%2C%sans-serif%3B'%3E%3Cdiv%3ETest%3C%2Fdiv%3E%3Cdiv%3E%3Cblockquote%style%3D%22border-left%3A%201px%dotted%20%23e5e5e5%3Bmargin-left%3A5px%3Bpadding-left%3A%205px%3B%22%3E%3Cdiv%style%3D%22padding-top%3A%2010px%3B%22%3E%3Cdiv%id%3D%22ZDeskInteg%22%3E%3Cmeta%itemprop%3D%22zdeskTicket%22%20content%3D%f10fd68b0cc315920245c7c0aa2b93fdf0a3db99826a12352285b65f8e5fa7a9458849c5b9779fef3c050436477df54fca922a12fa4922ab7fd919f18ae07%22%3E%3C%2Fdiv%3E%3C%2Fdiv%3E%3C%2Fblockquote%3E%3C%2Fdiv%3E%3C%2Fdiv%3E" }

Response:

Field Field Type Description
ticketId String ID of the ticket
fromAdddress String Email ID from which the response is sent
toAddress String Email ID(s) to which the response is sent, separated by commas
ccAddress String Email ID(s) CCed in the response, separated by commas
ticketSubject String Subject of the ticket and ticket number
threadId String ID of the thread
content String Content of the response


App.instance.on('agent.channelStatus', function(data){            })

Hook to Use When the Agent Status of a Channel is Changed


The agent.channelStatus hook helps monitor the "change agent availability in channel" action. With this hook in place, if the custom logic returns the value true or if the promise is resolved by the extension, the availability status of the agent in the channel is updated. If the custom logic returns false or if the promise is rejected by the extension, the availability status of the agent is not updated.

This hook can be invoked from the following locations:

{ chatStatus: "OFFLINE", mailStatus: "ONLINE", phoneStatus: "ONLINE", isOnline: true }

Response:

Field Field Type Description
isOnline Boolean Key that specifies if the agent is online or not
chatStatus Boolean Key that specifies if the agent is online in the Chat channel or not
mailStatus Boolean Key that specifies if the agent is online in the Mail channel or not
phoneStatus Boolean Key that specifies if the agent is online in the Phone channel or not

Multi-Widget Support

.... "widgets": [ { "location": "desk.ticket.detail.rightpanel", "name": "Client SDK", "url": "/app/app-iframe-view.html", "logo" : "./app/img/1.png", "icon" : "./app/img/2.png" }, { "location": "desk.ticket.detail.subtab", "name": "Client SDK", "url": "/app/app-iframe-view.html", "logo" : "./app/img/1.png", "icon" : "./app/img/1.png" } ] }, ....

As mentioned earlier, users can access an extension through more than one widget on the Zoho Desk UI. Adding multiple widgets for your extension is a simple task. All that you need to do is include the properties of each widget, separated by comma, as shown in the right panel. 

Inter-Widget Communication

In some cases where an extension has multiple widgets, communication between each widget becomes crucial. This is made possible through inter-widget communication.

App.instance.getWidgets().then(function(widgets) { siblingwidgetId = widgets[0].widgetID var siblingWidget = App.instance.getWidgetInstance(siblingwidgetId); siblingWidget.emit('event', { from: Math.random() }); });

For instance, let us say an extension has two widgets: one at desk.ticket.detail.rightpanel and the other at desk.ticket.datil.subtab. Data from the widget on the right panel needs to be sent to the widget on the subtab. This requirement is achieved through the following code snippet:

Code Explanation

App.instance.getWidgets() returns the array of sibling widgets of the extension.

siblingwidgetId = widgets[0].widgetID gets the widgetID of the widget on the subtab. Here, widgets will have only one element. Therefore, iteration is not required. 

var siblingWidget = App.instance.getWidgetInstance(siblingwidgetId) returns the whole instance of the widget on the subtab.

siblingWidget.emit('event', {from : Math.random()}) sends the event response with data to the widget on the subtab. 

App.instance.on('event', function(data){ // data({from : "randomnumber"}) sent from other widget });

To enable the widget on the subtab to receive the event sent from the widget on the right panel, use the following code snippet:

Interface APIs

Notifications

Alerts/Confirmations

Besides the main widgets, you can also display information or fetch user input through modal boxes. Modal boxes are UI elements in which users must perform a particular action as part of the overall process. As a result, users will be able to continue using the app on the main window only after performing the said action on the modal box. 

To configure a modal box in your extension, write the necessary code in the modal.html file in the project folder, and reference this file where required in your extension. 

App.instance.modal({ url: '/app/modal.html', title: "Modal box" }).then(function(modalInfo) { var modalInstance = App.instance.getWidgetInstance(modalInfo.widgetID); modalInstance.on('modal.opened', function(data) { console.log('modal opened ++++++++++++++++++') }); }).catch(function(err) { console.log(err, "Modal error"); })

Below is a sample code for a widget that invokes a modal box. 










You can configure a modal box to appear in a smaller size when invoked first and then expand to display more information. You can make this possible through the resize command.

 
ZOHODESK.invoke('RESIZE');

To include this resizing option in the modal box, use the following command in the modal.html file:



















Authorizations

An extension user may need to authorize the Desk or Third party services for the developer to perform the authorized actions such as calling the APIs.

Available inbuilt authentication mode is



Connections

                    
Sample of Manifest with connections
{ ..., "connectors":[ { "connectionLinkName" : "test216", "connectionName" : "test", "serviceName" : "jira", "userAccess" : true, "isUserDefinedService" : false }, { "connectionLinkName" : "myconni", "connectionName" : "myconni", "serviceName" : "zlabs_integration", "userAccess" : true, "isUserDefinedService" : false, "scope" : ["ZohoCliq.Channels.All"] } ] ... }

Connections can be used for the authentication of Zoho and External Services which provides the simplified solution where the authentication process (such as OAuth Client registration, redirection) is handled internally.

Developers can create a connection in the Sigma for the existing & request for new services.

Connection Configuration JSON

After successfully adding the connection in the external Sigma , obtain the Connection Configuration JSON details of the connection and speficy it in the manifest's connectors property. Value for the connectors property should be an array of obtained Connection Configuration JSONs.














Desk Invoke API

Desk's invoke API acts as a proxy between the extension and the External Services or Desk for calling the APIs.

With Desk Invoke API,


                    
INVOKE API REQUEST FORMAT:
URL : api/v1/invoke RequestMethod : POST QueryParams : orgId RequestHeaders : HASH Content-Type : application/x-www-form-urlencoded RequestBody :     #INVOKE_API_REQUEST_PAYLOAD.
INVOKE API RESPONSE FORMAT:
ResponseCode : 200 Content-Type : application/json Response : #INVOKE_API_RESPONSE_OBJECT

Using Desk Invoke API

The query parameters such as orgId , securityContext and headerparam HASH are required for calling Invoke API . orgId & securityContext will be provided by desk during the Platform event callbacks. HASH should be generated using the SECRET and the Request URL inputs. Refer Generating Hash for Invoke API

#INVOKE_API_REQUEST_PAYLOAD

Field Required Type Description
securityContext yes string An Extension specific encrypted token that is used for identifying & authenticating the extension while calling the invoke API.
SecurityContext can be obtained during Platform event callbacks
requestURL yes string URL to be invoked.
requestType yes string HTTP method for invoking the
requestURL
For example,
  • GET
  • POST
  • PATCH
  • PUT
  • DELETE
postBody yes string Payload or body to be sent while invoking the requestURL.
headers yes string Headers to be sent while invoking the requestURL.
queryParams yes string QueryParams to be sent while invoking the requestURL.
connectionLinkName yes string If the authorization needs to be applied from the connections while invoking the requestURL, specify the unique connection name provided by the DRE.

                    

Sample of Invoke API HASH Generation

If manifest.secret => "my_secret_key_238392" For Sample Invoke API Inputs => requestURL = https://api.google.com/test requestType = POST queryParams = {} postBody = {} headers = {"orgId":376723} connectionLinkName = myConnectionLinkName (*ignore what you don't need)' Hash generation would be => let stringToHash = 'requestURL=https://api.google.com/test&requestType=POST&queryParams={}&postBody={}&headers={"orgId":376723}&connectionLinkName=myConnectionLinkName' then, generated HASH = hmac_sha256 ( stringToHash, "my_secret_key_238392" ); *Note: While generating HASH, only include the parameters which will be used in the
invoke API. Order of the fields while generating hash should be exactly
1.requestURL, 2.requestType, 3.queryParams, 4.postBody, 5.headers, 6.connectionLinkName and you can ignore the fields which you don't send.

Generate Hash for Invoke API

To call invoke API, HASH is mandatory. Hash is used to verify the invoke API call was originally made by the extension developer. HASH is an HMAC sha256 encrypted string with the key as app's secret (manifest.secret) provided in the manifest and the input as invokeAPI's payload.

Desk also will generate the HASH with the shared app's secret with the provided payload in the invoke API and will match the provided HASH in the desk invoke API's header. If the HASH does not match with the desk generated HASH, then the invoke API will not be processed.

The same process of authentication can be used by the extension during the extension callback events to verify the callback was originally sent by the desk.









Specifying Placeholders in Invoke API

You can specify configParams, authentication details as placeholders while calling the desk invoke API. The placeholders will be replaced with the original values before sending the request to requestURL.

Supported placeholders

Placeholder Syntax Location(s) Description
InstallationId {{PROPERTY}} requestURL,
queryParams,
requestHeaders
eg: https://desk.zoho.com
/api/v1/installations/
{{installationId}}/storage
Config
Params
{{PROPERTY}} requestURL,
queryParams,
requestHeaders
Name of
Config Param
eg : {{jiraAuthKey}}
resourceName {{RESOURCETYPE.RESOURCENAME.ID}} requestURL,
queryParams,
postBody,
requestHeaders
Get Field
eg : https://desk.zoho.com/api/v1/
organizationFields/{{fields.field1.id}}
resourceName {{RESOURCETYPE.RESOURCENAME.APINAME}} requestURL,
queryParams,
postBody,
requestHeaders
Set Field
eg : "cf": {
"{{fields.field1.apiName}}": "Testing"
}

                    
Sample of Specifying Connection in invokeAPI payload ... requestURL = https://api.google.com/test connectionLinkName = googleConnection ...

Specifying Authentication Details in Invoke API

Invoke API applies the authentication details specified while calling the API, find the below table for specifying the authentication details while calling the invoke API.

Auth Type Where to specify
( Parameter Name )
Description
connections payload (connectionLinkName) Name of the connectionLinkName has to be given in the query parameter
connectionLinkName while calling the invoke API.

                    
Sample Response : { "responseHeaders" : { "Cache-Control" : "private,no-cache,no-store,max-age=0,must-revalidate", "Set-Cookie" : "drecn=9e1f7200-bcdf-426d-9628-79ff4e9241c8; Path=/; Secure", "Vary" : "Accept-Encoding", "Expires" : "Thu, 01 Jan 1970 00:00:00 GMT", "X-XSS-Protection" : "1", "Content-Type" : "application/json;charset=utf-8" }, "response" : "{\"data\":[{\"ticketNumber\":\"176\",\"customerResponseTime\":\"2014-03-22T05:05:08.471Z\",\"productId\":null,\"contactId\":\"1892000000045028\",\"subject\":\"from forum\",\"dueDate\":\"2016-06-01T14:04:07.000Z\",\"departmentId\":\"1892000000006907\",\"channel\":\"Forums\",\"threadCount\":\"72\",\"priority\":\"High\",\"assigneeId\":\"1892000000056007\",\"closedTime\":null,\"commentCount\":\"0\",\"phone\":null,\"contact\":{\"firstName\":\"\",\"lastName\":\"as\",\"phone\":null,\"id\":\"1892000000045028\",\"type\":null,\"email\":\"manojkumar.s+4444@zohocorp.com\",\"account\":{\"website\":\"qwe.com\",\"accountName\":\"Man_Account\",\"id\":\"1892000000980421\"}},\"createdTime\":\"2014-03-06T09:49:50.000Z\",\"id\":\"1892000000094004\",\"email\":\"example@example.com\",\"status\":\"Open\"}]}", "statusCode" : "200" }

#INVOKE_API_RESPONSE_OBJECT

Field Type Description
statusCode integer Status Code from the requestURL by the invoke API request.
response string Response from the requestURL by the invoke API request.
responseHeaders string Response headers sent by the requestURL.

Extension Data Specific APIs

The following are the APIs available to the extension that can be accessed using Desk Invoke API. InstallationId wont be provided to the developers, instead he has to use installationId placeholder in Invoke API to call the apis.

Storage API

Extensions can make use of Storage API to access & modify data from the extension's DB storage. The available storage API 's are,

                    
#STORAGE_DATA_OBJECT { "key" : "color-red-data-id-278783", "queriableValue" : "color-red-datas", "value" : { "myResult" : "Diluted solution for red color experiment 128ml " } }

#STORAGE_DATA_OBJECT

Every storage object has the following properties.

Field Type Description
key string Key for the value which can be used to lookup.
value JSONObject Specifies the value that needs to be stored for the given key.
queriableValue string Used to group the multiple storage data. Specifies a common lookup group of the given key-value pair which will be useful for lookup from the database.

                    
Add data to storage: Request Format:
URL : https://desk.zoho.com/api/v1/installedExtensions/{{installationId}}/storage OAuth Scope : Desk.extensions.CREATE RequestMethod : POST RequestHeaders : orgId, Authorization Content-Type : application/json RequestBody :     #STORAGE_DATA_OBJECT.
Response Format:
ResponseCode : 200 Content-Type : application/json Response : #STORAGE_DATA_OBJECT

Add data to storage

Use this API to add data to the extension storage.


















                    
Get data from storage: Request Format:
URL : https://desk.zoho.com/api/v1/installedExtensions/{{installationId}}/storage OAuth Scope : Desk.extensions.READ RequestMethod : GET RequestHeaders : orgId, Authorization QueryParams : key, queriableValue, from, limit
Response Format:
ResponseCode : 200 Content-Type : application/json Response : JSONObject with data property containing the result as array of #STORAGE_DATA_OBJECT.

Get data from storage

Use this API to get data from the extension storage with the matching criteria.


















                    
Delete data from storage: Request Format:
URL : https://desk.zoho.com/api/v1/installedExtensions/{{installationId}}/storage OAuth Scope : Desk.extensions.DELETE RequestMethod : DELETE RequestHeaders : orgId, Authorization QueryParams : key
Response Format:
ResponseCode : 200

Delete data from storage

Use this API to delete data from the extension storage specifying the key.


















Configuration Param API

Extensions can make use of Configuration Param API to access & modify the extension's config params. The available storage API's are,

                    
#CONFIG_PARAM_API_REQUEST_OBJECT { "variables" : [ { "name" : "configParam1", "value" : "value for the configparam 1" }, { "name" : "configParam3", "value" : "value for the configparam 3" } ] } #CONFIG_PARAM_API_RESPONSE_OBJECT { "data" : [ { "defaultValue" : "default1", "userDefined" : true, "name" : "variable1", "options" : "[op1, opt2]", "secure" : true, "type" : "text", "mandatory" : false, "value" : "testing", "varId" : "4000000011017" } ] }

#CONFIG_PARAM_API_REQUEST_OBJECT

Field Type Description
variables JSONArray <#CONFIG_PARAM> Array of config params to be saved in desk

#CONFIG_PARAM

Field Type Description
name string Name of the config param
value string Value of the config param























                    
Add config params: Request Format:
URL : https://desk.zoho.com/api/v1/installedExtensions/{{installationId}}/configParams OAuth Scope : Desk.extensions.CREATE RequestMethod : POST RequestHeaders : orgId, Authorization Content-Type : application/json RequestBody :     #CONFIG_PARAM_API_REQUEST_OBJECT.
Response Format:
ResponseCode : 200 Content-Type : application/json Response : #CONFIG_PARAM_API_RESPONSE_OBJECT

Add or update config params

Use this API to add or update config params.















                    
Get config params: Request Format:
URL : https://desk.zoho.com/api/v1/installedExtensions/{{installationId}}/configParams OAuth Scope : Desk.extensions.READ RequestMethod : GET RequestHeaders : orgId, Authorization
Response Format:
ResponseCode : 200 Content-Type : application/json Response : #CONFIG_PARAM_API_RESPONSE_OBJECT

Get config params

Use this API to get extension's config params.
















Extension Log APIs

Extensions can make use of Log API to log the extension processes. Logs can be viewed on customer's support portal.

Logs will expire after 3days.

Path to Logs:

Parent Logs List:

Sub Logs List:

During development, logs are loaded in the developer's portal.

The param - reference is a random string that is used to group multiple logs. If multiple logs are grouped using reference, the first log will be the parent log and the rest will be sub logs. Developer can group a maximum of 10 logs using a reference. Means, a parent log can have 9 sub logs as maximum.

If the logs are created without reference, they are considered as parent logs.

The available APIs for Logs are,

                    
#EXTENSION_LOG_OBJECT { "reference" : "1d8bd6c8-5424-11e8-9c2d-fa7ae01bbebc", "description" : "jira issue is created with id = 10223", "installationId" : "112343231355", "title" : "created a jira issue" } #LOG_API_RESPONSE_OBJECT { "parentLogId" : "4000000022003", "extensionName" : "Jira", "description" : "jira issue is created with id = 10223", "logId" : "4000000023001", "installationId" : "112343231355", "title" : "create jira issue", "logTime" : "2018-05-10T07:33:13.000Z" }

#EXTENSION_LOG_OBJECT

Field Type Description
reference string a UUID string used to group multiple logs. Logs having the same reference key will be stored as a single log.
description string Log message.
installationId long The id of the installed extension
title string Title of the log















                    
Add a log: Request Format:
URL : api/v1/extensionLogs OAuth Scope : Desk.extensions.CREATE RequestMethod : POST RequestHeaders : orgId, Authorization Content-Type : application/json RequestBody :     #EXTENSION_LOG_OBJECT.
Response Format:
ResponseCode : 200 Content-Type : application/json Response : #LOG_API_RESPONSE_OBJECT

Add a Log

Use this API to add extension logs.















My Custom Permission API

Use this API to retrive the permission details of logged in user.

                        
#MYPERMISSION_DATA_OBJECT { "apiName" : "createResolution", "isEnabled" : true }

#MYPERMISSION_DATA_OBJECT

Every permission object has the following properties.

Field Type Description
apiName string apiName for the permission which can be used to lookup.
isEnabled Boolean Specifies whether permission is enabled or not.

                                
Get my custom permission: Request Format:
URL : https://desk.zoho.com/api/v1/installedExtensions/{{installationId}}/myCustomPermissions OAuth Scope : Desk.extensions.READ RequestMethod : GET RequestHeaders : orgId, Authorization
Response Format:
ResponseCode : 200 Content-Type : application/json Response : JSONObject with data property containing the result as array of #MYPERMISSION_DATA_OBJECT.

Get permission data

Use this API to get permission details of logged in user.


















Platform Event Callbacks

                    

plugin-manifest.json

... "callbackListener":{ "onZohoAuthorise": "https://zohodeskapp.com/authorized.php", "onConfigParamAdd": "https://zohodeskapp.com/adjustfiler.php", "onUninstall": "https://zohodeskapp.com/revokeWebhook.php" }, ...

Marketplace supports callbacks for extension's events. Developer can subscribe to the supported events and when the extension event is triggered, callbacks are invoked.

Supported Platform Event Callback's are,

Subscribing to the Events

Extension manifest's callbackListener property is used for declaring the callbacks for the extension's events. To subscribe to an event, specify the callback URL in the manifest for the callbackListener's respective event.

                    
Event Callback Request
URL : manifest.callbackListener.{eventName} RequestMethod : POST RequestHeaders : HASH Content-Type : application/json RequestBody :     JSONObject in #EVENT_CALLBACK_PAYLOAD_FORMAT format.
EVENT_CALLBACK_URL https://zohodeskapp.com/authorized.php
EVENT_CALLBACK_HEADERS { "HASH" : "xxxxxxxxx" }
EVENT_CALLBACK_PAYLOAD_FORMAT { "event" : "onZohoAuthorise", "orgId" : 387238, "securityContext" : "2398deio3qwnx3c9xwi3nc3njkh9jfico" }

Event Callback Request

Whenever the extension event occurs, the callback URL is triggered with security parameters which can be used to call the authenticated APIs.

#EVENT_CALLBACK_PAYLOAD_FORMAT

Field Description
event Name of the event triggered the callback.

Possible values are,
  • onInstall
  • onZohoAuthorise
  • onTPAAuthorise
  • onTPARevoke
  • onUpdate
  • onConfigParamAdd
  • onConfigParamDelete
  • onUninstall
orgId orgId of the user's organisation.
securityContext securityContext which can be used for calling Desk's invoke API.


Events


onInstall

onInstall will be triggered when the customer installs an extension. The developer can subscribe to the event with the URL and perform his logic. 

Example usage

Creating a Third party resource once the customer installs the extension.


onZohoAuthorise

Once the end user installs the extension, he needs to authorize the desk & TPA (say jira). onZohoAuthorise will be triggered when the customer authorize the DESK. The developer can subscribe to the event with the URL and perform his logic. 

Example usecase

Creating a Zoho Desk webhook once the desk authorization is completed.


onTPAAuthorise

The event will be triggered once the user authorize the Third party app. 

Example usecase

Subscribing or creating a webhook in third party service.


onTPARevoke

The event will be triggered once the user revoke the Third party app authorization.

Example usecase

Revoking or deleting the added webhooks in the third party services.


onUpdate

The event will be triggered when the user upgrade the extension.

Example usecase

Adding or Updating config params.


onConfigParamAdd

The event will be triggered when the customer adds configuration params in the extension. 

Example usecase

In jira extension, when customer adds configuration params such as domain, jiraAuthKey , developer shall create a jira webhook against the jira account.


onConfigParamDelete

The event will be triggered when the customer adds configuration params in the extension. 

Example usecase

In jira extension, when customer deletes configuration params such as domain, jiraAuthKey , developer shall delete jira webhook in the jira account.


onUninstall

The event is triggered when customer uninstall the extension.

Example usecase

Operations to be performed during un-installation such as deleting webhooks.

Including Zoho Desk Resources

You can also include Zoho Desk resources, such as fields, webhooks, custom permissions, and channel integrations that can be used within the extension by defining in resources.json

For instance, if you want to create a field to be used in your extension, you have to use the organization fields API and manually associate the extension departments to the fields. This complicates the code logic of the extension and makes it difficult to manage it. You can avoid this complication by including Zoho Desk's resources in your extension's resources.json, thereby saving development time and effort substantially.

Among resources, fields are extension-specific, which means they are shared among multiple installation instances in the same portal. The other resources - webhooks, custom permissions, and channel integrations - are installation-specific, which means they are valid only in the particular installation instance.

resourceName

Each resource you create must have a unique identifier, which must be specified using the resourceName key. The resourceName(s) specified should be unique across the extension and this is validated before the extension is made available on the Marketplace.The resourceName you assign serves as the unique identifier across all organizations that install your extension.
To include a resource in your extension, you must mention its resourceName value in the resources.json file in the extension bundle.Currently, this key is supported only for fields and webhooks.


How it works?

When you define a resourceName in the resources.json file, the ID and apiName (if available) of the resource are also mapped to it automatically. After this, you can use the ID and/or apiName (if available) in APIs related to the resource, as required. For instance, if you are in need of apiName of a created field, you can use the resourceName value of the field to retrieve its apiName.

Retrieving the ID and apiName of a resource : Using the resourceName value, you can retrieve the ID and apiName of a resource in one of the following ways:
1. Through a merge field in the invoke API, or
2. Using the client SDK

1. Through a merge field in the invoke API
In this method, you must specify the resourceName value in the Desk's invoke API in the following format.
{{resourceType.resourceName.id}} or {{resourceType.resourceName.apiName}}.
Refer Specifying Placeholders in Invoke API.
Placeholders Details :
resourceType should be fields or webhook, based on your use case.
id returns the fieldId or webhookId, based on the value in resourceType.
apiName returns the apiName of the resource. it is applicable to fields.

2. Using the client SDK
In this method, you must use the Resource API for retrieving resource details.

Including Fields in Your Extension

                

resources.json

... { "fields": [{ "resourceName": "field1", "module": "tickets", "displayLabel": "Counter", "type": "Text" "maxLength": "100" }] }, ...

All fields that can be created using the Create field REST API are supported in extensions.
However, please keep in mind the following points:
1. resourceName key is mandatory in resources.json. Refer resourceName.
2. Make sure to not pass the layoutId key in the extension code because layoutId value is automatically chosen from the department associated with the extension. This means that the new field is mapped to the default layout configured for the department.

To include custom fields in your extension, add the code snippet to the resources.json file.

Any field you include in your extension will be added to the user's Zoho Desk portal on the very first installation of the extension. Subsequent installations map to the fields created already. The fields are deleted from the portal on the last uninstallation of the extension.
You can include a maximum of 10 custom fields in an extension.

Extension webhook

                    

resources.json

... "webhook":{ "resourceName": "webhook1", "url": "https://demowebhook.com/callbackurl", "name": "Demo extension webhook", "description": "Demo extension webhook listen to ticket events", "subscriptions":{ "Ticket_Add":null, "Ticket_Update":{ "includePrevState":true }, "Ticket_Thread_Add":null, "Ticket_Comment_Add":null, "Ticket_Comment_Update":null } "ignoreSourceId":"2df92c1a-973a-48f5-95b7-5792c68b9c36" }, ...
Sample of Manifest entry with Zoho Authorization - using Connnections (Desk.events.ALL scope should be included in connection scope list)
{ ..., "zohoAuthorisation" : { "type" : "connectors", "connectionLinkName" : "test12345", "connectionName" : "Webhook zoho desk", "serviceName" : "Zoho Oauth", "userAccess" : true, "scope" : ["Desk.events.ALL"], "isUserDefinedService" : false }, ... }

Marketplace supports Extension Webhook , which will allow a market place app to create extension specific desk webhooks. Extension webhook access is restricted to the extension app alone so that the normal user can't make any changes on these webhooks.

An extension developer can specify the webhook details in resources.json file. Extension webhooks will be created on authorising the extension and will be deleted on uninstalling the extension. We will apply department filtering on webhook events based on the departments choosed in the extesnion configuration.

On subscribing extension webhook we will append the orgId and securityContext to the webhook callback url. You can use this information to make API calls using Desk's invoke API.

Note:Extension webhook is allowed only for org based extensions

How to handle Authorisation

To use extension webhook you should include the webhook specific scope Desk.events.ALL in the authorisation scope list and the authorisation should be defined using the key zohoAuthorisationin plugin-manifest file.



#WEBHOOK_FIELDS

(Refer Webhook's Attribute section to know more about webhook fileds.We will apply department filtering on webhook events based on the departments choosed in the extesnion configuration. Kindly ignore the departmentIds key while giving the subscription details in resources.json file.)

Field Required Type Description
resourceName yes string Unique indentifier for created webhook.Refer resourceName.
url yes string Server endpoint to which event information must be sent.
name yes string Name of the webhook.
description no string Description of the webhook
ignoreSourceId yes string Client ID exempted from triggering webhooks. The value of this attribute must always be a UUID. For information on how to use this attribute, refer to the Ignoring Webhook Events section.

subscriptions yes JSONObject Events that you want to subscribe to. To know about the supported events and its payload refer Event Supported.

Introduction

Channel Integration lets you to sync data such as tickets, threads and contacts between Zoho Desk and External Services. It also lets the agents to send replies to the queries on external platforms directly from the desk. Channel Integration can be achieved by ZohoDesk Marketplace app which must act as a bridge between External Service and the Zoho Desk.

Marketplace App with Channel Integration:

Marketplace App which supports the channel integration is responsible for

Configuration

Requirements:

                    

resources.json

... "channel": { "channelLogoPath" : "/app/img/youtube_logo.png", "acceptAttachments" : false, "updateRecords" : true, "contentTypes" : ["text/plain","text/html"], "redirectUrl" : "https://zohodeskapp.example.com/youtube/handleRedirect", "includeQuotedMessage": false, "sync": { "push": "https://zohodeskapp.example.com/youtube/handlePull", "pull": "https://zohodeskapp.example.com/youtube/handlePush" } } ...

resources.json

Channel configuration in the resources.json specifies the settings of the channel and the endpoints to pull or push the data for syncing. The params to be specified in the manifest are listed below.

#CHANNEL_INTEGRATION_CONFIGURATION

Field Required Type Description
sync yes JSONObject SYNC_OBJECT Sync property of the channel defines the endpoints that are used for handling data sync.
channelLogoPath yes string (URL) Relative Path of the channel's logo image in the app directory.

Logo Specification:
image-format : png, jpg
max-size : 500kb
redirectUrl string (URL) URL which can redirect the user to the external resource of an entity. Refer Source Redirection
updateRecords boolean Specifies whether the replies of this channel can be updated.
Default : false
acceptAttachments boolean Specifies whether attachments can be added to the replies of this channel.
Default : false
includeQuotedMessage boolean Specifies whether it is advisable to add the previous replies as quoted to the replies while replying for this channel.
Default : false
contentTypes JSONArray <string> Specifies the allowed/supported content types (MIME types) for the replies of this channel.
Default : text/plain

Allowed Values :
  • text/plain
  • text/html


Sync Object

Field Required Type Description
push yes string (URL) An endpoint to accept the replies from the desk by agent to process them and update in the external service.
Refer Push Request from Desk
pull string (URL) An endpoint which supplies updated external data when desk periodically requests.
Refer Pull Request from Desk

Sync (Data Transfer)

Desk offers the below methods to perform two-way data syncing between Desk & External Service.


Overriding read-only fields

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 specified and overridden with the original value in the external service.

                    
#SYNC_RESPONSE_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 ] } }

#SYNC_RESPONSE_OBJECT

Desk accepts data to be synced in the below format during pull-request from desk and push-data to desk operation.

Field Required Type Description
channelState string Value to be stored in the app's channelState configParam. Can be used to store the state of the channel sync progress.
Refer Channel State
data yes JSONObject <#SYNC_DATA_OBJECT> Contains the data to be imported from the external service, converted to desk compatible format. Supported properties are,
  • tickets
  • threads

#SYNC_DATA_OBJECT

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

Field Required Type Description
tickets JSONArray (1000) <SYNC_TICKET_OBJECT> Array of ticket properties to be imported.
threads JSONArray (1000) <SYNC_THREAD_OBJECT> Array of thread properties to be imported.
                    
Example of #SYNC_TICKET_OBJECT
{ "data":{ "tickets":[ { "extId":"whatsapp:+919994411345", "subject":"How to reset the configuration?", "createdTime":"2018-09-10T13:34:26.000Z", "actor": #SYNC_ACTOR_OBJECT, "extra": #SYNC_EXTRA_OBJECT }, ... ], "threads":[] } }

Create or Update Tickets in Desk

Channel integration lets you create and update tickets for external resources. To import tickets to the desk, the details of the ticket has to be given in the #SYNC_TICKET_OBJECT format to the pull-request from desk & push-data to desk response's data.tickets property.

#SYNC_TICKET_OBJECT

Field Required Type Description
extId yes string Unique ID of the ticket in the external service. Refer External ID
actor yes JSONObject #SYNC_ACTOR_OBJECT Details about the author.
subject yes string (255) Details about the author.
extra yes JSONObject #SYNC_EXTRA_OBJECT Extra information about the ticket for the extension.
createdTime Timestamp ISO Format Created time of the ticket.
email string EMail ID of the ticket.
phone string Phone number of the ticket.
description string Description of the ticket.
status string Status of the ticket.
category string Ticket category.
subCategory string Ticket sub category.
resolution string Resolution of the ticket.
dueDate Timestamp ISO Format Due date for resolving the ticket.
priority string Priority of the ticket.
classification string Classification of the ticket.
customFields JSONObject Custom fields in the ticket.
assigneeId long ID of the agent to whom the ticket is assigned.
teamId long ID of the team assigned to resolve the ticket.
productId long Product to which the ticket is mapped.

External ID

ID of the resource on external service. External ID is the value which acts as a unique identifier of the resource on the external resource. The extId property plays a significant role in identifying the corresponding desk entity of the external resource during sync. If there is no entity found for the given extId of the channel in the desk, a new entity is created otherwise the existing entity which has the extId of the channel is updated. Allowed characters : A-Z a-z 0-9 @ $ & + : . { } ( ) # - _ +

For example

If we consider a Facebook post, every Facebook post has a unique ID (extId). When an external resource of Facebook service with extId "298393" is submitted to create a ticket in desk, if any existing ticket with Facebook channel and the same extId found then the ticket is updated with given data, otherwise a new ticket is created with facebook channel and given extId.


Adding External Attachments

If any external attachments need to be added to the tickets/threads, it has to be given as a JSON array of URLs which will be downloaded and attached to the tickets. The downloading of the attachments and adding to the tickets are asynchronous so that failure of attachments does not interrupt the process of adding/updating the tickets or threads. 

For example

A facebook comment may contain images that you want to add as a thread.


                    
Example of #SYNC_THREAD_OBJECT
{ "data":{ "threads":[ { "extId":"SMa8974b1b935d957ffd9", "extParentId":"+123456789", "createdTime":"2018-09-10T11:54:03.000Z", "content":"What surprised", "direction":"in", "from":"+00032882", "to":["+00000273637"], "canReply":true, "extra": #SYNC_EXTRA_OBJECT, "actor": #SYNC_ACTOR_OBJECT }, ... ], "tickets":[ { "extId":"+123456789", "subject":"Knew he didnt know, i never knew a lot too!", "createdTime":"2018-09-07T14:02:27.000Z", "extra": #SYNC_EXTRA_OBJECT, "actor": #SYNC_ACTOR_OBJECT }, ... ] } }

Create or Update Threads in Desk

Channel integration lets you to add threads or replies from the external service. To import threads, provide the threads details in the #SYNC_THREAD_OBJECT format to the pull-request from desk & push-data to desk response's data.tickets property.

#SYNC_THREAD_OBJECT

Field Required Type Description
extId yes string Unique ID of the thread in the external service. Refer External ID
extParentId yes string Parent Entity's ID of the thread in the external service.
Threads having the same parentId will be grouped under the same ticket which's extId is equal to the parentId. Refer External Parent ID
actor yes JSONObject #SYNC_ACTOR_OBJECT Details about the author.
content yes string Content of the thread.
direction string Specifies the direction of the thread. Supported Values are
  • in (incoming thread)
  • out (outgoing thread)
extra JSONObject #SYNC_EXTRA_OBJECT Extra information about the thread for the extension.
attachmentUrls JSONArray <string> Urls of the attachments to be added to the thread. Refer Adding External Attachments
createdTime Timestamp ISO Format Created time of the thread.
modifiedTime Timestamp ISO Format Modified time of the thread.
canReply boolean Specifies whether replies can be added to this thread.
contentType string Content-Type of the content of the thread. Supported Values are
  • text/plain
  • text/html
from string From address of the thread.
Default : Channel Name
to JSONArray <string> Direct Recipients of the thread
cc JSONArray <string> cc'ed Recipients of the thread
bcc JSONArray <string> bcc'ed Recipients of the thread


External Parent ID

This constraint is used for grouping multiple threads under a single ticket. extParentId contains the parent ID of the external resource in external service. For a thread external resource, extParentId is useful for identifying the correct ticket that the reply has to be added in the desk. Whenever an extParentId for a thread resource is given, a thread is added for the ticket that has the given extParentId as extId. If none of the ticket's extId matches the given extParentId then the thread will not be added.

For example

If we consider a facebook-post-comment, extParentId will be the ID of the facebook post. So that comments having the same externalaParentId will be grouped under the same ticket which has the extId.


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

#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.

Field Required Type Description
extId yes string Unique ID of the person in the external service. Used for contacts profiles sync.
name yes string Name of the person in the external service.
displayName string Name to display on contact's detail page. If not provided, defaults to name
email string Email ID of the author on external service. If provided, this profile will be added under the contact who have the same Email Id.
phone string Phone number of the author on external service. If provided, this profile will be added under the contact who have the same Phone Number.
photoURL string <URL> URL of the author's photo on external service.


Providing app specific custom data for resources

Apps may need to store some extra details about a particular resource in addition to the desk supported standard fields of an entity.
Place holders can be given for any of the Extra-Object's properties which will be replaced with the original values of the respective entities. For supported placeholders refer Desk Resource Template Placeholders

Example

An app developer may need to store likes and share counts of a facebook post which needs to be added as a ticket or thread in the desk so that the app may show that information in the desk widgets or may need to show some actions prior to that information.


                    
SYNC EXTRA OBJECT Sample:
{ "key" :"Post-{{ticket.id}}-Comment-{{thread.id}}-Details", "queriableValue" :"Post-{{ticket.id}}-Details" "value" :{ "likes":3809, "comments":453 } }

#SYNC_EXTRA_OBJECT

Field Required Type Description
key yes string 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
value yes JSONObject Specifies the value that needs to be stored for the given template key.
queriableValue yes string Specifies a common lookup group of the given key-value pair which will be useful for lookup from the database.

Desk Resource Template Placeholders

Developer may not know the values of the resource but might want to make use of it to store some information in extension's DB storage. So developers can make use of place holders which are replaced with original values after updating the resource.

Format for the templated string with placeholder - {{PLACEHOLDER}}

Supported Placeholders are,


extra.key

Value for the key property is a string which can be a template key. Key for which the given value has to be stored in the DB storage.

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.

                    
PULL REQUEST FORMAT:
URL : channel.sync.pull RequestMethod : POST QueryParams : securityContext, orgId Content-Type : application/json RequestBody :     All the non-secure configParams (CONFIG_PARAMS_OBJECT).
CONFIG_PARAMS_OBJECT: { "channelState" : "{\"my_last_fetch_time\":\"Jan 11\"}", "myConfigParam1" : "My value for configParam", "myConfigParam2" : "My value for configParam2", "myConfigParam3" : "My value for configParam3" } PULL REQUEST RESPONSE FORMAT:
ResponseCode : 200 Content-Type : application/json Response : Updated Data to be added in desk in the #SYNC_RESPONSE_OBJECT format.

Pull Request from Desk

Desk periodically invokes a request to the endpoint specified in the
extension manifest's channel.sync.pull property. Pull requests are made every 4 minutes from the desk. You can send the new/ updated data of tickets and threads that needs to be updated in desk in the specified #SYNC_RESPONSE_OBJECT format.

*Tip : You might need to call the external service's API to get new data which has to be sent to the desk. Call the external service's API through Desk Invoke API so that authentications are handled automatically (e.g Using connections) .

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

                    
PUSH REQUEST FORMAT:
URL : channel.sync.push RequestMethod : POST QueryParams : securityContext, orgId Content-Type : application/json RequestBody :     JSONObject containing reply & configParams in #PUSH_REPLY_PAYLOAD_FORMAT format.
PUSH REQUEST RESPONSE FORMAT:
ResponseCode : 200 Content-Type : application/json Response : Data to be updated in desk in #PUSH_REPLY_RESPONSE_OBJECT format.

Push Reply from Desk

When an agent replies from the desk for a ticket or thread created by channel integration, the reply is pushed to the Resources.channel.sync.push endpoint of the app which needs to be delivered to the external service so that the reply will be processed further. The push endpoint given in the manifest is responsible for handling the desk reply, delivering it to the external service and submit the status & response of the processed reply to the desk back.
The Progress of an agent reply from the desk for a ticket or thread created via channel integration are,


*Tip : To send the received agent reply to other service, you may need to call other service's APIs. Use Desk Invoke API to call the external service's API, so that authentications are handled automatically (e.g Using connections) .

                    
Sample of #PUSH_REPLY_PAYLOAD_FORMAT:
{ "configParams" :{ "channelState" : "{\"my_last_fetch_time\":\"Jan 11\"}", "myConfigParam1" : "My value for configParam", "myConfigParam2" : "My value for configParam2", "myConfigParam3" : "My value for configParam3" }, "resource":{ "extParentId" : "1276576533", "replyToExtId" : "3298dniuniu3", "id" : 287189379819, "content" : "Hi customer, thanks for writing to us", "summary" : "Hi customer, thanks for writing to us", "contentType" : "text/plain", "hasAttach" : false, "attachments" : [], "author" : { "name" : "John Snow", "email" : "john.snow@example.com", "type" : "AGENT", "photoURL" : "https://desk.zoho.com/api/v1/agents/387829/photo?orgId=28732" } } }

#PUSH_REPLY_PAYLOAD_FORMAT

Field Type Description
configParams JSONObject CONFIG_PARAMS_OBJECT All the non-secure config params belongs to the app.
resource JSONObject DESK_REPLY_THREAD_OBJECT Details of the Agent's reply to be sent to the external service.

#DESK_REPLY_THREAD_OBJECT

Field Type Description
extParentId string External Id of the thread's ticket. (i.e) Parent Entity's ID of the thread in the external service.
replyToExtId string Add the reply to this message in the External Service. External Id of the thread to which the reply has to be added.
id long ID of the thread.
author JSONObject Details about the author with the following properties.
  • name
  • email
  • type
  • photoURL
content string Content of the thread.
contentType string Content-Type of the content of the thread. Possible Values are
  • text/plain
  • text/html
summary string Summary of the thread.
hasAttach boolean Specifies whether thread has attachments.
attachments JSONArray <JSONObject> Attachments in the thread. Each attachment object contains properties:
  • id
  • name
  • size
  • href
attachmentCount integer Count of the attachments
createdTime Timestamp ISO Format URL to get the full content of the thread.

#PUSH_REPLY_RESPONSE_OBJECT

Field Required Type Description
extId yes string Unique ID of the thread added in the external service.
extra JSONObject #SYNC_EXTRA_OBJECT Extra information about the thread for the extension.
canReply boolean Specifies whether replies can be added to this thread.
from string From address of the thread.
Default : Channel Name
to JSONArray <string> Direct Recipients of the thread

Push-Data to Desk

                    
PUSH REQUEST FORMAT:
API Endpoint : /api/v1/channels/{{installationId}}/import RequestMethod : POST Content-Type : application/json RequestBody :     JSONObject in the #SYNC_RESPONSE_OBJECT format.

Whenever you need to manually push tickets & threads to the desk, you can use the following API. This API expects the payload in the #SYNC_RESPONSE_OBJECT format.

To call desk's "channels/import" API, orgId and securityContext params are mandatory. You may 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 Desk Invoke API so that authentications are handled automatically (e.g Using connections) .

For Example:

You may have subscribed to the events such as onConfigParamAdd, onDeskAuthorize or onTPAAuthorise in the manifest's callBacks. Which will make 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, 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 it to call the desk "channels/import"  API.

Channel State

About

Channel State represents the channelState non-secure configParam which is automatically added for the channel type of marketplace apps during installation. Channel State can be used as the current status of the channel's syncing process such as 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 app's channelState configParam during the pull & push requests. If you don't want to update the channelState of the app in ZohoDesk, then exclude the channelState key while sending the sync request response.



                    
Source Redirection:
URL : channel.redirectUrl RequestMethod : GET QueryParams : entity, id, parentId

Source Redirection

The redirectUrl endpoint specified in the Manifest's channel param is responsible for redirecting the user to the external resource of an entity when requested by the desk.

Source Redirection Query Params

Field Description
entity Type of the entity to be redirected.
Supported Values are
  • ticket
  • thread
  • user_profile
id External Id of the entity in the external service.
parentId External Parent Id of the entity. In case of threads, parentId contains the extId of the ticket.

For example:

When an agent views a ticket, a hyperlink to the external resource will be shown to the agent. On clicking the hyperlink, the agent is redirected the redirectUrl in the manifest with the extId of the resource and the type of the entity as queryParams of the Url. The redirect URL should parse the entity type and id of the resource and redirect the user to the external service which contains the resource.

Introduction

                

resources.json

... { "permissions": [{ "apiName": "createResolution", "displayName": "create Resolution test", "description": "Able to create the task", "defaultState": true }] }, ...

Custom permissions are new profile permissions that you can provide in the extension you develop. These permissions will be displayed under the PERMISSIONS tab on the extension detail page. Only the admins of a Zoho Desk portal will be able to configure these permissions.

How to define the custom permissions for the extension?
An extension developer can specify the Permissions details in resources.json file.

How does it work?
Custom permissions will be added when an admin installs the extension and deleted when they uninstall the extension. The permissions are specific to each installation of an extension. Therefore, admins can configure different permissions for an extension by installing it multiple times. 

Admin configuration Permission Tab:

You can fetch the details of custom permissions in two ways:
1. Through the My Custom Permissions API within the extension, or
2. Through the My Custom Permissions REST API.



#CUSTOM_PERMISSION_FIELDS

Field Required Type Description
apiName yes string apiName is the unique name given to each permission.apiName should be unique across each extension.apiName can have AlphaNumeric and underscore.
displayName yes string Name of the custom permission.This name will be displayed in "PERMISSIONS" tab.AlphaNumeric Values are allowed here.
description no string Description of the custom permission.
defaultState yes string Default state of custom permission. It can have boolean value. Either true or false(Case sensitive).