This tutorial walks through building an integration using Zendesk Integration Services (ZIS) where a message is posted to a Slack channel when a Zendesk ticket is updated. This example is shown for the sake of simplicity even though the same goal could be achieved by using Zendesk's custom trigger or target feature. The information in this tutorial provides a foundation for building a more complex integration using ZIS.

Prerequisites

ZIS overview

ZIS Engine is a core service in ZIS which functions like a workflow runner. It listens for an event, then triggers a ZIS Flow consisting of one or more ZIS Actions. These actions take the form of HTTP requests. In this example, a ticket updated event from Zendesk triggers a flow which makes a request to the Slack API, and posts a message to a channel. The diagram below illustrates how the integration would work:

ZIS resources, which includes flows and actions, are defined with a simple JSON syntax and packaged together into a bundle. See Anatomy of a ZIS Bundle.

Building an integration using ZIS

As well as uploading the bundle containing the integration’s flow and actions, a small middleware service needs to be built to provide an endpoint to handle the installation of our integration to a user’s account.

The middleware service also provides an endpoint which acts as an external action in the flow, to take some actions that ZIS is not yet capable of performing. For example, performing a complex transformation, data enrichment, or interacting with a customer’s bespoke system that doesn’t have public API endpoints.

In this example, you’ll use an external action to talk directly to ZIS and retrieve a Slack OAuth token from ZIS Connections, then use that token and the event data to post a message to Slack. You can use any tech stack to build this middleware. In this tutorial, you’ll use AWS Lambda with an API Gateway. This is available on an AWS free tier account.

Setting up the middleware endpoints

In this section, you'll create two Lambda functions to serve as API endpoints You'll also need to provide a redirect URL to use for the Zendesk OAuth client, and a target for the ZIS action.

To create middleware endpoints

  1. In your AWS console, go to the Lambda service and create a new function called "example_integration_oauth_handler" and use the NodeJS runtime.
  2. Click Add Trigger and add an API gateway trigger.

You should now be able to see the API gateway URL to use in the next step. Repeat this process for a second action named "example_integration_external_action". You'll complete the code for the Lambda functions later on.

Registering the integration

The example integration needs to be registered with ZIS. This is done using the ZIS Registry API. The name of your integration is specified in the request URL, and needs to be globally unique.

Authentication methods can be basic authentication with a Zendesk username and password, or using an email address and API token. Make sure you enable API access in your Zendesk Support account for the authentication method you want to use.

Example request:

curl \  --request POST \  --url https://<SUBDOMAIN>YOUR_SUBDOMAIN.zendesk.com/api/services/zis/registry/<YOUR_INTEGRATION>YOUR_UNIQUE_INTEGRATION_NAME \  --user YOUR_EMAIL:YOUR_PASSWORD \  --header 'content-type: application/json' \  --data '{"description": "An example integration"}'

Example response:

{  "description": "An example integration",  "jwt_public_key": "The RSA Public Key to be saved",  "zendesk_oauth_client": {    "id": 10066,    "identifier": "zis_example_integration",    "secret": "secret"  }}

Save a copy of your response. You'll need to refer to it later on.

In the response, a couple of resources have been created - a Zendesk OAuth client and a RSA key that is used to verify JSON Web Tokens (JWT).

The Zendesk OAuth client is used to create a connection when a user installs the integration. The resulting access token is used by ZIS when running the integration’s actions. If required, the access token can also be used by your middleware service for making API calls to ZIS or other Zendesk APIs.

The JWT is used to authenticate requests from ZIS to our middleware service.

Setting up a Slack app

Next, create a new app in Slack.

To set up a Slack app

  1. Log in to your Slack account and create a new app. A Slack OAuth client is created for you automatically.
  2. On your app's config page, you can find the details in the Basic Information section.
  3. Go to the OAuth & Permissions section and add the "chat:write" scope under Bot Token Scopes.

Uploading the ZIS Bundle

The example bundle contains the flow which contains a single action. The action sends the ticket update event details to our middleware. The bundle also contains a Job Spec, which links the flow to an event that triggers it.

The ZIS Registry API is used to upload your bundle with the same authentication method. Make an API request using the Upload or Update Bundle endpoint.

