Schemas¶
The six H2A schemas are JSON Schema Draft 2020-12. Every $id resolves under
https://h2a-protocol.org/schemas/v0/, so a validator can fetch them directly. Each object below
lists its fields — type, whether it is required, and what it means — followed by a real example
taken straight from the repository.
Every example shown is checked in CI by scripts/validate-schemas.py:
the validated ones must pass their schema, and the two counter-examples
(decision-record.INVALID, memory.derived.INVALID) must be rejected. The tables and examples on
this page are generated from the schema files at build time, so they cannot drift from the schemas
they describe.
Grant h2a-core.grant raw ↗
Authority for a grantee (operator) to use a subject within a bounded scope, revocable at point of use. Central H2A-Core object. iss MUST resolve under a rights-holder or fiduciary namespace, MUST NOT resolve under the operator's domain, and SHOULD NOT resolve under the implementer's domain (ADR-001).
| Field | Type | Req | Description |
|---|---|---|---|
h2a_version | 0.1 const | ✓ | — |
grant_id | string · UUID | ✓ | Unique grant identifier; also serves as the jti for replay protection (ADR-004). |
iss | string · uri | ✓ | Issuer identity URI under a rights-holder / fiduciary namespace. Not operator; not implementer. |
subject_ref | string · subject URN | ✓ | Key-bound URN of the governed subject (digital actor / likeness / memory principal). |
grantee_ref | string · grantee URN | ✓ | Authenticated operator identity the grant is issued to (ADR-004). |
scope | object | ✓ | — |
profiles | array of h2a-media h2a-memory h2a-commercial | — | Which profiles apply to this grant. 'h2a-commercial' (SPEC-COMMERCIAL) is optional and never a Core conformance requirement (ADR-012). |
lease | object | — | Budget Authority — cross-provider spend cap enforced as a lease, not a counter. |
status | object | ✓ | — |
revocation_horizon | string · ISO 8601 duration | ✓ | Declared maximum time from revocation to non-conformant-transmission cutoff. The single conformance dial. ISO 8601 duration. |
delegation | object | — | Attenuation-only delegation chain (ADR-004). A child grant may only narrow scope. |
exposure | object | — | Optional commercial-profile extension (SPEC-COMMERCIAL / h2a-commercial). Opaque to Core: its shape is constrained by h2a-commercial.profile.schema.json only when this grant lists 'h2a-commercial' in profiles. Not a Core conformance requirement (ADR-012). A verifier that does not implement the commercial profile MUST ignore this field and MUST NOT refuse the grant because it is present. |
alg | ES256 ES384 EdDSA · default ES256 | ✓ | Signature algorithm. ES256 (P-256) is mandatory-to-implement; curve-agnostic via this header. |
signatures | array of object | ✓ | Two detached signatures (ADR-004): consent (by subject/custodian) and issuance (by issuer). Kept separate so consent and issuance never collapse into one act. Exactly one of each role, enforced below — minItems/maxItems alone admitted two consent signatures and no issuance, which is the collapse this separation exists to prevent. |
iat | string · date-time | ✓ | — |
nbf | string · date-time | ✓ | Not-before. REQUIRED: without it a verifier cannot decide whether the grant is yet in force, so a grant lacking nbf is schema-valid and un-evaluable. Implementations already reject its absence (bridle packages/core/src/verify.ts) — the schema was the one permitting it. |
exp | string · date-time | ✓ | — |
scope
| Field | Type | Req | Description |
|---|---|---|---|
purposes | array of string | ✓ | — |
media_types | array of string | — | — |
territories | array of string | — | ISO 3166-1 alpha-2 or 'GLOBAL'. |
channels | array of string | — | — |
exclusions | array of string | — | Hard denials, e.g. political, adult, defamatory. |
lease
| Field | Type | Req | Description |
|---|---|---|---|
cap | number · > 0 | ✓ | — |
unit | string | ✓ | e.g. 'usd', 'render-seconds', 'tokens'. |
provider_agnostic | boolean · default true | — | — |
nbf | string · date-time | ✓ | — |
exp | string · date-time | ✓ | — |
status
| Field | Type | Req | Description |
|---|---|---|---|
uri | string · uri | ✓ | Primary status-list URI. MUST resolve under the issuer's own endpoint, never the Foundation's. |
mirrors | array of string · uri | — | Additional CDN mirror URIs of the same signed list. |
index | integer · ≥ 0 | ✓ | This grant's bit position in the status list. |
delegation
| Field | Type | Req | Description |
|---|---|---|---|
parent | string | null | — | grant_id of the parent, or null at chain root. |
depth | integer · ≥ 0 | ✓ | — |
effective_chain_horizon | string | — | Realised horizon of the whole chain = max of all links (a chain is only as revocable as its slowest link). |
max_chain_horizon | string | — | Ceiling the chain may not exceed. |
signatures[] — each item
| Field | Type | Req | Description |
|---|---|---|---|
role | consent issuance | ✓ | — |
kid | string | ✓ | — |
value | string | ✓ | base64url detached signature over the canonicalised grant. |
✓ validated · example · grant.valid.json
{
"h2a_version": "0.1",
"grant_id": "3f2a1c40-0d1e-4b2a-9c33-8a1b2c3d4e5f",
"iss": "https://issuer.example.org/h2a/issuer",
"subject_ref": "urn:h2a:subject:jane-actor-001",
"grantee_ref": "urn:h2a:grantee:example-operator",
"scope": {
"purposes": ["promotional-video"],
"media_types": ["video"],
"territories": ["GB", "US"],
"channels": ["social", "web"],
"exclusions": ["political", "adult", "defamatory"]
},
"profiles": ["h2a-media"],
"lease": { "cap": 500, "unit": "usd", "provider_agnostic": true, "nbf": "2026-07-16T00:00:00Z", "exp": "2026-10-16T00:00:00Z" },
"status": {
"uri": "https://issuer.example.org/h2a/status/2026-q3.json",
"mirrors": ["https://cdn.example.net/h2a/status/2026-q3.json"],
"index": 42
},
"revocation_horizon": "PT30M",
"delegation": { "parent": null, "depth": 0, "effective_chain_horizon": "PT30M", "max_chain_horizon": "PT1H" },
"alg": "ES256",
"signatures": [
{ "role": "consent", "kid": "subject-key-1", "value": "BASE64URL_CONSENT_SIG" },
{ "role": "issuance", "kid": "issuer-key-1", "value": "BASE64URL_ISSUANCE_SIG" }
],
"iat": "2026-07-16T00:00:00Z",
"nbf": "2026-07-16T00:00:00Z",
"exp": "2026-10-16T00:00:00Z"
}
Attestation h2a-core.attestation raw ↗
Signed receipt of a single act of use, binding the committed output to the grant and to the point-of-use status check that authorised it.
| Field | Type | Req | Description |
|---|---|---|---|
h2a_version | 0.1 const | ✓ | — |
attestation_id | string · UUID | ✓ | — |
grant_id | string · UUID | ✓ | — |
subject_ref | string · subject URN | ✓ | — |
grantee_ref | string · grantee URN | ✓ | — |
output_hash | string · sha256 | ✓ | SHA-256 of the committed output. |
use | object | — | The use this attestation actually attests: what was checked at the moment of the act, not what the grant permits in general. Optional so objects written before this field existed stay valid, but SHOULD be present — an attestation that does not say what use it attests records only that something happened, which is a decoration rather than evidence (Bridle defect B6). A verifier comparing this against the grant's scope is what makes the attestation checkable offline. |
decision_record_ref | string | — | record_id of the Decision Record this attestation was emitted under. Optional for backwards compatibility, but without it the attestation and the decision that authorised it cannot be tied together by a third party — which is the join a stranger needs to verify the act offline. |
generator | object | — | What produced the output. The operator governs, never generates — generation is outsourced to a third-party generation provider via an implementer's adapter. |
status_check | object | ✓ | The check performed at the point of use. Never cached; always live. |
alg | ES256 ES384 EdDSA · default ES256 | ✓ | — |
signature | string | ✓ | base64url signature over the canonicalised attestation. |
created_at | string · date-time | ✓ | — |
use
| Field | Type | Req | Description |
|---|---|---|---|
purpose | string | — | The single purpose checked, drawn from the grant's scope.purposes. |
territory | string | — | ISO 3166-1 alpha-2 or 'GLOBAL'. |
spend | object | — | What this act drew against the grant's lease, in the lease's own unit. |
use.spend
| Field | Type | Req | Description |
|---|---|---|---|
amount | number · ≥ 0 | ✓ | — |
unit | string | ✓ | MUST match the grant's lease.unit; a spend in a different unit is not comparable to the cap. |
generator
| Field | Type | Req | Description |
|---|---|---|---|
adapter | string | — | — |
provider | string | — | — |
model | string | — | — |
status_check
| Field | Type | Req | Description |
|---|---|---|---|
result | valid refused | ✓ | — |
reason_code | string | — | e.g. asset-revoked, out-of-scope, lease-exhausted. |
checked_at | string · date-time | ✓ | — |
status_uri | string · uri | — | — |
witness_beacon | string | — | Reference to the external timestamp/beacon the check was bracketed against (ADR-005). |
✓ validated · example · attestation.valid.json
{
"h2a_version": "0.1",
"attestation_id": "7c9e6a10-1111-4b2a-9c33-8a1b2c3d4e5f",
"grant_id": "3f2a1c40-0d1e-4b2a-9c33-8a1b2c3d4e5f",
"subject_ref": "urn:h2a:subject:jane-actor-001",
"grantee_ref": "urn:h2a:grantee:example-operator",
"output_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"generator": { "adapter": "example-adapter", "provider": "example-provider", "model": "avatar-v3" },
"status_check": {
"result": "valid",
"checked_at": "2026-07-20T14:03:00Z",
"status_uri": "https://issuer.example.org/h2a/status/2026-q3.json",
"witness_beacon": "beacon:2026-07-20T14:02:59Z:ab12"
},
"alg": "ES256",
"signature": "BASE64URL_SIG",
"created_at": "2026-07-20T14:03:01Z"
}
Decision Record h2a-core.decision-record raw ↗
The signed record of a conformant-vs-non-conformant transmission decision. First-class schema object (ADR-006): H2A evidences, it does not enforce. This is the most commercially significant artefact for union / CMO procurement leverage — a non-conformant downstream transmission is recorded here explicitly.
non_conformant_transmissionis required whendecision=TRANSMITTED_NON_CONFORMANT
| Field | Type | Req | Description |
|---|---|---|---|
h2a_version | 0.1 const | ✓ | — |
record_id | string · UUID | ✓ | — |
grant_id | string · UUID | ✓ | — |
decision | PERMITTED_CONFORMANT REFUSED_REVOKED REFUSED_OUT_OF_SCOPE REFUSED_LEASE_EXHAUSTED TRANSMITTED_NON_CONFORMANT | ✓ | — |
reason_code | string | — | — |
output_hash | string · sha256 | — | — |
measured_latency_ms | integer · ≥ 0 | — | Revocation-to-cutoff latency, measured at the issuer endpoint (not the operator), cross-checked against committed work. |
effective_chain_horizon | string | — | Realised horizon of the chain = max of all links. |
max_chain_horizon | string | — | — |
horizon_committed_ms | integer · ≥ 0 | — | Committed revocation horizon in force for this act, in milliseconds. What was promised. Core evidence field; when the commercial profile applies it is the horizon of the grant's assurance_level. |
horizon_measured_ms | integer · ≥ 0 | — | Measured self-executor halt latency in milliseconds (ADR-006 as amended; ADR-013). What was delivered. Feeds the loss table. MAY be absent where a deployment does not yet instrument halt latency (see measured_latency_ms, which it supersedes for the SLA). |
horizon_breach | boolean | — | Derived: true when horizon_measured_ms > horizon_committed_ms. The SLA trigger. Absent when horizon_measured_ms is not populated. |
chain_conformance | conformant non_conformant_downstream unknown | — | Typed chain-conformance outcome for this act (formerly implicit in non_conformant_transmission). |
exposure_snapshot | object | — | Optional commercial-profile extension (SPEC-COMMERCIAL / h2a-commercial): per-act limit, aggregate limit, aggregate consumed, and assurance_level at the time of the act. Opaque to Core; constrained by the commercial profile. Not a Core conformance requirement (ADR-012); verifiers that do not implement the commercial profile MUST ignore it. |
non_conformant_transmission | object | null | — | Present and non-null ONLY when decision is TRANSMITTED_NON_CONFORMANT. Records a transmission that occurred outside conformance so it becomes admissible evidence. |
witness | object | — | External anchoring (ADR-005): RFC 3161 eIDAS-qualified TSA token + independent witness co-signature on the chain head. No blockchain. |
created_at | string · date-time | ✓ | — |
alg | ES256 ES384 EdDSA · default ES256 | ✓ | — |
signature | string | ✓ | — |
non_conformant_transmission
| Field | Type | Req | Description |
|---|---|---|---|
downstream_ref | string | ✓ | Identifier of the downstream executor / channel that transmitted non-conformantly. |
why | horizon_exceeded downstream_not_conformant check_bypassed | ✓ | — |
horizon_exceeded_by_ms | integer · ≥ 0 | — | — |
witness
| Field | Type | Req | Description |
|---|---|---|---|
tsa_token_ref | string | — | — |
witness_cosign_ref | string | — | — |
✓ validated · example · decision-record.conformant.json
{
"h2a_version": "0.1",
"record_id": "9d1f2b30-2222-4b2a-9c33-8a1b2c3d4e5f",
"grant_id": "3f2a1c40-0d1e-4b2a-9c33-8a1b2c3d4e5f",
"decision": "PERMITTED_CONFORMANT",
"reason_code": "in-scope",
"output_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"measured_latency_ms": 0,
"effective_chain_horizon": "PT30M",
"max_chain_horizon": "PT1H",
"non_conformant_transmission": null,
"witness": { "tsa_token_ref": "tsa:rfc3161:abc", "witness_cosign_ref": "witness:example:def" },
"created_at": "2026-07-20T14:03:01Z",
"alg": "ES256",
"signature": "BASE64URL_SIG"
}
✓ validated · example · decision-record.non-conformant.json
{
"h2a_version": "0.1",
"record_id": "9d1f2b30-3333-4b2a-9c33-8a1b2c3d4e5f",
"grant_id": "3f2a1c40-0d1e-4b2a-9c33-8a1b2c3d4e5f",
"decision": "TRANSMITTED_NON_CONFORMANT",
"reason_code": "downstream-exceeded-horizon",
"measured_latency_ms": 5200,
"effective_chain_horizon": "PT30M",
"max_chain_horizon": "PT1H",
"non_conformant_transmission": {
"downstream_ref": "channel:partner-cdn-eu",
"why": "horizon_exceeded",
"horizon_exceeded_by_ms": 3400
},
"witness": { "tsa_token_ref": "tsa:rfc3161:xyz", "witness_cosign_ref": "witness:example:uvw" },
"created_at": "2026-07-20T14:35:00Z",
"alg": "ES256",
"signature": "BASE64URL_SIG"
}
✗ rejected · counter-example · decision-record.INVALID.json — TRANSMITTED_NON_CONFORMANT with no non_conformant_transmission block
{
"h2a_version": "0.1",
"record_id": "9d1f2b30-5555-4b2a-9c33-8a1b2c3d4e5f",
"grant_id": "3f2a1c40-0d1e-4b2a-9c33-8a1b2c3d4e5f",
"decision": "TRANSMITTED_NON_CONFORMANT",
"created_at": "2026-07-20T14:35:00Z",
"alg": "ES256",
"signature": "BASE64URL_SIG"
}
Status List h2a-core.status-list raw ↗
A static, signed revocation artefact served from the issuer's own endpoint (and optional CDN mirrors). Short-TTL and re-signed. The Foundation is never in this fetch path.
| Field | Type | Req | Description |
|---|---|---|---|
h2a_version | 0.1 const | ✓ | — |
status_list_id | string · UUID | ✓ | — |
iss | string · uri | ✓ | Issuer namespace — same root as the grants it covers. |
purpose | revocation const | ✓ | — |
encoding | base64url-bitstring const | ✓ | — |
list | string | ✓ | base64url-encoded, gzip-compressed bitstring. Bit set = revoked. |
mirrors | array of string · uri | — | — |
valid_from | string · date-time | ✓ | — |
valid_until | string · date-time | ✓ | Short TTL. A verifier treats an expired list as fail-closed. |
alg | ES256 ES384 EdDSA · default ES256 | ✓ | — |
signature | string | ✓ | — |
✓ validated · example · status-list.valid.json
{
"h2a_version": "0.1",
"status_list_id": "5a6b7c80-4444-4b2a-9c33-8a1b2c3d4e5f",
"iss": "https://issuer.example.org/h2a/issuer",
"purpose": "revocation",
"encoding": "base64url-bitstring",
"list": "eJzTBQAAMAAw",
"mirrors": ["https://cdn.example.net/h2a/status/2026-q3.json"],
"valid_from": "2026-07-20T00:00:00Z",
"valid_until": "2026-07-20T01:00:00Z",
"alg": "ES256",
"signature": "BASE64URL_SIG"
}
Media Profile h2a-media.profile raw ↗
Asset-bound media profile. Not a Core requirement — applies only when a grant lists 'h2a-media'. C2PA composition lives here for multi-part supply chains.
| Field | Type | Req | Description |
|---|---|---|---|
h2a_version | 0.1 const | ✓ | — |
profile | h2a-media const | ✓ | — |
grant_id | string · UUID | ✓ | — |
likeness_ref | string · subject URN | ✓ | — |
asset | object | ✓ | — |
composition | array of object | — | Parent asset references for multi-part supply chains. Effective revocability of the composite = max horizon of all parts. |
constraints | object | — | Modality-specific limits (e.g. no singing voice, no minors' likeness). |
asset
| Field | Type | Req | Description |
|---|---|---|---|
asset_id | string | ✓ | — |
media_type | audio video image 3d | ✓ | — |
content_hash | string · sha256 | ✓ | — |
c2pa_manifest_ref | string | — | Pointer to the C2PA manifest, if present. |
composition[] — each item
| Field | Type | Req | Description |
|---|---|---|---|
asset_ref | string | ✓ | — |
grant_ref | string · UUID | — | — |
constraints — open object (modality-specific keys)
✓ validated · example · media.valid.json
{
"h2a_version": "0.1",
"profile": "h2a-media",
"grant_id": "3f2a1c40-0d1e-4b2a-9c33-8a1b2c3d4e5f",
"likeness_ref": "urn:h2a:subject:jane-actor-001",
"asset": {
"asset_id": "asset-991",
"media_type": "video",
"content_hash": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"c2pa_manifest_ref": "c2pa:urn:manifest:991"
},
"composition": [
{ "asset_ref": "asset-880", "grant_ref": "3f2a1c40-0d1e-4b2a-9c33-8a1b2c3d4e5f" }
],
"constraints": { "no_singing_voice": true }
}
Memory Profile h2a-memory.profile raw ↗
Memory-record profile. Applies only when a grant lists 'h2a-memory'. Enforces the negative test: a DERIVED memory record MUST carry non-empty provenance, or it is rejected.
provenanceis required whenrecord.memory_type=derived
| Field | Type | Req | Description |
|---|---|---|---|
h2a_version | 0.1 const | ✓ | — |
profile | h2a-memory const | ✓ | — |
grant_id | string · UUID | ✓ | — |
subject_ref | string · subject URN | ✓ | — |
record | object | ✓ | — |
provenance | object | — | — |
retention | object | — | — |
record
| Field | Type | Req | Description |
|---|---|---|---|
record_id | string | ✓ | — |
memory_type | raw derived | ✓ | — |
content_hash | string · sha256 | ✓ | — |
provenance
| Field | Type | Req | Description |
|---|---|---|---|
sources | array of object | ✓ | — |
provenance.sources[] — each item
| Field | Type | Req | Description |
|---|---|---|---|
source_ref | string | ✓ | — |
method | string | ✓ | — |
retention
| Field | Type | Req | Description |
|---|---|---|---|
retention_horizon | string | — | ISO 8601 duration. |
deletion_on_revoke | boolean · default true | — | — |
✓ validated · example · memory.derived.valid.json
{
"h2a_version": "0.1",
"profile": "h2a-memory",
"grant_id": "3f2a1c40-0d1e-4b2a-9c33-8a1b2c3d4e5f",
"subject_ref": "urn:h2a:subject:jane-actor-001",
"record": { "record_id": "mem-1", "memory_type": "derived", "content_hash": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" },
"provenance": { "sources": [ { "source_ref": "transcript:call-7", "method": "summarise" } ] },
"retention": { "retention_horizon": "P90D", "deletion_on_revoke": true }
}
✗ rejected · counter-example · memory.derived.INVALID.json — derived record carrying no provenance
{
"h2a_version": "0.1",
"profile": "h2a-memory",
"grant_id": "3f2a1c40-0d1e-4b2a-9c33-8a1b2c3d4e5f",
"subject_ref": "urn:h2a:subject:jane-actor-001",
"record": { "record_id": "mem-2", "memory_type": "derived", "content_hash": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" }
}