System One
System One
DocumentationSDK documentationModelsAPI referenceOpen System OneDocumentation index

SDK & API

API referenceAPI versioningIdempotency keysAPI error codes

Build with decisions

Choose a modelDecision primitivesAgent integration: typed tool routingSystem One for coding agents (Claude Code, Cursor, Codex)

Usage & data

Billing and creditsPrivacy and zero data retention
API reference

API reference

HTTP decisions, auth, response contracts, and TypeScript examples for Choice, Score, and yes-no.

System One provides structured choices, scores, and yes/no probabilities over HTTP. This reference covers the hosted decision API and the console's account endpoints. The standalone System One SDK is also a first-class integration path, with its own adapters and credentials. Start with the hosted SDK quickstart for a TypeScript application, or use the HTTP examples below.

Answer-ready

Q: What is the System One API?
A: The System One API is the hosted decision API for Choice, Score, and Noul (yes-no). It authenticates with an API key and returns typed decision results for agents.

Q: Where are error codes and idempotency documented?
A: See the API errors, idempotency, and versioning pages under /docs/api/.

Base URL and authentication

SettingValue
Base URLhttps://system-one.dev/v1
Decision and extraction endpointPOST https://system-one.dev/v1/systemone
AuthenticationAuthorization: Bearer YOUR_API_KEY
Request formatContent-Type: application/json
ModelCopy the exact model ID (name) from the current models list.

Create an API key in your System One console. Store it in your integration tool's secret field or on your server, not in browser code, shared documents, or screenshots. Manage and revoke keys in the console. Requests use your account's credits.

