Decision API: schemas

The request and response bodies of the operations in this API.

AbstainTreatment

What an ABSTAIN from a non-human decider means to M_OF_N arithmetic. skip (default) waits for the others; reject_implicit counts it against. Without it a flaky model can sink an ask by abstaining.

Type: one of "skip", "reject_implicit"

BulkDeferDecisionsDto

PropertyTypeRequiredDescription
decisionIdsarray of stringyesDecision ids to defer.
deferUntilstringyesISO 8601 instant to defer until. Must be in the future.

BulkDispositionResponseDto

PropertyTypeRequiredDescription
changednumberyesHow many rows the SERVER actually changed. Deliberately NOT the number of ids the caller sent: those two differ exactly when something went wrong — an id in another org, an already-settled record, a decision the caller may not see — and reporting the request size would hide it.
requestednumberyesHow many ids the caller submitted, echoed for comparison.
skippedarray of stringyesThe ids that were NOT changed, so the caller can say why to a human.

BulkDispositionResponseDtoDataEnvelope

PropertyTypeRequiredDescription
dataBulkDispositionResponseDtoyes

CommitOptionDto

PropertyTypeRequiredDescription
notestringnoFree-text note recorded with the answer.
optionKeystringyesThe key of an option the record ALREADY CARRIES. A key the record does not carry is a 422 — never coerced to the nearest option, never accepted as a new one.
replyobjectnoThe typed reply for this option. VALIDATED against the option's declared replySchema when it has one; stored opaquely otherwise. The service checks the shape and never interprets the content.

DecisionAnswerOrigin

Where the recorded decider’s answer was given. Present exactly when recordedBy is (a commit recorded before origin existed may carry none).

Type: one of "FRONTEND", "TERMINAL", "API"

DecisionBadgeDto

PropertyTypeRequiredDescription
cappedbooleanyesTrue when the real count exceeded the counting cap. A badge says "lots", never a wrong number.
countnumberyesAnswerable, non-deferred decisions addressed to the caller.

DecisionBadgeDtoDataEnvelope

PropertyTypeRequiredDescription
dataDecisionBadgeDtoyes

DecisionCommitResponseDto

PropertyTypeRequiredDescription
committedAtstringyes
committedBystringyesThe committer’s BARE subject id (SubjectRef.id) for every commit written after committer existed, whatever the kind. A commit written before that carries whatever its writer stored (a bare id, or a composite <kind>:<id> for a recorded or exhaustion commit); read committer for the kind.
committerSubjectRefDto or nullyesWHO committed, as a kernel subject ref ({kind, id}). null for a commit written before the kind was recorded whose kind cannot be derived from stored facts (a bare id carries none): a reader must refuse rather than guess.
idstringyes
notestring or nullno
optionEffectDecisionOptionEffectyes
optionKeystringyes
recordedAtstring or nullnoWhen the external decision was taken (ISO 8601), as the producer stated it. Present exactly when recordedBy is.
recordedBystring or nullnoThe PRODUCER that recorded this commit on the decider’s behalf, for a decision taken outside Xema. null for an answer made here. When set, committer is the DECIDER, not the producer.
recordedEvidencestring or nullnoOpaque producer-supplied evidence pointer; only on a recorded commit.
recordedOriginDecisionAnswerOrigin or nullnoWhere the recorded decider’s answer was given. Present exactly when recordedBy is (a commit recorded before origin existed may carry none).
replyobject or nullno

DecisionDeadlineEffect

Type: one of "CLOSE", "HIDE", "ESCALATE"

DecisionExplanationResponseDto

PropertyTypeRequiredDescription
approvalReachablebooleanyesWhether an acting option can still reach its approval threshold.
boundToViewerbooleanyesTrue only when exactly one bound slot of this decision names you (the verified viewer). Computed from your verified identity; no other viewer's binding is returned.
causeDecisionPolicySettlementCause or nullyesThe rule that settled the decision; null while it is pending.
excludedGroupsarray of objectyes
optionsarray of objectyes
outcomeobjectyes

DecisionExplanationResponseDtoDataEnvelope

PropertyTypeRequiredDescription
dataDecisionExplanationResponseDtoyes

DecisionFactResponseDto

PropertyTypeRequiredDescription
hrefstring or nullno
labelstringyes
valuestringyes

DecisionKind

Type: one of "CONFIRM", "VERDICT", "PROPOSAL_SET", "OPEN"

DecisionListScope

Type: one of "ADDRESSED_TO_ME", "ALL_ANSWERABLE"

DecisionOptionEffect

Type: one of "SETTLING_ACT", "SETTLING_NO_ACT", "ADDITIVE"

DecisionOptionResponseDto

PropertyTypeRequiredDescription
capabilityArgumentsobject or nullnoPre-filled arguments, returned so the answerer can SEE what committing this option would run.
capabilityRefstring or nullno
commitCountnumberyesDistinct live commits on this option.
commitThresholdnumberyesLive commits required for this option to settle.
committedByYoubooleanyesWhether the CALLING answerer already holds a live commit here.
descriptionstring or nullno
effectDecisionOptionEffectyes
executablebooleanyesTrue when this option carries an executable payload the PRODUCER will run on commit. decision-api itself never executes anything.
keystringyes
labelstringyes
replySchemaobject or nullnoJSON Schema the renderer builds this option’s reply form from.

DecisionOutcome

Terminal outcome, null while live. COMMITTED_NO_ACTION ("decided to do nothing") is permanent; NO_JUDGEMENT ("nobody answered") is retriable. Collapsing the two silently discards real work.

Type: one of "COMMITTED_ACTION", "COMMITTED_NO_ACTION", "NO_JUDGEMENT", "WITHDRAWN"

DecisionPolicySettlementCause

The rule that settled the decision; null while it is pending.

Type: one of "APPROVAL", "VETO", "REFUSAL_ANY_ONE", "REFUSAL_APPROVAL_UNREACHABLE"

DecisionProducerEvidenceResponseDto

PropertyTypeRequiredDescription
canonicalJsonstringyesExact canonical-JSON bytes. Object keys must already be sorted at every depth; decision-api refuses and never rewrites non-canonical input.
contentHashstringyesLowercase SHA-256 of the exact UTF-8 canonicalJson bytes.
schemaRefstringyes

DecisionReplyPolicyDto

PropertyTypeRequiredDescription
abstainTreatmentAbstainTreatmentnoWhat an ABSTAIN from a non-human decider means to M_OF_N arithmetic. skip (default) waits for the others; reject_implicit counts it against. Without it a flaky model can sink an ask by abstaining.
kindReplyPolicyKindyesSINGLE / ANY_OF: the first answer decides. M_OF_N: m answers decide. ALL_OF: every resolved human recipient must answer.
mnumbernoHow many answers settle the ask. Required by M_OF_N; rejected on the other kinds. A value larger than the resolved audience is a 422 — a quorum bigger than the people asked can never be reached.

DecisionResponseDto

