System One
System One
DocumentationSDK documentationModelsAPI referenceOpen System OneDocumentation index

SDK & API

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

Decision primitives

Pick Choice, Score, or yes-no probability for agent routing, ranking, and binary classification — with a shared example.

Choose a primitive based on the answer your code needs: Choice selects from named options, Score rates against ordered levels, and Noul estimates the probability of yes. For one support ticket, you might ask which team should review it, how completely a draft reply addresses it, and whether it reports a duplicate charge. These are three different judgments, not interchangeable ways to measure confidence.

The examples below use @system-one-ai/core@0.8.0. See install and configure the TypeScript SDK and the hosted quickstart with an explicitly selected model, or try the same concepts in the decision Playground. For typed extraction, see Clairic extraction.

Answer-ready

Q: What are System One decision primitives?
A: System One provides three primitives: Choice (pick one option), Score (ordered scoring), and Noul / yes-no (probability that a statement is true). They return typed decisions for agent routing, ranking, and binary checks.

Q: What is Noul?
A: Noul is the yes-no primitive: it returns a probability that a condition holds, used for completion checks and binary gates.

Choose by the decision your application needs

Start with the output your application will consume, not with a preferred numeric format.

Application taskChooseWhy it fitsWhen not to use it
Route a ticket, classify intent, or propose one allowed next stepChoiceYour code needs one named option from a defined set, such as billing, support, or review.Not for returning every applicable label, generating an unrestricted plan, or executing the selected action.
Rate response quality or rank items by a defined criterionScoreYour code needs a position on an ordered rubric, such as incomplete → partial → complete.Not for unordered categories, a universal percentage of correctness, or comparisons between unrelated rubrics.
Estimate whether a specific condition holdsNoulYour code needs P(true) for a binary proposition, such as “This message reports a duplicate charge.”Not for general model confidence, choosing among several teams, or proving that an event occurred.

Use Choice when the important distinction is which option, Score when it is how much according to this rubric, and Noul when it is how likely this precise statement is true. A yes/no Choice can select a label, but use Noul when the downstream policy needs the probability of the positive condition. A low Noul value means the condition is estimated to be unlikely, not that the model lacks confidence.

If several labels can apply at once, define a separate Noul question for each condition instead of forcing one Choice. Those probabilities describe separate propositions; they are not a distribution over mutually exclusive categories and need not sum to one. For a fact already known from authoritative application data, use that data directly rather than asking a model to infer it.

One support ticket, three useful judgments

Use this shared context for the builders and combined request below:

Customer message: “I was charged twice for the same order.”

Draft reply: “Please send your order number so we can review both charges.”

Named questionWhat it evaluatesHow the application can use the answer
team — ChoiceWhich team should review the message: billing, technical support, or human review?Propose a queue, then check access and assignment rules before routing.
quality — ScoreHow completely does the supplied draft reply address the customer's message, using the three levels below?Flag incomplete replies for revision or compare drafts evaluated under the same rubric.
duplicate — NoulDoes the message report a duplicate charge?Flag a possible billing issue for investigation, not approve a refund.

This table describes the task, not recorded model results. The quality question evaluates the draft already present in state; it does not write a reply or rate an answer generated by another question. The duplicate question concerns what the customer reports. Whether two settled charges actually exist requires payment records, and refund eligibility requires the applicable policy. None of these answers establishes those facts or authorizes an action.

You do not need all three primitives for every ticket. Ask only the questions the workflow needs. If the task changes from reply quality to ticket urgency, define a separate ordered urgency rubric; reusing the quality score would change the meaning without changing the number.

Shared state, independent questions

state is the context for every named question in a request. A string, JSON object, or array is one shared state, not a batch of independent evaluations. Send separate calls for independent states. Include only the information needed and permitted for the decision.

Each question evaluates that shared context independently. One answer does not become evidence for another question in the same call. If a later judgment depends on an earlier answer, review that answer and include it as context in a subsequent call. Independent here describes the question interface, not a guarantee that model errors are statistically independent.

Core, native HTTP, and official client types

ContractSystem One core 0.8.0Native HTTP / official TypeSafe SDK 0.6.0
ContextRequired non-null State: string, object, or arrayRequired state; native types also permit null
InstructionsRequired field/argument; Description permits nullMay be omitted or null in the native types
Yes/no questiontype: 'boolean'; criteria omitted or objecttype: 'noul'; criteria omitted, object, or null in the native types
Yes/no answerBooleanAnswer: probabilityNoulResponse: noul
Choice/Score statisticsOptional in normalized ChoiceAnswer / ScoreAnswerNative Choice requires probabilities and confidence; native Score also requires legend

A type accepting null does not mean every service or model accepts it. A selected model can require non-empty instructions, restrict question names, reject null descriptions, require multiple Choice options, or disallow extensions. Use the current model directory and API contract, not the broadest SDK type, to decide what to send. The examples use explicit text and non-null descriptions.

Choice: select a named option

import { choice } from '@system-one-ai/core';

const team = choice('Choose the team that should review this message.', {
  billing: 'Payments and invoices',
  support: 'Technical help',
  review: 'Needs human review or more information',
});

The factory preserves 'billing' | 'support' | 'review' as the answer's choice type. Keep descriptions distinct, and include a review option if forcing a category would be misleading.

The following is an illustrative native HTTP answer object, not a recorded result or the whole response envelope:

{
  "type": "choice",
  "choice": "review",
  "probabilities": { "billing": 0.1, "support": 0.2, "review": 0.7 },
  "confidence": 0.5
}

