> The full index of these docs is at [llms.txt](https://u22a8.ai/llms.txt).

# API overview

What every request to A8 shares, from the base URL and authentication to the request parts and the response fields.

Every interface to A8 shares one kind of key and one set of request parts and response fields. Each interface carries them in its own fields, and its page shows where.

## 1 Base URL and routes

| Interface                                                            | Base URL                       | Route                                    |
| -------------------------------------------------------------------- | ------------------------------ | ---------------------------------------- |
| [Chat Completions](https://u22a8.ai/docs/a8-chat-completions.md)     | `https://api.u22a8.ai/eval/v1` | `POST /chat/completions`                 |
| [Responses](https://u22a8.ai/docs/a8-responses.md)                   | `https://api.u22a8.ai/eval/v1` | `POST /responses`                        |
| [Anthropic Messages](https://u22a8.ai/docs/a8-anthropic-messages.md) | `https://api.u22a8.ai/eval`    | `POST /v1/messages`                      |
| [MCP](https://u22a8.ai/docs/a8-mcp.md)                               | `https://api.u22a8.ai/mcp`     | The `judge`, `teach` and `retract` tools |
| Models list                                                          | `https://api.u22a8.ai/eval/v1` | `GET /models`                            |

*Table 1.* The four interfaces and the models list. Routes are relative to the base URL. An Anthropic SDK takes the shorter base URL and adds `/v1/messages` itself.

The same request returns the same verdict through any interface.

## 2 Authentication

Every request carries an API key, as `Authorization: Bearer <key>` or as `x-api-key: <key>`. The key's permissions set what the request can do: `models:infer` to judge, `models:explain` for precedents and recall, and `models:train` to teach and retract. See [API keys](https://u22a8.ai/docs/api-keys.md).

## 3 Request

A request has six parts:

| Part             | Role                                                                                                                                                                                                                                  |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model`          | Required. `a8` for your organization’s latest version, a pinned version, or `a8-1-1@reference` for the reference model. Its tag carries the minimum accuracy. See [Model identifiers](https://u22a8.ai/docs/a8-model-identifiers.md). |
| Criterion        | What is being judged, in plain words. Required, unless every property of the verdict shape carries its own. Several system or developer messages are joined into one.                                                                 |
| Subject          | The thing under judgment, in the user messages. Text: a string, or a list of text parts. A user message after A8’s response is a follow-up.                                                                                           |
| Verdict shape    | Optional. A JSON Schema object. The default verdict is one label, or one number from `0` to `100`.                                                                                                                                    |
| Minimum accuracy | Optional, 90 by default. Set by the model’s tag, as `a8:95`, in every interface. See [Accuracy and abstention](https://u22a8.ai/docs/a8-abstention.md).                                                                               |
| Reasoning        | Optional, on by default. On reads the request however it is laid out; off reads the layout as written. Set by each interface’s own reasoning field.                                                                                   |

*Table 2.* The parts of a request.

Each interface carries the parts in the fields its own clients already send:

| Interface          | Criterion       | Subject       | Verdict shape          | Minimum accuracy | Reasoning off                    |
| ------------------ | --------------- | ------------- | ---------------------- | ---------------- | -------------------------------- |
| Chat Completions   | System messages | User messages | `response_format`      | `model`’s tag    | `reasoning_effort: "none"`       |
| Responses          | `instructions`  | `input`       | `text.format`          | `model`’s tag    | `reasoning: {"effort": "none"}`  |
| Anthropic Messages | `system`        | User messages | `output_config.format` | `model`’s tag    | `thinking: {"type": "disabled"}` |
| MCP                | `input`         | `input`       | `format`               | `model`’s tag    | Always on                        |

*Table 3.* Where each part travels. Each interface’s page lists every field A8 reads.

With reasoning on, A8 also accepts a prompt that holds the criterion and the subject in one message, as many eval frameworks write their graders. A8 finds each part and answers in the format the prompt asks for. With reasoning off, A8 reads the layout as written, as each interface’s page lays out. See [Chat Completions](https://u22a8.ai/docs/a8-chat-completions.md).

### 3.1 Verdict shape

Each property of the schema is one field of the verdict. Its type sets the kind of judgment the field holds:

| Property                | Verdict                                                                                                            |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `boolean`, or an `enum` | A label: one of the declared values                                                                                |
| `number` or `integer`   | A score between `minimum` and `maximum`, which default to `0` and `100`                                            |
| `precedents`            | Reserved. Filled with the precedents behind the verdict; see [Precedents](https://u22a8.ai/docs/a8-precedents.md). |

*Table 4.* How a schema property is read. A `$ref` into `$defs` and a nullable `anyOf` are followed, so a schema generated from a Pydantic or Zod type reads the same as one written by hand.

A property with a `description` carries its own criterion and is judged on its own, so one request can carry several criteria. The request's criterion applies to the other properties. In this schema, `urgent` and `tone` are judged against their descriptions, and `queue` against the system message:

```json
{
  "type": "object",
  "properties": {
    "queue": {"enum": ["billing", "shipping", "account"]},
    "urgent": {
      "type": "boolean",
      "description": "Does the customer need an answer today?"
    },
    "tone": {
      "type": "integer",
      "minimum": 1,
      "maximum": 5,
      "description": "How polite is the message?"
    }
  }
}
```

A forced tool call is read the same way. When a request requires one function, the function's parameters are the verdict shape, and the verdict comes back as that tool call.

### 3.2 Subject context

Some judgments relate the subject to something else: a source it must be grounded in, a question it must answer, a reference it must match. For those, the subject is a JSON object with `output`, plus any of `reference`, `context` and `question`. A user message that holds a JSON object with `output` is read as a subject object, and any other text, JSON included, is judged as text:

| Key         | Holds                                                                  |
| ----------- | ---------------------------------------------------------------------- |
| `output`    | The thing under judgment. Required.                                    |
| `reference` | A string, or a list of strings: what the output should match           |
| `context`   | A string, or a list of strings: the material the output should rest on |
| `question`  | One string: what the output should answer                              |

*Table 5.* The keys of a subject object. Any other key returns `400` with the code `unknown_subject_key`.

The object travels inside the user message as a JSON string:

```python
import json

subject = {
    "output": "Your refund went out on Monday.",
    "context": ["Refund issued on Monday to the original card."],
}
messages = [
    {"role": "system", "content": "Is the output supported by the context?"},
    {"role": "user", "content": json.dumps(subject)},
]
```

A criterion taught against a standard is judged against that standard. A request that leaves it out abstains and names it in `abstention.missing`.

### 3.3 Continue a conversation

A request is a conversation, and A8 answers its latest user message. To follow up on a verdict, send the conversation again with A8’s response as an assistant message and a new user message after it. The follow-up can bring a new subject to judge against the same criterion, change the subject or add a standard such as a reference:

```python
messages = [
    {"role": "system", "content": "Which queue does the ticket belong in?"},
    {"role": "user", "content": "It arrived crushed. I want a refund."},
    {"role": "assistant", "content": '{"queue": "shipping"}'},
    {
        "role": "user",
        "content": "The customer adds: the box was fine. I was charged twice.",
    },
]
```

```json
{"queue": "billing"}
```

Recorded from `a8-1-1@20260715t140322z` on 2026-10-22.

- **A8 answers the conversation as sent.** Assistant messages are read as earlier turns, whatever they hold, and the response depends on the messages in the request alone.
- **The whole conversation is the request.** The same conversation against the same version gets the same verdict, and a teach of it records the whole conversation as its `input`.
- **With reasoning off, A8 judges the latest user message alone.** Earlier turns are history, so the conversation gets the verdict its latest user message gets on its own.
- **On Responses, `previous_response_id` continues from a stored response.** Its input and output come before the new `input`, and the verdict is the one the whole conversation gets. See [Responses](https://u22a8.ai/docs/a8-responses.md#continue-from-a-stored-response).

## 4 Response

Each interface answers with its own object and puts the verdict where that API puts a model's output. These fields are on every response, under the same names:

| Field                   | Meaning                                                                                                                                                                                                                  |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `model`                 | The exact version that answered, with the minimum accuracy in its tag. Send it back to get the same verdict. See [Horizons](https://u22a8.ai/docs/a8-horizons.md).                                                       |
| `usage`                 | The tokens A8 read, and the reasoning tokens reading the request took as the output count, since the verdict is scored. A repeat reports its reading as cached input tokens.                                             |
| `system_fingerprint`    | A token that moves when anything behind the verdict moves. See [Reproducibility](https://u22a8.ai/docs/a8-reproducibility.md).                                                                                           |
| `min_accuracy`          | On every verdict: the level the verdict is promised at, which is the minimum accuracy asked for or the nearest earned level above it                                                                                     |
| `abstention`            | On an abstention: `kind`, `asked`, `answerable_at`, `earned` and `missing`. Keyed by property under `fields` when properties declined separately. See [Accuracy and abstention](https://u22a8.ai/docs/a8-abstention.md). |
| `interval`, `intervals` | On a score answered at a minimum accuracy above `0`: the `[low, high]` range the level guarantees, in the schema's units                                                                                                 |
| `precedents`            | When the request asks for them: the taught examples behind the verdict. See [Precedents](https://u22a8.ai/docs/a8-precedents.md).                                                                                        |

*Table 6.* The response fields. `min_accuracy`, `abstention`, `interval`, `intervals` and `precedents` are extensions, which standard clients carry through untouched.

A Chat Completions response, with the fields in place:

```json
{
  "id": "eval-9f2c1d84ab30e5f7",
  "object": "chat.completion",
  "model": "a8-1-1:95@20260715t140322z",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "{\"queue\": \"shipping\"}",
        "refusal": null
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 812,
    "completion_tokens": 0,
    "total_tokens": 812,
    "prompt_tokens_details": {"cached_tokens": 0},
    "completion_tokens_details": {"reasoning_tokens": 0}
  },
  "system_fingerprint": "fit-1a2b3c4d5e6f7890.9e2a1b3c",
  "min_accuracy": 95
}
```

Recorded from `a8-1-1@20260715t140322z` on 2026-10-22.

An abstention returns `200` and sits where each API puts an answer a model declined to give, so a typed client reads it like any other response. See [Accuracy and abstention](https://u22a8.ai/docs/a8-abstention.md).

With reasoning on, A8 filters some requests whose text is harmful, such as hate speech, violence or self-harm. A filtered request returns `200` in the slot each API keeps for content its filters stop, apart from verdicts and abstentions, and is free. Each interface’s page shows its form.

## 5 Versioning and compatibility

The request and response formats follow the APIs A8 implements. Within a model version such as `a8-1`, A8’s own fields change by addition only. These changes count as compatible:

- A new optional request parameter.
- A new response field, or a new field inside `abstention` or `precedents`.
- A new value of `abstention.kind`. Read an unfamiliar kind as an abstention.
- A new error `code` under an existing status.
- A new response header.
- A different order of properties in a JSON object.

Every other change ships with a new model version, announced on [Deprecations](https://u22a8.ai/docs/deprecations.md) and in the [Release notes](https://u22a8.ai/docs/release-notes.md). See [Model identifiers](https://u22a8.ai/docs/a8-model-identifiers.md) for how identifiers and horizons hold a verdict fixed.

## 6 Request IDs

Every response carries an `x-request-id` header, and the Anthropic route also sends it as `request-id`, the header the Anthropic SDKs read. An error body carries the same ID. Quote it when you write to <hello@u22a8.ai> about a request.

| SDK                   | Where the ID is       |
| --------------------- | --------------------- |
| OpenAI, Python        | `resp._request_id`    |
| OpenAI, TypeScript    | `resp._request_id`    |
| Anthropic, Python     | `message._request_id` |
| Anthropic, TypeScript | `message._request_id` |

*Table 7.* Where each SDK exposes the request ID.

## 7 SDKs

You call A8 through the standard SDKs or any client that takes a custom base URL, such as LangChain, LlamaIndex, the Vercel AI SDK, Pydantic AI and LiteLLM.

| SDK                | Package     | Written against |
| ------------------ | ----------- | --------------- |
| OpenAI, Python     | `openai`    | 3.36.1          |
| OpenAI, TypeScript | `openai`    | 7.42.0          |
| Anthropic, Python  | `anthropic` | 2.1.0           |

*Table 8.* The SDKs the samples in these docs use, and the version each was written against.

The SDKs keep A8’s extension fields on the response object:

**Python**

```python
resp = client.chat.completions.create(**request)

resp.model                         # the version that answered
resp.model_extra.get("min_accuracy")  # the level the verdict is promised at
resp.model_extra.get("abstention") # set on an abstention
```

**TypeScript**

```typescript
type A8Fields = { min_accuracy?: number; abstention?: { kind: string } };

const resp = await client.chat.completions.create(request);
const { min_accuracy, abstention } = resp as typeof resp & A8Fields;
```

## Next steps

- [Errors](https://u22a8.ai/docs/errors.md): Every status A8 returns for a failed request, with its body, its cause and its fix, and how each SDK raises it.
- [A8 through Chat Completions](https://u22a8.ai/docs/a8-chat-completions.md): Call A8 through Chat Completions, the fields it reads, minimum accuracy and where the verdict, an abstention and precedents land.
- [API keys](https://u22a8.ai/docs/api-keys.md): Create an API key, grant its permissions, authenticate requests with it and revoke it.
