Switch from Jev · API migration checklist

A practical checklist for switching from Jev

Move an existing TypeSafe Jev integration to the Decision Models Jev-compatible API: review configuration, test requests and switch traffic deliberately.

Jev is a product of TypeSafe AI; Decision Models is not affiliated with TypeSafe AI.

Get API keySee the EU tier

Before switching traffic

  1. Inventory the existing integration

    Save the current configuration and representative test cases. Check question types, options, images, answer parsing and retries. Keep the previous provider configuration available for a deliberate rollback.

  2. Set the base URL and path

    Use https://api.decisionmodels.io as the base URL and POST /v1/systemone for text. Remove an existing /v1 suffix if your client appends the path itself. Check the actual outgoing path.

  3. Create a separate API key

    Create a Decision Models key in the dashboard and send it as Authorization: Bearer. TypeSafe keys, balances and accounts do not transfer. Store the key in an environment variable.

  4. Replace the model ID

    Replace the TypeSafe model ID with an available Decision Models ID, such as s1-fast. Check GET /v1/models rather than carrying over a TypeSafe alias.

  5. Choose the processing region explicitly

    For example, set S1-Region: eu. Values are eu or global, subject to key permissions; without a header the key default applies. EU requests stay in the EU. Peer-to-Peer processing outside the EU requires worldwide-processing consent on the key. Review processing and contract terms.

  6. Split questions to fit model limits

    s1-pro accepts up to 3 text questions per request; s1-fast accepts 1 and s1-vision 1. Split additional questions yourself. The service does not silently split or discard them; excess questions return 400 request_limit_exceeded. Also check the token and option limits below.

  7. Review question types and images

    Keep state and the questions object keyed by question name. Use the criteria shapes below. For images, use s1-vision through POST /v1/multimodal with exactly one image in the images array. PNG, JPEG and WebP data URLs or public HTTPS URLs are supported, bounded to 4 MiB decoded and 2,000,000 pixels. The text examples below do not test image requests.

  8. Read answers and usage accounting

    Read results under answers by question name. Capture usage.input_tokens, usage.output_tokens, usage.decisions and the reported model_version. Token counts come from the model, including image positions for image requests. Use dashboard usage receipts for monetary amounts; do not convert characters into billable tokens.

  9. Test errors, retries and rollback

    Test invalid keys, insufficient credit, model limits, rate limits and timeouts. Use the error codes below. Evaluate results on your own cases and switch traffic after your acceptance checks. A local contract test does not establish model quality or production availability.

Question types and current input limits

ModelInput tokens per questionQuestionsOptions
s1-fast8,000116
s1-pro8,192364
s1-vision8,000126

Each question includes its state, criteria and prompt template; s1-vision also counts image positions. Check the full input limits.

What changes when you migrate

Checked 27 October 2026. The dated Jev alternatives comparison and migration notes define the compatibility boundary: separate keys, model IDs, processing tiers, billing, limits, metadata and errors. Switching models requires your own output evaluation. Byte-for-byte compatibility is not claimed.

Our score rubrics require at least two levels; TypeSafe’s published schema also accepts a single level. Excess questions are a non-retryable error.

With Idempotency-Key, an ongoing request returns 202 in_progress. After settlement, or while settlement is uncertain, it returns 409 idempotency_replay_unavailable with a receipt and no second charge, but no stored answer. A confirmed failure with no answer and no charge permits retrying the same key and body. A different body under the same key returns 409 idempotency_conflict. Use a new key only for a deliberately new inference.

Tested text examples

Each file below was executed unchanged against an isolated local HTTP mock of our published contract. Checks covered method, path, Bearer authentication, S1-Region, JSON body and reading a simulated answer with usage and model_version. No production inference request was made.

Set DECISIONMODELS_API_KEY to your key. The optional DECISIONMODELS_BASE_URL can point at your local mock; otherwise the public API base is used. Python examples require Python 3.10 and the package version shown. The fetch example is JavaScript-compatible TypeScript, executed with Node.js 20.19.4.

