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
| Property | Type | Required | Description |
|---|
decisionIds | array of string | yes | Decision ids to defer. |
deferUntil | string | yes | ISO 8601 instant to defer until. Must be in the future. |
BulkDispositionResponseDto
| Property | Type | Required | Description |
|---|
changed | number | yes | How 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. |
requested | number | yes | How many ids the caller submitted, echoed for comparison. |
skipped | array of string | yes | The ids that were NOT changed, so the caller can say why to a human. |
BulkDispositionResponseDtoDataEnvelope
CommitOptionDto
| Property | Type | Required | Description |
|---|
note | string | no | Free-text note recorded with the answer. |
optionKey | string | yes | The 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. |
reply | object | no | The 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
| Property | Type | Required | Description |
|---|
capped | boolean | yes | True when the real count exceeded the counting cap. A badge says "lots", never a wrong number. |
count | number | yes | Answerable, non-deferred decisions addressed to the caller. |
DecisionBadgeDtoDataEnvelope
DecisionCommitResponseDto
| Property | Type | Required | Description |
|---|
committedAt | string | yes | |
committedBy | string | yes | The 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. |
committer | SubjectRefDto or null | yes | WHO 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. |
id | string | yes | |
note | string or null | no | |
optionEffect | DecisionOptionEffect | yes | |
optionKey | string | yes | |
recordedAt | string or null | no | When the external decision was taken (ISO 8601), as the producer stated it. Present exactly when recordedBy is. |
recordedBy | string or null | no | The 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. |
recordedEvidence | string or null | no | Opaque producer-supplied evidence pointer; only on a recorded commit. |
recordedOrigin | DecisionAnswerOrigin or null | no | Where the recorded decider’s answer was given. Present exactly when recordedBy is (a commit recorded before origin existed may carry none). |
reply | object or null | no | |
DecisionDeadlineEffect
Type: one of "CLOSE", "HIDE", "ESCALATE"
DecisionExplanationResponseDto
| Property | Type | Required | Description |
|---|
approvalReachable | boolean | yes | Whether an acting option can still reach its approval threshold. |
boundToViewer | boolean | yes | True 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. |
cause | DecisionPolicySettlementCause or null | yes | The rule that settled the decision; null while it is pending. |
excludedGroups | array of object | yes | |
options | array of object | yes | |
outcome | object | yes | |
DecisionExplanationResponseDtoDataEnvelope
DecisionFactResponseDto
| Property | Type | Required | Description |
|---|
href | string or null | no | |
label | string | yes | |
value | string | yes | |
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
| Property | Type | Required | Description |
|---|
capabilityArguments | object or null | no | Pre-filled arguments, returned so the answerer can SEE what committing this option would run. |
capabilityRef | string or null | no | |
commitCount | number | yes | Distinct live commits on this option. |
commitThreshold | number | yes | Live commits required for this option to settle. |
committedByYou | boolean | yes | Whether the CALLING answerer already holds a live commit here. |
description | string or null | no | |
effect | DecisionOptionEffect | yes | |
executable | boolean | yes | True when this option carries an executable payload the PRODUCER will run on commit. decision-api itself never executes anything. |
key | string | yes | |
label | string | yes | |
replySchema | object or null | no | JSON 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
| Property | Type | Required | Description |
|---|
canonicalJson | string | yes | Exact canonical-JSON bytes. Object keys must already be sorted at every depth; decision-api refuses and never rewrites non-canonical input. |
contentHash | string | yes | Lowercase SHA-256 of the exact UTF-8 canonicalJson bytes. |
schemaRef | string | yes | |
DecisionReplyPolicyDto
| Property | Type | Required | Description |
|---|
abstainTreatment | AbstainTreatment | no | 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. |
kind | ReplyPolicyKind | yes | SINGLE / ANY_OF: the first answer decides. M_OF_N: m answers decide. ALL_OF: every resolved human recipient must answer. |
m | number | no | How 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
| Property | Type | Required | Description |
|---|
answerCapabilityRef | string | yes | |
answerable | boolean | yes | Whether this record still accepts an answer. |
attempt | number | yes | 1-based re-ask counter for this idempotency key. |
boundToViewer | boolean | no | Present (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. |
commits | array of DecisionCommitResponseDto | yes | The answer trail, newest first — who answered what, and when. Rendered as "alice approved 12:34, bob rejected 12:35". |
createdAt | string | yes | |
deadlineAt | string or null | no | |
deadlineEffect | DecisionDeadlineEffect | yes | |
deepLink | string or null | no | |
deferredUntil | string or null | no | |
escalationLevel | number | yes | Which 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. |
escalationLevelCount | number | yes | How 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. |
facts | array of DecisionFactResponseDto | yes | |
id | string | yes | |
kind | DecisionKind | yes | |
options | array of DecisionOptionResponseDto | yes | |
outcome | DecisionOutcome or null | no | 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. |
producerEvidence | DecisionProducerEvidenceResponseDto or null | no | |
producerRef | string or null | no | |
producerService | string | yes | |
replyPolicy | DecisionReplyPolicyDto or null | no | The 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. |
retriable | boolean or null | no | Whether a producer may legitimately re-ask. Null while the record is still live. |
revision | number | yes | Optimistic-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. |
settledOptionKey | string or null | no | |
settlement | DecisionSettlement | yes | |
status | DecisionStatus | yes | 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. |
subjectClassification | DecisionSubjectClassification | yes | |
subjectResourceRef | string | yes | |
summary | string or null | no | |
supersedesDecisionId | string or null | no | The 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. |
title | string | yes | |
updatedAt | string | yes | |
DecisionResponseDtoDataEnvelope
DecisionResponseDtoPaginatedEnvelope
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
| Property | Type | Required | Description |
|---|
deferUntil | string | yes | Hide 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. |
| Property | Type | Required | Description |
|---|
hasMore | boolean | no | |
limit | number | no | |
nextCursor | string or null | no | |
page | number | no | |
previousCursor | string or null | no | |
total | number | no | |
totalPages | number | no | |
PolicySimulationResponseDto
| Property | Type | Required | Description |
|---|
compiled | object | yes | The compiled policy vector. |
explanation | DecisionExplanationResponseDto | yes | The verdict and its reason: outcome, cause and per-option, per-group progress. |
PolicySimulationResponseDtoDataEnvelope
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.
| Property | Type | Required | Description |
|---|
actionableBy | ProblemActionableBy | no | 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. |
code | string | yes | SCREAMING_SNAKE_CASE. The machine-branchable refusal, and the value a policy rule, a retry predicate and a UI branch all name. |
detail | string | no | Human-readable and specific to THIS occurrence. |
errors | array of ProblemFieldIssueDto | no | Field-level issues, when the refusal is about the request body. |
instance | string | no | URI reference identifying the occurrence — a request path, a run, a row. |
params | map of string or number or boolean | no | Safe i18n interpolation values for the message keyed by code — typed and allowlisted per code by its declaration. Never server-authored copy, never internal diagnostics. |
retry | ProblemRetry | no | 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. |
status | number | yes | The HTTP status code, repeated in the body so a logged document is self-contained. |
title | string | yes | Short, human-readable, and STABLE for a given type. Never per-occurrence — that is detail. |
traceId | string | no | W3C trace-context trace-id, present when the caller sent a traceparent. Omitted rather than invented when it did not. |
type | string | yes | Absolute 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
| Property | Type | Required | Description |
|---|
code | string | no | SCREAMING_SNAKE_CASE code of THIS issue, for a frontend i18n key. Present when the issue was minted by a declared problem. |
detail | string | yes | Prose for logs and developers. A frontend localises from code + params instead. |
params | map of string or number or boolean | no | Safe i18n interpolation values for this issue's code. |
pointer | string | yes | RFC 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
| Property | Type | Required | Description |
|---|
count | number | yes | Hypothetical live commits counted into the (option, group) pair. |
groupId | string | yes | The policy group id the count is for. |
optionKey | string | yes | The option key the count is on. |
SimulateLegacyFieldsDto
| Property | Type | Required | Description |
|---|
replyPolicy | object | no | |
resolvedHumanCount | number | yes | Binding humans the ask resolves to. |
SimulateOptionDto
| Property | Type | Required | Description |
|---|
commitThreshold | number | no | Legacy form only: the declared threshold (default 1). |
effect | DecisionOptionEffect | yes | |
key | string | yes | The option key. |
SimulatePolicyDto
| Property | Type | Required | Description |
|---|
audienceSizes | map of integer | no | Audience size per group id (grouped form). |
authorizedNonHumanGroups | array of string | no | |
counts | array of SimulateCountDto | no | Hypothetical commit states. |
facts | object | no | Facts conditions read. |
legacyFields | SimulateLegacyFieldsDto | no | |
options | array of SimulateOptionDto | yes | |
policy | object | no | The grouped DecisionPolicy. Give this or legacyFields, not both. |
SubjectKind
Type: string (12 values)
SubjectRefDto
| Property | Type | Required | Description |
|---|
id | string | yes | The bare per-kind subject id. |
kind | SubjectKind | yes | |