The API origin is the main application, not the documentation host. POST /v1/systemone requires a System One Bearer key and accepts decision questions or extraction fields; GET /v1/models is public. /api/system-one/* account endpoints use your logged-in browser session instead. A direct provider's API key is not a System One account key. See Other endpoints for the exact authentication requirements.

YOUR_API_KEY, YOUR_MODEL_ID, and YOUR_OPERATION_KEY below are placeholders, not working credentials or model names. Replace the model placeholder with a selected ID; see Choose a model.

GET /v1/models

Fetch the current models list. This endpoint is public; a caller Bearer is ignored and cannot cause 401.

curl 'https://system-one.dev/v1/models' \
  -H 'Accept: application/json'

The JSON response is an object with a models array, not a data array. Do not assume a fixed number or order of entries.

Model fieldHow to use it
nameExact value to copy into the decision request's model field.
displayNameReadable name for model selection.
descriptionModel description.
pricing.creditsPerBlockCredits per question per billing block.
pricing.bytesPerBlockNumber of request bytes in a billing block.
capabilities.inputs, capabilities.questionsSupported input and question types.
limitsRequest limits, including bytes for the complete body, question rules, and optional images: { count, bytesPerImage, totalBytes } for decoded image limits.

Check the Playground selector and its Model information button for current availability, pricing and limits. All current models accept text and JSON; entries with image in capabilities.inputs and a limits.images object also accept images. Models that don't support images reject requests that include images. The models list can change; keep selection open to new entries rather than embedding a permanent catalog in your integration. Listing models does not consume decision credits and is limited separately to 60 requests per hashed IP per minute. Public HTML pages for enabled models are at /en/models and /zh-CN/models. Those pages link here for request rules and gateway ceilings.

Developer details

Public model pages show retail facts: price, request size, inputs, and choice/score limits. Slugs, capability tokens, gateway ceilings, and state/instruction/question-ID rules live here.

Public slugs

HTML pages are /en/models/<slug> and /zh-CN/models/<slug>. Published slugs: jev, clef-flash, clef, clef-omni, decider-v1-1, clairic. Copy the exact name from GET /v1/models into the request. Do not send the public slug as the model ID unless that slug is also an accepted name on the catalog entry. Clairic's public name is clairic; it does not appear in GET /v1/models until it is open for API calls.

Capabilities

Read capabilities.inputs and capabilities.questions from the selected catalog entry. Current models accept text and json; entries with image also accept images. Question types are choice, score, and noul. Do not infer capabilities from a model name.

Gateway ceilings

These are service maxima. A selected model's limits may be tighter. Sizes use 1024-based units: 64KB = 65,536 bytes, 13MB = 13,631,488 bytes, 4MB = 4,194,304 bytes, 8MB = 8,388,608 bytes.

LimitCeiling
UTF-8 JSON request body64KB for text-only models; 13MB for image-capable models
Images on supported modelsUp to 4; 4MB decoded each; 8MB decoded total
Questions per request32
Choice options255
Score levels2–10

State, instructions, and question IDs

Use the selected entry's limits.requiresInstructions, questionNameLength, and questionNamePattern.

  • Models that require instructions: state cannot be null. Every question needs nonempty string, object, or array instructions. IDs use A–Z, a–z, 0–9, _, ., and -. Score criteria cannot be null; Noul criteria must be omitted or an object with only true/false keys. Additional question fields are not supported.
  • Other models: nullable state and optional nullable instructions. Question IDs cannot be blank.

See Request fields and Limits and cost for the full HTTP contract.

POST /v1/systemone

Replace YOUR_MODEL_ID and save this body as request.json:

{
  "model": "YOUR_MODEL_ID",
  "state": { "message": "I was charged twice for one order." },
  "questions": {
    "team": {
      "type": "choice",
      "instructions": "Choose the reviewing team.",
      "criteria": { "billing": "Payments and refunds", "support": "Technical help" }
    },
    "urgency": {
      "type": "score",
      "instructions": "Evaluate urgency with the ordered rubric.",
      "criteria": ["Normal review", "Timely response", "Immediate human attention"]
    },
    "duplicate": {
      "type": "noul",
      "instructions": "Does the message report a duplicate charge?"
    }
  }
}

Send it with your key and a unique operation key, such as a UUID. Save the operation key and request before sending; reuse them only to recover the same operation.

curl --include 'https://system-one.dev/v1/systemone' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: YOUR_OPERATION_KEY' \
  --data-binary @request.json

This shell example makes a real, potentially billable request after replacement. The response is ordinary JSON, not a stream. The operation key is optional, but without it a repeated request is a new evaluation. Read Idempotency and retries before enabling retries in a tool.

On Windows PowerShell, use curl.exe and PowerShell's quoting conventions, or use the SDK quickstart. The documented compatibility snapshot is typesafe-2026-09-18; it is not a model ID or a version-selection header.

Request fields

The following input style works across the current model choices; the selected model's limits still apply.

FieldWhat to send
modelThe selected entry's name, a non-blank string of at most 128 characters without control characters. Always send it explicitly to avoid relying on a default.
stateRequired shared context: a string, JSON object, or array. "" is allowed, including image-only tasks. An array is one shared context, not a batch of separate requests.
imagesOptional ordered array of ImageInput: embedded base64 data URL strings or objects with mediaType and base64. Only image-capable models accept a nonempty array; [] is text-compatible.
questionsAn object with 1–32 named questions.
questions.*.typechoice, score, or noul. The HTTP type for a yes/no probability is noul, not boolean.
questions.*.instructionsProvide a non-empty string explaining the question. JSON objects and arrays are also accepted; some models require this field.
Choice criteriaAn object mapping option names to descriptions. Use at least two options; the model's minChoiceOptions determines its minimum. Maximum: 255.
Score criteriaAn array of 2–10 ordered descriptions, starting at index zero. Use non-null descriptions.
Noul criteriaOptional object with true and/or false descriptions. Omit it when unnecessary.

Use short question IDs containing letters, digits, dots, underscores, or hyphens, no longer than 100 characters. Option names must be non-blank and at most 128 characters. Consult the selected model's question-name rules before using other names. Use only the documented fields; do not assume null values or extra fields are supported by every model.

Model-specific and nullable inputs

The HTTP schema distinguishes a required field from a nullable value. state must be present; the native schema also represents null, omitted or null instructions, and omitted or null Noul criteria. These forms are not accepted by every model. Models requiring instructions need a non-empty string, object, or array; models with stricter inputs reject null state, null score descriptions, and null Noul criteria. Choice and Noul description values can be null where the selected model accepts them. Descriptions may otherwise contain nested JSON.

Use the selected entry's limits.minChoiceOptions, questionNameLength, questionNamePattern, and requiresInstructions, rather than hard-coding one model's rules for all models. Some accept one Choice option; others require at least two. Unsupported extension fields can be rejected, including extra question fields or Noul keys other than true and false. An HTTP schema or a standalone SDK adapter accepting a form does not mean every hosted model supports it. The core SDK also has stricter validation than the native TypeSafe schema; see SDK types and adapters.

Omitting model requires a configured service default, reported by public configuration; without one, the request returns 422 before charging. Explicit values take precedence. Hosted IDs and aliases come from the catalog; HTTP providers receive exact IDs without prefix rewriting. Preserve the selected model for reproducibility and recovery; validation failure never switches models automatically.

Images with Fetch

For a catalog entry with image capability, send PNG (image/png), JPEG (image/jpeg) or WebP (image/webp). Gateway ceilings are 4 images, 4MB (4,194,304 bytes) decoded each, 8MB (8,388,608 bytes) decoded total, and 13MB (13,631,488 bytes) for complete UTF-8 JSON. The selected limits.bytes and limits.images may be tighter. Data URLs contain actual base64 bytes; the object form is { mediaType, base64 }, not provider wire content_type. Do not infer support from a model ID. Remote file URLs, server-side downloads, audio and video are unsupported.

systemOneAdapter@0.7.0 does not encode evaluate() images. Use standard Fetch for hosted decision images; do not pass them through that adapter's evaluate() or replace its feature flag. Clairic images use extract() instead. Save this example as hosted-image.mts, set SYSTEM_ONE_API_KEY and SYSTEM_ONE_MODEL to an enabled image-capable catalog name such as clef or clef-flash, then run node --env-file=.env hosted-image.mts ./receipt.png with Node.js 22.18+ and a real PNG file. No SDK installation is required.

import { readFile } from 'node:fs/promises';

const apiKey = process.env.SYSTEM_ONE_API_KEY;
const model = process.env.SYSTEM_ONE_MODEL;
const imagePath = process.argv[2];
if (!apiKey || !model || !imagePath) {
  throw new Error('Set System One credentials/model and supply a PNG file path.');
}
const operation = {
  idempotencyKey: crypto.randomUUID(),
  body: JSON.stringify({
    model,
    state: '',
    images: [{ mediaType: 'image/png', base64: (await readFile(imagePath)).toString('base64') }],
    questions: {
      receipt: { type: 'noul', instructions: 'Does the image show a receipt?' },
    },
  }),
};
const response = await fetch('https://system-one.dev/v1/systemone', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': operation.idempotencyKey,
  },
  body: operation.body,
});
const responseText = await response.text();
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${response.headers.get('X-System-One-Error-Code') ?? 'unknown'}`);
}
console.log({ credits: response.headers.get('X-System-One-Credits'), responseText });

Each execution creates a new billable operation. This example retains the operation only in memory; persist the key and complete body before first sending in a recoverable application. Retries reuse the same body, images and array order, and key; do not reread a modified file or regenerate the UUID. Image base64 contributes to credit costs and idempotency identity. Invalid image representations, unsupported models and size violations fail before charging; ordinary upstream failures refund the reservation. Images go to the model service and are not separately stored as image uploads; echoed content can be part of a cached response. See data and privacy.

Native TypeSafe mode is a separate contract: it retains raw extension fields and the 64KB HTTP bound. This upgrade does not claim image support for that provider or change its nullable inputs or response precision.

JSON precision

Duplicate object keys are rejected, including names that become identical after escape decoding. Numbers outside the supported finite range are rejected. Idempotency preserves exact decimal distinctions: 9007199254740993 differs from 9007199254740992, and 1e-400 differs from zero. Equivalent decimal spellings compare equally. This does not make JavaScript number arbitrary-precision: serializing an already rounded value cannot recover its original digits. Retain the original JSON text when exact numeric request identity matters; model computation and SDK normalization are separate from that identity.

Limits and cost

LimitService maximum
UTF-8 JSON request bodySelected limits.bytes: Jev/HTTP modes 64KB (65,536 bytes); migrated Clef/Clef Flash up to 13MB (13,631,488 bytes)
Images on supported modelsUp to 4; decoded bytes up to 4MB each and 8MB total; selected limits.images may be tighter
Questions per request32
Options per choice255
Ordered levels per score10, with a minimum of 2
JSON nesting depth32

A selected model can impose stricter limits. Send valid UTF-8 JSON without duplicate object keys. Account rate limits are shared across API keys and Playground requests, including replay attempts. The default decision limit is 120 requests per minute. API authentication has a separate 600-attempt-per-IP-per-minute guard; model discovery has its own 60-request-per-account-per-minute limit. Follow the response headers for the window that applies, including on a 429 response.

Credits = question count × max(1, ceil(canonical parsed full request UTF-8 bytes / selected model's pricing.bytesPerBlock)) × pricing.creditsPerBlock. Billing measures the compact parsed request, including its effective model, context, questions, and criteria, not only the message or raw formatting whitespace. Exact-decimal idempotency comparison is separate from this billing-size calculation. For a hypothetical rate of 2 credits, three questions within one billing block cost 6 credits. This is arithmetic, not a quoted model price. Read current pricing fields before a new operation. In-flight operations retain their admitted price; successful same-key replay is free even after repricing or disabling the model. See Credits and billing.

The billing size includes the complete base64 image strings and their media types, not merely decoded pixels or text. Image order and representation also participate in request identity. The model's full-body byte limit applies even to a text-only request.

Read the result

This illustrative response shows answer shapes, not a recorded output. ACTUAL_MODEL_ID stands for the model name returned by the service; it is not a model ID to copy into a request.

{
  "model": "ACTUAL_MODEL_ID",
  "answers": {
    "team": {
      "type": "choice",
      "choice": "billing",
      "probabilities": { "billing": 0.8, "support": 0.2 },
      "confidence": 0.6
    },
    "urgency": {
      "type": "score",
      "score": 1.5,
      "probabilities": { "0": 0.1, "1": 0.3, "2": 0.6 },
      "confidence": 0.3,
      "legend": { "0": "Normal review", "1": "Timely response", "2": "Immediate human attention" }
    },
    "duplicate": { "type": "noul", "noul": 0.8 }
  },
  "usage": { "input_tokens": 120, "output_tokens": 0 }
}

Answer names match your question names. Choice returns an option name; score can be fractional; noul is the probability of true, from 0 to 1. Confidence is a separate model statistic, not necessarily the selected option's probability. See Decision types.

The returned model can differ from the requested ID. Record both when comparing results. Optional details such as token usage may be absent; missing or null token counts mean unknown, not zero. Accept additional response fields without treating them as billing information.

Choice answers include type, choice, probabilities, and confidence; Score answers include type, score, probabilities, confidence, and legend. Probability keys correspond to the options or zero-based rubric levels. The score is the probability-weighted rubric value, allowing for the model's numeric precision. Do not round returned values or synthesize missing usage, warnings, or rounding fields.

Successful responses do not add System One billing, request_id, or replayed fields to the body. If a model supplies fields with those names, treat them as model data, not System One accounting. The core SDK with systemOneAdapter normalizes Noul into { type: 'boolean', probability } and token usage into camelCase; it does not return every raw field. For full HTTP access use Fetch or the official TypeSafe client's withResponse(). That client's convenient requestId refers to TypeSafe tracking; read X-Request-Id explicitly for the System One request ID.

Response headers

HeaderMeaning
X-Request-IdSystem One request ID to save for support; a successful replay keeps the original ID.
X-Upstream-Request-IdModel-service tracking ID, when supplied; selected from x-typesafe-request-id, then x-request-id, then request-id.
X-TypeSafe-Request-IdTypeSafe tracking ID, when supplied, also used by the official client's withResponse().requestId.
X-System-One-CreditsNew charge for this attempt. Zero for successful replay or a confirmed refund. A missing header does not mean zero.
X-Idempotency-Replayedtrue when a previous successful result is returned without another evaluation or charge.
X-RateLimit-Limit, X-RateLimit-RemainingApplicable request-window limit and remaining attempts, when available.
X-RateLimit-ResetWindow reset time as a Unix timestamp in seconds.
X-Upstream-RateLimit-Limit, X-Upstream-RateLimit-Remaining, X-Upstream-RateLimit-ResetModel-service limits in a separate namespace; do not confuse them with your account's limit.
Retry-AfterWait time in seconds or an HTTP date before retrying.
X-System-One-Error-CodeStable error classification; see Errors.
X-System-One-Error-Sourceupstream for a forwarded model-service JSON error, or platform for a System One error or safety replacement.

Use these headers and the console's usage records for charges, request IDs, and replay status, rather than similarly named response-body fields. Responses use Cache-Control: no-store; this is separate from the explicit 24-hour idempotency window. Only documented tracking/rate headers, Content-Type, and Retry-After are forwarded from model services. Cookies, authorization, redirect locations, and arbitrary headers are not forwarded; sensitive header values are omitted.

Browser access and CORS

/v1/* supports unauthenticated OPTIONS preflight with status 204, Access-Control-Allow-Origin: *, allowed methods GET, POST, OPTIONS, and allowed request headers Authorization, Content-Type, Idempotency-Key. The tracking, credit, rate-limit, retry, replay, and error-classification headers above are exposed to browser JavaScript. This is Bearer authentication, not cross-origin cookie authentication: Access-Control-Allow-Credentials is not enabled. CORS support does not make it safe to embed a long-lived account key in public browser code.

Console account endpoints do not inherit this cross-origin policy. Call them from the signed-in application origin with the session cookie; browser mutations must carry the exact application Origin (scheme, host, and port, without a path). A Bearer key cannot replace a session, and an Origin header cannot replace authentication.

Extraction

When the body includes fields, this endpoint extracts typed values instead of answering decision questions. Send one request with { model, state, fields } plus optional images, reference_date, and options. Authentication, billing, refunds, and validation stay on this endpoint. Field count does not change the price. The public extraction model name is clairic. The TypeScript SDK exposes the same contract as client.extract(); see Clairic extraction.

{
  "model": "clairic",
  "state": "INVOICE INV-2291\nFrom: Northwind Supply\nIssued: 2026-09-02\nTotal due: 1080.00",
  "fields": {
    "invoice_number": { "type": "text" },
    "total": { "type": "number", "decimals": 2 }
  }
}

A decision body uses questions and is not an extraction request. Extraction models reject questions; decision models reject fields. Do not pass fields to SDK evaluate().

Other endpoints

These paths are relative to https://system-one.dev, not the /v1 base URL.

EndpointAuthenticationPurpose
GET /v1/modelsPublicCurrent { models: [...] } catalog with model details, as described above; no decision charge or decision record. A caller Bearer is ignored.
GET /api/system-one/configPublicDefault model, contract, readiness, model catalog, limits, welcome credits, and console feature availability.
GET /api/system-one/keysLogged-in sessionList your keys' metadata and prefixes, including revoked keys.
POST /api/system-one/keysSession + same originCreate a key from { "name": "Server" }; returns its secret once.
DELETE /api/system-one/keys/:idSession + same originRevoke a key belonging to your account.
GET /api/system-one/usageLogged-in sessionCurrent balance, last 30 UTC calendar days of usage, and up to 50 latest requests in that window.
POST /api/system-one/playgroundSession + same originSubmit a decision using the same request contract, model selection, credit accounting, and account limit as /v1/systemone.

Public configuration

GET /api/system-one/config returns a JSON object, without credentials:

FieldMeaning
name, model, contractProduct name, default model ID, and compatibility snapshot.
configuredWhether the service is configured for requests; not a guarantee that every request will succeed.
modelsCatalog entries with current prices, capabilities, and per-model limits. Do not assume a fixed length; use authenticated /v1/models for model discovery.
signupCredits, billingEnabledCurrent welcome-credit amount and whether billing is available.
authProvidersSign-in availability flags: github, google, wechat, and phone. These describe sign-in choices, not inference capabilities.
pricing.bytesPerCreditRequest bytes in the base billing block; per-model rates come from each entry's pricing.
limitsGlobal bytes, questions, choiceOptions, scoreLevels, and depth limits.
docsUrlDocumentation link; may be a relative URL.
providerInformational service identifier. Do not use it in place of model capabilities or as a client connection setting.

API key management

GET /api/system-one/keys returns { keys: [...] }, newest first, with at most 100 records. Each record has id, name, prefix, createdAt, lastUsedAt, and revokedAt. Timestamps are Unix milliseconds; lastUsedAt and revokedAt are null when not set. The full secret is never included in this list.

Create with Content-Type: application/json and exactly { "name": "Server" }. The trimmed name must contain 1–64 characters, with no control characters. Unknown body fields are rejected; the key-creation body limit is 4,096 bytes. Creation returns HTTP 201 with this shape (values are illustrative):

{
  "key": {
    "id": "example-key-id",
    "name": "Server",
    "prefix": "so_live_example_",
    "createdAt": 1790985600000
  },
  "secret": "YOUR_NEW_API_KEY"
}

secret is the complete key and is shown only in the creation response. Save it securely before dismissing the result. If that response is lost, list your keys and revoke the unwanted entry before creating a replacement; key creation does not provide decision-style idempotent replay. Prefixes identify keys but cannot authenticate requests.

Keys are account-scoped and use the account's balance; creation does not accept a narrower permission scope or a per-key rate limit. All keys and Playground share the account's decision limit. An account can have at most 20 active keys; creation is limited to 10 attempts per account per minute. DELETE /api/system-one/keys/:id returns { "ok": true }, including when your key was already revoked. An unknown key or a key belonging to someone else returns 404 key_not_found. A revoked key cannot authenticate new requests or replays.

Usage response

GET /api/system-one/usage returns { balance, summary, daily, requests } for the signed-in account, across all its API keys and Playground:

FieldShape and meaning
balanceCurrent numeric credit balance, not a balance restricted to the reporting window.
summaryrequests, credits, inputTokens, outputTokens, averageLatencyMs. Request counts include recorded statuses; average latency includes successful requests only.
daily30 entries with date (YYYY-MM-DD, UTC), requests, and credits, oldest first. Days without requests are filled with zero counts and credits.
requestsUp to 50 newest records in the window: id, model, status, questionCount, credits, inputTokens, outputTokens, latencyMs, createdAt, and errorCode. createdAt is Unix milliseconds.

The window begins at UTC midnight 29 days before today and includes today. This response has no pagination cursor. Unknown token counts and unset metrics/error codes can be null. If any successful request has an unknown input or output token count, that aggregate count is null rather than an incomplete total; empty aggregates can also be null. Do not convert null to zero. Failed or pending records are not successful answers, and successful replay does not create an additional evaluation record.

Session Playground requests

POST /api/system-one/playground accepts the same JSON body and optional Idempotency-Key as /v1/systemone, and returns the same decision/error shapes and accounting headers. The only authentication difference is the required logged-in session and same-origin check. Idempotency is shared across the account's session and API keys. Keep these session endpoints on the signed-in application origin rather than copying session cookies into another service.

Submitted content is processed by model services to produce answers. Send only information needed for the task and read Privacy. For an unresolved request or charge, contact support@system-one.dev with the request ID, never your API key.

Get started with the TypeScript SDK

Install the System One TypeScript SDK, connect to the hosted API, pick a model, and read a typed decision.

API versioning

How System One versions the decision API and which headers to send from the TypeScript SDK.

On this page

Answer-readyBase URL and authenticationGET /v1/modelsDeveloper detailsPublic slugsCapabilitiesGateway ceilingsState, instructions, and question IDsPOST /v1/systemoneRequest fieldsModel-specific and nullable inputsImages with FetchJSON precisionLimits and costRead the resultResponse headersBrowser access and CORSExtractionOther endpointsPublic configurationAPI key managementUsage responseSession Playground requests