choice names one declared option. The native distribution uses those option names as keys. confidence is a separate reported statistic, not necessarily the selected option's probability. Neither is a universal action threshold. The hosted API allows at most 255 Choice options; check the selected model's minimum and other limits.

Score: use an ordered rubric

import { score } from '@system-one-ai/core';

const quality = score('Evaluate how completely this answer addresses the question.', [
  'Does not address the question',
  'Partially addresses the question',
  'Fully addresses the question',
]);

Rubric positions run from 0 to levels - 1. The hosted API requires 2–10 ordered levels. A native Score answer contains type, score, probabilities, confidence, and legend. The probability and legend maps use rubric indices as keys.

The score is the weighted rubric result, not necessarily the most likely integer level:

score = sum(levelIndex * probabilityAtThatLevel)
      = (0 * 0.1) + (1 * 0.3) + (2 * 0.6)
      = 1.5

This arithmetic example corresponds to the following illustrative native HTTP answer:

{
  "type": "score",
  "score": 1.5,
  "probabilities": { "0": 0.1, "1": 0.3, "2": 0.6 },
  "confidence": 0.3,
  "legend": {
    "0": "Does not address the question",
    "1": "Partially addresses the question",
    "2": "Fully addresses the question"
  }
}

A score of 1.5 here is not automatically 75% correct, and is not a score out of 10. Preserve the rubric with the result. Changing descriptions or their order changes its meaning and the request used for idempotency.

Rank items using a consistent rubric

Score produces a rating, not a sorted list. To rank candidate replies by completeness, evaluate each draft as its own state with the same customer message, question, ordered levels, and selected model. Then sort the returned scores in application code, with an explicit policy for ties and missing or failed results. A state array is still one shared context; it is not a shortcut for batching independent candidates.

Compare results only within the criterion you defined. A completeness rubric does not also measure factual accuracy, tone, or refund eligibility. Use separate questions for those judgments when needed, and do not treat a small score difference as a guaranteed ordering. Validate the ranking on representative examples before relying on it. Separate candidate evaluations are separate calls; account for them in your hosted request credit estimates.

Boolean / Noul: estimate P(true)

import { booleanQuestion } from '@system-one-ai/core';

const duplicate = booleanQuestion('Does this message report a duplicate charge?', {
  true: 'The same order was charged more than once',
  false: 'No duplicate charge is reported',
});

With the HTTP adapter, the SDK converts this question from boolean to native noul. An illustrative HTTP answer is:

{ "type": "noul", "noul": 0.8 }

The equivalent normalized core answer is:

import type { BooleanAnswer } from '@system-one-ai/core';

const answer: BooleanAnswer = { type: 'boolean', probability: 0.8 };

Both represent P(true) between 0 and 1. A value of 0.8 is an estimated 80% chance that the answer is yes, not a confirmed fact, an automatic boolean, or confidence in either outcome. Noul has no separate confidence field. Your application decides thresholds, abstention, and human review.

Keep different checks separate: detecting a refund request does not establish refund eligibility. Eligibility needs policy and order facts, not just the customer's wording.

Combine named questions with typed answers

In the following continuation, team, quality, and duplicate are the builders defined above; client is a configured SystemOne client from the SDK guide. This performs a real evaluation using that client's selected service and model:

import { defineQuestions } from '@system-one-ai/core';
import type { Answers } from '@system-one-ai/core';

const questions = defineQuestions({ team, quality, duplicate });
const result = await client.evaluate({
  state: {
    message: 'I was charged twice for the same order.',
    answer: 'Please send your order number so we can review both charges.',
  },
  questions,
});
const answers: Answers<typeof questions> = result.answers;
console.log({
  team: answers.team.choice,
  quality: answers.quality.score,
  duplicateProbability: answers.duplicate.probability,
});

Native HTTP places each answer under its matching answers key. Core's Answers<Q> likewise maps each question to its answer type, but the result is normalized, not the original HTTP JSON. Missing optional distributions, confidence, or token counts remain unknown. Use response headers for hosted charges and request identifiers, not body fields that happen to have similar names.

Limits and application policy

Hosted requests allow up to 32 questions, up to 255 Choice options, and 2–10 Score levels. Complete UTF-8 request limits are model-specific: Jev and HTTP modes retain 64KB (65,536 bytes); migrated Clef/Clef Flash entries allow up to 13MB (13,631,488 bytes), including images. A selected model can impose stricter limits. Images accompany the shared state as a separate top-level images array; see image inputs. These are hosted API bounds, not promises about every direct SDK service.

Try routine, ambiguous, incomplete, and consequential cases before automating. Evaluate any threshold using representative examples and the cost of errors, not a single convincing response. Treat missing answers or failed requests as unresolved, not a default approval or lowest score.

A probability is a prediction, not permission to issue a refund, alter access, or send a message. Your application owns authorization, action allowlists, and required confirmations. See integrate a decision step into an agent with permissions and recovery for workflow design.

For the integration path, compare the open-source TypeScript decision SDK with the hosted decision API and current model catalog. The SDK can connect to services with their own credentials and billing; the hosted API uses a platform key and prepaid credits. To use them together, follow the hosted SDK quickstart for credentials, model selection, and a first request.

Choose a model

Compare System One models (Jev, Clef Flash, Clef Omni, Clairic), costs, and when to switch.

Agent integration: typed tool routing

Use System One Choice for next-step tool routing in TypeScript agents while you keep permissions and execution local.

On this page

Answer-readyChoose by the decision your application needsOne support ticket, three useful judgmentsShared state, independent questionsCore, native HTTP, and official client typesChoice: select a named optionScore: use an ordered rubricRank items using a consistent rubricBoolean / Noul: estimate P(true)Combine named questions with typed answersLimits and application policy