Part 4: Install the integration
This tutorial is part of a series that builds a Zendesk integration for Slack:
- Part 1: Build a Zendesk app with OAuth
- Part 2: Connect to Slack
- Part 3: Add a configuration UI
- Part 4: Install the integration — YOU ARE HERE
The integration listens for Ticket Created events in Zendesk. When it detects an event with a specific ticket priority, the integration posts a related message to a provided Slack channel.
If you followed along in the series, you should now have a working Zendesk app. The app serves as a user interface (UI) for your integration. In this tutorial, you'll install the integration and finish building the app.
Create a bundle
To run, your integration requires a Zendesk Integration Services (ZIS) bundle. A bundle contains JSON objects that define the logic and resources for an integration.
The bundle for this integration should include JSON objects for:
- A custom action that gets ticket data
- A custom action that posts a message to a Slack channel
- A ZIS flow that defines the integration's logic as states
- A job spec that runs the above flow when ZIS detects a Ticket Created event
Create a bundle skeleton
To start, create a skeleton file for your bundle.
-
Create a file named zendesk-to-slack-bundle.json.
-
Add the following JSON to the file:
{
"name": "Post Slack message on ticket creation",
"description": "Posts ticket data to Slack channel on ticket creation",
"zis_template_version": "2019-10-14",
"resources": {
"get_zendesk_ticket_http_action": {
"_placeholder_": "Action properties here"
},
"post_message_to_slack_http_action": {
"_placeholder_": "Action properties here"
},
"post_ticket_update_flow": {
"_placeholder_": "Flow properties here"
},
"post_message_to_slack_on_ticket_created_job_spec": {
"_placeholder_": "Job spec properties here"
}
}
}
The JSON contains placeholders for the bundle's action, flow, and job spec objects.
Define the custom actions
Next, define the bundle's custom actions.
-
In zendesk-to-slack-bundle.json, replace the
get_zendesk_ticket_http_action
placeholder with the following:"get_zendesk_ticket_http_action": {
"type": "ZIS::Action::Http",
"properties": {
"name": "get_zendesk_ticket_http_action",
"definition": {
"method": "GET",
"path": "/api/v2/tickets/{{$.ticketId}}.json",
"connectionName": "zendesk"
}
}
},
When run, the action calls the Zendesk Show Ticket endpoint to get ticket data. To make the call, the action requires the "zendesk" OAuth connection and the
{{$.ticketId}}
input variable. You'll set the{{$.ticketId}}
variable when you define the bundle's flow. -
Replace the
post_message_to_slack_http_action
placeholder with the following:"post_message_to_slack_http_action": {
"type": "ZIS::Action::Http",
"properties": {
"name": "post_message_to_slack_http_action",
"definition": {
"method": "POST",
"url": "https://slack.com/api/chat.postMessage",
"connectionName": "slack",
"requestBody": {
"channel": "{{$.channelId}}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "{{$.title}}"
},
"fields": [
{
"type": "mrkdwn",
"text": "*Subject*"
},
{
"type": "mrkdwn",
"text": "*Description*"
},
{
"type": "plain_text",
"text": "{{$.ticketSubject}}"
},
{
"type": "plain_text",
"text": "{{$.ticketDescription}}"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "{{$.link}}"
}
}
]
}
}
}
},
When run, the action calls the Slack API's chat.postMessage method to post a message to a Slack channel. To post the message, the action requires the "slack" OAuth connection and the following input variables:
{{$.channelId}}
{{$.title}}
{{$.ticketSubject}}
{{$.ticketDescription}}
{{$.link}}
You'll set the input variables when you define the bundle's flow.
Define the flow
Next, define a ZIS flow for the bundle.
In zendesk-to-slack-bundle.json, replace the post_ticket_update_flow
placeholder with the following JSON. In the JSON, replace "SUBDOMAIN" with
your Zendesk subdomain.
"post_ticket_update_flow": {
"type": "ZIS::Flow",
"properties": {
"name": "post_ticket_update_flow",
"definition": {
"StartAt": "LoadSettings",
"States": {
"LoadSettings": {
"Type": "Action",
"ActionName": "zis:common:action:LoadConfig",
"Parameters": {
"scope": "slackNotification"
},
"ResultPath": "$.settings",
"Next": "IsTicketPriorityMatch"
},
"IsTicketPriorityMatch": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.input.ticket_event.ticket.priority",
"StringEqualsPath": "$.settings.priority",
"Next": "GetZendeskTicket"
}
],
"Default": "Done"
},
"GetZendeskTicket": {
"Type": "Action",
"ActionName": "zis:SUBDOMAIN_zendesk_to_slack:action:get_zendesk_ticket_http_action",
"Parameters": {
"ticketId.$": "$.input.ticket_event.ticket.id"
},
"ResultPath": "$.retrievedTicket",
"Next": "PostMessageToSlack"
},
"PostMessageToSlack": {
"Type": "Action",
"ActionName": "zis:SUBDOMAIN_zendesk_to_slack:action:post_message_to_slack_http_action",
"Parameters": {
"channelId.$": "$.settings.channel",
"ticketSubject.$": "$.retrievedTicket.ticket.subject",
"ticketDescription.$": "$.retrievedTicket.ticket.description",
"title.$": "*_New {{$.input.ticket_event.ticket.priority}} ticket #{{$.input.ticket_event.ticket.id}}_*",
"link.$": "<{{$.retrievedTicket.ticket.url}}|View in Zendesk>"
},
"ResultPath": "$.postMessageResponse",
"Next": "Done"
},
"Done": {
"Type": "Succeed"
}
}
}
}
},
The flow object contains five states:
-
LoadSettings
loads the configuration saved using your Zendesk app's config UI. The state makes the config's data available to other states as the "$.settings" object. -
IsTicketPriorityMatch
is a choice state. The state compares two properties:- The ticket priority from a Ticket Created event
- The ticket priority from the config settings
If the priorities are the same, the flow moves to the
GetZendeskTicket
state. Otherwise, the flow moves to theDone
state. -
GetZendeskTicket
uses the customget_zendesk_ticket_http_action
action. The state gets ticket data for the Ticket Created event. The state makes this ticket data available to following states as the "$.retrievedTicket" object. -
PostMessageToSlack
uses the custompost_message_to_slack_http_action
action. The state posts a message to the Slack channel provided in the config settings. The message contains ticket data from the Ticket Created event and the "$.retrievedTicket" object. -
The
Done
state ends the flow in a successful state.
Define the job spec
Next, define a job spec for the bundle. The job spec runs the bundle's ZIS flow when it detects a Ticket Created event.
In zendesk-to-slack-bundle.json, replace the placeholder line in the
post_message_to_slack_on_ticket_created_job_spec
with the following JSON. In
the JSON, replace "SUBDOMAIN" with your Zendesk subdomain.
"post_message_to_slack_on_ticket_creation_job_spec": {
"type": "ZIS::JobSpec",
"properties": {
"name": "post_message_to_slack_on_ticket_creation_job_spec",
"event_source": "support",
"event_type": "ticket.TicketCreated",
"flow_name": "zis:SUBDOMAIN_zendesk_to_slack:flow:post_ticket_update_flow"
}
}
Code complete
Your actions, flow, and job spec now make up a complete ZIS bundle. Your zendesk-to-slack-bundle.json file should look as follows:
{
"name": "Post Slack message on ticket creation",
"description": "Posts ticket data to Slack channel on ticket creation",
"zis_template_version": "2019-10-14",
"resources": {
"get_zendesk_ticket_http_action": {
"type": "ZIS::Action::Http",
"properties": {
"name": "get_zendesk_ticket_http_action",
"definition": {
"method": "GET",
"path": "/api/v2/tickets/{{$.ticketId}}.json",
"connectionName": "zendesk"
}
}
},
"post_message_to_slack_http_action": {
"type": "ZIS::Action::Http",
"properties": {
"name": "post_message_to_slack_http_action",
"definition": {
"method": "POST",
"url": "https://slack.com/api/chat.postMessage",
"connectionName": "slack",
"requestBody": {
"channel": "{{$.channelId}}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "{{$.title}}"
},
"fields": [
{
"type": "mrkdwn",
"text": "*Subject*"
},
{
"type": "mrkdwn",
"text": "*Description*"
},
{
"type": "plain_text",
"text": "{{$.ticketSubject}}"
},
{
"type": "plain_text",
"text": "{{$.ticketDescription}}"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "{{$.link}}"
}
}
]
}
}
}
},
"post_ticket_update_flow": {
"type": "ZIS::Flow",
"properties": {
"name": "post_ticket_update_flow",
"definition": {
"StartAt": "LoadSettings",
"States": {
"LoadSettings": {
"Type": "Action",
"ActionName": "zis:common:action:LoadConfig",
"Parameters": {
"scope": "slackNotification"
},
"ResultPath": "$.settings",
"Next": "IsTicketPriorityMatch"
},
"IsTicketPriorityMatch": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.input.ticket_event.ticket.priority",
"StringEqualsPath": "$.settings.priority",
"Next": "GetZendeskTicket"
}
],
"Default": "Done"
},
"GetZendeskTicket": {
"Type": "Action",
"ActionName": "zis:SUBDOMAIN_zendesk_to_slack:action:get_zendesk_ticket_http_action",
"Parameters": {
"ticketId.$": "$.input.ticket_event.ticket.id"
},
"ResultPath": "$.retrievedTicket",
"Next": "PostMessageToSlack"
},
"PostMessageToSlack": {
"Type": "Action",
"ActionName": "zis:SUBDOMAIN_zendesk_to_slack:action:post_message_to_slack_http_action",
"Parameters": {
"channelId.$": "$.settings.channel",
"ticketSubject.$": "$.retrievedTicket.ticket.subject",
"ticketDescription.$": "$.retrievedTicket.ticket.description",
"title.$": "*_New {{$.input.ticket_event.ticket.priority}} ticket #{{$.input.ticket_event.ticket.id}}_*",
"link.$": "<{{$.retrievedTicket.ticket.url}}|View in Zendesk>"
},
"ResultPath": "$.postMessageResponse",
"Next": "Done"
},
"Done": {
"Type": "Succeed"
}
}
}
}
},
"post_message_to_slack_on_ticket_creation_job_spec": {
"type": "ZIS::JobSpec",
"properties": {
"name": "post_message_to_slack_on_ticket_creation_job_spec",
"event_source": "support",
"event_type": "ticket.TicketCreated",
"flow_name": "zis:SUBDOMAIN_zendesk_to_slack:flow:post_ticket_update_flow"
}
}
}
}
Upload the bundle
After creating your bundle file, you can deploy your integration. First, upload the bundle using the Create or Update Bundle endpoint.
-
In your command-line interface, navigate to the folder that contains your zendesk-to-slack-bundle.json file.
-
Run:
curl -X POST https://{subdomain}.zendesk.com/api/services/zis/registry/{subdomain}_zendesk_to_slack/bundles \
-u {email}/token:{api_token} \
-H "Content-Type: application/json" \
-i -d @zendesk-to-slack-bundle.json
If successful, the request returns an empty 200 response.
Add an "Enable integration" button
Next, you'll add an Enable integration button to your Zendesk app. When an admin clicks the button, the app installs the job spec specified in your bundle. ZIS gets the job spec from the bundle you uploaded.
-
In your Zendesk app's assets folder, open iframe.html. Add the following HTML directly after the
<div>
tag containing the Connect to Slack button....
Connect to Slack
</button>
</div>
<div class="c-txt u-mb">
<button id="btnEnableIntegration" class="c-btn c-btn--primary">
Enable integration
</button>
</div>
...
-
In bootstrap.js, add the following:
...
fetchConfig(integrationName);
// Bind button to install the job spec
document
.getElementById("btnEnableIntegration")
.addEventListener("click", function () {
enableIntegration(integrationName);
});
...
The code binds the Enable integration button to the
enableIntegration
function. -
In the assets folder, create a registry.js file. Paste the following into the file:
// Install the job spec.
function enableIntegration(integrationName) {
const jobSpec =
"zis:" +
integrationName +
":job_spec:post_message_to_slack_on_ticket_creation_job_spec";
let request = {
type: "POST",
url:
"/api/services/zis/integrations/" +
integrationName +
"/job_specs/installation",
contentType: "application/json",
data: JSON.stringify([{ name: jobSpec }]),
};
return client.request(request).then(
function () {
console.log("Integration enabled");
client.invoke("notify", "Integration enabled");
},
function (error) {
console.log("Failed to enable flow: ", response);
}
);
}
The code defines the
enableIntegration
function. When called,enableIntegration
uses the Install Job Spec endpoint to install the job spec defined in your bundle. -
In iframe.html, add the following:
...
<!-- Manage configuration settings -->
<script type="text/javascript" src="config.js"></script>
<!-- Enable workflow -->
<script type="text/javascript" src="registry.js"></script>
...
-
Save iframe.html, bootstrap.js, and registry.js. Then refresh the app.
Your app now displays an Enable integration button.
Install the job spec
Next, use the Enable Integration button to install the job spec.
In your app, click Enable integration. A success notification is displayed.
Test the integration
To test the integration, create a ticket with the "urgent" priority in Zendesk. To create the ticket, use the Zendesk Agent Workspace or run:
curl -X POST https://{subdomain}.zendesk.com/api/v2/tickets.json \
-u {email_address}/token:{ap_token} \
-H "Content-Type: application/json" \
-d '
{
"ticket": {
"subject": "My printer is on fire!",
"priority": "urgent",
"status": "open",
"comment": { "body": "The smoke is very colorful." }
}
}'
The integration posts a message in the configured Slack channel.
Congratulations! You've completed this tutorial series. You now have a working integration and Zendesk app.
As a next step, you can install your Zendesk app as a private app if wanted. Refer to Uploading and installing a private app.