Signed Authorization Verdicts for Agent-Initiated Payments
draft-dias-agent-payment-verdicts-00
This document is an Internet-Draft (I-D).
Anyone may submit an I-D to the IETF.
This I-D is not endorsed by the IETF and has no formal standing in the
IETF standards process.
| Document | Type | Active Internet-Draft (individual) | |
|---|---|---|---|
| Author | Ithelia Dias | ||
| Last updated | 2026-10-09 | ||
| RFC stream | (None) | ||
| Intended RFC status | (None) | ||
| Formats | |||
| Stream | Stream state | (No stream defined) | |
| Consensus boilerplate | Unknown | ||
| RFC Editor Note | (None) | ||
| IESG | IESG state | I-D Exists | |
| Telechat date | (None) | ||
| Responsible AD | (None) | ||
| Send notices to | (None) |
draft-dias-agent-payment-verdicts-00
Network Working Group I. Dias
Internet-Draft Saifuro LLC
Intended status: Informational 9 October 2026
Expires: 12 April 2027
Signed Authorization Verdicts for Agent-Initiated Payments
draft-dias-agent-payment-verdicts-00
Abstract
When a software agent initiates a payment on behalf of an
organization, the party that settles the payment usually cannot see
the policy under which it was permitted. This document describes a
signed authorization verdict: a JSON Web Token, signed with ES256,
that records the outcome of evaluating one payment request, including
its amount, currency, and recipient, against a spending policy that
the organization configured. A party holding the issuer's published
public key can verify a verdict without contacting the issuer.
The document describes the claim set, key publication and rotation,
and a verification procedure, and states what a verdict does not
assert. It describes the format as one implementation issues it,
including the points where it departs from JSON Web Token Best
Current Practices (RFC 8725). It is offered as input to IETF
discussion of authorization records for agent-initiated actions and
is not a proposal for standardization.
Discussion Venues
This note is to be removed before publishing as an RFC.
Comments are welcome by email to the author. Whether signed
authorization decisions are in scope for the work proposed for a
potential AUDIT BoF can be discussed on the audit@ietf.org list
[AUDIT].
Status of This Memo
This Internet-Draft is submitted in full conformance with the
provisions of BCP 78 and BCP 79.
Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF). Note that other groups may also distribute
working documents as Internet-Drafts. The list of current Internet-
Drafts is at https://datatracker.ietf.org/drafts/current/.
Dias Expires 12 April 2027 [Page 1]
Internet-Draft Agent Payment Verdicts October 2026
Internet-Drafts are draft documents valid for a maximum of six months
and may be updated, replaced, or obsoleted by other documents at any
time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress."
This Internet-Draft will expire on 12 April 2027.
Copyright Notice
Copyright (c) 2026 IETF Trust and the persons identified as the
document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents (https://trustee.ietf.org/
license-info) in effect on the date of publication of this document.
Please review these documents carefully, as they describe your rights
and restrictions with respect to this document. Code Components
extracted from this document must include Revised BSD License text as
described in Section 4.e of the Trust Legal Provisions and are
provided without warranty as described in the Revised BSD License.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1. Scope . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2. Requirements Language . . . . . . . . . . . . . . . . . . 5
1.3. Terminology . . . . . . . . . . . . . . . . . . . . . . . 5
2. Related Work . . . . . . . . . . . . . . . . . . . . . . . . 6
3. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4. Verdict Format . . . . . . . . . . . . . . . . . . . . . . . 11
4.1. Serialization, Header, and Signature . . . . . . . . . . 11
4.2. Claims . . . . . . . . . . . . . . . . . . . . . . . . . 11
4.3. The request Claim . . . . . . . . . . . . . . . . . . . . 13
4.4. Presence by Verdict Code . . . . . . . . . . . . . . . . 14
4.5. Verdict Codes . . . . . . . . . . . . . . . . . . . . . . 15
4.6. Amounts . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.7. What the Token Does Not Contain . . . . . . . . . . . . . 17
5. Key Publication . . . . . . . . . . . . . . . . . . . . . . . 17
5.1. JWK Set . . . . . . . . . . . . . . . . . . . . . . . . . 17
5.2. Locating the JWK Set . . . . . . . . . . . . . . . . . . 17
5.3. Test and Production Keys . . . . . . . . . . . . . . . . 18
5.4. Caching and Unknown Key Identifiers . . . . . . . . . . . 18
5.5. Rotation . . . . . . . . . . . . . . . . . . . . . . . . 18
6. Verification Procedure . . . . . . . . . . . . . . . . . . . 19
6.1. Matched, Contradicted, and Unsupported . . . . . . . . . 23
6.2. Checking a Stored Verdict . . . . . . . . . . . . . . . . 23
7. What a Verdict Asserts . . . . . . . . . . . . . . . . . . . 24
8. Versioning and Extensibility . . . . . . . . . . . . . . . . 25
Dias Expires 12 April 2027 [Page 2]
Internet-Draft Agent Payment Verdicts October 2026
9. Implementation Status . . . . . . . . . . . . . . . . . . . . 25
9.1. Saifuro . . . . . . . . . . . . . . . . . . . . . . . . . 26
10. Security Considerations . . . . . . . . . . . . . . . . . . . 27
10.1. Trust in the Issuer and Its Key Set . . . . . . . . . . 27
10.2. Who Stands Behind a Verdict . . . . . . . . . . . . . . 28
10.3. Payee Substitution . . . . . . . . . . . . . . . . . . . 28
10.4. Bearer Tokens . . . . . . . . . . . . . . . . . . . . . 29
10.5. Algorithms and Signatures . . . . . . . . . . . . . . . 29
10.6. Cross-JWT Confusion and Explicit Typing . . . . . . . . 29
10.7. Audience . . . . . . . . . . . . . . . . . . . . . . . . 30
10.8. Replay and Signature Malleability . . . . . . . . . . . 30
10.9. Clock Skew . . . . . . . . . . . . . . . . . . . . . . . 31
10.10. Key Compromise . . . . . . . . . . . . . . . . . . . . . 31
10.11. Amounts and Parsing . . . . . . . . . . . . . . . . . . 31
10.12. Approval of Held Requests . . . . . . . . . . . . . . . 32
10.13. Aggregate Limits . . . . . . . . . . . . . . . . . . . . 32
10.14. Event Tokens . . . . . . . . . . . . . . . . . . . . . . 33
11. Privacy Considerations . . . . . . . . . . . . . . . . . . . 33
12. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 34
13. References . . . . . . . . . . . . . . . . . . . . . . . . . 34
13.1. Normative References . . . . . . . . . . . . . . . . . . 34
13.2. Informative References . . . . . . . . . . . . . . . . . 35
Appendix A. Example . . . . . . . . . . . . . . . . . . . . . . 38
A.1. Example Key . . . . . . . . . . . . . . . . . . . . . . . 38
A.2. Verdict . . . . . . . . . . . . . . . . . . . . . . . . . 39
A.3. Checking the Example . . . . . . . . . . . . . . . . . . 39
Appendix B. Open Issues . . . . . . . . . . . . . . . . . . . . 40
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . 41
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 42
1. Introduction
Three properties of an agent-initiated payment can be checked
separately: which agent initiated it, whether it was within the
authority the agent was given, and whether value moved. The first
and third are the subject of active protocol work (Section 2). The
second is usually decided inside the operator's own systems or a
provider's, and the decision does not travel with the payment.
Dias Expires 12 April 2027 [Page 3]
Internet-Draft Agent Payment Verdicts October 2026
This document describes a verdict: a signed statement, valid for a
short time, that names the request it was made about, the mandate it
was checked against, if any, and the outcome of the check. A party
asked to settle the payment can check a verdict without contacting
the issuer, using the issuer's published keys, its own record of the
payment, a record of the verdicts it has already accepted, and, where
the payment draws on a particular operator's funds, the mandate
identifiers and amount unit agreed with that operator (Section 6).
Refusals produced by policy evaluation are signed in the same way as
approvals; an operator's refusal of a request held for review
produces no verdict (Section 4.5).
This document is descriptive. It records the format that one
implementation issues (Section 9), including the points where the
format departs from [RFC8725] and other properties the author
considers weaknesses (Section 10, Appendix B). It is offered as
input to IETF discussion of authorization records for agent actions,
such as the discussion on the audit mailing list [AUDIT] of a
potential AUDIT BoF and working group charter; in particular,
Section 6.1 and Section 7 state, for one deployed format, what a
successful check establishes and what it does not. The author does
not ask for this format to be adopted; a standard format in this area
would need to resolve the issues in Appendix B and might not be
compatible with this one.
The author is the founder of Saifuro LLC, which operates the
implementation described here, and has a commercial interest in this
area.
1.1. Scope
In scope are the verdict format (Section 4), the publication of
verification keys (Section 5), and the procedure a relying party
follows before it relies on a verdict (Section 6).
Out of scope are: the language in which spending policy is expressed;
how an agent authenticates to the issuer; user consent; how a payment
is executed or settled; how a verdict is carried inside a particular
payment protocol; and the identity of an agent beyond an identifier
assigned by its operator. The issuer described here does not hold or
move funds, and a verdict is not a payment instrument.
Dias Expires 12 April 2027 [Page 4]
Internet-Draft Agent Payment Verdicts October 2026
1.2. Requirements Language
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.
In this document, BCP 14 key words state what a relying party has to
do to rely safely on verdicts in this format. In Table 2, and where
Section 4.3 says that members are REQUIRED, they state what a verdict
contains: a verifier rejects a token in which a required claim or
member is absent (Section 6, steps 4 and 6). Section 5.5,
Section 10.4, and Section 11 also address operators and others that
carry or keep verdicts. The key words do not define requirements for
other issuers or formats.
1.3. Terminology
Operator: The organization that runs an agent and configures the
authority it has to spend.
Agent: Software that proposes payments on an operator's behalf.
Mandate: An operator-configured policy object at the issuer that
limits what one agent may spend. In the deployed implementation
it holds a per-transaction limit and an optional rolling 30-day
limit in one currency, a list of allowed categories, an expiry
date, an optional review threshold, and the name of the principal
expected to approve reviews (Section 10.12). It does not restrict
recipients. Mandates are not edited, only revoked and replaced.
A mandate in this sense is not the same object as the mandates of
[AP2], and it is not a credential signed by a user.
Environment: One of the issuer's separately keyed instances, test
("sandbox") or production. Each operator has its own agents and
mandates in each.
Issuer: The service that evaluates a payment request against the
operator's mandates and signs the result; in the terminology of
[AUTHZEN], a policy decision point.
Verdict: The signed token defined in Section 4. "Verdict code"
means the value of its verdict claim.
Relying party: The party that is about to settle, ship, or release
funds on the strength of a verdict: for example a seller, a
payment service provider, or an escrow release process.
Dias Expires 12 April 2027 [Page 5]
Internet-Draft Agent Payment Verdicts October 2026
Verifier: The component of a relying party that performs Section 6.
Payment record: The relying party's own record of the payment it is
about to settle: amount, currency, and recipient. It comes from
the relying party's systems, never from the token.
Names of claims, members of request, and header parameters that this
format or its event tokens (Section 10.14) use are written without
quotation marks (iss, request.amount, kid); values, and names that
the format does not use, are written in quotation marks ("ES256",
"allow", "aud").
2. Related Work
Several specifications address agent-initiated payments, or
authorization decisions in general, at different points: when a user
authorizes a purchase or delegates authority to an agent ([AP2]),
when a payment credential is handed to a merchant's payment service
provider ([ACP]), on individual requests ([RFC9421], [TAP], [X402]),
and when authorization is requested or decided ([RFC9396],
[AUTHZEN]). Not all of them define a signed object. The
descriptions below are based on the cited public specifications as
accessed on 7 October 2026, and on 9 October 2026 for
[I-D.laxsharma-pact].
OAuth 2.0 Rich Authorization Requests [RFC9396]:
A client requests fine-grained authorization, for example to
initiate a single payment, from an authorization server, usually
with the resource owner's consent, and presents the resulting
access token to a resource server that trusts that authorization
server. A verdict is not an access token: it records the outcome
of a policy evaluation, including refusals, and is meant to be
checked by a relying party whose only relationship with the issuer
is holding its public keys.
Transaction Tokens [I-D.ietf-oauth-transaction-tokens]:
Carry user identity, workload identity, and authorization context
between workloads within a single trust domain. A verdict is
meant to be verified outside the issuing organization, by a party
that does not share its trust domain.
OpenID AuthZEN Authorization API 1.0 [AUTHZEN]:
Defines how a policy enforcement point asks a policy decision
point for a decision, which is returned as JSON. It permits a
decision point to sign its response (Section 11.6 of [AUTHZEN])
but does not define a signed response format. A verdict can be
read as a signed decision about a payment, meant to be verified
further downstream than the enforcement point that requested it.
Dias Expires 12 April 2027 [Page 6]
Internet-Draft Agent Payment Verdicts October 2026
Agent Payments Protocol [AP2]:
Carries the user's authorization as signed mandates that the
merchant, the credential provider, and the payment network verify.
A verdict instead carries the result of an evaluation performed by
the issuer against the operator's policy, without disclosing the
policy. It carries no user consent and can accompany such
mandates without replacing them.
Agentic Commerce Protocol [ACP]:
In its delegated payment specification, the agent platform sends a
payment credential to the merchant's payment service provider with
a single-use allowance (a maximum amount, a currency, a checkout
session, a merchant, and an expiry), and the provider restricts
the resulting token to that allowance. A verdict is a separate
token that the relying party verifies itself before settlement.
x402 [X402]:
The client signs a scheme-specific payment authorization, and the
resource server verifies and settles the payment, either itself or
through a facilitator. That signature is the payer's
authorization of the transfer. A verdict authorizes no transfer;
it is a statement by a third party about a policy evaluation,
signed by neither the payer nor the payee.
HTTP Message Signatures [RFC9421], Web Bot Auth
[I-D.ietf-webbotauth-httpsig-protocol], and Trusted Agent Protocol
[TAP]:
Let a server authenticate an automated client's HTTP requests.
[TAP] profiles HTTP Message Signatures so that a merchant can
recognize an agent's requests using public keys that the payment
network publishes, and adds signed objects that identify the
consumer and carry payment data. A verdict identifies neither the
agent's software nor the consumer; it states the outcome of a
policy evaluation.
Auditing and transparency:
[I-D.kuehlewind-audit-architecture] describes an architecture for
auditing agent delegation and interactions; a verdict is one kind
of record such an architecture could carry or reference.
[RFC9943] describes registering signed statements with a
transparency service that issues receipts, using COSE rather than
JOSE. It addresses long-term verification, which this format
leaves to each party that stores its own copy of the JWK Set
(Section 5.5, Section 10.1).
PACT [I-D.laxsharma-pact]:
Uses the term "Verdict" for a different object: a signed record in
which an evaluator judges whether work an agent delivered under a
Dias Expires 12 April 2027 [Page 7]
Internet-Draft Agent Payment Verdicts October 2026
task contract passes or fails. PACT does not address payments or
their authorization, and the two formats are unrelated apart from
the term.
3. Overview
Agent Issuer Relying Party
| | |
| (1) request | |
|------------------>| |
| | (2) evaluate against |
| | the mandates |
| (3) verdict (JWS) | |
|<------------------| |
| | |
| (4) payment, verdict attached |
|------------------------------------------->|
| | (5) JWK Set (cached) |
| |<-----------------------|
| |----------------------->|
| | | (6) verify,
| | | compare with
| | | payment record
Figure 1: Issuing and checking a verdict
1. The agent sends a payment request, stating amount, currency,
recipient, and category, to the issuer.
2. The issuer evaluates the request against the agent's mandates and
records the decision.
3. The issuer returns the decision, including a verdict. A verdict
is issued for every evaluated request, whatever the outcome.
4. The agent, or a component acting for the operator, presents the
verdict to the relying party together with the payment.
5. The relying party obtains the issuer's JWK Set, normally from its
cache.
6. The relying party verifies the verdict and compares it with its
payment record (Section 6).
Dias Expires 12 April 2027 [Page 8]
Internet-Draft Agent Payment Verdicts October 2026
The relying party needs no account with the issuer and sends it no
request about individual verdicts: from the issuer it needs only the
published JWK Set. It also needs its own payment record, its store of
accepted nonces, and, for step 8 of Section 6, the mandate
identifiers and the amount unit it has agreed with the operator.
Table 1 collects the values that this document refers to: those of
the deployed implementation, and those this document sets for
verifiers.
Dias Expires 12 April 2027 [Page 9]
Internet-Draft Agent Payment Verdicts October 2026
+==============================+==================================+
| Value | Setting |
+==============================+==================================+
| Verdict lifetime (exp - iat) | 300 seconds |
+------------------------------+----------------------------------+
| Clock skew allowed to | at most 60 seconds |
| verifiers | |
+------------------------------+----------------------------------+
| Review expiry | 24 hours |
+------------------------------+----------------------------------+
| Frequency rule | 10 decisions per agent in 60 |
| | seconds |
+------------------------------+----------------------------------+
| Window of the rolling limit | 30 days |
+------------------------------+----------------------------------+
| JWK Set cache lifetime (max- | 300 seconds |
| age) | |
+------------------------------+----------------------------------+
| Refetches on unknown kid | at most one per 30 seconds |
| | (recommended) |
+------------------------------+----------------------------------+
| Pre-publication of a new key | at least 1 hour |
+------------------------------+----------------------------------+
| Retention of a retired key | at least 25 hours after its last |
| | token |
+------------------------------+----------------------------------+
| Redelivery of event tokens | last retry due 24 hours after |
| | creation |
+------------------------------+----------------------------------+
| Identifier prefixes | "dec_", "mnd_", "n_", "agt_", |
| | "evt_" |
+------------------------------+----------------------------------+
| Nonce | 64 random bits |
+------------------------------+----------------------------------+
| Amount | greater than 0, less than 10^15, |
| | at most 6 fractional digits |
+------------------------------+----------------------------------+
| Idempotency-Key retention | 24 hours |
+------------------------------+----------------------------------+
| API request body limit | 64 KB |
+------------------------------+----------------------------------+
| Length of recipient; of | at most 1,024 characters; at |
| category and currency | most 128 characters each |
+------------------------------+----------------------------------+
Table 1: Values referred to in this document
Dias Expires 12 April 2027 [Page 10]
Internet-Draft Agent Payment Verdicts October 2026
4. Verdict Format
4.1. Serialization, Header, and Signature
A verdict is a JSON Web Token (JWT) [RFC7519] in JWS Compact
Serialization [RFC7515]: three base64url segments without padding,
separated by periods.
The issuer emits exactly three header parameters: alg, with the value
"ES256" (ECDSA using P-256 and SHA-256, Section 3.4 of [RFC7518]);
typ, with the value "JWT"; and kid, naming the signing key in the
issuer's JWK Set (Section 5). It does not emit "crit", "jku", "jwk",
"x5u", "x5c", "x5t", "cty", or any other header parameter. The typ
value does not distinguish a verdict from other tokens signed with
the same key (Section 10.6).
The signature is the 64-byte concatenation of R and S, each 32 bytes,
big-endian, as Section 3.4 of [RFC7518] requires, not an ASN.1 DER
encoding. The issuer uses randomized ECDSA (Section 10.5) and does
not normalize S (Section 10.8).
The payload is a JSON object [RFC8259] encoded in UTF-8 [RFC3629].
The issuer writes it without insignificant whitespace and in the
member order of Table 2. Verifiers MUST NOT depend on member order,
whitespace, or string escaping.
4.2. Claims
+================+=========+==========+==========================+
| Claim | Type | Presence | Meaning |
+================+=========+==========+==========================+
| iss | string | REQUIRED | Issuer identifier. |
+----------------+---------+----------+--------------------------+
| jti | string | REQUIRED | Identifier the issuer |
| | | | assigns to the decision. |
| | | | Unique per verdict. |
+----------------+---------+----------+--------------------------+
| iat | integer | REQUIRED | Time of the decision; |
| | | | for an "allow" that |
| | | | approves a review, the |
| | | | time of the approval. |
+----------------+---------+----------+--------------------------+
| exp | integer | REQUIRED | Expiry, on every verdict |
| | | | whatever its code. |
+----------------+---------+----------+--------------------------+
| verdict | string | REQUIRED | Verdict code |
| | | | (Section 4.5). |
+----------------+---------+----------+--------------------------+
Dias Expires 12 April 2027 [Page 11]
Internet-Draft Agent Payment Verdicts October 2026
| mandate | string | see | Identifier of the |
| | | Section | mandate whose evaluation |
| | | 4.4 | produced the verdict. |
+----------------+---------+----------+--------------------------+
| policy_version | string | REQUIRED | Per-operator label; |
| | | | opaque to verifiers. |
+----------------+---------+----------+--------------------------+
| nonce | string | REQUIRED | Random value generated |
| | | | for each verdict; the |
| | | | replay key (Section 6, |
| | | | step 12). |
+----------------+---------+----------+--------------------------+
| request | object | REQUIRED | The payment request that |
| | | | was evaluated |
| | | | (Section 4.3). |
+----------------+---------+----------+--------------------------+
| supersedes | string | see | The jti of the "review" |
| | | Section | verdict that this |
| | | 4.4 | "allow" resolves. |
+----------------+---------+----------+--------------------------+
Table 2: Verdict claims
In Table 2 and in step 6 of Section 6, "integer" means a JSON number
whose value is an integer; the issuer writes such values with no
fraction or exponent part. This narrows NumericDate (Section 2 of
[RFC7519]), which also allows non-integer values.
In the deployed implementation, policy_version is assigned once per
operator and is not updated when mandates change; despite its name,
it does not identify the rules that were applied. Because mandates
are not edited, the mandate claim identifies the limits that were
evaluated. Verifiers MUST treat policy_version as an opaque string.
The nonce is 64 random bits in the deployed implementation; jti is
enforced unique by the issuer.
Dias Expires 12 April 2027 [Page 12]
Internet-Draft Agent Payment Verdicts October 2026
The claims iss, jti, iat, and exp are used with their registered
meanings (Section 4.1 of [RFC7519]). The claim nonce is registered
in the IANA "JSON Web Token Claims" registry by [OIDC-CORE];
Section 12.7.1 of [RFC9449] updated the registration to state that it
may also be used for nonce values in other applications of JWTs, and
it is used here in that sense, as a non-repeating value used to
detect replay. Unlike the nonce of [OIDC-CORE] and [RFC9449], which
is supplied by the party that later checks the token, this nonce is
chosen by the issuer, so it gives the relying party no assurance of
freshness beyond exp. The claims verdict, mandate, policy_version,
request, and supersedes are Private Claim Names (Section 4.3 of
[RFC7519]) and can collide with other uses of the same names
(Appendix B).
Apart from the "sandbox-" prefix of kid (step 3 of Section 6) and the
"evt_" prefix of jti (step 6), verifiers SHOULD treat identifiers as
opaque strings.
Verifiers MUST ignore private claims, and members of request, that
they do not understand (Section 8). Registered claims that verdicts
do not carry are processed as [RFC7519] specifies: a verdict carrying
"aud" is rejected unless the verifier identifies itself with one of
its values (Section 4.1.3 of [RFC7519]), and one carrying "nbf" is
rejected before that time (Section 4.1.5 of [RFC7519]).
4.3. The request Claim
+===========+========+==================================+
| Member | Type | Meaning |
+===========+========+==================================+
| agent | string | Identifier of the agent, unique |
| | | among one operator's agents in |
| | | one environment. |
+-----------+--------+----------------------------------+
| amount | number | Amount evaluated (Section 4.6). |
+-----------+--------+----------------------------------+
| currency | string | Currency or asset code, exactly |
| | | as the request stated it. When |
| | | the mandate claim is present, it |
| | | equals that mandate's currency. |
+-----------+--------+----------------------------------+
| recipient | string | Payee identifier, exactly as |
| | | sent in the request. |
+-----------+--------+----------------------------------+
| category | string | Spending category, exactly as |
| | | sent in the request. |
+-----------+--------+----------------------------------+
Dias Expires 12 April 2027 [Page 13]
Internet-Draft Agent Payment Verdicts October 2026
Table 3: Members of the request claim
All five members are REQUIRED.
The issuer matches currency against the mandate's currency by exact,
case-sensitive comparison, and does not normalize the value or
restrict it to a list of codes. The issuer's documentation uses
uppercase ISO 4217 codes such as "USD", and "USDC" for an asset that
has no ISO 4217 code. Other specifications use other forms, for
example lowercase codes and integer minor units in [ACP]; a relying
party that holds its payment record in another form converts the
currency code to this form, and the amount to the unit agreed with
the operator (Section 4.6; Section 6, step 8), before steps 9 and 10
of Section 6; that conversion is part of its own code.
The format of recipient is agreed between the operator and the
relying party; examples are "vendor:acme_saas" and a wallet address.
The issuer does not check it against the identity of any party
(Section 10.3).
In the deployed implementation's test environment, creating an escrow
is also evaluated. Such a verdict carries the escrow's seller as
recipient and the fixed category "escrow", and the evaluation skips
the category check.
4.4. Presence by Verdict Code
+===========================================+=========+============+
| Verdict code | mandate | supersedes |
+===========================================+=========+============+
| allow, from an evaluation | present | absent |
+-------------------------------------------+---------+------------+
| allow, from approving a review | present | present |
+-------------------------------------------+---------+------------+
| review | present | absent |
+-------------------------------------------+---------+------------+
| deny_limit, deny_velocity, deny_category, | present | absent |
| deny_expired, deny_revoked | | |
+-------------------------------------------+---------+------------+
| deny_no_mandate | absent | absent |
+-------------------------------------------+---------+------------+
Table 4: Conditional claims
Dias Expires 12 April 2027 [Page 14]
Internet-Draft Agent Payment Verdicts October 2026
4.5. Verdict Codes
+=================+===============================================+
| Code | Meaning |
+=================+===============================================+
| allow | The request passed every check in this table, |
| | or it was held for review and the operator |
| | approved it (Section 10.12). The only code |
| | on which a relying party may settle. |
+-----------------+-----------------------------------------------+
| review | The amount is above the mandate's review |
| | threshold, and the operator must approve or |
| | deny the request. Not an approval. |
+-----------------+-----------------------------------------------+
| deny_limit | The amount exceeds the per-transaction limit, |
| | or, together with the amounts allowed under |
| | the same mandate in the previous 30 days, the |
| | rolling 30-day limit. |
+-----------------+-----------------------------------------------+
| deny_velocity | The agent already had 10 or more decisions in |
| | the previous 60 seconds. |
+-----------------+-----------------------------------------------+
| deny_category | The category is not one of the mandate's |
| | allowed categories. |
+-----------------+-----------------------------------------------+
| deny_expired | The mandate is past its expiry date. |
+-----------------+-----------------------------------------------+
| deny_revoked | The mandate has been revoked. |
+-----------------+-----------------------------------------------+
| deny_no_mandate | The agent is not active, holds no mandate, or |
| | holds no mandate in the requested currency. |
+-----------------+-----------------------------------------------+
Table 5: Verdict codes
For each of the agent's mandates in the requested currency, the
checks run in this order: revoked, expired, category, per-transaction
limit, rolling 30-day limit, frequency rule, review threshold. When
the agent holds several such mandates, they are evaluated in order of
creation, and the verdict is the first "allow", otherwise the first
"review", otherwise the result for the oldest mandate. These checks
are the whole of the evaluation; in particular, a mandate does not
restrict the recipient (Section 10.3).
A relying party MUST treat every code other than "allow", including
codes this document does not define, as "do not settle".
Dias Expires 12 April 2027 [Page 15]
Internet-Draft Agent Payment Verdicts October 2026
When the operator approves a review, the issuer issues a new verdict
with the code "allow", its own nonce and expiry, and a supersedes
claim naming the "review" verdict (Section 10.12). The "review"
verdict is not modified. A review that the operator denies, or that
expires, produces no verdict.
An operator records, per environment, a mode of "observe" or
"enforce", which tells its own systems whether to block payments that
are not allowed; the issuer blocks nothing in either mode. In the
deployed implementation, a production environment is in observe mode
until the operator changes it. The issuer evaluates, records, and
signs the same verdicts in either mode, and the mode is not in the
token, so a signed refusal does not show that a payment was blocked.
4.6. Amounts
request.amount is a JSON number in the same units as the mandate's
limits. The issuer does not interpret or convert units; its
documentation uses major units, so that 340 with currency "USD" means
340 US dollars, not 340 cents. The amount is greater than zero and
less than 10^15, and has at most six fractional digits. The issuer
writes it in minimal fixed-point form: an integral amount has no
decimal point (340, never 340.00), other amounts have no trailing
zeros (340.5, never 340.50), and the issuer never uses exponent
notation or leading zeros other than the single 0 before the decimal
point of an amount below 1 (0.5).
Amounts can therefore carry up to 21 significant digits, more than an
IEEE 754 binary64 number holds. This departs from Section 2.2 of
[RFC7493], which says that I-JSON messages should not include such
numbers and recommends JSON strings for them. Many JSON parsers,
including those inside common JWT libraries, read every number as
binary64, and from 2^33 (about 8.6 x 10^9) upward two amounts that
differ in the sixth fractional digit can map to the same binary64
value; 8589934592.000001 and 8589934592.000002 do. A verifier
therefore compares amounts by decimal value (Section 6, step 9).
Nothing in the token states the unit. An operator that configures a
mandate in minor units obtains verdicts whose amounts are in minor
units, and a relying party that compares them with a payment record
in major units (Section 6, step 9) would match a verdict evaluated
for 340 cents against a payment of 340 US dollars. Step 8 of
Section 6 therefore includes agreeing the unit with the operator.
Dias Expires 12 April 2027 [Page 16]
Internet-Draft Agent Payment Verdicts October 2026
4.7. What the Token Does Not Contain
The issuer keeps information about each decision that is not in the
token and is therefore not signed. The decision record it returns to
the operator contains the enforcement mode (Section 4.5), a human-
readable reason, operator metadata such as order references, and, for
a "review" verdict, the review identifier and the principal it waits
for. The issuer also stores, but neither signs nor returns in the
decision record, the payment protocol named in the request. A
verdict is therefore not bound to an order, and a verdict evaluated
for one payment protocol can be presented on another.
The token has no "aud", "sub", "nbf", or "cnf" claim, and no claim
that identifies the operator (Section 10.2, Section 10.4,
Section 10.7).
5. Key Publication
5.1. JWK Set
The issuer publishes its public keys as a JWK Set (Section 5 of
[RFC7517]). Each key has "kty" "EC", "crv" "P-256", "x", "y", "use"
"sig", "alg" "ES256", and a "kid". The deployed implementation
serves the set over HTTPS with "Cache-Control: public, max-age=300"
[RFC9111] and "Access-Control-Allow-Origin: *", with the media type
"application/json" rather than "application/jwk-set+json"
(Section 8.5 of [RFC7517]).
5.2. Locating the JWK Set
The deployed implementation publishes its JWK Set at the path
"/.well-known/jwks.json" under its issuer identifier,
https://verdicts.saifuro.com (Section 9), and keeps an older location
that permanently redirects to it. The suffix "jwks.json" is widely
used but is not registered in the Well-Known URIs registry; serving
the set under it departs from Section 3 of [RFC8615], which requires
registration, and this document does not register it (Appendix B).
A relying party MUST take the list of issuers it accepts, and the JWK
Set location for each, from its own configuration, and MUST fetch
each set over HTTPS with certificate validation. It MUST NOT fetch
keys from a location derived from a token. Otherwise any party can
mint verdicts that verify against keys it publishes itself. A
verifier SHOULD NOT follow HTTP redirects when fetching a JWK Set,
since a redirect moves the source of its keys to a location that is
not in its configuration, and SHOULD bound the size of the response
and the time it waits for it.
Dias Expires 12 April 2027 [Page 17]
Internet-Draft Agent Payment Verdicts October 2026
5.3. Test and Production Keys
The deployed implementation operates a test environment and a
production environment. Each has its own signing key, and both keys
are published in the same JWK Set. The kid of a test key begins with
"sandbox-"; the kid of a production key does not. The issuer
identifier, the claim set, and the token shape are otherwise
identical. Anyone with access to the test environment can obtain a
correctly signed test "allow" for any recipient and amount, so a
verifier rejects tokens whose kid begins with "sandbox-" unless it is
used only for testing (Section 6, step 3; Section 10.6).
5.4. Caching and Unknown Key Identifiers
Verifiers SHOULD cache each JWK Set and refresh it when it is older
than the max-age it was served with. If a refresh fails, a verifier
MAY keep using the last copy it fetched successfully, SHOULD raise an
alert, and SHOULD stop accepting verdicts once that copy is older
than a bound it chooses: the longer it uses a stale copy, the longer
a key the issuer has removed stays trusted (Section 10.10).
The kid is untrusted input, used only as a lookup key, never in a
file path, query, or URL (Section 3.10 of [RFC8725]). On a kid that
is absent from the cached set, a verifier fetches the set again once
and looks again. Because anyone can send a token with any kid, these
refetches MUST be rate-limited; an interval of at least 30 seconds
between refetches triggered by unknown kids is RECOMMENDED, and
scheduled refreshes SHOULD NOT count against that limit. Because the
refetch is rate-limited, a stream of tokens with made-up kids can
delay acceptance of a verdict signed with a key the verifier has not
yet fetched; pre-publication (Section 5.5) avoids that for verifiers
that refresh on schedule.
Pinning a single key in code is NOT RECOMMENDED: an integration that
does so rejects valid verdicts after the next rotation.
5.5. Rotation
The issuer's rotation procedure extends the approach of
Section 10.1.1 of [OIDC-CORE], in which a verifier refetches the set
on an unfamiliar kid, with a period of pre-publication:
1. The issuer publishes the new key in the set at least one hour
before it signs anything with it, so that every verifier that
refreshes on schedule holds the key before the first token signed
with it.
2. It switches signing to the new kid.
Dias Expires 12 April 2027 [Page 18]
Internet-Draft Agent Payment Verdicts October 2026
3. It keeps the previous key in the set for at least 360 seconds
after the last verdict that key signed (the 300-second lifetime
of a verdict plus the 60-second maximum clock skew allowed in
Section 6), and for as long as any event token it signed can
still be delivered (Section 10.14). The deployed
implementation's procedure keeps it for at least 25 hours after
the last token of either kind.
4. It removes the previous key.
The deployed implementation has not yet rotated a key (Section 9), so
this procedure has not been exercised.
The set carries only keys that are about to be used, in use, or
recently retired. A party that keeps verdicts as records, for audit
or disputes, SHOULD store a copy of the JWK Set with them, so that
the signatures can be checked after the key has been removed
(Section 6.2).
6. Verification Procedure
The inputs to verification are the token; the configured list of
accepted issuers with the JWK Set of each; the current time from a
synchronized clock; the payment record; a store of nonces from
verdicts already accepted; whether the verifier is used only for
testing; where step 8 requires them, the mandate identifiers agreed
with the operator and the unit its mandates use; and, for the check
of Section 10.12, a store of the supersedes values of verdicts
already accepted.
A relying party MUST apply every step below before it relies on a
verdict, and MUST reject the token if any step fails. It MAY perform
the steps in a different order, provided that it verifies the
signature (step 5) before it relies on any claim, and records the
nonce (step 12) only after every other step has passed. Every check
MUST use values taken from the payload bytes whose signature step 5
verified. A verifier that parses those bytes more than once, for
example with a JWT library for steps 4 and 7 and with a decimal-
preserving parser for step 9, MUST reject a header or payload that
contains duplicate member names at any depth, so that the parses
cannot disagree about which member they read.
1. Parse. Bound the size of the token before decoding it (verdicts
that the deployed implementation issues are shorter than 11,000
characters; Section 10.11), and bound the nesting depth of JSON
that is parsed before the signature is verified. Split the
token into exactly three segments. Decode each as base64url
without padding (Section 2 of [RFC7515]), rejecting padding,
Dias Expires 12 April 2027 [Page 19]
Internet-Draft Agent Payment Verdicts October 2026
whitespace, any character outside the base64url alphabet, and a
segment whose length is 1 modulo 4; many base64 decoders accept
these, so a verifier that uses one checks the segments itself.
A verifier MAY reject a segment whose unused trailing bits are
not zero. Decode the header and payload as UTF-8, rejecting
invalid UTF-8 (Section 3.7 of [RFC8725]). Each MUST parse as a
JSON object. Header parameter and claim names MUST be unique: a
verifier MUST either reject a header or payload with duplicate
member names or use a parser that keeps only the lexically last
one (Section 4 of [RFC7515], Section 4 of [RFC7519]), and SHOULD
reject duplicates at any depth.
2. Header. The alg value MUST be exactly "ES256", compared case-
sensitively (Section 4.1.1 of [RFC7515]). The accepted
algorithms MUST come from the verifier's configuration for the
issuer, never from the token, and the key selected in step 4
MUST be one the verifier uses only with ES256 (Section 3.1 of
[RFC8725]). Reject a token that has a "crit" parameter
(Section 4.1.11 of [RFC7515]). The kid value MUST be a non-
empty string. Keys MUST NOT be taken from "jku", "jwk", "x5u",
or "x5c", and verifiers SHOULD reject tokens that carry them.
Verifiers do not use typ to identify a verdict (Section 10.6).
3. Environment. Unless the verifier is used only for testing,
reject a token whose kid begins with "sandbox-" (Section 5.3).
4. Issuer and key. The iss claim MUST equal, exactly, the
identifier of an issuer in the verifier's configuration,
compared as a case-sensitive string with no transformations or
canonicalizations (Section 2 of [RFC7519]). Find the key with
the token's kid in that issuer's JWK Set, and only there: a
verifier that accepts more than one issuer MUST NOT look up the
kid in the union of their key sets. This is the binding of keys
to an issuer that Section 3.8 of [RFC8725] requires; iss is read
before the signature is verified and is authenticated by step 5.
The key MUST have "kty" "EC" and "crv" "P-256"; if it has "alg",
the value MUST be "ES256"; if it has "use", the value MUST be
"sig". If the kid is absent, refetch the set once under the
rate limit of Section 5.4; reject the token if the rate limit
does not permit a refetch, if the kid is still absent, or if it
appears more than once.
5. Signature. Verify the signature as ES256 (Section 3.4 of
[RFC7518]). The signature MUST be exactly 64 bytes. The
verifier MUST reject a signature in which R or S is not an
integer in the interval [1, n-1], where n is the order of the
P-256 group (Section 4.1.4 of [SEC1]); an implementation that
omitted this check accepted an all-zero signature for any
Dias Expires 12 April 2027 [Page 20]
Internet-Draft Agent Payment Verdicts October 2026
message (CVE-2022-21449; [PSYCHIC]). Verifiers MUST NOT require
S to be in the lower half of the group order: the issuer does
not normalize S, and such a check rejects about half of genuine
verdicts. Use an implementation that validates that the public
key is a point on P-256 (Section 3.4 of [RFC8725]).
6. Token kind and structure. Reject a token whose payload has a
type claim or whose jti begins with "evt_": it is an event token
signed with the same key (Section 10.14). The jti claim MUST be
a non-empty string. The iat and exp claims MUST be integers
with exp greater than iat; a verifier MAY reject a token whose
exp minus iat exceeds 300. The verdict claim, policy_version,
and nonce MUST be non-empty strings. The request claim MUST be
an object whose agent, currency, recipient, and category are
non-empty strings and whose amount is a number greater than
zero. The mandate and supersedes claims, when present, MUST be
non-empty strings. If the verdict claim is "allow", mandate
MUST be present. If supersedes is present, the verdict claim
MUST be "allow". Reject a token that carries an "aud" claim
unless the verifier identifies itself with one of its values
(Section 4.2).
7. Time. With a clock skew allowance L of at most 60 seconds,
reject the token if the current time is at or after exp + L
(Section 4.1.4 of [RFC7519]), or if iat is later than the
current time + L. If the token carries an "nbf" claim, reject
it while the current time + L is before the time in "nbf".
Expiry does not change a verdict's code: an expired verdict is
rejected whatever its code.
8. Decision and operator. Reject the token unless the verdict
claim is "allow". If the payment draws on funds, credit, or an
account of a particular operator, also reject it unless mandate
is one of the identifiers that operator has agreed with the
relying party (Section 10.2). Without this check, a verdict
that passes every other step shows only that some operator
configured a mandate that allows the payment, and any operator
can do that for any recipient and amount. The relying party
also agrees with that operator the unit in which its mandates
state amounts (Section 4.6); where no particular operator is
involved, nothing establishes the unit (Section 7).
9. Amount. Compare request.amount with the amount in the payment
record, expressed in the unit agreed in step 8, if any, by
decimal value: 340 equals 340.00, and 340 does not equal 340.5.
Read the amount from the verified payload bytes with a JSON
parser that preserves the literal number or yields a decimal
type; most JWT libraries return claims in which numbers are
Dias Expires 12 April 2027 [Page 21]
Internet-Draft Agent Payment Verdicts October 2026
already binary64. The issuer never writes an amount with an
exponent, more than 15 digits before the decimal point, or more
than 6 after it; a verifier SHOULD reject such an amount before
converting it to a decimal type, wherever it first converts it
(step 6 compares it with zero), since some decimal
implementations take time or memory proportional to the
exponent. Verifiers MUST NOT compare amounts as binary
floating-point numbers, except that a verifier MAY compare
binary64 values when both amounts are below 2^30 (about 1.07 x
10^9) and both have at most six fractional digits: in that
range, distinct amounts with at most six fractional digits map
to distinct binary64 values.
10. Currency. The member request.currency MUST equal the currency
code in the payment record exactly, compared case-sensitively.
11. Recipient. The member request.recipient MUST equal the payee
identifier in the payment record, compared code point for code
point, with no Unicode normalization, case folding, or trimming.
It names the payee, not the verifier, and binds the verdict to
the relying party only when the relying party is the payee
(Section 10.7).
12. Replay. Reject the token if the pair of iss and nonce is in the
store of accepted nonces. Otherwise record the pair in the same
atomic operation that accepts the token, and keep it at least
until exp + L. All verifier instances that could accept the
same token MUST share the store, and a verifier that cannot read
or write the store MUST reject the token. The replay key is a
claim value, never the token string (Section 10.8); jti is also
unique per verdict, so recording jti instead of nonce gives the
same protection. A relying party that applies Section 10.12
checks and records the pair of iss and supersedes in the same
atomic operation.
JWT libraries perform most of steps 1, 2, 4, and 5, and the exp and
"nbf" checks of step 7, when they are configured with the algorithm
list ["ES256"], the accepted issuer, and a clock tolerance of at most
60 seconds. Some libraries, such as jose, check "aud" only when an
audience is configured. Implementers need to check what their
library leaves out: a library that is handed a key object cannot
check that key's "alg" and "use" members or whether its kid appears
twice in the set, and some libraries accept padded or whitespace-
broken segments. Steps 3, 6, 8, 9, 10, 11, and 12 are the relying
party's own code, and so is the iat check of step 7 where the library
does not perform it.
Dias Expires 12 April 2027 [Page 22]
Internet-Draft Agent Payment Verdicts October 2026
6.1. Matched, Contradicted, and Unsupported
For each property of the payment record that the relying party relies
on, verification has one of three outcomes:
Matched: a signed claim covers the property and agrees with the
payment record.
Contradicted: a signed claim covers the property and disagrees with
the payment record.
Unsupported: no signed claim covers the property.
A contradicted property causes rejection, by steps 9 to 11. An
unsupported property is neither evidence of a problem nor evidence of
correctness: the relying party has to establish it independently. In
this format, amount, currency, and recipient can be matched or
contradicted. The members request.agent and request.category are
signed, but the payment record has no corresponding property, so they
can be neither matched nor contradicted. The operator behind the
verdict is unsupported unless the relying party maps the mandate
claim to an operator through identifiers agreed out of band
(Section 10.2); the order, the payment protocol, and the identity of
an intermediary relying party are always unsupported. A relying
party SHOULD record which properties it took from its own records
rather than from the signature, so that a later audit can tell the
two apart.
6.2. Checking a Stored Verdict
A party that checks a verdict as a record rather than to settle a
payment, such as an auditor examining a refusal, applies steps 1, 2,
4, 5, and 6 using a copy of the JWK Set that contains the key
(Section 5.5), and step 3 if it needs to distinguish test verdicts.
It evaluates step 7 against the time at which the verdict was
received if that time was recorded, and otherwise does not apply step
7; it does not apply steps 8 to 12. It does not refetch a key set
for a stored verdict: a kid missing from the stored copy is a
failure. Verdicts that the deployed implementation issued before
09:12 UTC on 9 October 2026 may lack request.currency, may contain
Unicode noncharacters, and may be up to about 94,000 characters long;
a party checking one of them as a record applies step 6 without the
currency requirement, and step 1 with a size bound that admits them.
Unless the party obtained the copy of the JWK Set itself, a
successful check shows only that the verdict matches the key set that
the record keeper stored (Section 10.1).
Dias Expires 12 April 2027 [Page 23]
Internet-Draft Agent Payment Verdicts October 2026
7. What a Verdict Asserts
A verdict that passes steps 1 to 6 of Section 6 is a statement,
signed by the holder of the signing key, that it evaluated a payment
request with the stated agent, amount, currency, recipient, and
category against the named mandate (none, for "deny_no_mandate") and
reached the stated verdict code. For a verdict without supersedes,
iat is the time of that evaluation; for an "allow" that carries
supersedes, the evaluation took place when the superseded "review"
verdict was issued, some of its checks were repeated at approval, and
iat is the time of the operator's approval (Section 10.12). The
signature shows that the statement was made, not that it is correct
(Section 10.1). In the terms used on the audit mailing list [AUDIT],
a verdict is an attestation, not a record that a relying party can
recompute: neither the mandate's limits nor the history behind the
rolling 30-day limit is in it. A verdict does not show:
* that an end user consented: no user key is involved;
* that the operator or the agent authorized the payment: neither
signs the token, so it is not evidence of either's authorization;
* that the operator approved the recipient (Section 10.3);
* that the category describes what is being bought: it is a label
supplied with the request, and the relying party has nothing to
compare it with;
* the unit of request.amount: the issuer does not interpret it, and
an operator's mandates can use a different unit from the relying
party's payment record (Section 4.6);
* that the named agent made the request: in the deployed
implementation, a request made with an operator-wide credential
names the agent in its body;
* which operator requested it, or that the operator is the party
paying the relying party (Section 10.2);
* that the payment was made, settled, or delivered, or that funds
exist;
* which payment protocol or order it was evaluated for
(Section 4.7);
* whether the operator's systems block payments that are not allowed
(Section 4.5);
Dias Expires 12 April 2027 [Page 24]
Internet-Draft Agent Payment Verdicts October 2026
* that the mandate is still in force when the verdict is verified:
an "allow" remains valid until exp even if the mandate is revoked
after it was issued, and the relying party has no way to learn of
the revocation from the issuer;
* that the payment is not one of several, each within the per-
transaction limit and below the review threshold, that together
make up a larger purchase;
* that a collection of verdicts is complete: verdicts are returned
only to the operator that requested them, through its API keys and
webhook endpoints, and an operator's denial of a review produces
no verdict.
8. Versioning and Extensibility
The token carries no version number, and the format has no mechanism
for signalling a change. Verifiers detect features by the presence
of claims. An issuer can add private claims and members of request,
which verifiers ignore if they do not understand them (Section 4.2),
and verdict codes, which verifiers treat as "do not settle"
(Section 4.5).
Because verifiers ignore private claims they do not understand, a new
private claim that narrows when a verdict may be relied on constrains
only verifiers updated to check it; until every relying party checks
it, a verdict carrying it is accepted where it was not meant to be.
Adding "aud" is incompatible in the other direction: verifiers that
apply Section 4.1.3 of [RFC7519] without a configured audience reject
every verdict that carries it. Adding either kind of claim is
therefore an incompatible change, as are removing a claim and
changing a claim's type or meaning. Explicit typing alone would not
make such a version fail at verifiers built to this document, which
do not check typ (Section 6, step 2); a new issuer identifier would
(step 4). The deployed implementation announces changes that
verifiers have to act on in its documentation [SAIFURO-VERIFY], with
the date from which they apply.
9. Implementation Status
This section is to be removed before publishing as an RFC.
This section records the status of known implementations of the
format described in this document at the time of posting of this
Internet-Draft, and is based on a proposal described in [RFC7942].
The description of implementations in this section is intended to
assist the IETF in its decision processes in progressing drafts to
RFCs. Please note that the listing of any individual implementation
Dias Expires 12 April 2027 [Page 25]
Internet-Draft Agent Payment Verdicts October 2026
here does not imply endorsement by the IETF. Furthermore, no effort
has been spent to verify the information presented here that was
supplied by IETF contributors. This is not intended as, and must not
be construed to be, a catalog of available implementations or their
features. Readers are advised to note that other implementations may
exist.
According to [RFC7942], "this will allow reviewers and working groups
to assign due consideration to documents that have the benefit of
running code, which may serve as evidence of valuable experimentation
and feedback that have made the implemented protocols more mature.
It is up to the individual working groups to use this information as
they see fit".
Note to the RFC Editor: please also remove the reference to
[RFC7942].
9.1. Saifuro
Organization: Saifuro LLC. The author is its founder and chief
executive.
Description: An authorization service for agent-initiated payments.
It issues a verdict in the format of Section 4 for every evaluated
request, in a test environment and a production environment.
Issuer and keys: Issuer identifier https://verdicts.saifuro.com; JWK
Set at https://verdicts.saifuro.com/.well-known/jwks.json, served
through a content delivery network and holding one production key
and one test key.
Coverage: Section 4 and Section 5, with the departures from
[RFC8725], [RFC7493], and [RFC8615] stated in this document.
Version compatibility: This document (-00). Verdicts issued since
09:12 UTC on 9 October 2026 carry request.currency; verdicts
issued before then may lack it, and all of them have expired.
Implementation experience: Limits are enforced atomically
(Section 10.13). Approving a review repeats the checks described
in Section 10.12. No key rotation has taken place yet. The event
tokens of Section 10.14 resemble Security Event Tokens [RFC8417]
but do not follow that specification.
Level of maturity: Early deployment; in service since September
2026. Issuing verdicts requires an account with Saifuro.
Verifying them requires no account.
Dias Expires 12 April 2027 [Page 26]
Internet-Draft Agent Payment Verdicts October 2026
Licensing: Proprietary service. Verification uses unmodified open-
source JOSE libraries. The public documentation [SAIFURO-VERIFY]
gives starting examples for jose (JavaScript), PyJWT (Python), and
jwx (Go); they do not implement every step of Section 6, and the
documentation lists the main checks they leave to the relying
party.
Documentation: [SAIFURO-VERIFY], [SAIFURO-LIMITS].
Test vectors: [SAIFURO-VECTORS]: 138 tokens with their expected
results, covering each step of Section 6 and Section 6.2, signed
with the example key of Appendix A and further published test
keys.
Contact: Ithelia Dias, ithelia.dias@saifuro.com.
Last updated: 9 October 2026.
10. Security Considerations
The security considerations of [RFC7515], [RFC7519], and [RFC8725]
apply. [I-D.ietf-oauth-rfc8725bis], which would obsolete [RFC8725],
states that a typ value of "JWT" is not effective explicit typing
(Section 3.11 of [I-D.ietf-oauth-rfc8725bis]) and extends the
audience requirement to issuers that may issue JWTs for more than one
relying party in the future (Section 3.9 of
[I-D.ietf-oauth-rfc8725bis]); both widen the departures described in
Section 10.6 and Section 10.7.
10.1. Trust in the Issuer and Its Key Set
A verdict proves that the issuer made a statement, not that the
statement is correct. An issuer that is compromised, misconfigured,
or acting in bad faith can sign approvals for requests outside any
mandate. How a relying party decides to accept an issuer is outside
the scope of this document.
The JWK Set is not signed. Whoever can change what is served at its
configured location, including through control of the domain, its
DNS, certificate issuance, or a TLS-terminating intermediary such as
a content delivery network in front of the server, can add a key and
mint verdicts that every verifier accepts. Protecting that endpoint
is equivalent to protecting the signing keys. A copy of the JWK Set
kept with stored verdicts (Section 5.5) is the relying party's own
record; it is not evidence to a third party that a key belonged to
the issuer, because the deployed implementation neither signs its JWK
Set nor publishes a history of retired keys.
Dias Expires 12 April 2027 [Page 27]
Internet-Draft Agent Payment Verdicts October 2026
10.2. Who Stands Behind a Verdict
A verdict is evidence about one operator's policy, not about the
payer. The token names no operator, and any operator that the issuer
serves can configure a mandate that allows any recipient and amount,
and then obtain a valid "allow" under it. A relying party that needs
to know which operator stands behind a payment agrees the mandate
identifiers with that operator out of band and accepts only verdicts
whose mandate claim is one of them (Section 6, step 8). In the
deployed implementation, mandate identifiers are assigned by the
issuer and are not reused.
The policy_version claim is the same on every verdict of one operator
and is shared only with operators whose accounts were created on the
same day. It links verdicts to an operator without reliably
identifying one, and relying parties MUST NOT use it as an operator
identifier.
The member request.agent is unique only among one operator's agents
in one environment, and in the deployed implementation it is "agt_"
followed by a name the operator chooses, so any operator can obtain
verdicts that name the same agent identifier as another operator.
Relying parties MUST NOT use request.agent to identify an operator or
a payer; step 8 of Section 6 uses the mandate claim for that.
10.3. Payee Substitution
Mandates in the deployed implementation do not restrict recipients.
An agent that is compromised or misled, or anyone holding its
credentials, can obtain an "allow" for a recipient of its choosing
within the mandate's limits, and a relying party controlled by that
recipient finds that every step of Section 6 passes. Verdicts
therefore do not defend against payee substitution; an operator that
needs that defence applies its own controls to the recipients its
agents request. Restricting recipients in mandates is an open issue
(Appendix B).
The issuer accepts as recipient (up to 1,024 characters) or category
(up to 128) any non-empty string of Unicode characters other than
noncharacters, including characters that render invisibly, reorder
text, or resemble other characters, and an operator approving a
review is shown the strings as the agent supplied them. An approval
can therefore be obtained for a recipient that looks like another.
Step 11 of Section 6 does not help: it compares exactly, and the
recipient that benefits is the one the token names.
Dias Expires 12 April 2027 [Page 28]
Internet-Draft Agent Payment Verdicts October 2026
10.4. Bearer Tokens
A verdict is a bearer token. It is not bound to a key held by the
agent or the operator, it has no "cnf" claim [RFC7800], and nothing
in it identifies the party presenting it, the payer, or the order.
Anyone who obtains an unused "allow" before it expires, such as the
agent's runtime, an intermediary between the agent and the relying
party, anyone who can read a log that recorded it, or a receiver of
an event token that carries it (Section 10.14), can present it first,
with a payment of the same amount and currency to the same recipient;
the legitimate presentation then fails step 12 of Section 6. A
relying party that releases goods, services, or funds on a verdict
has to bind the order to the payer by its own means. Verdicts SHOULD
be carried only over authenticated, confidential channels. Agents,
operators, and relying parties SHOULD NOT log unexpired "allow"
verdicts in full; jti or nonce identifies a verdict in a log without
making it presentable. Proof of possession is an open issue
(Appendix B).
10.5. Algorithms and Signatures
Only ES256 is used and accepted. Verifiers pin it in configuration
and reject "none", every HMAC algorithm, including when a public key
is offered as the HMAC secret, and any spelling that differs in case
(Section 4.1.1 of [RFC7515], Section 2.1 of [RFC8725], Section 3.1 of
[RFC8725]). Every key in the set is published with "alg" "ES256".
Section 3.2 of [RFC8725] says that JWT libraries should implement
ECDSA using the deterministic approach of [RFC6979]. The deployed
issuer does not: it signs through OpenSSL 3.0, which derives the per-
signature value from fresh random bytes mixed with the private key
and the message digest. Its signatures are therefore not
deterministic and do not reproduce the test vectors of [RFC6979]; the
mixing is intended to keep a failure of the random number generator
from exposing the key. Verifiers are unaffected: deterministic and
randomized signatures verify identically.
10.6. Cross-JWT Confusion and Explicit Typing
Verdicts and event tokens share the key, the issuer identifier, and
the typ value "JWT", so Section 2.8 of [RFC8725] requires a
mitigation. This format uses the second strategy listed in
Section 3.12 of [RFC8725], different required claims: a verdict
requires exp, verdict, nonce, and request, which event tokens lack,
and step 6 of Section 6 rejects tokens that carry a type claim or an
"evt_" jti. That step is the mutually exclusive validation rule that
Section 3.12 of [RFC8725] requires for verdict verifiers; it rests on
claims rather than on the header. Event receivers that require type
Dias Expires 12 April 2027 [Page 29]
Internet-Draft Agent Payment Verdicts October 2026
and data, which verdicts lack, complete the mutual exclusion.
The format does not use explicit typing, which Section 3.11 of
[RFC8725] recommends for new kinds of JWT. With explicit typing, the
distinction would rest on the header rather than on each verifier
implementing step 6. Adopting it is an open issue (Appendix B).
Test and production verdicts are the same kind of JWT from different
environments, and differ only in the signing key. The only rule that
excludes test verdicts is step 3 of Section 6, which reads a prefix
of kid. JOSE leaves the structure of kid unspecified (Section 4.1.4
of [RFC7515]), so no generic JWT library applies this rule, and a
relying party that omits step 3 accepts test verdicts. A verifier
used only for testing does not reject production verdicts. A
separate issuer identifier and JWK Set for test verdicts is an open
issue (Appendix B).
10.7. Audience
Verdicts have no "aud" claim. Section 3.9 of [RFC8725] requires one
when an issuer issues JWTs intended for more than one relying party,
which is the case here; this format does not meet that requirement.
The same section requires a relying party to reject a JWT that has no
audience value; a relying party that follows Section 6 accepts
verdicts without one, and so departs from that requirement as well.
The member request.recipient is not an audience in the sense of
Section 4.1.3 of [RFC7519]: it names the payee, which the agent
chooses and the issuer does not check. When the relying party is the
payee, step 11 of Section 6 rejects a verdict presented for any other
payee. When the relying party is an intermediary acting for the
payee, such as a payment service provider or an escrow release
process, nothing in the token names it, and two intermediaries
settling for the same payee run separate nonce stores and could each
accept the same verdict within its lifetime. A relying party SHOULD
make sure that the identifier it compares against is specific to it
and not shared with other parties. Adding an audience claim is an
open issue (Appendix B).
10.8. Replay and Signature Malleability
A verdict is valid for 300 seconds, and a relying party accepts its
nonce only once (Section 6, step 12); relying parties that do not
share a nonce store can each accept it (Section 10.7).
ECDSA signatures are malleable: if (R, S) is a valid signature, so is
(R, n - S), where n is the order of the P-256 group. A verifier
whose base64url decoder ignores unused trailing bits also accepts
several spellings of the same signature segment. The same signed
Dias Expires 12 April 2027 [Page 30]
Internet-Draft Agent Payment Verdicts October 2026
claims can therefore appear in more than one token string, and
verifiers MUST NOT use the token string, or a hash of it, as the
replay key.
A verifier that recorded the nonce before comparing the verdict with
its payment record would let anyone with a copy of a verdict burn it
by presenting a mismatched payment; this is why step 12 comes last.
The deployed implementation supports idempotent retries: an operator
that retries an authorization request with the same Idempotency-Key,
the same request body, and the same API credential within 24 hours
receives the stored response, with the identical token and nonce, so
a retry does not produce a second approval. A retry after 24 hours
is a new evaluation with a new nonce, and each "allow" counts toward
the mandate's rolling 30-day limit.
10.9. Clock Skew
With a 300-second lifetime and a 60-second skew allowance, a verifier
accepts a verdict until its own clock reads iat + 360, and rejects an
iat more than 60 seconds ahead of its clock. Step 7 of Section 6
assumes a synchronized clock.
10.10. Key Compromise
There is no per-token revocation. The short lifetime of a verdict
bounds the use of a stolen verdict, and removal from the JWK Set is
the response to a stolen key. A verifier that refreshes on schedule
stops trusting a removed key within the max-age of the set; one that
pinned the key, or that keeps using a stale copy, keeps trusting it
for as long as it does so. A party holding a compromised signing key
can mint verdicts with current timestamps until the key is removed,
and after a compromise, verdicts signed with that key, including
those kept as records, can no longer be told apart from forgeries.
How the issuer protects its signing keys is outside the scope of this
document.
A verifier that cannot obtain a key for a verdict MUST reject the
verdict rather than accept it unverified.
10.11. Amounts and Parsing
JSON leaves numeric range and precision to implementations, and
Section 6 of [RFC8259] notes that good interoperability can be
achieved by implementations that expect no more precision or range
than binary64 provides; this is why amounts are compared by decimal
value (Section 4.6; Section 6, step 9). Step 1 of Section 6 bounds
the size of tokens; the deployed implementation limits recipient to
Dias Expires 12 April 2027 [Page 31]
Internet-Draft Agent Payment Verdicts October 2026
1,024 characters and category and currency to 128 characters each, so
the verdicts it has issued since 09:12 UTC on 9 October 2026 are
shorter than 11,000 characters. Its earlier verdicts could reach
about 94,000 characters. Event tokens (Section 10.14) are not
bounded in this way: an event token that carries a decision also
carries that decision's verdict and is longer than it.
10.12. Approval of Held Requests
An "allow" that carries supersedes records an operator's approval of
a request held for review. The format does not state whether the
mandate was evaluated again at approval time, and nothing in the
token prevents an issuer from issuing more than one "allow" that
supersedes the same "review" verdict; such verdicts have different
nonces, so the replay check in Section 6 does not detect them. A
relying party that must not settle against a revoked or expired
mandate needs to learn of revocations from the operator. A relying
party SHOULD record the supersedes value of each "allow" it accepts,
keep it at least for the review expiry plus the verdict lifetime and
skew allowance (24 hours and 360 seconds in the deployed
implementation), and reject a second "allow" that supersedes the same
verdict.
In the deployed implementation, approving a review repeats three
checks whose outcome can have changed since the review: that the
agent is active, that the mandate is neither revoked nor expired, and
that the amount, added to every "allow" issued under the mandate in
the 30 days before the approval, is within the rolling 30-day limit.
It does not repeat the frequency rule; the category and the per-
transaction limit cannot have changed, because mandates are not
edited. An approval that fails these checks is refused and issues no
verdict, and a review yields at most one "allow". Any holder of an
operator-wide credential can approve a review; the principal named on
the mandate is recorded but not authenticated.
10.13. Aggregate Limits
A verdict records the outcome of one evaluation. Whether an issuer
enforces aggregate limits, such as the rolling 30-day limit and the
frequency rule, across concurrent evaluations is a property of the
issuer and is not visible in the token. The deployed implementation
reads the limits and records each decision in a single database
transaction.
Dias Expires 12 April 2027 [Page 32]
Internet-Draft Agent Payment Verdicts October 2026
10.14. Event Tokens
The deployed implementation notifies operators of events, such as a
pending or resolved review, by webhook. Each event is a JWT with the
same header, signed with the same key as the verdicts of its
environment, under the same iss. Its claims are iss, jti (beginning
"evt_"), iat, type, and data; it has no exp and no audience, and the
issuer adds no claim that names the operator; only a "ping" event
names the endpoint, by the webhook identifier in its data. A
"decision.resolved" event carries, inside data, either the decision
that approves a review, whose verdict member is "allow", together
with that decision's verdict token, or the review object of a review
that was denied or expired. A failed delivery is retried with the
identical token; the last retry falls due 24 hours after the event is
created.
Because all operators' events in one environment are signed with the
same key under the same iss, a genuine event issued to one operator
also verifies at another operator's endpoint, at any later time: any
operator served by the issuer can forward a "decision.resolved" event
from its own endpoint, carrying an "allow" for a recipient it chose,
to another operator's endpoint. Signature checks and deduplication
on jti do not stop this. A receiver therefore confirms, through its
own authenticated access to the issuer or against identifiers from
its own responses, that an object named in an event is its own before
acting on it, and bounds how old an event it accepts. A verdict
verifier rejects event tokens by step 6 of Section 6 and MUST NOT
accept one as a verdict.
11. Privacy Considerations
Verdicts are signed, not encrypted. Anyone who sees a token can read
the agent identifier, which in the deployed implementation contains a
name the operator chose for the agent; the amount, currency,
recipient, and category; the mandate identifier; the time of the
decision; and policy_version, which in the deployed implementation
contains the date on which the operator's account was created.
A token does not contain the operator's name or identifier, the end
user's identity, payment credentials, the operator's metadata, or the
reason text. The agent and mandate identifiers are stable across
verdicts, and policy_version is the same for all of an operator's
agents, so relying parties that compare the tokens they received can
link payments made by the same agent, under the same mandate, or by
different agents of the same operator.
Dias Expires 12 April 2027 [Page 33]
Internet-Draft Agent Payment Verdicts October 2026
The token travels through the agent and any intermediary between the
agent and the relying party. Operators SHOULD NOT put personal data
in category, or in recipient beyond what identifies the payee; when
the payee is a natural person, the recipient identifier is personal
data and is visible to every party that handles the token. Relying
parties SHOULD protect retained verdicts as they protect the payment
records to which the verdicts relate.
12. IANA Considerations
This document has no IANA actions.
A later version might request registration of the private claim names
in Section 4.2, of a media type for explicit typing, and of a well-
known URI suffix for the JWK Set (Appendix B).
13. References
13.1. Normative References
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/info/rfc2119>.
[RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
10646", STD 63, RFC 3629, DOI 10.17487/RFC3629, November
2003, <https://www.rfc-editor.org/info/rfc3629>.
[RFC7515] Jones, M., Bradley, J., and N. Sakimura, "JSON Web
Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, May
2015, <https://www.rfc-editor.org/info/rfc7515>.
[RFC7517] Jones, M., "JSON Web Key (JWK)", RFC 7517,
DOI 10.17487/RFC7517, May 2015,
<https://www.rfc-editor.org/info/rfc7517>.
[RFC7518] Jones, M., "JSON Web Algorithms (JWA)", RFC 7518,
DOI 10.17487/RFC7518, May 2015,
<https://www.rfc-editor.org/info/rfc7518>.
[RFC7519] Jones, M., Bradley, J., and N. Sakimura, "JSON Web Token
(JWT)", RFC 7519, DOI 10.17487/RFC7519, May 2015,
<https://www.rfc-editor.org/info/rfc7519>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
Dias Expires 12 April 2027 [Page 34]
Internet-Draft Agent Payment Verdicts October 2026
[RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data
Interchange Format", STD 90, RFC 8259,
DOI 10.17487/RFC8259, December 2017,
<https://www.rfc-editor.org/info/rfc8259>.
[RFC8725] Sheffer, Y., Hardt, D., and M. Jones, "JSON Web Token Best
Current Practices", BCP 225, RFC 8725,
DOI 10.17487/RFC8725, February 2020,
<https://www.rfc-editor.org/info/rfc8725>.
[SEC1] Certicom Research, "SEC 1: Elliptic Curve Cryptography",
Standards for Efficient Cryptography, Version 2.0, May
2009, <https://www.secg.org/sec1-v2.pdf>.
13.2. Informative References
[ACP] OpenAI and Stripe, "Agentic Commerce Protocol: Delegate
Payment API, version 2026-04-17", April 2026,
<https://github.com/agentic-commerce-protocol/agentic-
commerce-
protocol/blob/17adf494cf8b4bcb41967a0386188b8103316d2f/
spec/2026-04-17/openapi/openapi.delegate_payment.yaml>.
[AP2] Google, "Agent Payments Protocol (AP2) Specification,
version 0.2", accessed 7 October 2026,
<https://ap2-protocol.org/ap2/specification/>.
[AUDIT] IETF, "audit: discussion of a potential Agent Use of
Delegation and Interaction Traceability (AUDIT) BoF",
<https://mailarchive.ietf.org/arch/browse/audit/>.
[AUTHZEN] Gazitt, O., Ed., Brossard, D., Ed., and A. Tulshibagwale,
Ed., "Authorization API 1.0", OpenID Foundation Final
Specification, 11 January 2026,
<https://openid.net/specs/authorization-api-1_0.html>.
[I-D.ietf-oauth-rfc8725bis]
Sheffer, Y., Hardt, D., and M. B. Jones, "JSON Web Token
Best Current Practices", Work in Progress, Internet-Draft,
draft-ietf-oauth-rfc8725bis-10, 21 August 2026,
<https://datatracker.ietf.org/doc/html/draft-ietf-oauth-
rfc8725bis-10>.
Dias Expires 12 April 2027 [Page 35]
Internet-Draft Agent Payment Verdicts October 2026
[I-D.ietf-oauth-transaction-tokens]
Tulshibagwale, A., Fletcher, G., and P. Kasselman,
"Transaction Tokens", Work in Progress, Internet-Draft,
draft-ietf-oauth-transaction-tokens-11, 30 July 2026,
<https://datatracker.ietf.org/doc/html/draft-ietf-oauth-
transaction-tokens-11>.
[I-D.ietf-webbotauth-httpsig-protocol]
Meunier, T. and S. Major, "HTTP Message Signatures for
automated traffic", Work in Progress, Internet-Draft,
draft-ietf-webbotauth-httpsig-protocol-00, 1 September
2026, <https://datatracker.ietf.org/doc/html/draft-ietf-
webbotauth-httpsig-protocol-00>.
[I-D.kuehlewind-audit-architecture]
Kühlewind, M. and H. Birkholz, "An Architecture for
Auditing Agent Delegation and Interactions", Work in
Progress, Internet-Draft, draft-kuehlewind-audit-
architecture-01, 7 September 2026,
<https://datatracker.ietf.org/doc/html/draft-kuehlewind-
audit-architecture-01>.
[I-D.laxsharma-pact]
Sharma, L., "PACT: Co-Signed Task Contracts, Delivery and
Verdict Records, and Outcome Records for Autonomous
Agents", Work in Progress, Internet-Draft, draft-
laxsharma-pact-02, 17 September 2026,
<https://datatracker.ietf.org/doc/html/draft-laxsharma-
pact-02>.
[OIDC-CORE]
Sakimura, N., Bradley, J., Jones, M., de Medeiros, B., and
C. Mortimore, "OpenID Connect Core 1.0 incorporating
errata set 2", OpenID Foundation, December 2023,
<https://openid.net/specs/openid-connect-core-1_0.html>.
[PSYCHIC] Madden, N., "CVE-2022-21449: Psychic Signatures in Java",
19 April 2022, <https://neilmadden.blog/2022/04/19/
psychic-signatures-in-java/>.
[RFC2606] Eastlake 3rd, D. and A. Panitz, "Reserved Top Level DNS
Names", BCP 32, RFC 2606, DOI 10.17487/RFC2606, June 1999,
<https://www.rfc-editor.org/info/rfc2606>.
[RFC6979] Pornin, T., "Deterministic Usage of the Digital Signature
Algorithm (DSA) and Elliptic Curve Digital Signature
Algorithm (ECDSA)", RFC 6979, DOI 10.17487/RFC6979, August
2013, <https://www.rfc-editor.org/info/rfc6979>.
Dias Expires 12 April 2027 [Page 36]
Internet-Draft Agent Payment Verdicts October 2026
[RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493,
DOI 10.17487/RFC7493, March 2015,
<https://www.rfc-editor.org/info/rfc7493>.
[RFC7800] Jones, M., Bradley, J., and H. Tschofenig, "Proof-of-
Possession Key Semantics for JSON Web Tokens (JWTs)",
RFC 7800, DOI 10.17487/RFC7800, April 2016,
<https://www.rfc-editor.org/info/rfc7800>.
[RFC7942] Sheffer, Y. and A. Farrel, "Improving Awareness of Running
Code: The Implementation Status Section", BCP 205,
RFC 7942, DOI 10.17487/RFC7942, July 2016,
<https://www.rfc-editor.org/info/rfc7942>.
[RFC8417] Hunt, P., Ed., Jones, M., Denniss, W., and M. Ansari,
"Security Event Token (SET)", RFC 8417,
DOI 10.17487/RFC8417, July 2018,
<https://www.rfc-editor.org/info/rfc8417>.
[RFC8615] Nottingham, M., "Well-Known Uniform Resource Identifiers
(URIs)", RFC 8615, DOI 10.17487/RFC8615, May 2019,
<https://www.rfc-editor.org/info/rfc8615>.
[RFC9111] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
Ed., "HTTP Caching", STD 98, RFC 9111,
DOI 10.17487/RFC9111, June 2022,
<https://www.rfc-editor.org/info/rfc9111>.
[RFC9396] Lodderstedt, T., Richer, J., and B. Campbell, "OAuth 2.0
Rich Authorization Requests", RFC 9396,
DOI 10.17487/RFC9396, May 2023,
<https://www.rfc-editor.org/info/rfc9396>.
[RFC9421] Backman, A., Ed., Richer, J., Ed., and M. Sporny, "HTTP
Message Signatures", RFC 9421, DOI 10.17487/RFC9421,
February 2024, <https://www.rfc-editor.org/info/rfc9421>.
[RFC9449] Fett, D., Campbell, B., Bradley, J., Lodderstedt, T.,
Jones, M., and D. Waite, "OAuth 2.0 Demonstrating Proof of
Possession (DPoP)", RFC 9449, DOI 10.17487/RFC9449,
September 2023, <https://www.rfc-editor.org/info/rfc9449>.
[RFC9943] Birkholz, H., Delignat-Lavaud, A., Fournet, C., Deshpande,
Y., and S. Lasker, "An Architecture for Trustworthy and
Transparent Digital Supply Chains", RFC 9943,
DOI 10.17487/RFC9943, June 2026,
<https://www.rfc-editor.org/info/rfc9943>.
Dias Expires 12 April 2027 [Page 37]
Internet-Draft Agent Payment Verdicts October 2026
[SAIFURO-LIMITS]
Saifuro LLC, "Limits of a signed verdict", accessed 9
October 2026,
<https://docs.saifuro.com/reference/verdict-limits>.
[SAIFURO-VECTORS]
Saifuro LLC, "Verdict test vectors", accessed 9 October
2026,
<https://docs.saifuro.com/reference/verdict-test-vectors>.
[SAIFURO-VERIFY]
Saifuro LLC, "Verifying a verdict", accessed 9 October
2026,
<https://docs.saifuro.com/reference/verify-a-verdict>.
[TAP] Visa, "Trusted Agent Protocol Specifications", accessed 7
October 2026, <https://developer.visa.com/capabilities/
trusted-agent-protocol/trusted-agent-protocol-
specifications>.
[X402] x402 Foundation, "x402 Protocol Specification, Protocol
Version 2", accessed 7 October 2026, <https://github.com/
x402-foundation/x402/blob/
e187dda1ef0c69c85416625342e5bb7c4b857dac/specs/x402-
specification-v2.md>.
Appendix A. Example
This appendix contains a verdict signed with an example key. The
private key is published here so that anyone can reproduce and extend
the example. It is not a key of any issuer, a token signed with it
proves nothing, and it MUST NOT be added to a verifier that protects
real payments. The issuer identifier is a reserved example domain
[RFC2606].
A.1. Example Key
{
"kty": "EC",
"crv": "P-256",
"x": "50tYIab3ytEcPnb_fuZCPc4cM87aNhpYhOYb9LmSseA",
"y": "iZho4NBfqRZnM7AgedlHkvEMRU030NPOYGoTv7s2Ls0",
"d": "34KI9Rx04_gEp_Ljm4-dGFq7O75vEFednnvRwcDdvUc",
"use": "sig",
"alg": "ES256",
"kid": "example-1"
}
Dias Expires 12 April 2027 [Page 38]
Internet-Draft Agent Payment Verdicts October 2026
A.2. Verdict
The token, with line breaks for display purposes only:
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImV4YW1wbGUtMSJ9.eyJ
pc3MiOiJodHRwczovL2lzc3Vlci5leGFtcGxlIiwianRpIjoiZGVjXzViMmU5YzQ
xIiwiaWF0IjoxNzkxMzc0NDAwLCJleHAiOjE3OTEzNzQ3MDAsInZlcmRpY3QiOiJ
hbGxvdyIsIm1hbmRhdGUiOiJtbmRfN2YzYTBkMTIiLCJwb2xpY3lfdmVyc2lvbiI
6InBvbF8yMDI2LTA5LTAyLjEiLCJub25jZSI6Im5fOWQwMmM2ZTQxYTdmM2I4NSI
sInJlcXVlc3QiOnsiYWdlbnQiOiJhZ3RfcHJvY3VyZW1lbnRfMDEiLCJhbW91bnQ
iOjM0MCwiY3VycmVuY3kiOiJVU0QiLCJyZWNpcGllbnQiOiJ2ZW5kb3I6YWNtZV9
zYWFzIiwiY2F0ZWdvcnkiOiJzYWFzIn19.5NLM85wfRPdBIBaW_fY_gmgfml5w-0
KZlVbDj0YUVxa03wiYmqXamfLynH_S_DUkk2j_HTAlorcS3tHdtNvwYA
Decoded JOSE Header:
{"alg":"ES256","typ":"JWT","kid":"example-1"}
Decoded claims, with whitespace added:
{
"iss": "https://issuer.example",
"jti": "dec_5b2e9c41",
"iat": 1791374400,
"exp": 1791374700,
"verdict": "allow",
"mandate": "mnd_7f3a0d12",
"policy_version": "pol_2026-09-02.1",
"nonce": "n_9d02c6e41a7f3b85",
"request": {
"agent": "agt_procurement_01",
"amount": 340,
"currency": "USD",
"recipient": "vendor:acme_saas",
"category": "saas"
}
}
A.3. Checking the Example
The verdict was issued at 2026-10-07T12:00:00Z and expired at
12:05:00Z. A verifier configured with the example key, the issuer
identifier https://issuer.example, and a payment record of 340.00 USD
to "vendor:acme_saas", with its clock pinned to 2026-10-07T12:02:00Z
(Unix time 1791374520), accepts it; for step 8, the mandate
identifier agreed with the operator is "mnd_7f3a0d12" and the agreed
unit is major units (US dollars). With the clock unpinned, it
rejects the token at step 7 of Section 6, as intended. A production
Dias Expires 12 April 2027 [Page 39]
Internet-Draft Agent Payment Verdicts October 2026
verifier never pins its clock.
The S component of the example signature is in the upper half of the
group order, so a verifier that enforces low-S signatures, contrary
to step 5, rejects this example.
Appendix B. Open Issues
1. Explicit typing. Use a dedicated typ value for verdicts, such
as "verdict+jwt", as Section 3.11 of [RFC8725] recommends, and
register the corresponding media type.
2. Audience. Add an "aud" claim naming the relying party or the
settlement endpoint, so that the format meets Section 3.9 of
[RFC8725], and define how the agent learns the value before
evaluation.
3. Proof of possession. Bind a verdict to a key held by the agent,
for example with a confirmation claim [RFC7800], so that a copy
cannot be used by another presenter.
4. Environment separation. Use a separate issuer identifier and
JWK Set for test verdicts, instead of a kid prefix.
5. Operator binding. Decide whether a verdict should identify the
operator, and how a relying party would use that identifier.
6. Recipient restrictions. Let a mandate restrict the recipients
for which an "allow" can be issued.
7. Policy identification. Remove policy_version, or make it, or a
new claim such as a digest of the mandate as evaluated, identify
the rules that were applied.
8. Claim names. Register the private claims in the "JSON Web Token
Claims" registry, or use collision-resistant names.
9. Replay identifier. jti is the unique token identifier of
Section 4.1.7 of [RFC7519], which notes that it can be used to
prevent replay; the separate nonce claim could be removed.
10. Amount representation. Choose between JSON numbers in a
constrained decimal form, decimal strings as [RFC7493]
recommends, and integers in minor units.
11. Asset codes. Define identifiers for assets that have no ISO
4217 code.
Dias Expires 12 April 2027 [Page 40]
Internet-Draft Agent Payment Verdicts October 2026
12. Enforcement mode. Decide whether a verdict should state whether
the issuing environment was enforcing.
13. Event tokens. Profile event tokens as Security Event Tokens
[RFC8417], with typ "secevent+jwt", an "events" claim, and an
"aud" naming the operator's endpoint. [RFC8417] recommends
against exp in such tokens, and the absence of exp is one of the
properties that makes an event token fail step 6 of Section 6.
14. Deterministic signatures. Sign with [RFC6979].
15. Key set integrity. Sign the JWK Set or publish a history of
retired keys, for example through a transparency service
[RFC9943], so that stored verdicts remain verifiable to third
parties.
16. Amount unit. State the unit of request.amount in the token, or
fix it, so that a relying party does not need to agree it out of
band (Section 4.6).
17. JWK Set location. Register a well-known URI suffix for the JWK
Set (Section 3 of [RFC8615]), or let relying parties take the
location from issuer metadata, instead of the unregistered
"jwks.json".
18. Revocation status. Decide whether relying parties need a way to
learn that a mandate was revoked after an "allow" was issued
(Section 7).
19. Transport. Define how a verdict accompanies a payment in HTTP
and in agent payment protocols such as [X402], [ACP], and [AP2].
20. Related formats. Determine whether a verdict can be expressed
as a signed form of an [AUTHZEN] evaluation response, or its
request claim as an "authorization_details" object ([RFC9396],
which registers that claim in Section 14.2) of a payment type.
Acknowledgments
The author thanks Nicholas Templeman, whose messages on the audit
mailing list [AUDIT] set out the distinction between an unsupported
association and a contradicted one, which Section 6.1 adopts, and the
binding problem that Section 10.7 describes.
Dias Expires 12 April 2027 [Page 41]
Internet-Draft Agent Payment Verdicts October 2026
The author also thanks Nancy Sahu and Gareth Wong, whose proposal on
the same list, that the charter state, for each verification
mechanism, what a successful check establishes, under what
assumptions, and whether an omitted record would be detectable, poses
the question that Section 7 answers for this format.
The text of this document was drafted with an AI system (Claude, by
Anthropic) from the implementation's source code and documentation
and from the cited specifications, and was checked against them in
further AI-assisted reviews. The author has reviewed the whole
document and is responsible for its content.
Author's Address
Ithelia Dias
Saifuro LLC
Sheridan, WY
United States of America
Email: ithelia.dias@saifuro.com
URI: https://saifuro.com
Dias Expires 12 April 2027 [Page 42]