API Reference
The Open Record REST API.
Read the open record — the 22 entities behind every wave, outcome, and contribution.
Provisional
@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 iscommit_window_closed.
Error
A request error, named as a state (e.g. record_mismatch, commit_window_closed, not_found) — never a failure-of-luck.
codestringrequiredMachine-readable state.
messagestringrequiredHuman-readable, exact, never alarmist.
open_record_refstring | nullWhere applicable, the open-record entry the reader can cross-check.
Example response — every verifiable field (✓) is checkable against the open record
{
"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.
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
200WavePageA page of waves.
A round of participation. Runs one mechanism; contains many participations; resolves to outcomes.
Parameters
idpath · requiredThe wave id.
Responses
200WaveThe wave.404
Wave
A round of participation. Runs one mechanism; contains many participations; resolves to outcomes.
idstringrequiredSequential wave identifier, zero-padded.
opens_atstringrequiredWhen participants may begin seating.
closes_atstringrequiredLast moment to seat a participation. Must be ≥ opens_at.
resolves_atstringrequiredWhen the mechanism seed is revealed and the outcome resolves. Must be ≥ closes_at.
mechanism_idstringrequiredULID reference to the published mechanism for this wave. The mechanism is published — including its seed commitment — before the wave opens.
seatsobjectrequiredtotalintegerrequiredtakenintegerrequiredcharitable_sharenumberrequiredFraction of the wave's outcome that flows to the beneficiary cause. Expressed in [0, 1], e.g. 0.12 == 12%.
beneficiary_cause_idstringrequiredSlug of the verified cause receiving the charitable share.
statusenumrequiredannounced · open · resolving · settled · cancelledWave 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.
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
200MechanismPageA page of mechanisms.
The published selection rule (commit-reveal-v1), committed to the open record before a wave runs.
Parameters
idpath · requiredThe mechanism id.
Responses
200MechanismThe mechanism.404
Mechanism
The published selection rule (commit-reveal-v1), committed to the open record before a wave runs.
idstringrequiredULID of this mechanism document.
algorithmstringrequiredIdentifier of the algorithm spec in the veraq-protocol repository.
parametersobjectAlgorithm-specific parameters. Shape is determined by the algorithm field and validated by the spec in veraq-protocol/algorithms/<algorithm>.md.
seed_commitmentstringrequiredLowercase hex SHA-256 of (seed || parameters_canonical_json). Binding commitment published before the wave opens.
seed_revealedstring | nullLowercase 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_atstringrequiredWhen the commitment was published. Must be < the linked wave's opens_at.
revealed_atstring | nullWhen seed_revealed was published. Null while undisclosed; ≥ wave.resolves_at when set.
verifying_transcript_urlstring | nullOptional URL to a verifying transcript (re-run output) for additional inspectability.
Participant
A person taking part. Makes participations; chooses a cause.
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
200ParticipantPageA page of participants.
A person taking part. Makes participations; chooses a cause.
Parameters
idpath · requiredThe participant id.
Responses
200ParticipantThe participant.404
Participant
A person taking part. Makes participations; chooses a cause.
idstringrequiredStable participant identifier (ULID).
verdify_identity_idstringrequiredForeign 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 | nullOptional public-facing handle. May be null for participants who choose not to display one.
created_atstringrequiredWhen the participant first seated.
verified_atstring | nullWhen identity verification was completed, if applicable. Null if unverified.
Participation
One participant's entry into a wave; written to the open record.
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
200ParticipationPageA page of participations.
One participant's entry into a wave; written to the open record.
Parameters
idpath · requiredThe participation id.
Responses
200ParticipationThe participation.404
Participation
One participant's entry into a wave; written to the open record.
idstringrequiredWave-scoped participation id. Format: wave_id, hyphen, 4-character suffix.
wave_idstringrequiredparticipant_pseudonymstringrequiredReplaces `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_atstringrequiredweightnumberOptional weighting used by weighted-selection mechanisms. Omitted means weight = 1.
kindenumcontributor · recipientADR 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 | nullOptional 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.
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
200OutcomePageA page of outcomes.
What a wave resolves to. A defined share routes to a contribution.
Parameters
idpath · requiredThe outcome id.
Responses
200OutcomeThe outcome.404
Outcome
What a wave resolves to. A defined share routes to a contribution.
idstringrequiredWave-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_idstringrequiredrecipient_participation_idstringrequiredThe participation selected by the mechanism. The participant behind it can be looked up via the participation record.
mechanismobjectrequiredSnapshot 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_idstringrequiredalgorithmstringrequiredseed_revealedstringrequiredverifying_transcript_sha256string | nullOptional content hash of the verifying transcript, if one was published.
verifiedbooleanrequiredWhether at least one independent verifier has replayed the mechanism and matched the recipient. Outcomes are not considered fully settled until verified == true.
settled_atstringrequiredContribution
The defined share routed to a cause, derived from an outcome.
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
200ContributionPageA page of contributions.
The defined share routed to a cause, derived from an outcome.
Parameters
idpath · requiredThe contribution id.
Responses
200ContributionThe contribution.404
Contribution
The defined share routed to a cause, derived from an outcome.
idstringrequiredoutcome_idstringrequiredThe 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_idstringrequiredamountobjectrequiredvaluestringrequiredDecimal string. Stored as string to avoid floating-point precision loss.
currencystringrequiredISO-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 | nullWhen the transfer was confirmed. Null while pending.
proof_urlstring | nullOptional URL to a third-party transfer proof — bank receipt, on-chain transaction, etc.
Cause
A verified public-good recipient of contributions chosen by participants.
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
200CausePageA page of causes.
A verified public-good recipient of contributions chosen by participants.
Parameters
idpath · requiredThe cause id.
Responses
200CauseThe cause.404
Cause
A verified public-good recipient of contributions chosen by participants.
idstringrequiredStable slug. Used wherever a cause is referenced.
namestringrequireddescriptionstringrequiredverificationobjectrequiredHow this cause was verified as eligible to receive charitable share. The brand promise requires this to be inspectable.
methodenumrequiredregistry · manual-review · third-party-attestationHow verification was performed.
registrystring | nullWhen method == 'registry', the registry identifier (e.g., 'gs1-charities-uk:1148231').
verified_atstringrequiredverifierstringrequiredName or identifier of the verifying party.
evidence_urlstring | nullpayout_addressobjectWhere 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_jurisdictionstringrequiredADR 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').
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
200AllocationPageA page of 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').
Parameters
idpath · requiredThe allocation id.
Responses
200AllocationThe allocation.404
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').
idstringrequiredIdentity. 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_idstringrequiredThe 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_idstringrequiredThe 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_positiveQuantityrequiredThe 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_instantrequiredWhen 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.
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
200AttestationPageA page of 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.
Parameters
idpath · requiredThe attestation id.
Responses
200AttestationThe attestation.404
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.
idstringrequiredIdentity. 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_idstringrequiredThe 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.
attesterstringrequiredWHO 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 · mismatchWhat 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_digestobjectrequiredThe 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_instantrequiredWhen 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.
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
200ChallengePageA page of 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.
Parameters
idpath · requiredThe challenge id.
Responses
200ChallengeThe challenge.404
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.
idstringrequiredIdentity. 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_idstringrequiredThe 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 | nullThe 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_instantrequiredWhen 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.
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
200CommitmentPageA page of commitments.
What was pinned before a container opened, per container, discriminated on verification form.
Parameters
idpath · requiredThe commitment id.
Responses
200CommitmentThe commitment.404
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.
idstringrequiredIdentity.
container_idstringrequiredThe container this commitment belongs to. 03 §1.2: a PER-CONTAINER Commitment.
formobjectrequiredpublished_atCommitment_instantrequiredWhen 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_digestrequiredThe 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 | nullrequiredLowercase hex of the revealed seed. NULL until the reveal, present after — never absent, per 03 §1.4.
revealed_atobjectrequiredWhen the seed was revealed. Null until then.
transcript_digestCommitment_nullableDigestrequiredDigest 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.
idstringrequiredIdentity.
container_idstringrequiredThe container this commitment belongs to. 03 §1.2: a PER-CONTAINER Commitment.
formobjectrequiredpublished_atCommitment_instantrequiredWhen 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_digestrequiredDigest 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_nullableDigestrequiredDigest 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.
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
200ContainerPageA page of containers.
A cohort with a freeze predicate.
Parameters
idpath · requiredThe container id.
Responses
200ContainerThe container.404
Container
A cohort with a freeze predicate.
idstringrequiredIdentity. 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_idstringrequiredProvenance: 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_instantrequiredThe 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_onobjectrequiredThe 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 · cancelledThe 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.
termsobjectrequiredA 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_bearingbooleanrequiredTrue 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_windowstringThe 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_digeststringrequiredcanonical-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_idstringrequiredThe 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.
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
200ContainerClassTotalPageA page of 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.
Parameters
idpath · requiredThe containerclasstotal id.
Responses
200ContainerClassTotalThe containerclasstotal.404
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_idstringrequiredThe 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_idstringrequiredOne 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_nonNegativeAmountrequiredThe 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).
currencystringrequiredISO-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).
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
200FundPageA page of 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).
Parameters
idpath · requiredThe fund id.
Responses
200FundThe fund.404
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).
idstringrequiredIdentity. 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_jurisdictionstringrequiredISO 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 · additionalWhich 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 | nullrequiredThe 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.
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
200FundingPageA page of 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.
Parameters
idpath · requiredThe funding id.
Responses
200FundingThe funding.404
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.
idstringrequiredIdentity. 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_idstringrequiredThe 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_positiveQuantityrequiredThe 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_instantrequiredWhen 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).
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
200JurisdictionBalanceCommitmentPageA page of 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).
Parameters
idpath · requiredThe jurisdictionbalancecommitment id.
Responses
200JurisdictionBalanceCommitmentThe jurisdictionbalancecommitment.404
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_idstringrequiredThe 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_idstringrequiredThe 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_digestrequiredSHA-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).
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
200ParticipationPartitionReportPageA page of 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).
Parameters
idpath · requiredThe participationpartitionreport id.
Responses
200ParticipationPartitionReportThe participationpartitionreport.404
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_idstringrequiredThe 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_digestrequiredThe 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.
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
200PayoutRailPageA page of payoutrails.
A discriminated payout-rail taxonomy.
Parameters
idpath · requiredThe payoutrail id.
Responses
200PayoutRailThe payoutrail.404
PayoutRail
A discriminated payout-rail taxonomy.
BankIbanRail
kindobjectrequiredibanstringrequiredISO 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
kindobjectrequiredaddressstringrequiredA 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
kindobjectrequiredhandlestringrequiredDeliberately 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').
`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
200PolicyBindingPageA page of 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').
Parameters
idpath · requiredThe policybinding id.
Responses
200PolicyBindingThe policybinding.404
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_idstringrequiredIdentity — 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.
marketstringrequiredISO 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 · withdrawnThis 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_bystringrequiredThe 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 | nullrequiredThe 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 | nullrequiredThe 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 | nullrequiredThe 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.
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
200ReserveObligationPageA page of 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.
Parameters
idpath · requiredThe reserveobligation id.
Responses
200ReserveObligationThe reserveobligation.404
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.
idstringrequiredIdentity. 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_jurisdictionstringrequiredADR 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_idstringrequiredThe 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.
currencystringrequiredThe 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_versionstringrequiredThe 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_versionstringrequiredThe 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_positiveQuantityrequiredThe 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_nonNegativeQuantityrequiredIncome 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_nonNegativeQuantityrequiredFees 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_statusobjectrequiredADR 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.
valueobjectrequiredDefaults 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_instantrequiredEffective-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_digestrequiredEvidence-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_statusstringrequiredADR 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>requiredThe 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".
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
200ResultObservationPageA page of 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".
Parameters
idpath · requiredThe resultobservation id.
Responses
200ResultObservationThe resultobservation.404
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".
idstringrequiredIdentity. 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_idstringrequiredThe 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_fromstringrequiredWHERE — the location actually read (the pinned `ResultSource.publication_url`, or the archived capture reached in its place).
fetched_atResultObservation_instantrequiredWHEN — the moment of the capture this provenance describes.
fetched_bystringrequiredBY 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_countintegerrequiredThe 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>requiredEVERY 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_instantrequiredWHEN `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_instantrequiredWHEN 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".
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
200ResultSourcePageA page of 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".
Parameters
idpath · requiredThe resultsource id.
Responses
200ResultSourceThe resultsource.404
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_authoritystringrequiredWHICH 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_urlstringrequiredWHERE 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.
fieldstringrequiredThe 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.
formatstringrequiredThe 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_closeWHICH 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_declarationobjectrequiredFive 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.