From the team

Developer Blog

Product announcements, technical deep dives, and behind-the-scenes looks at what we are building for the Zoom Developer platform.


AI-Services

Route customer complaints to the right teams with the Zoom Decisions API

Ekaansh Arora4 min read
  • api
  • ai-services
  • customer-support

Summary:Use the Zoom Decisions API to classify customer complaints, check whether they need escalation, and return structured results your ticketing system can use.

A customer asks for a refund because they were charged twice. Another asks for a refund because they can't sign in to a service they've paid for. Billing handles the duplicate charge; technical support handles the sign-in problem. Routing both complaints by the word "refund" can leave one team passing a ticket to another before anyone can help.

The Zoom Decisions API evaluates a complaint against questions you define and returns structured answers. Give it the customer's message and your team descriptions. Your application can read the selected team and each option's probability to assign a ticket or send it for review.

The same request can also check whether the complaint needs escalation and score its urgency. For support calls, transcribe the audio with the Scribe API first, then pass the transcript to Decisions as input.

Prerequisites

Try it in the playground

In the Platform Studio playground, select Decisions API under AI Services and enter this complaint:

I was charged twice for my subscription this month. Please refund the duplicate charge.

Add a choice question that asks which team should handle the complaint, with these choices:

  • sales: sales inquiries and purchasing
  • support: technical issues, account access, and product assistance
  • billing: payments, invoices, duplicate charges, and refunds caused by billing errors

Run the request and inspect the result in Output.

Route by the underlying issue. In this example, support handles account-access problems even when the customer asks for a refund; billing handles refunds caused by billing errors. Include those boundaries in your team descriptions and question instructions.

Route a complaint from your application

To make the same request from your backend, set TOKEN to your AI Services JWT and call the Fast mode endpoint:

curl --fail-with-body -X POST "https://api.zoom.us/v2/aiservices/decisions/decide" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "input": "I was charged twice for my subscription this month. Please refund the duplicate charge.",
    "questions": [
      {
        "type": "choice",
        "name": "route",
        "instructions": "Which team should handle the underlying issue in this customer complaint? Route account-access problems to support even when the customer asks for a refund.",
        "choices": [
          {
            "value": "sales",
            "description": "Sales inquiries and purchasing."
          },
          {
            "value": "support",
            "description": "Technical issues, account access, and product assistance."
          },
          {
            "value": "billing",
            "description": "Payments, invoices, duplicate charges, and refunds caused by billing errors."
          }
        ]
      }
    ]
  }'

Fast mode returns the completed answers in one synchronous HTTP response. The default model is zoom-decisions-standard; set model to zoom-decisions-pro for higher decision accuracy on more complex workloads.

The routing answer looks like this (values are illustrative):

{
    "type": "choice",
    "name": "route",
    "choice": "billing",
    "probabilities": [
        { "value": "sales", "probability": 0.01 },
        { "value": "support", "probability": 0.02 },
        { "value": "billing", "probability": 0.97 }
    ],
    "confidence": 0.97
}

Find the answer named route in the response's answers array. Check its type before reading choice, since a refusal answer has no choice. For a choice answer, map the selected value to a queue and create or update the ticket in your application.

Add escalation and urgency checks

A predicate question estimates whether a condition is true. Write your escalation rule in its instructions, for example "Does the customer report an outage that blocks their work?" The answer is a probability between 0 and 1. Choose the threshold at which your application escalates the ticket.

The local AI Services playground shows a separate outage example with a needs_escalation predicate:

Decisions playground evaluating a production outage with a predicate question and displaying the escalation probability and JSON response

A score question rates urgency against ordered levels you define, starting at index 0. The returned score is a probability-weighted average of the level indices, so it can fall between two levels. Define how your application maps that score to ticket priorities.

Add both questions to the same questions array as the routing question, and Decisions evaluates all three against the complaint in one call:

{
    "input": "I was charged twice for my subscription this month. Please refund the duplicate charge.",
    "questions": [
        {
            "type": "choice",
            "name": "route",
            "instructions": "Which team should handle the underlying issue in this customer complaint? Route account-access problems to support even when the customer asks for a refund.",
            "choices": [
                {
                    "value": "sales",
                    "description": "Sales inquiries and purchasing."
                },
                {
                    "value": "support",
                    "description": "Technical issues, account access, and product assistance."
                },
                {
                    "value": "billing",
                    "description": "Payments, invoices, duplicate charges, and refunds caused by billing errors."
                }
            ]
        },
        {
            "type": "predicate",
            "name": "needs_escalation",
            "instructions": "Does the customer report an outage that blocks their work?"
        },
        {
            "type": "score",
            "name": "urgency",
            "instructions": "Rate how much this issue disrupts the customer.",
            "levels": [
                { "label": "Minor inconvenience" },
                { "label": "Workaround available" },
                { "label": "Fully blocked" }
            ]
        }
    ]
}

The response contains one answer per question, in request order. This excerpt shows illustrative values:

{
    "answers": [
        { "type": "choice", "name": "route", "choice": "billing" },
        {
            "type": "predicate",
            "name": "needs_escalation",
            "probability": 0.04
        },
        { "type": "score", "name": "urgency", "score": 0.3 }
    ]
}

Each question evaluates the complaint independently. If a follow-up question depends on the selected team, send it in a separate request after the routing result arrives.

This playground outage example displays all three results. It uses two routing choices and four urgency levels, so its score ranges from 0 to 3:

Decisions playground displaying escalation, support routing probabilities, and an urgency score of 2.86 across four levels

Test before routing live tickets

Test with complaints your team has already resolved and compare the selected teams with the queues that resolved them. Include messages that mention several issues or leave out details. Use these comparisons to choose confidence and probability thresholds for automatic routing and human review. Send failed requests and refusals to the review queue too.

See the Decisions API docs for question schemas and response handling.