Triggering a support ticket creation on feedback submission

Triggering a support ticket creation on feedback submission

With the introduction of the onFeedbackSubmitted callback, it is possible to deeply integrate Markprompt into your customer experience flow. In this guide, we will set up the following flow:

  • User asks a question, and is not presented with a satisfactory response.
  • User downvotes the response as a result.
  • User is presented with a custom form to submit a ticket.
  • Ticket fields are pre-populated with a category and summary based on the conversation history.

Step 1: Setup a callback

First, let's setup our Markprompt component with a callback when feedback is submitted. Here is an example with the React component, but it works for all components:

jsx
1import '@markprompt/css';
2import { Markprompt } from '@markprompt/react';
3
4export function Component() {
5  return <Markprompt
6    projectKey="YOUR-PROJECT-KEY"
7    feedback={{
8      enabled: true,
9      onFeedbackSubmitted: (feedback, messages) => {
10        console.debug(`Feedback: ${feedback.vote}.\n\nMessages: ${JSON.stringify(messages, null, 2)}`);
11      }
12    }}
13  />;
14}

Step 2: Summarizing the conversation

In order to submit the ticket with as much info as possible, let's first generate a summary of the conversation. This can be done with our /v1/chat endpoint with an adequate system prompt. Also, since we don't need to pull in any context, we will add the doNotInjectContext parameter to the query.

js
1const getSummary = async (messages) => {
2  // Coalesce the conversation into one message:
3  const conversation = messages.map(m => m.content).join("\n\n===\n\n);
4
5  const res = await fetch("https://api.markprompt.com/v1/chat", {
6      method: 'POST',
7      headers: {
8        'Content-Type': 'application/json',
9      },
10      body: JSON.stringify({
11        projectKey: "YOUR-PROJECT-KEY",
12        doNotInjectContext: true,
13        excludeFromInsights: true,
14        stream: false,
15        model: "gpt-4",
16        messages: [
17          { content: "Summarize the provided conversation in one paragraph.", role: "system"},
18          { content: conversation, role: "user" }
19        ]
20      })
21    });
22
23  if (!res.ok) {
24    const error = await res.text();
25    throw new Error("Unable to fetch summary. Reason:", error);
26  }
27
28  return (await res.json())?.text;
29}

Step 3: Categorize the conversation

Let's say that the ticketing system also allows us to specify a category from a fixed list, enabling easier triage for the support team. Given the summary, we can let Markprompt categorize the conversation with the category that best matches the conversation topic. Our system prompt will look as follows:

Here is a list of categories:

  • Technical Issues
  • Billing Issues
  • Account Management
  • System Bug Reports
  • Product Information Request
  • Troubleshooting Assistance
  • Feature Requests
  • User Education/Training
  • Service Interruptions/Downtime
  • Compliance and Security Issues

Given this list, assign the following conversation to one of these categories. Reply with only this category.

js
1const getCategory = async (content) => {
2  const categories = [
3    "Technical Issues",
4    "Billing Issues",
5    "Account Management",
6    "System Bug Reports",
7    "Product Information Request",
8    "Troubleshooting Assistance",
9    "Feature Requests",
10    "User Education/Training",
11    "Service Interruptions/Downtime",
12    "Compliance and Security Issues"
13  ];
14
15  const systemPrompt = `Here is a list of categories:
16
17  ${categories.map(c => `- ${c}`).join("\n")}
18
19  Given this list, assign the following conversation to one of these categories. Reply with only this category.`
20
21  const res = await fetch("https://api.markprompt.com/v1/chat", {
22      method: 'POST',
23      headers: {
24        'Content-Type': 'application/json',
25      },
26      body: JSON.stringify({
27        projectKey: "YOUR-PROJECT-KEY",
28        doNotInjectContext: true,
29        excludeFromInsights: true,
30        stream: false,
31        model: "gpt-3.5-turbo",
32        messages: [
33          { content: systemPrompt, role: "system"},
34          { content, role: "user" }
35        ]
36      })
37    });
38
39  if (!res.ok) {
40    const error = await res.text();
41    throw new Error("Unable to fetch category. Reason:", error);
42  }
43
44  return (await res.json())?.text;
45}

Step 4: Building the custom UI

Now that we have all the available information to create a ticket, let's show a custom UI that allows the user to submit it with prepopulated fields:

jsx
1import '@markprompt/css';
2import { useRef } from "react";
3import { Markprompt } from '@markprompt/react';
4
5export function Component() {
6  const dialogRef = useRef();
7  const [ticket, setTicket] = useState(undefined);
8
9  return <>
10    <Markprompt
11      projectKey="YOUR-PROJECT-KEY"
12      feedback={{
13        enabled: true,
14        onFeedbackSubmitted: async (feedback, messages) => {
15          if (feedback.vote !== '-1') {
16            // Skip unless it's a downvote
17            return;
18          }
19
20          const summary = await getSummary(messages);
21          const category = await getCategory(summary);
22          setTicket({ summary, category });
23
24          // Show the modal
25          dialogRef.current.showModal();
26        }
27      }}
28    />
29    <dialog ref={dialogRef}>
30      <a
31        href={`/tickets/new?category=${encodeURIComponent(ticket?.category)}&summary=${encodeURIComponent(ticket?.summary)}`}>
32        Create new ticket
33      </a>
34    </dialog>
35  </>;
36}

That's it! We now have a workflow that binds user feedback on Markprompt with an external ticketing system, automatically prepopulating fields so that customers don't have to.