PropertyTypeRequiredDescription
answerCapabilityRefstringyes
answerablebooleanyesWhether this record still accepts an answer.
attemptnumberyes1-based re-ask counter for this idempotency key.
boundToViewerbooleannoPresent (and true) only when exactly one bound slot of this decision names you, the verified viewer: the ask is yours, held for your session. Absent means it is not bound to you. Computed from your verified identity by the same rule as the explanation route; no other viewer's binding is returned, and a client never derives it from the recipients.
commitsarray of DecisionCommitResponseDtoyesThe answer trail, newest first — who answered what, and when. Rendered as "alice approved 12:34, bob rejected 12:35".
createdAtstringyes
deadlineAtstring or nullno
deadlineEffectDecisionDeadlineEffectyes
deepLinkstring or nullno
deferredUntilstring or nullno
escalationLevelnumberyesWhich escalation level holds this ask. 0 is the original recipients. Exposed because an escalation is otherwise INVISIBLE to the person now holding it: the addressed set changes underneath them and nothing on the card says the ask reached them because an earlier level did not answer.
escalationLevelCountnumberyesHow many levels the chain declares, so a card can say "level 2 of 3" and a reader can tell whether anywhere is left to escalate to. 0 means no chain.
factsarray of DecisionFactResponseDtoyes
idstringyes
kindDecisionKindyes
optionsarray of DecisionOptionResponseDtoyes
outcomeDecisionOutcome or nullnoTerminal outcome, null while live. COMMITTED_NO_ACTION ("decided to do nothing") is permanent; NO_JUDGEMENT ("nobody answered") is retriable. Collapsing the two silently discards real work.
producerEvidenceDecisionProducerEvidenceResponseDto or nullno
producerRefstring or nullno
producerServicestringyes
replyPolicyDecisionReplyPolicyDto or nullnoThe ask-level aggregation rule, when the producer declared one. Presentation and audit only — it is RESOLVED INTO the per-option commitThresholds at open time, and those are what count. A card reads the numbers off the options; this is what lets it say WHY ("everyone must answer") instead of leaving a reader to infer the rule from them.
retriableboolean or nullnoWhether a producer may legitimately re-ask. Null while the record is still live.
revisionnumberyesOptimistic-concurrency token. Send it back as expectedRevision on a producer close so the write is a compare-and-set bound to the revision you read.
settledOptionKeystring or nullno
settlementDecisionSettlementyes
statusDecisionStatusyesDERIVED lifecycle. DEFERRED, LAPSED and EXPIRED are read-model predicates over deferredUntil / deadlineAt — no row moves and no job runs. LAPSED is past a HIDE deadline and is STILL ANSWERABLE; EXPIRED is past a CLOSE deadline and is terminal.
subjectClassificationDecisionSubjectClassificationyes
subjectResourceRefstringyes
summarystring or nullno
supersedesDecisionIdstring or nullnoThe attempt this record replaced, or null for a first ask. This is the ONLY way to walk a re-ask history from outside: attempt says how many times the producer asked, and the key that groups them is deliberately not on the wire — it is an opaque producer content hash, and exposing it would leak the operation it digests. May DANGLE if the predecessor was erased; resolving it to nothing means the earlier attempt existed and is gone, which is a true answer rather than a missing one.
titlestringyes
updatedAtstringyes

DecisionResponseDtoDataEnvelope

PropertyTypeRequiredDescription
dataDecisionResponseDtoyes

DecisionResponseDtoPaginatedEnvelope

PropertyTypeRequiredDescription
dataarray of DecisionResponseDtoyes
paginationPaginationMetayes

DecisionSettlement

Type: one of "BY_COMMIT", "BY_PRODUCER"

DecisionStatus

DERIVED lifecycle. DEFERRED, LAPSED and EXPIRED are read-model predicates over deferredUntil / deadlineAt — no row moves and no job runs. LAPSED is past a HIDE deadline and is STILL ANSWERABLE; EXPIRED is past a CLOSE deadline and is terminal.

Type: one of "OPEN", "DEFERRED", "LAPSED", "COMMITTED", "EXPIRED", "CLOSED"

DecisionSubjectClassification

Type: one of "PUBLIC", "INTERNAL", "CONFIDENTIAL", "SECRET", "REGULATED"

DeferDecisionDto

PropertyTypeRequiredDescription
deferUntilstringyesHide the decision from the default list until this instant (ISO 8601). A read-model predicate — the list condition flips on its own and there is no restore job to fail. Must be in the future.

PaginationMeta

PropertyTypeRequiredDescription
hasMorebooleanno
limitnumberno
nextCursorstring or nullno
pagenumberno
previousCursorstring or nullno
totalnumberno
totalPagesnumberno

PolicySimulationResponseDto

PropertyTypeRequiredDescription
compiledobjectyesThe compiled policy vector.
explanationDecisionExplanationResponseDtoyesThe verdict and its reason: outcome, cause and per-option, per-group progress.

PolicySimulationResponseDtoDataEnvelope

PropertyTypeRequiredDescription
dataPolicySimulationResponseDtoyes

