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

# Classify

> Answer yes/no, multiple-choice and scored questions about any text or JSON state with a classification model through the AI Gateway.

<Note>
  Classify is in Beta, and its request and response contract matches the [TypeSafe Classify API](https://docs.typesafe.ai/api).

  * Answers natively on `typesafe/jev-latest`, and through structured outputs on a set of small chat models: see [Supported models](#supported-models).
  * Does not apply PII plugins, guardrails, or evaluators yet: see [Enforcement during the Beta](#enforcement-during-the-beta).
  * Requires an API key with the `classify` permission.
</Note>

## Overview

**Classify** answers typed questions about a piece of content in one call. Send the content as `state` and one or more named `questions` to [`POST /classify`](/reference/classify/create-classify) on the **AI Gateway** base URL, `https://my.orq.ai/v3/router`.

The response returns one structured answer per question, so nothing has to be parsed out of free-form text. A `choice` or `score` answer also carries the probabilities behind it and a confidence for the selected option or level.

A question pairs plain-language `instructions` with a `type` that fixes the answer shape, and `criteria` that define the rubric:

| `type`   | What the answer is                                                        | Reach for it when                                                         |
| -------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `noul`   | The probability that the statement in `instructions` is true, in `[0, 1]` | Yes/no decisions, or a probability to threshold instead of a hard label   |
| `choice` | One option from a named rubric, with a probability for each option        | The content belongs to one of a known set: a queue, a topic, a category   |
| `score`  | A position on an ordered scale, with a probability for each level         | The answer is a degree rather than a category: sentiment, quality, effort |

`state` is not limited to text: an object or array is serialized as JSON before classification, so the same call can judge a customer message, a tool payload, or a whole record.

Several questions can be asked at once, each with its own rubric, so a request costs one round trip instead of one call per question. See [Question types](#question-types) for the `criteria` each type expects.

The request is traced, priced, and rate-limited like other gateway traffic. See [Enforcement during the Beta](#enforcement-during-the-beta) for what applies today.

## Use cases

* **Triage and routing.** Ask `noul` whether the message reports a problem and `choice` for the topic or product, then route on the labels and escalate the rows whose confidence is low.
* **Building labelled data.** Turn a rubric into a `choice` question to label a corpus for [Evaluators](/ai-studio/optimize/evaluators) or [Datasets](/ai-studio/optimize/datasets), and use `probabilities` to pick the rows worth human review.
* **Scoring open feedback.** Use `score` on survey answers, support transcripts, or review text to get an ordinal reading without writing a scoring prompt.
* **Gating expensive work.** Use a `noul` question on the native model, whose output tokens are free, as a pre-check, and send only the requests that pass on to a larger model.
* **Enriching structured records.** Pass a record as `state` and ask one question per field to derive, instead of building a prompt per field.
* **Comparing models on one rubric.** Ask the same questions against `typesafe/jev-latest` and a chat model to see where they disagree. Thresholds tuned on one do not transfer, so compare answers rather than raw probabilities.

## Supported models

| Model                                                                                                                 | How it answers                                                       | Output tokens            |
| --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | ------------------------ |
| `typesafe/jev-latest`                                                                                                 | Native classify model. Probabilities are calibrated.                 | Free                     |
| `anthropic/claude-haiku-4-5`, also as `google/claude-haiku-4-5` and the Bedrock `aws/...claude-haiku-4-5...` profiles | One structured-output call; the model reports its own probabilities. | Billed at the model rate |
| `google-ai/gemini-3.8-flash`, `google/gemini-3.8-flash`                                                               | One structured-output call; the model reports its own probabilities. | Billed at the model rate |
| `zai/glm-5.3-flash`                                                                                                   | One forced tool call; the model reports its own probabilities.       | Billed at the model rate |
| `openai/gpt-5.6-luna`, also as `azure/gpt-5.6-luna`                                                                   | One structured-output call; the model reports its own probabilities. | Billed at the model rate |
| `groq/openai/gpt-oss-20b`, `groq/openai/gpt-oss-120b`, `groq/openai/gpt-oss-safeguard-20b`, `groq/qwen/qwen3.8-27b`   | One structured-output call; the model reports its own probabilities. | Billed at the model rate |

The table lists model families. Every offering of these models, including regional and reseller variants, is in [Supported models](/ai-gateway/supported-models#classify-models).

Chat models return the same fields with their own probabilities, which express the model's estimate and are not calibrated, so thresholds tuned on one model do not transfer to another.

Reasoning is set to the minimum the model allows.

Any other model returns `400`.

## Quick start

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST https://my.orq.ai/v3/router/classify \
    -H "Authorization: Bearer $ORQ_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "typesafe/jev-latest",
      "state": "The package arrived two days late and the box was damaged, but support refunded me right away.",
      "questions": {
        "is_complaint": {
          "type": "noul",
          "instructions": "Does the customer report a problem with their order?",
          "criteria": {
            "true": "A problem with the order is described",
            "false": "No problem is described"
          }
        },
        "topic": {
          "type": "choice",
          "instructions": "What is the main topic of the message?",
          "criteria": {
            "shipping": "Delivery timing or packaging",
            "billing": "Charges, refunds or invoices",
            "other": null
          }
        },
        "satisfaction": {
          "type": "score",
          "instructions": "How satisfied is the customer overall?",
          "criteria": ["Unhappy", "Neutral", "Satisfied"]
        }
      }
    }'
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import { Orq } from "@orq-ai/node";

  const orq = new Orq({
    apiKey: process.env.ORQ_API_KEY ?? "",
  });

  const result = await orq.router.classify.create({
    model: "typesafe/jev-latest",
    state:
      "The package arrived two days late and the box was damaged, but support refunded me right away.",
    questions: {
      is_complaint: {
        type: "noul",
        instructions: "Does the customer report a problem with their order?",
        criteria: {
          true: "A problem with the order is described",
          false: "No problem is described",
        },
      },
      topic: {
        type: "choice",
        instructions: "What is the main topic of the message?",
        criteria: {
          shipping: "Delivery timing or packaging",
          billing: "Charges, refunds or invoices",
          other: null,
        },
      },
      satisfaction: {
        type: "score",
        instructions: "How satisfied is the customer overall?",
        criteria: ["Unhappy", "Neutral", "Satisfied"],
      },
    },
  });

  console.log(result.answers);
  console.log(result.usage);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  from orq_ai_sdk import Orq
  import os

  orq = Orq(api_key=os.environ.get("ORQ_API_KEY", ""))

  result = orq.router.classify.create(
      model="typesafe/jev-latest",
      state="The package arrived two days late and the box was damaged, but support refunded me right away.",
      questions={
          "is_complaint": {
              "type": "noul",
              "instructions": "Does the customer report a problem with their order?",
              "criteria": {
                  "true": "A problem with the order is described",
                  "false": "No problem is described",
              },
          },
          "topic": {
              "type": "choice",
              "instructions": "What is the main topic of the message?",
              "criteria": {
                  "shipping": "Delivery timing or packaging",
                  "billing": "Charges, refunds or invoices",
                  "other": None,
              },
          },
          "satisfaction": {
              "type": "score",
              "instructions": "How satisfied is the customer overall?",
              "criteria": ["Unhappy", "Neutral", "Satisfied"],
          },
      },
  )

  print(result.answers)
  print(result.usage)
  ```
</CodeGroup>

## Request

| Field       | Type                    | Required | Description                                                                                                                                                                                                       |
| ----------- | ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model`     | string                  | Yes      | One of the [supported models](#supported-models).                                                                                                                                                                 |
| `state`     | string, object or array | Yes      | The content to classify. Objects and arrays are serialized as JSON before classification.                                                                                                                         |
| `questions` | object                  | Yes      | Maps each question key to its definition. The response uses the same keys in `answers`.                                                                                                                           |
| `name`      | string                  | No       | Name shown on the trace. A default name is used when omitted.                                                                                                                                                     |
| `metadata`  | object                  | No       | Key-value pairs attached to the trace, for filtering later.                                                                                                                                                       |
| `retry`     | object                  | No       | Retry configuration for this request: `count` (1 to 5) and `on_codes`, the status codes that trigger a retry.                                                                                                     |
| `identity`  | object                  | No       | Attributes the request to an end user, like the other gateway endpoints: the body identity takes precedence over `X-ORQ-IDENTITY-ID` and the API-key owner. See [Request Metadata](/ai-gateway/request-metadata). |

Each question has a `type` and `instructions`, which can be a string, object, or array. The shape of `criteria` depends on the question type.

### Question types

#### `noul`

Returns the probability that the statement in `instructions` is true for `state`. The probability is calibrated on `typesafe/jev-latest` and model-reported on chat models.

The optional `criteria` object can define what counts as `true` and `false`.

```json JSON theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "type": "noul",
  "instructions": "Does the customer report a problem with their order?",
  "criteria": {
    "true": "A problem with the order is described",
    "false": "No problem is described"
  }
}
```

#### `choice`

Selects one option from a rubric. The required `criteria` object maps each option name to a description. Set a description to `null` when the option name is enough for the model to interpret it.

```json JSON theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "type": "choice",
  "instructions": "What is the main topic of the message?",
  "criteria": {
    "shipping": "Delivery timing or packaging",
    "billing": "Charges, refunds or invoices",
    "other": null
  }
}
```

#### `score`

Returns a position on an ordered scale. Set `criteria` to an array with at least two level descriptions, ordered from lowest to highest. The answer index refers to this array.

```json JSON theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "type": "score",
  "instructions": "How satisfied is the customer overall?",
  "criteria": ["Unhappy", "Neutral", "Satisfied"]
}
```

## Response

Two paths produce `confidence` and `score`, and the numbers mean different things on each: `typesafe/jev-latest` answers natively, and a chat model answers through one structured-output call. Every field below states both.

```json JSON theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "model": "typesafe/jev-latest",
  "answers": {
    "is_complaint": { "type": "noul", "noul": 0.97 },
    "topic": {
      "type": "choice",
      "choice": "shipping",
      "probabilities": { "shipping": 0.79, "billing": 0.2, "other": 0.01 },
      "confidence": 0.68
    },
    "satisfaction": {
      "type": "score",
      "score": 1.41,
      "legend": { "0": "Unhappy", "1": "Neutral", "2": "Satisfied" },
      "probabilities": { "0": 0.03, "1": 0.52, "2": 0.45 },
      "confidence": 0.28
    }
  },
  "usage": {
    "input_tokens": 412,
    "output_tokens": 0,
    "input_cost": 0.000017304,
    "output_cost": 0,
    "total_cost": 0.000017304
  },
  "telemetry": {
    "trace_id": "4f2294c4bd55372a354be496871aa917",
    "span_id": "a354be496871aa91"
  }
}
```

| Field                                      | Description                                                                                                                                                                                                                                                                                                                                                                                                    |
| ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model`                                    | The model ID from the request.                                                                                                                                                                                                                                                                                                                                                                                 |
| `answers`                                  | One entry for each question key. Each entry repeats the question `type`.                                                                                                                                                                                                                                                                                                                                       |
| `answers.<key>.noul`                       | `noul` only. Probability in the range `[0, 1]` that the statement holds.                                                                                                                                                                                                                                                                                                                                       |
| `answers.<key>.choice`                     | `choice` only. The selected option name.                                                                                                                                                                                                                                                                                                                                                                       |
| `answers.<key>.score`                      | `score` only. Position on the scale, not an index. On chat models it is the weighted index: each level index multiplied by that level's probability, summed, so a three-level answer lands on a fraction such as `1.42` for the distribution above. `typesafe/jev-latest` returns its own score instead, which is why that answer shows `1.41` beside a distribution whose weighted index is `1.42`.           |
| `answers.<key>.legend`                     | `score` only. Maps each level index to its description in the `criteria` array.                                                                                                                                                                                                                                                                                                                                |
| `answers.<key>.probabilities`              | `choice` and `score`. Probability for each option name or level index.                                                                                                                                                                                                                                                                                                                                         |
| `answers.<key>.confidence`                 | `choice` and `score`. How sure the model is of the answer it selected. On chat models it is the largest value in `probabilities`, so it carries no information beyond them. `typesafe/jev-latest` returns its own calibrated value instead, which can sit below the largest probability: in the example above, `0.68` against `0.79` for the `choice` answer and `0.28` against `0.52` for the `score` answer. |
| `usage.input_tokens`                       | Billed input tokens.                                                                                                                                                                                                                                                                                                                                                                                           |
| `usage.output_tokens`                      | Output tokens. Free for `typesafe/jev-latest`, billed at the model rate for chat models.                                                                                                                                                                                                                                                                                                                       |
| `usage.input_cost`                         | Cost in USD of the input tokens. Present when billing was computed.                                                                                                                                                                                                                                                                                                                                            |
| `usage.output_cost`                        | Cost in USD of the output tokens. `0` for `typesafe/jev-latest`. Present when billing was computed.                                                                                                                                                                                                                                                                                                            |
| `usage.total_cost`                         | Total cost in USD. This follows the same format as the Responses API `usage` fields.                                                                                                                                                                                                                                                                                                                           |
| `telemetry.trace_id` / `telemetry.span_id` | Identifiers of the trace and root span recorded for the request. The same values are returned in the `x-orq-trace-id` and `x-orq-trace-span-id` headers.                                                                                                                                                                                                                                                       |

The SDK response models for this endpoint expose `model`, `answers`, and `usage`, so a caller that needs the trace identifiers reads the `x-orq-trace-id` and `x-orq-trace-span-id` response headers. The handler sets those once the request is traced, so a request rejected before that point carries neither: authentication and authorization (`401`, and `403` for a missing `classify` permission or a model that is not enabled for the workspace or shared with the project), plan and budget limits (`429`), and request-body validation (the `400` for a malformed body, the `422` for an invalid request). Everything rejected after tracing carries them, including the `400` for a model that does not support classify. The `telemetry` object itself is optional and returned by builds newer than some deployments run, so fall back to the headers when it is absent.

Chat models return the same fields, so parsing code does not change with the model, but the numbers do not mean the same thing: their probabilities are the model's own estimates and are not calibrated, and their `confidence` is simply the largest of them. Compare `choice` and `score` across models, not `confidence`.

See the [Create Classify](/reference/classify/create-classify) API reference for the full request and response schema, including the fields accepted alongside the ones above.

## Enforcement during the Beta

Enforced on every request:

| What                         | Effect                                                                                                                                                                                             |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authentication               | A missing or invalid key is rejected with `401`.                                                                                                                                                   |
| The `classify` permission    | A key without it is rejected with `403`. The standard gateway key preset grants it.                                                                                                                |
| Model access                 | The model must be enabled for the workspace or shared with the project, otherwise the request is rejected with `403`; it must also support classify, otherwise the request is rejected with `400`. |
| Plan rate limits and budgets | The request counts toward both, and a rejection returns `429` exactly as on other endpoints. See [Rate limits and quotas](/ai-gateway/features/rate-limits).                                       |
| Tracing, usage, and cost     | The request is traced and billed, and the response reports `usage`; `telemetry` is optional.                                                                                                       |
| Trace masking                | Masks configured through workspace plugins apply to the stored trace. On chat models, the instructions that drive the structured answer are always scrubbed from the model span.                   |
| Request-level `retry`        | Applied, using `count` and `on_codes` from the request.                                                                                                                                            |

Not applied during the Beta:

* **PII plugins.** `state` and `instructions` reach the provider exactly as sent, even when a redaction plugin is configured for traffic that would otherwise match.
* **Guardrails and evaluators.** They do not run, so a matching guardrail rule cannot block a Classify request.
* **Routing rules.** A rule does not rewrite the model, add fallbacks, or serve from cache.
* **Request-level `fallbacks`, `load_balancer`, `timeout`, `cache`, `plugins`, and `guardrails`.** They are not part of this contract. Unknown body fields are ignored rather than rejected, so sending them has no effect and returns no error.

## Pricing and limits

| Model                 | Input                 | Output               | Limits                                                                                                       |
| --------------------- | --------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------ |
| `typesafe/jev-latest` | \$0.042 per 1M tokens | Free                 | 64k context window, and `state` plus the longest question must fit within 32k tokens                         |
| Chat models           | The model's own rate  | The model's own rate | The model's limits from the Model Garden. The structured answer adds a few hundred output tokens per request |

## Errors

| Status        | Cause                                                                                                                                                            |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`         | Malformed JSON body, or `model` is not a [supported model](#supported-models).                                                                                   |
| `401`         | The API key is missing, invalid, or revoked.                                                                                                                     |
| `403`         | The key is valid but lacks the `classify` permission, or the model is not enabled for the workspace or shared with the project.                                  |
| `422`         | The JSON is valid, but the classification structure is not. For example, a `choice` question has no `criteria`, or a `score` question has fewer than two levels. |
| `429`         | A plan rate limit or a budget limit was reached before the provider was called. See [Rate limits and quotas](/ai-gateway/features/rate-limits).                  |
| `429` / `529` | The upstream provider reached a rate limit or is overloaded. The response keeps the upstream status and `Retry-After` header. Retry after the specified delay.   |
