<- All posts

Artificial Intelligence Tickets with Budibase and OpenAI

Ronan McQuillan
19 min read · Jun 26, 2024

Ticketing systems are some of the most common internal tools there are. They’re central to all kinds of ITSM, HR, finance, ops, and other workflows. However, they can also introduce huge amounts of tedious admin work.

This includes basic tasks like categorizing and prioritizing issues, assigning tasks, directing users to self-service resources, and handling communications.

Today, we’re checking out how we can use artificial intelligence to drive more efficient ticketing workflows.

Specifically, we’re going to see how Budibase’s OpenAI integration makes it a breeze to build advanced, custom AI-assisted ticketing tools.

By the end of this guide, you’ll have a fully functional solution that you can modify to your specific requirements.

Let’s start with the basics.

What is an AI ticketing system?

Ticketing systems allow users to submit information about requests, issues, incidents, bugs, or cases.

Service desk colleagues can then view, manage, and respond to tickets.

The idea is to create a centralized platform for receiving and handling service requests according to consistent rules and processes.

This supports a whole range of related workflows. This includes data collection, categorizing and routing issues, prioritizing tickets, scheduling tickets, communicating with stakeholders, and recording outcomes.

An AI ticketing system uses artificial intelligence to assist with these with the goal of providing faster, more effective resolutions to service users. This could be user-facing tasks, such as recommending knowledge base articles or even providing a chatbot.

Alternatively, we might leverage AI for back-end tasks, like categorizing tickets or routing them to relevant service agents.

Check out our round-up of the top ServiceNow alternatives to learn more.

Why use AI for ticketing?

So, how does artificial intelligence improve our ticketing workflows?

We can think about this at two levels. The first is efficiency. That is, we can use AI to perform many tasks that would otherwise require our service agents’ attention. In turn, this means we can provide faster resolutions with lower labor costs.

The second is service quality.

In addition to providing faster responses, AI-powered ticketing can be leveraged to improve outcomes. For instance, providing highly personalized responses at scale or using insights to predict future service needs.

That leads us to…

What are we building?

We’re building a powerful artificial intelligence ticketing system for handling ITSM requests. The goal is to empower users to submit tickets using natural language. We’ll then use OpenAI to categorize and prioritize submissions.

That way, users have a fast, easy way to input ticket data. We’re even going to add translation capabilities to provide multilingual ticketing.

We’re using Budibase to build our UIs, data layer, and automation rules. We’ll start by using our low-code database to set up a data model for our ticketing system.

We’ll then use the dedicated OpenAI integration in our automation builder to create prompts based on our users’ submissions to categorize, translate, and, if required, translate our tickets.

Then, we’re going to build interfaces for service users and service desk colleagues to submit, view, and manage tickets, leveraging Budibase’s autogenerated CRUD UIs and built-in role-based access control.

Here’s what our artificial intelligence ticketing system will look like when we’re done.

You might also enjoy our guide to open-source help desk software .

Let’s start building.

Building an AI ticketing system in 5 steps

The first thing you’ll want to do is sign up for a free Budibase account to start building as many apps as you want. We offer a cloud-based product, but today we’re going to use a self-hosted instance so that we can access the OpenAI integration.

Join 100,000 teams building workflow apps with Budibase

We’ll start by creating a new application. We can import an existing app dump or use one of Budibase’s pre-built templates, but today, we’re going to start from scratch.

When we choose this option, we’ll be prompted to give our app a name that will also be used to generate a URL slug.

We’ll call ours AI Ticketing System.

Artificial Intelligence Tickets

1. Setting up our ticketing data

Once we’ve done this, we’ll be prompted to choose a data source for our application. Budibase offers dedicated connectors for querying relational databases, NoSQL tools, data warehouses, spreadsheets, APIs, and more.

Budibase acts as a proxy to query external data sources without storing them in our platform.

Data Sources

However, we’re going to import a CSV into BudibaseDB. When we choose this option, we’re prompted to give our data table a name and select a file to upload. We’re going to call our table Tickets.

Table

Here’s the CSV data we’re using.

When we upload this, we’ll be prompted to configure the data types of each individual column. Some of our columns can be left as the default option of Text, but we’ll need to make the following changes:

  • Description - Long-Form Text,
  • Status - Options,
  • Priority - Options,
  • Date Created - Date,
  • Date Updated - Date,
  • Category - Options,
  • Comments - Long-Form Text,
  • Translated Comments - Long Form Text.

Here’s what this should look like.

Import

Once our data is imported, we can edit the schema and values with a spreadsheet-like experience in Budibase’s Data section.

Database

Before we move on, we need to make a few more minor tweaks.

A second ago, we set three of our columns to the Options type. However, for this to work, we need to set the available options for each.

We can do this by hitting Edit Column.

Options

The options we’re setting for each column are:

  • Status - Open, Closed, In-Progress,
  • Priority - High, Medium, Low,
  • Category - Hardware, Software, Network, Security, Account, Service Request, Other.

Ticket Category

Adding translation columns

The last change we’re going to make to our data model is adding a couple of extra columns that will allow us to handle multi-lingual ticketing.

First, we’ll use the plus icon to add a new column with the Boolean type and call it Translated.

Translation

Then, we’ll add a JSON column and call it Ticket Translation.

JSON

We’ll need to define the schema of the JSON objects we’re going to store. Hit Open Schema Editor and we’ll add two strings called Title and Description.

Schema

And that’s our data model ready to go.

2. Connecting to OpenAI

Next, we can start building our AI-driven ticketing logic. Budibase offers a dedicated integration for the OpenAI API, making it easy to send prompts from directly within our platform.

In order to utilize this, you’ll need to add your API key as an environment variable within your Budibase installation.

Check out our OpenAI docs to learn more.

Creating an automation

Head to the Automation section. Here, we’ll create a new rule and call it New Ticket. We have a choice of several triggers, including user actions, webhooks, database events, and chron expressions.

Today, we’re going to choose App Action.

Create Automation

We can set our trigger to accept arguments whenever it’s initiated. We’re going to set three variables called rowID, title, and description. Later, we’ll bind these to corresponding values from a given row in our database from our UI.

Trigger

Writing prompts

Next, we’ll hit the plus icon to add an automation action and choose OpenAI.

Automation Step

This action block offers two fields: Prompt, where we can write our ChatGPT prompt, and Model, where we can choose a specific LLM. We’re leaving our Model set to the default option of GPT-3.5 Turbo.

Artificial Intelligence Tickets

Before we start writing our prompt, let’s remind ourselves what we want ChatGPT to do.

Based on the Title and Description that users submit within their tickets, we want to assign the Category, Priority, and Language attributes. If the ticket is submitted in a language other than English, we’ll also populate the Translated Ticket JSON object and set Translated to true.

We want our response to provide this information as a JSON blob so that we can use it later in our automation run.

Start by hitting the lightning bolt icon beside Prompt to open the bindings drawer. Here, we can access all of the data our action is exposed to, including the arguments that were passed to our trigger.

The first thing we want to do in our prompt is provide the Title and Description, and give context to what these are using the following statement:

The following text is the title and description field from an IT ticket: Title: {{ trigger.fields.title }}, Description: {{ trigger.fields.description }}.

Prompt

We’ll then add a statement to translate the ticket if it’s not already in English.

The following text is the title and description field from an IT ticket: Title: {{ trigger.fields.title }}, Description: {{ trigger.fields.description }}.

If a ticket is submitted in a language other than English, translate it to English before proceeding.

The first attribute we want to set is our ticket’s category. We’ll do this by providing the same options that we defined in our data model earlier.

The following text is the title and description field from an IT ticket: Title: {{ trigger.fields.title }}, Description: {{ trigger.fields.description }}.

If a ticket is submitted in a language other than English, translate it to English before proceeding.

Use this to decide if the ticket's category should be Hardware, Software, Security, Network, Account, Service Request, or Other.

Of course, we could provide more detailed logic for how to choose the category, but for demo purposes, we’ll keep it simple.

Hit Save, and we’ll test what we have so far by providing the information from one of our existing rows.

Test

