Skip to main content
This page maps the message structure of the Agent2Agent (A2A) protocol, version 1.0.0, onto the Sanning hand-off record. It is for a developer whose agents already exchange A2A messages and who wants to know what a hand-off record says about one. The hand-off record uses its own names with its own definitions. This table is a mapping between two sets of names. It is not a claim that Sanning implements or supports the A2A protocol.
Dated mapping. Read on 2026-09-23 from the A2A specification at version 1.0.0: the A2A 1.0.0 specification, and its normative Protocol Buffers definition, specification/a2a.proto at the v1.0.0 tag, whose commit is dated 2026-03-12. A later A2A version can change any row, and this page does not follow it until it is re-read.
The Sanning side of each row holds for @sanning/anchor and sanning-anchor 0.16.0, the current release of each SDK. See Record a hand-off between two agents.

How an A2A message becomes a record

Pass the A2A Message object, in its JSON form, as the message argument to send. The handoff.sent record commits to SHA-256 over that object’s canonical form (JCS, RFC 8785), in event.message_hash. The receiver commits to the object it received in the same way, and the two hashes match only if the objects are equal. So every A2A field is inside one commitment. None of them is copied into a field of its own, and a reader checks them by comparing the hash, not by reading them out of the record. For example, this A2A 1.0.0 message, sent through both SDKs, produced the same message_hash in each (623664a4…a132), and re-serializing it with different whitespace on the way did not change the hash:

The message fields

The following table covers each field of the A2A 1.0.0 Message, by its JSON name:

The part fields

In A2A 1.0.0 a Part carries one of text, raw, url or data, and may carry mediaType, filename and metadata beside it. The following table says what the commitment covers for each: A2A 1.0.0 defines no content hash for a part. To commit a file that travels beside the message rather than inside it, put a Sanning file descriptor in the message instead of a url part: filePart (file_part in Python) builds one that carries the file’s SHA-256 and length. The hand-off guide says what receive does and does not check about it.
The commitment covers the base64 string itself, not the bytes it decodes to. A proto-JSON intermediary that re-encodes a raw part, with different padding, a different alphabet, or line breaks, respells that string without changing what it decodes to. The receiver reads the respelled message as altered, because its bytes differ from what the sender committed to, even though the underlying file did not change.

Record fields with no A2A counterpart

The following fields exist in the hand-off record and have no counterpart in the A2A 1.0.0 Message: