---
title: "HTTP API: POST /v1/systemone and GET /v1/models"
type: reference
tags: [http-api, reference, systemone, models, errors]
created: 2026-09-17
updated: 2026-09-17
confidence: high
sources:
  - raw/docs/api.md
  - raw/site/openapi.json
  - raw/docs/introduction__quickstart.md
  - raw/docs/models.md
jev_version: "jev-1.13.0"
summary: "Complete wire contract for POST https://api.typesafe.ai/v1/systemone and GET /v1/models: auth, request body, question and answer shapes, usage, errors."
---

# HTTP API: POST /v1/systemone and GET /v1/models

> **TL;DR** `POST https://api.typesafe.ai/v1/systemone` with `Authorization: Bearer <API_KEY>`, `Content-Type: application/json`, and a body of `{state, model, questions}`. You get back `{model, answers, usage}` where `answers` is keyed by the question ids you chose. `GET /v1/models` lists the model names your key may send.

## Endpoints

| Method | URL | Purpose | Request schema | 200 schema | 422 schema |
|---|---|---|---|---|---|
| `POST` | `https://api.typesafe.ai/v1/systemone` | Evaluate `state` against a map of typed questions | `SystemOneRequest` | `SystemOneResponse` | `HTTPValidationError` |
| `GET` | `https://api.typesafe.ai/v1/models` | List models and aliases available to the account | — | `ModelMetadataList` | `HTTPValidationError` |

OpenAPI `operationId`s: `systemone_v1_systemone_post`, `models_v1_v1_models_get` (raw/site/openapi.json). See [[reference/openapi-schemas]] for every component schema.

## Authentication

| Header | Value | Required | Notes |
|---|---|---|---|
| `Authorization` | `Bearer <API_KEY>` | Yes | OpenAPI security scheme `HTTPBearer` (`type: http`, `scheme: bearer`) applies to both endpoints. A missing or invalid key returns `401 Unauthorized`. |
| `Content-Type` | `application/json` | Yes for `POST` | The only request content type in the spec. |

Get a key at `https://console.typesafe.ai/settings/keys` (raw/docs/introduction__quickstart.md). The SDKs read it from `TYPESAFE_API_KEY`; see [[reference/environment-variables]].

Headers the official SDKs also send or read (raw/github/typesafe-sdk-python/src/typesafe_sdk/_core/constants.py, raw/github/typesafe-sdk-js/src/client.ts): request `User-Agent: typesafe-sdk/<version>`, `Accept`, `X-TypeSafe-SDK`, `X-TypeSafe-Runtime`, `X-TypeSafe-Retry-Count`; response `x-typesafe-request-id` (surfaced as `request_id` / `requestId` on errors), `retry-after`, `retry-after-ms`. None of these are documented as required in raw/docs/api.md.

## Request body (`SystemOneRequest`)

| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
| `state` | `string \| object \| array` | Yes | — | The content all questions in this request refer to. A plain string for text, or structured data (object/array) for chat logs, records, or application state. |
| `model` | `string` | Yes | — | The model that handles the request. Use `"jev-latest"`. Names come from `GET /v1/models`; versioned IDs such as `jev-1.13.0` are also accepted. |
| `questions` | `map<string, Question>` | Yes | `minProperties: 1` (raw/site/openapi.json) | A map of typed question objects. You choose each key; answers come back under the same keys. The key is not sent to the underlying model and is not used in inference (raw/docs/api.md). |

`state` and the questions share one budget: 64k tokens per request, and 32k tokens for `state` plus the single longest question (raw/docs/models.md). See [[reference/models-and-pricing]].

### Minimal example request (verbatim, raw/docs/api.md)

```json
{
  "state": "Help! My payouts have been failing for 3 days.",
  "model": "jev-latest",
  "questions": {
    "is_urgent": {
      "type": "noul",
      "instructions": "Does this convey urgency?"
    }
  }
}
```

## Question types