And we can see that ChatGPT is returning “account” which is the appropriate category for a password reset ticket.

Response

We can carry on making adjustments to our prompt and testing the responses as necessary.

We’ll also add a statement with some basic logic on setting a priority level.

The following text is the title and description field from an IT ticket: Title: {{ trigger.fields.title }}, Description: {{ trigger.fields.description }}.

If a ticket is submitted in a language other than English, translate it to English before proceeding.

Use this to decide if the ticket's category should be Hardware, Software, Security, Network, Account, Service Request, or Other.

Also, provide a priority based on how many employees it is likely to affect - High, Medium, or Low.

And again, we’ll test this out.

Response

However, our response now includes the rationale for why a particular priority level was chosen. We don’t want this, so we’ll need to add a statement on how to format our response.

The following text is the title and description field from an IT ticket: Title: {{ trigger.fields.title }}, Description: {{ trigger.fields.description }}.

If a ticket is submitted in a language other than English, translate it to English before proceeding.

Use this to decide if the ticket's category should be Hardware, Software, Security, Network, Account, Service Request, or Other.

Also, provide a priority based on how many employees it is likely to affect - High, Medium, or Low.

Response should be parsable key/value pairs.

Here’s what our new response looks like.

Response Object

Lastly, we’re going to add a statement to provide the English version of the submitted Title and Description, the original language, and a boolean value for whether or not the submission was translated.

The following text is the title and description field from an IT ticket: Title: {{ trigger.fields.title }}, Description: {{ trigger.fields.description }}.

If a ticket is submitted in a language other than English, translate it to English before proceeding.

Use this to decide if the ticket's category should be Hardware, Software, Security, Network, Account, Service Request, or Other.

Also, provide a priority based on how many employees it is likely to affect - High, Medium, or Low.

When a ticket is translated, return the title and description as translatedTitle and translatedDescription. Provide a boolean value for whether or not the original ticket was translated. Call this translated. Also, return the original language and call it language.

The response should be parsable key/value pairs.

Now, we’ll test this out with a non-English submission.

German

And we can see that OpenAI has correctly identified German as the original language and translated our submission into English.

Artificial Intelligence Tickets

Assigning values based on our response

Once we’re happy with our prompt, we need to add an action that will add the values it generated to the original row in our tickets table. We’ll do this by adding an Update Row action and setting the Row ID setting to the corresponding value from our trigger.

Update Row

We need to assign values to the Priority, Category, Ticket Translation, Translated, and Language fields, based on the response from our ChatGPT prompt.

The easiest way to do this is using the JSON.parse() JavaScript method to access the individual values. Start by hitting the lightning bolt icon next to the priority field and selecting JavaScript. Here, we’ll add the following code.

1var jsonString = $("steps.1.response")
2
3var ticketObject = JSON.parse(jsonString);
4
5// Access individual values
6
7var priority = ticketObject.priority;
8
9return priority

JavaScript

We’ll use very similar code for our remaining fields. Here’s what this will look like for our Category.

1var jsonString = $("steps.1.response")
2
3var ticketObject = JSON.parse(jsonString);
4
5// Access individual values
6
7var category = ticketObject.category;
8
9return category

Language:

1var jsonString = $("steps.1.response")
2
3var ticketObject = JSON.parse(jsonString);
4
5// Access individual values
6
7var language = ticketObject.language;
8
9return language

Translated:

1var jsonString = $("steps.1.response")
2
3var ticketObject = JSON.parse(jsonString);
4
5// Access individual values
6
7var translated = ticketObject.translated;
8
9return translated

And Ticket Translation:

 1const jsonString = $("steps.1.response");
 2
 3// Parse the JSON string into a JavaScript object
 4
 5const ticketObject = JSON.parse(jsonString);
 6
 7const translatedTitle = ticketObject.translatedTitle;
 8
 9const translatedDescription = ticketObject.translatedDescription;
10
11// Create a JSON object directly
12
13const jsonObject = {
14
15  Title: translatedTitle,
16
17  Description: translatedDescription
18
19};
20
21// Return the jsonObject
22
23return jsonObject

We’ll then test this out with the data from one of our real rows and confirm that it has executed as expected.

