Specifications Draft for comment
Emissar Mandate v0.1
A scoped, revocable credential that says what an AI agent may do for one person or company, and an A2A extension for presenting it in a task.
JSON Schema · Demo (Node) · Validator (Python) · Example mandate
| Field | Value |
|---|---|
| Version | 0.1, draft for comment |
| Date | 2026-09-26; published for comment 2026-10-05 |
| Author | Emissar team |
| Extension URI | https://emissar.ai/ext/mandate/v1 (redirects to this document) |
| Credential media type | application/vnd.emissar.mandate+sd-jwt (JOSE typ: vnd.emissar.mandate+sd-jwt) |
| JSON Schema | mandate-v0.1.schema.json |
| Runnable proof | The demo and example files in Appendix C (output in Appendix A) |
Abstract
This document defines the Emissar Mandate, a credential stating what one AI agent may do for one principal (a person or organization) at one verifier, and an Agent2Agent (A2A) extension for presenting it inside a task. A mandate is an SD-JWT (RFC 9901) signed by an issuer the verifier trusts. It names the principal, binds use to the agent’s key by JWK thumbprint (cnf.jkt), names the verifier (aud), lists permitted actions and resources as RFC 9396 authorization details with amount, count and rate limits, expires, and points to a Token Status List entry so the principal can revoke it.
The agent presents it in A2A message metadata with a Key Binding JWT that proves possession of the key, answers the verifier’s single-use challenge, and names the one operation being authorized. The document specifies the data model, issuance obligations, the extension, a numbered verification algorithm, revocation checking, and the A2A task states and errors returned when a mandate is missing, insufficient, expired or revoked. It fills the gap A2A section 7.6.4 leaves open and composes with AP2 payment mandates rather than replacing them.
Status of this document
- A draft published for comment on 2026-10-05. Not submitted to any standards body. Emissar Front Door implements it for businesses that turn it on.
- v0.1 will change incompatibly. Breaking changes get a new extension URI (
.../v2, A2A section 4.6.3). - Comments and questions: the contact form at https://emissar.ai/contact (topic “General”).
- Third-party facts were checked against primary sources on 2026-09-26 (section 15). A2A citations are to
main, where section 7.6.4 was added after the v1.0.1 release.
1. Conventions
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.
| Convention | Rule |
|---|---|
| base64url | Base64 with the URL-safe alphabet and no padding, as in RFC 7515 section 2 |
| Time | NumericDate (RFC 7519) in JWTs; RFC 3339 strings in A2A metadata |
| JSON | Member names MUST be unique. RFC 7519 section 4 allows rejecting duplicates or keeping the last; verifiers SHOULD reject |
| Money | Integer amount in minor units plus ISO 4217 currency, as in AP2’s Amount type. {"amount": 4200, "currency": "CAD"} is CAD 42.00 |
| Examples | Illustrative, with reserved .example names, A2A ProtoJSON field names and the JSON-RPC binding. Keys and identifiers come from the demo run in Appendix A |
2. Terminology
| Term | Meaning |
|---|---|
| Principal | The person or organization the action is for. The mandate’s sub |
| Agent (holder) | The agent that holds the mandate and its bound private key, and presents it. The A2A client |
| Verifier | The A2A server agent (or the system behind it) asked to act. It checks the presentation, then performs or refuses the operation. Named in aud |
| Issuer | Signs the mandate after the principal approves it. iss. May be the verifier itself |
| Trusted surface | The non-agentic interface where the principal reviews and approves a mandate (term and rule from AP2) |
| Mandate | The issuer-signed SD-JWT of section 5, with all its Disclosures |
| Presentation | An SD-JWT+KB: the issuer-signed JWT, the Disclosures the agent reveals, and a Key Binding JWT (KB-JWT) signed with the agent’s key |
| Operation | One concrete action: type, action, optional resource, optional amount. The KB-JWT claim op |
| Authorization detail | One RFC 9396-style object in authorization_details: actions on a resource type, optionally one resource, within limits |
| Challenge | A single-use nonce the verifier issues with TASK_STATE_AUTH_REQUIRED, which the next presentation on the task must carry |
| Status List Token | The issuer’s signed, compressed status bit array (IETF Token Status List draft). Each mandate has one index |
3. Overview
3.1 The gap this fills
A2A lets an agent pause a task in TASK_STATE_AUTH_REQUIRED when it needs authorization (section 7.6). By default, the credential arrives out of band. An in-band exchange needs prior agreement or an extension (7.6.1, 7.6.3). Section 7.6.3 recommends that in-band credentials be bound so that only one agent can use them, and that sensitive ones be protected from other agents in a chain. Section 7.6.4 then leaves the credential’s scope, representation, validity and revocation undefined. It forbids treating the state change itself as authorization, requires the meaning of any resulting credential to come from the implementation, the credential issuer or an extension, requires implementations to define how an authorized operation is identified and checked before it runs, and forbids assuming that one credential authorizes later messages on the task unless that behavior is defined.
This extension defines all of those points for one class of credential: a principal’s delegation to an agent.
3.2 Goals and non-goals
| Goal | Mechanism |
|---|---|
| Say who authorized the action | sub and principal, signed by a trusted iss |
| Only one agent can use it | cnf.jkt plus a KB-JWT signed with that key |
| Only the intended verifier accepts it | aud, the KB-JWT aud, and a per-task challenge |
| Say exactly what is allowed | authorization_details; the agent names one op per presentation |
| Bound the damage | limits: per-operation amount, total amount, use count, rate |
| Expire | iat, optional nbf, exp |
| Let the principal withdraw it | status (Token Status List); checked before every operation |
| Leave evidence | jti, sd_hash, and an op signed by the agent key; a result record in the task |
| Reveal little | Selective disclosure of principal details and of individual authorization details |
Out of scope for v0.1: agent-to-agent re-delegation, an issuance wire protocol (section 6 sets obligations only), payment execution (AP2, x402), agent reputation (Emissar Verify), signed receipts (Emissar Receipt), and encryption.
3.3 Walk-through
Illustrative: a customer’s assistant agent asks a retailer’s support agent to refund an order.
Principal Issuer Client agent (holder) Verifier agent (A2A server)
| (trusted surface) | |
| | | 1 SendMessage: "refund ORD-1042" |
| | |--------------------------------->|
| | | 2 Task AUTH_REQUIRED |
| | | challenge nonce, needed detail |
| | |<---------------------------------|
| | 3 mandate request | |
| | (details, aud, JWK) | |
| |<-------------------------| |
| 4 review, approve | |
|<------------->| | |
| | 5 SD-JWT, cnf.jkt=agent | |
| |------------------------->| |
| | | 6 SendMessage(taskId) with |
| | | metadata: SD-JWT+KB |
| | |--------------------------------->|
| | 7 GET Status List Token (cached up to its ttl) |
| |<------------------------------------------------------------|
| | | 8 Task COMPLETED, result record |
| | |<---------------------------------|
In step 2 the challenge carries a nonce, the verifier’s audience identifier and the detail it needs (refund ORD-1042, up to CAD 200.00). In step 4 the principal sees the verifier, the grant, the limits, the expiry and the agent. In step 6 the agent reveals only the Disclosures this operation needs and signs a KB-JWT over them with the nonce and the exact operation. In step 8 the verifier performs exactly that operation.
With a standing mandate, steps 3 to 5 happen once, and steps 1, 2 and 6 repeat for every operation. Illustrative business-to-business case: a buyer organization’s identity system gives its procurement agent a 90-day mandate to place purchase orders with one supplier, at most CAD 5,000.00 each and 20 per month.
4. Credential format
The format was open at the start of this work: a signed JWT/JWS or a W3C Verifiable Credential. Three candidates were compared.
| Criterion | JWT with cnf (RFC 7519, RFC 7800) |
SD-JWT with Key Binding (RFC 9901) | W3C VC 2.0 (secured with VC-JOSE-COSE or Data Integrity) |
|---|---|---|---|
| Standard status on 2026-09-26 | RFCs since 2015 and 2016 | Standards Track RFC, November 2025. The SD-JWT VC profile is still a draft (-19, IESG state “Waiting for AD Go-Ahead”) | W3C Recommendations, 15 May 2025 |
| Proof of possession at presentation | Not defined. Needs a custom proof token, or DPoP, which is bound to HTTP method and URL and does not fit a credential carried in message metadata | Built in: the KB-JWT carries aud, nonce, iat and sd_hash |
A holder-signed Verifiable Presentation; details depend on the securing mechanism |
| Selective disclosure | None: the verifier sees every claim | Per claim and per array element, with salted digests | Depends on the securing mechanism (SD-JWT through VC-JOSE-COSE, or specific Data Integrity suites) |
| Fit with AP2 v0.2 and Verifiable Intent | Different structure | Same family. AP2 mandates are SD-JWTs with cnf key binding; Verifiable Intent chains SD-JWTs |
AP2 allows other credential formats in principle, but specifies SD-JWT |
| Revocation | Token Status List status claim works with any JWT |
Token Status List names SD-JWT as a Referenced Token format (draft section 6.2) | Bitstring Status List (W3C Recommendation) |
| Authorization semantics | None; this document defines them | None; this document defines them | VC 2.0 section 5.9 says the data model is not an appropriate authorization tool without an accompanying authorization framework |
| Implementation cost | Lowest: any JOSE library | Low: JOSE plus about 100 lines of hashing and Disclosure handling (the demo script has it) | Highest with JSON-LD processing; lower with VC-JOSE-COSE |
| Carried in A2A metadata as | One compact string | One compact ~-separated string |
A JSON object or an enveloped string |
Decision for v0.1: SD-JWT (RFC 9901) with a mandatory Key Binding JWT, explicitly typed vnd.emissar.mandate+sd-jwt, not profiled as SD-JWT VC.
Why: the KB-JWT is a standard proof of possession carrying exactly what a presentation needs (audience, freshness, nonce, a hash of the disclosed content), which a plain JWT would force this extension to invent. An SD-JWT with no Disclosures costs no more than a JWT, and selective disclosure is there when a mandate covers several resources or personal data. AP2 and Verifiable Intent use the same format, so verifiers can share code. RFC 9901 is a published RFC, while SD-JWT VC is not yet (section 14, question 3). VC 2.0 adds a JSON-LD model this check does not need and disclaims authorization semantics.
Consequences: revocation uses the Token Status List draft (section 9), ES256 is the only algorithm, and the agent key is referenced by thumbprint (5.4).
5. Data model
5.1 Structure
A mandate, as issued, is the compact SD-JWT serialization (RFC 9901 section 4):
<issuer-signed JWT>~<Disclosure 1>~...~<Disclosure N>~
The issuer-signed JWT is a JWS in compact serialization over the claims in section 5.3. Disclosures follow RFC 9901 section 4.2. The data model in this section describes the Processed SD-JWT Payload: the claims after the presented Disclosures are applied and the digest placeholders removed (RFC 9901 section 7.1). The JSON Schema in section 5.12 validates that processed form.
5.2 Issuer-signed JWT header
| Parameter | Requirement |
|---|---|
alg |
REQUIRED. ES256 (ECDSA P-256 with SHA-256, RFC 7518 section 3.4). Verifiers MUST reject none, MAC algorithms and anything not on their allowlist (RFC 8725 sections 3.1 and 3.2) |
typ |
REQUIRED. vnd.emissar.mandate+sd-jwt, checked by verifiers (explicit typing, RFC 8725 section 3.11, RFC 9901 section 9.11). Vendor tree, since it is unregistered |
kid |
REQUIRED. Selects the issuer key from the verifier’s trust configuration |
jku, jwk, x5u |
MUST NOT be present; verifiers MUST reject them. Issuer keys come only from the verifier’s trust configuration |
crit |
MUST NOT be present in v0.1 |
5.3 Claims
“SD” says whether the claim may be selectively disclosable. Anything marked “No” MUST appear in plaintext in the issuer-signed JWT.
| Claim | Req. | SD | Content | Source |
|---|---|---|---|---|
iss |
REQUIRED | No | Issuer identifier, an HTTPS URI | RFC 7519 |
sub |
REQUIRED | No | Principal identifier, unique within the issuer. SHOULD be pairwise: a different value for each verifier | RFC 7519 |
aud |
REQUIRED | No | Verifier identifier (string), or an array of them. A single value is RECOMMENDED | RFC 7519 |
iat |
REQUIRED | No | Issue time | RFC 7519 |
nbf |
OPTIONAL | No | Start of validity | RFC 7519 |
exp |
REQUIRED | No | End of validity | RFC 7519 |
jti |
REQUIRED | No | Unique mandate identifier within the issuer, with at least 128 bits of randomness | RFC 7519 |
cnf |
REQUIRED | No | Object with jkt: the agent key thumbprint (5.4) |
RFC 7800, RFC 9449 section 6.1 |
act |
OPTIONAL | No | Object with sub: an identifier for the acting agent, such as its Agent Card URL. Informational only |
RFC 8693 section 4.1 |
status |
REQUIRED | No | Object with status_list: {idx, uri} (5.9) |
Token Status List section 6.2 |
principal |
OPTIONAL | Members only | Object with type (person or organization, plaintext) and optional name and account, each of which SHOULD be selectively disclosable |
This document |
authorization_details |
REQUIRED | Elements only | Array of one or more authorization details (5.6). The array is plaintext; each element MAY be selectively disclosable | RFC 9396 (profiled) |
_sd_alg |
Conditional | No | sha-256 when any Disclosure exists. No other value is allowed in v0.1 |
RFC 9901 section 4.1.1 |
Selective disclosure anywhere else MUST be rejected (step 6). RFC 9901 section 9.7 leaves the list of security-critical claims to each profile; here, everything deciding validity, binding, audience, revocation and the shape of the grant is plaintext. Verifiers MUST ignore other claims they do not understand, but unknown members inside an authorization detail make it unusable (5.6).
5.4 Agent binding
cnf.jkt is the base64url SHA-256 JWK Thumbprint (RFC 7638) of the agent’s public key, as RFC 9449 section 6.1 defines the jkt confirmation method. For a P-256 key, the thumbprint input is the JSON object with the members crv, kty, x and y in that order and without whitespace. The demo checks its implementation against the RFC 9449 example key (Appendix A, self-tests).
RFC 9901 section 4.1.2 suggests the jwk confirmation method but allows others. jkt is used because the thumbprint is a stable agent-key identifier that Ledger records and Emissar Verify can share, because DPoP-capable OAuth resource servers already compare it, and because it keeps the mandate short. The agent therefore supplies its public key in the KB-JWT header as jwk (RFC 7515 section 4.1.3), and the verifier checks the thumbprint before using the key, which meets RFC 9901 section 4.3.2. cnf MUST NOT contain jwk, jwe or jku in v0.1.
5.5 Audience
The verifier’s identifier is the audience it declares in its Agent Card (7.2), an HTTPS URI it controls such as its A2A endpoint’s origin. aud MUST contain it. A mandate with several audiences MUST NOT carry stateful limits (5.7), since no single verifier could count them.
5.6 Authorization details
Each element of authorization_details is a JSON object in the shape of RFC 9396 section 2, restricted to these members:
| Member | Req. | Content |
|---|---|---|
type |
REQUIRED | A string naming the kind of resource and the meaning of the other members. RFC 9396 section 2.1 recommends a collision-resistant URI the definer controls. In v0.1 the verifier defines its types and lists them in its Agent Card |
actions |
REQUIRED | Non-empty array of action names defined by the type, such as refund or cancel |
identifier |
OPTIONAL | One specific resource, such as an order or subscription number. If absent, the detail covers every resource of the type that belongs to the principal. Issuers SHOULD include it |
limits |
OPTIONAL | Object, section 5.7 |
RFC 9396’s locations, datatypes and privileges are not used: aud names the verifier, and the other two describe data access. A detail with any other member, an unknown limit, or a type the verifier does not support grants nothing (“unsupported”), as an unknown constraint fails in AP2. A detail grants every listed action on its resource within every limit; different limits for different actions need separate details.
Illustrative detail granting refunds on one order:
{
"type": "https://provider.example/mandate-types/order",
"actions": ["refund"],
"identifier": "ORD-1042",
"limits": {
"max_amount": { "amount": 20000, "currency": "CAD" },
"total_amount": { "amount": 20000, "currency": "CAD" },
"max_uses": 2
}
}
5.7 Limits
| Limit | Meaning | Needs verifier state |
|---|---|---|
max_amount |
Largest op.amount for one operation |
No |
total_amount |
Largest sum of op.amount over all accepted operations under this detail |
Yes |
max_uses |
Largest number of accepted operations under this detail | Yes |
rate |
{count, window}: at most count accepted operations in any sliding window of window seconds |
Yes |
Rules:
max_amountandtotal_amountMUST share one currency. An operation in another currency is not covered; there is no conversion.- Only a detail with
max_amountortotal_amountcovers an operation that moves money. - Stateful limits are counted per
(iss, jti, i), whereiis the detail’s zero-based position in the array as issued, counting undisclosed placeholders, which the verifier always sees. They need a single audience (5.5). - Limits are the principal’s ceiling. The verifier’s own policy, such as its refund policy, still applies.
5.8 Validity
Issuers SHOULD set exp as short as the task allows, as AP2 advises for open mandates. Verifiers MUST reject mandates whose exp minus iat exceeds a local maximum (the demo uses 30 days). Long standing mandates lean harder on revocation (section 9).
5.9 Status reference
status.status_list.uri identifies the issuer’s Status List Token and status.status_list.idx the mandate’s index in it (Token Status List section 6.2). The claim is REQUIRED whatever the lifetime, so the public promise holds: a principal can revoke, and the verifier checks before acting.
5.10 Selective disclosure
Only the members of principal (other than type) and whole elements of authorization_details may be selectively disclosable (5.3). The agent discloses principal.account when the verifier needs it to find the principal, principal.name only when asked, and only the details that cover the operation. An undisclosed detail grants nothing. Issuers MAY add decoy digests (RFC 9901 section 4.2.5).
5.11 Example
Mandate M1 from the demo run in Appendix A (full file: examples/mandate/mandate.json). The issuer-signed JWT header and payload, as signed:
{ "alg": "ES256", "typ": "vnd.emissar.mandate+sd-jwt", "kid": "mandate-key-2026-09" }
{
"iss": "https://mandates.assistant.example",
"sub": "psub-6f1c2a9e",
"aud": "https://agents.provider.example",
"iat": 1790468221,
"exp": 1790471821,
"jti": "D-ekl0v6NGLB9TPxac_hGQ",
"cnf": { "jkt": "F1HWDtbr4TVd6yoilQPcluMOKm8EqUUxOCEZJcdPBEk" },
"act": { "sub": "https://agents.assistant.example/billing" },
"status": { "status_list": { "idx": 108115, "uri": "https://mandates.assistant.example/status/3" } },
"principal": {
"type": "person",
"_sd": ["5gzmGTgTyhk1LJ3oeLm7CD7jB_OBb-wMe9QPgcNIZU0", "zlkEreM-9cg8jPSuKqRVxTt_tEY0ilau9QNlR0yugpU"]
},
"authorization_details": [
{ "...": "LitkWRLnCpP3fxwQOzNTy0PwBrjZstOLSU3D0Uu3v-Q" },
{ "...": "Cw---7_D7bYl9llorl2zQAQHOuCNx_nw2yRq4am-0vI" }
],
"_sd_alg": "sha-256"
}
The four Disclosures reveal principal.name (“Alex Example”), principal.account (“CUST-88121”), detail 0 (refund ORD-1042, the object in section 5.6) and detail 1 (cancel subscription SUB-3310). In scenario B the agent disclosed only the account and detail 0, so the verifier never saw the principal’s name or the subscription.
5.12 JSON Schema
mandate-v0.1.schema.json (JSON Schema draft 2020-12) validates the processed payload at its root. Its $defs cover the other objects in this document: issuerJwtHeader, kbJwtHeader, kbJwtPayload, operation, presentationEnvelope, challengeEnvelope, resultEnvelope, cardParams and statusListTokenPayload. validate.py (Appendix C) checks every example file against it, runs nine negative tests, and validates the A2A objects against the A2A JSON Schema bundle generated from a2a.proto (Appendix B). Schema validity is necessary but not sufficient: only the algorithm in section 8 decides acceptance.
6. Issuance
6.1 Issuer models
Who may issue is the verifier’s decision, expressed in its trust configuration. Four models fit v0.1:
| Model | Issuer and trusted surface | Why a verifier trusts it | Typical use |
|---|---|---|---|
| Verifier-issued | The verifier, through its own sign-in and consent page | It authenticated the principal itself | Consumer accounts; strongest account link |
| Organization-issued | The principal organization’s identity system and approval workflow | Agreement made at supplier or partner onboarding | Procurement, logistics |
| Agent-provider-issued | The agent’s provider, through a deterministic part of its app | Agreement with the provider. AP2 calls this “Trusted Agent Provider” | Consumer assistants |
| Third-party issuer | A bank, identity provider or wallet | Accreditation or a trust framework | Regulated actions |
A verifier MUST decide per issuer what that issuer may assert. A link between the principal and an account at the verifier (principal.account, or a sub the verifier maps to an account) is trustworthy only from the verifier itself or from an issuer that verified the link with it. A verifier MUST NOT act on an account named by an issuer it does not trust for account linking.
6.2 Issuer requirements
- The issuer MUST obtain the principal’s approval on a non-agentic trusted surface: no processing on it may be delegated to a language model (AP2’s rule for its Trusted Surface).
- The surface MUST display, from the structured request rather than agent-written prose: the verifier, each authorization detail (type, actions, resource, limits) in plain words, the validity period, and the agent that will hold the mandate.
- The agent MUST NOT be able to use the issuer’s signing key without the principal’s approval.
- The issuer SHOULD require the agent to prove possession of the private key before binding it, for example by signing the request with it.
- The issuer MUST assign a fresh
jtiwith at least 128 random bits, a status list index (section 9.1), and a new salt of at least 128 random bits for each Disclosure (RFC 9901 section 9.3). - The issuer MUST record which mandates are bound to which
cnf.jkt, so it can revoke all of them if an agent key is compromised. - The issuer MUST return the mandate with all Disclosures to the agent over an authenticated, encrypted channel, and SHOULD NOT keep the Disclosures longer than it needs them.
- The issuer MUST give the principal a way to see and revoke their active mandates (section 9.2).
6.3 Issuance steps
v0.1 defines no issuance wire protocol (section 14, question 5). Whatever protocol is used MUST carry these steps: (1) the agent requests authorization_details, an audience and a lifetime, and sends its public key as a JWK; (2) the issuer applies its own policy and may narrow the request; (3) the principal authenticates and approves, narrows or declines on the trusted surface; (4) the issuer builds the section 5.3 claims with cnf.jkt set to the RFC 7638 thumbprint of the agent’s key, creates the Disclosures and signs; (5) the issuer returns the SD-JWT with all Disclosures.
6.4 Keys
Issuers SHOULD publish their public keys as a JWK Set at an HTTPS URL, which verifiers pin in their trust configuration. The issuer signs its Status List Tokens with a key from the same set (the single-entity case of Token Status List section 11.3). Rotation uses a new kid, and retired keys stay published until every mandate they signed has expired.
7. Presentation inside A2A
7.1 Extension identity
The URI is https://emissar.ai/ext/mandate/v1. It is a profile extension in the A2A extensions guide’s terms: it adds typed data under the URI in the metadata of messages, status messages and artifacts, and adds no methods, fields or enum values. New outcomes use existing task states. It works over all three standard bindings and depends on no other extension.
7.2 Agent Card declaration
A verifier that accepts mandates MUST declare the extension in capabilities.extensions:
params member |
Req. | Content |
|---|---|---|
audience |
REQUIRED | The verifier identifier that mandates and KB-JWTs must name (5.5) |
algorithms |
REQUIRED | Accepted signing algorithms. ["ES256"] in v0.1 |
types |
OPTIONAL | The authorization_details types the verifier supports |
acceptedIssuers |
OPTIONAL | Issuer identifiers the verifier trusts. A verifier MAY leave this out and name issuers per challenge instead |
required SHOULD be false: mandates usually apply to some skills only, and required: true makes every request without the extension fail with ExtensionSupportRequiredError (A2A section 3.3.4). The A2A extensions guide likewise reserves required for extensions fundamental to the agent.
Illustrative declaration (from examples/mandate/agent-card.json, which validates against the A2A AgentCard schema):
{
"uri": "https://emissar.ai/ext/mandate/v1",
"description": "Refunds and membership cancellations need an Emissar Mandate from the account holder.",
"required": false,
"params": {
"audience": "https://agents.provider.example",
"algorithms": ["ES256"],
"types": [
"https://provider.example/mandate-types/order",
"https://provider.example/mandate-types/subscription"
],
"acceptedIssuers": ["https://mandates.assistant.example"]
}
}
7.3 Activation
A client that presents a mandate MUST list the URI in the A2A-Extensions service parameter of that request: the HTTP header for JSON-RPC and HTTP+JSON, the a2a-extensions metadata key for gRPC. It SHOULD also list the URI in Message.extensions. A verifier MUST process mandate metadata only when the extension is activated for the request. When it is not, the verifier MUST ignore any metadata under the URI and treat the request as carrying no mandate. A verifier that activates the extension SHOULD echo the URI in the response’s A2A-Extensions header.
7.4 The challenge
When an operation needs a mandate and none was presented, or a presentation failed, the verifier moves the task to TASK_STATE_AUTH_REQUIRED (section 10) and puts this object in status.message.metadata under the URI:
| Member | Req. | Content |
|---|---|---|
error |
REQUIRED | An error code from section 10.2 (mandate_required when nothing was presented) |
errorDescription |
OPTIONAL | Short human-readable text. It MUST NOT reveal information that helps an attacker (section 12.9) |
challenge |
REQUIRED with AUTH_REQUIRED |
{nonce, audience, expiresAt}: a fresh nonce with at least 128 random bits, single use and bound to this task; the verifier’s audience identifier; and the nonce’s expiry as an RFC 3339 time (RECOMMENDED: 10 minutes ahead) |
authorizationDetails |
OPTIONAL | The details the operation needs, in the section 5.6 shape, so the agent can request a matching mandate |
acceptedIssuers |
OPTIONAL | Issuers the verifier would accept for this operation |
The status message MUST also contain a text part explaining what is needed, for the principal to read (A2A section 7.6.1 asks for one). A new challenge MUST invalidate any earlier challenge for the task. The challenge is input from another party: authorizationDetails is a request for the principal to decide on the issuer’s trusted surface, never something the client approves on the principal’s behalf (section 12.6).
7.5 Carrying the presentation
The client continues the task by sending a message with the task’s taskId and this object in Message.metadata under the URI:
{ "presentation": "<issuer-signed JWT>~<selected Disclosures>~<KB-JWT>" }
The object MUST contain exactly the presentation member, a compact SD-JWT+KB of at most 16,384 characters. One message carries at most one presentation.
The agent builds the KB-JWT as RFC 9901 section 4.3 defines, with these profile rules:
| Part | Member | Content |
|---|---|---|
| Header | typ |
kb+jwt |
| Header | alg |
ES256 |
| Header | jwk |
The agent’s public key. Its RFC 7638 thumbprint MUST equal cnf.jkt. It MUST NOT contain private key members |
| Payload | iat |
Current time |
| Payload | aud |
The verifier’s audience identifier: one string, equal to challenge.audience |
| Payload | nonce |
challenge.nonce from the task’s latest AUTH_REQUIRED status |
| Payload | sd_hash |
base64url SHA-256 over the presented <issuer JWT>~<Disclosures>~ string (RFC 9901 section 4.3.1) |
| Payload | op |
The one operation this presentation authorizes (below) |
op is an extra KB-JWT claim, which RFC 9901 says to avoid without a compelling reason. The reason: a mandate grants a range, so the agent’s key must commit to the concrete operation. Otherwise the verifier would take it from message text, which prompt injection can shape, or from unsigned metadata. The signed op is also evidence for the Ledger record. AP2 has the agent sign a closed mandate per transaction for the same reason.
op member |
Req. | Content |
|---|---|---|
type |
REQUIRED | An authorization detail type |
action |
REQUIRED | One action |
identifier |
OPTIONAL | The resource acted on. REQUIRED when the covering detail has an identifier |
amount |
OPTIONAL | Money moved by the operation (section 1). REQUIRED when the operation moves money |
The agent SHOULD disclose only the Disclosures the operation needs: the covering authorization detail, and principal members the verifier asked for. It MUST NOT send a Disclosure twice or one that was not issued (RFC 9901 section 4).
7.6 Example
Scenario B from Appendix A, presented on the task that scenario A put in AUTH_REQUIRED. The presentation string is shortened here; the full request is examples/mandate/send-message-request.json.
POST /a2a/v1 HTTP/1.1
Host: agents.provider.example
Content-Type: application/json
A2A-Version: 1.0
A2A-Extensions: https://emissar.ai/ext/mandate/v1
{
"jsonrpc": "2.0",
"id": 2,
"method": "SendMessage",
"params": {
"message": {
"messageId": "accc4087-21a2-4a97-a443-e3c93d6b9300",
"role": "ROLE_USER",
"parts": [{ "text": "Refund 42.00 CAD on ORD-1042. Mandate attached." }],
"taskId": "ba8e2999-d7b1-4cf5-9391-fb4389c48851",
"extensions": ["https://emissar.ai/ext/mandate/v1"],
"metadata": {
"https://emissar.ai/ext/mandate/v1": {
"presentation": "eyJhbGciOiJFUzI1NiIsInR5cCI6InZuZC5lbWlzc2FyLm1hbmRhdGUrc2Qt..."
}
}
},
"configuration": { "historyLength": 0 }
}
}
The KB-JWT inside that presentation (examples/mandate/presentation.json):
{ "typ": "kb+jwt", "alg": "ES256",
"jwk": { "kty": "EC", "crv": "P-256",
"x": "tdSAJw2C_T7Wt_hYlGlhUYoYz3czD2O3H9UymXC8nK8",
"y": "Bznu_9Twp-3RzdFVjR00gxyopAZwx6KT48kAR4dxe-0" } }
{
"iat": 1790468221,
"aud": "https://agents.provider.example",
"nonce": "-Wzblvf9Zom1-tuyljKiOg",
"sd_hash": "NfZhRWM2VtPe6phbp6-tveuSUpZyMVTgwxI-JSsQOlY",
"op": {
"type": "https://provider.example/mandate-types/order",
"action": "refund",
"identifier": "ORD-1042",
"amount": { "amount": 4200, "currency": "CAD" }
}
}
7.7 Result record
After an accepted presentation, the verifier SHOULD put this object under the URI in the metadata of the artifact that records the outcome, or of the final status message when there is no artifact:
| Member | Content |
|---|---|
result |
accepted |
iss, jti |
The mandate used |
sdHash |
The KB-JWT sd_hash, identifying the exact presentation |
jkt |
OPTIONAL. The agent key thumbprint |
The record is protected only by the transport; signed receipts belong to Emissar Receipt v0.1. Example: examples/mandate/send-message-response-completed.json.
8. Verification algorithm
The verifier runs these steps in order for each message carrying a presentation. The first failing step sets the error code (section 10.2). Steps 2 to 16 match the numbered steps in mandate-demo.mjs (trace in Appendix A). Inputs: the request; the verifier’s audience identifier; its trust configuration (issuers, their keys, permitted status URIs, accepted types); clock skew s (RECOMMENDED 60 seconds); a KB-JWT age limit (RECOMMENDED 300 seconds); a maximum mandate lifetime; the task’s live challenge; the usage store.
- Locate. If
A2A-Extensionsdoes not list the URI, ignore mandate metadata. IfMessage.metadata[URI]is absent and the operation needs a mandate, returnmandate_required. If it is not an object whose only member is a stringpresentation, returnInvalid params(10.3). Resolve the task as A2A requires (unknown:TaskNotFoundError; terminal:UnsupportedOperationError). - Split. Reject presentations over 16,384 characters. Split on
~. The last component is the KB-JWT and MUST be non-empty. The first is the issuer-signed JWT. The ones between are Disclosures and MUST be non-empty. Failure:mandate_invalid. - Check the issuer JWT header. It MUST be a compact JWS.
typMUST bevnd.emissar.mandate+sd-jwt;algMUST be on the allowlist;kidMUST be a string;jku,jwk,x5uandcritMUST be absent. Failure:mandate_invalid. - Resolve the issuer key. Read
issfrom the not-yet-verified payload. Look it up in the trust configuration and select the key bykidfrom that issuer’s keys only. Unknown issuer or key:mandate_invalid. - Verify the issuer signature (RFC 7515 section 5.2). ES256 signatures are the 64-byte
R||Sform. Failure:mandate_invalid. - Apply Disclosures as RFC 9901 section 7.1 describes, with this profile’s limits.
_sd_alg, if present, MUST besha-256. Each Disclosure MUST decode to a JSON array (three elements for an object member, two for an array element) whose first element is a string salt. Compute each digest over the ASCII Disclosure string. Each digest MUST be referenced exactly once, and only inprincipal._sdor as an{"...": digest}element ofauthorization_details. A disclosed member name MUST NOT be_sdor...and MUST NOT already exist. Remove undisclosed placeholders,_sdand_sd_alg. Remember each remaining detail’s issued position. Failure:mandate_invalid. - Check claims.
iss,sub,aud,iat,exp,jti(at least 16 characters),cnf.jkt,status.status_list.idxandstatus.status_list.uriMUST be present with the right types, and the issuedauthorization_detailsarray MUST be non-empty.expMUST be later thaniat, andexpminusiatMUST NOT exceed the maximum lifetime. Failure:mandate_invalid. - Check the validity window. With skew
s:iatmore thansin the future givesmandate_invalid; now earlier thannbf - sgivesmandate_not_yet_valid; now at or afterexp + sgivesmandate_expired. - Check the audience.
audMUST equal, or be an array containing, the verifier’s audience identifier. Failure:mandate_invalid. - Check key binding. The KB-JWT MUST be a compact JWS with header
typkb+jwt, an allowlistedalg, and ajwkthat is a P-256 public key without private members. Compute its RFC 7638 thumbprint. It MUST equalcnf.jkt. Verify the KB-JWT signature with that key. Failure:proof_invalid. - Check KB-JWT claims.
sd_hashMUST equal the base64url SHA-256 of the presented<issuer JWT>~<Disclosures>~string.audMUST be a string equal to the verifier’s audience identifier.iatMUST be no more thansin the future and no older than the age limit.nonceMUST be a string andopan object. Failure:proof_invalid. - Consume the challenge.
nonceMUST match a challenge this verifier issued for this task that has not expired. Delete it whatever the later steps decide, so it can never be used twice. Failure:proof_invalid. - Check status (section 9.3). Fetch or reuse the Status List Token named by
status.status_list.uri, fetching only URIs the trust configuration permits for this issuer (any other URI counts as unobtainable). Verify it, decompress it, and read the value atidx. Out-of-bounds index:mandate_invalid.0x01(INVALID):mandate_revoked.0x02(SUSPENDED):mandate_suspended. Any other non-zero value:mandate_invalid. Token unobtainable, unverifiable or expired:status_unavailable. Only0x00(VALID) continues. - Match the operation.
opMUST contain onlytype,action,identifierandamount, with valid types (malformed:proof_invalid). A disclosed detail coversopwhen it is well formed and supported (5.6), itstypeequalsop.type, itsactionscontainop.action, itsidentifier(if any) equalsop.identifier, and, whenop.amountis present, it has an amount limit in the same currency. The verifier MUST also confirm, from its own records, that the resource belongs to the principal. No covering detail:unsupported_detailif a detail ofop.typewas unsupported, otherwiseinsufficient_scope. - Check limits. For covering details in issued order, accept the first one where
op.amountdoes not exceedmax_amount, the accepted total plusop.amountdoes not exceedtotal_amount, accepted uses plus one do not exceedmax_uses, and accepted uses within the lastrate.windowseconds plus one do not exceedrate.count. None passes:limit_exceeded. - Commit. Record the use (count, amount, time) under
(iss, jti, position)atomically with the decision, so concurrent presentations cannot overspend. The verifier MAY release the reservation if the operation then fails. - Act and record. Perform exactly the operation in
op, nothing broader. If the verifier derives the operation independently, for example from the conversation, it MUST confirm it equalsopin type, action, identifier and amount. Add the result record (7.7). Logiss,jti,sub,jkt,sd_hash,op, the task id and the outcome, but not the presentation or Disclosures unless required and protected (A2A section 13.4).
Steps 2 to 16 MUST be decided by deterministic code. A language model in the verifier agent may run the conversation but MUST NOT decide whether a mandate authorizes an operation.
9. Revocation and status checking
9.1 Issuer duties
- The issuer is the Status Issuer for its mandates in v0.1, and signs Status List Tokens (
typstatuslist+jwt) with a key from its published set. - Each list SHOULD hold at least 131,072 entries (Bitstring Status List’s default, chosen for herd privacy), with indices assigned at random.
bitsis 1 when only revocation is needed, and 2 when suspension is also used.- Tokens MUST include
subequal to the list URI,iat,expandttl.ttlSHOULD be 300 seconds or less for lists covering mandates that move money. - The list MUST be served over HTTPS at its URI in response to
GETwithAccept: application/statuslist+jwt(Token Status List section 8.1).
9.2 Principal actions
| Action | Effect | Status value |
|---|---|---|
| Revoke | Permanent. The mandate is dead | 0x01 INVALID |
| Suspend | Temporary pause, for example while a dispute is open | 0x02 SUSPENDED |
| Resume | Only from suspension | back to 0x00 VALID |
| Revoke all for one agent | Every mandate with that cnf.jkt (6.2, item 6) |
0x01 for each |
The issuer MUST republish the Status List Token promptly after any change, and SHOULD tell the principal when it takes effect (9.4).
9.3 Verifier duties
- Check status on every presentation (step 13), after the checks on the mandate itself, as Token Status List section 8.3 requires.
- Validate the Status List Token:
typ, a signature by a key trusted for the mandate’s issuer,subequal to the URI,expnot passed, andbitsone of 1, 2, 4 or 8. - Cache a token no longer than the shorter of its
ttland local policy. For operations above a local amount threshold, verifiers SHOULD refetch regardless of the cache. - Fail closed: if the status cannot be established, do not act (
status_unavailable). - Fetch only status URIs the trust configuration allows for the issuer, such as its own origin. Trust to sign mandates is not trust to direct the verifier’s HTTP requests.
9.4 Revocation latency
Worst case from revocation to refusal: the issuer’s publishing delay, plus the verifier’s cache age (at most ttl), plus any CDN caching. With ttl 300 seconds, a revoked mandate can still be accepted for about five minutes. Tighter bounds need a shorter ttl and refetching for high-value operations. The demo refetches every time, so scenario H refuses at once.
10. Interaction with TASK_STATE_AUTH_REQUIRED
10.1 Rules
- An operation that needs a mandate MUST run as a Task (A2A section 7.6.1).
- Unless rule 3 applies, when a mandate is missing or a presentation fails, the verifier MUST move the task to
TASK_STATE_AUTH_REQUIREDwith the section 7.4 challenge and a text part. It MUST NOT answer with an A2A error (such as a JSON-RPC error), which would leave the client no task to continue. - The verifier MAY instead end the task in
TASK_STATE_REJECTEDwhen policy says stop, for example after three failed presentations, aproof_invalidsuggesting theft, or a barred issuer or principal. The status message then carrieserroranderrorDescriptionbut nochallenge. - A blocking
SendMessagereturns atAUTH_REQUIRED(A2A section 3.2.2), so the client gets the challenge in the response. The client answers with a presentation on the sametaskId, or cancels. - After acceptance the task moves to
TASK_STATE_WORKINGand then a terminal state. If the operation then fails for business reasons, the task ends inTASK_STATE_FAILEDand the verifier MAY release the usage reserved in step 16.
10.2 Outcomes
| Condition | Step | Task state | error |
What the client can do |
|---|---|---|---|---|
| No presentation for an operation that needs one | 1 | AUTH_REQUIRED |
mandate_required |
Get a mandate |
Malformed, wrong typ or alg, untrusted issuer, bad signature or Disclosures, missing claims, lifetime too long, wrong audience, status index out of range |
2 to 9, 13 | AUTH_REQUIRED (or REJECTED) |
mandate_invalid |
Get one from an accepted issuer |
| Not yet valid | 8 | AUTH_REQUIRED |
mandate_not_yet_valid |
Wait |
| Expired | 8 | AUTH_REQUIRED |
mandate_expired |
Get a new mandate |
Wrong agent key, bad KB-JWT, sd_hash or aud mismatch, stale iat, unknown or reused nonce, malformed op |
10 to 12, 14 | AUTH_REQUIRED (or REJECTED) |
proof_invalid |
Re-present with the bound key and new nonce |
| Revoked | 13 | AUTH_REQUIRED |
mandate_revoked |
Ask the principal |
| Suspended | 13 | AUTH_REQUIRED |
mandate_suspended |
Ask the principal |
| Status unavailable | 13 | AUTH_REQUIRED |
status_unavailable |
Retry later |
| No disclosed detail covers the operation | 14 | AUTH_REQUIRED |
insufficient_scope |
Disclose the right detail, or get a broader mandate |
| The matching detail has an unsupported type or member | 14 | AUTH_REQUIRED |
unsupported_detail |
Use the verifier’s declared types |
| Over a limit | 15 | AUTH_REQUIRED |
limit_exceeded |
New mandate, or a person |
| Accepted | 16, 17 | WORKING, then terminal |
result record accepted |
None |
Every AUTH_REQUIRED response carries a new challenge that replaces the old one. insufficient_scope is RFC 6750’s name; section 11.1 maps the codes to AP2’s.
10.3 Protocol-level errors
These are A2A errors, not mandate outcomes. They follow A2A sections 3.3.2, 3.3.4 and 5.4, with google.rpc.ErrorInfo reasons in the A2A style (upper snake case, domain a2a-protocol.org for A2A-specific errors).
| Condition | JSON-RPC | gRPC | HTTP+JSON |
|---|---|---|---|
The Agent Card marks the extension required: true and the request does not activate it |
-32008 ExtensionSupportRequiredError |
FAILED_PRECONDITION |
400 |
Message.metadata[URI] is not an object whose only member is a string presentation |
-32602 Invalid params, with a google.rpc.BadRequest field violation on message.metadata["https://emissar.ai/ext/mandate/v1"] |
INVALID_ARGUMENT |
400 |
taskId unknown or not accessible |
-32001 TaskNotFoundError |
NOT_FOUND |
404 |
taskId names a terminal task |
-32004 UnsupportedOperationError |
FAILED_PRECONDITION |
400 |
Scenario C1 in Appendix A shows the last row: replaying an accepted request on its finished task is refused by A2A itself, before any mandate check.
10.4 Later messages on the same task
Per A2A section 7.6.4, this extension defines the scope of what a presentation authorizes: exactly one operation, the op in its KB-JWT, performed once. A presentation authorizes nothing else on the task, including later messages. A further operation on the same task needs a new challenge (the verifier moves the task back to AUTH_REQUIRED) and a new KB-JWT. The same mandate may be presented again within its limits. Credentials a verifier receives out of band, such as OAuth tokens, are outside this extension and keep whatever meaning their own issuer gives them.
10.5 Chains of agents
A2A section 7.6.2 lets a client that is itself an agent pass an AUTH_REQUIRED request upstream. If agent B, working for agent A, meets a verifier that wants a mandate, B moves its own task with A to AUTH_REQUIRED, and the mandate must be issued to B’s key: a mandate bound to A’s key is useless to B. Re-delegation is out of scope (section 14, question 6). Intermediaries that relay a presentation can read its disclosed content but cannot use it, since they lack the agent’s key and the nonce and audience tie it to one verifier’s task. That is the property A2A section 7.6.3 asks in-band credentials to have.
11. Relationship to AP2, OAuth and related work
11.1 AP2: compose, do not compete
AP2 v0.2 (released 2026-04-28) defines Checkout and Payment Mandates as SD-JWT credentials, in closed form (final values approved by the user) and open form (constraints plus the agent’s key in cnf, from which the agent signs closed mandates). Its general Agent Authorization model says the approach could apply beyond payments, and that new mandate and constraint types may be defined. Emissar Mandate is such an application for non-payment actions, built from the same parts.
| Aspect | AP2 v0.2 | Emissar Mandate v0.1 |
|---|---|---|
| Domain | Purchases: checkout and payment | Any action a verifier defines: refunds, cancellations, bookings, orders, disputes |
| Format | SD-JWT (SD-JWT VC claims such as vct) |
SD-JWT (RFC 9901), own typ, no vct |
| Agent binding | cnf.jwk in open mandates |
cnf.jkt, key supplied in the KB-JWT header |
| Concrete action | A closed mandate, signed by the user or by the agent | The op claim in the KB-JWT |
| Constraints | Typed constraints; unknown ones fail | Authorization details plus limits; unknown members fail |
| Revocation | None defined; reuse limited by receipts and short expiry | Token Status List, checked before every operation |
| Receipts | Verifier-signed Mandate Receipts | Unsigned result record now; signed receipts in Emissar Receipt |
| Agent-to-agent delegation | Out of scope | Out of scope |
Composition rules:
- Separate jobs. In a purchase, AP2 mandates authorize the checkout and payment; an Emissar Mandate authorizes the business action around it, such as changing a booking. A verifier MUST NOT treat one kind as the other.
- Side by side. Both extensions can be active in one request, each with its own metadata entry. Neither credential embeds the other.
- One agent key. If the agent uses one key for both, the verifier can check that the thumbprint of the AP2
cnf.jwkequals the mandate’scnf.jkt. - Stricter limit wins. Where both constrain an amount, the operation must satisfy both. Emissar Settle is planned to enforce this.
- Refunds. The AP2 v0.2 documents reviewed define no refund mandate. A refund requested by a customer’s agent is an Emissar Mandate operation; executing it is outside both specifications.
Emissar error |
Closest AP2 receipt error |
|---|---|
mandate_invalid, proof_invalid, mandate_expired, mandate_not_yet_valid, mandate_revoked, mandate_suspended |
invalid_credential |
unsupported_detail |
unresolved_constraint |
insufficient_scope, limit_exceeded |
invalid_mandate |
| (verifier does not support the extension) | mandates_not_supported |
11.2 Verifiable Intent
Mastercard’s Verifiable Intent (v0.1.0, changelog dated 2026-02-19, Apache-2.0) layers SD-JWTs: a credential provider binds the user’s key, the user signs mandates, and in autonomous mode the agent signs final values, each layer bound to the one before by cnf and sd_hash. It covers checkout and payment with AP2’s mandate types and defers revocation to a future version. On 2026-04-28 the FIDO Alliance announced that Google contributed AP2 and Mastercard contributed Verifiable Intent to its Payments Technical Working Group, and formed an Agentic Authentication Technical Working Group. Emissar Mandate uses the same building blocks for non-payment actions and adds revocation (section 14, question 12).
11.3 OAuth
The mandate reuses OAuth data shapes so that a verifier’s existing policy code applies:
| OAuth element | Use here |
|---|---|
RFC 9396 authorization_details |
The scope format (5.6). RFC 9396 section 9.1 lets JWT access tokens carry the same claim, so one policy engine can check both |
RFC 9449 cnf.jkt |
The agent binding (5.4) |
RFC 8693 act |
Names the acting agent (informational) |
RFC 7800 cnf |
The confirmation claim |
| Token Status List | Revocation without introspection calls |
When the verifier runs an authorization server and the principal has an account there, a short-lived DPoP-bound access token with authorization_details, obtained out of band, is the established path, and A2A’s default (sections 7.3, 7.6.1). Emissar’s delegated-authority guide walks through it. A mandate adds in-band carriage (no redirect mid-task), an issuer that need not be the verifier, a per-operation signature by the agent key, and revocation by status list instead of introspection. The verifier’s authorization server can itself be the issuer (6.1). Exchanging an accepted mandate for an internal token with RFC 8693 is possible but not defined here.
11.4 The OID4VP in-task authorization extension
This experimental a2aproject extension puts an OpenID for Verifiable Presentations request in an AUTH_REQUIRED status message, and the principal’s wallet answers the verifier out of band. It proves facts about the principal; a mandate carries the principal’s delegation to an agent, in band. Both can sit on one Agent Card, and an SD-JWT VC profile (question 3) would let an OID4VP request ask a wallet for a mandate.
11.5 Other Emissar modules
Verify answers whether to trust the agent and its provider; a mandate answers whether the principal authorized the action, and a verifier needs both. Settle is planned to check payment amounts against mandate limits. Ledger and Receipt record iss, jti, sd_hash, jkt, op and the outcome. Front Door’s per-tenant policy says which skills require a mandate.
12. Security considerations
AP2’s threat model assumes prompt injection cannot be fully prevented and treats every model and agent as a potential attacker. This document makes the same assumption: the principal’s agent, the verifier’s agent and any intermediary may be manipulated.
12.1 Replay
- Each presentation needs a verifier-issued, single-use, task-bound nonce (step 12). A copied presentation fails in any other task (scenario C2).
- The KB-JWT
aud,iatwindow andsd_hashstop reuse at another verifier, after a delay, or with different Disclosures. - A2A refuses messages to finished tasks (scenario C1).
- Stateful limits bound what a replay could achieve even if a nonce check were missed.
12.2 Confused deputy
- Wrong verifier. A mandate for verifier X cannot be accepted by Y:
audand the KB-JWTaudmust both name Y. - Wrong principal’s resource. Naming
ORD-1042does not prove it belongs to the principal. The verifier MUST check ownership in its own records (step 14) and accept account links only from issuers trusted for them (6.1). - The verifier’s own agent. A model-driven agent can be talked into doing more. Step 17 limits it to the signed
op, decided by deterministic code (section 8).
12.3 Scope escalation
- Unknown types, members and limits grant nothing (5.6). Undisclosed details grant nothing (5.10).
- An operation that moves money needs a detail with an amount limit in the same currency (5.7).
- A detail without
identifiercovers every resource of its type the principal owns. Issuers SHOULD avoid that for consequential actions, and surfaces MUST say “any” plainly. - The agent cannot widen a mandate: it only chooses which signed parts to reveal.
12.4 Key compromise
| Key | Effect | Response |
|---|---|---|
| Agent key | The attacker can present every mandate bound to it until expiry | Issuer revokes all mandates with that cnf.jkt (9.2). Short lifetimes, hardware-backed keys, and per-verifier keys limit the damage |
| Issuer key | The attacker can mint mandates for any principal the issuer serves, and sign status lists | Verifiers remove the key from their trust configuration. Issuers keep keys in a hardware security module and rotate by kid |
| Stolen mandate without the agent key | Useless: step 10 fails (scenario D) | None needed; the issuer may revoke as a precaution |
12.5 Clock skew
Clock checks use a skew of 60 seconds (RECOMMENDED) on iat, nbf and exp, and on the KB-JWT iat. Challenge expiry is measured on the verifier’s own clock, so skew does not affect it. Parties SHOULD synchronize clocks. Verifiers SHOULD NOT raise the skew to accommodate a peer’s broken clock.
12.6 Prompt-injection-driven misuse
- At the principal’s agent. Injected content can lead the agent to request operations the principal did not intend. Mandate limits cap the damage. The trusted surface shows the principal the structured request, not the agent’s summary of it, so a manipulated agent cannot word its way to approval.
- Malicious challenges. A hostile verifier can ask, in
authorizationDetails, for far more than the task needs. The client MUST NOT forward such a request for approval without the trusted surface rendering it in full, and SHOULD warn the principal when requested details go beyond what the conversation needs. - At the verifier. Authorization comes only from
op, checked by steps 14 and 15. Text parts of the message cannot add to it. - Explanations.
errorDescriptionand status text are untrusted input to the client. They MUST NOT steer the client’s tools.
12.7 Algorithm and type confusion
The issuer JWT, the KB-JWT and the Status List Token have distinct typ values, each checked (steps 3, 10, 13). Only ES256 is accepted and none never is. Issuer keys never come from token headers. The one embedded key, in the KB-JWT header, is used only after its thumbprint matches the issuer-signed cnf.jkt.
12.8 Denial of service
Presentations are capped at 16,384 characters, and verifiers SHOULD rate-limit presentations per task and per client (A2A section 13.4). The status fetch, the only network call, comes after the local checks, and only to URIs permitted for the issuer (9.3), which also keeps the verifier from being used for server-side request forgery.
12.9 Error detail
Detailed failure reasons help attackers probe. Verifiers MAY report every failure in steps 2 to 9 as mandate_invalid and in steps 10 to 12 as proof_invalid, with no detail, and keep the precise reason in their logs. The demo does this: the client gets a generic errorDescription, and only the report prints the internal reason.
12.10 Trusted surface
Everything rests on the surface showing the principal the truth. A surface an agent can drive, or one that shows agent-written text as the grant, defeats every check in section 8. Hence the non-agentic rule in section 6.2, taken from AP2.
13. Privacy considerations
| Topic | Design |
|---|---|
| What the verifier learns | The plaintext claims, the disclosed details and principal members; not undisclosed ones, and not other mandates |
| Minimal disclosure | Principal members and individual details are selectively disclosable (5.10). The agent discloses only what the operation needs (7.5) |
| Correlation across verifiers | Mandates are audience-bound, so a verifier never sees another verifier’s mandate. sub SHOULD be pairwise. The agent key thumbprint is a correlation handle, so agents SHOULD use a separate key for each verifier |
| Issuer tracking use | Verifiers fetch whole lists; large lists with random indices give herd privacy, and caching lowers fetch frequency. The issuer still sees which verifiers fetch (Token Status List section 12.1) |
| Salts | 128-bit random salts stop verifiers from guessing hidden values (RFC 9901 section 9.3) |
| Intermediaries | A presentation passing through other agents exposes its disclosed content to them. v0.1 does not encrypt presentations (question 9). Agents SHOULD send presentations directly to the verifier |
| Retention | Verifiers SHOULD keep only the step 17 log fields, only as long as disputes and law require. If Emissar runs an issuer, verifier or status service for customers, the trust center and privacy policy change first and retention is enforced in code (repository rule 4) |
14. Open questions
Items 1 and 2 were settled when this draft was published. Items 3 to 13 are open for v0.2; comments are welcome.
| # | Question | Default until decided |
|---|---|---|
| 1 | Publish this draft for comment? | Settled: published on 2026-10-05 |
| 2 | The A2A extensions guide says an extension’s specification should be hosted at its URI | Settled: https://emissar.ai/ext/mandate/v1 redirects to this document |
| 3 | Profile the mandate as an SD-JWT VC (dc+sd-jwt, a vct value) once that draft is an RFC? That would let OID4VP wallets and AP2 toolchains carry mandates |
Own typ, no vct |
| 4 | Accept cnf.jwk as well as cnf.jkt, for tools built for AP2 and Verifiable Intent? |
jkt only |
| 5 | Standardize issuance: an OAuth authorization request with authorization_details and a DPoP-proved key whose response is a mandate, or OpenID for Verifiable Credential Issuance? |
Obligations only (section 6) |
| 6 | Re-delegation from one agent to another: the Delegate SD-JWT individual draft that AP2 cites, or RFC 8693 token exchange? | Out of scope |
| 7 | Allow one-round-trip presentations without a challenge, using messageId as the nonce plus a replay cache? Saves a round trip for standing mandates |
Challenge required |
| 8 | Publish shared authorization detail types for the procurement and logistics wedge (quote request, purchase order, delivery exception) so suppliers need not each invent them? | Types are verifier-defined |
| 9 | Encrypt presentations (JWE) for multi-hop chains, as A2A section 7.6.3 recommends for sensitive credentials? | Not encrypted; send directly |
| 10 | Should verifiers return a signed receipt for every accepted presentation, as AP2 requires, or leave that to Emissar Receipt? | Unsigned result record |
| 11 | Who hosts status lists for small issuers? If Emissar does, it becomes a data processor with privacy-policy and retention duties | Issuer hosts |
| 12 | Track and align with the FIDO Alliance agentic work (AP2, Verifiable Intent, Agentic Authentication Technical Working Group), which may extend beyond payments | Monitor |
| 13 | The media type (vnd.emissar...) and the URI embed the name Emissar. A rename would change both identifiers |
Keep |
15. References
All URLs were accessed on 2026-09-26.
15.1 Normative
| Reference | Title | URL |
|---|---|---|
| [A2A] | A2A Protocol Specification, main branch (sections 3.2, 3.3, 3.4, 4.6, 5.4, 7.6 including 7.6.4, 8.4, 9, 13.4, 14.2.2) |
https://github.com/a2aproject/A2A/blob/main/docs/specification.md |
| [A2A-PROTO] | A2A protocol definition (a2a.proto) |
https://github.com/a2aproject/A2A/blob/main/specification/a2a.proto |
| [RFC2119] | Key words for use in RFCs to Indicate Requirement Levels | https://www.rfc-editor.org/rfc/rfc2119 |
| [RFC8174] | Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words | https://www.rfc-editor.org/rfc/rfc8174 |
| [RFC3339] | Date and Time on the Internet: Timestamps | https://www.rfc-editor.org/rfc/rfc3339 |
| [RFC7515] | JSON Web Signature (JWS) | https://www.rfc-editor.org/rfc/rfc7515 |
| [RFC7517] | JSON Web Key (JWK) | https://www.rfc-editor.org/rfc/rfc7517 |
| [RFC7518] | JSON Web Algorithms (JWA) | https://www.rfc-editor.org/rfc/rfc7518 |
| [RFC7519] | JSON Web Token (JWT) | https://www.rfc-editor.org/rfc/rfc7519 |
| [RFC7638] | JSON Web Key (JWK) Thumbprint | https://www.rfc-editor.org/rfc/rfc7638 |
| [RFC7800] | Proof-of-Possession Key Semantics for JSON Web Tokens (JWTs) | https://www.rfc-editor.org/rfc/rfc7800 |
| [RFC8725] | JSON Web Token Best Current Practices | https://www.rfc-editor.org/rfc/rfc8725 |
| [RFC9396] | OAuth 2.0 Rich Authorization Requests | https://www.rfc-editor.org/rfc/rfc9396 |
| [RFC9449] | OAuth 2.0 Demonstrating Proof of Possession (DPoP), section 6.1 (jkt) |
https://www.rfc-editor.org/rfc/rfc9449 |
| [RFC9901] | Selective Disclosure for JSON Web Tokens (SD-JWT), Standards Track, November 2025 | https://www.rfc-editor.org/rfc/rfc9901 |
| [TSL] | Token Status List, draft-ietf-oauth-status-list-21 (2026-06-21). IESG state: RFC Editor queue | https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/ |
| [ISO4217] | ISO 4217 Currency codes | https://www.iso.org/iso-4217-currency-codes.html |
15.2 Informative
| Reference | Title | URL |
|---|---|---|
| [A2A-EXT] | Extensions in A2A | https://github.com/a2aproject/A2A/blob/main/docs/topics/extensions.md |
| [A2A-JSON] | A2A JSON Schema bundle generated from a2a.proto (non-normative build artifact) |
https://a2a-protocol.org/latest/spec/a2a.json |
| [A2A-OID4VP] | OID4VP In-Task Authorization Extension, v1 draft (experimental) | https://github.com/a2aproject/experimental-ext-oid4vp-auth/blob/main/v1/spec.md |
| [AP2] | Agent Payments Protocol specification v0.2 | https://github.com/google-agentic-commerce/AP2/blob/main/docs/ap2/specification.md |
| [AP2-AUTH] | AP2 Agent Authorization | https://github.com/google-agentic-commerce/AP2/blob/main/docs/ap2/agent_authorization.md |
| [AP2-PM] | AP2 Payment Mandate | https://github.com/google-agentic-commerce/AP2/blob/main/docs/ap2/payment_mandate.md |
| [AP2-SEC] | AP2 Security and Privacy Considerations | https://github.com/google-agentic-commerce/AP2/blob/main/docs/ap2/security_and_privacy_considerations.md |
| [AP2-LOG] | AP2 changelog (0.2.0, 2026-04-28) | https://github.com/google-agentic-commerce/AP2/blob/main/CHANGELOG.md |
| [AP2-AMT] | AP2 Amount schema | https://github.com/google-agentic-commerce/AP2/blob/main/code/sdk/schemas/ap2/types/amount.json |
| [DSDJWT] | Delegate SD-JWT, draft-gco-oauth-delegate-sd-jwt-00 (individual draft, 2026-04-22) | https://datatracker.ietf.org/doc/draft-gco-oauth-delegate-sd-jwt/ |
| [SDJWTVC] | SD-JWT-based Verifiable Digital Credentials, draft-ietf-oauth-sd-jwt-vc-19 (2026-08-31). IESG state: Waiting for AD Go-Ahead | https://datatracker.ietf.org/doc/draft-ietf-oauth-sd-jwt-vc/ |
| [VC2] | W3C Verifiable Credentials Data Model v2.0, Recommendation, 15 May 2025 (section 5.9) | https://www.w3.org/TR/vc-data-model-2.0/ |
| [VC-JOSE] | W3C Securing Verifiable Credentials using JOSE and COSE, Recommendation, 15 May 2025 | https://www.w3.org/TR/vc-jose-cose/ |
| [VC-DI] | W3C Verifiable Credential Data Integrity 1.0, Recommendation, 15 May 2025 | https://www.w3.org/TR/vc-data-integrity/ |
| [BSL] | W3C Bitstring Status List v1.0, Recommendation, 15 May 2025 | https://www.w3.org/TR/vc-bitstring-status-list/ |
| [VI] | Verifiable Intent specification (v0.1.0) | https://github.com/agent-intent/verifiable-intent/blob/main/spec/README.md |
| [FIDO] | FIDO Alliance to Develop Standards for Trusted AI Agent Interactions (2026-04-28) | https://fidoalliance.org/fido-alliance-to-develop-standards-for-trusted-ai-agent-interactions/ |
| [RFC6750] | OAuth 2.0 Bearer Token Usage (insufficient_scope) |
https://www.rfc-editor.org/rfc/rfc6750 |
| [RFC8693] | OAuth 2.0 Token Exchange | https://www.rfc-editor.org/rfc/rfc8693 |
| [RFC6838] | Media Type Specifications and Registration Procedures (vendor tree) | https://www.rfc-editor.org/rfc/rfc6838 |
| [EMISSAR-DA] | Emissar guide: Delegated authority | https://emissar.ai/learn/delegated-authority |
| [EMISSAR-EXT] | Emissar guide: A2A extensions | https://emissar.ai/learn/a2a-extensions |
Appendix A. Demo output
node mandate-demo.mjs --write . in the examples folder, run on 2026-09-26 (local time; the script prints UTC) with Node v24.13.0. The script generates new keys on each run, so keys, identifiers and signatures differ between runs; the outcomes do not. The example JSON files in examples/mandate/ were written by this run. Long strings are shortened in the printout only.
| Scenario | What it tests |
|---|---|
| A | First contact without a mandate |
| B | A valid presentation: refund CAD 42.00 on ORD-1042 |
| C1, C2 | Replay: on the finished task, and of the presentation in a new task |
| D | Wrong agent key: an attacker holds the mandate and Disclosures but signs with its own key |
| E | Over the per-operation limit: CAD 250.00 against max_amount CAD 200.00 |
| F | Out of scope: cancel SUB-9999 when the mandate covers SUB-3310 |
| G | Expired mandate |
| H | Revoked mandate: the issuer flips the status bit and republishes the list |
Emissar Mandate v0.1 demo. node v24.13.0, clock 2026-09-27T00:17:01.000Z
Self-tests
RFC 7638 thumbprint of the RFC 9449 example key: 0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I (matches RFC 9449 section 6.1)
Token Status List example "eNrbuRgAAhcBXQ" inflates to b9a3 (matches draft section 4.2)
Setup
issuer https://mandates.assistant.example (kid mandate-key-2026-09); verifier audience https://agents.provider.example
agent key jkt F1HWDtbr4TVd6yoilQPcluMOKm8EqUUxOCEZJcdPBEk
attacker key jkt hPtwcgortBgK2E9s7u18B-jW8glSQ6WTBl4MP39lSzA
status list https://mandates.assistant.example/status/3: 131072 entries, bits 1, lst 52 chars
Issued mandate M1: issuer JWT header
{
"alg": "ES256",
"typ": "vnd.emissar.mandate+sd-jwt",
"kid": "mandate-key-2026-09"
}
Issued mandate M1: issuer JWT payload (as signed; _sd and ... hold digests)
{
"iss": "https://mandates.assistant.example",
"sub": "psub-6f1c2a9e",
"aud": "https://agents.provider.example",
"iat": 1790468221,
"exp": 1790471821,
"jti": "D-ekl0v6NGLB9TPxac_hGQ",
"cnf": {
"jkt": "F1HWDtbr4TVd6yoilQPcluMOKm8EqUUxOCEZJcdPBEk"
},
"act": {
"sub": "https://agents.assistant.example/billing"
},
"status": {
"status_list": {
"idx": 108115,
"uri": "https://mandates.assistant.example/status/3"
}
},
"principal": {
"type": "person",
"_sd": [
"5gzmGTgTyhk1LJ3oeLm7CD7jB_OBb-wMe9QPgcNIZU0",
"zlkEreM-9cg8jPSuKqRVxTt_tEY0ilau9QNlR0yugpU"
]
},
"authorization_details": [
{
"...": "LitkWRLnCpP3fxwQOzNTy0PwBrjZstOLSU3D0Uu3v-Q"
},
{
"...": "Cw---7_D7bYl9llorl2zQAQHOuCNx_nw2yRq4am-0vI"
}
],
"_sd_alg": "sha-256"
}
Issued mandate M1: disclosures
principal.name digest zlkEreM-9cg8jPSuKqRVxTt_tEY0ilau9QNlR0yugpU ["W02UKv9i1nflJH6CiNwSig","name","Alex Example"]
principal.account digest 5gzmGTgTyhk1LJ3oeLm7CD7jB_OBb-wMe9QPgcNIZU0 ["bVsAmshBY4EUkfxMnQ0hsw","account","CUST-88121"]
authorization_details[0] digest LitkWRLnCpP3fxwQOzNTy0PwBrjZstOLSU3D0Uu3v-Q ["ijmTP0iJwsG9xv67alS6pA",{"type":"https://provider.example/mandate-types/order","actions"
authorization_details[1] digest Cw---7_D7bYl9llorl2zQAQHOuCNx_nw2yRq4am-0vI ["wv_-YH7oR3Y3e2mhqIJuPQ",{"type":"https://provider.example/mandate-types/subscription","a
compact SD-JWT as issued: 1722 chars
B: SendMessage request body (A2A v1.0 JSON-RPC)
{
"jsonrpc": "2.0",
"id": 2,
"method": "SendMessage",
"params": {
"message": {
"messageId": "accc4087-21a2-4a97-a443-e3c93d6b9300",
"role": "ROLE_USER",
"parts": [
{
"text": "Refund 42.00 CAD on ORD-1042. Mandate attached."
}
],
"taskId": "ba8e2999-d7b1-4cf5-9391-fb4389c48851",
"extensions": [
"https://emissar.ai/ext/mandate/v1"
],
"metadata": {
"https://emissar.ai/ext/mandate/v1": {
"presentation": "eyJhbGciOiJFUzI1NiIsInR5cCI6InZuZC5lbWlzc2FyLm1hbmRhdGUrc2Qt…[2173 chars]"
}
}
},
"configuration": {
"historyLength": 0
}
}
}
B: verification trace
step 2 ok issuer JWT, 2 disclosure(s), KB-JWT
step 3 ok typ vnd.emissar.mandate+sd-jwt, alg ES256, kid mandate-key-2026-09
step 4 ok trusted issuer https://mandates.assistant.example
step 5 ok issuer signature valid
step 6 ok 2 disclosure(s) applied, 1 detail(s) visible
step 7 ok jti D-ekl0v6NGLB9TPxac_hGQ, lifetime 3600 s
step 8 ok valid until 2026-09-27T01:17:01.000Z
step 9 ok aud https://agents.provider.example
step 10 ok agent key thumbprint matches cnf.jkt F1HWDtbr4TVd6yoilQPcluMOKm8EqUUxOCEZJcdPBEk
step 11 ok sd_hash, aud and iat valid
step 12 ok challenge nonce consumed
step 13 ok status VALID at index 108115
step 14 ok covered by authorization_details[0]
step 15 ok within limits (use 1 of 2)
step 16 ok usage recorded
B: response (task completed; artifact metadata carries the result)
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"task": {
"id": "ba8e2999-d7b1-4cf5-9391-fb4389c48851",
"contextId": "e51e4c94-cbc1-4059-b73e-b8ef02cbe4ed",
"status": {
"state": "TASK_STATE_COMPLETED",
"timestamp": "2026-09-27T00:17:01.000Z"
},
"artifacts": [
{
"artifactId": "34265d41-6d0a-4806-b1ab-b83ac9fe8873",
"name": "Refund",
"parts": [
{
"text": "Refund of 42.00 CAD on order ORD-1042 approved."
},
{
"data": {
"orderId": "ORD-1042",
"refunded": {
"amount": 4200,
"currency": "CAD"
},
"status": "approved"
},
"mediaType": "application/json"
}
],
"extensions": [
"https://emissar.ai/ext/mandate/v1"
],
"metadata": {
"https://emissar.ai/ext/mandate/v1": {
"result": "accepted",
"iss": "https://mandates.assistant.example",
"jti": "D-ekl0v6NGLB9TPxac_hGQ",
"sdHash": "NfZhRWM2VtPe6phbp6-tveuSUpZyMVTgwxI-JSsQOlY",
"jkt": "F1HWDtbr4TVd6yoilQPcluMOKm8EqUUxOCEZJcdPBEk"
}
}
}
]
}
}
}
H: response (revoked; task back in AUTH_REQUIRED with a fresh challenge)
{
"jsonrpc": "2.0",
"id": 14,
"result": {
"task": {
"id": "5111ddb1-8ced-4d5f-a286-fd67234f4824",
"contextId": "9377c64d-daa2-4cd9-82c1-e3bff6a72371",
"status": {
"state": "TASK_STATE_AUTH_REQUIRED",
"timestamp": "2026-09-27T00:17:01.000Z",
"message": {
"messageId": "c7d2c78f-2ecb-477e-ace4-768f0a1ffd34",
"contextId": "9377c64d-daa2-4cd9-82c1-e3bff6a72371",
"taskId": "5111ddb1-8ced-4d5f-a286-fd67234f4824",
"role": "ROLE_AGENT",
"parts": [
{
"text": "The mandate was revoked. This refund needs a mandate from th…[114 chars]"
}
],
"extensions": [
"https://emissar.ai/ext/mandate/v1"
],
"metadata": {
"https://emissar.ai/ext/mandate/v1": {
"error": "mandate_revoked",
"errorDescription": "The mandate was revoked.",
"challenge": {
"nonce": "vEDD36Y146IHPgvbCt8htA",
"audience": "https://agents.provider.example",
"expiresAt": "2026-09-27T00:27:01.000Z"
},
"authorizationDetails": [
{
"type": "https://provider.example/mandate-types/order",
"actions": [
"refund"
],
"identifier": "ORD-1042",
"limits": {
"max_amount": {
"amount": 20000,
"currency": "CAD"
}
}
}
],
"acceptedIssuers": [
"https://mandates.assistant.example"
]
}
}
}
}
}
}
}
Scenarios
id scenario result step pass
A no mandate presented TASK_STATE_AUTH_REQUIRED mandate_required - yes
B valid mandate, refund 42.00 CAD TASK_STATE_COMPLETED accepted - yes
C1 replay of B on its finished task JSON-RPC -32004 UNSUPPORTED_OPERATION - yes
C2 replay of B's presentation in a new task TASK_STATE_AUTH_REQUIRED proof_invalid 12 yes
reason: nonce is not a live challenge for this task
D stolen mandate, attacker's own key TASK_STATE_AUTH_REQUIRED proof_invalid 10 yes
reason: presenting key hPtwcgortBgK2E9s7u18B-jW8glSQ6WTBl4MP39lSzA is not the bound agent key F1HWDtbr4TVd6yoilQPcluMOKm8EqUUxOCEZJcdPBEk
E refund 250.00 CAD, max_amount 200.00 TASK_STATE_AUTH_REQUIRED limit_exceeded 15 yes
reason: 250.00 CAD exceeds max_amount 200.00 CAD; total would exceed 200.00 CAD
F cancel SUB-9999, mandate covers SUB-3310 TASK_STATE_AUTH_REQUIRED insufficient_scope 14 yes
reason: no disclosed authorization detail covers cancel SUB-9999
G expired mandate TASK_STATE_AUTH_REQUIRED mandate_expired 8 yes
reason: expired at 2026-09-26T23:17:01.000Z
H revoked mandate, refund 10.00 CAD TASK_STATE_AUTH_REQUIRED mandate_revoked 13 yes
reason: status list says INVALID (revoked)
9/9 scenarios matched the expected outcome; self-tests passed.
Wrote example files to .
Appendix B. Validation output
python validate.py (jsonschema 4.26.0), run after the demo on the same files:
Mandate schema: examples
ok mandate.json processedPayloadAsPresentedInB (root)
ok mandate.json with every disclosure applied (root)
ok mandate.json header ($defs/issuerJwtHeader)
ok compact issuer JWT decodes to the listed header and payload
ok presentation.json kbJwtHeader ($defs/kbJwtHeader)
ok presentation.json kbJwtPayload ($defs/kbJwtPayload)
ok KB-JWT in the presentation decodes to the listed header and payload
ok send-message-request.json metadata entry ($defs/presentationEnvelope)
ok request carries the presentation from presentation.json
ok auth-required-response.json status metadata ($defs/challengeEnvelope)
ok send-message-response-completed.json artifact metadata ($defs/resultEnvelope)
ok agent-card.json extension params ($defs/cardParams)
ok status-list-token.json payload ($defs/statusListTokenPayload)
Mandate schema: negative tests
ok detail limits with an unknown member (rejected as expected)
ok detail with a member v0.1 does not define (locations) (rejected as expected)
ok processed payload that still contains _sd (rejected as expected)
ok mandate without a status claim (rejected as expected)
ok mandate bound with cnf.jwk instead of cnf.jkt (rejected as expected)
ok amount that is not an integer in minor units (rejected as expected)
ok KB-JWT header carrying a private key (rejected as expected)
ok issuer JWT header with jku (rejected as expected)
ok presentation without a KB-JWT (rejected as expected)
A2A JSON Schema (generated from a2a.proto)
source https://a2a-protocol.org/latest/spec/a2a.json
ok send-message-request.json params ($defs/SendMessageRequest)
ok send-message-response-completed.json result ($defs/SendMessageResponse)
ok auth-required-response.json result ($defs/SendMessageResponse)
ok agent-card.json ($defs/AgentCard)
All checks passed.
Appendix C. Files
All files are published under https://emissar.ai/specs/ with the paths below; keep the same layout locally (the validator reads the schema at ../../schemas/ from its folder).
| Path | Content |
|---|---|
mandate/v0.1 |
This document |
schemas/mandate-v0.1.schema.json |
JSON Schema (draft 2020-12): processed mandate payload and $defs |
examples/mandate/mandate-demo.mjs |
Issuer, agent, verifier and a minimal A2A JSON-RPC verifier agent. Node 20 or later, no dependencies |
examples/mandate/validate.py |
Schema checks, negative tests, and A2A schema checks |
examples/mandate/mandate.json |
Mandate M1: compact form, header, payload as issued, Disclosures, processed payload |
examples/mandate/presentation.json |
Scenario B’s SD-JWT+KB with its decoded KB-JWT |
examples/mandate/send-message-request.json |
Scenario B’s full SendMessage request |
examples/mandate/send-message-response-completed.json |
Scenario B’s response with the result record |
examples/mandate/auth-required-response.json |
Scenario H’s response: AUTH_REQUIRED, mandate_revoked, new challenge |
examples/mandate/agent-card.json |
Illustrative verifier Agent Card declaring the extension |
examples/mandate/status-list-token.json |
The Status List Token after revocation, with the revoked index |