Example request:

curl \  --request POST \  --url https://YOUR_SUBDOMAIN.zendesk.com/api/services/zis/registry/YOUR_UNIQUE_INTEGRATION_NAME/bundles \  --user YOUR_EMAIL:YOUR_PASSWORD \  --header 'content-type: application/json' \  --data '{    "zis_template_version": "2019-10-14",    "name": "ExampleIntegration",    "description": "Post ticket update to Slack",    "resources": {      "post_ticket_update_flow": {        "type": "ZIS::Flow",        "properties": {          "name": "post_ticket_update_flow",          "definition": {            "StartAt": "PostTicketUpdate",            "States": {                "PostTicketUpdate": {                "Type": "Action",                "ActionName": "zis:<YOUR_INTEGRATION>:action:post_message",                "Parameters": {                  "commentBody.$": "$.input.ticket_event.comment.body"                },                "End": true              }            }          }        }      },      "post_message_action": {        "type": "ZIS::Action::Http",        "properties": {          "name": "post_message",          "definition": {            "method": "POST",            "url": "<EXTERNAL_ACTION_LAMBDA_URL>",            "requestBody": {              "message.$": "*New Comment*: {{$.commentBody}}"            }          }        }      },      "ticket_update_to_slack_job_spec": {        "type": "ZIS::JobSpec",        "properties": {          "name": "ticket_update_to_slack",          "event_source": "support",          "event_type": "ticket.CommentAdded",          "flow_name": "zis:<YOUR_INTEGRATION>:flow:post_ticket_update_flow"        }      }    }  }'

Replace the following placeholder values:

  • <YOUR_INTEGRATION> with your integration name
  • <EXTERNAL_ACTION_LAMBDA_URL> with the URL of the external action Lambda you created earlier in Setting up the middleware endpoints.

Completing the middleware service setup

The middleware service has two functions - handling the OAuth flow and other steps required when a user installs your integration, and receiving a request from ZIS then taking some further action. In this example, it makes a final call to the Slack API.

Handling the OAuth flow

You can begin the OAuth flow manually, but you'll need the middleware to complete it. You've already created a Lambda function, so some additional code is required to handle the OAuth redirect, exchange the authentication code for an access token, and store the result.

The Zendesk OAuth connection is similar to the Zendesk OAuth pattern with one small difference. When exchanging the authorization code for an access token, instead of using the zendesk.com/oauth/tokens endpoint, you will use the ZIS Connections API. This enables ZIS to store a copy of the access token which is used when running our flow.

To set up your OAuth flow

  1. On your local system, create a new directory called example_integration_oauth_handler.

  2. In the directory, create a new package with npm init --yes, then add the OAuth package with npm install oauth.

  3. Create an index.js file and paste the code below, substituting your integration's identifier, secret, and Zendesk subdomain.

    Note: ZENDESK_CLIENT_IDENTIFIER should be replaced with the identifier value. Example: zis_example_integration in Registering the integration.

    const oauth = require('oauth')
    exports.handler = (event, context, callback) => {  // Get auth code from query string  const authorizationCode = event.queryStringParameters.code
      // Complete OAuth flow (exchange code for access token)  var oauth2 = new oauth.OAuth2(    '<ZENDESK_CLIENT_IDENTIFIER>',    '<ZENDESK_CLIENT_SECRET>',    'https://<SUBDOMAIN>.zendesk.com/',    null,    'api/services/zis/connections/oauth/tokens/<YOUR_INTEGRATION>',    null  )
      oauth2.getOAuthAccessToken(    authorizationCode,    {      grant_type: 'authorization_code',      redirect_uri: '<THE_URI_OF_THIS_HANDLER>'    },    function (e, _, _, results) {      if (e) {        console.log('Exception: ', e)        return callback(null, {          statusCode: e.statusCode,          body: `Exception - ${JSON.stringify(e)}`        })      } else if (results.error) {        console.log('Error: ', results.error)        return callback(null, { statusCode: e.statusCode, body: 'Error' })      } else {        console.log('Result: ', results)        return callback(null, {          statusCode: 200,          body: JSON.stringify(results)        })      }    }  )}
  4. In the directory run the command zip -r function.zip to collate all the function code and dependencies into a zip file.

  5. In your web browser, go to the AWS console and open your Lambda function "example_integration_oauth_handler".

  6. In the Function code section, select the Actions menu and upload the zip file.

