| Internet-Draft | CAID | September 2026 |
| Schrock | Expires 1 April 2027 | [Page] |
Authorization, delegation, execution, and audit artifacts often identify an action using format-local content and digests. Those digests are not directly comparable when the formats select or encode material action fields differently. This document defines the Canonical Action Identifier (CAID): a typed action object, a canonicalization and digest suite, a compact identifier string, and versioned action-type definitions with required material fields. External value sets are bound to integrity-checked snapshots. The document specifies a strict JSON input profile, a fixed and ordered set of refusal reasons, and a digest that identifies the validation semantics of a type definition. It requests seven registries: suites, action types, field types, code formats, reason codes, mapping transforms, and mapping loss policies. It also defines an Action-Mapping Profile for projecting natively verified artifacts into a common action type, with the closed results EQUIVALENT_UNDER_PROFILE, NOT_EQUIVALENT, and INDETERMINATE. CAID carries no trust semantics. It does not establish identity, authority, authorization, execution, safety, or legal reliance.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 1 April 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Many formats for permits, receipts, mandates, delegation, and outcome evidence reference "the action" by digest. Each format selects an action representation and canonicalization appropriate to its own protocol. The resulting identifiers are useful inside that format but are not necessarily comparable across formats.¶
This creates two interoperability risks. The first is the material-fields failure: a digest over an underspecified object can omit the facts a relying party considers material. A digest computed over {"action": "wire"} commits to no amount, no currency, and no beneficiary; the artifact carrying it looks cryptographically bound to an action while committing to nothing that makes the action consequential. The second is the join failure: absent an agreed common definition, two artifacts about the same action, issued by different systems in different formats, can carry digests that cannot be compared without an explicit, reviewable mapping.¶
This document defines the Canonical Action Identifier (CAID) to close both gaps. A CAID names typed content: an action object that declares its own type, carries every material field that type requires, is canonicalized under a registered suite, and is digested. The identifier is a compact string embedding the version, the action type, the suite, and the digest. When formats cannot emit the same action object, an Action-Mapping Profile (Section 8) defines a relying-party-pinned, loss-aware projection into the common type and abstains when the comparison cannot be made.¶
As informative examples of the artifact classes that can carry a CAID: authorization receipts [I-D.schrock-ep-authorization-receipts], permit receipts [I-D.lee-orprg-permit-receipts], and execution outcome attestations [I-D.morrow-sogomonian-exec-outcome-attest] each reference an action by digest and could carry a CAID in place of or alongside their existing action reference. These citations are examples only; this document neither depends on nor modifies any of them, and carrying a CAID changes nothing about how any such artifact is verified.¶
A CAID deliberately carries no trust semantics. It does not assert that an action was authorized, executed, safe, or wise. Section 9 states the boundary.¶
An executor that uses CAID as an authorization or execution join computes the action object from the operation it is prepared to perform, or validates every material field against an authoritative source. A presenter-supplied identifier, discovery document, authorization challenge, or receipt cannot substitute for that derivation (Section 7).¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
Every operation of Section 1.2 that an implementation offers MUST return exactly the result that this document specifies for it, including every reason and the order of the reasons.¶
CAID processing consists of the operations below. Each one returns either its result or an ordered list of reasons from Appendix B. None of them fails in any other way: malformed or hostile input of any shape yields reasons, never an exception, a crash, or a partial result, given the memory that an input within the limits of Section 2.6 can require (Section 10.7).¶
Computation proceeds as follows:¶
JSON text ---decode (2.4), phase 0 (gate)---+
+--> data-model value
host value ---convert (2.5)-----------------+ |
v
phase 1 action_type is a valid type name (gate)
phase 2 exactly one conforming definition (gate)
phase 3 required fields present
phase 4 present fields valid for their types
phase 5 suite registered and implemented
phase 6 every number, down to depth 64 and within the
value count, in the data model
phase 7 every other value in the data model
|
v
canonical bytes (suite) --> digest (suite) --> base64url
--> caid:1:<action_type>:<suite>:<digest>
¶
The suite fixes the canonical bytes and the digest (Section 3.1).¶
An action object is a JSON object that identifies material action content. The same object is referenced, by CAID, from pre-execution artifacts (permits, challenges, receipts) and post-execution artifacts (outcome attestations, audit records, reliance events).¶
An action object:¶
A value of the data model is one of:¶
Surrogate code points are not Unicode scalar values [UNICODE], so a string that contains an unpaired surrogate is outside the model. The noncharacters are U+FDD0 through U+FDEF and the last two code points of every plane: U+FFFE and U+FFFF, U+1FFFE and U+1FFFF, and so on through U+10FFFE and U+10FFFF. I-JSON [RFC7493] excludes both, and so does this model.¶
The nesting depth of a value MUST NOT exceed 64. The outermost object or array is at depth 1, and each nested object or array adds one. The RFC 8785 encoding of an action object MUST NOT exceed 16,777,216 octets. That size is measured only for a value that is otherwise inside the model: a value outside it has no RFC 8785 encoding, so an oversized object that also holds a number outside the model yields unsupported_number and no unsupported_value. A host value is also bounded by a value count (Section 2.5); past it, a host action object yields unsupported_value from phase 7 and no unsupported_number, whatever else it holds, while phases 3 and 4 still run (Section 5), and any other host value is refused by the step that reads it (Section 2.6). The contents of an object or array nested deeper than 64 are not examined and are not counted toward the value count, so a number inside one adds no unsupported_number; a number held by an object or array at depth 64 or less still does. In a host value, a reference back to an enclosing object or array is a cycle: it counts as one value, and nothing beyond it is counted or examined, since what it refers to is counted and examined where it sits. An action object can be canonicalized when it is a value of the data model whose RFC 8785 encoding is at most 16,777,216 octets, whether or not the checks of computation phases 1 through 4 pass.¶
A value outside the data model is refused and is never rewritten into a value inside it. Computation reports a number outside the model as unsupported_number, and anything else, including nesting beyond 64 and an encoding beyond the limit, as unsupported_value (Section 5, phases 6 and 7). A number is examined only where an object or array at depth 64 or less holds it, and not at all in a host action object past the value count, which yields unsupported_value instead (Section 2.5).¶
The value of a number is the result of converting the exact decimal value it denotes to binary64 [IEEE754] under the roundTiesToEven rounding-direction attribute: the correctly rounded value. Under that attribute, a value of magnitude at least 2^1024 - 2^970 rounds to an infinity. A number is in the data model if and only if its value is finite, is an integer, and has magnitude at most 2^53-1 (9,007,199,254,740,991). It is then canonicalized as that integer, which RFC 8785 serializes in plain decimal form. The literal form is irrelevant: the literals 1e3, 1000.0, and 1000 all denote the integer 1000, exactly as an ECMAScript JSON parser sees them.¶
Rationale: ECMAScript number serialization is the leading source of cross-language canonicalization divergence, and a rule over the correctly rounded value is the only one that every language can implement identically. A rule over the literal form cannot be enforced by implementations, such as ECMAScript, whose JSON parsers do not preserve it.¶
An implementation that receives an action object or a mapping source as JSON text MUST decode it as specified in this section before validating, canonicalizing, or mapping it, and MUST refuse input that fails with the single reason malformed_json. An implementation MUST NOT compute, verify, or map over received JSON text through a decoder that does not meet these rules.¶
When the action object arrives as a member of a larger JSON text, such as a receipt, the carrying protocol identifies exactly one member that holds it, and the party that extracts it MUST apply rules 2 through 4 to the whole enclosing text, so that no object anywhere in that text, including one that holds a second candidate action object or a sibling CAID string, has two members with the same name. Rule 1 then limits the octets of the member value that encodes the action object, and rule 5 limits that value's nesting, counted from the action object as depth 1. A failure of any of these is malformed_json.¶
The input is refused unless all of the following hold:¶
The decoder never refuses a number token that [RFC8259] admits. It converts the token to its correctly rounded value, and Section 2.3 decides whether that value is in the data model. A very long literal is therefore not a decoding failure; if its value is not a suitable integer, computation reports unsupported_number.¶
Every failure of rules 1 through 5 yields malformed_json and nothing else, whichever rules failed. An implementation MAY report an informative detail beside the reason; that detail is not part of the result.¶
A type definition, a registry, an enum snapshot (Section 4.4 gives its shape), or a mapping profile read from JSON text is decoded under rules 2 through 5. Rule 1 does not apply to them: a registry or a value set can legitimately grow beyond any fixed bound, and these documents are configuration that a relying party pins by digest, not input to an operation. An implementation MUST NOT use such a document when it fails those rules. How the failure is reported is outside this document.¶
An implementation MAY also accept an action object, a type definition, a mapping profile, or a mapping source as a host value that the application constructed. Which host types represent each kind of the data model is a property of the implementation's language binding: a Python dict subclass, for example, can be read as an object through the dict's own accessors, while an ECMAScript object whose prototype is neither Object.prototype nor null is outside the model. Such an implementation:¶
A host language's representation of an absent member, such as an ECMAScript property whose value is undefined, counts as absent for field presence (phase 3). The value itself is not a value of the data model, so the object is also refused as unsupported_value. The reason for a string that contains an unpaired surrogate or a noncharacter depends on the entry point: malformed_json when it arrives as JSON text, and unsupported_value when it arrives as a host value. It never depends on the implementation language.¶
The limits of this document are collected in Table 1. Octets are UTF-8 octets. Every limit is a refusal with the reason shown, never an exception. A length limit on a string that a rule of Appendix A matches is checked before the rule is applied. The document encoding limit applies wherever a document other than an action object is canonicalized: a definition whose validation projection exceeds it is invalid_definition, a mapping source that exceeds it is source_not_canonicalizable, and an enum value array that exceeds it cannot match its values_sha256, so a present field is mistyped_field:<name>. The nesting limit and the value count apply in the same way to a host value that is not an action object (Section 2.5): a host definition whose validation projection nests deeper than 64 or exceeds the value count is invalid_definition, a host mapping profile that does is invalid_mapping_profile and has no digest (Section 8.3), and a host mapping source that does is source_not_canonicalizable.¶
| Limit | Value | Applies to | Refusal |
|---|---|---|---|
| JSON text | 33,554,432 octets | an action object or a mapping source received as JSON text (decode) | malformed_json |
| Nesting depth | 64 levels | every value | malformed_json (action object or mapping source as JSON text); unsupported_value (host action object); otherwise the reason of the step that reads the value |
| Canonical encoding | 16,777,216 octets | an action object | unsupported_value |
| Integer magnitude | 2^53-1 | every number held at depth 64 or less in a value within the value count | unsupported_number (action object); otherwise the reason of the step that reads the value |
| Value count | 33,554,432 values | a host value, each value counted once per path; an object or array nested deeper than 64, or a reference back to an enclosing one, counts as one value | unsupported_value and no unsupported_number (host action object); otherwise the reason of the step that reads the value |
| Document encoding | 134,217,728 octets | the RFC 8785 encoding of a validation projection, an enum value array, or a mapping source | the reason of the step that needs the encoding |
| Identifier | 1,024 octets | a CAID string | malformed_caid |
| Code system | 2,048 octets | the code_system of a code field | invalid_definition |
| Action type | 512 octets | an action type in a CAID, an action object, or a definition | malformed_caid; invalid_action_type; invalid_definition |
| Mapping rules | 1 to 128 rules | rules of a mapping profile | invalid_mapping_profile |
| Source path | at most 2,048 octets | each source path of a mapping profile | invalid_mapping_profile |
| Profile string | 1 to 512 octets | profile_id, media_type, schema, version, and target_action_type | invalid_mapping_profile |
| Omission reason | 1 to 2,048 octets | each omission reason | invalid_mapping_profile |
A definition, registry, enum snapshot, or mapping profile read from JSON text that nests deeper than 64 fails the decoding of Section 2.4, whose report this document leaves to the implementation. The JSON text limit is twice the canonical limit, which leaves room for insignificant whitespace and escapes. A tool call whose arguments would push an action object past the canonical limit is addressed in Section 4.7. A protocol that carries action objects can impose tighter limits on its own messages, such as a node count or a bound on each string; such a limit belongs to that protocol and is not a CAID refusal.¶
A suite names a canonicalization scheme and a digest algorithm, and fixes the length of its digest in octets. This document defines two suites; the CAID Suites registry (Section 12.1) carries one entry per suite. A registered suite is never removed, reassigned, or redefined.¶
A suite's status is not a type's status (Section 4.2.4). A suite is deprecated once practical collision attacks on its digest are known or anticipated (Section 12.1). Issuers then stop emitting new CAIDs under it (Section 10.1), and a relying party SHOULD remove it from the suites it accepts once the artifacts it relies on carry a CAID under a successor. Deprecation does not by itself make a verifier refuse a CAID: the suites that the relying party accepts decide (Section 3.5). A document that deprecates jcs-sha256 names the suite that becomes mandatory to implement in its place.¶
The digest is computed directly over the canonical bytes of the action object:¶
digest = H(canonical_bytes(action_object))¶
The suite fixes both the canonicalization that produces canonical_bytes and the digest function H (Section 3.1). For the two suites of this document, H is SHA-256.¶
There is deliberately no domain separation prefix. The object is self-typed via its action_type member, and a design goal of CAID is that an existing artifact format whose signed or hashed payload is itself a valid action object can reuse the digest it already computes over the same canonical bytes under the same suite. The goal is limited to that reuse: it does not make an identifier computed under another profile comparable with a CAID (Section 3.6). Conforming verifiers MUST check that the in-object action_type equals the action type carried in the CAID string (Section 6); that check, not a byte prefix, is what prevents cross-context reinterpretation. Section 10 states this trade explicitly, and states that a signature meant to commit to a CAID covers the whole identifier string.¶
The identifier is a string matched by the caid rule of Appendix A:¶
caid:1:<action_type>:<suite>:<digest-b64url>¶
For example, the action object of Appendix C.1 has this CAID (long lines in this document are folded as specified in [RFC8792]):¶
=============== NOTE: '\' line wrapping per RFC 8792 ================ caid:1:payment.release.1:jcs-sha256:liLG9pKgkLt3silrjf1wa0xIHz5YFrBB\ 9HI-arxrO1Y¶
The grammar uses the ABNF of [RFC5234] with the case-sensitive string syntax of [RFC7405], so the prefix is the lowercase string "caid". caid-version is the version of this identifier syntax and is "1" for this document. action-type is one or more lowercase dotted name segments followed by a final segment that is the integer version of the type (for example, payment.release.1). suite is a name from the suite registry, lowercase; the ABNF bounds the names a registry can assign, and a parser accepts only registered names (Section 3.4). digest is the digest encoded in base64url (Section 5 of [RFC4648]), unpadded and case-sensitive. A CAID is at most 1,024 octets, and its action type at most 512 octets (Section 2.6).¶
A suite whose digest is n octets encodes it in ceil(8n/6) characters, and the unused low bits of the final character are zero; Appendix A derives the digest syntax from n. For the two suites defined here the digest is exactly 43 characters, encoding 32 octets. Those 43 characters carry 258 bits, so the two low bits of the final character are unused and zero: the final character is one of "A", "E", "I", "M", "Q", "U", "Y", "c", "g", "k", "o", "s", "w", "0", "4", or "8".¶
A parser takes an identifier string and the suite registry and yields either the four components or exactly one reason. It applies these checks in order and stops at the first that fails:¶
A parser operates on the exact sequence of code points it is given. It MUST NOT trim, case-fold, Unicode-normalize, or percent-decode it; removing a transport encoding is the carrying protocol's job before parsing. As a consequence, a conforming parser refuses as malformed_caid: base64url padding ("=") in the digest; uppercase characters in the prefix, the action type, or the suite (the digest is case-sensitive, and both cases are significant there); empty segments anywhere, including empty dotted segments within the action type; any content after the digest, including trailing separators, whitespace, or additional fields; a digest that is not exactly the length of the named suite or whose final character encodes nonzero unused bits; and any caid-version other than "1", which is never guessed at or parsed leniently.¶
An unregistered suite is refused as unknown_suite rather than malformed_caid so that the reason says what the relying party needs to know. An identifier under a suite registered after the parser's registry snapshot is well formed, and the right response is a newer suite registry, not a report of corruption. Because a registered suite is never removed or redefined, an identifier that one registry snapshot refuses as malformed_caid is refused as malformed_caid by every later snapshot.¶
A registered suite that an implementation does not implement is not unknown to its parser: the identifier is well formed, and computation and verification report the suite as unknown_suite.¶
Two CAIDs are equal if and only if their strings are byte-equal. Cross-suite equivalence is out of scope: no procedure in this document relates a jcs-sha256 CAID to a cbor-sha256 CAID for the same object. An artifact MAY carry multiple CAIDs for one action, one per suite its issuer computed.¶
A relying party decides which suites it accepts, and a verifier MUST NOT accept a CAID under any other suite. When an artifact carries several CAIDs for one action, a verifier MUST verify every CAID whose suite it accepts and implements, and MUST treat the failure of any one as the failure of all. It MUST NOT select the CAID that verifies. When no carried CAID is under a suite that the verifier accepts and implements, verification fails. The relying party applies the suites it accepts to the suite of the parsed identifier (Section 3.4); the verification operation of Section 6 does not take that policy as an input.¶
Direct CAID equality is content equality, not inferred semantic equality. The strings "1.50" and "1.5" are different content and yield different digests. Per-field normalization guidance lives in the type definition's digest_notes (Section 4.2), and normalization is the issuer's job: the issuer normalizes before computing the CAID, and no party renormalizes afterward.¶
When two native formats cannot produce byte-equal action objects, the Action-Mapping Profile in Section 8 provides a narrower result: equivalence of the material projection under exact profiles pinned by a relying party. It does not claim general semantic equivalence.¶
Other specifications define their own canonical action identifiers over their own canonical material, some under a distinct namespace prefix. Section 3.2 limits the no-prefix design goal to the reuse of a digest over the same canonical bytes under the same suite. This section states the consequence for identifiers derived under other profiles.¶
An identifier derived under a different profile MUST NOT be compared for equality with a CAID derived under this document, in either direction, and a verifier MUST NOT treat inequality between them as evidence that two artifacts describe different actions. The profiles canonicalize different material; inequality carries no information.¶
An executor holding artifacts under two profiles for what it believes is one action derives an identifier under each profile from its own representation of the effect, compares within each profile, and joins on that representation rather than on the identifier strings. Where a relying party needs a stronger statement than same-representation, the Action-Mapping Profile in Section 8 is the mechanism, and its result is equivalence of a material projection under pinned profiles, not identifier equality.¶
[I-D.thallapelly-oasnt-caid] reaches the same conclusion for its own profile.¶
An action type name is one or more lowercase dotted name segments with a final segment that is the integer version of the type, per the action-type rule of Appendix A, and is at most 512 octets long (Section 2.6). Registered types live in the CAID Action Types registry (Section 12.2), one entry per versioned type. A registered name has at least two name segments before its version, such as payment.release.1; a local definition (Section 4.6) can use one.¶
Once a version is published, every declaration that affects validation or material meaning is immutable, with the one exception below. Adding or removing a required or optional field, retyping a field, changing a normalization rule or a field's semantics, or changing an enum set in any other way MUST be published as a new version of the type (payment.release.2), never as an in-place edit.¶
Monotone enum advance. A registry version MAY advance the value-set pin of an enum field in place, without a new type version, to a later edition that contains every member of the set the field accepted under the previous registry version, and it MUST NOT advance a pin in place to any other edition. The first pin of a field that previously resolved no set is such an advance, because that field accepted no value. Removing, renaming, or redefining a member is never an advance. The registry records each advance field by field.¶
Under this rule an action object that is valid under one registry version remains valid under every later version of the same registry, while an advance can make valid an object that an earlier version refused. An advance changes the type's definition_sha256 (Section 4.2.2), and every computation and verification result reports definition_sha256, so a relying party that needs exact reproducibility pins definition_sha256, and verification then refuses any other definition as definition_mismatch (Section 6). The registry keeps every definition_sha256 an entry has held (Section 12.2), so an earlier result can be replayed under the definition that produced it.¶
The members of a type definition are those described after the example below, and they are the same for local definitions (Section 4.6). The example is informative. It is the entry for payment.release.1 in registry version 5 of the reference registry, whose definition_sha256 is the value that Appendix C.1 shows. Line breaks inside its strings are for presentation only: each line break inside a string, and the indentation after it, reads as one space. Its long line is folded as specified in [RFC8792].¶
=============== NOTE: '\' line wrapping per RFC 8792 ================
{
"action_type": "payment.release.1",
"status": "active",
"risk_class": "irreversible-financial",
"summary": "Release of a payment instruction to settlement.",
"required_fields": [
{"name": "amount", "type": "amount-string",
"notes": "decimal string, no exponent, no leading '+',
no thousands separators"},
{"name": "currency", "type": "enum",
"values_ref": "ISO 4217 alpha-3",
"values_snapshot": "SIX ISO 4217 List One published 2026-09-17",
"values_sha256": "sha256:27f824317e9f271b956123fb77608daece5106\
e1ee8253a17769390855ade270"},
{"name": "beneficiary_account", "type": "digest",
"notes": "sha256:<lowercase hex> of the normalized account
identifier; normalization stated by the issuing
system of record"},
{"name": "payment_instruction_id", "type": "string"}
],
"optional_fields": [
{"name": "memo", "type": "string"}
],
"digest_notes": "amounts never renormalized after signing; the
system of record's form is canonical",
"references": []
}
¶
action_type is the versioned type name. status is the lifecycle state of the entry (Section 4.2.4). risk_class is a descriptive label for the consequence class of the action; it carries no verification semantics. required_fields declares the material fields, each with a name, a field type from Section 4.3, the members that field type defines, and optional notes. Notes guide issuers and never change how a field is validated. optional_fields declares fields the type recognizes but does not require. digest_notes carries per-type normalization guidance for issuers. references carries citations for the type's semantics. supersedes and superseded_by, when present, name the type version this one replaces and the one that replaces it.¶
A definition conforms if and only if all of the following hold:¶
An entry whose type is not a field type of Section 4.3 is not member-checked; that field is refused as mistyped_field:<name> whenever it is present, and it does not make the definition nonconforming. The same holds for a code field whose format is well formed but not a registered code format. A definition written for a later field type therefore still computes while the field is absent. The form of an enum field is not checked here: an enum that does not resolve fails as mistyped_field:<name> when present (Section 4.4). Members of a definition other than action_type, required_fields, and optional_fields are not checked and never affect validation; in a host value they are never read (Section 2.5).¶
A definition that does not conform is refused as invalid_definition.¶
The validation projection of a definition that meets the first four conditions of Section 4.2.1 is the object with exactly three members:¶
The arrays keep their order, because field order fixes the order of reasons (Section 5.1). Each entry is the definition's entry with its notes member removed and every other member kept.¶
definition_sha256 is "sha256:" followed by the 64 lowercase hexadecimal digits of the SHA-256 digest of the RFC 8785 encoding of the validation projection. It is defined only for a conforming definition; computing it for a nonconforming one yields invalid_definition.¶
Everything outside the projection, including status, risk_class, summary, notes, digest_notes, references, supersedes, and superseded_by, never changes validation. Two definitions with equal definition_sha256 therefore validate every action object identically, given the same enum snapshots and the same registered field types and code formats. A field whose type or code format an implementation does not know is refused whenever present (Section 4.3), so implementations that know different field types or code formats can disagree about such a field. definition_sha256 does not cover the normalization guidance that notes and digest_notes give issuers (Section 10.10). Computation and verification report the definition_sha256 of the definition they used (Sections 5 and 6), and verification accepts an expected value. The reference registry lists the definition_sha256 of every type in each registry version. Appendix C.4 gives an example.¶
This is the only digest this document calls a definition digest. A digest over a whole registry entry, including status or notes, identifies that entry's bytes, not its validation semantics, and a specification that uses one names it differently.¶
To resolve the definition for an action type, an implementation:¶
The order in which definitions are configured never affects the result. A local definition that shares a name with a registered one resolves only when the two have the same validation projection.¶
status is "active" or "deprecated". Status never affects resolution, computation, or verification: a deprecated type resolves, computes, and verifies wherever its fields resolve, and deprecating a type never makes an earlier result invalid. Deprecation tells issuers to use the successor that superseded_by names; supersedes, on the successor, names its predecessor. Both are informative and outside definition_sha256. An issuer MAY decline to issue new CAIDs under a deprecated type as a matter of policy. A verifier MUST NOT refuse a CAID because its type is deprecated.¶
A field type fixes which values a field accepts. Every check applies to the whole value: nothing may precede or follow the matched text, not even a line feed. A string field accepts the empty string, and a required field that holds "" is present. The field types of this document are listed below; the CAID Field Types registry (Section 12.3) records them.¶
A decimal amount carried as a string that matches the amount-string rule of Appendix A. There is no exponent, leading "+", whitespace, or thousands separator, and the integer part has no leading zero. For example, "0", "0.50", "-0", and "-0.50" match, while "01.5", "00", "+1", "1e3", ".5", "1.", and "1,000" do not. The rule is lexical and never normalizes: "0.50" and "0.5" produce different digests (Section 3.5). A string that does not match is refused as invalid_amount:<name>, and a value that is not a string as mistyped_field:<name>.¶
Except where stated above, a present field whose value fails its field type is refused as mistyped_field:<name>. A field whose type is not one of these is refused as mistyped_field:<name> whenever it is present.¶
A new validation constraint on an existing field type is always a new field type, never a new member on an existing type. An implementation of an earlier revision ignores an unknown member and would accept what a later one refuses, while an unknown field type makes it refuse. A registered field type or code format is never removed, renamed, or redefined; a changed grammar is registered as a new code format name.¶
A member of a field definition is present when its name is
present; a values or values_ref member whose value
is null is present and malformed, not absent. An enum definition
MUST take exactly one of three forms:¶
values, and no values_ref member.¶
inline:. The
prefix is case-sensitive. The text after it is split at every
"|" character, and each member is trimmed of leading and
trailing U+0020 SPACE characters only; no other whitespace or
control character is trimmed. The members
MUST be non-empty and duplicate-free. A
values member that accompanies this form
MUST equal the resulting list exactly, in
order.¶
values_snapshot and values_sha256 members that
are non-empty strings. The
pinned array is the definition's own values member when
that member is present, and otherwise a locally available snapshot
whose values_ref, values_snapshot, and values_sha256 all match
exactly. A supplied snapshot never replaces an embedded
values member.¶
For an external reference, the issuer or verifier
MUST check that the pinned array is a non-empty,
duplicate-free array of non-empty strings, compute SHA-256 over the
RFC 8785 canonical JSON encoding of that complete array, and compare
the lowercase hexadecimal result, prefixed with sha256:, to
values_sha256 before testing membership.¶
An enum snapshot read from JSON text is a JSON object whose values_ref, values_snapshot, and values_sha256 members are strings and whose values member is the pinned array. Other members, such as a record of provenance, never affect resolution. A set of snapshots read from JSON text is a JSON array of such objects.¶
A definition in none of these forms, a bare external values_ref, a
missing or unresolved snapshot, a digest mismatch, or a value absent
from the resolved list MUST fail as
mistyped_field:<name> whenever the field is present.
A type with such a field among its required fields therefore
cannot produce or verify any CAID until the value set is pinned.
Computation and verification
MUST NOT fetch a mutable network resource to resolve an
enum. An external source's later additions, removals, or corrections
do not change a pinned array. Publishing a different accepted array
requires a new action-type version, except for the monotone advance
of Section 4.1.¶
Some material values come from code systems that are large, change often, or are licensed: diagnosis and procedure codes, drug product codes, payment return reasons. A pinned value set cannot follow such a system without a new type version for every edition, and a licensed system cannot be redistributed as a snapshot. A code field identifies such a value by its system and its syntax instead:¶
{"name": "diagnosis_code", "type": "code",
"code_system": "http://hl7.org/fhir/sid/icd-10-cm",
"format": "icd-10-cm"}
¶
CAID pins the syntax and the system of a code field, never a value set. Whether a code exists in some edition of its system, is billable, or suits the action is a question for the relying party. A registry MUST NOT publish a value set for a code field. For a licensed system it MUST NOT redistribute codes, descriptors, or value sets at all; CPT, whose codes the American Medical Association licenses, is the leading example. The hcpcs format accepts HCPCS Level I (CPT) and Level II codes by syntax alone, so a type can carry either without the registry carrying a single CPT code. Each lexical form of a code is its own format, so that no format admits two spellings of one code: the 11-digit and the hyphenated 10-digit forms of a National Drug Code are the formats ndc-11 and ndc-10-hyphenated.¶
A code system whose complete value list is small and stable, and whose maintenance agency publishes that list free of charge, such as ISO 3166-1 alpha-2 [ISO3166-1] or ISO 4217 [ISO4217], is an enum pinned to a snapshot (Section 4.4), never a code format. The Nacha Standard Entry Class codes are few, but their complete list is published only in the licensed Nacha Operating Rules, so nacha-sec is a code format.¶
Every code format matches only strings of bounded length and compiles to a regular expression with no nested quantifiers, no quantified alternatives that can begin with the same character, and no optional repetition whose first characters overlap what can follow it. Matching therefore takes time linear in the length of the value, in backtracking and automaton engines alike (Section 10.8); this document calls that the linear-matching property. A new code format is registered in the CAID Code Formats registry (Section 12.4) with its ABNF and must have this property.¶
There is no reserved private-use name syntax. The distinction between registered and local types is presence: a type is either present in a definition source the implementation is configured with (the public registry, or a local definitions file in the same schema) or it is unknown. A deployment inside a single vendor's boundary can therefore use CAID with purely local definitions. Parties that use CAID across domains MUST pin the same exact definition, registry snapshot, or definition_sha256. A local deployment SHOULD give its types an organization-specific first segment (for example, acmecorp.ledger.close.1), so that a later registration does not take the same name. A local definition that collides by name with a different registered definition MUST NOT be treated as interoperable, and resolution refuses the pair when both are configured (Section 4.2.3).¶
A definition source read from JSON text is a JSON array of type definitions, or a JSON object whose types member is such an array, as the reference registry is. Other members of that object never affect resolution.¶
An unknown type is a refusal for issuers and verifiers alike. A verifier that holds no definition for the type reports invalid_object with the detail unknown_action_type (Section 6). A deployment that correlates identifiers of unknown types by digest alone is not performing CAID verification and MUST NOT report the result as valid.¶
Agent frameworks invoke a named tool with an argument object. That is a common material action in agent deployments. This section registers a type for it so that its action object is fixed by the registry rather than by an implementation. The registration is the tool.call.1 entry of the reference registry [CAID-REGISTRY], reproduced here member for member:¶
{
"action_type": "tool.call.1",
"status": "active",
"risk_class": "varies-by-tool",
"summary": "Invocation of a named agent-framework tool with a
complete argument object.",
"required_fields": [
{"name": "target",
"type": "string",
"notes": "stable identity of the service that will execute the
call, supplied by the governing profile and compared
case-sensitively; use the literal local only when execution
occurs in the caller process; never infer this value from
ambient routing metadata"},
{"name": "tool",
"type": "string",
"notes": "tool name exactly as the invoking framework declares
it"},
{"name": "args",
"type": "object",
"notes": "complete argument object handed to the tool after
framework defaults; all nested values remain subject to the
CAID numeric profile"}
],
"optional_fields": [
{"name": "occurrence_id",
"type": "string",
"notes": "executor occurrence identity when the relying profile
requires two otherwise identical invocations to remain
distinct"}
],
"digest_notes": "The CAID Action Object includes action_type,
target, tool, args, and occurrence_id when present.
Framework-local selector or routing hints derived from args are
not separate members. Existing base:sha256:<hex> executor-binding
strings are a different profile and are not CAIDs.",
"references": []
}
¶
Line breaks inside the strings of this registration are for presentation only; each line break and the indentation after it read as one space.¶
occurrence_id is optional because args can already carry an occurrence identifier. A deployment that needs to tell repeated identical calls apart (Section 10.4) MUST include occurrence_id unless args already carries an identifier that tells them apart, and one whose calls need to resist recovery by guessing (Section 11) MUST include an occurrence_id that carries at least 128 bits of entropy unless args already carries an identifier with at least that entropy.¶
The member is named args. An implementation
MUST NOT substitute another name such as
arguments for the same content: the member name is
inside the canonical bytes, so a rename produces a different
digest for an identical call, and two conforming-looking
adapters then emit identifiers that never compare equal.¶
A framework-derived selector or routing key
MUST NOT be added as a member when it is
computed from values already present in args. Such a
hint adds no material content and, being optional in practice,
makes the digest depend on whether a particular adapter chose
to compute it. This does not apply to target, which is
material, is a required field, and is not derived from
args.¶
The target field is required because a tool name is
not unique across providers. Where a caller can reach two
services offering the same tool name, an object omitting the
callee yields one identifier for two different executions, and
a permit issued against one provider recomputes equal against
the other. An issuer MUST NOT omit
target merely because its deployment currently reaches
one provider: the identifier can outlive that assumption. The
governing profile defines the stable target identifier;
implementations compare its exact, case-sensitive string and
MUST NOT infer it from ambient connection state,
a display label, or a derived routing hint.¶
A tool.call.1 action object consists of exactly
action_type, target, tool, and
args, plus occurrence_id when present. An issuer
MUST NOT add any other member to a tool.call.1 action
object: content material to the call belongs in args,
target, or occurrence_id, and any other member
makes the digest depend on the adapter. A verifier still accepts
extra members (Section 2.1). The complete object
is canonicalized and encoded as the CAID string in
Section 3. Existing framework-local strings,
such as the base:sha256:<hex> executor-binding
strings that the digest_notes name, are not CAIDs and
MUST NOT be compared with one.¶
For example, this action object:¶
{
"action_type": "tool.call.1",
"target": "https://payments.example",
"tool": "payment.release",
"args": {
"amount_usd": 4000,
"beneficiary": "vendor@example.com",
"memo": "invoice 7781"
}
}
¶
has the following CAID under jcs-sha256:¶
=============== NOTE: '\' line wrapping per RFC 8792 ================ caid:1:tool.call.1:jcs-sha256:FdawgFwgN5tAtiZa-SCkVDrV3dS9w1yeXVQaDa\ ZLQQQ¶
The example's args carries amount_usd as a JSON number, as the tool receives it; Section 10.6 states how a relying party that evaluates such a number reads it.¶
This registration is motivated by a divergence observed in the author's own software: two of its framework adapters hashed the same argument object under different member names (args and arguments), and one also hashed a derived selector. The resulting strings were not CAIDs, but the incident showed why a future cross-framework CAID profile cannot leave its material member names to implementers.¶
All nested members of args remain subject to the
data model of Section 2.2. A framework call
containing a fractional JSON number can use its own exact-action
binding profile, or map the quantity to a CAID type that carries
the value as a string; it is not a conforming
tool.call.1 CAID.¶
A call whose arguments carry a large body inline, such as a file or an image, can push the action object past the canonical limit of 16,777,216 octets (Section 2.6). Such a call is not a conforming tool.call.1 CAID. It is identified by a tool-specific type in which the large member is a digest field over the body's octets: the digest binds the body without placing it in the action object, and the body travels beside the artifact.¶
A conforming issuer computes a CAID from an action object (JSON text or a host value), a suite name, its configured definition sources, and the enum snapshots available to it. A suite, definition-source, or enum-snapshot option of the wrong type is treated as absent. An absent suite yields unknown_suite: a suite given as anything other than a string is no suite. Absent definition sources leave every action type unknown, so a valid action type is unknown_action_type. Absent enum snapshots refuse nothing by themselves; they matter only to a present field whose external enum has no embedded values member, which is then mistyped_field:<name> (Section 4.4).¶
Computation evaluates the phases of Table 2 in order. Phases 0 through 2 are gates: the first gate that fails yields exactly one reason, and computation stops. When the gates pass, phases 3 through 7 are all evaluated, and every reason they produce is collected. The reason strings are normative. Each reason of phases 3 and 4 takes the name of the field it concerns as its parameter (Appendix B), as in missing_material_field:<name>.¶
A field is present when the object has a member of that name of its own. A name that an implementation's object model inherits from elsewhere, for example through an ECMAScript prototype, is not present. Phase 4 checks only present fields that the definition declares, in required_fields or in optional_fields. Enum validation includes the snapshot resolution and integrity check of Section 4.4. A string that contains an unpaired surrogate has no UTF-8 encoding, and Section 3.2.2.2 of [RFC8785] requires a JCS implementation to refuse it; phase 7 is that refusal. A host value of kind "object" that holds values outside the data model passes phase 1 and is refused by phase 6 or 7. Phase 6 does not examine values inside an object or array nested deeper than 64, or beyond a reference back to an enclosing object or array, and the value count does not count them (Section 2.2). When a host action object is made of more values than the value count (Section 2.5), phase 6 yields no reason and phase 7 yields unsupported_value, whatever the value holds; phases 3 and 4 still run.¶
| Phase | Reasons | Refused when |
|---|---|---|
| 0 | malformed_json | the object is JSON text that fails Section 2.4 |
| 1 | invalid_action_type | the value's kind (Section 6.1) is not "object", or its action_type member is absent, is not a string, is longer than 512 octets, or does not match the action-type rule of Appendix A |
| 2 | unknown_action_type, invalid_definition | resolution fails (Section 4.2.3) |
| 3 | missing_material_field | a required field is not present |
| 4 | mistyped_field, invalid_amount, invalid_code | a present declared field fails its field type (Section 4.3) |
| 5 | unknown_suite | the suite is not a registered suite that the implementation implements |
| 6 | unsupported_number | a number that an object or array at depth 64 or less holds, in an object within the value count, is outside the data model |
| 7 | unsupported_value | any other value is outside the data model, including a string or member name with an unpaired surrogate or a noncharacter, nesting beyond 64, a canonical encoding beyond 16,777,216 octets, and a host value past the value count |
The collected reasons are sorted by phase. Within phase 3 they are sorted by the field's index in required_fields, and within phase 4 by the field's index in required_fields followed by optional_fields. Identical reason strings are then reduced to the first. A field yields at most one phase 4 reason, and phases 5, 6, and 7 yield at most one reason each.¶
The order does not depend on the order in which an implementation traverses the object, so every implementation, and every suite, reports the same list for the same input. In particular unsupported_number always precedes unsupported_value. The result is an ordered list; implementations MUST NOT reorder, merge, or add reasons. Appendix C.2 gives an example.¶
On success, computation returns three strings: the CAID; the digest, which is the suite's digest of the canonical bytes, written for the two suites of this document as "sha256:" followed by 64 lowercase hexadecimal digits; and the definition_sha256 of the resolved definition. On failure it returns the ordered list of reasons and no identifier. Computation is fail-closed: malformed or junk input of any shape yields reasons, never an exception or a partial identifier (Section 1.2).¶
A conforming verifier checks a presented CAID string against a presented action object (JSON text or a host value), using its own configured definition sources and enum snapshots and, when the relying party supplies one, an expected definition_sha256. An expected definition_sha256 that is supplied is never treated as absent: phase 5 compares it, whatever its type, with the definition_sha256 of the resolved definition, and any value other than that string is definition_mismatch. A host language's representation of an absent option, such as an ECMAScript undefined, is absent. The verifier evaluates the phases of Table 3 in order. Phases 1 through 3 are gates that yield exactly one reason and stop verification. Phases 4 through 7 are all evaluated, and each yields at most one reason.¶
| Phase | Reasons | Refused when |
|---|---|---|
| 1 | malformed_caid, unknown_suite | parsing fails (Section 3.4) |
| 2 | malformed_json | the object is JSON text that fails Section 2.4 |
| 3 | invalid_object | the value's kind is not "object" |
| 4 | action_type_mismatch | the object's action_type member is absent, is not a string, or is not the action type in the CAID |
| 5 | definition_mismatch | an expected definition_sha256 was supplied, a conforming definition resolved, and the expected value is not a string equal to its definition_sha256 |
| 6 | unknown_suite, digest_mismatch | unknown_suite when the verifier does not implement the CAID's suite; otherwise digest_mismatch when the object can be canonicalized (Section 2.2) and its digest differs from the CAID's |
| 7 | invalid_object | computation phases 1 through 4, 6, and 7 over the object yield any reason |
The in-object action type check of phase 4 is mandatory; see Section 3.2 and Section 10. No digest comparison is made for an object that cannot be canonicalized. An identifier whose object fails validation is invalid_object, not merely mismatched: a digest that matches an invalid object still binds nothing material. The suite check of computation phase 5 is not part of phase 7, because phase 6 checks the suite of the presented CAID.¶
Phases 5 and 7 resolve the definition of the object's own action_type member, not the type named in the CAID. They run even when phase 4 reports action_type_mismatch, and the definition_sha256 in the result is that of the object's type.¶
Phase 1 is the strict parse of Section 3.4, which yields malformed_caid or, for a grammatical suite outside the suite registry, unknown_suite. Phase 6 yields unknown_suite for a registered suite that the verifier does not implement.¶
The result has the members valid, reasons, and details, plus definition_sha256 whenever a conforming definition resolved, whether or not the result is valid. valid is true if and only if reasons is empty. Verification is deterministic and offline: the same inputs produce the same result, and any third party with the object, the identifier, the definitions, and any referenced enum snapshots can replay the check without contacting anyone.¶
details lists one entry for each reason, in the order of reasons. The exception is invalid_object, which contributes one entry for each computation reason it stands for instead of one for itself: for verification phase 7, the reasons of computation phases 1 through 4, 6, and 7; for verification phase 3, the computation reason for the value, invalid_action_type. Each entry is an object with exactly these four members:¶
A kind is one of "absent", "null", "boolean", "number", "string", "array", "object", and "unsupported". Every JSON number, and every host value that the language binding represents as a number (Section 2.5), is "number", whether or not it is in the data model; "unsupported" is a host value of no JSON kind, such as a map, a date, or a numeric type that the binding does not represent as a number. Details carry no other information. They are deterministic: two conforming verifiers given the same inputs produce the same details, as in the example of Appendix C.3.¶
| Reason | rule | field | observed |
|---|---|---|---|
| malformed_json | json-text | null | null |
| malformed_caid | caid | null | the CAID argument |
| unknown_suite | suite | null | null |
| invalid_action_type | action-type | action_type | the member |
| unknown_action_type | definition-resolution | null | null |
| invalid_definition | definition-conformance | null | null |
| definition_mismatch | definition-sha256 | null | null |
| missing_material_field | required-field | the parameter | the member |
| mistyped_field | field-type | the parameter | the member |
| invalid_amount | amount-string | the parameter | the member |
| invalid_code | code-format | the parameter | the member |
| unsupported_number | number | null | null |
| unsupported_value | data-model | null | null |
| action_type_mismatch | action-type-equal | action_type | the member |
| digest_mismatch | digest-equal | null | null |
When a CAID is used at an effect boundary, the enforcing executor MUST construct the action object from the operation it is prepared to invoke, or MUST validate every material field against a relying-party-controlled system of record. It MUST recompute the CAID from that object. It MUST then invoke the operation using only the values it constructed or validated, read from the data-model value that Section 2.4 or Section 2.5 produced, and MUST NOT use any other member, such as an optional field or an extra member, unless it validated that member too. It MUST NOT re-read the action object through a separate parser to drive the invocation (Section 10.6). A CAID received from a presenter MAY be compared with the recomputed value, but MUST NOT replace executor-side construction.¶
Discovery documents, authorization challenges, receipts, permits, and other evidence can carry a CAID. Their identifiers are claims about content under their own integrity and trust rules. Before comparing such an identifier with the executor-computed CAID, the artifact MUST be verified under its native specification and the relying party's native trust anchors (Section 8.1). If the native representation differs from the CAID action object, comparison MUST use an exact, relying-party-pinned Action-Mapping Profile as specified in Section 8.¶
A successful comparison establishes only the matching result in this document. Evidence satisfaction, local authorization, one-time consumption, invocation, execution status, reconciliation, revocation, and remedy remain separate protocol decisions. The Action Evidence Boundary [I-D.schrock-action-evidence-boundary] and the Authorization Evidence Chain [I-D.schrock-ep-authorization-evidence-chain] are informative examples of specifications that keep those decisions separate.¶
A multi-stage authorization program MAY bind a root CAID and the digests of predecessor stage receipts in a separate program artifact. It MUST NOT reinterpret the root CAID as proof that any stage completed. A stage that authorizes a materially different action uses a different CAID. Program order, quorum, stage completion, and authority attenuation are outside the CAID trust boundary.¶
Two native formats can represent the same material action with different member names, nesting, or format-local metadata. Direct digest comparison cannot establish whether those representations denote the same material action. This section defines a narrow, explicit projection into a common CAID action type. The projection is identified by its digest and pinned by the relying party.¶
A mapping result is content correlation only. It does not establish identity, authority, authorization, provenance, execution, or legal reliance.¶
Before mapping, each source artifact MUST be verified under its native specification and the relying party's native trust anchors. The source object supplied to the mapper MUST be the payload whose integrity that native verification established, or a projection for which the native adapter has established that every mapped source field is covered by the native integrity mechanism. A mapper MUST NOT read an unsigned sibling member and treat it as part of a signed payload.¶
The source media type, schema identifier, and version supplied to the mapper MUST come from relying-party configuration or the native verifier. They MUST NOT be accepted solely from a label inside the presenter-controlled artifact. CAID mapping does not replace native verification.¶
The mapping interface MUST receive a native verification result that is the boolean true through a relying-party-controlled adapter or equivalent trusted call path. That result is an API precondition, not a member a presenter can place in the artifact. Any other native verification result, including an absent one, yields INDETERMINATE.¶
A version 1 profile is a JSON object with the following shape:¶
{
"@version": "CAID-MAPPING-PROFILE-v1",
"profile_id": "urn:example:map:checkout-to-order:1",
"source_format": {
"media_type": "application/example-checkout+json",
"schema": "urn:example:checkout:1",
"version": "1"
},
"target_action_type": "order.place.1",
"loss_policy": "no-material-field-loss",
"material_source_paths": [
"/checkout/order_id",
"/checkout/merchant_id",
"/checkout/total_amount",
"/checkout/currency",
"/checkout/line_items",
"/checkout/customer_id",
"/checkout/fulfillment"
],
"rules": [
{"source_path": "/checkout/order_id",
"target_field": "order_id", "transform": "copy"},
{"source_path": "/checkout/merchant_id",
"target_field": "merchant_ref", "transform": "sha256-utf8"},
{"source_path": "/checkout/total_amount",
"target_field": "total_amount", "transform": "copy"},
{"source_path": "/checkout/currency",
"target_field": "currency", "transform": "copy"},
{"source_path": "/checkout/line_items",
"target_field": "items_digest", "transform": "sha256-jcs"},
{"source_path": "/checkout/customer_id",
"target_field": "customer_ref", "transform": "sha256-utf8"},
{"source_path": "/checkout/fulfillment",
"target_field": "fulfillment_ref", "transform": "sha256-jcs"}
]
}
¶
The profile digest is the lowercase hexadecimal SHA-256 digest of the profile's JCS serialization, prefixed with "sha256:". The relying party MUST pin that exact digest. A profile identifier by itself is not a trust anchor.¶
Version 1 is a closed object model. The members of a profile are exactly @version, profile_id, source_format, target_action_type, loss_policy, material_source_paths, rules, and, optionally, omitted_source_fields. source_format has exactly media_type, schema, and version; a rule has exactly source_path, target_field, and transform; an omitted_source_fields entry has exactly source_path and reason. A mapper MUST reject an unknown member at any of these levels, and MUST treat a member whose value is null as malformed. This prevents a misspelled or future policy member from being covered by the profile digest while being silently ignored by the mapping algorithm.¶
The members are constrained as follows. Lengths are counted in UTF-8 octets (Section 2.6), and strings are compared as exact sequences of code points.¶
The loss policies form a closed set, recorded in the CAID Mapping Loss Policies registry (Section 12.7):¶
The transforms form a closed set, recorded in the CAID Mapping Transforms registry (Section 12.6):¶
A mapper MUST refuse an unregistered transform or loss policy. Version 1 performs no unit conversion, currency conversion, date inference, case folding, or lossy normalization.¶
A mapper takes a source, a profile, the verifier-derived source descriptor, the relying party's pinned profile digest, the native verification result, definition sources, enum snapshots, and a suite. The suite is jcs-sha256 when the suite argument is absent, and only then. A suite argument that is present but is not a string is no suite, and stage D reports mapped_action:unknown_suite. The mapper runs four stages. Each ends in a gate: when a stage produces a reason, mapping stops after that stage, except that stage B always runs after stage A and the two stop together.¶
A source received as JSON text is decoded as specified in Section 2.4 before mapping begins. A decoding failure is the decode operation's malformed_json. It yields no mapping result and no comparison, and it is never a mapping or comparison reason.¶
A successful mapping result carries the source digest ("sha256:" and the SHA-256 of the source's JCS encoding), the profile digest, the projected action object, the resulting CAID, its digest, the definition_sha256 of the target type's definition, and the suite. A comparison result retains both mapping results. A consumer that persists or transmits only the final verdict or CAID loses the information needed to reproduce which profiles and source objects produced that result. Appendix C.6 gives an example.¶
Both mapping results of a comparison are computed under one suite. Comparison of two mapping results returns exactly one of:¶
A consumer MUST NOT convert INDETERMINATE into equivalence. It also MUST NOT treat EQUIVALENT_UNDER_PROFILE as authorization. Authorization remains a separate relying-party decision over separately verified evidence.¶
The mapping reasons are listed in Table 11, except unknown_action_type and invalid_definition, which stage A shares with computation and which Table 10 lists. Within a mapping result, stage A reasons come first, in the order invalid_mapping_profile, unknown_action_type, invalid_definition, and then the unmapped_material_field reasons in required_fields order, followed by stage B reasons in the order listed above; identical reason strings are reduced to the first. Stage C reasons are in rule order, and stage D reasons in computation order. These are the only mapping reasons: a conforming mapper produces a result for every input and never reports an internal fault as a reason.¶
The algorithm proves that a pinned profile maps every field the target action type declares material. It cannot prove that the profile author correctly identified every material concept in the native source schema. The relying party's selection and review of mapping profiles is therefore an explicit policy assumption. Profiles SHOULD be versioned, publicly reviewable, and accompanied by positive, negative, and indeterminate conformance vectors.¶
Under the selected suite's security assumptions (Section 10.1), recomputing a CAID establishes that an identifier commits to the supplied canonical typed content. Comparing CAIDs emitted directly from the same action type, or emitted through pinned Action-Mapping Profiles, provides the scoped matching result defined here. It does not prove that the action was authorized, executed, safe, or wise. It confers no trust, names no humans, and replaces no verifier: every artifact that carries a CAID still verifies inside its own trust boundary under its own specification, exactly as it did before carrying one.¶
In particular:¶
CAID defines a data format and processing rules, not a protocol exchange. Issuers and presenters may be adversarial and control every octet of an action object, including extra members (Sections 1.2 and 7). Definition sources and enum snapshots are trusted only through relying-party pins (Sections 4.4 and 10.10), and mapping profiles only through relying-party selection (Section 8.5). Insertion, removal, or substitution of a CAID in transit is governed by the carrying artifact's own integrity mechanism (Sections 7 and 10.2). The property that CAID provides is stated in Section 9; everything else there is a non-goal.¶
When the party that constructs the action object is trusted, the binding between an identifier and its content rests on the second-preimage resistance of the suite's digest. When that party may be adversarial, for example an agent that proposes an action for approval and later requests the operation, the binding rests on collision resistance: free-form fields such as the args of tool.call.1, and extra members (Section 2.1), give such a party bytes to vary. SHA-256 provides about 128 bits of collision resistance and more against second preimages. If practical collision attacks on SHA-256 become known, the migration path is suite agility: a new suite is registered and issuers move to it, emitting new identifiers (Section 3.1). There is no in-place algorithm change within a suite, ever. A suite's digest is at least 256 bits, and the suite registry does not accept a digest formed by truncating the output of a hash function to fewer octets than that function specifies; a function whose specification defines a shorter output, such as SHA‑384 or SHA‑512/256, is not such a truncation (Section 12.1).¶
Suite agility moves only the CAID digest. Whatever the suite, the following are fixed to SHA-256: definition_sha256 (Section 4.2.2), values_sha256 (Section 4.4), the profile and source digests of a mapping (Sections 8.2 and 8.3), the registry-file digest (Section 12.2), the digest field type, and the sha256-utf8, sha256-jcs, and sha256-hex-to-digest transforms. The "sha256:" prefix of each labels its algorithm. A new digest field type or transform is added through its registry (Sections 12.3 and 12.6). Replacing the algorithm of definition_sha256, of values_sha256, or of the profile and source digests requires a revision of this document, which follows the guidance of [RFC7696]. Equal definition_sha256 values in resolution (Section 4.2.3) and pinned profile digests also rest on collision resistance.¶
A new suite does not migrate digests embedded in action objects, so an object's commitment to a field that it carries only as a digest remains bounded by SHA-256 until the type is versioned with a new field type.¶
The digest input carries no domain separation prefix, deliberately, so that existing formats can adopt a digest they may already compute over canonical bytes (Section 3.2). The compensating control is mandatory: verifiers MUST enforce the check that the in-object action_type equals the type carried in the CAID string. Skipping that check re-opens cross-context reinterpretation, in which bytes canonicalized for one context are presented under a type label from another. The check is phase 4 of Section 6 and is not optional.¶
Because extra members are permitted, one JSON object can be both a valid action object and another protocol's signed payload, and its bare digest is the same in both roles. Reusing an existing digest for correlation is permitted. A signature, MAC, or commitment that is to be read as a commitment to a CAID MUST cover the complete CAID string, whose caid:1:<type>:<suite>: prefix supplies the domain separation that the digest omits.¶
A CAID MUST NOT be abbreviated for comparison. A display MAY abbreviate an identifier, but an abbreviated form is not a CAID: parsers refuse it (Section 3.4), and a relying party MUST NOT compare it with anything.¶
Identical action objects have identical CAIDs. A CAID is not a nonce and gives no replay protection. A consume-once permit keyed on a CAID alone blocks a legitimate second identical operation, and a permit without consume-once semantics authorizes both. A type that must distinguish repeated identical operations SHOULD require an occurrence identifier. The occurrence_id member of tool.call.1 is optional, so two identical tool calls without it share a CAID; Section 4.7 says when a deployment includes it.¶
An artifact that carries several CAIDs for one action invites a verifier to check only the one that passes. Section 3.5 forbids that: a verifier MUST verify every CAID whose suite it accepts and implements, and a relying party pins the suites it accepts, so an attacker cannot steer verification to a weaker suite. That rule protects only CAIDs that the carrying artifact integrity-protects as a set (Section 10.2). Against a suite that has weakened, the protection is the relying party's accepted set: an artifact that carries only a CAID under an accepted but weakened suite verifies, so a relying party removes a deprecated suite from its accepted set (Section 3.1).¶
Cross-language number serialization is the historical source of canonicalization divergence, which is why numbers are restricted to the value-based integer rule and money is carried as amount-string (Section 2.3). The canonical form of an action object is the one its suite fixes (Section 3.1); no other serialization, however stable it appears, is a conforming digest input.¶
Host JSON parsers differ in ways that change identified content. Some keep the first of two duplicate members and some the last. Some replace invalid UTF-8 or an unpaired surrogate escape with U+FFFD, so two different inputs yield one CAID. Some accept a byte order mark, NaN, or unbounded nesting. Each difference lets a native verifier and a CAID computation read different values from one text. Section 2.4 fixes one reading of JSON text, including the text that encloses an embedded action object, and Section 2.5 extends it to host values. For every text on which the parsers above disagree about structure or strings, Section 2.4 refuses the text, so no CAID exists to disagree with a native reading. It does not constrain a native verifier, and one number differential remains by design: Section 2.3 maps literals with different exact decimal values to one integer, such as 3999.99999999999999999, 4000.0000000000001, and 4000, or 1e-400 and 0. A native verifier or policy engine that reads numbers as exact decimals can therefore evaluate a value other than the one a matching CAID commits to. A relying party that evaluates a number in an action object SHOULD evaluate the value of Section 2.3; otherwise, before relying on a CAID match, it SHOULD refuse any number token that has a fraction or exponent part. A quantity that policy compares against a threshold SHOULD be typed amount-string.¶
Member names raise a related problem. Some host decoders match member names case-insensitively, as Go's encoding/json does when it decodes into a struct, and some compare names by canonical equivalence, as Swift's String type does. The object {"amount":"1.00","Amount":"99999.00"} has a valid CAID that commits to both members, yet a case-insensitive decoder reads 99999.00 for amount, and a decoder that compares names by canonical equivalence treats "caf" followed by U+00E9 and "cafe" followed by U+0301 as one name in the same way. Section 7 therefore requires an executor to act only on the values it validated.¶
An implementation bounds the work one input can cause: the text limit is checked before decoding, the depth limit before any recursive traversal, and the canonical limit before hashing (Section 2.6). Mapping profiles are bounded in rules and string lengths. Registries, definitions, and enum snapshots are exempt from the text limit, so an implementation bounds the size of the configuration it loads by its own policy. Every limit is a refusal with a reason, never an exception or a crash. The lengths of identifiers, action types, and code_system values are checked before any pattern runs, and a host value is bounded by its value count, so no input reaches the internal limits of a regular expression engine or walks an exponential expansion of shared references.¶
The limits still admit large inputs. Decoding and canonicalizing an action object near the 33,554,432-octet text limit can take seconds and more than a gigabyte of memory in some implementations. A protocol that accepts action objects from parties it does not trust SHOULD bound its own messages well below these limits (Section 2.6). The statement of Section 1.2 that every input yields a result assumes that memory; an implementation that cannot obtain it fails as its platform fails when memory runs out, which this document does not specify.¶
A pattern language chosen per definition would let a definition author, or anyone who can supply a definition over the network, choose a pattern with catastrophic backtracking. A pattern such as (a|a){1,99} takes time exponential in the length of the input in common backtracking engines. CAID has no such language. A code field names a registered format, and every format is a fixed grammar that matches strings of bounded length and compiles to a regular expression with no nested quantifiers, no quantified alternatives that can begin with the same character, and no optional repetition whose first characters overlap what can follow it. A bounded length alone is not enough: (a|a){1,99} matches only strings of at most 99 characters. Matching is therefore linear in the length of the input in every engine. An implementation MAY match a format with a hand-written recognizer instead of a regular expression.¶
CAID performs no Unicode normalization. Visually identical strings in different normalization forms, or homoglyphs such as U+0430 in place of "a", are different content with different CAIDs. That fails safe for equality but not for human review. An interface that displays an action object for approval SHOULD apply confusable detection to identifier-like fields such as hosts, accounts, email addresses, and tool names. It SHOULD also display every member, escape control and bidirectional formatting characters in member names, and flag any member that the type does not declare whose name equals a declared field name after case folding, after NFKC normalization [UNICODE], or under the confusable skeleton of [UTS39], because an executor that matched names that way would read the wrong member (Section 10.6). A relying party MAY refuse such an object as a matter of local policy. A type whose fields name such identifiers SHOULD state their normalization in digest_notes, and issuers apply it before computing. The action type and the suite are ASCII by grammar.¶
A reason carries a field name or a source path verbatim, and a field name can hold control and bidirectional formatting characters. An interface or log that displays reasons escapes them. The field names of the reference registry are printable ASCII.¶
A versioned action-type definition is immutable once published, apart from the monotone enum advance of Section 4.1. definition_sha256 identifies validation semantics exactly, and a relying party that needs exact reproducibility pins it. A local and a registered definition that share a name but differ in validation semantics are not the same type, and resolution refuses the pair when both are configured, so a second definition slipped into a configured source causes invalid_definition instead of silently changing validation. A definition source obtained over the network MUST be checked against the pinned SHA-256 digest of the registry file (Section 12.2) or against pinned definition_sha256 values before use. The registry-file digest covers the file's octets, so an implementation SHOULD check it before it parses the file; a check against definition_sha256 values necessarily follows decoding under Section 2.4, within a size that the implementation bounds by its own policy (Section 10.7). definition_sha256 does not cover notes or digest_notes, which carry normalization guidance for issuers, so a party that relies on that guidance checks the source against the registry-file digest.¶
A code-set name or URL does not identify immutable validation semantics. Issuers and verifiers MUST use the exact locally available snapshot and verified digest required by Section 4.4. Resolving a mutable network resource at computation or verification time can make the same object alternate between valid and invalid and is prohibited. Missing, unresolved, or digest-mismatched snapshots fail closed.¶
Direct CAID verification does not infer semantic equality. The Action-Mapping Profile makes only a narrower statement about material projections under exact pinned profiles. A malicious or incomplete profile can omit a concept that the relying party should have considered material. Profile selection is therefore a relying-party policy decision. The algorithm of Section 8.3 yields INDETERMINATE when a profile is unpinned or its source format does not match, when it declares source semantic loss, or when a required target field has no rule. It cannot detect a material source concept that the profile neither maps nor declares (Section 8.5); only profile review guards against that.¶
A valid native signature does not make unsigned members beside the signed payload trustworthy. A native adapter MUST establish that every mapped field is covered by the native integrity mechanism. The source descriptor is verifier-derived, not presenter-asserted (Section 8.1).¶
Possession of an identifier proves nothing and grants nothing. Protocols MUST NOT treat knowledge of a CAID as evidence of anything beyond knowledge of the object's content. A CAID also hides nothing that can be guessed; Section 11 explains why.¶
A CAID has the same confidentiality as its action object. It is an unsalted, deterministic hash of the whole object, and it carries the action type in cleartext. When every member of an object is guessable, as with a small amount, a currency, a known payee, and a sequential instruction identifier, anyone who holds the identifier can recover the object by testing candidates offline, at the cost of one SHA-256 computation each. The tool.call.1 example of Section 4.7 shows this: enumerating amount_usd upward from 1 recovers the value 4000 after 4000 candidates. A CAID therefore SHOULD be logged, transmitted, and placed in URLs only where the action object itself could be. The digest that computation returns and the source digest of a mapping result carry the same exposure.¶
A CAID is meant to be recomputed by a party that already holds the action object, such as a verifier (Section 6) or an executor (Section 7), and receipts and other evidence carry it for such parties. It is not designed as a correlation identifier to propagate to parties that do not hold the object: to them it discloses whatever of the object can be guessed.¶
The digest field type keeps raw values out of an action object but does not by itself provide confidentiality or unlinkability. A plain SHA-256 digest of an account number, email address, or patient identifier is pseudonymous, not anonymous: an observer can test likely values by dictionary attack. The digest-typed values in Appendix C are digests of short example strings and can be recovered this way.¶
Type authors handling personal data SHOULD prefer high-entropy opaque references or a deployment-specific privacy-preserving commitment scheme, document its normalization and correlation scope, and avoid placing unnecessary personal data in the action object. A type whose required fields can all be low-entropy SHOULD require a member that carries at least 128 bits of entropy from the system of record, such as a random instruction or occurrence identifier. A deployment-specific commitment scheme can require an explicit mapping profile for cross-domain joins.¶
The initial CAID Action Types entries rely on identifiers from the system of record, such as payment_instruction_id, authorization_number, and order_id, that the registry cannot require to be high-entropy. Where such an identifier is sequential and the other members can be guessed, anyone who holds a CAID of that type can recover its action object, so the CAID needs the protection its action object needs, including personal-data protection when the object carries personal data. The notes of a registered type fix what each of its digest fields commits to, and that is part of the entry's immutable meaning (Section 12.2), so a deployment does not carry a keyed commitment, such as HMAC-SHA-256 [RFC6234] under a deployment-scoped key, in a digest field whose notes, like those of patient_ref, describe a plain digest. A deployment that needs a keyed commitment uses a type whose notes specify it: a local type (Section 4.6) or a new version registered for that purpose. Nothing in an action object says which scheme a digest field used, so comparison across deployments then needs a mapping profile (Section 8).¶
Action objects, CAIDs, verification details, and their correlations should be assumed to travel and to appear in logs.¶
IANA is requested to create a registry group named "Canonical Action Identifier (CAID)" containing the seven registries of Sections 12.1 through 12.7, and to register the URI scheme of Section 12.8. The registration policies are those of [RFC8126]. Until IANA creates the registries, the author's reference registry records their contents: the CAID Suites and CAID Action Types entries in files published under a public-domain dedication beside [CAID-REGISTRY], and the other five registries in the specification sources of the same repository. Its Action Types file records a superset of the initial CAID Action Types contents: it also carries the entries that IANA is not asked to register, which Appendix D.2 lists. The reference for every initial entry of every registry below is this document; each initial CAID Action Types entry also references [CAID-REGISTRY], which holds its definition, and Section 4.7 reproduces the definition of tool.call.1. The change controller of every initial entry is the IETF.¶
Only an entry's change controller may request a change to that entry, such as deprecation with superseded_by or a monotone enum advance, and the designated expert reviews the request under the policy that applies to a new registration. For entries registered by IETF-stream documents, the change controller is the IETF, acting through the IESG. The IESG may also act for any other change controller that cannot be reached or does not respond.¶
Registration policy: Specification Required. Each entry records: the suite name, which matches the suite rule of Appendix A; the canonicalization scheme and its reference; the digest algorithm and its reference; the digest length in octets, at least 32; the status, active or deprecated; the change controller; and a reference. The designated expert refuses a digest formed by truncating the output of a hash function to fewer octets than that function specifies, and a name that differs from a registered name only by hyphens; a function whose specification defines a shorter output, such as SHA‑384 or SHA‑512/256, is not such a truncation. A registered name is never reassigned, an entry is never removed, and a suite is never redefined. A suite is deprecated once practical collision attacks on its digest are known or anticipated: its change controller requests the deprecation, or the IESG does for a controller that cannot be reached or does not respond (Section 12), and a new suite is registered. Section 3.1 states what deprecation means for issuers and relying parties. The initial contents are:¶
| Suite | Canonicalization | Digest | Octets | Status |
|---|---|---|---|---|
| jcs-sha256 | RFC 8785 | SHA-256, RFC 6234 | 32 | active |
| cbor-sha256 | RFC 8949, Section 4.2.1 | SHA-256, RFC 6234 | 32 | active |
Registration policy: Specification Required. Each entry records: the action type, which matches the action-type rule of Appendix A and has at least two name segments; the definition, in the schema of Section 4.2; its definition_sha256; the status; supersedes and superseded_by where they apply; the change controller; and a reference. The expert does not register a name whose first segment is organization-specific (Section 4.6) unless that organization is the change controller.¶
The content of an entry that affects validation or material meaning, including the normalization guidance in notes and digest_notes, is immutable, with two exceptions: the status may move from active to deprecated, with superseded_by naming the successor; and the expert may approve a monotone enum advance (Section 4.1) after checking that every previously accepted value remains accepted. An advance request cites the edition and values_sha256 of the new value set. An advance updates definition_sha256, and the entry keeps every earlier definition_sha256. A new version of a registered type name is registered by the change controller of its earlier versions, or with that controller's written agreement, and the expert refuses a registration, or a supersedes or superseded_by value, that would link type versions held by different change controllers without that agreement.¶
The expert applies the material-fields test: every required field is material; field names are printable ASCII; amounts are amount-string; identifiers that are personal or secret-adjacent are digest fields; in a registration made after this document, a type whose required fields can all be low-entropy requires a member that carries at least 128 bits of entropy, or its digest_notes or its specification state why not (Section 11); every enum is closed or pinned to an integrity-checked snapshot; and values from large, changing, or licensed code systems are code fields, with no value set and no licensed content. The initial entries of Appendix D.1 are registered by this document: Section 11 states why their identifiers are not required to carry 128 bits of entropy, and Section 4.7 why occurrence_id is optional in tool.call.1. The 9 deprecated initial entries are registered to record the history of the successors that superseded_by names, not for new identifiers, and they do not meet the test: each has a required enum field whose external values_ref has no pinned snapshot, and four of them hold as enums values that their successors carry as code fields. Such an enum field refuses whenever it is present, so, as registered, none of the 9 produces or verifies a CAID (Section 4.4). A registrant whose type pins an external enum supplies the snapshot file, in the shape of Section 4.4 together with the source it was derived from and what is known of that source's terms, and the SHA-256 digest of the file; the expert confirms that the file is publicly available under the terms it states before approving the entry.¶
The initial contents are the 54 entries of Appendix D.1, 45 active and 9 deprecated. The definition of each entry is the entry with that name in the file action-types.json of [CAID-REGISTRY], registry version 5 of the reference registry, whose SHA-256 digest over the octets of that file, in hexadecimal, is 1e30ddd312c888f6a27055dac0ed453398e20b64cfdcb7c5019fcf0e1ac2551a. Section 4.7 reproduces the tool.call.1 entry member for member. The reference registry also carries types defined by other specifications, and one whose name misdescribes it; they are listed in Appendix D.2 and are not initial entries. Each of the seven types defined by other specifications may be registered under Specification Required with its own specification and change controller. The eighth, dns.zone.transfer.1, is not to be registered, because its name misdescribes the type; the registrar transfer it defines can be registered under a name that describes it.¶
IANA is asked to store each entry's definition, in the schema of Section 4.2, as a file linked from the entry, and to record with each entry every definition_sha256 it has held, in order, with the date of each change, starting with the value that Appendix D.1 gives. A monotone enum advance adds a value to that list and never removes one. IANA is also asked to store, and to link from each entry that pins it, each enum snapshot file that an initial entry pins and that is derived from an IANA registry: the DNS Resource Record TYPEs list, the JOSE algorithm and elliptic curve lists, and the ISO 3166-1 alpha-2 list as the Language Subtag Registry carries it. The ISO 4217 snapshot, which every currency field of the initial entries pins, is archived with those files beside action-types.json at the commit that [CAID-REGISTRY] cites. Each snapshot file records the source it was derived from, and its entry in the enum_snapshot_files member of action-types.json records what is known of its terms.¶
Registration policy: IETF Review. A field type defines new members, refusal behavior, and processing, so it changes what every implementation validates. An entry records the name, which matches the format-name rule of Appendix A; the JSON kind of its values; the members it defines; its refusal reasons; and a reference. A registered field type is never removed, renamed, or redefined. The initial contents are:¶
| Field type | JSON kind | Members | Refusals |
|---|---|---|---|
| string | string | none | mistyped_field |
| amount-string | string | none | invalid_amount, mistyped_field |
| digest | string | none | mistyped_field |
| enum | string | values, values_ref, values_snapshot, values_sha256 | mistyped_field |
| code | string | code_system, format | invalid_code, mistyped_field |
| timestamp | string | none | mistyped_field |
| integer | number | none | mistyped_field |
| boolean | boolean | none | mistyped_field |
| object | object | none | mistyped_field |
| array | array | none | mistyped_field |
Registration policy: Specification Required. A code format is a grammar applied by the unchanged rule of Section 4.5; it adds no member, reason, or processing step. An implementation that does not know a format refuses only the fields that name it, as mistyped_field (Section 4.2.1), which fails closed, and the checks below bound what a format can admit. An entry records: the format name, which matches the format-name rule; its grammar in ABNF [RFC5234] [RFC7405] over printable ASCII, which is complete, defining every rule it uses other than the core rules of [RFC5234]; the maximum length of a matching string; the reference that defines its syntax; the change controller; and a reference. The designated expert checks that the format fixes syntax only, never a value set; that it matches only strings of bounded length; that it has the linear-matching property of Section 4.5 (see also Section 10.8); that each distinct lexical form of a code is a distinct format; and that the registration carries no licensed content. A registered code format is never removed, renamed, or redefined; a changed grammar is registered as a new format name. The grammar of each initial entry is the rule of part A.4 of Appendix A with the same name, together with the rules of part A.4 that it references: UALPHA and UALNUM, and, for hcpcs, the cpt and hcpcs-level-ii rules. The initial contents are:¶
| Code format | Maximum length | Syntax reference |
|---|---|---|
| icd-10-cm | 8 | ICD-10-CM Official Guidelines for Coding and Reporting, Section I.A.2 |
| ndc-11 | 11 | FDA National Drug Code format, 11-digit form |
| ndc-10-hyphenated | 12 | FDA National Drug Code format, 10-digit forms |
| cpt | 5 | AMA CPT code set |
| hcpcs-level-ii | 5 | CMS HCPCS Level II |
| hcpcs | 5 | CMS HCPCS Level II; AMA CPT (HCPCS Level I) |
| iso-3166-2 | 6 | ISO 3166-2 |
| iso20022-external-code | 4 | ISO 20022 External Code Sets |
| nacha-sec | 3 | Nacha Operating Rules, Standard Entry Class codes |
Registration policy: Standards Action. A new reason changes what every conforming implementation reports and where the reason sorts (Section 5.1), so it is registered only by a Standards Track RFC that also updates the reason rule of Appendix A and names the phase or stage that reports it. Each entry records: the reason code, in lowercase letters and underscores; the kind of its parameter (none, field-name, source-path, or compute-reason); the operations and phases, or the mapping stage, that report it; and a reference. The reason rule of Appendix A accepts exactly the reasons registered by this document. The prefixes left: and right: of a comparison (Section 8.3) are not registered reasons: each marks which mapping a registered mapping reason came from, and a mapping reason gains both prefixed forms when it is registered. The initial contents are the two tables of Appendix B.¶
Registration policy: IETF Review. A mapper written from an earlier specification refuses an unknown transform, so a registration changes what mappers accept. Each entry records: the transform name, which matches the format-name rule of Appendix A; the source values it accepts; its output; the reason for a value it does not accept; and a reference. The initial contents are:¶
| Transform | Accepts | Output | Refusal |
|---|---|---|---|
| copy | any value of the data model | the value | source_value_not_canonicalizable |
| sha256-utf8 | a string | "sha256:" and the SHA-256 of its UTF-8 octets | source_value_type_mismatch |
| sha256-jcs | any value of the data model | "sha256:" and the SHA-256 of its RFC 8785 encoding | source_value_not_canonicalizable |
| sha256-hex-to-digest | a string matching hex-sha256 | "sha256:" and the string | source_value_type_mismatch |
Registration policy: IETF Review. A mapper written from an earlier specification refuses an unknown loss policy, so a registration changes what mappers accept. Each entry records: the policy name, which matches the format-name rule of Appendix A; the constraint on omitted_source_fields; its effect on the mapping result; and a reference. The initial contents are:¶
| Policy | Omissions | Effect |
|---|---|---|
| no-material-field-loss | absent or empty | none |
| declared-source-semantic-loss | non-empty | INDETERMINATE, reason declared_source_semantic_loss |
A CAID is a syntactically valid URI [RFC3986]: the scheme "caid" followed by ":" and a rootless path made of unreserved characters and ":". IANA is requested to register the scheme in the "Uniform Resource Identifier (URI) Schemes" registry with the following template [RFC7595]:¶
Syntax: the caid rule of Appendix A. Semantics: a caid URI identifies typed action content by digest. It is not a locator: no operation is defined on it, and an application MUST NOT dereference it. Encoding: the identifier is ASCII and contains no percent-encoded octets; a percent-encoded form is not a CAID, and a carrying protocol removes any transport encoding before parsing. Interoperability: CAIDs are compared as exact strings (Section 3.5). [RFC3986] treats scheme names as case-insensitive and makes lowercase the normal form; every CAID is already in that form, so normalization under Section 6 of [RFC3986] leaves a CAID unchanged. An application MUST NOT normalize a string before parsing it as a CAID, because normalization can turn a string that a parser refuses, such as one whose scheme is written "CAID", into a CAID (Section 3.4). Security: Section 10 and Section 11.¶
Utility (Section 3.1 of [RFC7595]): a CAID names an action by the digest of a canonicalization that its suite fixes, applied to an action object whose type definition decides which members are required and how each is typed, and the identifier carries the action type and the suite, both of which take part in comparison (Section 3.5). An ni URI [RFC6920] names an object by the digest of its octets, unless a specification defines another input, under an algorithm from the Named Information Hash Algorithm Registry. It names no canonicalization, and two ni names that share the algorithm and the digest value refer to the same object whatever else they carry, so an action type or a suite in their authority or query parameters would take no part in comparison. A URN namespace [RFC8141] could carry the same fields in its namespace-specific string, but URN-equivalence lowercases "urn" and the namespace identifier, ignores the r-, q-, and f-components, and lets a namespace add equivalences but never remove one, so it would equate spellings that a parser under Section 3.4 refuses and that Section 3.5 compares as different strings.¶
This document registers no media type [RFC6838]. A media type earns registration when a recipient must recognize a message by it, and no step of this document sends one of its objects that way:¶
A specification that defines a transfer of any of these objects can register a media type for it.¶
This section records the known implementations of this specification at the time of posting, as described in [RFC7942]. Listing an implementation here does not imply endorsement by the IETF. The RFC Editor is asked to remove this section before publication.¶
EMILIA Protocol, Inc., the author's organization, maintains three implementations, in JavaScript, Python, and Go. Each implements decoding, parsing, computation, verification, the definition digest, and mapping for the jcs-sha256 suite, and refuses cbor-sha256 as unknown_suite. The JavaScript implementation uses only the platform's cryptographic library, and the Python and Go implementations use only their standard libraries. All three run one shared conformance corpus of core and mapping vectors, plus candidate cross-format mapping vectors that encode the author's reading of other formats' published specifications and have not been validated by those formats' authors; they skip the corpus vectors that apply only to an implementation of cbor-sha256. They are licensed under Apache-2.0 and published at <https://github.com/emiliaprotocol/emilia-protocol/tree/main/caid>. The next major release (6.0.0) of the author's verification package will vendor a byte-for-byte copy of caid.mjs, the module of the JavaScript implementation that carries every operation except mapping; the published 5.0.0 vendors a copy of an earlier revision. Contact: team@emiliaprotocol.ai.¶
Version: all three implement revision -04 of this document. Coverage: every operation of Section 1.2, for JSON text and host values, under the jcs-sha256 suite; none implements the cbor-sha256 suite. Maturity: they are the reference implementations of the author's conformance tooling, reviewed only within the author's organization. This section was last updated on 28 September 2026.¶
All three implementations come from the author's organization, and none of them is independent. No independent implementation is known to the author. A party that runs these implementations or their conformance corpus reproduces the author's results; such external re-runs are reproductions, not independent implementations.¶
This section is to be removed before publishing as an RFC.¶
This revision makes the processing model complete and checkable, and it is a substantive revision. The suites, the digest, and the canonical form of every object that both -03 and -04 accept do not change. The identifier syntax changes only by refusing the forms listed in Section 14.1. The lists below give every normative change. Each item in the first four lists changes what an implementation of the operations of Section 1.2 accepts, refuses, or reports, and is pinned by conformance vectors. The fifth list gives new requirements on parties whose behavior those operations cannot observe, and the last list gives changes to the text alone.¶
This revision refuses each of the following inputs, which -03 accepted or did not address. The first four items, and the timestamp, amount, length, and value count items, refuse action objects whose CAIDs were valid under -03.¶
history/action-types.v4.json, the last file
that declared registry version 4, which digests.json
pins by its SHA-256, and every type's definition_sha256 is
published.¶
These requirements bind issuers, type authors, registrants, executors, carrying protocols, relying parties, deployments, and applications. The operations of Section 1.2 cannot observe them, so no conformance vector pins them.¶
The following changes add or correct text. None of them changes what an implementation of the operations of Section 1.2 accepts, refuses, or reports.¶
This section is to be removed before publishing as an RFC.¶
Revision -03 makes enum validation replayable. External enum references now require a snapshot or edition label, a SHA-256 pin over the complete JCS values array, exact local resolution, and a verified membership check. Bare, unresolved, digest-mismatched, and out-of-set values fail closed.¶
The enum definition forms are now exact: a null member is present and malformed, compact inline members are trimmed of U+0020 SPACE only, a values array beside a compact inline reference must equal it, and an embedded values array beside an external reference is the pinned array. Values outside a compact inline list now fail closed.¶
Required-field presence means a member of the action object itself. Strings and member names containing an unpaired surrogate are refused as unsupported_value, making explicit the refusal that RFC 8785 already requires. Registries could correct an existing value-set pin in a new registry version under narrow conditions; -04 replaces that rule with the monotone advance of Section 4.1.¶
The reference registry advances from version 3 to version 4. It pins the SIX ISO 4217 List One snapshot published 2026-09-17 for every currency field, by values digest and by a digest over the whole snapshot file, and pins the Dispense As Written codes of rx.dispense.1 inline. Existing action objects containing a code in that snapshot produce the same CAID bytes; a code that version 3 accepted but the snapshot omits is refused. Eleven active types in the reference registry require an external value set that has no pinned snapshot yet and cannot produce a CAID under version 4 until one is published. Deployments must update their registry and enum-snapshot pins before reporting validation under registry v4. Historical v3 decisions remain decisions under the historical registry and are not retroactively relabeled v4.¶
Revision -03 does not change the action object, identifier syntax, digest suites, or mapping algorithm, and changes canonicalization only by making the unpaired-surrogate refusal explicit.¶
action-types.json at this fixed commit, whose octets
the URL above serves. The CAID Action Types section of
this document gives the SHA-256 digest of those octets. This
reference covers that file alone. Beside it at the same commit,
digests.json lists the definition_sha256 of every type,
history/action-types.v4.json holds registry version
4, and value-sets/ holds the enum snapshots.
This appendix collects every grammar of this document in the ABNF of [RFC5234], with the case-sensitive string syntax of [RFC7405]. ALPHA, DIGIT, and HEXDIG are the core rules of [RFC5234]. Each rule matches a string as a whole. Part A.1 is the identifier and part A.2 the digest syntax of each suite. Part A.3 holds the lexical field types, and part A.4 the code formats that this document registers (Section 4.5), each named by its rule name. Part A.5 constrains definitions, part A.6 the mapping profile, and part A.7 accepts every reason of Appendix B, including nested forms such as mapped_action:missing_material_field:amount. A timestamp's day is further limited by the length of its month (Section 4.3).¶
; Collected ABNF [RFC5234] with case-sensitive strings [RFC7405].
; A string matches a rule only as a whole: nothing precedes or
; follows it, not even a final line feed.
; A.1 Identifier
caid = %s"caid" ":" caid-version ":" action-type ":"
suite ":" digest
; at most 1024 octets, and its
; action-type at most 512 (2.6)
caid-version = "1"
action-type = 1*( name-segment "." ) type-version
; at most 512 octets (2.6)
name-segment = lower-char *( lower-char / DIGIT / "-" )
type-version = pos-int
pos-int = %x31-39 *DIGIT ; positive integer, no leading zero
suite = lower-char *( lower-char / DIGIT / "-" )
digest = 1*b64url-char
b64url-char = %x41-5A / %x61-7A / DIGIT / "-" / "_"
; A-Z / a-z / 0-9 / "-" / "_"
lower-char = %x61-7A ; a-z
; A.2 Digest syntax per suite
;
; A suite whose digest is n octets encodes it in c = ceil(8n/6)
; base64url characters. The final character carries u = 6c - 8n
; unused low bits, which are zero: of the 64 base64url characters,
; only those whose 6-bit value is a multiple of 2^u may end the
; digest. For n = 32 (jcs-sha256 and cbor-sha256), c = 43 and
; u = 2.
digest-256 = 42b64url-char b64url-z2
b64url-z2 = %x41 / %x45 / %x49 / %x4D / %x51 / %x55 / %x59
/ %x63 / %x67 / %x6B / %x6F / %x73 / %x77
/ %x30 / %x34 / %x38
; A E I M Q U Y c g k o s w 0 4 8
; A.3 Field types
amount-string = [ "-" ] int-part [ "." frac-part ]
int-part = "0" / ( %x31-39 *DIGIT ) ; no leading zero
frac-part = 1*DIGIT
digest-field = %s"sha256:" 64hex-lower
hex-lower = DIGIT / %x61-66 ; 0-9 / a-f
timestamp = full-date %x54 utc-time %x5A ; "T" and "Z"
full-date = date-year "-" date-month "-" date-mday
date-year = 4DIGIT
date-month = "0" %x31-39 / "1" %x30-32 ; 01-12
date-mday = "0" %x31-39 / %x31-32 DIGIT / "3" %x30-31
; 01-31, within the month
utc-time = time-hour ":" time-minute ":" time-second
[ time-fraction ]
time-hour = %x30-31 DIGIT / "2" %x30-33 ; 00-23
time-minute = %x30-35 DIGIT ; 00-59
time-second = %x30-35 DIGIT ; 00-59, never 60
time-fraction = "." 1*DIGIT
; A.4 Code formats
;
; The rule name of each code format is its registered format name.
; A format fixes syntax only, never which codes exist.
icd-10-cm = UALPHA 2UALNUM [ "." 1*4UALNUM ]
ndc-11 = 11DIGIT
ndc-10-hyphenated
= 4DIGIT "-" 4DIGIT "-" 2DIGIT
/ 5DIGIT "-" 4DIGIT "-" 1DIGIT
/ 5DIGIT "-" 3DIGIT "-" 2DIGIT
cpt = 4DIGIT ( DIGIT / UALPHA )
hcpcs-level-ii
= UALPHA 4DIGIT
hcpcs = cpt / hcpcs-level-ii
iso-3166-2 = 2UALPHA "-" 1*3UALNUM
iso20022-external-code
= 1*4UALNUM
nacha-sec = 3UALNUM
UALPHA = %x41-5A ; A-Z
UALNUM = UALPHA / DIGIT
; A.5 Definitions
field-name = 1*field-char
field-char = %x00-39 / %x3B-D7FF / %xE000-10FFFF
; any scalar value except ":"; a
; field name is also a string of
; the data model (2.2), so it
; holds no noncharacter
format-name = lower-char *( lower-char / DIGIT / "-" )
; the formats of this document: A.4
code-system = uri-scheme ":" 1*uri-char
; a scheme, ":", and URI characters:
; no fragment and no IP-literal
; host; at most 2048 octets (2.6)
uri-scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
uri-char = ALPHA / DIGIT / "-" / "." / "_" / "~"
/ "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+"
/ "," / ";" / "=" / ":" / "@" / "/" / "?"
/ pct-encoded
pct-encoded = "%" HEXDIG HEXDIG
; A.6 Action-Mapping Profile
json-pointer = *( "/" reference-token ) ; [RFC6901]
source-path = "/" reference-token json-pointer
; a json-pointer other than ""
reference-token
= *( unescaped / escaped )
unescaped = %x00-2E / %x30-7D / %x7F-D7FF / %xE000-10FFFF
escaped = "~" ( "0" / "1" )
array-index = "0" / ( %x31-39 *DIGIT )
hex-sha256 = 64hex-lower
; A.7 Reasons
reason = core-reason / mapping-reason / comparison-reason
core-reason = compute-reason / core-only
core-only = %s"malformed_json" / %s"malformed_caid"
/ %s"action_type_mismatch"
/ %s"definition_mismatch" / %s"digest_mismatch"
/ %s"invalid_object"
compute-reason
= compute-bare / field-code ":" field-name
compute-bare = %s"invalid_action_type" / %s"unknown_action_type"
/ %s"invalid_definition" / %s"unknown_suite"
/ %s"unsupported_number" / %s"unsupported_value"
field-code = %s"missing_material_field" / %s"mistyped_field"
/ %s"invalid_amount" / %s"invalid_code"
mapping-reason
= mapping-bare
/ %s"unmapped_material_field" ":" field-name
/ pointer-code ":" source-path
/ %s"mapped_action" ":" compute-reason
mapping-bare = %s"invalid_mapping_profile"
/ %s"unknown_action_type" / %s"invalid_definition"
/ %s"native_verification_required"
/ %s"mapping_profile_unpinned"
/ %s"source_format_mismatch" / %s"source_not_object"
/ %s"source_not_canonicalizable"
/ %s"declared_source_semantic_loss"
pointer-code = %s"invalid_source_path" / %s"missing_source_field"
/ %s"source_value_type_mismatch"
/ %s"source_value_not_canonicalizable"
/ %s"unknown_transform"
comparison-reason
= ( %s"left" / %s"right" ) ":" mapping-reason
/ %s"target_action_type_mismatch"
/ %s"material_projection_mismatch"
¶
These tables list every reason. A reason with a parameter is the code, ":", and the parameter, which matches the ABNF rule named in the Parameter column. Computation and verification phases are those of Sections 5 and 6; "(gate)" marks a phase that yields exactly one reason. Decoding and parsing are single steps, named without a phase. Mapping stages are those of Section 8.3, and comparison prefixes a failed mapping's reasons with "left:" or "right:". A reason that computation and mapping stage A share is listed once, in the first table. These tables are the initial contents of the CAID Reason Codes registry (Section 12.5).¶
| Reason | Parameter | Operations and phases |
|---|---|---|
| malformed_json | none | decode; compute 0 (gate); verify 2 (gate) |
| malformed_caid | none | parse; verify 1 (gate) |
| unknown_suite | none | parse; compute 5; verify 1 (gate); verify 6 |
| invalid_action_type | none | compute 1 (gate) |
| unknown_action_type | none | compute 2 (gate); mapping stage A |
| invalid_definition | none | compute 2 (gate); mapping stage A |
| definition_mismatch | none | verify 5 |
| missing_material_field | field-name | compute 3 |
| mistyped_field | field-name | compute 4 |
| invalid_amount | field-name | compute 4 |
| invalid_code | field-name | compute 4 |
| unsupported_number | none | compute 6 |
| unsupported_value | none | compute 7 |
| action_type_mismatch | none | verify 4 |
| digest_mismatch | none | verify 6 |
| invalid_object | none | verify 3 (gate); verify 7 |
| Reason | Parameter | Stage |
|---|---|---|
| invalid_mapping_profile | none | A |
| unmapped_material_field | field-name | A |
| native_verification_required | none | B |
| mapping_profile_unpinned | none | B |
| source_format_mismatch | none | B |
| source_not_object | none | B |
| source_not_canonicalizable | none | B |
| declared_source_semantic_loss | none | B |
| invalid_source_path | source-path | C |
| missing_source_field | source-path | C |
| source_value_type_mismatch | source-path | C |
| source_value_not_canonicalizable | source-path | C |
| unknown_transform | source-path | C |
| mapped_action | compute-reason | D |
| target_action_type_mismatch | none | comparison |
| material_projection_mismatch | none | comparison |
Every value in this appendix recomputes from the objects shown and the definitions of reference registry version 5 [CAID-REGISTRY], with the enum snapshots archived beside it at the same commit (Section 12.2). Long lines are folded as specified in [RFC8792].¶
This payment.release.1 action object:¶
=============== NOTE: '\' line wrapping per RFC 8792 ================
{
"action_type": "payment.release.1",
"amount": "250.00",
"currency": "EUR",
"beneficiary_account": "sha256:9f86d081884c7d659a2feaa0c55ad015a3b\
f4f1b2b0b822cd15d6c15b0f00a08",
"payment_instruction_id": "pi-2026-000117",
"memo": "invoice 4471"
}
¶
has members in a different order from its canonical encoding, which sorts them. Its RFC 8785 encoding is 230 octets:¶
=============== NOTE: '\' line wrapping per RFC 8792 ================
{"action_type":"payment.release.1","amount":"250.00","beneficiary_ac\
count":"sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6\
c15b0f00a08","currency":"EUR","memo":"invoice 4471","payment_instruc\
tion_id":"pi-2026-000117"}
¶
Computation under jcs-sha256 returns the CAID, the SHA-256 digest of those octets, and the definition_sha256 of payment.release.1 in registry version 5:¶
=============== NOTE: '\' line wrapping per RFC 8792 ================
{
"caid": "caid:1:payment.release.1:jcs-sha256:liLG9pKgkLt3silrjf1wa\
0xIHz5YFrBB9HI-arxrO1Y",
"digest": "sha256:9622c6f692a090bb77b2296b8dfd706b4c481f3e5816b041\
f4723e6abc6b3b56",
"definition_sha256": "sha256:3a5ad4c0a3a8dbf7eb9ea5f4c3b2ceb07ba19\
72fac49acc6006dcac64726ff12"
}
¶
Under cbor-sha256, the core deterministic CBOR encoding of the same object is 207 octets. Its map keys sort by their encoded octets, so shorter keys come first and the members appear in the order memo, amount, currency, action_type, beneficiary_account, and payment_instruction_id, which differs from the RFC 8785 order. In hexadecimal, 32 octets to a line:¶
a6646d656d6f6c696e766f696365203434373166616d6f756e74663235302e30 306863757272656e6379634555526b616374696f6e5f74797065717061796d65 6e742e72656c656173652e317362656e65666963696172795f6163636f756e74 78477368613235363a3966383664303831383834633764363539613266656161 3063353561643031356133626634663162326230623832326364313564366331 356230663030613038767061796d656e745f696e737472756374696f6e5f6964 6e70692d323032362d303030313137¶
Computation under cbor-sha256 returns this result; the definition_sha256 does not depend on the suite:¶
=============== NOTE: '\' line wrapping per RFC 8792 ================
{
"caid": "caid:1:payment.release.1:cbor-sha256:7bBO8giRaW3iBRR-C6cZ\
FnqkRjAPTwTPMrvwd-200vY",
"digest": "sha256:edb04ef20891696de205147e0ba719167aa446300f4f04cf\
32bbf077edb4d2f6",
"definition_sha256": "sha256:3a5ad4c0a3a8dbf7eb9ea5f4c3b2ceb07ba19\
72fac49acc6006dcac64726ff12"
}
¶
This object lacks two required fields, carries an amount with a leading zero, and carries a fractional number in the optional memo field:¶
{
"action_type": "payment.release.1",
"amount": "01.50",
"currency": "EUR",
"memo": 7.5
}
¶
Computation refuses it. The missing fields come first, in required_fields order; then the phase 4 reasons in field order, where amount is field 0 and memo, the first optional field, is field 4; then unsupported_number for the fractional value:¶
{
"refusals": [
"missing_material_field:beneficiary_account",
"missing_material_field:payment_instruction_id",
"invalid_amount:amount",
"mistyped_field:memo",
"unsupported_number"
]
}
¶
Verifying the CAID of Appendix C.1 against the object of Appendix C.2 yields invalid_object. The action types match, and no digest comparison is made because the object cannot be canonicalized. The details expand invalid_object into its computation reasons, and definition_sha256 is reported because a conforming definition resolved:¶
=============== NOTE: '\' line wrapping per RFC 8792 ================
{
"valid": false,
"reasons": [
"invalid_object"
],
"details": [
{
"reason": "missing_material_field:beneficiary_account",
"field": "beneficiary_account",
"rule": "required-field",
"observed": "absent"
},
{
"reason": "missing_material_field:payment_instruction_id",
"field": "payment_instruction_id",
"rule": "required-field",
"observed": "absent"
},
{
"reason": "invalid_amount:amount",
"field": "amount",
"rule": "amount-string",
"observed": "string"
},
{
"reason": "mistyped_field:memo",
"field": "memo",
"rule": "field-type",
"observed": "number"
},
{
"reason": "unsupported_number",
"field": null,
"rule": "number",
"observed": null
}
],
"definition_sha256": "sha256:3a5ad4c0a3a8dbf7eb9ea5f4c3b2ceb07ba19\
72fac49acc6006dcac64726ff12"
}
¶
The validation projection of the tool.call.1 definition of Section 4.7 keeps action_type, required_fields, and optional_fields, drops the definition's other members (status, risk_class, summary, digest_notes, and references), and drops the notes member of every field entry. Its RFC 8785 encoding is:¶
=============== NOTE: '\' line wrapping per RFC 8792 ================
{"action_type":"tool.call.1","optional_fields":[{"name":"occurrence_\
id","type":"string"}],"required_fields":[{"name":"target","type":"st\
ring"},{"name":"tool","type":"string"},{"name":"args","type":"object\
"}]}
¶
and its definition_sha256 is:¶
=============== NOTE: '\' line wrapping per RFC 8792 ================ sha256:c95e21136fd8b6df646be2957fe42fbf18935add20da6bc9569aa2d1a771a\ fa2¶
prior.auth.approve.2 carries a HCPCS code in service_code (format hcpcs) and an ICD-10-CM code in diagnosis_code (format icd-10-cm). This object computes:¶
=============== NOTE: '\' line wrapping per RFC 8792 ================
{
"action_type": "prior.auth.approve.2",
"patient_ref": "sha256:8d3148217a50cc7dc5c03c79a8932e5bf60dde17db0\
b3af90c9fed46d0c47e76",
"service_code": "E0601",
"diagnosis_code": "G47.33",
"authorization_number": "PA-2026-000418",
"valid_from": "2026-10-01T00:00:00Z",
"valid_until": "2026-12-31T23:59:59Z"
}
¶
=============== NOTE: '\' line wrapping per RFC 8792 ================
{
"caid": "caid:1:prior.auth.approve.2:jcs-sha256:j7WskhX5l_V70KUTEf\
d1ysXivWV5oFT1q-J1Tvra57U",
"digest": "sha256:8fb5ac9215f997f57bd0a51311f775cac5e2bd6579a054f5\
abe2754efadae7b5",
"definition_sha256": "sha256:c25345a298c9b0ec96d019e4e2bcfd6ddd259\
ad064a7cb75b581999eaaf10086"
}
¶
The same object with the codes written "e0601" and "G4733" is refused, because no format folds case or inserts a dot:¶
=============== NOTE: '\' line wrapping per RFC 8792 ================
{
"action_type": "prior.auth.approve.2",
"patient_ref": "sha256:8d3148217a50cc7dc5c03c79a8932e5bf60dde17db0\
b3af90c9fed46d0c47e76",
"service_code": "e0601",
"diagnosis_code": "G4733",
"authorization_number": "PA-2026-000418",
"valid_from": "2026-10-01T00:00:00Z",
"valid_until": "2026-12-31T23:59:59Z"
}
¶
{
"refusals": [
"invalid_code:service_code",
"invalid_code:diagnosis_code"
]
}
¶
Under the profile of Section 8.2, with a native verification result of the boolean true, a source descriptor equal to the profile's source_format, and the profile digest pinned, this source:¶
{
"checkout": {
"order_id": "ord-7781",
"merchant_id": "merchant-17",
"total_amount": "129.95",
"currency": "USD",
"line_items": [
{
"sku": "A-1",
"qty": 2
}
],
"customer_id": "cust-42",
"fulfillment": {
"method": "ship",
"postal_code": "94107"
}
},
"channel": "web"
}
¶
projects to this order.place.1 action object. The channel member is not a material source path and is not mapped:¶
=============== NOTE: '\' line wrapping per RFC 8792 ================
{
"action_type": "order.place.1",
"order_id": "ord-7781",
"merchant_ref": "sha256:9a52791e954783da73f738762755102e10296f3cfd\
07cebae3ed4fd540d82074",
"total_amount": "129.95",
"currency": "USD",
"items_digest": "sha256:82cff441e6205b34ff18c69acdd583fe265b410b61\
48f22c40bfcfbbcb117c75",
"customer_ref": "sha256:8a3c5a67cad508582b5edf6b8352cea3ffbad7f448\
12c1a736b4444c0f5746aa",
"fulfillment_ref": "sha256:35a83b30c4e6d088acc77990cf683e55d8f101c\
3d5570a8913528039cdb29f3c"
}
¶
The mapping result carries these digests and this CAID:¶
=============== NOTE: '\' line wrapping per RFC 8792 ================
{
"profile_digest": "sha256:ec833ebc5793db1cc25e917fb838856c048bedee\
eb35ff7cb2d172943b0732fa",
"source_digest": "sha256:371f6feb2f3d9767efe4190f25e0ba7b82208d9bc\
916cf1114c84b0c396feaf9",
"caid": "caid:1:order.place.1:jcs-sha256:UI-wYRrJp-P5iI5a2E2qKfT7_\
-ijOogjNoN-wjwIEcg"
}
¶
This appendix lists the 62 type versions of registry version 5 of the reference registry [CAID-REGISTRY] in two parts: Appendix D.1, the initial contents of the CAID Action Types registry (Section 12.2), and Appendix D.2, the entries that IANA is not asked to register. Each entry is one line with the action type, the status, and, for a deprecated type, "successor" and the type that its superseded_by member names, followed by an indented line with the 64 hexadecimal digits that follow "sha256:" in its definition_sha256 (Section 4.2.2). The definition of each entry is the entry with the same name in the registry file; Section 4.7 reproduces the tool.call.1 entry member for member.¶
These 54 entries, 45 active and 9 deprecated, are the initial contents of the CAID Action Types registry.¶
3a5ad4c0a3a8dbf7eb9ea5f4c3b2ceb07ba1972fac49acc6006dcac64726ff12¶
4fd20a272ef42f027c832b3cab3500219d695f2516e030a1d9cb1c1402a17df3¶
9421a7b0166e9c4e69aa1d166c779b93cff1236ae4940ee883d75cb45d2bdb50¶
f717a2a675fc94abf61cad351a6e7698da335b8bce18ece4f904cb3f3d873c3b¶
29ae2cafd17694e2ca65096bfc7cbd86c8c309082e7e9fc8cf92b67928fdfef2¶
b3875a1ee1855fd8ab9a8481578af72f13e151a8297c283ec3a68ea9c482b4ee¶
2ac0e6c6cbd6c8c0acc5ac4b71f9aaeb0adb9ed7511b5b7d0bf11b90425b7aba¶
24950ca02a77db28221df4f2e2bb81b20162ac9c7f8c5cc75db306374606509a¶
7ed8339092856720580669a5e2692cb4350f37ddafd97afad51672043e2163ea¶
f5365e3dc46b2a04b9e804d5921b5debc129a9eee0d1b916679fdf9232ce068f¶
739b77a1dbf3fd164f2ed047368bd8cdc55757c3a840db9140f43dc923111778¶
a6fcdb0e1644ad65449ba2bfe48baea60cd97f8ed1063f15fb5510b1affe1c5c¶
2840d845e32c5f09b3ba2a2b952a2ef495a2f28180a23e33bacf39a6d37ba3cd¶
f2c43de398bc9d1bd62554f867fdec194cb07bb241415d6dc8165f860a32725d¶
d0aa8847da551efe214d27c3e5fe0a341654b8682b0dc03e30c64242142a24aa¶
dc15633426de52ba4862948414a50182105ed839cc7c221ca46ac95913b19b8d¶
0b320a2b0e944df0820439683032e2ce216c6601300d71368cd6cbc2245cc9e4¶
ebb2e0a621a000f7dced69226a60ffc0b034781b412fceb99fc147414de782c4¶
25345b085c2bc02d92eeb5c909ec035d327eef33f0f1b0f56e890a0f26b9be29¶
4a768f2ed30354b053c40f3b6ca0bfdd4dc29d78fc5b6d5558f8f6970e890e8a¶
254b72b169a6675fed28ff2e5da97a10cb0332965ab222deccd3c48ffa8d2f74¶
fab030e96ff68a3ccf2b9ce53a732ed95d8c2053d95f1a826b8da19a8b9e8aa1¶
1caf662180cbfc278f9d8d4549481b22198ea77d72243bef0accebc48663f0bf¶
4cd79cec67565c65f7c99e14fc3b36a6ac18a352a9bf7d9f9475bd300f0b5d15¶
cbc581e8642f85425b300bef9c83c7ed86b04b998aaf0b62d72ebc2d3fbed508¶
842fd0e6d0f7c7ec168e6c29fa992230dea8da1dcb8295e4b56e044eea6e1648¶
fbef9a2810f87e8df07c84c0a71964f4f4606408df59a5d302dc22ca70556075¶
04c79261361a0db6caf4a2c69a19a3b9d19a39c5df7e3fb7ace8d29fe100d589¶
ed2f14b052e7ac7ba84c58391cf360176a8084b37cca55cd86faba4f2c551890¶
f56db5382d2a7ca5a7640664408778c1b6b4da8a632978a6337872ab08a16459¶
33b97769d9edbad183d13907468dbfaa85010629c305aaa70d8c0dc44814557a¶
31af7ddad65e14e9b5577e812934db3098fe8c23e53468e3ac0476aac110818b¶
103c8410093d6b0d15994a65e2cc3afbb725fc5c1db229178c743019a1448de7¶
5244e9721e4d97321bc45e2512860949092a650de0693f6ef48d11bf4704e527¶
8a18c77f7a211bfc64e64014d9f3ee8ab6122406d440a26ad180f7820baafb1f¶
6b0cf385a056e763c9a1a705ef96267af9c854ec0498e9e0828258a6daeb94a9¶
7ba6606177cad27ac3d12ac7875cf2b52e653229ccc3890097eb24129d0cac81¶
7ed8974c8f020de6aa6da1984f86a7feacbf4dcf4a859e170144d6143971e192¶
f5819f5e049731f3490d8a3d0a19b4079ebc629c8370cc51e16095da9a3d506c¶
8b14243a3c9cf02f6d92a4d725be08fd38f12e74be91c60a60291eb507aa4c73¶
468e056e3c1b3993af7cf0da05b7ab2c098421b25d631a792ac112ddb52fa250¶
d1f43cb28204cae2d330abb9a51a8e7934a966c15eea10695149f904083b4dbc¶
4b57279dd56a3007fda65844f90c4ffd5271a884685f37c485154f00b1a16a73¶
9e8f90820e84ea12a51dac9ba678a66df2aaba3b64e1f77f53c14a1568cf0c0c¶
54d81e6db3cb38b0e5b8a53d476100558f4c211a074c2543423eb6cde38338f7¶
943609964f09155f5509b045c23a7a0dc1003aec09f2d3b833cdf4fdabb79012¶
7b1f54c3dde91b1c7faa656859021a775e27f2d36bbe88cf6a1deafe6deb6d76¶
c25345a298c9b0ec96d019e4e2bcfd6ddd259ad064a7cb75b581999eaaf10086¶
bde03fd967ff8129af1b9ed23a6de6259ecc3dde0ac0a88b7ef06d74f28c8b69¶
23beb6c5da8d75d8443b314ef59fd46459b9d1378d6356fbb721cbf6e985ef80¶
5b5fbca19b3c59a0fa3594b67f8321ed02b5f1473b2ab739b2359f8de589ef76¶
2a5424eb4ac66ff7bdd1e10473c7c0384e5ea9d1b8645b8f2a022a30e77a05f3¶
716562ad88964db0c27e99594917e55a624565ccabb051ef293707984fde3dcc¶
c95e21136fd8b6df646be2957fe42fbf18935add20da6bc9569aa2d1a771afa2¶
These 8 entries, all active, stay in registry version 5 unchanged member for member (their RFC 8785 encodings are identical), so that an object valid under registry version 4, which carries them, stays valid (Section 4.1), but IANA is not asked to register them. emilia.mobile.authorized-action.1 is named for a product of the author's organization and is defined by that organization's specification. agent.state.export.1, agent.state.import.1, agent.state.key-release.1, and agent.state.retire-source.1 are defined by another specification of the author's organization, science.bio.experiment.execute.1 by another Internet-Draft of the author, and travel.cancel-notify.1 by an Internet-Draft that the author did not write; this document defines none of them. dns.zone.transfer.1 defines a registrar transfer of a domain under the Extensible Provisioning Protocol [RFC5730], not a DNS zone transfer [RFC5936], and a registered name is never reassigned. Each of the seven types defined by other specifications may be registered under Specification Required (Section 12.2) with its own specification and change controller, and the type emilia.mobile.authorized-action.1, whose first segment names an organization, only with that organization as its change controller. dns.zone.transfer.1 is not to be registered: the path for the registrar-transfer type is a successor whose name describes it.¶
b83fb16d90f9c61eedb802454b150cd5f2ee841b493935e5adcf2361b9575279¶
1599e98a6409c57ef3d022d586f4eb21bc7821a75f3316e2e6ae2a5e509d39c1¶
e80d210c9e009a1e1e1bd6ec4d2afa902d0620ac10eaab51d4e49c25e744a819¶
a3007f508e4d84e07a7c90a728496a5665f893919f2c7cde25de96c98d50fcb7¶
057c9aee5a661416e510bc620f0bbb6efdf3870a92ca389d535bbf0064b67285¶
e539901b9f14afe604952b0cd071337bb6269ab5d1d11ceee2a1292e46c9ab32¶
939f6a2b8ccfb4fed835e6bac211681b7ec51032270761c19b711f349839386f¶
d95b4a14ab5b7989bbc0d0047acf564648b381e1f48e0cc1411d8d911018a365¶
The separation between cryptographic validity and authority in this document was sharpened by review from Eric Rescorla. Linda Dunbar's cross-administrative-domain Agent Gateway scenarios motivated the requirement for a reproducible action join across operator boundaries. Chris Hood's work on agent transport and composition helped expose the need to keep action identity independent from any one authorization artifact. These discussions also benefited from A. Thallapelly's OASNT-CAID profile and its explicit treatment of profile-local identifier comparison. These acknowledgments do not imply endorsement of this document.¶