> ## 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. Its request and response contract matches the [TypeSafe Classify API](https://docs.typesafe.ai/api) 1:1. The only available model is `typesafe/jev-latest`. Output tokens are free. Classify does not apply PII plugins, guardrails, or evaluators yet. API keys need the standard `classify` permission.
</Note>

## Overview

Call `POST /classify` on the **AI Gateway** base URL, `https://my.orq.ai/v3/router`. Send a string, object, or array as `state`, along with a set of named `questions`. The response contains one structured answer for each question.

Questions can return the probability that a statement is true (`noul`), select one option from a rubric (`choice`), or place the input on an ordered scale (`score`). Because the result is calibrated and structured rather than free-form text, Classify works well for routing, triage, labeling, and scoring.

## 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"}}
  const response = await fetch("https://my.orq.ai/v3/router/classify", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.ORQ_API_KEY}`,
    },
    body: JSON.stringify({
      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"],
        },
      },
    }),
  });

  const result = await response.json();

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

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

  response = requests.post(
      "https://my.orq.ai/v3/router/classify",
      headers={"Authorization": f"Bearer {os.environ.get('ORQ_API_KEY')}"},
      json={
          "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"],
              },
          },
      },
  )

  result = response.json()

  print(result["answers"])
  print(result["usage"])
  ```
</CodeGroup>

## Request

| Field       | Type                    | Required | Description                                                                               |
| ----------- | ----------------------- | -------- | ----------------------------------------------------------------------------------------- |
| `model`     | string                  | Yes      | Classification model. During the Beta, the only available value is `typesafe/jev-latest`. |
| `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`.   |

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 calibrated probability that the statement in `instructions` is true for `state`. 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

```json JSON theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "model": "typesafe/jev-latest",
  "answers": {
    "is_complaint": { "type": "noul", "noul": 0.94 },
    "topic": {
      "type": "choice",
      "choice": "shipping",
      "probabilities": { "shipping": 0.87, "billing": 0.11, "other": 0.02 },
      "confidence": 0.87
    },
    "satisfaction": {
      "type": "score",
      "score": 0.86,
      "legend": { "0": "Unhappy", "1": "Neutral", "2": "Satisfied" },
      "probabilities": { "0": 0.31, "1": 0.52, "2": 0.17 },
      "confidence": 0.52
    }
  },
  "usage": {
    "input_tokens": 412,
    "output_tokens": 0,
    "input_cost": 0.000017304,
    "output_cost": 0,
    "total_cost": 0.000017304
  }
}
```

| Field                         | Description                                                                                       |
| ----------------------------- | ------------------------------------------------------------------------------------------------- |
| `model`                       | ID of the ORQ model that served 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, calculated by weighting each level index by its probability. |
| `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`. Probability of the selected answer.                                         |
| `usage.input_tokens`          | Billed input tokens.                                                                              |
| `usage.output_tokens`         | Included in the response but not billed.                                                          |
| `usage.input_cost`            | Cost in USD of the input tokens. Present when billing was computed.                               |
| `usage.output_cost`           | Always `0` for Classify. Present when billing was computed.                                       |
| `usage.total_cost`            | Total cost in USD. This follows the same format as the Responses API `usage` fields.              |

## Gateway controls during the Beta

Classify uses the same gateway pipeline as other router endpoints, but some controls do not support Classify yet. The table shows what currently applies.

| Control                                                                                 | Applied                              |
| --------------------------------------------------------------------------------------- | ------------------------------------ |
| API key authentication and the `classify` permission                                    | Yes                                  |
| Project and workspace model restrictions                                                | Yes                                  |
| Budgets and rate limits                                                                 | Yes                                  |
| Tracing, usage and cost recording                                                       | Yes                                  |
| Trace masking configured through workspace plugins                                      | Yes                                  |
| PII plugins on `state` and `instructions`                                               | No                                   |
| Guardrails and evaluators                                                               | No                                   |
| Routing rules (model rewrite, fallbacks, retry, cache)                                  | No                                   |
| Request-level `fallbacks`, `load_balancer`, `timeout`, `cache`, `plugins`, `guardrails` | Not accepted; the fields are ignored |

During the Beta, Classify does not apply plugins or guardrails, even when a routing or guardrail rule matches the request. The changelog will announce support before these controls are enabled.

## Pricing and limits

* Input tokens are billed at \$0.042 per 1M tokens. Output tokens are free.
* The context window is 64k tokens. The combined size of `state` and the longest question must fit within 32k tokens.

## Errors

| Status        | Cause                                                                                                                                                            |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`         | Malformed JSON body.                                                                                                                                             |
| `401` / `403` | The API key is missing, invalid, or does not have the `classify` permission.                                                                                     |
| `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` / `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.   |
