In this tutorial, you'll use ZIS JSON Web Tokens (JWTs) to verify requests from a ZIS integration. As part of the tutorial, you'll create a ZIS integration and a middleware service.

The integration you create listens for Ticket Comment Created events in Zendesk. When it detects the event, the integration's ZIS flow sends a POST request containing event data to the middleware.

Each request includes a ZIS JWT in the X-ZIS-Authorization header. The middleware verifies this JWT to ensure the request is legitimate. If so, the middleware passes the request to an external target. This target represents an external application.

For the tutorial, you'll use an AWS Lambda function as the middleware service. In production, you can use any service or tech stack that can handle HTTP requests and verify JWTs.

Disclaimer: Zendesk provides this article for instructional purposes only. Zendesk doesn't provide support for third-party services, such as AWS.

What you'll need

To complete this tutorial, you'll need the following:

Creating the AWS Lambda function

To start, create an AWS Lambda function named "zis_jwt_example". The function will run Node.js code and have a function URL. Later, you'll use the URL to call the function from a ZIS flow.

  1. Sign in to your AWS account and open the Functions page of the Lambda console.

  2. On the Functions page, click Create function.

  3. On the Create function page, do the following:

    • Enter a Function name of "zis_jwt_example"
    • Choose a Runtime of "Node.js 20.x"
    • Under Advanced settings, select Enable function URL. Under Auth type, select NONE.

    Leave other settings as is.

  4. Click Create function.

  5. Save the resulting Function URL. You'll use this URL later in the tutorial.

Uploading the Lambda function's code