ProblemActionableBy

Who can act on this refusal: user (correct the input, sign in), org-admin (an administrator of the organisation), platform (the operators of this installation), none (nobody — it cannot succeed). ABSENT when not known; never guessed.

Type: one of "user", "org-admin", "platform", "none"

ProblemDetailsDto

RFC 9457 problem document. The declared members are the reserved core; extension members are TOP-LEVEL and OPEN, owned by the refusing plane.

PropertyTypeRequiredDescription
actionableByProblemActionableBynoWho can act on this refusal: user (correct the input, sign in), org-admin (an administrator of the organisation), platform (the operators of this installation), none (nobody — it cannot succeed). ABSENT when not known; never guessed.
codestringyesSCREAMING_SNAKE_CASE. The machine-branchable refusal, and the value a policy rule, a retry predicate and a UI branch all name.
detailstringnoHuman-readable and specific to THIS occurrence.
errorsarray of ProblemFieldIssueDtonoField-level issues, when the refusal is about the request body.
instancestringnoURI reference identifying the occurrence — a request path, a run, a row.
paramsmap of string or number or booleannoSafe i18n interpolation values for the message keyed by code — typed and allowlisted per code by its declaration. Never server-authored copy, never internal diagnostics.
retryProblemRetrynoWhat a retry can achieve: transient (later, unchanged), after-change (only after someone changes something), never (a defect until a fixed version ships). ABSENT when not known.
statusnumberyesThe HTTP status code, repeated in the body so a logged document is self-contained.
titlestringyesShort, human-readable, and STABLE for a given type. Never per-occurrence — that is detail.
traceIdstringnoW3C trace-context trace-id, present when the caller sent a traceparent. Omitted rather than invented when it did not.
typestringyesAbsolute URI identifying the refusal TYPE. Derived one-to-one from code, so a caller may branch on either and can never be told two different things. Never about:blank.

ProblemFieldIssueDto

PropertyTypeRequiredDescription
codestringnoSCREAMING_SNAKE_CASE code of THIS issue, for a frontend i18n key. Present when the issue was minted by a declared problem.
detailstringyesProse for logs and developers. A frontend localises from code + params instead.
paramsmap of string or number or booleannoSafe i18n interpolation values for this issue's code.
pointerstringyesRFC 6901 JSON Pointer into the request body. Root is "/".

ProblemRetry

What a retry can achieve: transient (later, unchanged), after-change (only after someone changes something), never (a defect until a fixed version ships). ABSENT when not known.

Type: one of "transient", "after-change", "never"

ReplyPolicyKind

SINGLE / ANY_OF: the first answer decides. M_OF_N: m answers decide. ALL_OF: every resolved human recipient must answer.

Type: one of "single", "m_of_n", "all_of", "any_of"

SimulateCountDto

PropertyTypeRequiredDescription
countnumberyesHypothetical live commits counted into the (option, group) pair.
groupIdstringyesThe policy group id the count is for.
optionKeystringyesThe option key the count is on.

SimulateLegacyFieldsDto

PropertyTypeRequiredDescription
replyPolicyobjectno
resolvedHumanCountnumberyesBinding humans the ask resolves to.

SimulateOptionDto

PropertyTypeRequiredDescription
commitThresholdnumbernoLegacy form only: the declared threshold (default 1).
effectDecisionOptionEffectyes
keystringyesThe option key.

SimulatePolicyDto

PropertyTypeRequiredDescription
audienceSizesmap of integernoAudience size per group id (grouped form).
authorizedNonHumanGroupsarray of stringno
countsarray of SimulateCountDtonoHypothetical commit states.
factsobjectnoFacts conditions read.
legacyFieldsSimulateLegacyFieldsDtono
optionsarray of SimulateOptionDtoyes
policyobjectnoThe grouped DecisionPolicy. Give this or legacyFields, not both.

SubjectKind

Type: string (12 values)

SubjectRefDto

PropertyTypeRequiredDescription
idstringyesThe bare per-kind subject id.
kindSubjectKindyes