In this tutorial, you'll learn how to create a ZIS Flow, JobSpec, and package them up in a ZIS Bundle and upload it to ZIS Registry. You’ll create a flow that reacts to Ticket Created events and posts them to a webhook URL.

This tutorial is the third part of a series on building your first integration:


Before getting started, you need an external URL that can receive API requests. Create an external webhook target by visiting It will create a unique webhook URL for you that looks something like Note down the URL.

Understanding a ZIS Flow and JobSpec

A ZIS Flow is a sequence of states that captures the business logic of an integration in a declarative fashion. A flow can contain various types of states. In this tutorial you’ll create an action state which is used for making external HTTP calls with or without authentication.

A flow can be triggered with a JobSpec. The job spec is a subscription mechanism that maps event types to flows. It can be thought of as an event trigger for a workflow.

Creating an action definition

An action definition, also known as a ZIS Action, is the wrapper that makes the HTTP call. It requires a few arguments to be passed in as an input. The output object is returned to the caller. Any input passed to the definition is accessible using the JSONPath semantics. For example, if the input is:

{  "ticketId": 1}

the ticketId value can be evaluated using $.ticketId in the action definition.

You’ll create a custom action definition to post a ticket in Zendesk to the URL you created earlier.

{  "type": "ZIS::Action::Http",  "properties": {    "name": "post-to-webhook-site",    "definition": {      "method": "POST",      "url": "<YOUR_PATH>",      "headers": [        {          "key": "X-Zendesk-Ticket-Id",          "value.$": "$.ticketId"        }      ],      "requestBody": {        "webhook": {          "status.$": "$.ticketStatus",          "priority.$": "$.ticketPriority"        }      }    }  }}

Replace the url value with the unique URL you have obtained earlier from

Defining an action state

Now that you have created the Action definition, you can declare it in an action state. Example:

{  "Zendesk.TicketCreated": {    "Type": "Action",    "ActionName": "zis:<YOUR_INTEGRATION>:action:post-to-webhook-site",    "Parameters": {      "ticketId.$": "$",      "ticketStatus.$": "$.input.ticket_event.ticket.status",      "ticketPriority.$": "$.input.ticket_event.ticket.priority"    }  }}

The state Zendesk.TicketCreated has Type, ActionName, and Parameters properties. The only input that a definition has access to, is what gets passed in from the Parameters property of the state. The jsonPath syntax should look familiar from the action's definition.

ActionName takes the format zis:<YOUR_INTEGRATION>:action:<ACTION_NAME> that uniquely identifies an action. <YOUR_INTEGRATION> is the integration name that you created during in the first tutorial.

Defining a ZIS Flow

Now that you have the action state and its definition, let's look at how to include these in a ZIS Flow.

A flow can contain a series of states. However, in this example, there's a single state Zendesk.TicketCreated that posts ticket events to an external URL:

{  "StartAt": "Zendesk.TicketCreated",  "States": {    "Zendesk.TicketCreated": {      "Type": "Action",      "ActionName": "zis:<YOUR_INTEGRATION>:action:post-to-webhook-site",      "Parameters": {        "ticketId.$": "$",        "ticketStatus.$": "$.input.ticket_event.ticket.status",        "ticketPriority.$": "$.input.ticket_event.ticket.priority"      },      "End": true    }  }}

The flow has two required properties:

  • StartAt specifies the state to execute first. The StartAt tells ZIS Engine that execution should begin at the said state. States can contain a sequence of states. In this example, the Zendesk.TicketCreated state created previously is used
  • States contain a sequence of states. In this example, the Zendesk.TicketCreated state you defined previously is used. It also has an End value of "true" to signal the flow execution to stop

Defining a JobSpec

After defining the flow, you can define a JobSpec which creates a subscription so a flow will run anytime you receive a specific type of event.

Each job spec resource associates one event type with a flow. In the following example, the job spec for the ticket.Created event type is mapped to the flow.

{  "zendesk.TicketCreatedJobSpec": {    "type": "ZIS::JobSpec",    "properties": {      "name": "ticket_created_jobspec",      "event_source": "support",      "event_type": "ticket.TicketCreated",      "flow_name": "zis:<YOUR_INTEGRATION>:flow:post_tickets_to_webhook_flow"    }  }}

A job spec contains the following properties:

  • name: The job spec name. You’ll use this later to install the job spec
  • event_source: The origin system that generated the event
  • event_type: The event type tag. See Trigger events for supported Zendesk events and associated tags
  • flow_name: The flow that should run

In this example, a Ticket Created event initiates a flow.

Putting it all together in a ZIS Bundle

