Zendesk to Neo4j Integration

Better control over you reporting needs

Dana Canzano
Neo4j Developer Blog
8 min readAug 14, 2018

--

Zendesk, one of the leading customer service and engagement platforms, provides built in reporting functionality which will satisfy some reporting metrics. However at some point you are simply going to want more, whether it be entirely unique to your organization or something that the default reporting interface can not satisfy.

All hope should not be lost for they also provide a well documented and robust REST API . The REST APi returns all its results in JSON syntax.

The following details how to use the REST API to export data from Zendesk into Neo4 primarily using APOC through the use of apoc.periodic.commit and apoc.load.jsonParams

The data model we will construct in Neo4j will be

Zendesk Graph Model

Whereby the ZendeskTicket is the center of the model represented by the green node (circle).

Each ZendeskTicket has a relationship to the ZendeskUser, represented by the yellow node, who opened the ZendeskTicket as well as the agent who will solve the ZendeskTicket.

Each ZendeskUser has a relationship to the ZendeskOrg, represented by the pink node, the user is a member of.

Additionally, each ZendeskUser has a relationship to any KnowledgeBase Sections, represented by the grey node, the user has choosen to [Follows].

The remaining labels, namely Quarter represented by the blue node and Hour represented by the magenta node are created from the imported data and are used to facilitate reporting.

Finally the Import node, represented by the red node serves as meta-information to support pagination through the Zendesk API. This will be described in further detail.

As to usage of the Zendesk API the following needs to be be understood

Authentication

In the Cypher examples provided below, the calls to the Zendesk REST API will authenticate using a OAUTH token. Generating a token and its use is described at the Zendesk API

Rate Limits

Depending on your Zendesk subscription type, you are limited to the number of REST requests per minute. The Cypher to export the data assume the lowest request rate and to accommodate this you will see references to call apoc.util.sleep(x) which will cause the Cypher to sleep/pause for x milliseconds

Pagination

By default, most Zendesk Rest endpoints will return at most 100 results in a single call. As such the Cypher script will paginate and request the next 100 results until all results have been returned. This is primarily accomplished through the Neo4j meta-data node with label :Import.

Used Tools

The example below has been tested and implemented with Neo4j 3.4.0 and APOC 3.4.0.1 though would expect this to work with most any APOC supported Neo4j version.

For our Neo4j-Support Zendesk environment this will result in almost 10k nodes, and supporting indexes, with properties and associated relationships on the order of 7 minutes to complete on a empty graph.

Part of the slowness is due to the Rate Limits Zendesk imposes and thus the Cypher purposely slows itself down through 3 calls to apoc.util.sleep(60000), thus sleeping for 3 minutes in total.

Preparation

The script will create the following indexes namely for the benefit of MERGE statements.

Finally, the script will create 4 Import nodes which are used to support pagination.

Zendesk API Usage

Documenting input and output fields and sample JSON return string

  1. Zendesk Organizations.
  2. Zendesk Users
  3. Zendesk Incremental Tickets Export
  4. Zendesk KB Section and Subscription

For each type of object to be extracted from Zendesk the Cypher is constructed such that we first define the :Import node for the object, representing the starting ‘page’ in Zendesk to request and then will perform a apoc.load.jsonParams within a apoc.periodic.commit which will thus allow us to paginate. For example the following Cypher is used to load Zendesk Organizations

In all Cypher example below, references to https://neotechnology.zendesk.com/api/v2/ will need to be replaced with your Zendesk domain. We make this easy by defining an “url” parameter for it. Same goes for the :param zd_identifier: “Basic ZGFuYSTlnVlVDNnQ=” OAUTH Zendesk token

Importing Organizations

In the above example the first statement sets the starting page for the Zendesk API to page=1 for requesting Organizations.

The statement withinapoc.periodic.commit is responsible for allowing the Zendesk pagination to work. In this case we are evaluating the returned JSON parameter named next_page and if it is set we update the Import node to reflect the next page from the API to fetch.

Importing Users

The same process occurs for loading Users as the Cypher is:

Exporting of tickets is a bit more involved since rather than a next_page attribute on the API URL it passes a unix start time with parameter start_time. Further the api includes parameter include=metric_sets which will return internal data about the ticket.

Importing Tickets

The Cypher statement to import tickets:

Unlike exporting of Zendesk Organizations and Users, with Tickets we need to paginate until the JSON count value, now aliased as APICount, is less than 1000. If it the APICount is 1000 then this would indicate the JSON return has not consumed all the data and we need to fetch the next 1000. Also the references similar to FOREACH(_ IN CASE WHEN oneticketfields.id=24134566 may need to be modified as they represent custom fields in Zendesk.

KB Sections and Subscriptions

Finally loading of KB Sections and Subscriptions is performed via

Create Additional Relationships

Once the data is loaded appropriate additional relationships are created.

Now that the graph is loaded, what data can we get out of it?

Lets look for users who have never logged in or haven’t logged in for more than 6 months?

These user accounts may want to be deleted/suspended due to inactivity.

Users subscribed per section

Tickets closed per ticket owner and percent of total

Tickets closed per ticket owner timezoned when ticket was opened

Resulting in (not the actual numbers):

I hope this is useful for you, if so, please let me know which other queries you run on top of your data or if you have other improvement suggestions.

You can also find the full script in this GitHub gist

--

--