Open System One Specification
Status: early draft. Later releases may include breaking changes, marked in the changelog.
This document holds the normative rules of Open System One. The contract itself is the OpenAPI file of a release, the latest being openapi/2026-11-07.json; schema names in this document, such as SystemOneRequest, refer to its components.schemas. Where this document and the OpenAPI file disagree, that is a bug.
The key words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, and MAY are to be interpreted as described in RFC 2119.
Scope and terminology
- State: the input that all questions in a request refer to: a string, or structured data as an object or array (
SystemOneContent). - Question: a named, typed question about the state (
SystemOneQuestion). Itstypeselects the question shape and the answer shape. - Answer: the result for one question, under the question’s name, with the question’s type (
SystemOneAnswer). - Asset: an image referenced from the state, instructions, or criteria by a placeholder (
SystemOneAsset). - Implementer: a server that serves the System One operation at an endpoint URL, such as a model provider or a gateway in front of one.
- Client: anything that calls an implementer.
- Decision types (
noul,choice,score,box) judge the state: a probability, a choice, a score, or a box in an image. They come from TypeSafe’s format. - Extraction types (
text,number,date,time,datetime,duration,list,estimate,forecast) answer with values read, computed, or estimated from the state. - Core types are the decision and extraction types above (
SystemOneCoreQuestion,SystemOneCoreAnswer). Extension types are any other type, namedowner:type.
An implementer need not support every core type. It MUST reject the ones it does not support (see Errors).
Endpoint and envelope
The contract fixes no endpoint path. Its operation, components.pathItems.SystemOne, is served as an HTTP POST at a URL the implementer chooses, such as https://api.example.com/v1/systemone. A client MUST take the full endpoint URL as configuration, not build it from a base URL, so that switching implementers changes only the URL and model.
An implementer MUST accept an application/json body. It MAY also accept multipart/form-data, with the JSON request in the first part, named request, and each image in its own part.
SystemOneRequest |
Required | Rules |
|---|---|---|
model |
yes | Name or alias of the model. It stays required when an implementer serves a single model at its URL; an implementer MUST reject a model it does not serve. |
state |
yes | String, object, or array. |
questions |
yes | One or more questions keyed by names the client chooses. Types MAY be mixed in one request. |
assets |
no | Images keyed by placeholder. Each placeholder, wherever it appears as or inside a string in state, instructions, or criteria, stands for the asset. An asset carries data_base64 with format, or, in a multipart request, the name of its image part; every image part is used by exactly one asset. The placeholder’s position is a hint: an implementer MAY present assets before the state, for example when its model reads images only there. |
reference_date |
no | YYYY-MM-DD. Relative and year-less dates in the state resolve against it. |
SystemOneResponse |
Required | Rules |
|---|---|---|
model |
yes | The model that answered. It MAY differ from the alias in the request. |
answers |
yes | Exactly one answer per question, under the same names, each with its question’s type. |
usage |
yes | SystemOneUsage: input_tokens and output_tokens. Other counts are not part of the core contract. |
truncated |
no | See Truncation and state text. |
state_text |
no | See Truncation and state text. |
- A request either succeeds with every answer or fails. An implementer MUST NOT return a partial
answersobject. - Setting a field to its documented default is equivalent to omitting it.
- Implementers MAY add response fields. Clients MUST ignore response fields they do not know.
- Request fields this specification does not define are implementer-defined: an implementer MAY ignore or reject them.
Question types
Settings are the question fields other than type.
| Type | Kind | Question | Answer | Required settings | Optional settings |
|---|---|---|---|---|---|
noul |
decision | NoulQuestion |
NoulAnswer |
instructions, criteria |
|
choice |
decision | ChoiceQuestion |
ChoiceAnswer |
criteria |
instructions |
score |
decision | ScoreQuestion |
ScoreAnswer |
criteria |
instructions |
box |
decision | BoxQuestion |
BoxAnswer |
instructions |
|
text |
extraction | TextQuestion |
TextAnswer |
instructions, null_threshold |
|
number |
extraction | NumberQuestion |
NumberAnswer |
instructions, unit, integer, decimals, null_threshold |
|
date |
extraction | DateQuestion |
DateAnswer |
instructions, null_threshold |
|
time |
extraction | TimeQuestion |
TimeAnswer |
instructions, null_threshold |
|
datetime |
extraction | DateTimeQuestion |
DateTimeAnswer |
instructions, null_threshold |
|
duration |
extraction | DurationQuestion |
DurationAnswer |
instructions, unit, integer, decimals, null_threshold |
|
list |
extraction | ListQuestion |
ListAnswer |
items |
instructions, decimals |
estimate |
extraction | EstimateQuestion |
EstimateAnswer |
min, max |
instructions, unit, step, scale, quantiles |
forecast |
extraction | ForecastQuestion |
ForecastAnswer |
min, max, horizon |
instructions, unit, step, scale, interval |
owner:type |
extension | ExtensionQuestion |
ExtensionAnswer |
defined by the owner |
An implementer MUST NOT silently ignore a setting it cannot honor. It MUST reject the request with unsupported_parameter.
Answer guarantees
An implementer MUST return answers that satisfy these rules. Clients MAY rely on them across implementers.
Decision types
noul,choice,score, andboxanswers keep TypeSafe’s shapes exactly.choice:choiceMUST be one of the question’scriterianames and SHOULD be the one with the highest probability.probabilitiesis keyed by criteria names.score:legendandprobabilitiesare keyed by score level,"0"for the first criterion.scoreis within[0, number of criteria - 1].
Value types
text, number, date, time, datetime, and duration are value types. Their answers have value, p_null, confidence, and optionally guess and span.
- Null rule:
valueMUST be null whenp_nullis above the question’snull_threshold, and MUST NOT be null otherwise.null_thresholddefaults to 0.5. - Guess:
guessis the most likely value. An implementer that returnsguessMUST return it also whenvalueis null. An answer with a non-nullspanMUST haveguess. - Span:
spanis whereguesswas read in the state text, as a half-open range[start, end)ofSystemOneSpanoffsets, withstart <= end <= length. It indexes intostate_textwhen the response has it, and intostateotherwise. Fortext, the spanned text MUST equalguess; for other value types it is the textguesswas read from, such as$52.10for52.1. Whenguesswas not read from the state text, for example when it was read from an image or computed,spanis absent or null. - Offsets count Unicode code points.
numberanddurationvalues honorintegeranddecimals. Adurationanswer’sunitequals its question’sunit, which defaults toseconds.dateisYYYY-MM-DD.timeis 24-hourHH:MMorHH:MM:SSwithout a time zone.datetimeisSystemOneDateTime, with an offset only when the state gives one.
list
valueMUST be an array, never null: no match is an empty list.- Items MUST be in the order they appear in the input, each in the format of its
itemstype. spans, when present, has one entry per item in the same order: the item’s span, following the span rules above, or null when the item was not read from the state text. It is REQUIRED fortextitems, and each non-null entry for a text item MUST cover exactly that item.
estimate
- An estimate answer is never null.
value,median,p10,p90, and every entry ofquantilesMUST be within[min, max]and of the same kind asminandmax(numbers or dates).mean,p_below, andp_aboveare optional.mean, when present, is of the same kind asminandmax: a date for date estimates.value,median,p10, andp90MUST be on thestepgrid starting atmin, whenstepis set.meanMAY fall between grid points.p10 <= median <= p90.quantilesMUST be present when the question setsquantiles: true, as the 1st to 99th percentiles in non-decreasing order, and MUST be absent otherwise.
forecast
stepsMUST have exactlyhorizonentries, nearest step first. Parallel arrays per field, such as an array of values and an array of medians, are not part of the contract; an implementer MAY return them only as extra fields.- Each step MUST satisfy the
estimaterules, using the question’smin,max,step, andscale. Steps MUST NOT havequantiles: forecast questions have no quantiles setting.
Truncation and state text
state_textis REQUIRED whenever span offsets index text that differs from thestatesent, for example when structured state is serialized to text. It MUST be the text the offsets index into.- An answer MUST NOT have spans into a non-string
statewithoutstate_text. truncatedis REQUIRED whenever the implementer knows whether it shortened the state to fit the model’s context. An implementer that never truncates MAY omit it or set it to false.- Clients MUST NOT read an absent
truncatedas false. - When
truncatedis true,state_text, if returned, MAY be only the portion of the state the model read. Spans index into it. - Instead of truncating, an implementer MAY reject input too long for the model with an implementer-defined error.
Errors
Two error codes are standard. Both use HTTP 400 and the SystemOneErrorResponse envelope.
SystemOneError |
Required | Rules |
|---|---|---|
type |
yes | invalid_request_error for the standard codes. |
code |
yes | unsupported_question_type or unsupported_parameter for the standard cases. |
param |
yes | Path of the rejected field: questions.<name>.type, questions.<name>.<setting>, or a request field such as reference_date or assets. |
message |
yes | Human-readable. Clients MUST NOT parse it. |
An implementer MUST use:
unsupported_question_typefor a question whose type it does not support, whether core, extension, or unknown.paramisquestions.<name>.type.unsupported_parameterfor a request field or question setting defined by this specification that it cannot honor, such as areference_dateit cannot apply,assetsfor a model without image input, orquantiles: truefor a model without quantiles.paramnames the field.
An implementer MAY report only the first unsupported item. All other errors, such as unknown models, oversized input, authentication, and rate limits, are implementer-defined.
Extension types
- A non-core type MUST be named
owner:type(SystemOneExtensionType): a lowercase owner identifier, a colon, and a lowercase type name, such asacme:sentiment. - The owner identifier names the organization that defines the type, such as its domain name without the top-level domain. Owners SHOULD publish the question and answer shapes of their types.
- Unprefixed type names are reserved for core types. Implementers MUST NOT define unprefixed types.
- Only
typeis constrained byExtensionQuestionandExtensionAnswer. Owners SHOULD reuse core conventions such asp_null,confidence, andspan, and MUST follow the envelope, error, and state text rules. - An implementer MUST reject an extension type it does not support with
unsupported_question_type. - Clients and intermediaries SHOULD tolerate answer types they do not know: a gateway SHOULD pass them through unchanged, and a client MAY skip them.
Promotion to core
An extension type becomes core when at least two independent implementers support it with the same shape and a dated release adds it. That release defines it under the unprefixed name. Implementers that supported the prefixed name SHOULD keep accepting it, as an alias, until the next dated release.
Versioning
- A release is a single OpenAPI file named
openapi/YYYY-MM-DD.jsonby its release date. A released file MUST NOT change. - The first release is 2026-11-07.
openapi/draft.jsonis the working draft. It MAY change at any time and is not a release. At a release it is identical to the new release file, whose date is itsinfo.version; between releases itsinfo.versionisdraft.- Additive changes are new optional request fields or question settings, new response fields, and new core types. Any other change, such as removing, renaming, or retyping a field, or making an optional field required, is breaking. Breaking changes SHOULD be avoided and MUST be marked as breaking in the changelog.
- Releases after 2026-11-07 fix no endpoint path. The 2026-11-07 release defines its operation at
/v1/systemone. - Clients pin a release by vendoring its dated file, optionally with a checksum. Implementers SHOULD state which release they implement.
Compatibility with TypeSafe
- Open System One is a strict superset of TypeSafe’s format: a valid TypeSafe request is a valid
SystemOneRequest, and TypeSafe answers are validSystemOneAnswers. - Releases MUST NOT change TypeSafe’s request or answer shapes.
- If TypeSafe adds a type whose name collides with a core type, the next dated release adopts TypeSafe’s shape. If that shape is not compatible with the core one, the change is breaking and is marked as such; earlier releases keep the old shape.