Skip to content
veraq

API Reference

The Open Record REST API.

Read the open record — the 22 entities behind every wave, outcome, and contribution.

Provisional

Component schemas are generated verbatim from @veraq/contracts — the single source of truth — and agree with the protocol reference (the seven entities). The REST resources, cursor pagination, and state-shaped errors are documented here; the live servers, authentication, and rate limits are gated on the product backend this repository does not contain, and are marked [OWNER TO CONFIRM].

Conventions

  • One REST resource per entity — /waves, /mechanisms, /participants, /participations, /outcomes, /contributions, /causes — plural in paths, singular in prose.
  • Every list endpoint is cursor-paginated (cursor + limit).
  • Every record carries its open-record reference, so a reader can cross-check it.
  • Errors are documented as states, never failures-of-luck: a mismatch is record_mismatch, a closed window is commit_window_closed.

Error

A request error, named as a state (e.g. record_mismatch, commit_window_closed, not_found) — never a failure-of-luck.

codestringrequired

Machine-readable state.

messagestringrequired

Human-readable, exact, never alarmist.

open_record_refstring | null

Where applicable, the open-record entry the reader can cross-check.

Example response — every verifiable field (✓) is checkable against the open record

GET /waves/{id} — shape✓ verifiable
{
  "id": "014",
  "opens_at": "2026-06-14T16:00:00Z",
  "closes_at": "2026-06-14T16:00:00Z",
  "resolves_at": "2026-06-14T16:00:00Z",
  "mechanism_id": "string",
  "seats": {
    "total": 1,
    "taken": 0
  },
  "charitable_share": 0,
  "beneficiary_cause_id": "string",
  "status": "announced"
}

Wave

A round of participation. Runs one mechanism; contains many participations; resolves to outcomes.

GET/waves

A round of participation. Runs one mechanism; contains many participations; resolves to outcomes. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/waves/{id}

A round of participation. Runs one mechanism; contains many participations; resolves to outcomes.

Parameters

  • idpath · requiredThe wave id.

Responses

Wave

A round of participation. Runs one mechanism; contains many participations; resolves to outcomes.

idstringrequired

Sequential wave identifier, zero-padded.

opens_atstringrequired

When participants may begin seating.

closes_atstringrequired

Last moment to seat a participation. Must be ≥ opens_at.

resolves_atstringrequired

When the mechanism seed is revealed and the outcome resolves. Must be ≥ closes_at.

mechanism_idstringrequired

ULID reference to the published mechanism for this wave. The mechanism is published — including its seed commitment — before the wave opens.

seatsobjectrequired
totalintegerrequired
takenintegerrequired
charitable_sharenumberrequired

Fraction of the wave's outcome that flows to the beneficiary cause. Expressed in [0, 1], e.g. 0.12 == 12%.

beneficiary_cause_idstringrequired

Slug of the verified cause receiving the charitable share.

statusenumrequiredannounced · open · resolving · settled · cancelled

Wave lifecycle. 'announced' = mechanism published, not yet open. 'resolving' = window between closes_at and the recorded outcome. 'settled' = outcome verified and contributions transferred.

Mechanism

The published selection rule (commit-reveal-v1), committed to the open record before a wave runs.

GET/mechanisms

The published selection rule (commit-reveal-v1), committed to the open record before a wave runs. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/mechanisms/{id}

The published selection rule (commit-reveal-v1), committed to the open record before a wave runs.

Parameters

  • idpath · requiredThe mechanism id.

Responses

Mechanism

The published selection rule (commit-reveal-v1), committed to the open record before a wave runs.

idstringrequired

ULID of this mechanism document.

algorithmstringrequired

Identifier of the algorithm spec in the veraq-protocol repository.

parametersobject

Algorithm-specific parameters. Shape is determined by the algorithm field and validated by the spec in veraq-protocol/algorithms/<algorithm>.md.

seed_commitmentstringrequired

Lowercase hex SHA-256 of (seed || parameters_canonical_json). Binding commitment published before the wave opens.

seed_revealedstring | null

Lowercase hex of the 32-byte seed. Null until resolves_at; non-null after. Verifying: SHA-256(seed_revealed || parameters_canonical_json) must equal seed_commitment.

published_atstringrequired

When the commitment was published. Must be < the linked wave's opens_at.

revealed_atstring | null

When seed_revealed was published. Null while undisclosed; ≥ wave.resolves_at when set.

verifying_transcript_urlstring | null

Optional URL to a verifying transcript (re-run output) for additional inspectability.

Participant

A person taking part. Makes participations; chooses a cause.

GET/participants

A person taking part. Makes participations; chooses a cause. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/participants/{id}

A person taking part. Makes participations; chooses a cause.

Parameters

  • idpath · requiredThe participant id.

Responses

Participant

A person taking part. Makes participations; chooses a cause.

idstringrequired

Stable participant identifier (ULID).

verdify_identity_idstringrequired

Foreign key into Verdify's own identity space (ADR 0011). Names the `usr_…` identity this participant record projects. Required and non-null — an explicit design decision of this epic, not an inference from a ceremony this repo has not built yet (`04` `W6-E04`, `W6-E09` are unbuilt): ADR 0011 and `01-platform-architecture.md` §3.4 D4 establish only that Veraq builds no sign-in of its own, not when a `Participant` row is first created relative to identity resolution. If a future epic finds a real lifecycle state where a seat exists before its Verdify identity resolves, this field's nullability is this epic's call to revisit, not a settled fact to work around. Only the `usr_` prefix is validated; the token's own internal structure is Verdify's to define, not Veraq's — following the same opaque-external-id convention `commitment_id` and `product_id` already use (ADR 0023 Decision 6).

handlestring | null

Optional public-facing handle. May be null for participants who choose not to display one.

created_atstringrequired

When the participant first seated.

verified_atstring | null

When identity verification was completed, if applicable. Null if unverified.

Participation

One participant's entry into a wave; written to the open record.

GET/participations

One participant's entry into a wave; written to the open record. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/participations/{id}

One participant's entry into a wave; written to the open record.

Parameters

  • idpath · requiredThe participation id.

Responses

Participation

One participant's entry into a wave; written to the open record.

idstringrequired

Wave-scoped participation id. Format: wave_id, hyphen, 4-character suffix.

wave_idstringrequired
participant_pseudonymstringrequired

