# Jev: TypeSafe System One

> Run TypeSafe's Jev through INFRO with POST /v1/systemone: TypeSafe's body and model names, your TypeSafe account, spend limits and a cost per decision.

Source: https://infro.io/docs/api/systemone

> **Note:** INFRO is in private early access. Running these examples requires an early-access account, an active trial or subscription entitlement, and your own provider connection. Self-serve purchasing is not generally open. See [Early access](https://infro.io/early-access).

Jev, TypeSafe's System One model, answers typed questions about a `state` — yes or no (`noul`), one of several options (`choice`), a level on a scale (`score`) — with probabilities, and writes no text. INFRO serves TypeSafe's endpoint at the same path, with the same request body and model names, so code written for TypeSafe points at INFRO by changing its base URL and its key.

> **Note:** Every request runs on **your own TypeSafe connection**, under your TypeSafe key, and TypeSafe bills your account for it. INFRO estimates what TypeSafe will charge and takes 0% markup on model spend; what INFRO charges is your subscription. The same endpoint serves every other [decision model](https://infro.io/docs/features/decision-models) you connect — OpenAI's GPT-6 Luna, Cloudflare's Clef and the rest — on your own account with its provider.

## Jev through INFRO

1. Connect your TypeSafe key under **Providers** (TypeSafe issues it at console.typesafe.ai/keys). It is tested before it is saved, stored encrypted, and sent only to TypeSafe.
2. Create an INFRO gateway key under **Keys**. To keep it to Jev, limit it to TypeSafe in the key's providers.
3. Send requests to `https://api.infro.io/v1/systemone` with the gateway key as the bearer token. With TypeSafe's SDK, point it at INFRO's API host in place of TypeSafe's (`api.infro.io` for `api.typesafe.ai`) and give it the gateway key where it takes the TypeSafe key.
4. Name the model: a pinned version (`jev-1.13.0`, as TypeSafe advises), a moving name (`jev-latest`, `jev-preview`) or the catalog id `typesafe/jev-1.13`. Each response's `model` is the version that answered.
5. Read `usage.cost` on each response: the estimated TypeSafe charge for that decision.
6. Set a spend limit on the organization, a project, the key or a member under **Budgets**. It is checked before TypeSafe is called.
7. Find each decision in the request log by the response's `id`. Send `metadata` (for example `{"workflow": "support-triage"}`) to group decisions by workflow in usage, beside the LLM calls the same workflow makes.

```bash
curl https://api.infro.io/v1/systemone \
  -H "Authorization: Bearer $INFRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "jev-1.13.0",
    "state": "My order arrived broken and I want my money back.",
    "questions": {
      "intent": {
        "type": "choice",
        "instructions": "The primary intent of this customer message",
        "criteria": { "order_status": null, "return_refund": null, "complaint": null }
      },
      "urgent": { "type": "noul", "instructions": "Does this need a reply today?" }
    },
    "metadata": { "workflow": "support-triage" }
  }'
```

## Model names

| You send | Sent to TypeSafe as | Estimated as |
| --- | --- | --- |
| `jev-1.13.0` or `typesafe/jev-1.13` | `jev-1.13.0` | Jev 1.13 |
| `jev-latest` or `jev-preview`, with or without `typesafe/` | As typed | The version TypeSafe reports having served |
| A route's slug | The route's target | The target's model |

A moving name follows TypeSafe's releases: `jev-latest` is TypeSafe's latest stable release and `jev-preview` its latest release, both `jev-1.13.0` when TypeSafe's models page was read on 6 October 2026. The version that answers can therefore change without a deploy on your side. The response's `model` and the request log name it, and the estimate is priced as that version. A version INFRO's catalog does not carry yet is recorded as unpriced — `cost: null`, never $0 — until it is added.

## Request body

- `model` (string, required) — A Jev version, a moving name, the catalog id or a route. See Model names.
- `state` (string | object | array, required) — What the questions are about. TypeSafe reads it once for every question.
- `questions` (object, required) — Your question ids mapped to questions; the answers come back under the same ids, which are not sent to the model. INFRO accepts 1 to 1,000 per request.

| type | Fields | Answer |
| --- | --- | --- |
| `noul` | `instructions`; optional `criteria.true` and `criteria.false` | `noul`: the probability of yes |
| `choice` | `instructions`; `criteria`: up to 255 options | `choice`, `probabilities`, `confidence` |
| `score` | `instructions`; `criteria`: 2 to 10 levels | `score`, `legend`, `probabilities`, `confidence` |

INFRO checks the envelope — a `state`, between 1 and 1,000 questions, each with a known `type` — and leaves the rest of each question to TypeSafe, which validates it. TypeSafe's own limits apply: 64k tokens per request, with the state and the longest question within 32k, and rate limits TypeSafe adjusts as demand changes (docs.typesafe.ai/models, read 6 October 2026).

## INFRO extensions

- `metadata` (object) — Recorded on the request and not sent to TypeSafe. Group usage and the request log by it.
- `logging` (boolean) — `false` keeps this request's state and answers out of the request log, even when your organization turned prompt logging on.
- `fallbacks` (string[]) — Other Jev models or routes to try when every TypeSafe connection for `model` fails.

`X-INFRO-User-Id` names your end customer for [end-customer caps](https://infro.io/docs/org/end-customer-caps), and the [cost attribution](https://infro.io/docs/features/attribution) headers work as on every endpoint.

## Response

```json
{
  "id": "req_…",
  "model": "jev-1.13.0",
  "answers": {
    "intent": {
      "type": "choice",
      "choice": "return_refund",
      "probabilities": { "order_status": 0.03, "return_refund": 0.94, "complaint": 0.03 },
      "confidence": 0.91
    },
    "urgent": { "type": "noul", "noul": 0.72 }
  },
  "usage": { "input_tokens": 1000, "output_tokens": 0, "cost": 0.000042, "price_source": "list" },
  "route": "primary",
  "route_name": null,
  "provider": "typesafe",
  "connection": "conn_…"
}
```

- `id` (string) — The request id, also in `X-INFRO-Request-Id`. Look the decision up with `GET /v1/requests/{id}`.
- `model` (string) — The versioned model TypeSafe reports having served.
- `answers` (object) — TypeSafe's answers, unchanged, under your question ids.
- `usage.input_tokens` (integer) — As TypeSafe reports them.
- `usage.output_tokens` (integer) — As TypeSafe reports them. Output is free on Jev.
- `usage.cost` (number | null) — The estimated TypeSafe charge in US dollars, at TypeSafe's list rate or the rate you entered for your organization. `null` when it cannot be priced: a version the catalog does not carry, or no token counts reported.
- `usage.price_source` (string) — `list`, `custom` or `custom_provider`. Absent when `cost` is `null`.
- `route, route_name` (string | null) — `primary` when the first connection tried served it, `standby_a` and on when a later one did; `route_name` is the route's slug when `model` named one.
- `provider, connection` (string) — `typesafe` and the id of the TypeSafe connection that served it.

## Before TypeSafe is called

- **The key**: its project, environment, IP allowlist, model allowlist and providers. A key limited to other providers gets `503 connection_out_of_scope`.
- **Spend limits**: the request's size in bytes — never fewer than its tokens — is reserved at the Jev rate against every blocking limit that applies: organization, project, key, member and end customer. Over one, the request is refused with a 402, and TypeSafe is not called.
- **The key's rate limit**, and your organization's guardrail setting for endpoints guardrails cannot read (below).

## Several TypeSafe keys

With several TypeSafe keys connected, requests are spread across them. A key TypeSafe refuses (401, 403), a model the key cannot name (404) or a key over its rate limit (429) passes the request to the next key, then to `fallbacks`. A request TypeSafe rejects as invalid (400 or 422) is not retried: every TypeSafe key validates it the same way. Only connections whose provider answers decisions serve this endpoint: TypeSafe, and the providers of the other [decision models](https://infro.io/docs/features/decision-models) — an OpenRouter connection on OpenRouter's own System One endpoint, never on its chat completions.

## Errors

| Status | Code | When |
| --- | --- | --- |
| 400 | `invalid_request` | The envelope is wrong, or TypeSafe rejected the request: the message carries TypeSafe's words and the field it named (`param: …`). |
| 402 | `budget_exceeded` | A blocking spend limit. TypeSafe was not called. |
| 403 | `model_not_allowed`, `guardrail_blocked` | The key's allowlist excludes the model, or your guardrails refuse endpoints they cannot read. |
| 404 | `model_not_found` | A `vendor/model` id no provider names. |
| 429 | `rate_limit_exceeded`, `provider_rate_limited` | The key's rate limit, or every TypeSafe key is over TypeSafe's (with TypeSafe's `Retry-After`). |
| 502 | `upstream_error` | Every TypeSafe connection failed. |
| 503 | `no_provider_connected`, `connection_out_of_scope` | No connection serves the model (for Jev, TypeSafe or OpenRouter), or the key may not use the provider that does. |

The full list is on [Errors](https://infro.io/docs/api/errors).

## Compatibility notes

- The key changes: send an INFRO gateway key, not the TypeSafe key, which stays in your INFRO connection.
- Responses add `id`, `usage.cost`, `usage.price_source`, `route`, `route_name`, `provider` and `connection` to TypeSafe's fields.
- Errors use INFRO's error envelope (`error.message`, `error.type`, `error.code`) rather than TypeSafe's validation body; the field TypeSafe named is in the message.
- Guardrails inspect chat completions only and do not read these requests. A guardrail policy set to block endpoints it cannot inspect refuses them with `403 guardrail_blocked`.
- The state and the answers are stored only when your organization turned on prompt logging and the request did not send `logging: false`.
- The console Playground does not run System One requests yet; decision policies have their own Try it panel.

Use [decisions](https://infro.io/docs/api/decisions) when the question, its thresholds and its fallback should live in a policy rather than in your code.
