> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nativeport.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nativeport.ai/_mcp/server.

# TypeSafe

> Send state and a map of typed questions — yes/no, pick-one, rate-against-levels — and get answers your code can branch on, each with a probability distribution and a confidence. Input tokens only.

Fronts [TypeSafe](https://docs.typesafe.ai)'s Jev model, which answers **typed questions about state you supply** instead of generating prose. You send a `state` and a map of questions; you get back one answer per question, each carrying a probability distribution and — for two of the three types — a confidence. One route is mounted:

```
POST /typesafe/v1/systemone   → https://api.typesafe.ai/v1/systemone
```

That single endpoint is TypeSafe's whole billable surface, so it is mounted as a fixed literal route. Every other `/typesafe/*` path, and any method other than `POST`, draws the gateway's own `404` without reaching the upstream.

**This is not a chat API.** There is no streaming, no tool use, no conversation, and no free-text output. If you want a model to write something, use the [Inference API](/inference) or a provider route such as [OpenAI](/openai) or [Anthropic](/anthropic). Jev is for the decision your code makes *around* that: route this ticket, score this passage, decide whether the answer is in the document.

## Credential handling

TypeSafe reads its key from `Authorization: Bearer` — the same header that carries your gateway token — so the gateway **replaces** that header with its own TypeSafe key. Your gateway token stops at the gateway and never travels upstream. There is no query-parameter form of the key, so nothing is stripped from the query string.

## The request

Three fields, all required:

| Field       | What it accepts                                                                                            |
| ----------- | ---------------------------------------------------------------------------------------------------------- |
| `state`     | The content to evaluate: a string, or a JSON object or array for records, chat logs, or application state. |
| `model`     | `jev-1.13.0`, or the `jev-latest` / `jev-preview` aliases.                                                 |
| `questions` | A map of ids you choose to question objects. The answer comes back under the same id.                      |

The body is forwarded as you sent it. TypeSafe rejects any top-level field it does not recognize with its own `400`, so the gateway adds no second policy of its own.

## Question types

Every question carries a `type` and `instructions`; each type adds its own `criteria`.

#### Noul (yes/no)

Returns the probability the answer is yes, from `0` to `1`. `criteria` is optional and describes what each end means.

```json
{
  "type": "noul",
  "instructions": "Does this convey urgency?",
  "criteria": { "true": "Explicitly time-sensitive", "false": "No urgency expressed" }
}
```

```json
{ "type": "noul", "noul": 0.95 }
```

#### Choice (pick one)

Picks one option from a set you define and returns the full distribution plus a confidence. `criteria` maps each option to a description, or to `null` when the name is self-explanatory.

```json
{
  "type": "choice",
  "instructions": "Which team should handle this?",
  "criteria": {
    "billing": "Payments, invoicing, refunds",
    "technical": "Bugs, outages, integrations"
  }
}
```

```json
{
  "type": "choice",
  "choice": "billing",
  "probabilities": { "billing": 0.86, "technical": 0.14 },
  "confidence": 0.73
}
```

#### Score (rate)

Rates the state against ordered levels you describe, returning a probability-weighted value, the distribution, and a confidence. `criteria` is an ordered array of at least two levels.

```json
{
  "type": "score",
  "instructions": "How frustrated is the customer?",
  "criteria": ["Calm", "Frustrated", "Very angry"]
}
```

```json
{
  "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
}
```

## Asking several questions at once

The state is read **once** no matter how many questions ride along, and every question is evaluated against it independently. Since you are billed on input tokens, a long state split across separate calls is paid for on every one of them — batching is the single biggest cost lever on this route, and it is also faster.

```bash
curl -X POST "$GATEWAY/typesafe/v1/systemone" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "state": "Help! My payouts have been failing for 3 days.",
    "model": "jev-latest",
    "questions": {
      "is_urgent": { "type": "noul", "instructions": "Does this convey urgency?" },
      "department": {
        "type": "choice",
        "instructions": "Which team should handle this?",
        "criteria": { "billing": "Payments, invoicing, refunds", "technical": "Bugs, outages, integrations" }
      }
    }
  }'
```

TypeSafe's answer comes back exactly as it arrived — status, headers, and body bytes:

```json
{
  "model": "jev-1.13.0",
  "answers": {
    "is_urgent": { "type": "noul", "noul": 0.95 },
    "department": {
      "type": "choice",
      "choice": "billing",
      "probabilities": { "billing": 0.86, "technical": 0.14 },
      "confidence": 0.73
    }
  },
  "usage": { "input_tokens": 343, "output_tokens": 50 }
}
```

**Confidence is a second axis, not a restatement of the probability.** The answer tells you *what*; the confidence tells you whether to act on it alone. Thresholding it is how you decide what to route to a human — see TypeSafe's own [confidence guide](https://docs.typesafe.ai/confidence).

## Models

| Id            | Notes                                                                |
| ------------- | -------------------------------------------------------------------- |
| `jev-1.13.0`  | The current version. Pin it if you have tuned thresholds against it. |
| `jev-latest`  | Alias for the newest stable release.                                 |
| `jev-preview` | Alias for the newest release, stable or not.                         |

The response always names the concrete version that answered, so sending `jev-latest` comes back as `"model": "jev-1.13.0"`. An alias can move to a new model without notice, which is why pinning is worth it once your thresholds matter.

## Limits

* **64,000 tokens** per request for the state plus all questions combined, and **32,000** for the state plus the single longest question.
* **Text only.** A string, a JSON object, or an array of text values — no image, audio, or video input. Convert those to text or structured fields first.
* TypeSafe applies its own per-account rate limits and answers `429` when you exceed them; it publishes these as subject to change.

## Billing

Billed on **input tokens only — output tokens are free** — at **\$0.042 per million input tokens**, TypeSafe's own published rate with no per-request markup. Every response reports what it consumed in `usage.input_tokens`, and that is what the charge is settled from. A typical single-question call runs a few hundred input tokens.

Because output is free and the state is read once, the cost of a call is essentially the size of your state plus your questions — which is why packing many questions into one request is so much cheaper than one call each.

## Errors

Upstream statuses pass through untouched:

| Status | Meaning                                                              |
| ------ | -------------------------------------------------------------------- |
| `400`  | An unknown model, or a body TypeSafe could not accept.               |
| `422`  | The body failed validation — the response names the offending field. |
| `429`  | You exceeded TypeSafe's rate limit. Back off and retry.              |
| `529`  | TypeSafe is temporarily overloaded. Retry after a short delay.       |

A `401` means the gateway's own credential is the problem, not yours — your gateway token is checked before the upstream call and answers `401` separately if it is missing or revoked.

Field-level request and response detail lives in [TypeSafe's API reference](https://docs.typesafe.ai/api); the gateway changes neither.