Replaces `participant_id` per `01-platform-architecture.md` §4.3 change 5: "Replace `participant_id` and any durable signature in published entries with a per-instance pseudonym; hold the mapping salted in Custody, so erasure destroys the salt rather than the record." PER-INSTANCE, LOAD-BEARING NOT DECORATIVE: this value is scoped to THIS participation record only — never a stable value reused across the same participant's other participations in other waves. A single durable pseudonym reused across many published Participation records would ITSELF become "a durable signature" and re-enable cross-wave correlation of one participant's activity, which is exactly what change 5 also separately bans by naming "any durable signature," not only the literal field `participant_id`. RESOLUTION (pseudonym → the real participant) happens ONLY via a salted mapping held in Custody — the `.net` port `04` W2-E03 builds — never inline in this record, and never derivable from these bytes alone by an outside reader. NO PATTERN, DELIBERATELY, on fund.schema.json `id`'s own precedent ("no document fixes an id space for this... inventing one would freeze a byte before its gate"): this schema commits only to SHAPE — opaque, non-empty, per-instance, unlinkable without Custody's mapping — never to a derivation scheme (which hash, which KDF). The exact construction is UNSPECIFIED because the authority that was meant to specify it, "Core blueprint §11.4," does not exist anywhere in this repository or its git history — `G-blueprint` (`03-product-model.md`:611, "UNRESOLVED — recorded, not removed"; `04-implementation-roadmap.md`:169). Building that mapping, its salt storage and the cascade it must survive is `W8-E14`'s job (`04-implementation-roadmap.md`:740-742), not this schema-shape change's. ERASURE (later, `W8-E14`'s job) destroys the salt in Custody's mapping, not this record — the published Participation entry survives unchanged, append-only, per this corpus's Open Record philosophy, while re-identification becomes permanently impossible.

seated_atstringrequired
weightnumber

Optional weighting used by weighted-selection mechanisms. Omitted means weight = 1.

kindenumcontributor · recipient

ADR 0039 — the participation-role distinction. OPTIONAL, WITH NO DEFAULT: a family whose participations play one undifferentiated role (official-result, sponsored-funding, forecasts, community-participation) simply never sets this field, and omission asserts nothing about whether that participation paid into the container — declaring a default here would misdescribe those families' non-paying, free-entry seated participants as payers by default, exactly the false assertion ADR 0039 exists to avoid. `contributor`: this participation funded the container's pool. `recipient`: this participation did not fund the pool and may instead receive value the mechanism assigns to it — `sortition-funding`'s qualified-proposal recipient (`03-product-model.md` §5.2) is the motivating case, and `allocation.schema.json`'s `recipient_participation_id` is expected to resolve to a `recipient`-kind participation for that family, never to a `contributor`-kind one, though this schema enforces no cross-document check. BOTH KINDS MAY COEXIST IN ONE CONTAINER: this is a per-participation leaf property, never a per-container setting, because `sortition-funding`'s own pool holds contributing participants and qualified-proposal recipients side by side — that coexistence is exactly what invariant 3's 2026-08-14 qualification (`03` §5.2) names. THIS DOES NOT REACH ADR 0015 §5's closed, seven-item crossing-authority set: `Allocation` still discharges 'to a participant, from `OBLIGATION_PARTICIPANT`' (ADR 0015:100) exactly as written — this field only names which role a given `Participation` record plays; it neither widens, renames nor adds to the authority set, and `allocation.schema.json`'s own description already distinguishes the record-level `Allocation` entity from ADR 0015's crossing authority for the identical reason. NOT A REGULATORY CLAIM: this field records a mechanical fact — who paid in and who did not — and settles no question about whether any family is a chance-prize product in any jurisdiction; see `03` §5.2's own 'Regulatory character' paragraph and ADR 0012's classification-memo gate, which this field neither answers nor forecloses.

signaturestring | null

Optional Ed25519 signature (hex, 64 bytes) by the participant over canonical(wave_id || participant_pseudonym || seated_at) — updated from canonical(wave_id || participant_id || seated_at) now that `participant_id` no longer exists as a field (`01-platform-architecture.md` §4.3 change 5). When present, the open record can verify the participation was authorised by the participant's key. OPEN — RECORDED, NOT RESOLVED: change 5 names "any durable signature" as in scope, not only the literal `participant_id` field. A signature verified against a participant's own STABLE, reused Ed25519 key would itself remain a durable, cross-participation-correlatable signal — regardless of this pseudonym fix — if the same key is reused across a participant's multiple participations. Whether the signing key itself must become per-instance too (e.g. a fresh keypair per participation) is a genuine open cryptographic-protocol design question this schema-shape change does not resolve. Flagged, citing `G-blueprint` (the "Core blueprint §11.4" authority this all traces to does not exist in this repository or its git history — `03-product-model.md`:611): key-management policy is `W8-E14`'s to decide (or a future epic's), not this one's. No key-rotation scheme is asserted here.

Outcome

What a wave resolves to. A defined share routes to a contribution.

GET/outcomes

What a wave resolves to. A defined share routes to a contribution. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/outcomes/{id}

What a wave resolves to. A defined share routes to a contribution.

Parameters

  • idpath · requiredThe outcome id.

Responses

Outcome

What a wave resolves to. A defined share routes to a contribution.

idstringrequired

Wave-scoped composite (03 §2 level 6 — an Outcome is genuinely container-bound, unlike Contribution.outcome_id, which W1-E11 deliberately decoupled). NO LONGER derived from, or equal to, any recipient_participation_id by convention (canon-defect-register.md D20, resolved by this epic): W1-E08's Allocation set already gives an Outcome many recipients, so no single recipient_participation_id is left for this id to be named after — the derivation convention the pre-Allocation design practised is gone, not merely undocumented.

wave_idstringrequired
recipient_participation_idstringrequired

The participation selected by the mechanism. The participant behind it can be looked up via the participation record.

mechanismobjectrequired

Snapshot of the mechanism state at the moment of settlement. Sufficient on its own — combined with the wave's participation list — for an independent verifier to reproduce the selection.

mechanism_idstringrequired
algorithmstringrequired
seed_revealedstringrequired
verifying_transcript_sha256string | null

Optional content hash of the verifying transcript, if one was published.

verifiedbooleanrequired

Whether at least one independent verifier has replayed the mechanism and matched the recipient. Outcomes are not considered fully settled until verified == true.

settled_atstringrequired

Contribution

The defined share routed to a cause, derived from an outcome.

GET/contributions

The defined share routed to a cause, derived from an outcome. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/contributions/{id}

The defined share routed to a cause, derived from an outcome.

Parameters

  • idpath · requiredThe contribution id.

Responses

Contribution

The defined share routed to a cause, derived from an outcome.

idstringrequired
outcome_idstringrequired

The outcome this transfer settles. NO PATTERN: this field previously carried the wave-scoped composite pattern (`^[0-9]{3,6}-[0-9A-HJKMNP-TV-Z]{4}$`), which made this money leg wave-shaped in the frozen bytes even though `01` §1.3 calls it primitive-agnostic — the defect `03` §1.4 diagnoses: "`contribution.schema.json:25` patterns `outcome_id` as the wave-scoped composite, so the money leg that `01` §1.3 calls primitive-agnostic is wave-shaped in the frozen bytes." ADR 0025 §7 confirms `W1-E11` acceptance 3 undoes that here, on `Allocation.outcome_id`'s own no-pattern precedent (`allocation.schema.json`). `minLength: 1` is strictly weaker than any pattern: it refuses only the empty string, which names no outcome, and freezes no id space.

cause_idstringrequired
amountobjectrequired
valuestringrequired

Decimal string. Stored as string to avoid floating-point precision loss.

currencystringrequired

ISO-4217 alphabetic code, or a supported crypto ticker (e.g. USDC, ETH). The supported crypto set is defined at the application layer (@veraq/i18n CRYPTO); other pattern-valid codes are not rejected here. `01` §4.3 change 4 ('upstream the crypto currency pattern') is resolved by this field ALREADY carrying the correct, product-matching pattern — no schema edit was needed here. `packages/contracts/upstream/schemas/contribution.schema.json` is deliberately left untouched and still shows the older `^[A-Z]{3}$` shape: per its own `MANIFEST.json` ('This file DECIDES NOTHING... a refresh is a claim about what the world serves and should be made by a person'), that file is a dated, human-attested capture of a real historical commit, not a mirror to keep in sync with local fixes — editing it without a genuine new capture would misrepresent history. The resulting VENDORED divergence `tooling/ci/contracts-drift-check.mjs` reports for this field is therefore permanent and expected, not a residual defect; `G-Q5` (ADR 0029: satellite repos archived) governs whether that snapshot is ever regenerated, and this schema-authoring epic does not pre-empt it.

transferred_atstring | null

When the transfer was confirmed. Null while pending.

proof_urlstring | null

Optional URL to a third-party transfer proof — bank receipt, on-chain transaction, etc.

Cause

A verified public-good recipient of contributions chosen by participants.

GET/causes

A verified public-good recipient of contributions chosen by participants. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/causes/{id}

A verified public-good recipient of contributions chosen by participants.

Parameters

  • idpath · requiredThe cause id.

Responses

Cause

A verified public-good recipient of contributions chosen by participants.

idstringrequired

Stable slug. Used wherever a cause is referenced.

namestringrequired
descriptionstringrequired
verificationobjectrequired

How this cause was verified as eligible to receive charitable share. The brand promise requires this to be inspectable.

methodenumrequiredregistry · manual-review · third-party-attestation

How verification was performed.

registrystring | null

When method == 'registry', the registry identifier (e.g., 'gs1-charities-uk:1148231').

verified_atstringrequired
verifierstringrequired

Name or identifier of the verifying party.

evidence_urlstring | null
payout_addressobject

Where the contribution is transferred — now the payout-rail discriminated union `payout-rail.schema.json` defines (`01-platform-architecture.md` §4.3 change 3, driven by D5, ADR 0012; `04` W4-E02), rather than opaque free text. `01` §4.3 change 3's own defect text: 'Nullable free text, mixing escrow handles, IBANs and `0x` addresses — three incompatible rails, no discriminator.' `$defs.payoutRail` above is a byte-for-byte copy of `payout-rail.schema.json`'s own `oneOf` — `cause.schema.json` is a vendored schema and this package's ajv setup compiles each schema independently with no cross-file `$ref` registration (no `ajv.addSchema` linking files together), so the only reuse mechanism available here is verbatim duplication into a local `$defs` entry, proven never to silently diverge by this schema's own structural test in `cause.test.mjs` (`schemas.cause.$defs.payoutRail.oneOf` deep-equals `schemas.payout_rail.oneOf`). `null` remains valid — unchanged from this field's prior nullability, meaning 'not yet set.' Any non-null value must now match exactly one of the three named, `kind`-discriminated rails: `bank_iban`, `crypto_address`, or `escrow_handle`; the old bare free-text form no longer validates. Wired in HERE, in one commit with `01` §4.3's other five changes, because `04` W1-E11's own acceptance 5 requires exactly that sequencing.

operating_jurisdictionstringrequired

ADR 0031 Decision 3: '`Fund` and `Cause` gain a jurisdiction-linked, computed-allocation-input field at v1 — an actual schema field, not descriptive text — landing as part of `W1-E11`'s one-shot migration.' `02-ecosystem-architecture.md` §8, the Foundation Principle: 'The system records jurisdiction faithfully and makes jurisdiction-linked flows reportable... The system does not encode an allocation rule... The architecture supplies the evidence; the Foundation makes the decision.' THIS FIELD CARRIES NO ALLOCATION LOGIC ITSELF — ADR 0031 Consequences: 'No allocation-rule code exists yet... this is a decision, not an implementation.' A plain jurisdiction value, the same record-shape-not-computation separation this corpus draws everywhere (e.g. `reserve-obligation.schema.json`'s own top-level description: 'THIS IS A RECORD SHAPE, NOT A COMPUTATION'). Named `operating_jurisdiction`, deliberately NOT `source_jurisdiction` (`fund.schema.json`'s own field, ADR 0013 Decision 1) — `Cause` is destination-side while `Fund` is source-side (ADR 0028 Decision 4: Fund/Cause cardinality stays orthogonal), and this field creates no foreign key or cardinality relationship to `Fund`; each entity independently carries its own jurisdiction dimension. REQUIRED, not optional or nullable — matching this batch's established precedent of adding new required fields to existing vendored entities rather than leaving one that could silently go unpopulated (see `participant.schema.json`'s `verdify_identity_id`). ISO 3166-1 alpha-2 shape, the SAME pattern `fund.schema.json`'s `source_jurisdiction` uses. `G-jurisdictions` (which real markets are served) remains OPEN, so every real fixture uses an ISO 3166-1 user-assigned test code (e.g. `ZZ`), never a real market — matching `fund.schema.json`'s own fixtures' convention.

Allocation

One recipient's share of the participant-distributable class, reaching that recipient at the resolution of one outcome — a level-6 record entity (03 §2; ADR 0016 §1 spells the level out as 'the per-event record entities').

GET/allocations

One recipient's share of the participant-distributable class, reaching that recipient at the resolution of one outcome — a level-6 record entity (03 §2; ADR 0016 §1 spells the level out as 'the per-event record entities'). Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/allocations/{id}

One recipient's share of the participant-distributable class, reaching that recipient at the resolution of one outcome — a level-6 record entity (03 §2; ADR 0016 §1 spells the level out as 'the per-event record entities').

Parameters

  • idpath · requiredThe allocation id.

Responses

Allocation

One recipient's share of the participant-distributable class, reaching that recipient at the resolution of one outcome — a level-6 record entity (03 §2; ADR 0016 §1 spells the level out as 'the per-event record entities').

idstringrequired

Identity. NO PATTERN, on container.schema.json's precedent: no document fixes an id space for this leaf, and inventing one would freeze a byte before its gate. ITS IDENTITY IS INDEPENDENT OF THE RECIPIENT: an entry is not named after the participation it allocates to, and no description here says otherwise — which is the convention 04 W1-E08 acceptance 3 strikes, applied at the level this epic owns. The vendored `Outcome` still states the derivation for its own id; that sentence is 04 W1-E11's to remove, and ADR 0025 §3 records why it could not be removed here. NON-EMPTY: an empty string is not an identity, and a leaf naming nothing would validate while referring to no object. `minLength: 1` is strictly weaker than any pattern, so it freezes no id space — it only refuses the one value that can never be one.

outcome_idstringrequired

The outcome whose distribution this entry belongs to. MANY entries may name one outcome — that is the whole point, and it is what the singular `Outcome.recipient_participation_id` could not express. NO PATTERN, DELIBERATELY: 03 §1.4 diagnoses the defect a pattern would repeat — 'contribution.schema.json:25 patterns outcome_id as the wave-scoped composite, so the money leg that 01 §1.3 calls primitive-agnostic is wave-shaped in the frozen bytes. That leak happened BECAUSE THE CONTAINER HAD NO LEVEL, so its shape defaulted sideways into its neighbour.' 04 W1-E11 acceptance 3 has to undo that in `contribution`; committing it a second time here would double the migration. NON-EMPTY: an empty string is not an identity, and a leaf naming nothing would validate while referring to no object. `minLength: 1` is strictly weaker than any pattern, so it freezes no id space — it only refuses the one value that can never be one.

recipient_participation_idstringrequired

The participation this entry allocates to. NO PATTERN: participation.schema.json owns that id space and 04 W1-E11 owns the one-shot migration that may change it, so repeating the pattern here would put one piece of knowledge in two places and make the migration two edits. THE RECIPIENT IS A PARTICIPATION, NOT A PARTICIPANT — 01 §4.3 change 5 replaces participant identifiers in published entries with a per-instance pseudonym, and an allocation naming a participant directly would be a durable identifier in an anchored entry. NON-EMPTY: an empty string is not an identity, and a leaf naming nothing would validate while referring to no object. `minLength: 1` is strictly weaker than any pattern, so it freezes no id space — it only refuses the one value that can never be one.

amountAllocation_positiveQuantityrequired

The amount allocated, in the CONTAINER'S DECLARED UNIT — the same unit as that container's `target` (03 §1.3), resolved through this entry's outcome. No currency member, deliberately: the unit is declared once, by the container, and a per-entry currency would be a second place to say one thing AND would drag in contribution.schema.json's `^[A-Z]{3,4}$`, which is 01 §4.3 change 4 and blocked by G-Q5.

allocated_atAllocation_instantrequired

When this entry was recorded. Ordering against the container's resolution is not expressible in JSON Schema and is 04 W3-E09's, on the precedent container.schema.json set for `opens_at`.

Attestation

An independent party's report that it replayed a settled outcome and what it found — the open class 03 §5.5(b) names as the source of Outcome.verified: '.net's open attestation class, which anyone may publish and for which Veraq certifies nobody and pays nobody.' THE OPENNESS IS THE CONTROL, NOT A CONVENIENCE.

GET/attestations

An independent party's report that it replayed a settled outcome and what it found — the open class 03 §5.5(b) names as the source of Outcome.verified: '.net's open attestation class, which anyone may publish and for which Veraq certifies nobody and pays nobody.' THE OPENNESS IS THE CONTROL, NOT A CONVENIENCE. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/attestations/{id}

An independent party's report that it replayed a settled outcome and what it found — the open class 03 §5.5(b) names as the source of Outcome.verified: '.net's open attestation class, which anyone may publish and for which Veraq certifies nobody and pays nobody.' THE OPENNESS IS THE CONTROL, NOT A CONVENIENCE.

Parameters

  • idpath · requiredThe attestation id.

Responses

Attestation

An independent party's report that it replayed a settled outcome and what it found — the open class 03 §5.5(b) names as the source of Outcome.verified: '.net's open attestation class, which anyone may publish and for which Veraq certifies nobody and pays nobody.' THE OPENNESS IS THE CONTROL, NOT A CONVENIENCE.

idstringrequired

Identity. NO PATTERN, on container.schema.json's precedent: no document fixes an id space for this class, and inventing one would freeze a byte before its gate. NON-EMPTY, because an empty string is not an identity.

outcome_idstringrequired

The settled outcome this attestation reports on. NO PATTERN, DELIBERATELY: 03 §1.4 records that contribution.schema.json patterns `outcome_id` as the wave-scoped composite, so 'the money leg that 01 §1.3 calls primitive-agnostic is wave-shaped in the frozen bytes', and 04 W1-E11 acceptance 3 has to undo it there. Repeating it here would make that migration one file wider. NON-EMPTY, because an attestation of nothing is not an attestation.

attesterstringrequired

WHO REPLAYED IT — AN OPEN SET. Resolved against nothing: no pattern, no enum, no registry, no format. 03 §5.5(b): 'anyone may publish, and for which Veraq certifies nobody and pays nobody.' This is the exact inverse of the envelope's `publisher`, which specs/record-envelope-v1.md §2 makes registry-resolved and fail-closed; see this schema's own description for why the two do not share a name. `minLength: 1` is the ONLY bound and is not a narrowing of the set: an unnamed attester is not a party, and an attestation nobody made cannot be checked by anyone. A verifier reading this member learns who to go and ask, and learns nothing about whether Veraq agrees.

stateenumrequiredattested · pending · mismatch

What the attester found. `attested` — replayed and matched. `mismatch` — replayed and did not match. `pending` — has not finished. The three are 04 W1-E09 acceptance 1's, and NONE of them names Veraq or an endorsement, because the value set is as much a surface for an upward default as a member name is. A CLOSED enum rather than a registry: 03 §11's resolution discipline is for the SEVEN registry kinds, an eighth would fail tooling/ci/registry-artefact-check.mjs's seven-artefact rule, and 05 §11 forbids this epic adding a registry row. The cost of the closed set — 03 §1.3's 'a new enum member added after the first anchor invalidates every existing verifier's exhaustiveness check' — is real and recorded at ADR 0026 open item 1 rather than resolved here.

observed_digestobjectrequired

The transcript digest the attester actually computed — 03 §5.5's 'an attestation that a published algorithm on published inputs produced transcript digest D'. NULL while `pending`, present otherwise, and never absent: 03 §1.4 rules out an optional field in an anchored proof format, so a verifier reads a present null rather than guessing at an absence. It is the OBSERVED value only. The published value it is compared against is reached through `outcome_id` and is not copied here, because a figure with two homes in one record is a figure that can disagree with itself. The hex shape is stated ONCE, in `$defs/digest`, and reached here through a `oneOf` with null — the shape commitment.schema.json uses for `revealed_at`.

attested_atAttestation_instantrequired

When the attester recorded this report. Ordering against the outcome's settlement is not expressible in JSON Schema and belongs to the verification pipeline, on the precedent container.schema.json set for `opens_at`.

Challenge

A dispute raised against a container's settlement — 03 §2 level 6's sixth and last leaf ('Participation, Outcome, Allocation, Contribution, Funding, Challenge'), never built until 04 W3-E13.

GET/challenges

A dispute raised against a container's settlement — 03 §2 level 6's sixth and last leaf ('Participation, Outcome, Allocation, Contribution, Funding, Challenge'), never built until 04 W3-E13. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/challenges/{id}

A dispute raised against a container's settlement — 03 §2 level 6's sixth and last leaf ('Participation, Outcome, Allocation, Contribution, Funding, Challenge'), never built until 04 W3-E13.

Parameters

  • idpath · requiredThe challenge id.

Responses

Challenge

A dispute raised against a container's settlement — 03 §2 level 6's sixth and last leaf ('Participation, Outcome, Allocation, Contribution, Funding, Challenge'), never built until 04 W3-E13.

idstringrequired

Identity. NO PATTERN, on `container.schema.json`/`attestation.schema.json`'s shared precedent: no document fixes an id space for this class, and inventing one would freeze a byte ahead of its own gate.

container_idstringrequired

The container this challenge is raised against — container-generic from the start (`03` §1.2's container level, ADR 0023), never a family-scoped id space. Deliberately NOT `wave_id`: `participation.schema.json`'s own inherited mismatch (a Wave-only id pattern at a container-generic entity) is a defect this schema, authored after the container level already existed, has no reason to repeat.

statusenumrequiredraised · upheld · dismissed

`raised` — filed, undecided. `upheld` — the challenge stands; `03` §5.1's own reward-model line names exactly this state ('surviving void, an empty match set and an upheld challenge'), and it is what `apps/io`'s `provisional -> final` gate checks for (04 W3-E13 criterion 3). `dismissed` — the challenge does not stand; final is unaffected. A CLOSED enum, on `attestation.schema.json`'s own precedent for the identical reason: `03` §11's registry discipline is for the seven registry KINDS, and adding an eighth here would fail `tooling/ci/registry-artefact-check.mjs`'s seven-artefact rule for no corpus-named benefit — this is a small, closed, non-extensible outcome set, not a membership `03` §11 ever asks to keep open. This epic does not build the adjudication workflow that moves a Challenge from `raised` to `upheld`/`dismissed` — out of scope, `04` W3-E13's own acceptance criteria test only that an ALREADY-`upheld` challenge blocks `final`, never how one comes to be upheld.

reasonstring | null

The challenger's stated grounds. Optional and nullable — no acceptance criterion of `04` W3-E13 requires a Challenge's own reason to be present (contrast `container.schema.json`'s forthcoming void-reason requirement, `03` §6.2, which is a DIFFERENT record — the container's own `void_reason` column, `tooling/io/migrations/0004_challenge_window.sql` — not this entity). Carried here anyway, matching every other leaf's own practice of recording why, because a Challenge with no stated ground is harder to adjudicate honestly, not because a numbered criterion requires it.

raised_atChallenge_instantrequired

When the challenge was filed. Ordering against the container's own `provisional_at` (when its challenge window began counting, `tooling/io/migrations/0004_challenge_window.sql`) is not expressible in JSON Schema and belongs to the write path, on `container.schema.json`'s own precedent for `opens_at`.

Commitment

What was pinned before a container opened, per container, discriminated on verification form.

GET/commitments

What was pinned before a container opened, per container, discriminated on verification form. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/commitments/{id}

What was pinned before a container opened, per container, discriminated on verification form.

Parameters

  • idpath · requiredThe commitment id.

Responses

Commitment

What was pinned before a container opened, per container, discriminated on verification form.

SealedCommitment

03 §4: the determining input is a secret committed before opening and revealed after. Carries the mechanism state 03 §1.2 moves out of Wave — the seed commitment, the revealed seed, the reveal time and the transcript. The trust statement is 03 §4's and lives there, not here. THE REVEAL IS ATOMIC: `seed_revealed` and `revealed_at` are null together or present together, which is the invariant mechanism.schema.json enforced before these members moved and which a move must not drop. A `revealed_at` with a null `seed_revealed` would record a reveal that revealed nothing, and a verifier replaying ALG(secret, params, inputs) — 03 §4's sealed action — would have a time and no secret.

idstringrequired

Identity.

container_idstringrequired

The container this commitment belongs to. 03 §1.2: a PER-CONTAINER Commitment.

formobjectrequired
published_atCommitment_instantrequired

When the commitment was published. Must be STRICTLY EARLIER than the container's `opens_at` (03 §1.3; 04 W3-E09 tests the equal-timestamp boundary). The comparison is not expressible in JSON Schema and is W3-E09's.

seed_commitmentCommitment_digestrequired

The binding commitment published before the container opens. Never null: it is the thing being committed, and a sealed commitment without one commits nothing.

seed_revealedstring | nullrequired

Lowercase hex of the revealed seed. NULL until the reveal, present after — never absent, per 03 §1.4.

revealed_atobjectrequired

When the seed was revealed. Null until then.

transcript_digestCommitment_nullableDigestrequired

Digest of the verifying transcript, once one exists. A DIGEST and not a URL: a Commitment is a proof artefact, and a URL commits to a location rather than to bytes.

ExternalCommitment

03 §4: the determining input is a fact published by an independent authority. THIS BRANCH IS DELIBERATELY MINIMAL. 04 W1-E07 owns the external-form record shapes and the rest of 03 §5.1's committed parameters — the observation window, the fallback rule (void | extend | substitute_none), the evidence digest algorithm and the observer set — alongside ResultSource and ResultObservation. What is fixed here is the one member W1-E07 acceptance 4 already names: the source is carried BY DIGEST REFERENCE and never inline.

idstringrequired

Identity.

container_idstringrequired

The container this commitment belongs to. 03 §1.2: a PER-CONTAINER Commitment.

formobjectrequired
published_atCommitment_instantrequired

When the commitment was published. 02 §7 requires the commitment to predate opening, and a third party confirms it via the anchor (03 §5.1).

result_source_digestCommitment_digestrequired

Digest of the committed ResultSource. 04 W1-E07 acceptance 4: the external branch accepts a digest reference and REJECTS an inline source — pinning the source by its bytes is what defeats source substitution (02 §7.1).

observer_set_digestCommitment_nullableDigestrequired

Digest of the committed observer set (03 §5.1's own list of parameters committed before opening names 'the observer set' alongside source authority, publication URL, field, format, revision policy, observation window, fallback rule and evidence digest algorithm) — the SAME digest-reference convention result_source_digest already uses on this branch, never an inline array. Computed as sha256_hex(canonical(observer_set sorted ascending)) — sorted because this is a SET of observers, not an ordered sequence, so two callers naming the identical set in a different order commit to the identical digest; this ordering rule is fixed by 04 W3-E10's own verifier (apps/io/src/observations/pipeline.ts), not by any corpus document. NULLABLE, NOT OPTIONAL, on `transcript_digest`'s own precedent above and 03 §1.4's own rule this file's test suite enforces mechanically (a Commitment branch may declare no optional member): this member was deferred at 04 W1-E07 (this branch's own description above: 'the rest of 03 §5.1's committed parameters ... alongside ResultSource and ResultObservation') and no external-form Commitment already on record before this fix carried one, so `null` is what every such Commitment reads back as, honestly, rather than omitting the member and inviting the absence 03 §1.4 forbids. 04 W3-E10's own capture pipeline is what actually depends on this value being genuinely committed rather than trusted off a request body (criterion 4's real guarantee, an adversarial-review fix), and enforces it as REQUIRED-NON-NULL at the one write path that matters — apps/io refuses to capture at all when a container's external Commitment carries a null observer_set_digest (E_OBSERVER_SET_NOT_COMMITTED), never silently falling back to an unverified caller-supplied array.

Container

A cohort with a freeze predicate.

GET/containers

A cohort with a freeze predicate. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/containers/{id}

A cohort with a freeze predicate.

Parameters

  • idpath · requiredThe container id.

Responses

Container

A cohort with a freeze predicate.

idstringrequired

Identity. NO PATTERN: 03 §1.2 preserves Wave's own id space ('Wave keeps its name, its id space and its app destination'), which is `^[0-9]{3,6}$`, while Pool's is unfixed — so a shared pattern would either break the one the corpus preserves or invent the other.

product_idstringrequired

Provenance: the product this container belongs to. Opaque and NOT registry-resolved (record-envelope-v1.md §2, corrected 2026-08-05). The kill switch is specified at product_id granularity (03 §8.4), and 04 W3-E04 requires killing a product to prevent its containers reaching `open` — which is why the container must carry it.

opens_atContainer_instantrequired

The start. A container is immutable after `opens_at` (03 §2 level 5), and its Commitment must be timestamped STRICTLY EARLIER (03 §1.3; 04 W3-E09 tests the equal-timestamp boundary).

closes_onobjectrequired

The freeze predicate, as a DISCRIMINATED OBJECT and never a shape enum. 03 §1.3: 'A new enum member added after the first anchor invalidates every existing verifier's exhaustiveness check. A new keyed branch is purely additive — if and only if verifiers are required to fail closed on an unknown kind.' Two members ship and no third is named; a third (continuous participation) is 03 §9 item 5 and 04 §12, deliberately unscheduled. Both branches are closed, so a document mixing their parameters matches neither.

statusenumrequiredannounced · open · closed · observing · provisional · final · void · cancelled

The container's state. 03 §1.2 keeps a status in the shared container, so the member is REQUIRED — 03 §1.4 rules out an optional field in an anchored proof format. THE VALUE SET LANDS HERE, per ADR 0023 §5's own delegation ('The enum lands there, with the transitions, in one change'): the six main-sequence states 03 §5.1 names for the `official-result` family (announced, open, closed, observing, provisional, final), promoted to the CONTAINER level by 04 W3-E09 as shared vocabulary — `observing` is harmless for a non-`external` container, which simply never visits it — plus `void`/`cancelled`, the two exits that same section names. 04 W3-E09 owns the transition machine (`apps/io/src/lifecycle.ts`, `tooling/io/migrations/0002_containers.sql`) and its own dedicated table-driven test enumerates every ordered pair of the SIX main-sequence states; `void`/`cancelled` reachability is a separate, explicitly flagged, provisional design choice — see that epic's own report.

termsobjectrequired

A digest-bound snapshot of the product manifest, and THE ONLY PLACE a regulated fact may live (03 §1.2). Open on purpose: its members are the product's — the partition rule, the assistance binding, share origin, funded_by, the cause class — and several are blocked on counsel or on an owner decision. What is fixed today is `value_bearing`, plus `challenge_window` (04 W3-E13). Note that a `terms` carrying a value outside canonical-form-v1.md §2's domain has no canonical form, so its `terms_digest` cannot be computed; that is enforced by Rule D4's computation refusing, not by this schema, which is schema-blind to the distinction.

value_bearingbooleanrequired

True iff money moves through the container, regardless of who pays it in (03 §2). The estate's real gate boundary — per container, not per family — and the flag `standing` may not be declared against.

challenge_windowstring

The product's binding-5 resolution policy (`03` §3: 'challenge window, dispute route, void rule, and the maximum float duration'), snapshotted in ALONGSIDE `value_bearing` — the same 'digest-bound snapshot of the product manifest' the top-level description names, not a second mechanism. `03` §3.1: 'A declared challenge window. Settlement is provisional until it closes' — `apps/io`'s `provisional -> final` transition trigger (`tooling/io/migrations/0004_challenge_window.sql`, 04 W3-E13 criterion 2) reads exactly this member, cast to a real Postgres INTERVAL, and refuses to gate a container that lacks one rather than silently treating an absent window as `provisional -> final` remaining unconditional. NOT REQUIRED at this schema — deliberately: `04` W3-E09's own container fixtures (and every other epic's, across the monorepo) predate this member and construct `terms: {value_bearing: false}` alone; making it required here would retroactively invalidate every one of those, which is not this epic's `.io + contracts`-scoped job. The gate this member exists to serve is enforced at the point that actually needs it (the storage-layer trigger, at the moment `final` is attempted), not by widening this schema's own `required` list estate-wide. ISO 8601 DURATION, matching `packages/product-registry/src/manifest.ts`'s `ResolutionPolicyInput.challenge_window` (04 W3-E03's own pre-existing, previously-unenforced fixture convention — `P7D`, `P30D`, `P90D` already appear in that package's own test fixtures) — SAME PATTERN TEXT as `packages/product-registry/src/duration.ts`'s `ISO8601_DURATION_PATTERN`, cross-referenced rather than silently re-derived, so the two do not drift into accepting different strings for the identical concept. A known, flagged simplification: this pattern does not enforce ISO 8601's rule that a week designator (`W`) may not combine with `Y`/`M`/`D` in the same duration — every fixture and every real product declares day-only durations (`P7D`), so the simplification costs nothing today and is recorded here rather than silently assumed correct for every future value.

terms_digeststringrequired

canonical-form-v1.md Rule D4: SHA-256(utf8(canonicalForm(terms))) over the `terms` object ALONE — no wrapper, no key added, no reference to the product manifest included — rendered per Rule D3 as lowercase unprefixed hexadecimal. The partition rule and the assistance binding are bound BY INCLUSION, as members of `terms`, so neither needs a separate digest.

commitment_idstringrequired

The per-container Commitment (03 §1.2) — where the mechanism state that left Wave now lives, `oneOf` on verification form. Built by 04 W1-E06; this is the reference, not the object.

ContainerClassTotal

A container's open-record total for one obligation class — the shape `04` W4-E10's own design artefact (docs/ops/foundation-principle-reporting-dimension.md:82-93) specifies for `03` §7.3 rule 2's first half: 'Per-container class totals are open-record entries.' ADR 0015 §6 states the same rule in its own words: 'Per-container class totals are Open Record entries.

GET/containerclasstotals

A container's open-record total for one obligation class — the shape `04` W4-E10's own design artefact (docs/ops/foundation-principle-reporting-dimension.md:82-93) specifies for `03` §7.3 rule 2's first half: 'Per-container class totals are open-record entries.' ADR 0015 §6 states the same rule in its own words: 'Per-container class totals are Open Record entries. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/containerclasstotals/{id}

A container's open-record total for one obligation class — the shape `04` W4-E10's own design artefact (docs/ops/foundation-principle-reporting-dimension.md:82-93) specifies for `03` §7.3 rule 2's first half: 'Per-container class totals are open-record entries.' ADR 0015 §6 states the same rule in its own words: 'Per-container class totals are Open Record entries.

Parameters

  • idpath · requiredThe containerclasstotal id.

Responses

ContainerClassTotal

A container's open-record total for one obligation class — the shape `04` W4-E10's own design artefact (docs/ops/foundation-principle-reporting-dimension.md:82-93) specifies for `03` §7.3 rule 2's first half: 'Per-container class totals are open-record entries.' ADR 0015 §6 states the same rule in its own words: 'Per-container class totals are Open Record entries.

container_idstringrequired

The container this total is computed over. NO PATTERN, on container.schema.json's and funding.schema.json's precedent: no document fixes an id space for a container reference here. NON-EMPTY: an empty string is not an identity, and a total naming no container belongs to no container's report.

class_idstringrequired

One of ADR 0015 §4's seven segregation domains, or a policy-extensible obligation class mapping onto one — the design doc's own wording (docs/ops/foundation-principle-reporting-dimension.md:89). NO ENUM, deliberately: the design doc's own description already states the set is policy-extensible, and `03` §11's registry-resolution discipline is reserved for the SEVEN registry kinds — an enum here would freeze a membership the design doc itself declares open. NON-EMPTY, on the same reasoning as `container_id`.

total_amountContainerClassTotal_nonNegativeAmountrequired

The container's total for this class, in the container's declared unit (paired with `currency` below, the way `03` §1.3 pairs a Funding `amount` with a Container's declared unit). Decimal string, per canonical-form-v1.md Rule N4 — never a float (docs/ops/foundation-principle-reporting-dimension.md:90).

currencystringrequired

ISO-4217 alphabetic code, or a supported crypto ticker — the identical spelling contribution.schema.json's `amount.currency` uses (`^[A-Z]{3,4}$`), reused rather than reinvented so one pattern governs one meaning across the corpus. The supported crypto set is defined at the application layer (`@veraq/i18n` `CRYPTO`); other pattern-valid codes are not rejected here.

Fund

A long-lived economic, governance and accounting boundary anchored to a jurisdiction — ADR 0028 Decision 1, answering `01` §8 Q14 ('What is a Fund?'), OPEN from ADR 0013 (2026-08-02) until ADR 0028 (2026-08-11).

GET/funds

A long-lived economic, governance and accounting boundary anchored to a jurisdiction — ADR 0028 Decision 1, answering `01` §8 Q14 ('What is a Fund?'), OPEN from ADR 0013 (2026-08-02) until ADR 0028 (2026-08-11). Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/funds/{id}

A long-lived economic, governance and accounting boundary anchored to a jurisdiction — ADR 0028 Decision 1, answering `01` §8 Q14 ('What is a Fund?'), OPEN from ADR 0013 (2026-08-02) until ADR 0028 (2026-08-11).

Parameters

  • idpath · requiredThe fund id.

Responses

Fund

A long-lived economic, governance and accounting boundary anchored to a jurisdiction — ADR 0028 Decision 1, answering `01` §8 Q14 ('What is a Fund?'), OPEN from ADR 0013 (2026-08-02) until ADR 0028 (2026-08-11).

idstringrequired

Identity. NO PATTERN: ADR 0028's Consequences say a Fund is 'keyed by its own id (e.g. a ULID)' — an illustrative example, not a mandate, exactly as funding.schema.json's own `id` reasons: 'no document fixes an id space for this leaf, and inventing one would freeze a byte before its gate.' NON-EMPTY: an empty string is not an identity, and a Fund naming nothing would validate while referring to no object.

source_jurisdictionstringrequired

ISO 3166-1 alpha-2 — the one shape the corpus already fixes for a jurisdiction value: `04`:236 tests `01` §8 Q1 against '≥1 market by ISO 3166-1 alpha-2'. ADR 0013 Decision 1: 'Fund becomes a first-class Core entity carrying source_jurisdiction. Every inflow inherits its classification from the Fund it lands in.' `G-jurisdictions` — 'the named target market set' — is OPEN today; no repository names one. This field fixes SHAPE only, never a real value: every fixture in this corpus uses an ISO 3166-1 user-assigned code (e.g. `ZZ`), reserved by the standard for exactly this — a value that validates without naming a target market this schema-authoring epic is not authorised to name. DUAL ROLE, per ADR 0031 Decision 3 (answering `G-FD6`, 2026-08-11): this field satisfies ADR 0031's requirement that `Fund` 'gain a jurisdiction-linked, computed-allocation-input field at v1' — it is not only a reported dimension (`04` W4-E10's own scope) but also the computed *input* Decision 1 activates for how a `ReserveObligation`'s disposition is allocated. No SECOND field is added for this: `source_jurisdiction` already carries the one fact both roles need, and duplicating it would put one piece of knowledge in two places (the same reasoning `source_fund_id`'s own description gives for not restating `Fund`'s fields elsewhere). ADR 0031's own Consequences are explicit that this is 'a decision, not an implementation' — no allocation-rule code exists yet, and none is authorised by this field's presence.

creation_tierenumrequireddefault · additional

Which of ADR 0028 Decision 7's two creation tracks this Fund was created on. `default`: 'A jurisdiction's first Fund is created by the operator, once, at jurisdiction onboarding.' `additional`: 'Any additional Fund in an already-served jurisdiction requires an owner-recorded governance reason before it may be created... following an admission discipline analogous to 03 §11's before-code gate.' A Fund is never created by a container, a product, or a participant action (Decision 7's own closing sentence). CLOSED to these two, because Decision 7 names exactly two tracks and no document names a third.

governance_reason_digeststring | nullrequired

The recorded governance reason for an `additional` Fund, by digest reference — a SHA-256 digest in canonical-form-v1.md Rule D3's rendering (the spelling commitment.schema.json and attestation.schema.json both use for the same kind of value), or NULL for a `default` Fund, because Decision 7 gives the jurisdiction's first Fund no reason to record (it coincides with jurisdiction onboarding, an operational event ADR 0013 already assumes) — PRESENT for an `additional` Fund, whose creation 'requires an owner-recorded governance reason BEFORE it may be created.' Present-but-null rather than absent, on record-envelope-v1.md's and attestation.schema.json's shared convention: 03 §1.4 rules out an optional field in an anchored proof format, so a verifier reads a present null instead of guessing at an absence. The pairing is enforced structurally by this schema's top-level `if`/`then`/`else`, on attestation.schema.json's own precedent for its `pending`/`observed_digest` pairing — never left to review. `pattern` applies only where `type` resolves to `string`, per JSON Schema's own per-keyword type scoping, so it constrains the digest form without rejecting `null`.

Funding

One inflow recorded against a container — a level-6 record entity (03 §2), and the moving input a threshold container's freeze predicate is read against.

GET/fundings

One inflow recorded against a container — a level-6 record entity (03 §2), and the moving input a threshold container's freeze predicate is read against. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/fundings/{id}

One inflow recorded against a container — a level-6 record entity (03 §2), and the moving input a threshold container's freeze predicate is read against.

Parameters

  • idpath · requiredThe funding id.

Responses

Funding

One inflow recorded against a container — a level-6 record entity (03 §2), and the moving input a threshold container's freeze predicate is read against.

idstringrequired

Identity. NO PATTERN, on container.schema.json's precedent: no document fixes an id space for this leaf, and inventing one would freeze a byte before its gate. NON-EMPTY: an empty string is not an identity, and a leaf naming nothing would validate while referring to no object. `minLength: 1` is strictly weaker than any pattern, so it freezes no id space — it only refuses the one value that can never be one.

container_idstringrequired

The container this inflow is recorded against, and therefore the container whose `raised` it projects into. REQUIRED: 03 §1.3 makes the projection per-container, and an entry that names no container belongs to no projection and can be counted into any of them. NO PATTERN — container.schema.json carries none, because 03 §1.2 preserves Wave's id space while a Pool's is unfixed. NON-EMPTY: an empty string is not an identity, and a leaf naming nothing would validate while referring to no object. `minLength: 1` is strictly weaker than any pattern, so it freezes no id space — it only refuses the one value that can never be one.

amountFunding_positiveQuantityrequired

The amount, in the CONTAINER'S DECLARED UNIT — the same unit as that container's `closes_on.target` (03 §1.3: 'a quantity in the container's declared unit'). No currency member, deliberately: the unit is declared once, by the container, and a per-entry currency would be a second place to say one thing AND would drag in contribution.schema.json's `^[A-Z]{3,4}$`, which is 01 §4.3 change 4 and blocked by G-Q5.

funded_atFunding_instantrequired

When the inflow was recorded. The threshold predicate reads the entries whose `funded_at` falls inside the container's window; that ordering is not expressible in JSON Schema and is 04 W3-E09's, on the precedent container.schema.json set for `opens_at`.

JurisdictionBalanceCommitment

Control-ledger only, per `03` §7.3 rule 2's second half and ADR 0015 §6, same rule — 'Per-jurisdiction balances remain control-ledger, anchored as digests only.' The shape `04` W4-E10's own design artefact specifies (docs/ops/foundation-principle-reporting-dimension.md:94-104).

GET/jurisdictionbalancecommitments

Control-ledger only, per `03` §7.3 rule 2's second half and ADR 0015 §6, same rule — 'Per-jurisdiction balances remain control-ledger, anchored as digests only.' The shape `04` W4-E10's own design artefact specifies (docs/ops/foundation-principle-reporting-dimension.md:94-104). Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/jurisdictionbalancecommitments/{id}

Control-ledger only, per `03` §7.3 rule 2's second half and ADR 0015 §6, same rule — 'Per-jurisdiction balances remain control-ledger, anchored as digests only.' The shape `04` W4-E10's own design artefact specifies (docs/ops/foundation-principle-reporting-dimension.md:94-104).

Parameters

  • idpath · requiredThe jurisdictionbalancecommitment id.

Responses

JurisdictionBalanceCommitment

Control-ledger only, per `03` §7.3 rule 2's second half and ADR 0015 §6, same rule — 'Per-jurisdiction balances remain control-ledger, anchored as digests only.' The shape `04` W4-E10's own design artefact specifies (docs/ops/foundation-principle-reporting-dimension.md:94-104).

container_idstringrequired

The container whose per-jurisdiction breakdown this digest commits to. NO PATTERN, NON-EMPTY, on `container-class-total.schema.json`'s precedent — the same container a `ContainerClassTotal` for the same `class_id` totals without a jurisdiction breakdown.

class_idstringrequired

The obligation class this per-jurisdiction breakdown belongs to — the same open, policy-extensible set `container-class-total.schema.json`'s `class_id` names (ADR 0015 §4's seven segregation domains, or a policy-extensible mapping onto one). NO ENUM, on the same reasoning: the membership is not this schema's to freeze.

jurisdiction_balances_digestJurisdictionBalanceCommitment_digestrequired

SHA-256 hex digest over the per-jurisdiction breakdown, per canonical-form-v1.md Rule D2's computation and Rule D3's rendering (docs/ops/foundation-principle-reporting-dimension.md:102). The breakdown itself stays control-ledger — THIS FIELD IS THE ONLY MEMBER of this schema that carries jurisdiction-linked information, and it carries it as an opaque committed digest, never as a raw jurisdiction value, a balance keyed by jurisdiction, or any structure a reader could invert into one. `additionalProperties: false` plus this `required` list is what makes 'anchored as digests only' (ADR 0015 §6) a fact this schema cannot violate, rather than a sentence a future author could work around by adding a member.

ParticipationPartitionReport

Per participation, per `03` §7.3 rule 3 and ADR 0015 §6, same rule — 'Per participation, only `partition_rule_digest` is published, openable by the holder — because publishing the applied `(product, jurisdiction)` pair would publish the participant's jurisdiction.' The shape `04` W4-E10's own design artefact specifies (docs/ops/foundation-principle-reporting-dimension.md:105-114).

GET/participationpartitionreports

Per participation, per `03` §7.3 rule 3 and ADR 0015 §6, same rule — 'Per participation, only `partition_rule_digest` is published, openable by the holder — because publishing the applied `(product, jurisdiction)` pair would publish the participant's jurisdiction.' The shape `04` W4-E10's own design artefact specifies (docs/ops/foundation-principle-reporting-dimension.md:105-114). Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/participationpartitionreports/{id}

Per participation, per `03` §7.3 rule 3 and ADR 0015 §6, same rule — 'Per participation, only `partition_rule_digest` is published, openable by the holder — because publishing the applied `(product, jurisdiction)` pair would publish the participant's jurisdiction.' The shape `04` W4-E10's own design artefact specifies (docs/ops/foundation-principle-reporting-dimension.md:105-114).

Parameters

  • idpath · requiredThe participationpartitionreport id.

Responses

ParticipationPartitionReport

Per participation, per `03` §7.3 rule 3 and ADR 0015 §6, same rule — 'Per participation, only `partition_rule_digest` is published, openable by the holder — because publishing the applied `(product, jurisdiction)` pair would publish the participant's jurisdiction.' The shape `04` W4-E10's own design artefact specifies (docs/ops/foundation-principle-reporting-dimension.md:105-114).

participation_idstringrequired

The participation this partition-rule digest is published for. NO PATTERN — participation.schema.json's own `id` carries none either. NON-EMPTY: an empty string is not an identity, and a report naming no participation belongs to no participation's report. NOT a `participant_id`: `03` §7.3 rule 3 makes jurisdiction-linked reporting a property of the PARTICIPATION, never of the PARTICIPANT — see this schema's own top-level description.

partition_rule_digestParticipationPartitionReport_digestrequired

The digest of the published, versioned partition rule applied to this participation (ADR 0015 §6: 'a required member of the Product manifest… digest-bound into `Container.terms_digest`'), openable by the holder. THIS IS THE ONLY MEMBER of this schema — the applied `(product, jurisdiction)` pair is never published beside it, because doing so would publish the participant's jurisdiction (`03` §7.3 rule 3, quoted verbatim in this schema's top-level description). `additionalProperties: false` plus a two-member `required` list is what makes that non-publication a fact this schema cannot violate, rather than a sentence a future author could work around by adding a member.

PayoutRail

A discriminated payout-rail taxonomy.

GET/payoutrails

A discriminated payout-rail taxonomy. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/payoutrails/{id}

A discriminated payout-rail taxonomy.

Parameters

  • idpath · requiredThe payoutrail id.

Responses

PayoutRail

A discriminated payout-rail taxonomy.

BankIbanRail

kindobjectrequired
ibanstringrequired

ISO 13616 structure: two-letter country code, two check digits, 11-30 alphanumeric BBAN characters — the real range across every current IBAN country, from Norway's 11-character BBAN (15 total) to the longest at 30 (34 total). A structural check only — no mod-97 checksum arithmetic, matching this schema's other patterns (e.g. record-envelope-v1.md's instant pattern), which fix shape and defer deeper arithmetic to the validating application.

CryptoAddressRail

kindobjectrequired
addressstringrequired

A 20-byte hex address, 0x-prefixed — the exact shape `01` §4.3 change 3 names as one of the three mixed-in rails today. SCOPE: this branch admits EVM-shaped addresses only. `01` §4.3 change 3 names only `0x` addresses for this field; it does not name base58 chains (e.g. Bitcoin, Solana). A non-EVM crypto payout rail is a new branch this schema does not yet declare, not a value this branch should be widened to accept.

EscrowHandleRail

kindobjectrequired
handlestringrequired

Deliberately opaque, matching `cause.schema.json`'s existing description of the field this taxonomy discriminates — a third-party escrow reference, not validated beyond non-empty.

PolicyBinding

`04` W4-E06 — `03` §3 binding 6, verbatim: 'eligibility, jurisdiction, age, limits.' `02` §4: 'Policy: `.io` executes, `.org` binds via `PolicyBinding`' — this is that binding, real for the first time (`02` §4 previously filed governance binding as 'currently mis-filed as a future E1 plugin').

GET/policybindings

`04` W4-E06 — `03` §3 binding 6, verbatim: 'eligibility, jurisdiction, age, limits.' `02` §4: 'Policy: `.io` executes, `.org` binds via `PolicyBinding`' — this is that binding, real for the first time (`02` §4 previously filed governance binding as 'currently mis-filed as a future E1 plugin'). Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/policybindings/{id}

`04` W4-E06 — `03` §3 binding 6, verbatim: 'eligibility, jurisdiction, age, limits.' `02` §4: 'Policy: `.io` executes, `.org` binds via `PolicyBinding`' — this is that binding, real for the first time (`02` §4 previously filed governance binding as 'currently mis-filed as a future E1 plugin').

Parameters

  • idpath · requiredThe policybinding id.

Responses

PolicyBinding

`04` W4-E06 — `03` §3 binding 6, verbatim: 'eligibility, jurisdiction, age, limits.' `02` §4: 'Policy: `.io` executes, `.org` binds via `PolicyBinding`' — this is that binding, real for the first time (`02` §4 previously filed governance binding as 'currently mis-filed as a future E1 plugin').

policy_binding_idstringrequired

Identity — the value a product manifest's binding 6 names as `policy_binding_ref` (`packages/product-registry/src/manifest.ts`'s `PolicyBindingInput.policy_binding_ref`, `04` W4-E06 criterion 1). NO PATTERN, on `fund.schema.json`'s own `id` reasoning: no document fixes an id space for this record, and inventing one would freeze a byte before its gate. NON-EMPTY: an empty string is not an identity.

marketstringrequired

ISO 3166-1 alpha-2 — the SAME shape `fund.schema.json`'s `source_jurisdiction` already fixes for a jurisdiction value, reused rather than reinvented. Unlike that field's own fixtures (which deliberately use the reserved `ZZ` code because `G-jurisdictions` named no real market when that schema was authored), THIS schema's own fixtures use `GB` for real: `04` W4-E06's own 2026-08-18 unblocking note states `G-jurisdictions` is now 'partially answered, UK named, which is sufficient to build and test the per-market age-floor machinery' — this field's real first value. Renamed from `03` §3's own table header 'jurisdiction' to `market` to match this epic's own accept-criteria language ('the age floor is per-market') — the identical fact under the vocabulary this epic actually uses, not a second one.

eligibilityobjectrequired

`03` §3 binding 6's `eligibility` member. Open shape, presence-only — `packages/product-registry/src/manifest.ts`'s own `PolicyBindingInput` gives every binding-6 member this same presence-only treatment ('no epic in this catalogue owns their shape yet'); this epic's own four accept criteria do not name an eligibility shape either, so none is invented here.

age_floorintegerrequired

`03` §3 binding 6's `age` member, as a plain non-negative integer — the number of years. `04` W4-E06 criterion 3: read from `apps/org`'s own per-market adapter (`governance/market-age-floor.ts`) at PROPOSE time, never asserted independently of it and never hardcoded anywhere this schema's own validation runs. This schema fixes only the STORED SHAPE of the number the adapter already resolved — it cannot itself call the adapter (a JSON Schema has no access to a market table), so 'never hardcoded into domain logic' is enforced one layer up, in code, not here.

limitsobjectrequired

`03` §3 binding 6's `limits` member. Open shape, presence-only — same reasoning as `eligibility` above.

statusenumrequiredproposed · in_force · withdrawn

This record's own lifecycle position. `proposed`: recorded, awaiting a second, distinct principal's approval. `in_force`: approved by a principal DISTINCT from the proposer (`04` W4-E06 criterion 2) — the ONLY status a product manifest's `policy_binding_ref` may resolve to and still be admitted (`04` W4-E06 criterion 1: 'a manifest whose binding 6 references no in-force PolicyBinding is rejected at admission'). `withdrawn`: no longer governs anything; retained, never deleted, on `apps/org`'s own oversight-trail 'superseded, never amended in place' convention (`04` W4-E08 acceptance 4).

proposed_bystringrequired

The identity that proposed this PolicyBinding — opaque, on `apps/console/src/lib/operator/identity-port.ts`'s own 'opaque Verdify-issued reference' convention. `04` W4-E06 criterion 2's whole subject: `approved_by` below must never equal this value once `status` is `in_force`.

approved_bystring | nullrequired

The identity that approved this PolicyBinding, or `null` while `status` is `proposed` (no approval has happened yet). `04` W4-E06 criterion 2, direct template of `apps/console/src/lib/operator/actions.ts`'s `approveAction`: an approval whose identity equals `proposed_by` is refused by the WORKFLOW that would otherwise write this field (`E_SELF_APPROVAL_FORBIDDEN`, ADR 0015 §11.6) — never by this schema, which sees only the already-accepted result of that check, on `container.schema.json`'s own 'schema-blind to the distinction' precedent. Present-but-null rather than absent while unresolved, on `attestation.schema.json`'s and `fund.schema.json`'s own shared convention (`03` §1.4: no optional field in an anchored shape).

effective_fromstring | nullrequired

The instant this PolicyBinding became `in_force`, or `null` before that has happened. `apps/org/src/governance/policy-binding-registry.ts` sets this at the moment `approvePolicyBinding` succeeds — the same 'effective-dated, not merely stored inertly' discipline `apps/console/src/lib/operator/controls/declared-deficiency-register.ts`'s own `effectiveFrom` and `apps/org/src/oversight/supersession.ts`'s own `effectiveFrom` already establish for an unrelated record.

withdrawn_atstring | nullrequired

The instant this PolicyBinding was withdrawn, or `null` while it has not been. Present-but-null, same reasoning as `approved_by` and `effective_from` above.

ReserveObligation

The per-class record for a jurisdiction's public-obligation classes — `01-platform-architecture.md` §4.3 change 6, RETAINED under ADR 0015 §11.1's amended point 1 and its own text: 'it remains the per-class record for the public-obligation classes; the partition rule decides what enters it.' THIS IS A RECORD SHAPE, NOT A COMPUTATION — the same distinction allocation.schema.json draws against ADR 0015's crossing authorities: a record shape at level 6 versus an authority over a money crossing.

GET/reserveobligations

The per-class record for a jurisdiction's public-obligation classes — `01-platform-architecture.md` §4.3 change 6, RETAINED under ADR 0015 §11.1's amended point 1 and its own text: 'it remains the per-class record for the public-obligation classes; the partition rule decides what enters it.' THIS IS A RECORD SHAPE, NOT A COMPUTATION — the same distinction allocation.schema.json draws against ADR 0015's crossing authorities: a record shape at level 6 versus an authority over a money crossing. Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/reserveobligations/{id}

The per-class record for a jurisdiction's public-obligation classes — `01-platform-architecture.md` §4.3 change 6, RETAINED under ADR 0015 §11.1's amended point 1 and its own text: 'it remains the per-class record for the public-obligation classes; the partition rule decides what enters it.' THIS IS A RECORD SHAPE, NOT A COMPUTATION — the same distinction allocation.schema.json draws against ADR 0015's crossing authorities: a record shape at level 6 versus an authority over a money crossing.

Parameters

  • idpath · requiredThe reserveobligation id.

Responses

ReserveObligation

The per-class record for a jurisdiction's public-obligation classes — `01-platform-architecture.md` §4.3 change 6, RETAINED under ADR 0015 §11.1's amended point 1 and its own text: 'it remains the per-class record for the public-obligation classes; the partition rule decides what enters it.' THIS IS A RECORD SHAPE, NOT A COMPUTATION — the same distinction allocation.schema.json draws against ADR 0015's crossing authorities: a record shape at level 6 versus an authority over a money crossing.

idstringrequired

Identity. NO PATTERN, on container.schema.json's and funding.schema.json's shared precedent: no document fixes an id space for this record, and inventing one would freeze a byte before its gate. NON-EMPTY: an empty string is not an identity.

source_jurisdictionstringrequired

ADR 0013 Decision 3: 'The jurisdiction on a reserved amount is snapshotted, not referenced. Re-domiciling a Fund never rewrites the classification of amounts already calculated.' This is a COPY taken at recognition, never a live pointer through `source_fund_id` — which is exactly why it is a distinct field here rather than something a reader derives by following the Fund reference. ISO 3166-1 alpha-2, the SAME shape `fund.schema.json`'s own `source_jurisdiction` carries (proven structurally in this schema's test suite), because a snapshot of a fact and the fact itself must be the same fact, byte for byte. `G-jurisdictions` is open; every fixture uses an ISO 3166-1 user-assigned code, never a real market.

source_fund_idstringrequired

The Fund this amount was recognised into (ADR 0013 Decision 1). A REFERENCE ONLY: fund.schema.json owns `creation_tier` and `governance_reason_digest`, and this record does not restate them — on allocation.schema.json's own precedent for `recipient_participation_id` ('participation.schema.json owns that id space'). NO PATTERN: fund.schema.json's own `id` fixes none, so repeating one here would put one piece of knowledge in two places. NON-EMPTY, because a reserved amount naming no Fund is unclassifiable by jurisdiction at all.

currencystringrequired

The SAME spelling contribution.schema.json already established for a currency field (ISO-4217 alphabetic code, or a supported crypto ticker) — reused rather than reinvented, proven structurally in this schema's test suite. ADR 0015 §11.1 amended point 5: 'No implicit FX. Held and remitted in the currency it arose in. Conversion is a policy decision with counsel input, never an operational convenience' — so this is the ONE currency field on the record, never a pair, and there is no conversion-rate member anywhere in this schema.

calc_rule_versionstringrequired

The versioned calculation rule this amount was recognised under (ADR 0013 Decision 4: 'The reserve is calculated from the first day, under a versioned calculation rule'). ADR 0015 §11.1 amended point 1 makes `calc_rule_version` a REQUIRED POSTING-LEVEL DIMENSION — 'which is what makes per-obligation balances derived rather than stored.' NO PATTERN: the versioning SCHEME belongs to whichever registry eventually publishes calculation rules, not to this record shape, and inventing one here would freeze a byte that registry has not yet fixed.

partition_rule_versionstringrequired

The versioned partition rule under which this amount's obligation class was assigned at receipt (ADR 0015 §1 and §6: 'a published, versioned partition rule keyed on (product, jurisdiction)'). A COMPANION to `calc_rule_version`, not a substitute for it — ADR 0013's own amended Consequences: 'calc_rule_version gains a companion partition_rule_version.' Distinct from `partition_rule_digest` (`03` §7.3 rule 3; ADR 0015 §6): the digest is what is PUBLISHED per participation, openable by the holder; the version is the ledger-side posting dimension this record carries. NO PATTERN, for the same reason `calc_rule_version` carries none: `04` W5-E05 (the partition-rule registry) owns that versioning scheme, and this epic does not pre-empt it.

principalReserveObligation_positiveQuantityrequired

The reserved amount itself, before income or fees. A SEPARATE ACCOUNT from `income` and `fees` from day one (ADR 0013 Consequences), never their sum folded into one figure. STRICTLY POSITIVE, unlike `income`/`fees`: no document states principal is ever expected to be zero — a `ReserveObligation` records that a jurisdiction's obligation class received something, so a principal of zero is not this record's normal case the way a zero income or fee is.

incomeReserveObligation_nonNegativeQuantityrequired

Income attributable to this obligation, tracked separately even though 'expected to be zero' (ADR 0013 Consequences: 'A retroactive determination that income belongs to the obligee is unanswerable if income was never tracked separately'). ADR 0015 §11.1 amended point 4: 'Income is separately classified and unavailable, attributed by a versioned rule, rounded against Veraq, under the SAME RESTRICTIONS AS PRINCIPAL' — which is why this shares `principal`'s exact `nonNegativeQuantity` $defs rather than a looser spelling of its own.

feesReserveObligation_nonNegativeQuantityrequired

Fees attributable to this obligation (e.g. a rule-declared, capped `cost-of-acceptance` class — ADR 0015 §8), tracked as a separate account from day one, 'even though income and fees are expected to be zero' (ADR 0013 Consequences).

legal_statusobjectrequired

ADR 0013 Decision 5: 'The legal classification is an explicit field defaulting to PROVISIONAL, effective-dated and evidence-referenced. It is never inferred from any other state in the system.' ONE FIELD bundling the three facts Decision 5 names together, on container.schema.json's own precedent for `closes_on` (one field, several required members) — never three top-level members whose pairing could drift apart. CLOSED TO `PROVISIONAL` TODAY: ADR 0015's Deferred table restates ADR 0013 unchanged — 'PROVISIONAL until counsel classifies' — and no document names a second value; that value is blocked on counsel (`G-memo`), and inventing one here would answer a counsel-gated question by implication.

valueobjectrequired

Defaults to `PROVISIONAL` (ADR 0013 Decision 5). Closed to this one value because it is the only legal classification any document names; widening this to a second, counsel-classified value is a future, counsel-gated schema change, not a decision this epic may make.

effective_atReserveObligation_instantrequired

Effective-dated, per Decision 5. 'Reclassification never rewrites history. It is a new effective-dated event; balances as at a past date remain what they were' (ADR 0013 Consequences).

evidence_digestReserveObligation_digestrequired

Evidence-referenced, per Decision 5 — by digest, never inline, matching commitment.schema.json's and fund.schema.json's own governance-evidence field. NEVER INFERRED: the classification stays independent of `lifecycle_status` and of every other field on this record, which is why it is required rather than filled in silently from something else.

lifecycle_statusstringrequired

ADR 0013 Decision 5: 'lifecycle status is a separate axis from legal status.' ITS VALUE SET IS DELIBERATELY UNCONSTRAINED HERE — the same recorded gap container.schema.json's own `status` field states rather than hides: 'no document fixes a member set' for this record's lifecycle today. Recognition is atomic at receipt (ADR 0015 §2); what happens after — accrual, partial or full disposition, closure — is a transition machine `04` W5-E04's crossing authorities and `04` W5-E07's reconciliation harness are positioned to own, on `04` W3-E09's precedent for `Container.status`. REQUIRED, never optional: `03` §1.4 rules out an optional field in a record shape, so a ReserveObligation with no lifecycle state at all is not a valid record even before that transition machine lands.

disposition_historyarray<object>required

The append-only history of amounts discharged from this obligation via ADR 0015 §5's crossing authorities (`Disposition`, `Release`, `Return`, `ContinuityRelease`) — the field `01` §4.3 change 6 names directly. EMPTY BY DEFAULT AT RECOGNITION: ADR 0015's own 'What's needed to execute' blocks 'any Release or ContinuityRelease execution' and 'all rule evaluation' on counsel, so a freshly recognised obligation has discharged nothing yet, and this schema must not require a first entry that cannot yet exist. Each entry references the discharging event BY ID rather than embedding the crossing authority's own mechanics — `04` W5-E04's `packages/obligation-*` owns those authorities' shape, not this schema.

ResultObservation

What was RECORDED AT RESOLUTION for an `external`-form container — `03` §5.1, verbatim: "observed value, provenance, source-publication digest, and every failed capture attempt"; `02` §7 unpacks provenance as "where fetched, when, by whom".

GET/resultobservations

What was RECORDED AT RESOLUTION for an `external`-form container — `03` §5.1, verbatim: "observed value, provenance, source-publication digest, and every failed capture attempt"; `02` §7 unpacks provenance as "where fetched, when, by whom". Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/resultobservations/{id}

What was RECORDED AT RESOLUTION for an `external`-form container — `03` §5.1, verbatim: "observed value, provenance, source-publication digest, and every failed capture attempt"; `02` §7 unpacks provenance as "where fetched, when, by whom".

Parameters

  • idpath · requiredThe resultobservation id.

Responses

ResultObservation

What was RECORDED AT RESOLUTION for an `external`-form container — `03` §5.1, verbatim: "observed value, provenance, source-publication digest, and every failed capture attempt"; `02` §7 unpacks provenance as "where fetched, when, by whom".

idstringrequired

Identity. NO PATTERN, on `container.schema.json`'s precedent, applied here as `allocation.schema.json` already applies it to a level-6-shaped leaf: no document fixes an id space for this class, and inventing one would freeze a byte before its gate.

outcome_idstringrequired

The outcome this observation was recorded for — `official-result-reference-v1.md` "Outcome integrity": "the `observed_value` recorded in `Outcome.verification`'s `ResultObservation`". NO PATTERN, DELIBERATELY, on `allocation.schema.json`'s own precedent and its own citation of `03` §1.4: a pattern here would repeat the leak that made `contribution.schema.json` wave-shaped in its frozen bytes.

observed_valueobjectrequired

`official-result-reference-v1.md` "Inputs" §4, verbatim: "an integer at `parameters.value_scale`, or `null` if no capture landed inside `parameters.observation_window_seconds`" — the `substitute_none` fallback's legitimate value, never an absent member (`03` §1.4: a verifier reads a present null rather than guessing at an absence). The declared scale itself is `Mechanism.parameters`', not this schema's — `canonical-form-v1.md` §2.1 places scale enforcement at the schema that declares a decimal-with-scale field, and this one carries no per-container scale to enforce against.

provenanceobjectrequired

`02` §7's own unpacking of `03` §5.1's "provenance": "where fetched, when, by whom". Required as a whole, whether or not the capture ultimately succeeded — even a `substitute_none` resolution names the observation process and the moment its window closed, not only a successful fetch.

fetched_fromstringrequired

WHERE — the location actually read (the pinned `ResultSource.publication_url`, or the archived capture reached in its place).

fetched_atResultObservation_instantrequired

WHEN — the moment of the capture this provenance describes.

fetched_bystringrequired

BY WHOM — an observer identifier, drawn from the committed `Mechanism.parameters.observer_set` this schema does not duplicate.

evidence_digestobjectrequired

`03` §5.1's "source-publication digest" / `02` §7's "an evidence digest of the source publication" — computed under `Mechanism.parameters.evidence_digest_algorithm` (fixed to `sha256` at v1). ATOMIC WITH `observed_value` (the root `if`/`then`/`else` above, on `attestation.schema.json`'s `RevealIsAtomic`-shaped precedent): null exactly when `observed_value` is null — there is no evidence to digest for a capture that never landed — and a real digest exactly when a value was observed.

attempt_countintegerrequired

The total number of capture attempts this document claims were made. `04` `W1-E07` acceptance 1: must equal `failed_capture_attempts.length` plus one more if `observed_value` is not `null` — enforced by `veraqAttemptCountConsistent`, not merely declared here.

failed_capture_attemptsarray<object>required

EVERY FAILED CAPTURE ATTEMPT — `03` §5.1's own phrase, verbatim, and the member that makes mis-observation (THREAT_MODEL.md adversary 2) inspectable rather than asserted: a `ResultObservation` claiming a success is not permitted to omit the failures `attempt_count` says preceded it (`veraqAttemptCountConsistent`). MAY be empty — a capture that succeeded on its first attempt records zero failures, and `attempt_count` reflects that.

observer_independenceenumrequiredsingle_party · multi_party

`03` §5.1, verbatim: `Outcome.verification.observer_independence ∈ {single_party, multi_party}` — "recorded and rendered, never asserted beyond what it states." `04` `W1-E07` acceptance 2: required, exactly these two members, no default — omission rejects precisely because `required` above names it and this is a `type: string` `enum`, never a member with a fallback value.

batch_digestResultObservation_resultDigestrequired

`03` §5.1 "Relationship to `.net`", verbatim: "the observation batch digest" — the digest of the WHOLE capture batch (every attempt, failures included), anchored on the open record before `value_published_at`. Never null: a batch digest exists once observation begins, independent of whether any attempt inside it succeeds.

batch_digest_published_atResultObservation_instantrequired

WHEN `batch_digest` was anchored to the open record. `04` `W1-E07` acceptance 3: must be STRICTLY EARLIER than `value_published_at` — the observe-dislike-reobserve defence, enforced by `veraqStrictlyBefore` below, not left to the write path alone. This is an INTRA-document comparison (both timestamps live on this one entity), unlike `commitment.schema.json`'s `published_at` vs. a DIFFERENT document's `Container.opens_at`, which is genuinely cross-entity and stays `04` `W3-E09`'s.

value_published_atResultObservation_instantrequired

WHEN the extracted value — `observed_value`, including the `substitute_none` sentinel — was published to the open record. Always present, even under `substitute_none`: `official-result-reference-v1.md` "Fallback handling" states `ALG` **is** invoked and returns a legitimate verdict in that case, so a value (the `none` verdict) is still published.

ResultSource

The pinned definition of WHAT TO OBSERVE for an `external`-form product — `03` §5.1's committed "source authority, publication URL, exact field, format, revision policy".

GET/resultsources

The pinned definition of WHAT TO OBSERVE for an `external`-form product — `03` §5.1's committed "source authority, publication URL, exact field, format, revision policy". Cursor-paginated; every record carries its open-record reference so the reader can cross-check it in the explorer. [OWNER TO CONFIRM]: authentication and rate limits are defined by the live API backend (not in this repo).

Parameters

  • cursorqueryOpaque pagination cursor returned as `next_cursor` by a prior response.
  • limitqueryMaximum records to return (1–100, default 20).

Responses

GET/resultsources/{id}

The pinned definition of WHAT TO OBSERVE for an `external`-form product — `03` §5.1's committed "source authority, publication URL, exact field, format, revision policy".

Parameters

  • idpath · requiredThe resultsource id.

Responses

ResultSource

The pinned definition of WHAT TO OBSERVE for an `external`-form product — `03` §5.1's committed "source authority, publication URL, exact field, format, revision policy".

source_authoritystringrequired

WHICH independent authority is observed — `official-result-reference-v1.md`'s worked example: "National Meteorological Office". Self-declared and unverifiable by the protocol alone, the same posture `revision_policy` has (the spec's own "Known limitation").

publication_urlstringrequired

WHERE the authority publishes the figure. Part of the four-member pin (`source_authority`, `publication_url`, `field`, `format`) `03` §5.1 names verbatim as committed before opening, and THREAT_MODEL.md adversary 4's mitigation: "the field is ambiguous" is exactly what an honestly-pinned source without this member would leave open.

fieldstringrequired

The EXACT field, inside the publication, the observers watch — `official-result-reference-v1.md`'s worked example: "mean_temperature_c". A publication with several figures on one page is not enough; this pins which one.

formatstringrequired

The shape the pinned field is published in — `official-result-reference-v1.md`'s worked example: "json:number". Free text at v1: the spec fixes no closed vocabulary for this member, so this schema fixes none either rather than inventing a stricter contract the spec itself does not commit to.

revision_policyenumrequiredfirst_published · final_only · latest_as_of_window_close

WHICH revision counts, committed in advance, for an authority that later revises a preliminary figure — `official-result-reference-v1.md` "Inputs" §1, verbatim: `first_published` binds the earliest published figure regardless of later revision; `final_only` binds observers to wait for the authority's own "final" marking (the observation stays absent if none arrives inside the window); `latest_as_of_window_close` binds whatever revision was current when `observation_window_seconds` elapses. EXACTLY these three members, spelled exactly as the spec spells them — a fourth or a renamed member would silently stop being a superset of what `veraq-protocol` already fixed.

quantity_declarationobjectrequired

Five booleans, one per disjunct of `03` §5.1's source-exclusion clause, required because the exclusion is drawn "by object referenced rather than by publisher" (`03` §5.1) — the predicate needs something to check that is not a heuristic over `publication_url` or `field` text. `official-result-reference-v1.md` "Source exclusion": a `ResultSource` with any flag `true` MUST NOT be committed — checked once, at commitment time, by `veraq-protocol`'s `is_excluded`, not by this schema.

is_macroeconomic_indicatorbooleanrequired

`03` §5.1's first excluded disjunct — CPI, unemployment, GDP and energy-output figures are the spec's own canonical underliers of this disjunct.

is_pricebooleanrequired

`03` §5.1's second excluded disjunct.

is_ratebooleanrequired

`03` §5.1's third excluded disjunct — an FX rate is `official-result-reference-v1.md`'s own worked counter-example.

is_index_levelbooleanrequired

`03` §5.1's fourth excluded disjunct — an equity-index level is `official-result-reference-v1.md`'s own worked counter-example.

has_listed_or_otc_derivativebooleanrequired

`03` §5.1's fifth excluded disjunct: "any quantity on which a listed or OTC derivative exists", checked by object referenced, never by publisher.