Creating an external action package

When a flow runs, it executes an action (a HTTP request) that sends a formatted ticket update event message to the middleware. The "external action" in our middleware makes a request to the Slack Incoming Webhooks. See Enable Incoming Webhooks in the Slack documentation to obtain your Slack webhook URL.

To create an external action package

  1. On your local system, create a new directory called example_integration_external_action.

  2. Follow the same steps in the previous section to create another NPM package for this function However, this time add the "jsonwebtoken" library as a dependency. Example: npm install jsonwebtoken. This is used to authenticate the request from ZIS.

  3. In your index.js file, paste the code below, substituting information specific to your integration. Replace SLACK_INCOMING_WEBHOOK_URL with the Slack webhook URL you created earlier.

    const jwt = require('jsonwebtoken')const https = require('https')const util = require('util')
    exports.handler = (event, context, callback) => {  // Verify JWT  const token = event.headers['x-zis-authorization']  const jwtPublicKey = ZENDESK_JWT_PUBLIC_KEY  const key = Buffer.from(jwtPublicKey, 'base64').toString('utf-8')
      console.log(`Received token: ${token}, key: ${key}`)
      jwt.verify(token, key, 'RS256', function (err, _) {    if (err) {      console.log('Error: ', err)      const response = {        statusCode: 401,        body: JSON.stringify('JWT Invalid')      }      return response    }
        // Post message to Slack    const params = {      hostname: 'hooks.slack.com',      path: SLACK_INCOMING_WEBHOOK_URL,      method: 'POST'    }    var request = https      .request(params, function (res) {        res.setEncoding('utf8')        res.on('data', function (data) {          console.log('Message Sent: ' + data)          context.succeed('Message Sent: ' + data)        })      })      .on('error', function (e) {        console.log('Failed: ' + e)        context.fail('Failed: ' + e)      })
        // Parse input    const input = JSON.parse(event.body)    console.log('Input: ', input)
        // Send message    const message = {      channel: '#general',      text: input.message    }    request.write(util.format('%j', message))    request.end()  })}
  4. Run the command zip -r function.zip. to collate all the function code and dependencies into a zip file.

  5. In your web browser, go to the AWS console and open your Lambda function "example_integration_external_action"`.

  6. In the Function code section, select the *Actions menu and upload the zip file.

Installing the integration

To install the integration, you need to grant permission for it to access your account.

To install the integration

  1. In Admin Center, navigate to Apps and Integrations > APIs > Zendesk API and select the OAuth Clients tab.

  2. Select "ZIS Client" you created earlier when you registered the integration and add the URL of your OAuth Handler Lambda function to the Redirect URLs section.

  3. Click Save.

  4. Start the Zendesk authorization flow in your browser, go to the URL https://<SUBDOMAIN>.zendesk.com/oauth/authorizations/new with the following query parameters:

    • response_type = code
    • redirect_uri = YOUR_EXAMPLE_INTEGRATION_OAUTH_HANDLER_URI
    • client_id = YOUR_ZENDESK_OAUTH_CLIENT_ID
    • scope = zis:read zis:write

    Note: All parameters must be URL encoded. The Zendesk OAuth token generated from this step can be used to access a variety of other ZIS APIs.

  5. Make an API request using the Install JobSpec endpoint.

    curl \--request POST \--url https://<SUBDOMAIN>.zendesk.com/api/services/zis/registry/job_specs/install?job_spec_name=zis:<YOUR_INTEGRATION>:job_spec:ticket_update_to_slack \--user <YOUR_EMAIL>:<YOUR_PASSWORD>

    This lets ZIS know your account wants to use the integration to respond to a ticket updated event.

Testing the integration

To test the integration, you need to trigger the event for the job spec specified earlier. Log in to your Support account and add a comment to a ticket. This triggers a flow in ZIS, which makes a request to our middleware, and posts a message to your Slack channel.