Next, create and upload the Node.js code that runs when you call the Lambda function. The code you upload must include any dependencies.

  1. On your computer, create a new directory named zis_jwt_example.

    mkdir zis_example_jwt
  2. In your shell, navigate to the zis_example_jwt directory.

    cd zis_example_jwt
  3. In the directory, use the following command to create a new npm package.

    npm init -y
  4. Your Lambda function's code has two dependencies: jsonwebtoken and axios. You need to install these packages in the zis_jwt_example directory.

    Use npm to install the jsonwebtoken package.

    npm install jsonwebtoken
  5. Use npm to install the axios package.

    npm install axios
  6. Create a JavaScript file named index.js.

    touch index.js
  7. Add the following code to index.js. In the code, replace:

    • "ZIS_JWT_PUBLIC_KEY" with the JWT public key you received when you registered the ZIS integration
    • Replace "EXTERNAL_TARGET_URL" with your RequestBin endpoint URL
    const jwt = require("jsonwebtoken");const axios = require("axios");
    // Replace with your integration's JWT public key and your RequestBin endpoint URLconst jwtPublicKey = "ZIS_JWT_PUBLIC_KEY";const externalTargetURL = "EXTERNAL_TARGET_URL";
    exports.handler = (event, context) => {  // Verify JWT  const token = event.headers["x-zis-authorization"];  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;    }
        // Parse input    const input = JSON.parse(event.body);    console.log("Input: ", input);
        // Post request to external target    if (input) {      axios        .post(externalTargetURL, input)        .then((response) => {          console.log("Request sent: " + response.data);          context.succeed("Request sent: " + response.data);        })        .catch((error) => {          console.error("Failed: " + error);          context.fail("Failed: " + error);        });    }  });};

    When a ZIS flow makes a request to non-Zendesk URL, ZIS includes a JWT in the X-ZIS-Authorization request header. This code retrieves and decodes the JWT from the header. The code then verifies the JWT using the integration's JWT public key. If successful, the code then posts the request's body to your RequestBin endpoint URL.

  8. Use the following command to compress the zis_jwt_example directory's contents into a ZIP file.

    zip -r function.zip .

    The command creates a functions.zip file in the zis_jwt_example directory.

  9. On the Lambda function's page, click Upload from and select .zip file.

  10. Click Upload.

  11. In the zis_jwt_example directory, select the functions.zip file you created in step 8.

  12. Click Save.

Creating the integration

Next, create a ZIS integration that calls the Lambda function URL when it detects a Ticket Comment Created event.

  1. Create a JSON file named my_zis_bundle.json.

  2. Add the following bundle skeleton to my_zis_bundle.json:

    {  "name": "Example ZIS integration with JWT verification",  "description": "Send post to AWS Lambda function when comment is added",  "zis_template_version": "2019-10-14",  "resources": {    "PostToLambda": {      "_placeholder_": "ZIS custom action definition goes here"    },    "TicketCommentAddedFlow": {      "_placeholder_": "ZIS flow goes here"    },    "TicketCommentAddedJobSpec": {      "_placeholder_": "ZIS job spec goes here"    }  }}

    You'll define the custom action, ZIS flow, and job spec in the next steps.

  3. In my_zis_bundle.json, replace the PostToLambda placeholder with the following custom action definition. In the definition, replace "AWS_LAMBDA_FUNCTION_URL" with the Lambda function URL you created earlier.

    "PostToLambda": {  "type": "ZIS::Action::Http",  "properties": {    "name": "PostToLambda",    "definition": {      "method": "POST",      "url": "AWS_LAMBDA_FUNCTION_URL",      "requestBody": {        "ticket_id.$": "$.ticket_id",        "comment_body.$": "$.comment_body"      }    }  }},

    When called, the custom action sends a POST request to the AWS Lambda function's API endpoint. The request body contains a comment from a Zendesk ticket.

  4. Replace the TicketCommentAddedFlow placeholder with the following ZIS flow definition. In the definition, replace "INTEGRATION" with your integration key.

    "TicketCommentAddedFlow": {  "type": "ZIS::Flow",  "properties": {    "name": "TicketCommentAddedFlow",    "definition": {      "StartAt": "PostTicketUpdate",      "States": {          "PostTicketUpdate": {          "Type": "Action",          "ActionName": "zis:INTEGRATION:action:PostToLambda",          "Parameters": {            "ticket_id.$": "$.input.ticket_event.ticket.id",            "comment_body.$": "$.input.ticket_event.comment.body"          },          "End": true        }      }    }  }},

    The flow contains a single Action state. The state calls the custom action you defined in the previous step.

  5. Replace the TicketCommentAddedJobSpec placeholder with the following job spec definition. In the definition, replace "INTEGRATION" with your integration key.

    "TicketCommentAddedJobSpec": {  "type": "ZIS::JobSpec",  "properties": {    "name": "TicketCommentAddedJobSpec",    "event_source": "support",    "event_type": "ticket.CommentAdded",    "flow_name": "zis:INTEGRATION:flow:TicketCommentAddedFlow"  }}

    The job spec tells ZIS to run the ZIS flow when it detects a Commend Created event.

  6. Save my_zis_bundle.json. The file should now look like this:

    {  "name": "Example ZIS integration with JWT verification",  "description": "Send post to AWS Lambda function when comment is added",  "zis_template_version": "2019-10-14",  "resources": {    "PostToLambda": {      "type": "ZIS::Action::Http",      "properties": {        "name": "PostToLambda",        "definition": {          "method": "POST",          "url": "AWS_LAMBDA_FUNCTION_URL",          "requestBody": {            "ticket_id.$": "$.ticket_id",            "comment_body.$": "$.comment_body"          }        }      }    },    "TicketCommentAddedFlow": {      "type": "ZIS::Flow",      "properties": {        "name": "TicketCommentAddedFlow",        "definition": {          "StartAt": "PostTicketUpdate",          "States": {            "PostTicketUpdate": {              "Type": "Action",              "ActionName": "zis:INTEGRATION:action:PostToLambda",              "Parameters": {                "ticket_id.$": "$.input.ticket_event.ticket.id",                "comment_body.$": "$.input.ticket_event.comment.body"              },              "End": true            }          }        }      }    },    "TicketCommentAddedJobSpec": {      "type": "ZIS::JobSpec",      "properties": {        "name": "TicketCommentAddedJobSpec",        "event_source": "support",        "event_type": "ticket.CommentAdded",        "flow_name": "zis:INTEGRATION:flow:TicketCommentAddedFlow"      }    }  }}
  7. Upload the bundle to ZIS.

    curl -X POST https://{subdomain}.zendesk.com/api/services/zis/registry/{integration}/bundles \  -u {email}/token:{api_token} \  -H "Content-Type: application/json" \  -d @my_zis_bundle.json
  8. Install the job spec to enable the integration.

    curl -X POST "https://{subdomain}.zendesk.com/api/services/zis/registry/job_specs/install?job_spec_name=zis:{integration}:job_spec:TicketCommentAddedJobSpec" \  -u {email}/token:{api_token}

Testing the integration

To test the integration and middleware, use the following request to create a ticket with a comment.body.

curl -X POST https://{subdomain}.zendesk.com/api/v2/tickets.json \  -u {email}/token:{api_token} \  -H "Content-Type: application/json" \  -d '{    "ticket": {      "subject": "My printer is on fire!",      "comment": {        "body": "The smoke is very colorful."      }    }  }'

Navigate to your RequestBin dashboard. You should see a JSON payload with the ticket id and comment. This may take several minutes.

Congratulations! You've used JWTs to verify requests from a ZIS integration. For another example of a ZIS integration, see the Zendesk app as an admin interface tutorial series.