Message format
The JSON envelope, field reference, and cryptographic integrity chain
Every ASP message follows a consistent JSON envelope structure. Messages are cryptographically signed and hash-chained to form a tamper-evident conversation record. The format supports both JSON and MessagePack serialization, with JSON as the default.
This page covers the full envelope structure, field-by-field reference, agent URI format, integrity chain, DPoP proof binding, and context objects.
Message envelope
{
"version": "asp/1.0",
"messageId": "019478a3-7b2c-7def-8a12-3456789abcde",
"sessionId": "019478a3-0001-7def-8a12-000000000001",
"sequenceNumber": 5,
"timestamp": "2026-03-07T14:30:00.000Z",
"sender": {
"agentId": "agent://acme.com/services/procurement",
"orgId": "org_acme",
"trustScore": 72.5,
"dpopProof": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYifX0..."
},
"recipient": {
"agentId": "agent://widgets.co/services/sales",
"orgId": "org_widgets"
},
"performative": "PROPOSE",
"content": {
"mimeType": "application/json",
"body": {
"proposalId": "prop_019478a3",
"type": "service-agreement",
"subject": "Widget procurement terms",
"terms": { "quantity": 5000, "pricePerUnit": 12.50 },
"validUntil": "2026-03-08T14:30:00.000Z"
},
"context": []
},
"integrity": {
"hash": "sha256:a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890",
"previousHash": "sha256:9f8e7d6c5b4a39281f8e7d6c5b4a39281f8e7d6c5b4a39281f8e7d6c5b4a3928",
"signature": "ed25519:f7e8d9c0b1a2f7e8d9c0b1a2f7e8d9c0b1a2f7e8d9c0b1a2f7e8d9c0b1a2f7e8d9c0b1a2f7e8d9c0b1a2f7e8d9c0b1a2f7e8d9c0b1a2f7e8d9c0b1a2f7e8"
},
"constraints": {
"maxResponseTimeMs": 5000,
"maxTokenBudget": 10000,
"requiredTrustScore": 50.0,
"allowedPerformatives": ["ACCEPT", "REJECT", "COUNTER"]
}
}Every ASP message consists of six sections.
1. Protocol header. The version, messageId, sessionId, sequenceNumber, and timestamp fields identify the message within the protocol and the session timeline.
2. Participants. The sender object identifies who sent the message, including their trust score and DPoP proof. The optional recipient object specifies the intended audience (required in multi-party sessions).
3. Performative. One of the 13 performatives that declares the sender's communicative intent.
4. Content. The content object carries the performative-specific payload, a MIME type, and optional context objects for shared session state.
5. Integrity. The integrity object contains the SHA-256 content hash, link to the previous message's hash, and an Ed25519 signature. Together these form a tamper-evident chain.
6. Constraints. The optional constraints object sets expectations for the response: maximum response time, token budget, required trust score, and allowed performatives.
Field reference
Top-level fields
| Field | Type | Required | Description |
|---|---|---|---|
version | string | Yes | Protocol version. Always "asp/1.0" for this specification. |
messageId | string | Yes | UUID v7, unique per message. The time-ordered nature of UUID v7 provides natural chronological sorting. |
sessionId | string | Yes | UUID v7 identifying the session. Present on all messages within a session. |
sequenceNumber | integer | Yes | Monotonically increasing counter per sender per session. Starts at 0 for each sender's first message. |
timestamp | string | Yes | ISO 8601 with milliseconds and UTC suffix. Example: "2026-03-07T14:30:00.000Z". |
sender | object | Yes | Identity of the message sender. See Sender and recipient. |
recipient | object | No | Identity of the intended recipient. Required in multi-party sessions. Omitted for broadcast. |
performative | string | Yes | One of the 13 performatives. |
content | object | Yes | The message payload. See Content object. |
integrity | object | Yes | Hash chain and cryptographic signature. See Integrity chain. |
constraints | object | No | Response constraints. See Response constraints. |
Agent identifiers
Agents are identified by URIs using the agent:// scheme:
agent://{org-domain}/{department}/{agent-name}The three components of an agent URI are:
| Component | Description | Example |
|---|---|---|
org-domain | The domain of the organization that operates the agent | acme.com |
department | Organizational unit or functional grouping | services, procurement, ops |
agent-name | Unique name within the department | buyer-01, monitor-east |
Examples:
| URI | Description |
|---|---|
agent://acme.com/services/procurement | A procurement agent operated by acme.com |
agent://widgets.co/services/sales | A sales agent operated by widgets.co |
agent://internal.myorg.com/ops/monitor | An internal monitoring agent |
Sender and recipient
| Field | Type | Required | Description |
|---|---|---|---|
agentId | string | Yes | The agent:// URI of the agent |
orgId | string | Yes | Organization identifier |
trustScore | number | Sender only | The sender's current composite trust score (0 to 100) |
dpopProof | string | Sender only | DPoP proof-of-possession token binding this message to the sender's key pair |
The trustScore and dpopProof fields are included by the sender for the recipient's verification. The recipient object only requires agentId and orgId.
Content object
The content field carries the performative-specific payload.
| Field | Type | Required | Description |
|---|---|---|---|
mimeType | string | Yes | Content type of the body |
body | object | Yes | Performative-specific payload (see each performative's body schema) |
context | array | No | Array of context objects for shared session state |
Supported MIME types
| MIME type | Usage |
|---|---|
application/json | Default. Structured data in JSON format. |
application/msgpack | Binary-efficient alternative to JSON. Same schema, smaller wire format. |
text/plain | Unstructured text content. |
application/octet-stream | Raw binary data. Use sparingly; prefer URI references for large payloads. |
Context objects
Context objects attach shared state to messages. They allow agents to build up a common understanding over the course of a session without repeating information in every message.
{
"context": [
{
"type": "schema-contract",
"id": "ctx_schema_01",
"version": 1,
"data": {
"eventSchema": "https://schemas.acme.com/events/order-placed/v2"
},
"ttl": 3600,
"visibility": "session"
},
{
"type": "negotiation-state",
"id": "ctx_neg_01",
"version": 3,
"data": {
"currentOffer": "prop_019478b7",
"roundNumber": 3,
"concessionsMade": ["latency", "price"]
},
"visibility": "session"
}
]
}Context object fields
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | One of the eight defined context types (see table below) |
id | string | Yes | Unique identifier for this context object within the session |
version | integer | Yes | Version number, incremented when the context is updated |
data | object | Yes | Type-specific payload |
ttl | integer | No | Time-to-live in seconds. The context expires after this duration. |
visibility | string | No | "session" (visible to all participants), "turn" (valid for current exchange only), or "persistent" (survives session close). Defaults to "session". |
Defined context types
| Type | Purpose |
|---|---|
schema-contract | Agreed-upon data schemas for the session (event formats, API contracts) |
agent-card | Agent capability declarations (supported performatives, protocols, trust requirements) |
negotiation-state | Current negotiation progress (round number, concessions, standing offers) |
shared-knowledge | Facts established during the session that both parties agree on |
execution-state | Progress of ongoing work (task completion, milestones, metrics) |
trust-evidence | Trust-related data (verification results, compliance attestations) |
economic-state | Financial context (budget remaining, costs incurred, escrow status) |
custom | Application-specific context. Use a namespaced type in the data to avoid collisions. |
Integrity chain
Every ASP message includes three integrity fields that together create a tamper-evident, cryptographically verifiable conversation record.
How the hash chain works
Message 0 Message 1 Message 2 Message 3
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ hash: H0 │◄──────│prevHash: │◄──────│prevHash: │◄──────│prevHash: │
│ prev: 0s │ │ H0 │ │ H1 │ │ H2 │
│ sig: S0 │ │ hash: H1 │ │ hash: H2 │ │ hash: H3 │
└──────────┘ │ sig: S1 │ │ sig: S2 │ │ sig: S3 │
└──────────┘ └──────────┘ └──────────┘Each message's previousHash points to the preceding message's hash, forming an unbroken chain. If any message is altered, removed, or reordered, the chain breaks and verification fails.
hash
SHA-256 hash of the canonical JSON representation of the content object. Canonical JSON is produced by:
- Sorting all object keys lexicographically
- Removing all whitespace between tokens
- Normalizing Unicode to NFC form
- Using minimal numeric representation (no trailing zeros)
sha256:SHA-256(canonicalJSON(content))previousHash
The hash value of the previous message in the session, creating a chain link. For the first message in a session, previousHash is 64 zero hex characters:
sha256:0000000000000000000000000000000000000000000000000000000000000000Chain ordering is determined by (timestamp, sender.agentId, sequenceNumber). When two messages have the same timestamp, the sender's agent ID breaks the tie. When both match, the sequence number provides the final deterministic order.
signature
Ed25519 signature computed over the concatenation of the following fields, separated by null bytes (\0):
version \0 sessionId \0 sequenceNumber \0 timestamp \0 sender.agentId \0 performative \0 hash \0 previousHashThe signature binds the message metadata to its content hash, proving that the sender produced this specific message with this specific content at this specific point in the conversation.
Hash chains make the conversation tamper-evident. If any party modifies, reorders, inserts, or removes a message after the fact, the chain breaks. Both participants can independently verify the integrity of the entire conversation by replaying the hash chain from the first message. This is critical for dispute resolution, where the unmodified conversation record serves as evidence.
Verification process
Compute the content hash
Serialize the content object to canonical JSON and compute its SHA-256 hash. Compare the result to the hash field in the message.
Verify the chain link
Confirm that previousHash matches the hash of the preceding message in the session (or is all zeros for the first message).
Verify the signature
Concatenate the signed fields with null byte separators. Verify the Ed25519 signature using the sender's public key (obtained from their Agent Card during the INTRODUCE phase).
import { verify } from "@noble/ed25519"
function verifyMessage(
message: AspMessage,
previousMessage: AspMessage | null,
senderPublicKey: Uint8Array
): boolean {
// 1. Verify content hash
const canonical = canonicalJSON(message.content)
const computedHash = sha256(canonical)
if (computedHash !== message.integrity.hash) return false
// 2. Verify chain link
const expectedPrevHash = previousMessage
? previousMessage.integrity.hash
: "sha256:" + "0".repeat(64)
if (message.integrity.previousHash !== expectedPrevHash) return false
// 3. Verify signature
const signedData = [
message.version,
message.sessionId,
String(message.sequenceNumber),
message.timestamp,
message.sender.agentId,
message.performative,
message.integrity.hash,
message.integrity.previousHash,
].join("\0")
return verify(
message.integrity.signature,
new TextEncoder().encode(signedData),
senderPublicKey
)
}DPoP proof structure
Each message from a sender includes a DPoP (Demonstrating Proof-of-Possession) proof that binds the message to the sender's cryptographic key pair. This prevents token replay and impersonation.
The DPoP proof is a JWT with the following structure:
{
"header": {
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "...",
"y": "..."
}
},
"payload": {
"htm": "ASP",
"htu": "asp://019478a3-0001-7def-8a12-000000000001",
"iat": 1741354200,
"jti": "unique-proof-id",
"ath": "sha256:..."
}
}Understanding the DPoP proof
| Field | Location | Description |
|---|---|---|
typ | Header | Always "dpop+jwt" |
alg | Header | Signing algorithm. ES256 (ECDSA with P-256) is required. |
jwk | Header | The sender's public key in JWK format |
htm | Payload | The method being bound. Always "ASP" for ASP messages. |
htu | Payload | The URI being bound. Uses asp://{sessionId} to bind the proof to a specific session. |
iat | Payload | Issued-at timestamp (Unix seconds). Prevents replay of old proofs. |
jti | Payload | Unique proof identifier. Each proof must have a unique jti. |
ath | Payload | Access token hash. Required when an access token is present. SHA-256 hash of the token. |
The jwk in the DPoP proof must match the dpopPublicKey in the sender's Agent Card. If they differ, the message should be rejected. This is verified during the INTRODUCE phase and on every subsequent message.
Response constraints
The optional constraints object lets the sender specify expectations for the response.
| Field | Type | Description |
|---|---|---|
maxResponseTimeMs | integer | Maximum time the sender will wait for a response, in milliseconds |
maxTokenBudget | integer | Suggested upper bound on the token budget for generating the response. Advisory only. |
requiredTrustScore | number | Minimum trust score (0 to 100) the responding agent must have for the response to be considered valid |
allowedPerformatives | string[] | List of performatives the sender expects in the response |
{
"constraints": {
"maxResponseTimeMs": 30000,
"maxTokenBudget": 4096,
"requiredTrustScore": 50.0,
"allowedPerformatives": ["ACCEPT", "REJECT", "COUNTER"]
}
}Response constraints are advisory. The protocol does not enforce them at the transport level. An agent that ignores constraints may receive lower trust scores over time, but the message itself will still be delivered and processed.
Encoding and size limits
Encoding
| Format | Description |
|---|---|
| JSON | Primary format. All examples in this documentation use JSON. |
| MessagePack | Optional binary format for bandwidth-sensitive scenarios. Same schema as JSON. |
| String encoding | All strings are UTF-8. |
| Binary data | Base64url encoding for any binary values within JSON payloads. |
Size limits
| Limit | Value |
|---|---|
| Maximum message size | 1 MiB |
Maximum content.body size | 512 KiB |
For payloads that exceed these limits, use a URI reference in the body that points to the full content hosted externally. Include a hash of the referenced content so the recipient can verify integrity after retrieval.
{
"body": {
"topic": "result",
"data": {
"summary": "Analysis complete. 47,000 records processed.",
"fullResultUri": "https://storage.acme.com/results/analysis-019478a3.json",
"fullResultHash": "sha256:e3b0c44298fc1c149afbf4c8996fb924..."
}
}
}Next steps
Was this page helpful?