Automation Test

Translating comments

We’re going to add a second automation rule that will handle translations for comments from our service agents.

Our data model contains a column called Comments, where service agents can submit extra information about the tickets, and one called Translated Comments, where we can provide these in the ticket’s original language.

We’ll start by creating a new rule called Translate Comments, again using an App Action Trigger. We’ll set our trigger arguments to rowId, language, and comment.

Trigger

We’ll then add an OpenAI action with the following prompt.

Translate the following response from English into {{ trigger.fields.language }}

{{ trigger.fields.comment }}

Prompt

Then, we’ll add an update row action, setting our Row ID to {{ trigger.fields.rowID }} and our Translated Comments to {{ steps.1.response }}.

As ever, we can test this to confirm that it worked.

Translation

3. Building a ticketing form

Now, we’re ready to start building user interfaces for our artificial intelligence ticketing system.

Head to the Design tab, and we’ll be shown a few options for how we want to create our first screen, including several options for autogenerating layouts based on connected data tables.

New Screen

The first thing we want to build is a screen where service users can submit tickets. So, we’re going to select the Form layout. We’ll then be asked which data table we want to point this at, although our app only has one table anyway.

Table

We then need to choose a form type. We want to create a new row.

New Row

When we’re asked to choose an access role, we’ll leave this set to the default option, Basic.

RBAC

The Form layout generated a working data collection form based on the schema of whichever table we select.

Here’s what this looks like out of the box.

Form

However, we don’t need to display form fields for most of our table’s columns. We’re going to start by deselecting everything except Title and Description using the sliders on the right-hand side.

We’ll also replace our form Title with something more descriptive.

Ticketing Form

Under Styles, we’ll also set our Button Position to Top.

Button Position

Lastly, since our form UI only offers subset of our tables fields, we’ll need to populate the rest of these automatically. Some of these can be added by triggering our New Ticket automation, but the rest will need to be added manually.

Start by opening the actions drawer for our Save button. Under the Save Row action, we’ll hit the Add Column button to populate values for the Status and Date Created fields. We’ll set Status to Open and Date Created to the following JavaScript expression.

1var date = new Date();
2
3return date;

However, we also want to add a record of who created the ticket. Back in the Data section, we’ll add a Single User column called Created By. This will link rows in our Tickets table to Budibase’s internal Users table.

User Column

Then, under our Save Row button action, we’ll bind this new column to {{ Current User._id }}.

Created By

Next, we’ll add a button action to trigger our New Ticket automation. We’ll use bindings to set the rowID to the _id of the row we just saved and the title and description to the corresponding values from our form.

Trigger Automation

Eventually, we’re going to display this form in a modal UI, so we’ll also add a Close Screen Modal action.

Close Modal

Lastly, we’ll publish our app and submit a row of data to confirm that our form behaves as expected.

Note that automations won’t run in our app preview, only in the live application.

AI Ticketing System

And we’ll check that this works in our Data section.

Database

Viewing previous submissions

Next, we’re going to add a screen where service users can view their ticket submissions. Start by hitting the plus icon to add a new screen. This time, we’re choosing the option for a table with detailed side-panels.

Again, we’ll choose our Tickets table and leave the access role set to Basic. This will output a working CRUD UI based on our table’s schema.

CRUD Screen

However, as we said a second ago, we only want to display tickets that were created by the current user. So, we’ll add a filtering expression on our Table Block component, setting the Created By attribute to {{ Current User.globalId }}.

Filter

Now, we can only see the records that are associated with our user account.

Filtered Table

Next, we’ll tidy up our table by deselecting any columns that are lower priority.

Configure Columns

Currently, if a user hits the Create Row button, it will open a form in a side panel. However, we just created a custom form that we want to display in a modal UI. We’ll start by opening the actions drawer for our button and deleting the existing action with the X icon.

Side Panel

We’ll then add a Navigate To action, point this at our form screen, and select the option to use a modal.

Navigate to

We’ll also set our button’s display text to something more descriptive.

new ticket