Now that you have defined the flow and job spec, let's look at how to include these resources in a ZIS Bundle. The bundle is a JSON file that contains the flow, action definitions, and job specs. See Anatomy of a ZIS Bundle and the Bundles API.

The resources previously defined for the integration are included in the bundle:

In your text editor, create a file named post_tickets_to_webhook_bundle.json and paste the following content

{  "description": "Post ZD Ticket create events to webhook",  "name": "post_tickets_to_webhook",  "zis_template_version": "2019-10-14",  "resources": {    "post_tickets_to_webhook_flow": {      "type": "ZIS::Flow",      "properties": {        "name": "post_tickets_to_webhook_flow",        "definition": {          "StartAt": "Zendesk.TicketCreated",          "States": {            "Zendesk.TicketCreated": {              "Type": "Action",              "ActionName": "zis:<YOUR_INTEGRATION>:action:post-to-webhook-site",              "Parameters": {                "ticketId.$": "$",                "ticketStatus.$": "$.input.ticket_event.ticket.status",                "ticketPriority.$": "$.input.ticket_event.ticket.priority"              },              "End": true            }          }        }      }    },    "post-to-webhook-site": {      "type": "ZIS::Action::Http",      "properties": {        "name": "post-to-webhook-site",        "definition": {          "method": "POST",          "url": "",          "headers": [            {              "key": "X-Zendesk-Ticket-Id",              "value.$": "$.ticketId"            }          ],          "requestBody": {            "webhook": {              "status.$": "$.ticketStatus",              "priority.$": "$.ticketPriority"            }          }        }      }    },    "ticket_created_jobspec": {      "type": "ZIS::JobSpec",      "properties": {        "name": "ticket_created_jobspec",        "event_source": "support",        "event_type": "ticket.TicketCreated",        "flow_name": "zis:<YOUR_INTEGRATION>:flow:post_tickets_to_webhook_flow"      }    }  }}

Don’t forget to replace <YOUR_INTEGRATION> with the integration name and <YOUR_PATH> with your webhook URL path. Save the file.

Enabling the integration

Now that you have a bundle file, you can upload it, install the jobs spec, and test the integration. Private integrations can only be installed on the Suite Growth plan or above or the Support Professional plan or above.

Uploading the bundle

Bundles are uploaded by making an API request to the Create or Update Bundle endpoint.

Using Postman

  1. In Postman, click the New button in the upper left and select Request.
  2. In the Postman request editor, enter the API endpoint:


Replace {subdomain} with your Zendesk subdomain and {integration} with the integration name you created in the first tutorial. 4. Select POST as the method on the left side. 5. Click the Body tab below your request path. Make sure the raw and JSON options are selected and paste the content from the post_tickets_to_webhook_bundle.json file. 6. Click the Authorization tab, and under Type, select Basic Auth, and enter your credentials. Make sure you’ve enabled password access in Admin Center at Apps and integrations > APIs > Zendesk API. 7. Click Send.

Using cURL

Alternatively, you can use a command line tool such as cURL to make the API request. It will post data from the post_tickets_to_webhook_bundle.json file you created in the previous section.

curl https://<SUBDOMAIN><YOUR_INTEGRATION>/bundles \  -d @post_tickets_to_webhook_bundle.json \  -H "Content-Type: application/json" \  -v -u <EMAIL>:<PASSWORD> -X POST

You should see an empty 200 response.

Install the JobSpec

Make an API call to the Install JobSpec endpoint to install the job spec in the bundle and enable a flow on your account.

Using Postman

  1. In Postman, click the New button in the upper left and select Request.
  2. In the Postman request editor, enter the API endpoint:


Replace {subdomain} with your Zendesk subdomain and {job_spec_name} with "zis:<YOUR_INTEGRATION>:job_spec:ticket_created_jobspec", where <YOUR_INTEGRATION> is your registered integration name. 4. Select POST as the method on the left side. 5. Click the Authorization tab, select Basic Auth as the type, and enter your credentials. 6. Click Send.

Using cURL

To make the API request using cURL:

curl https://<SUBDOMAIN><YOUR_INTEGRATION>%3Ajob_spec%3Aticket_created_jobspec \  -v -u <EMAIL>:<PASSWORD> -X POST

The response should be an empty 200 OK.

Testing the integration

You can test your integration by creating a new ticket in Zendesk. Make sure you give it a status and priority.

Navigate to your dashboard!/`<YOUR_PATH>, replacing <YOUR_PATH>` with your unique webhook URL path. You should see a JSON payload with the ticket status and priority.

If you have trouble receiving the webhook, a good starting point for troubleshooting is viewing the integrations log.

Congratulations, you just created your first flow! It contains all the key building blocks of an integration.