> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gloo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Triage Support Tickets with System One

> Send a System One decision request in Python and TypeScript, read the typed answers, and route a support ticket automatically or to a person based on the probabilities.

This tutorial builds a small support-ticket triage step with [System One](https://docs.gloo.com/api-guides/system-one.md). You'll send a ticket as `state`, ask two questions about it, read the typed answers, and route the ticket automatically when the model is sure, or to a person when it isn't.

Want the concepts first? Read the [System One guide](https://docs.gloo.com/api-guides/system-one.md).

## Prerequisites

Before starting, ensure you have:

* A Gloo AI Studio account
* Your API key from the [API Keys page](https://docs.gloo.com/studio/manage-api-credentials.md), exported as `GLOO_API_KEY`
* **Authentication setup** - Complete the [Authentication Tutorial](https://docs.gloo.com/tutorials/authentication.md) first
* Python 3 with `requests`, or Node.js 18 or later (for the built-in `fetch`)

```bash theme={null}
export GLOO_API_KEY="your_actual_api_key_here"
```

## Understanding the endpoint

| Setting | Value |
| :- | :- |
| Endpoint | `POST https://platform.ai.gloo.com/ai/v2/systemone` |
| Auth | `Authorization: Bearer <your Gloo API key>` |
| Body | `state` (string, object or array) and `questions` (object), plus optional `session_id`, `user` and `trace` |
| Model | Chosen by Gloo. Don't send `model`; the response names the version that answered. |
| Response | One JSON object, not a stream |

See the [API reference](https://docs.gloo.com/api-reference/system-one/v2.md) for every field.

***

## Step 1: Describe the ticket and the questions

The `state` is everything System One reads: here, the ticket text and the customer's plan. The `questions` are what you want decided:

* `category` is a `choice`: which team should handle the ticket. Each option in `criteria` says when to pick it, and `other` catches tickets that fit none of them.
* `wants_refund` is a `noul`: a statement that is either true or false of the ticket.

```python Python theme={null}
import os
import requests

SYSTEM_ONE_URL = "https://platform.ai.gloo.com/ai/v2/systemone"

QUESTIONS = {
    "category": {
        "type": "choice",
        "instructions": "Which team should handle this ticket?",
        "criteria": {
            "billing": "Payments, invoices, refunds, or charges.",
            "technical": "Bugs, errors, or how the product works.",
            "account": "Login, profile, or account settings.",
            "other": "Anything that fits none of the other teams.",
        },
    },
    "wants_refund": {
        "type": "noul",
        "instructions": "The customer is asking for their money back.",
    },
}
```

```typescript TypeScript theme={null}
const SYSTEM_ONE_URL = "https://platform.ai.gloo.com/ai/v2/systemone";

const QUESTIONS = {
  category: {
    type: "choice",
    instructions: "Which team should handle this ticket?",
    criteria: {
      billing: "Payments, invoices, refunds, or charges.",
      technical: "Bugs, errors, or how the product works.",
      account: "Login, profile, or account settings.",
      other: "Anything that fits none of the other teams.",
    },
  },
  wants_refund: {
    type: "noul",
    instructions: "The customer is asking for their money back.",
  },
};
```

## Step 2: Send the request

Send the state and questions, and fail loudly on any non-2xx status:

```python Python theme={null}
def decide(ticket: str, customer_tier: str) -> dict:
    resp = requests.post(
        SYSTEM_ONE_URL,
        headers={"Authorization": f"Bearer {os.environ['GLOO_API_KEY']}"},
        json={
            "state": {"ticket": ticket, "customer_tier": customer_tier},
            "questions": QUESTIONS,
        },
        timeout=30,
    )
    if not resp.ok:
        raise RuntimeError(f"System One returned {resp.status_code}: {resp.text}")
    return resp.json()
```

```typescript TypeScript theme={null}
async function decide(ticket: string, customerTier: string) {
  const resp = await fetch(SYSTEM_ONE_URL, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.GLOO_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      state: { ticket, customer_tier: customerTier },
      questions: QUESTIONS,
    }),
  });
  if (!resp.ok) {
    throw new Error(`System One returned ${resp.status}: ${await resp.text()}`);
  }
  return resp.json();
}
```

## Step 3: Read the answers

Each key in `questions` comes back as a key in `answers`. A `choice` answer has the picked option in `choice` and a probability for every option in `probabilities`. A `noul` answer is the probability that the statement is true.

Read each value without assuming it is there. If one is missing, the code below gets `None` (Python) or `undefined` (TypeScript) instead of failing, and Step 4 sends that ticket to a person.

```python Python theme={null}
result = decide("I was charged twice for my subscription this month.", "pro")
answers = result.get("answers", {})

category = answers.get("category", {}).get("choice")
category_p = answers.get("category", {}).get("probabilities", {}).get(category)
refund_p = answers.get("wants_refund", {}).get("noul")

print(result.get("model"), category, category_p, refund_p)
```

```typescript TypeScript theme={null}
const result = await decide("I was charged twice for my subscription this month.", "pro");
const answers = result.answers ?? {};

const category: string | undefined = answers.category?.choice;
const categoryP: number | undefined =
  category === undefined ? undefined : answers.category?.probabilities?.[category];
const refundP: number | undefined = answers.wants_refund?.noul;

console.log(result.model, category, categoryP, refundP);
```

A response looks like this:

```json theme={null}
{
  "id": "gen-dec-1793981370-uOwFt34AIeyibLNhYu26",
  "model": "typesafe/jev-1.13-20260917",
  "provider": "TypeSafe",
  "answers": {
    "category": {
      "type": "choice",
      "choice": "billing",
      "confidence": 1,
      "probabilities": { "account": 0, "billing": 1, "other": 0, "technical": 0 }
    },
    "wants_refund": { "type": "noul", "noul": 0.66 }
  },
  "usage": { "input_tokens": 386, "output_tokens": 58 }
}
```

## Step 4: Route on the probabilities

Act automatically only when the model is sure, and send everything else to a person. Here, a ticket goes straight to its team when the chosen category's probability is at least 0.9 and the category isn't `other`. A refund request is flagged only when `wants_refund` is at least 0.9; between 0.5 and 0.9 a person checks it. If a value is missing, a person handles it too. To choose thresholds for your own app, see [Act on probabilities](https://docs.gloo.com/best-practices/system-one.md#act-on-probabilities-not-just-the-top-answer).

```python Python theme={null}
THRESHOLD = 0.9

def route(result: dict) -> dict:
    answers = result.get("answers", {})
    category = answers.get("category", {}).get("choice")
    category_p = answers.get("category", {}).get("probabilities", {}).get(category)
    refund_p = answers.get("wants_refund", {}).get("noul")

    # A missing value goes to a person, never through automatically.
    if category is None or category_p is None or category == "other" or category_p < THRESHOLD:
        queue = "human-review"
    else:
        queue = category
    if refund_p is None:
        refund = "check"
    elif refund_p >= THRESHOLD:
        refund = "flag"
    elif refund_p >= 0.5:
        refund = "check"
    else:
        refund = "none"
    return {"queue": queue, "refund": refund, "model": result.get("model")}

print(route(result))
```

```typescript TypeScript theme={null}
const THRESHOLD = 0.9;

function route(result: any) {
  const answers = result.answers ?? {};
  const category: string | undefined = answers.category?.choice;
  const categoryP: number | undefined =
    category === undefined ? undefined : answers.category?.probabilities?.[category];
  const refundP: number | undefined = answers.wants_refund?.noul;

  // A missing value goes to a person, never through automatically.
  const queue =
    typeof category === "string" &&
    typeof categoryP === "number" &&
    category !== "other" &&
    categoryP >= THRESHOLD
      ? category
      : "human-review";
  const refund =
    typeof refundP !== "number" ? "check" : refundP >= THRESHOLD ? "flag" : refundP >= 0.5 ? "check" : "none";
  return { queue, refund, model: result.model };
}

console.log(route(result));
```

For the example response above, the ticket goes to `billing` and the refund request (0.66) goes to a person to check. Keep the `model` with each decision so you can tell which version made it.

For more patterns built the same way (moderation with a review band, intent routing, yes-or-no policy checks and choosing a next step), see [System One Examples](https://docs.gloo.com/tutorials/system-one-examples.md).

## Run it

Put the code from every step, in order, into one file:

* **Python:** save it as `triage.py` and run `python triage.py`.
* **TypeScript:** save it as `triage.mts` and run `npx tsx triage.mts`. The `.mts` extension makes the file an ES module, so the top-level `await` in Step 3 works without a build step.

## Troubleshooting

* **`400` with `state is required` or `questions must be an object`.** The body must be one JSON object with a `state` field and an object-valued `questions` field.
* **`401`.** The `Authorization` header is missing or the key is not valid. Check `GLOO_API_KEY`.
* **`429`.** If `error.type` is `billing_error`, your [organization](https://docs.gloo.com/getting-started/glossary.md#organization) reached its [spend limit](https://docs.gloo.com/studio/billing.md#spend-limits). Otherwise wait for the `Retry-After` delay and retry.

See [Best Practices: System One](https://docs.gloo.com/best-practices/system-one.md#handle-errors-and-retries) for every status and how to retry.

## Next steps

> [card] [Best Practices: System One](https://docs.gloo.com/best-practices/system-one.md)
>
> Write questions that get clear answers, set thresholds, and handle errors in production.

> [card] [System One Examples](https://docs.gloo.com/tutorials/system-one-examples.md)
>
> Short patterns for moderation, intent routing, policy checks and next-step choices.

> [card] [API Reference: System One Decisions](https://docs.gloo.com/api-reference/system-one/v2.md)
>
> Every request and response field, and every error status.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.