Lastly, we can access a side panel for editing entries by clicking on any of our table’s rows. We’re going to make a few tweaks to this. Specifically, we’ll set the Type to View, update our Title, and deselect any fields that aren’t relevant to service users.

Form

That’s it for our service user screens.

4. Adding admin screens

Next, we want to add an equivalent screen where service desk colleagues can view and respond to all tickets.

We’ll start by adding another screen with the same Table layout, only this time we’ll set our minimum access role to Power.

RBAC

We’ll start by repeating the process of reducing the display columns, and then we’ll delete the Create New button entirely.

crud

Adding searchability and filtering

Next, we want to make it easy for service agents to find specific tickets. To do this, we’ll use a component called a Dynamic Filter, which allows users to set complex filtering expressions from the front end.

To do this, we’ll first need to add a component called a Data Provider. This accepts a data source and exposes other components on the screen to it. We’re setting ours to our Tickets table.

data provider

We’ll then set our Table block to the output of our Data Provider.

Filtering

And we’ll add our Dynamic FIlter alongside our Heading, pointing it at our Data Provider.

Dynamic filter

Now, we can use this to create custom filtering expressions in our app’s front end.

filtering

Updating our edit form

Lastly, we’re going to make some key changes to the edit form on this screen, too.

Specifically, we want to make some fields editable while others will be read-only. We’re also going to use Budibase’s custom conditionality rules to display the translated submission for non-English tickets instead of the original values.

We’ll start by editing our Edit Row Form block to hide the Translated Comments, Translated Ticket.Title, and Translated Ticket.Description fields.

Form

We’ll then update our title.

Artificial Intelligence Tickets

Next, we’ll select the Disabled option on all form fields except for Status, Category, Priority, and Comments.

Disabled

Now, our service desk colleagues will be able to edit certain fields but only view others.

The last piece of functionality we want to add is dynamically displaying either the original ticket submission or the translated version, depending on which language it was submitted in.

The easiest way to do this is to start by ejecting our Form Block exposing its underlying components.

Eject

Now, we can see each of the individual fields that make up our form.

Component Tree

Select the Title field and open the conditions drawer.

conditions

Here, we can create rules to hide, display, or update the native settings of our components based on any of the data they’re exposed to.

To start, we’ll create a rule that updates the Field component to Ticket Translation.Title when {{ Repeater.Tickets.Translated }} equals True.

Condition

Then, we’ll add a second rule that updates the Label setting to Title (Translated From {{ Repeated.Tickets.Language }}) when the same condition is met.

Conditions

We’ll also repeat this process for the description field.

Description

To wrap up, we’re going to make a couple of tweaks to our Save button actions, just like we did before. Specifically, we want to populate the Date Updated value and trigger our Translate Comments automation.

So, we’ll start by opening the Save Row action and hitting Add Column. Just like we did earlier, we’re going to set Date Updated to the following JavaScript.

1var date = new Date();
2
3return date;

Date

Then, we’ll add a Trigger Automation action pointed at our Translate Comments rule. We’ll bind the rowID argument to {{ Repeater.Tickets._id }}, the language to {{ Repeater.Tickets.Language }}, and the comment to {{ Form.Fields.Comments }}.

ARguments

We’ll publish and open our app to confirm this works by adding a comment to our ticket in English.

Comment

And back on our service user screen, we can see that our comment has been translated back into the original ticket language.

Translation

5. Design tweaks and publishing

From a functional point of view, our artificial intelligence ticketing system is ready to go.

However, before we push it live for users, we’re going to make a few final design adjustments.

First of all, each group of users can only really access a single screen. So, we can remove the links from our nav bar.

To do this, head to Navigation and remove each individual link using the X icon.

Nav

Here’s how this should look.

Nav

Next, under Screen, we’ll head to Theme and select Midnight.

Theme

While we’re here, we’ll also update our default accent colors to match the Budibase brand.

Button Color

When we’re happy, we’ll hit Publish one final time to push our app live.

Publish

Here’s a reminder of what our finished artificial intelligence ticketing system looks like.

Artificial Intelligence Tickets

Budibase is the open-source, low-code platform that empowers IT teams to turn data into action.

Check out our features overview to learn more.