System One (legacy)
The TypeSafe-compatible /v1/systemone endpoint, to be deprecated soon in favor of the OpenAI-compatible /v1/decisions
Deprecated soon
/v1/systemone will be deprecated soon in favor of the OpenAI-compatible
/v1/decisions endpoint. It keeps working for existing
integrations, but it serves TypeSafe models (Jev) only and gets no new models
— gpt-6-luna-decisions and future decision models are available on
/v1/decisions only. Jev works on /v1/decisions at the same price; see
Migrating from
/v1/systemone.
LLMGateway exposes a TypeSafe-compatible /v1/systemone endpoint for System
One models: models that read a state, answer questions you name, and return
typed decisions with calibrated probabilities. There is no free-form text in the
response — every answer is a value your code can branch on directly.
New integrations should use /v1/decisions instead.
Requesting a non-TypeSafe decision model here returns a 400 pointing you at
/v1/decisions.
For the full request and response schema, see the API reference.
Endpoint
POST https://api.llmgateway.io/v1/systemone
Question types
Every question has a type, instructions, and — for choice and score — its
own criteria. Answers come back under the ids you chose.
| Type | Ask | Answer |
|---|---|---|
noul | A yes/no question | noul: probability the answer is yes, 0 to 1 |
choice | One option from a set you define | choice, probabilities per option, confidence |
score | A rating across ordered levels | score (can land between levels), probabilities, legend, confidence |
box | Where a target is in an image | box (top, bottom, left, right as fractions), confidence |
A box question takes instructions only, needs an image in the request, and
works on Jev 1.14 only.
cURL
curl -X POST "https://api.llmgateway.io/v1/systemone" \
-H "Authorization: Bearer $LLM_GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "jev-1.14.0",
"state": "Help! My payouts have been failing for 3 days.",
"questions": {
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoicing, refunds",
"technical": "Bugs, outages, integrations",
"sales": "Pricing, upgrades, new accounts"
}
},
"is_urgent": {
"type": "noul",
"instructions": "Does this convey urgency?"
},
"impact": {
"type": "score",
"instructions": "Rate the operational impact.",
"criteria": ["None", "Limited", "Critical"]
}
}
}'{
"model": "typesafe/jev-1.14.0",
"answers": {
"department": {
"type": "choice",
"choice": "billing",
"probabilities": { "billing": 0.88, "technical": 0.12, "sales": 0.0 },
"confidence": 0.81
},
"is_urgent": { "type": "noul", "noul": 0.95 },
"impact": {
"type": "score",
"score": 1.9,
"legend": { "0": "None", "1": "Limited", "2": "Critical" },
"probabilities": { "0": 0.0, "1": 0.1, "2": 0.9 },
"confidence": 0.88
}
},
"usage": { "input_tokens": 318, "output_tokens": 34 }
}The model field reports the pinned model that answered, prefixed with the
provider it was served by. Provider aliases that move between releases (for
example jev-latest) are accepted and resolve to the pinned version, so a
request is always billed and logged against the version you can read prices for.
Request fields
| Field | Type | Description |
|---|---|---|
model | string | Decision model to use. Optionally prefixed with a provider (typesafe/…). |
state | string | object | array | The content to evaluate. Structured data lets questions reference fields. |
questions | map<string, Question> | At least one typed question, keyed by ids you choose. |
assets | map<string, Asset> | Optional images, keyed by a reference string. See below. |
Images
Put an image part anywhere in state, in a question's instructions (as an
array or object), or in its criteria, at any depth:
{
"model": "jev-1.14.0",
"state": [
"Product photo from the listing:",
{
"type": "image_url",
"image_url": { "url": "data:image/jpeg;base64,/9j/4AAQ…" }
}
],
"questions": {
"damaged": { "type": "noul", "instructions": "Is the item damaged?" },
"product": { "type": "box", "instructions": "the product in the photo" }
}
}The url is a PNG or JPEG data URL, or an HTTPS URL the gateway downloads.
Object keys are never read as images. A Score legend that echoes an image
level returns your image part.
You can also send TypeSafe's own shape: list each image once in assets as
{ "type": "image", "format": "png" | "jpg", "data_base64": "…" } (base64
without a data: prefix), keyed by a reference string, and write the reference
anywhere in state, instructions or criteria. Use a fresh UUID per image so
the reference can't match your text by accident. Both shapes can be mixed.
The gateway sends TypeSafe's TypeSafe-Danger-Zone-Images opt-in for you, and
runs every request with an image, or with more than about 32 KB of text, in
TypeSafe's long-context mode (262k tokens). Images must be 280 to 1568 pixels
per side. Image input is experimental at TypeSafe; multipart uploads are not
supported.
{
"model": "jev-1.14.0",
"state": { "photo": "a3f1c2e8-photo" },
"assets": {
"a3f1c2e8-photo": {
"type": "image",
"format": "jpg",
"data_base64": "/9j/4AAQ…"
}
},
"questions": {
"damaged": {
"type": "noul",
"instructions": "Is the item in `photo` damaged?"
}
}
}Deciding in code
The point of a typed answer is that the policy stays in your code, not in a
prompt. Threshold the probability, and use confidence as a second axis to send
uncertain cases to a human instead of acting on a coin flip:
const res = await fetch("https://api.llmgateway.io/v1/systemone", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LLM_GATEWAY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "jev-1.14.0",
state: ticket,
questions: {
department: {
type: "choice",
instructions: "Which team should handle this?",
criteria: { billing: null, technical: null, sales: null },
},
is_urgent: { type: "noul", instructions: "Does this convey urgency?" },
},
}),
});
const { answers } = await res.json();
if (answers.department.confidence < 0.7) {
await queueForTriage(ticket);
} else {
await route(ticket, answers.department.choice, {
priority: answers.is_urgent.noul > 0.8 ? "high" : "normal",
});
}Ask everything you might need in one request: the state is read once and every question is evaluated against it, so a batch of questions costs far less than one request each — including speculative questions you only read when another answer makes them relevant.
Decision models are billed on input tokens only — the state plus every question. Output tokens are free, since the response is a set of values rather than generated text. Question text counts as input on every call, so a large rubric has a real per-request cost.
Decision models only work on /v1/decisions and /v1/systemone. Requesting
one on /v1/chat/completions returns a 400 pointing you at the right
endpoint, they cannot generate text or call tools, and they are not available
in the playground.
How is this guide?
Last updated on