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 Create 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 to create 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

The ZIS Flow is a sequence of states (steps) that captures the business logic of an integration in a declarative fashion. The flow contains various types of states which is explained later. For this example, you’ll use a type of state called Action. The Action state is useful when making external HTTP calls with or without authentication.

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

Create an action definition

First, let's create a ZIS Action, also known as an the action definition. The action definition is the wrapper that makes the HTTP call. It requires a few arguments to be passed in as an input, and 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 value of ticketId can be evaluated using $.ticketId inside the definition.

The action definition is shown below.

{  "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"        }      }    }  }}

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, let's look at how to make use of it by declaring 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 written, let's look at how to wire these in a ZIS Flow.

A flow can contain a series of states. However, in this example, there's a single state 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 and States. 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.

The Zendesk.TicketCreated state has an Endproperty to signal ZIS Engine whether this is the terminal state for the execution.

Defining a JobSpec

Now you have the flow, let's look at how to set up a trigger so the flow gets to run anytime you receive a specific type of event.

A trigger is set up by specifying a job spec resource. In this example, you’ll set up JobSpec for the ticket.Created event type and map it 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"    }  }}

JobSpec requires a few properties:

  • name: The name of the JobSpec. You’ll use this later to 'install' the JobSpec
  • event_source: The origin system that generated the event
  • event_type: The domain event type. See Trigger events for all supported event types
  • flow_name: The flow that should run

In this example, a Support Ticket Created event triggers a flow. This is just one of many types of events that can trigger a flow. See Trigger events for more information.

Putting it all together in a ZIS Bundle

Now that you have written the flow and job spec, let's look at how to pack these resources in a ZIS Bundle. The bundle is the format that contains the flow, and can contain action definitions and job specs. Example:

{  "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"      }    }  }}

Uploading the ZIS Bundle

Save the bundle you have created as post_tickets_to_webhook_bundle.json file. Run the following command to upload the bundle using the ZIS Bundles API:

curl \  --request POST \  --url https://<SUBDOMAIN><YOUR_INTEGRATION>/bundles \  --user <EMAIL>:<PASSWORD> \  --header 'content-type: application/json' \  -d @post_tickets_to_webhook_bundle.json

Enabling the JobSpec

Make an API call to the Install JobSpec endpoint that installs the respective JobSpec and enables a flow on a customer's account.

Example request:

curl --request POST \  --url 'https://<SUBDOMAIN><YOUR_INTEGRATION>%3Ajob_spec%3Aticket_created_jobspec' \  --user <EMAIL>:<PASSWORD>

The response should be an empty 200 OK.

It enables the JobSpec that was uploaded to be part of your bundle.

Testing the integration

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

Then visit your URL. You should see a JSON payload that reflects the status and priority you entered when creating the ticket. Example:

{  "status": "open",  "priority": "high"}

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.