A `Question` is one of three types, selected by its `type` field. All three share `type` and `instructions`; each adds its own `criteria`. The OpenAPI discriminator is `type` with mapping `noul → NoulQuestion`, `choice → ChoiceQuestion`, `score → ScoreQuestion`.

### Common fields

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | `"noul" \| "choice" \| "score"` | Yes (all three types) | Selects the question type; the answer carries the same `type`. |
| `instructions` | `string \| object \| array \| null` | Marked **required** in raw/docs/api.md for all three types; **not** listed in `required` in raw/site/openapi.json (nullable there). See [Source disagreements](#source-disagreements). | What the model should decide, rate, or answer yes/no about. |

### Noul (`type: "noul"`)

A yes/no question. Returns the probability the answer is yes.

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | `"noul"` | Yes | Identifies a yes/no question or statement. |
| `instructions` | `string \| object \| array` | Yes (api.md) | The yes/no question or statement to evaluate. |
| `criteria` | `NoulCriteria \| null` | No | Optional descriptions of what a yes and a no mean. |
| `criteria.true` | `string \| object \| array \| null` | No | What a yes (value near 1) means. |
| `criteria.false` | `string \| object \| array \| null` | No | What a no (value near 0) means. |

```json
{
  "state": "Help! My payouts have been failing for 3 days.",
  "model": "jev-latest",
  "questions": {
    "is_urgent": {
      "type": "noul",
      "instructions": "Does this convey urgency?",
      "criteria": {
        "true": "Explicitly time-sensitive",
        "false": "No urgency expressed"
      }
    }
  }
}
```

### Choice (`type: "choice"`)

Picks one option from a set you define. Returns the chosen option and the full probability distribution.

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | `"choice"` | Yes | Identifies a question that selects one of the choices in `criteria`. |
| `instructions` | `string \| object \| array` | Yes (api.md) | What the model should decide. |
| `criteria` | `map<string, string \| object \| array \| null>` | Yes | Option name → rubric description. Use `null` when an option needs no extra detail; a choice without a description is interpreted by its name alone. |

```json
{
  "state": "Help! My payouts have been failing for 3 days.",
  "model": "jev-latest",
  "questions": {
    "department": {
      "type": "choice",
      "instructions": "Which team should handle this?",
      "criteria": {
        "billing": "Payments, invoicing, refunds",
        "technical": "Bugs, outages, integrations",
        "sales": "Pricing, upgrades, new accounts"
      }
    }
  }
}
```

### Score (`type: "score"`)

Rates the state along a rubric you define. Returns a probability-weighted value across your levels.

| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
| `type` | `"score"` | Yes | — | Identifies a question that rates the content using the levels in `criteria`. |
| `instructions` | `string \| object \| array` | Yes (api.md) | — | What the model should rate. |
| `criteria` | `array<string \| object \| array>` | Yes | `minItems: 1` (raw/site/openapi.json); "at least two levels" (raw/docs/api.md) | Ordered descriptions of the score levels. Each description's position determines its score, starting at zero. |

```json
{
  "state": "Help! My payouts have been failing for 3 days.",
  "model": "jev-latest",
  "questions": {
    "frustration": {
      "type": "score",
      "instructions": "How frustrated is the customer?",
      "criteria": ["Calm", "Frustrated", "Very angry"]
    }
  }
}
```

## Response body (`SystemOneResponse`)

| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
| `model` | `string` | Yes | — | The model that performed the evaluation. May differ from the alias supplied in the request — log it to know which version answered. |
| `answers` | `map<string, Answer>` | Yes | `minProperties: 1` | One answer per question, keyed by the same ids you used in `questions`. Each answer's `type` matches its question's `type`. |
| `usage` | `Usage` | Yes | — | Token usage for this evaluation. |
| `usage.input_tokens` | `integer` | Yes | — | Billable input tokens used to evaluate the request. |
| `usage.output_tokens` | `integer` | Yes | — | Output tokens used to answer. Output tokens are currently free of charge (raw/site/openapi.json, raw/docs/models.md). |

### Answer types

Every answer carries a `type` matching its question. Choice and Score answers also carry a `confidence` between 0 and 1, derived from the answer's probability distribution (raw/docs/api.md). Discriminator mapping: `noul → NoulAnswer`, `choice → ChoiceAnswer`, `score → ScoreAnswer`.

#### Noul answer

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | `"noul"` | Yes | Identifies a yes/no answer. |
| `noul` | `number` | Yes | The yes/no answer on a scale from 0 (no) to 1 (yes). Near 0.5 indicates uncertainty. |

A Noul answer carries **no** `confidence` field.

#### Choice answer

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | `"choice"` | Yes | Identifies a selection from the requested choices. |
| `choice` | `string` | Yes | The name of the highest-probability option among the question's `criteria`. |
| `probabilities` | `map<string, number>` | Yes | Every option mapped to its probability, 0 to 1; values sum to approximately 1. |
| `confidence` | `number` | Yes | How certain the model is, from 0 to 1, derived from `probabilities`. |

#### Score answer

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | `"score"` | Yes | Identifies a rating against the requested score levels. |
| `score` | `number` | Yes | Probability-weighted average of the rubric levels; may fall between integer levels. |
| `legend` | `map<string, string \| object \| array>` | Yes | Each level number (string key) mapped back to its criteria description. |
| `probabilities` | `map<string, number>` | Yes | Each level (string key, same keys as `legend`) mapped to its probability; values sum to approximately 1. |
| `confidence` | `number` | Yes | How certain the model is, from 0 to 1, derived from `probabilities`. |

### Verbatim example response (raw/docs/api.md)

```json
{
  "model": "jev-latest",
  "answers": {
    "is_urgent": {
      "type": "noul",
      "noul": 0.92
    }
  },
  "usage": { "input_tokens": 312, "output_tokens": 48 }
}
```

Choice answer (raw/docs/api.md):

```json
{
  "model": "jev-latest",
  "answers": {
    "department": {
      "type": "choice",
      "choice": "technical",
      "probabilities": { "billing": 0.08, "technical": 0.85, "sales": 0.07 },
      "confidence": 0.82
    }
  },
  "usage": { "input_tokens": 312, "output_tokens": 48 }
}
```

Score answer (raw/docs/api.md):

```json
{
  "model": "jev-latest",
  "answers": {
    "frustration": {
      "type": "score",
      "score": 1.6,
      "legend": { "0": "Calm", "1": "Frustrated", "2": "Very angry" },
      "probabilities": { "0": 0.05, "1": 0.3, "2": 0.65 },
      "confidence": 0.78
    }
  },
  "usage": { "input_tokens": 312, "output_tokens": 48 }
}
```

## cURL example (verbatim, raw/docs/introduction__quickstart.md)

```bash
curl -X POST https://api.typesafe.ai/v1/systemone \
  -H "Authorization: Bearer $TYPESAFE_API_KEY" \
  -H "Content-Type: application/json" \
  -d @- <<'EOF'
  {
    "state": "Hi, I've been trying to connect my Stripe account for 3 days and it keeps failing. I'm losing sales. Please help ASAP.",
    "model": "jev-latest",
    "questions": {
      "urgency": {
        "type": "noul",
        "instructions": "Does this message express urgency?"
      }
    }
  }
EOF
```

### Mixed-type request and response (verbatim, raw/docs/introduction__quickstart.md)

```json
{
  "state": "Hi, I've been trying to connect my Stripe account for 3 days and it keeps failing. I'm losing sales. Please help ASAP.",
  "model": "jev-latest",
  "questions": {
    "department": {
      "type": "choice",
      "instructions": "Which team should handle this",
      "criteria": {
        "billing": "Payment or subscription issues",
        "technical": "Bugs or integration problems",
        "sales": "Pricing or account questions"
      }
    },
    "frustration": {
      "type": "score",
      "instructions": "How frustrated the customer appears",
      "criteria": [
        "Calm, just stating facts",
        "Frustrated but civil",
        "Very angry, strong language"
      ]
    },
    "is_urgent": {
      "type": "noul",
      "instructions": "The message conveys urgency or time-sensitivity"
    }
  }
}
```

```json
{
  "model": "jev-latest",
  "answers": {
    "department": {
      "type": "choice",
      "choice": "billing",
      "probabilities": {
        "billing": 0.84,
        "technical": 0.159,
        "sales": 0.001
      },
      "confidence": 0.596
    },
    "frustration": {
      "type": "score",
      "score": 1.035,
      "legend": {
        "0": "Calm, just stating facts",
        "1": "Frustrated but civil",
        "2": "Very angry, strong language"
      },
      "confidence": 0.842
    },
    "is_urgent": {
      "type": "noul",
      "noul": 0.999
    }
  },
  "usage": {
    "input_tokens": 312,
    "output_tokens": 48
  }
}
```

Note: this quickstart response omits `probabilities` on the Score answer, although both raw/docs/api.md and raw/site/openapi.json mark it required. Treat the quickstart sample as abridged (inferred).

## GET /v1/models

Returns the names your account can send in the `model` field, with a description and release date for each. It currently lists the aliases. Versioned IDs such as `jev-1.13.0` are accepted by the `model` field whether or not they appear in the list (raw/docs/models.md).

```bash
curl https://api.typesafe.ai/v1/models \
  -H "Authorization: Bearer $TYPESAFE_API_KEY"
```

Response (`ModelMetadataList`):

| Field | Type | Required | Description |
|---|---|---|---|
| `models` | `array<ModelMetadata>` | Yes | One entry per model or alias. |
| `models[].name` | `string` | Yes | The model ID or alias, as accepted by the `model` field. |
| `models[].description` | `string` | Yes | What the model is for. |
| `models[].release_date` | `string` | Yes | Release date, formatted `YYYY-MM-DD`. |

Example from the OpenAPI `examples` (raw/site/openapi.json):

```json
{
  "models": [
    {
      "name": "jev-latest",
      "description": "General-purpose system one model.",
      "release_date": "2026-09-15"
    }
  ]
}
```

## Errors

Errors use standard HTTP status codes with a JSON body describing what went wrong (raw/docs/api.md).

| Status | Meaning |
|---|---|
| `400 Bad Request` | The request was invalid. Not listed in raw/docs/api.md; both SDKs map it (`BadRequestError` / `TypeSafeBadRequestError`). Not retryable. |
| `401 Unauthorized` | Missing or invalid API key. Check the `Authorization` header. |
| `403 Forbidden` | Access was denied. Not listed in raw/docs/api.md; both SDKs map it (`PermissionDeniedError` / `TypeSafePermissionDeniedError`). Not retryable. |
| `404 Not Found` | The resource was not found. Not listed in raw/docs/api.md; both SDKs map it (`NotFoundError` / `TypeSafeNotFoundError`). Check the path and `TYPESAFE_BASE_URL`. |
| `408 Request Timeout` | Not described in the docs; present only in the SDK retry defaults (`http_statuses` / `httpStatuses` include `408`). Retryable. |
| `422 Unprocessable Entity` | The request body failed validation — for example a missing required field or a malformed question. The body details the offending field (`HTTPValidationError`). |
| `429 Too Many Requests` | You have exceeded your rate limit. Back off and retry after a short delay. |
| `5xx` (`500`–`599`) | The server failed to process the request. Not listed as a range in raw/docs/api.md; both SDKs map the whole range to `InternalServerError` / `TypeSafeInternalServerError`. Retryable. |
| `529 Overloaded` | TypeSafe is temporarily overloaded. Retry after a short delay. (Handled by the `5xx` branch in both SDKs; neither has a dedicated class.) |

This list is **not exhaustive**; the SDK-level mapping incl. 400/403/404/408/5xx is in [[reference/rate-limits-and-errors]]. The 422 body is `{"detail": [ValidationError, ...]}`; each entry has `loc`, `msg`, `type`, and optionally `input` and `ctx`. Full shape in [[reference/openapi-schemas]]. For the complete status → SDK exception → retry mapping see [[reference/rate-limits-and-errors]].

### Handling rate limits

Verbatim guidance (raw/docs/api.md): "When you receive a `429 Too Many Requests` or `529 Overloaded` response, retry the request with exponential backoff instead of retrying immediately. Our client SDKs handle this automatically, so no extra handling is needed if you use one of our SDKs with its default retry policy."

Additional facts for direct HTTP callers:

- Limits are 250,000 tokens per second and 1,200 requests per minute; exceeding **either** returns `429` (raw/docs/models.md).
- The SDKs "honor the `retry-after` header when the response carries one" (raw/docs/models.md). The SDK implementations also read `retry-after-ms`, preferring it over `retry-after` (raw/github/typesafe-sdk-js/src/retry.ts, raw/github/typesafe-sdk-python/src/typesafe_sdk/_core/errors.py). If you implement your own client, read both.
- Reference backoff, matching the SDK defaults: initial 500 ms, doubling, capped at 5,000 ms, 25% jitter, 2 retries after the initial attempt; cap any honored server delay at 60,000 ms before falling back to backoff (raw/github/typesafe-sdk-js/src/retry.ts).
- Rate limits are stated to be "adjusting dynamically" and can change without notice; higher limits are on custom and enterprise plans via sales@typesafe.ai (raw/docs/models.md).

## Source disagreements

| Point | raw/docs/api.md | raw/site/openapi.json | Guidance |
|---|---|---|---|
| `instructions` requiredness | Marked `required` on Noul, Choice, and Score questions | Not in any question's `required` list; typed `anyOf [string, object, array, null]` | Always send `instructions`; the server schema tolerates its absence but the docs treat it as mandatory. |
| Score `criteria` minimum | "You must include at least two levels" | `minItems: 1` | Send at least two levels. |
| Score answer `probabilities` | Required | Required | The quickstart example (raw/docs/introduction__quickstart.md) omits it; the two contract sources agree it is present. |

## Version notes

- OpenAPI document version `0.2.0`, OpenAPI spec version `3.1.0` (raw/site/openapi.json).
- This is the v1 API. The preview endpoint `POST /preview/evaluation` is replaced by `POST /v1/systemone`; field names changed. See [[reference/migrating-to-v1]].
- `document` is no longer accepted; a request with `document` fails validation (raw/docs/migrating-to-v1.md).

## Related

- [[reference/openapi-schemas]] — every component schema, field by field
- [[reference/models-and-pricing]] — model IDs, aliases, price, limits, context
- [[reference/rate-limits-and-errors]] — status → exception → retry table
- [[reference/migrating-to-v1]] — what changed from `/preview/evaluation`
- [[reference/environment-variables]] — `TYPESAFE_API_KEY` and friends
- [[concepts/primitives]] — what Choice, Score, and Noul mean
- [[reference/python-sdk]] — the typed Python client over this wire format
- [[reference/javascript-sdk]] — the typed JS/TS client over this wire format
- [[guides/quickstart]] — first call end to end

## Sources

- raw/docs/api.md (https://docs.typesafe.ai/api)
- raw/site/openapi.json (https://docs.typesafe.ai/openapi.json)
- raw/docs/introduction__quickstart.md (https://docs.typesafe.ai/introduction/quickstart)
- raw/docs/models.md (https://docs.typesafe.ai/models)
- raw/docs/migrating-to-v1.md (https://docs.typesafe.ai/migrating-to-v1)
- raw/github/typesafe-sdk-js/src/retry.ts, raw/github/typesafe-sdk-python/src/typesafe_sdk/_core/constants.py
