Support toolsTicket routing

Route, classify and label your support tickets

Overview

This guide demonstrates how to use the Inkeep AI Context API to programmatically triage and route tickets.

With inkeep-context you can:

  • Categorize the ticket (e.g. between "feature_request", "account_billing", or "production_issue").
  • Extract static fields - for example, detect if a user has mentioned an invoice ID.
  • Label the ticket with a Subject or other fields you'd typically ask a user to fill out manually.
  • Summarize the ticket and leave the summary as an internal note for your team.

For example, you may:

  • Route production issues directly to an engineering-led queue.
  • Choose topics you'd like Inkeep auto-reply AI to respond to vs ones you'd like to route directly to your team.
  • Populate custom, structured fields in your support ticket system from the user's request.

The inkeep-context API automatically uses your Inkeep knowledge base as context while generating a response.

Note
Note

While this example leverages the Vercel AI SDK and assumes a JavaScript backend, the same principle pattern can be used with any backend language and with any OpenAI compatible SDK.

Since the API follows the OpenAI chat completions format, you can define any custom fields or tool calls relevant for your scenario.

Get started

  1. Inkeep API Key: If you haven't created one yet, get an Inkeep API Key.
  2. Install dependencies: Install the ai, @ai-sdk/openai and zod packages.
npm install @ai-sdk/openai ai zod
bun add @ai-sdk/openai ai zod
pnpm add @ai-sdk/openai ai zod
yarn add @ai-sdk/openai ai zod

Example

In the following scenario, we demonstrate how a single call to the inkeep-context API can be used to output all the relevant fields we'd like for our workflow.

1. Define a schema

You can use zod to define a schema of any structured information we'd like from the AI.

import { z } from 'zod';
 
export const TicketSchema = z.object({
  subject: z.string().describe(
    'A concise (max 80 chars) subject line summarizing the main topic or request of the ticket.'
  ),
  summary: z.string().describe(
    'A clear, direct internal note capturing the key context and summary of key aks of the ticket. Will be left as an internal note to the support team.'
  ),
  category: z.union([
    z.literal('production_issue').describe('Issues affecting production systems or service disruptions'),
    z.literal('account_billing').describe('Billing requests related to an existing account, including refunds, cancellations, etc.'),
    z.literal('feature_request').describe('Requests for new features or enhancements'),
    z.literal('other').describe('Any other category that does not fit well with the above categories'),
  ]).describe('The primary category of the ticket. Choose only from the valid options.'),
  invoiceId: z.string().nullish().describe(
    '(Optional) The invoice ID, if mentioned in the text. Should be undefined or null if no invoice ID is mentioned'
  )
});

We use the describe() function to prompt the AI on how it should "think" about each field. See our prompting guide for more advanced scenarios.

1. Initiate the AI client

First, lets create the Inkeep AI client.

import { createOpenAI } from '@ai-sdk/openai';
 
export const inkeepClient = createOpenAI({
  apiKey: process.env.INKEEP_API_KEY,
  baseURL: 'https://api.inkeep.com/v1',
});
 
export const model = inkeepClient('inkeep-context-expert')

Alternatively, you can use any AI provider for this task. For example:

import { anthropic } from '@ai-sdk/anthropic';
 
export const contextModel = anthropic('claude-3-5-sonnet-20241022')
import { openai } from '@ai-sdk/openai';
 
export const contextModel = openai('gpt-4o')

This may be beneficial for low-latency scenarios where the AI model doesn't need extensive context about your product.

3. Add business logic

Here, we'll use the generateObject function to request data in this structure from the inkeep-context API.

import { generateObject, type CoreUserMessage } from 'ai';
import { TicketSchema } from './schema.ts';
import { contextModel } from './model.ts';
 
export async function triageTicket(serializedTicket: CoreUserMessage['content']) {
  const { object: ticketInfo } = await generateObject({
    model: contextModel,
    schema: TicketSchema,
    maxRetries: 2, 
    messages: [
      {
        role: 'system',
        content: 'You are a system that classifies and extracts data from a user\'s support request.',
      },
      {
        role: 'user',
        content: serializedTicket,
      },
    ],
  });
 
  const { category, invoiceId, subject, summary } = ticketInfo;
 
  /* BUSINESS LOGIC */
  if (ticketInfo.category === 'production_issue') {
    console.log('Routing to engineering queue...');
  } else if (ticketInfo.category === 'account_billing') {
    console.log('Escalating to finance team...');
  } else if (ticketInfo.category === 'feature_request') {
    console.log('Routing to product backlog...');
  } else {
    console.log('Routing to general support...');
    // ...etc.
  }
 
  return ticketInfo;
}

In the BUSINESS LOGIC section, you can handle creating or updating a ticket in your support platform, calling the inkeep-qa API to get an automated reply, add internal comments, etc.

For more advanced workflows or AI agents, you can use tool calling as well.

On this page