LiteLLM strips the typesafe/ provider prefix; the mock received s1-fast. Only Ollama’s Python client was tested, not its CLI. Typed SDKs may discard extra metadata such as model_version; the direct HTTP examples preserve it.

curl 7.81.0

tested on 27 Oct 2026 against our published contract with curl 7.81.0

curl.sh
curl --fail-with-body --silent --show-error --max-time 15 \
  "${DECISIONMODELS_BASE_URL:-https://api.decisionmodels.io}/v1/systemone" \
  -H "Authorization: Bearer $DECISIONMODELS_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'S1-Region: eu' \
  --data '{"model":"s1-fast","state":"Mia owns a red bicycle.","questions":{"color":{"type":"choice","instructions":"Which color is the bicycle?","criteria":{"red":null,"blue":null}}}}'

Python requests 2.32.5

tested on 27 Oct 2026 against our published contract with Python requests 2.32.5

requests.py
import os
import requests

base = os.environ.get("DECISIONMODELS_BASE_URL", "https://api.decisionmodels.io")
response = requests.post(
    base + "/v1/systemone",
    headers={
        "Authorization": "Bearer " + os.environ["DECISIONMODELS_API_KEY"],
        "S1-Region": "eu",
    },
    json={
        "model": "s1-fast",
        "state": "Mia owns a red bicycle.",
        "questions": {"color": {
            "type": "choice",
            "instructions": "Which color is the bicycle?",
            "criteria": {"red": None, "blue": None},
        }},
    },
    timeout=15,
)
response.raise_for_status()
print(response.json())

Node.js fetch 20.19.4

tested on 27 Oct 2026 against our published contract with Node.js fetch 20.19.4

fetch.ts
// JavaScript-compatible TypeScript; native fetch in Node.js.
const base = process.env.DECISIONMODELS_BASE_URL ?? "https://api.decisionmodels.io";
const response = await fetch(`${base}/v1/systemone`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.DECISIONMODELS_API_KEY}`,
    "Content-Type": "application/json",
    "S1-Region": "eu",
  },
  body: JSON.stringify({
    model: "s1-fast",
    state: "Mia owns a red bicycle.",
    questions: { color: {
      type: "choice",
      instructions: "Which color is the bicycle?",
      criteria: { red: null, blue: null },
    } },
  }),
  signal: AbortSignal.timeout(15000),
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());

LiteLLM 1.106.4

tested on 27 Oct 2026 against our published contract with LiteLLM 1.106.4

litellm.py
import os
import litellm

response = litellm.systemone(
    model="typesafe/s1-fast",
    api_base=os.environ.get("DECISIONMODELS_BASE_URL", "https://api.decisionmodels.io"),
    api_key=os.environ["DECISIONMODELS_API_KEY"],
    extra_headers={"S1-Region": "eu"},
    state="Mia owns a red bicycle.",
    questions={"color": {
        "type": "choice",
        "instructions": "Which color is the bicycle?",
        "criteria": {"red": None, "blue": None},
    }},
    timeout=15,
)
print(response.model_dump())

Ollama Python 0.6.3

tested on 27 Oct 2026 against our published contract with Ollama Python 0.6.3

ollama.py
import os
from ollama import Client

client = Client(
    host=os.environ.get("DECISIONMODELS_BASE_URL", "https://api.decisionmodels.io"),
    headers={
        "Authorization": "Bearer " + os.environ["DECISIONMODELS_API_KEY"],
        "S1-Region": "eu",
    },
    timeout=15,
)
response = client.systemone(
    model="s1-fast",
    state="Mia owns a red bicycle.",
    questions={"color": {
        "type": "choice",
        "instructions": "Which color is the bicycle?",
        "criteria": {"red": None, "blue": None},
    }},
)
print(response.model_dump())

Client documentation: LiteLLM System One · Ollama Python 0.6.3. Tests cover the named versions and the text example shown.

Handle errors before switching

Handle these codes explicitly; the full rules are in errors and retries.

Next step

Review the full migration notes and your own test cases before moving production requests.