x401: HTTP Proof Requirement Protocol§
Status:
Version: 0.2.0
Latest Draft: https://x401.proof.com/spec/latest/
Previous Version: https://x401.proof.com/spec/0.1.0/
- Editors:
- Daniel Buchner - Proof
- Bhushit Agarwal - Circle
- Reviewers:
- Jacky Lao - Lightspark
- Oliver Terbu - MATTR
- Tobias Looker - MATTR
- Tim Cappalli - Okta
- Nick Steele - OpenAI
- Darren Louie - Proof
- Adam Lemmon - Proof
- Tony England - Visa
- Steven Sacia - Visa
- Participate:
- GitHub repo
- File an issue
- Commit history
Abstract§
x401 defines an HTTP-based, route-scoped proof requirement protocol for requiring credential-based proof before access to a protected resource is granted.
x401 uses:
- the
PROOF-REQUESTHTTP header field to carry proof requirements - the
PROOF-RESPONSEHTTP header field to carry credential results, result references, or reusable proof-satisfaction tokens - the
PROOF-RESULTHTTP header field to carry verifier response information, including x401 proof errors - HTTP status codes to express the overall response semantics independently of x401 proof state
- the W3C Digital Credentials API (DC API) request shape as the carrier for the Verifier-composed proof request, so the request can be executed by native Credential Manager or handler methods or relayed to other Credential Managers and remote services
- OpenID for Verifiable Presentations (OpenID4VP) over the DC API for the composed proof request, using Digital Credentials Query Language (DCQL) to describe the credential requirements
- OAuth 2.0 for optional exchange of a verified result for an access token
- the DCQL
trusted_authoritiesthe Verifier already places inside the request as the authoritative issuer constraint, and — for its dereferenceable types (openid_federation,etsi_tl) — as the acquisition and discovery hint an Agent can follow to find where qualifying credentials are issued - OpenID for Verifiable Credential Issuance (OpenID4VCI) for resolving credential issuer metadata when the request’s
trusted_authoritiesresolve to OpenID4VCI issuers
The x401 payload carries a composed, valid Digital Credentials request authored by the Verifier. The Verifier authors the request and, in the RECOMMENDED signed mode, is its relying party. The request is carried in the digital member of the payload’s credential_requirements — itself a CredentialRequestOptions value usable directly as the argument to native Credential Manager or handler methods (navigator.credentials.get(payload.credential_requirements)) — so it can be executed directly, relayed to another remote Credential Manager or presentation service, or handed off for fully remote generation. This version of x401 specifies only the digital member; the container leaves room for other navigator.credentials.get() request types in future versions. The payload’s other top-level members — such as OAuth token exchange metadata and reusable requirement identifiers — carry x401-specific values that are not yet expressible inside a native Digital Credentials request.
x401 is intentionally separate from payment protocols. When payment is required, it MUST be handled with HTTP 402 Payment Required and an appropriate payment protocol. x401 MUST NOT redefine payment semantics.
This document defines the x401 payload, processing rules, interoperability requirements, and examples for proof requirements with optional payment handling.
x401 defines proof requirement semantics only. When payment is required, implementations still use 402 Payment Required and a separate payment protocol.
Introduction§
HTTP provides a standard challenge mechanism for authentication via 401 Unauthorized and WWW-Authenticate, but it does not define a general-purpose, machine-readable protocol for route-scoped proof requirements such as:
- proving personhood
- proving country of residency
- proving membership or accreditation
- proving entitlement issued by a specific issuer class
- proving organizational standing
- proving workload identity attributes
At the same time, OpenID4VP, DCQL, OAuth, OpenID4VCI, and the W3C Digital Credentials API define interoperable mechanisms for requesting presentations, evaluating credential requirements, invoking Credential Managers, issuing access tokens, and issuing credentials, but they are not themselves an HTTP route proof requirement protocol.
x401 fills that gap by defining an HTTP-native wrapper that:
- signals proof requirements at the protected route
- carries x401 proof objects as base64url values in dedicated proof header fields
- carries a composed, valid Digital Credentials request, authored and signed by the Verifier, in the
digitalmember of the payload’scredential_requirements - recommends a signed OpenID4VP request for the DC API — making the Verifier the relying party and binding the request to the Verifier independently of which surface invokes it — while allowing an unsigned request when the request must be fulfilled at an invocation origin the Verifier cannot declare in advance
- lets the Agent execute the request through native credential methods, relay it to another Credential Manager or remote service, or hand it off for fully remote generation and then acquire the result
- includes OAuth token exchange metadata for Agents that want a reusable access token after proving
- lets Agents discover where to acquire qualifying credentials by following the dereferenceable issuer pointers (
openid_federation,etsi_tl) the Verifier already places in the request’s DCQLtrusted_authorities, without adding an x401-specific issuer list - composes with, but does not subsume, payment protocols
In the typical flow, an Agent receives an x401 proof requirement from a Verifier, obtains a result for the Verifier-composed Proof Request — by invoking the request through native credential methods, relaying it to a Credential Manager, or acquiring a remotely generated result — and retries the original protected route with the credential result inline or by reference. The dereferenceable issuer pointers in the request’s DCQL trusted_authorities can help the Agent discover where to acquire qualifying credentials, but the Verifier remains authoritative for issuer trust enforcement.
Design Goals§
The goals of x401 are:
- Define a route-scoped proof requirement for HTTP resources.
- Carry a composed, valid Digital Credentials request, authored by the Verifier, that can be executed by native Credential Manager or handler methods.
- Let the Verifier be the relying party for that request through a signed request, while allowing an unsigned request bound to the invoking origin and nonce when the invocation origin cannot be known in advance.
- Preserve the Agent’s ability to execute the request natively, relay it to another Credential Manager or remote service, or hand it off for fully remote generation and then acquire and present the result.
- Reserve the top level of the x401 payload, alongside the native request, for x401-specific values that are not yet expressible inside a native Digital Credentials request.
- Reuse OpenID4VP over the DC API, DCQL, OAuth, and OpenID4VCI rather than redefining them.
- Remain separate from payment semantics.
- Allow issuer discovery and credential acquisition to follow the issuer constraints the Verifier already expresses in the request’s DCQL
trusted_authorities, without adding an x401-specific issuer list. - Support stateless verifier deployments without dictating how the Verifier achieves it.
- Allow optional caller authentication, request signing, and delegation artifacts to compose with x401 without making any one agent identity or binding system mandatory.
Non-Goals§
x401 does not:
- define a new credential format
- replace OpenID4VP or the W3C Digital Credentials API
- replace OpenID4VCI
- redefine how a Digital Credentials request is composed, signed, or invoked
- mandate a single transport for delivering the composed request to a Credential Manager
- define a payment protocol
- require all Verifiers to maintain server-side session state
- require VP response encryption or any single agent authentication or binding mechanism
Terminology§
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 RFC 2119 and RFC 8174.
- Verifier:
- The party protecting a resource or operation and requiring proof.
- Agent:
- The HTTP caller that requests a protected route, receives an x401 proof requirement, obtains a result for the Verifier-composed Proof Request — by invoking it through native credential methods, relaying it to a Credential Manager or remote service, or acquiring a remotely generated result — and retries the protected route. The Agent does not compose the proof request and is not, by default, the relying party for it. A deployment MAY additionally bind the Agent to the request using an optional Agent Identifier.
- Agent Identifier:
- An optional identifier for the Agent that a Verifier MAY bind to the HTTP caller. This specification does not register a single Agent Identifier scheme; a Verifier that chooses to bind the Agent MUST define which schemes it accepts, including any accepted DID, HTTPS origin, domain-bound client identifier, or certificate-bound identifier schemes. See Agent Binding Options.
- Holder:
- The subject or entity that possesses credentials and can authorize a Credential Manager to present proof.
- Credential Manager:
- Software or a service that holds, or has access to, a Holder's credentials and can receive an OpenID4VP request — through the Digital Credentials API or another transport — and return a credential result authorized by the Holder. The term is used broadly here and is not limited to a platform Credential Manager API entity: a Credential Manager can be a browser or operating-system credential manager, a remote or cloud wallet, or any other app or service that holds and offers credentials on a Holder’s behalf.
- Digital Credentials Request:
- The composed, valid Digital Credentials request the Verifier places in the
digitalmember of the payload’scredential_requirements. It is aDigitalCredentialRequestOptionsvalue ({ "requests": [ ... ] }) — the value thedigitalmember ofnavigator.credentials.get()takes. Each entry is an OpenID4VP request for the DC API, signed (RECOMMENDED) or unsigned. - Proof Request:
- The Verifier-authored OpenID4VP request carried inside the Digital Credentials Request. It identifies the credential requirement as
dcql_queryand carries the OpenID4VPnonce. When signed (RECOMMENDED), the Verifier is its relying party and it binds to the Verifier through the request signature,client_id, andexpected_origins; when unsigned, it binds to the invoking origin and thenonce. See Verifier Binding. - Credential Result:
- The result a Credential Manager returns for a Digital Credentials Request, shaped as
{ "protocol": ..., "data": ... }as returned by the Digital Credentials API. - Result Artifact:
- A retry artifact carrying a Credential Result — either inline or as a Credential Reference — together with the x401 metadata the Verifier needs to correlate proof fulfillment, encoded for use in a
PROOF-RESPONSErequest header or OAuth token exchange. - Credential Reference:
- A by-reference form of the Result Artifact that carries a URL the Verifier dereferences to fetch a Credential Result instead of carrying it inline, for results that exceed header size limits or are generated at a remote location.
- Verification Token:
- A verifier-issued, short-lived access token returned after successful proof verification and used by the Agent on later protected-route requests so that the Result Artifact does not need to be repeated. A Verification Token can be used as the route’s normal
Authorizationtoken when the deployment supports that model, or carried as an x401 Token Object inPROOF-RESPONSEwhen the route already usesAuthorizationfor existing application authentication. - x401 Token Object:
- A JSON object carried as a base64url value in
PROOF-RESPONSEto pass a Verification Token without replacing the route’s existingAuthorizationcredentials. - x401 Payload:
- The JSON object defined by this specification, UTF-8 encoded, and carried as a base64url value in the
PROOF-REQUESTheader field. - x401 Error Object:
- A JSON object carried as a base64url value in the
PROOF-RESULTheader field to describe why a presented proof failed or could not be processed.
Protocol Overview§
The x401 protocol is made up of four legs: a Verifier exposes identity proof requirements that gate access to a resource by composing a Digital Credentials request, the Agent obtains a result for that request, the Agent presents the result back to the Verifier, and the Agent may exchange that proof for a reusable Verification Token. The tabs below summarize each leg and link to the detailed section that defines the processing rules that pertain to them.
The Verifier declares a route-scoped proof requirement by returning a PROOF-REQUEST header. The header carries the base64url-encoded x401 payload that defines the route’s proof requirement; see x401 Gated Resource Configuration for details. The response status code describes the overall HTTP response and is not the x401 protocol carrier.
HTTP/1.1 401 Unauthorized
PROOF-REQUEST: <base64url-x401-payload>
Cache-Control: no-store
The Agent decodes the x401 payload and obtains a result for the Verifier-composed request carried in the digital member of the payload’s credential_requirements. The Agent does not compose or alter the request; it invokes it through native credential methods, relays it to a Credential Manager or remote service, or acquires a remotely generated result; see Obtaining a Result for details. The credential_requirements object is directly usable as the argument to navigator.credentials.get().
const result = await navigator.credentials.get(payload.credential_requirements);
// result => { protocol: "openid4vp-v1-signed", data: { /* credential result */ } }
After obtaining a credential result, the Agent packages it as a Result Artifact for protected-route retry. The Result Artifact carries the Credential Manager-returned credential result inline, or a reference the Verifier dereferences; see Credential Result Delivery for details.
{
"request_id": "proof-template-financial-customer-v1",
"credential_result": {
"protocol": "openid4vp-v1-signed",
"data": "<credential-manager-returned-result>"
}
}
The Agent can submit the same Result Artifact to the Verifier’s OAuth token endpoint to obtain a reusable Verification Token. The token exchange uses fixed x401 token-exchange parameters and then the Agent retries protected routes with either an upgraded Authorization token or a PROOF-RESPONSE proof-satisfaction token object; see Access Token Acquisition for details.
POST /oauth/token HTTP/1.1
Host: bank.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
subject_token_type=urn:x401:params:oauth:token-type:result_artifact&
subject_token=<base64url-result-artifact-json>
Primary Flow§
In the primary x401 flow, the Agent is the HTTP caller and is assumed to have access to a Credential Manager, keys, credentials, or local capabilities needed to fulfill the proof requirement.
- The Agent requests a protected route.
- The Verifier determines that proof is required.
- The Verifier returns an HTTP response with:
PROOF-REQUEST: <base64url-x401-payload>
- The Agent decodes the x401 payload and reads the composed Digital Credentials Request in the
digitalmember ofcredential_requirementsand the OAuth token endpoint. - The Agent obtains a Credential Result for
credential_requirementswithout altering it, by one of:- invoking the request through a native credential method such as
navigator.credentials.get(payload.credential_requirements), - relaying the request to a Credential Manager or remote presentation service that can execute it, or
- handing the request to a remote fulfillment surface and acquiring the generated result.
- invoking the request through a native credential method such as
- The chosen Credential Manager, handler, or service returns a credential result bound per the request’s mode — to the Verifier as relying party for a signed request, or to the invoking origin and nonce for an unsigned request.
- The Agent retries the same protected route that produced the x401 proof requirement with one of:
- a Result Artifact in a
PROOF-RESPONSErequest header, carrying the credential result inline or as a Credential Reference, - a Verification Token carried as an x401 Token Object in a
PROOF-RESPONSErequest header, or - a Verification Token in the route’s normal
Authorizationrequest header when the deployment uses x401 proof satisfaction to upgrade or replace the route’s ordinary authorization credential.
- a Result Artifact in a
- The Verifier validates the Result Artifact or Verification Token, dereferencing a Credential Reference when one is supplied.
- If proof is satisfied and payment is not required or is already satisfied, the Verifier returns the protected resource.
- If proof is satisfied but payment remains unsatisfied, the Verifier returns
402 Payment Requiredwith payment protocol details. After satisfying payment, the Agent retries the same route with proof or token material and the payment artifact required by the selected payment protocol.
Optional OAuth Token Exchange§
An Agent MAY exchange a Result Artifact for a Verification Token before retrying the protected route. The token endpoint is supplied by the Verifier in the x401 payload.
The technical sections that follow are organized by the four main legs of the protocol: x401 gated resource configuration, obtaining a result, credential result delivery, and access token acquisition.
x401 Gated Resource Configuration§
The initial leg of the protocol defines how a protected HTTP resource declares what proof is required. The Verifier responds to the original protected-route request with a PROOF-REQUEST header whose value contains the base64url-encoded x401 payload.
The requirement is route-scoped. If a representation includes gated and ungated material, this version of x401 does not define per-fragment requirements or separate requirement mapping inside the page. Fulfillment of the complete credential demand in the PROOF-REQUEST payload satisfies the route’s x401 gate.
POST /accounts/applications HTTP/1.1
Host: bank.example.com
Accept: application/json
HTTP/1.1 401 Unauthorized
PROOF-REQUEST: <base64url-x401-payload>
Cache-Control: no-store
HTTP Semantics§
| HTTP status or condition | Meaning in a x401-capable deployment | Agent expectation |
|---|---|---|
Any response with PROOF-REQUEST |
Proof is required, advertised, or not yet satisfied for the route | Decode the x401 payload from the PROOF-REQUEST field value |
Any response with PROOF-RESULT containing an x401 Error Object |
A presented x401 proof failed or could not be processed | Decode the x401 error object and treat the x401 proof branch as failed |
2xx with PROOF-REQUEST |
The HTTP response is otherwise successful, but the route includes x401-gated material or actions | Process the response normally and fulfill the route-scoped proof requirement when access to the gated material is needed |
4xx with PROOF-REQUEST |
The route cannot be completed without proof | Fulfill the route-scoped proof requirement before retrying |
402 Payment Required |
Payment remains unsatisfied | Switch to the payment protocol |
The x401 proof header fields are the x401 protocol carriers. HTTP status codes describe the overall response and do not, by themselves, define x401 proof state.
Status Code Independence§
A server that requires proof for access to a protected resource, route, representation, or operation whose response would be changed as a whole by fulfilling a single set of proof requirements MUST return PROOF-REQUEST: <base64url-x401-payload>. The PROOF-REQUEST header is the authoritative carrier for an x401 proof requirement that gates the entire response.
The response MAY use any HTTP status code appropriate for the whole response. A Verifier can use a successful status code when the response body is still useful without proof, or a client or server error status when the requested operation cannot proceed until proof is satisfied.
Example:
HTTP/1.1 200 OK
PROOF-REQUEST: <base64url-x401-payload>
Cache-Control: no-store
An x401 proof requirement carried in PROOF-REQUEST SHOULD NOT require the Agent to parse a response body in order to understand the proof requirement.
This specification does not carry x401 proof requirements in WWW-Authenticate. A protected route may already use that header for an existing HTTP authentication scheme, and combining multiple schemes — whether through comma-separated field values or duplicate header lines — is not consistently or well handled by common HTTP servers, proxies, client libraries, and middleware. Carrying x401 proof requirements in a dedicated PROOF-REQUEST field avoids those interoperability gaps and lets x401 compose with any existing WWW-Authenticate-based authentication without contention.
402 for Payment§
A server that requires payment MUST use 402 Payment Required and MUST NOT overload x401 to represent payment as proof.
Payment metadata MAY be declared in a x401 payload for informational purposes when payment may also be required, but payment satisfaction itself remains governed by the payment protocol used with 402.
x401 Error for Failed Policy Satisfaction§
If an Agent presents a proof artifact that is structurally valid but does not satisfy the Verifier’s policy, the Verifier SHOULD return a PROOF-RESULT: <base64url-x401-error-object> header. The x401 Error Object is the protocol-specific indication of failure for x401 exchanges. The HTTP response status remains independent and describes the overall response.
Examples include:
- credential from an untrusted issuer
- credential does not satisfy predicates
- expired or revoked credential
- result is not bound as the request mode requires (wrong relying party, audience, or invoking origin)
- nonce is not one the Verifier issued, or is expired or replayed
- insufficient assurance level
Proof Header Fields§
The proof header fields carry x401 protocol objects. This specification uses “x401 proof requirement” for the overall route-gating declaration carried in PROOF-REQUEST.
Header Syntax§
Each x401 proof header field carries exactly one base64url-encoded UTF-8 JSON object. The header field name identifies the protocol leg, so x401 does not use an action keyword inside a shared carrier header.
Header field names are shown in uppercase for consistency with the examples. As HTTP field names, they are case-insensitive.
PROOF-REQUEST: <base64url-json>
PROOF-RESPONSE: <base64url-json>
PROOF-RESULT: <base64url-json>
The defined x401 proof header fields are:
| Header field | Direction | Decoded payload |
|---|---|---|
PROOF-REQUEST |
Verifier to Agent | x401 Payload |
PROOF-RESPONSE |
Agent to Verifier | Result Artifact or x401 Token Object |
PROOF-RESULT |
Verifier to Agent | x401 response information, including x401 Error Object |
The encoded value MUST be base64url-encoded UTF-8 JSON using the URL and filename safe alphabet defined by RFC 4648 Section 5 without padding. The decoded value MUST be a single JSON object.
When PROOF-RESPONSE carries a Result Artifact, the request is presenting a credential result directly. When it carries an x401 Token Object, the request is presenting a reusable proof-satisfaction token issued after prior verification.
PROOF-RESULT is the Verifier-to-Agent response information channel for x401-specific results and diagnostics. This specification defines the x401 Error Object for failed proof or token processing. Deployments MAY define additional response objects for accepted proof state, token metadata, or other x401-specific return information.
A sender MUST NOT generate more than one field line with the same x401 proof header name in the same HTTP message. A sender MUST NOT combine multiple x401 protocol objects in a single proof header using commas or other list syntax. If a recipient receives multiple field lines for the same proof header, or receives a proof header value containing a comma-separated list of messages, it MUST treat that proof header as invalid.
A response SHOULD NOT contain both PROOF-REQUEST and PROOF-RESULT unless the response both reports verifier response information and intentionally advertises a fresh proof requirement for a subsequent attempt. A request SHOULD NOT contain more than one PROOF-RESPONSE value.
When browser-based JavaScript needs to read PROOF-REQUEST or PROOF-RESULT from a cross-origin response, the response needs CORS exposure for those fields. When browser-based JavaScript sends PROOF-RESPONSE cross-origin, the server needs to allow the PROOF-RESPONSE request header through CORS preflight.
x401 Payload§
A x401 payload is a single JSON object encoded in the PROOF-REQUEST header field.
The payload SHOULD remain compact. Sensitive route state SHOULD be omitted, stored server-side, or carried only inside verifier-protected nonce state.
Top-Level Members§
{
"scheme": "x401",
"version": "0.2.0",
"credential_requirements": {},
"oauth": {},
"request_id": "...",
"satisfied_requirements": [],
"payment": {}
}
The top level of the x401 payload is itself the envelope: the native, standard credential request is the credential_requirements member — a CredentialRequestOptions value whose digital member carries the Digital Credentials request — and the remaining x401-specific members, which are not yet expressible inside a native credential request, sit alongside it so the Agent and Verifier can polyfill their use.
Member Definitions§
| Name | Definition |
|---|---|
scheme |
REQUIRED. Value MUST be the string "x401". |
version |
REQUIRED. The x401 payload version. |
credential_requirements |
REQUIRED. The Verifier-composed credential request, a CredentialRequestOptions value. This version of x401 specifies its digital member, the composed Digital Credentials Request. See Credential Requirements. |
oauth |
REQUIRED. OAuth token exchange metadata for obtaining a reusable Verification Token. See OAuth Members. |
request_id |
OPTIONAL. A stable verifier-defined identifier for the proof template, as an Agent-visible hint. See Reusable Requirement Hints. |
satisfied_requirements |
OPTIONAL. Stable verifier-defined identifiers for the reusable proof requirements this proof would satisfy, as an Agent-visible reuse hint. See Reusable Requirement Hints. |
return_uri |
OPTIONAL. An https URL added by a relaying intermediary (never by the Verifier) telling a remote handler where to deliver the credential result. See Relayed Delivery to a Remote Handler. |
payment |
OPTIONAL. Describes that payment is additionally required, without replacing 402 semantics. |
The credential requirement, the OpenID4VP nonce, and the request expiry all live inside the request in credential_requirements.digital; x401 does not duplicate them at the payload level. Of the payload-level members, only credential_requirements and oauth are load-bearing; request_id and satisfied_requirements are optional hints and optimizations. Issuer constraints and any acquisition pointers live inside the request, in its DCQL trusted_authorities, not at the payload level.
Credential Requirements§
The credential_requirements member is the Verifier-composed credential request: a CredentialRequestOptions value, usable directly as the argument to navigator.credentials.get(). This version of x401 specifies a single member, digital, which carries the Digital Credentials Request. The object is structured so that other navigator.credentials.get() request types MAY be specified in future versions of x401; this version defines processing only for digital (see Additional Credential Request Types). The Verifier authors the request; in the RECOMMENDED signed mode it also signs the request and is its relying party (see Verifier Binding).
General Structure§
{
"credential_requirements": {
"digital": {
"requests": [
{
"protocol": "openid4vp-v1-signed",
"data": {
"request": "eyJhbGciOiJFUzI1NiIsInR5cCI6Im9hdXRoLWF1dGh6LXJlcStqd3QifQ..."
}
}
]
}
},
"oauth": {
"token_endpoint": "https://bank.example.com/oauth/token"
},
"request_id": "proof-template-financial-customer-v1",
"satisfied_requirements": [
"urn:example:x401:satisfaction:financial-customer:v1"
]
}
The digital member is a DigitalCredentialRequestOptions value — the value the digital member of navigator.credentials.get() takes. Each entry in its requests array is an OpenID4VP request for the DC API. The example below uses the RECOMMENDED signed form, whose JAR carries the OpenID4VP request the Verifier authored — RP identity and freshness live there, not in any x401-specific field. Decoded for readability, a typical JAR payload is:
{
"response_type": "vp_token",
"response_mode": "dc_api",
"client_id": "x509_san_dns:bank.example.com",
"expected_origins": ["https://bank.example.com"],
"nonce": "uX7Vq3mZJH6MeN0qz2L7SQ",
"dcql_query": {
"credentials": [
{
"id": "financial_customer",
"format": "jwt_vc_json",
"meta": {
"type_values": ["FinancialCustomerCredential"]
},
"claims": [
{
"path": ["credentialSubject", "assurance_level"],
"values": ["VC-AL2", "VC-AL3"]
}
]
}
]
},
"client_metadata": {},
"exp": 1746557100
}
The credential_requirements.digital value is a DigitalCredentialRequestOptions. The credential requirement (dcql_query), the OpenID4VP nonce, and the request expiry (exp) all live inside the request; x401 does not restate or duplicate them at the payload level, and adds no expiry member of its own.
Request Members§
This version of x401 specifies the digital member of credential_requirements. It is a DigitalCredentialRequestOptions with the following members:
| Name | Definition |
|---|---|
digital |
REQUIRED in this version. The composed Digital Credentials Request, a DigitalCredentialRequestOptions carrying the requests array below. |
digital.requests |
REQUIRED. A non-empty array of Digital Credentials request entries. A Verifier MAY include more than one entry (for example, offering different credential formats) for a Credential Manager, handler, or remote service to select among. Every entry MUST be a valid OpenID4VP request for the DC API. |
digital.requests[].protocol |
REQUIRED. The DC API protocol identifier. For this version of x401 the value MUST be "openid4vp-v1-signed" (RECOMMENDED) or "openid4vp-v1-unsigned". See Verifier Binding for the trade-off. |
digital.requests[].data |
REQUIRED. The protocol-specific request data. For openid4vp-v1-signed, an object carrying the signed OpenID4VP request (a JWT-Secured Authorization Request); for openid4vp-v1-unsigned, the OpenID4VP request parameters directly. |
OAuth Members§
| Name | Definition |
|---|---|
token_endpoint |
REQUIRED. OAuth 2.0 token endpoint where the Agent can exchange a Result Artifact for a Verification Token. |
audience |
OPTIONAL. OAuth token exchange audience value the Agent should request. |
resource |
OPTIONAL. OAuth token exchange resource value the Agent should request. |
The x401 OAuth profile fixes grant_type, subject_token_type, and Bearer token usage. These values MUST NOT be repeated in the x401 payload.
Reusable Requirement Hints§
request_id and satisfied_requirements are OPTIONAL, Agent-visible hints that support cross-route token reuse. They are not inputs to proof validation or token issuance — the Verifier determines what a proof satisfies from its own policy. See Reuse Across Routes.
| Name | Definition |
|---|---|
request_id |
OPTIONAL. A stable verifier-defined identifier for the proof template. It lets an Agent recognize that two routes ask for the same proof. |
satisfied_requirements |
OPTIONAL. Stable verifier-defined identifiers for the reusable proof requirements this proof would satisfy, letting an Agent decide whether an existing Verification Token may apply to a later route. |
Payload Example§
{
"scheme": "x401",
"version": "0.2.0",
"credential_requirements": {
"digital": {
"requests": [
{
"protocol": "openid4vp-v1-signed",
"data": {
"request": "eyJhbGciOiJFUzI1NiIsInR5cCI6Im9hdXRoLWF1dGh6LXJlcStqd3QifQ..."
}
}
]
}
},
"oauth": {
"token_endpoint": "https://bank.example.com/oauth/token"
},
"request_id": "proof-template-financial-customer-v1",
"satisfied_requirements": [
"urn:example:x401:satisfaction:financial-customer:v1"
]
}
Verifier State and Stateless Operation§
Because the Verifier authors, signs, and validates its own request, freshness, replay protection, and correlation between a returned result and the issued request are internal Verifier concerns. x401 places no requirements on how the Verifier composes the OpenID4VP nonce, recognizes a returned result as one it requested, or recovers route and policy context. The freshness and replay properties of the nonce are governed by OpenID4VP.
A Verifier MAY operate statelessly. Because the OpenID4VP nonce is the value echoed back and cryptographically bound in the returned result, a Verifier MAY make the nonce a verifier-protected, self-contained value that encodes the route, method, policy, and expiry it needs to validate the retry without server-side storage. How that value is constructed, whether one-time-use replay protection is enforced with a small shared cache, and how any response-encryption decryption key is managed are deployment decisions left to the implementer.
Credential Acquisition Guidance§
x401 does not define a separate issuer list. The issuers a Verifier accepts are already expressed inside the request, as the DCQL trusted_authorities of each credential query in credential_requirements.digital. That same constraint is the acquisition hint: when an Agent does not already hold a qualifying credential, it discovers where to obtain one by resolving the trusted_authorities entries — for the entry types that are dereferenceable. This reuses the Verifier’s authoritative issuer constraint as the discovery surface, so the discovery path and the enforcement path cannot diverge.
Each trusted_authorities entry is a { "type": ..., "values": [...] } object. How an Agent can resolve it for acquisition depends on its type:
openid_federation— each value is an HTTPS Entity Identifier. The Agent resolves it as an OpenID Federation entity by fetching<value>/.well-known/openid-federation. If the entity’smetadatacarries anopenid_credential_issuerentry, that entity is itself a credential issuer and its OpenID4VCI metadata is published in-band. If the entity is a federation anchor or intermediate, the Agent walks the subordinate listing and trust chain to find subordinate entities that publishopenid_credential_issuermetadata, validating the chain back to the anchor named invalues. This is the fully resolvable path from issuer constraint to issuance endpoint.etsi_tl— each value identifies an ETSI TS 119 612 Trusted List. The Agent MAY fetch and parse the list, enumerate the trust service providers and services whose service type matches the required credential, and follow a service’s supply point to locate an issuer. Whether a listed service exposes an OpenID4VCI issuance endpoint is an ecosystem convention, not a guarantee of this format, so acquisition frometsi_tlis best-effort.aki— each value is an Authority Key Identifier: an opaque issuer key identifier with no resolution mechanism. It constrains acceptable issuers but provides no acquisition pointer. For a request whose only issuer constraint isaki, x401 defines no native acquisition path; the Agent must already hold a qualifying credential or resolve the issuer out of band.
When an entry resolves to one or more OpenID4VCI Credential Issuer Identifiers, the Agent resolves issuer metadata using the OpenID4VCI issuer metadata discovery rules and drives issuance against the discovered issuer. See OpenID4VCI Section 12.2.2: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-final.html.
Acquisition guidance is advisory and never decides validity. The Verifier enforces issuer trust during proof validation against the trusted_authorities in the request it issued and its own policy, independently of how — or whether — the Agent used these pointers to acquire a credential.
Payment Object§
When payment may also be required, a x401 payload MAY declare the existence of that additional payment requirement. This is to help avoid situations where the user is not willing or able to pay, but does not find out about the payment requirement until after they have already disclosed their credential(s), resulting in needless sharing of identity information without achieving the outcome the user intended.
The payment object is informational and orchestration-oriented only. It does not replace 402 Payment Required.
The presence of a payment member does not create a distinct x401 proof flow. The Agent completes the same proof steps described in the base flow. If the Verifier accepts the proof but payment remains unsatisfied, the Verifier uses 402 Payment Required and the selected payment protocol to complete payment before granting access.
Example§
{
"required": true,
"scheme_hint": "x402",
"notes": "Payment is required after proof is satisfied."
}
There is good reason to include hints about payment requirements, but because it could result in replication of nearly everything 402-related protocols define, the payment hint has been constrained to a boolean to allow the community to drive how much payment requirement information to include.
Members§
| Name | Definition |
|---|---|
required |
OPTIONAL. Boolean indicating whether payment is additionally required. |
scheme_hint |
OPTIONAL. A hint naming the expected payment protocol. |
notes |
OPTIONAL. Human-readable notes. |
If proof is accepted but payment is still unsatisfied, the Verifier responds with 402 Payment Required using the payment protocol indicated by the verifier.
Obtaining a Result§
This leg defines how the Agent obtains a result for the Verifier-composed request carried in the digital member of the payload’s credential_requirements. The Verifier authored the request; the Agent does not compose it and is not, by default, its audience. The Agent’s task is to get the request executed and to acquire the resulting Credential Result.
An Agent obtains a result by one of the following, all of which carry the same Verifier-composed request unchanged:
-
Native invocation. In an environment with the Digital Credentials API, the Agent invokes the request directly:
const result = await navigator.credentials.get(payload.credential_requirements); // result => { protocol, data }A native platform credential handler MAY be used in place of the Web API where an equivalent mechanism exists.
-
Relay to a remote handler. The Agent forwards the request to a remote handler — a remote Credential Manager or service that produces the result without invoking the Digital Credentials API itself — and the handler returns the result to a URL the Agent supplies. See Relayed Delivery to a Remote Handler.
-
Remote, out-of-band generation. When the Agent’s environment cannot invoke the request directly — for example, a consumer AI client without a credential handler — the request can be handed to a remote fulfillment surface (such as a verifier-hosted page) that invokes the Digital Credentials API in its own context and generates the result. The Agent then acquires the result and replays the protected route. See Remote and Out-of-Band Fulfillment.
In every case the result is bound per the request’s mode and transport — to the Verifier as relying party for a signed request, or to the invoking origin and the Verifier’s nonce for an unsigned request (see Verifier Binding) — not to the Agent.
Composed Request Invariants§
The composed request is authored and signed by the Verifier. The Agent treats it as opaque:
- The Agent MUST NOT modify any entry in
credential_requirements. The request signature binds its contents, so any modification invalidates it. - When
credential_requirements.digital.requestscontains more than one entry, the Agent MAY narrow them to entries whoseprotocolor credential formats the chosen Credential Manager or handler implements, and MAY pass the entries through unchanged; which entry is ultimately used is determined by the Credential Manager or handler matching the request against available credentials, not chosen up front by the Agent. - The Agent MUST arrange to acquire the Credential Result returned for the request, whether it invokes the request itself, relays it, or acquires a remotely generated result.
A Verifier composing credential_requirements.digital:
- MUST make each entry a valid OpenID4VP request for the Digital Credentials API, using
protocol: "openid4vp-v1-signed"(RECOMMENDED) orprotocol: "openid4vp-v1-unsigned". - SHOULD use a signed request and set its
client_idandexpected_origins. A signed request lets the Credential Manager authenticate the Verifier, binds the result’s audience to the Verifier’s identity, and lets the Verifier pre-authorize the invocation origin; see Verifier Binding. - MAY use an unsigned request when it needs the request fulfilled at an invocation origin it cannot declare in advance — for example, when the Agent invokes the request directly in its own context or relays it to an arbitrary surface. An unsigned request binds the result to the invoking origin and the Verifier’s
noncerather than to a signed Verifier identity, with the trade-offs described in Verifier Binding. - MAY request response encryption (for example, a
dc_api.jwtresponse mode with encryption keys inclient_metadata) or omit it. Response encryption is OPTIONAL in x401.
x401 does not restate the field-level rules for composing, signing, or invoking a Digital Credentials request; those are defined by the W3C Digital Credentials API and by OpenID4VP for the DC API. x401 requires only that credential_requirements.digital is a valid DigitalCredentialRequestOptions whose entries are valid OpenID4VP requests for the DC API; the Verifier validates whichever binding its chosen request mode provides.
The request’s DCQL trusted_authorities can help Agents discover where to acquire acceptable credentials, but they never delegate verification behavior to the Agent, and discovery does not decide validity — the request’s dcql_query and trusted_authorities do, as enforced by the Verifier. When a trusted_authorities entry resolves to OpenID4VCI issuers, Agents resolve those issuers using OpenID4VCI issuer metadata discovery; see Credential Acquisition Guidance.
Relayed Delivery to a Remote Handler§
Native invocation returns the result through the navigator.credentials.get() call, so it needs no return channel. When an intermediary — for example, an MCP tool or other Agent — instead hands the request to a remote handler (a remote Credential Manager or service that produces the result without invoking the Digital Credentials API itself), two things must be supplied that the Verifier cannot know: where the result is returned, and how a non-DC-API handler is expected to process a Digital Credentials request. The intermediary forwards the request unchanged and adds only the return channel.
Return Channel§
An intermediary that relays the x401 payload to a remote handler MUST add a return_uri member to the payload it forwards. return_uri is an https URL the handler delivers the Credential Result to; it is supplied by the relaying intermediary, never by the Verifier, and SHOULD be unguessable, short-lived, single-use, and controlled by the intermediary. The handler returns the result by sending an HTTP POST of the { "protocol": ..., "data": ... } Credential Result to return_uri. The intermediary then packages that result into a Result Artifact (inline or as a Credential Reference) and retries the protected route.
{
"scheme": "x401",
"version": "0.2.0",
"credential_requirements": { "digital": { "requests": [ { "protocol": "openid4vp-v1-signed", "data": { "request": "eyJ..." } } ] } },
"oauth": { "token_endpoint": "https://bank.example.com/oauth/token" },
"return_uri": "https://mcp.example/x401/return/9f1c2a"
}
Handler Processing§
The intermediary passes credential_requirements to the handler unchanged; the handler is not asked to disassemble it, nor is it told which entry to use. It evaluates the request the way the Digital Credentials API would and returns a result for whichever entry it can satisfy. A remote handler that does not invoke the Digital Credentials API:
- MUST consider the
requests[]entries whoseprotocoland credential formats it implements, and among those, MUST attempt to satisfy each entry’sdcql_query— including anycredential_sets/claim_setsalternatives within it — against the credentials available to it, with Holder selection where applicable. Which entry is used is the outcome of this matching, not a prior choice; an entry is usable only if a held credential satisfies its query. - For a satisfiable entry, MUST read the OpenID4VP request from its
data— the claims of the signed request object foropenid4vp-v1-signed, or the request parameters directly foropenid4vp-v1-unsigned— and produce a result that satisfies thatdcql_query, bound to itsnonce. It honorsclient_metadatafor accepted formats and any response-encryption key, and, for a signed request, binds the result’s audience to the request’sclient_id. - MUST treat the Digital Credentials API transport members as not applicable to relayed delivery: it does not return through
response_mode: dc_api/dc_api.jwt(it returns toreturn_uriinstead) and does not enforceexpected_origins(there is no invoking Web origin). - MUST NOT weaken or alter the
dcql_queryornonce, and MUST deliver the resulting Credential Result toreturn_uri. If it can satisfy no entry, it returns no result and SHOULD signal that failure to the intermediary rather than returning a partial or substitute result.
This matching, credential discovery, and Holder selection follow the same OpenID4VP and DCQL rules a Credential Manager applies under the Digital Credentials API; x401 does not redefine them.
Because relayed delivery does not go through a Web origin, expected_origins is not enforced. A signed request still binds the result’s audience to the Verifier through its client_id, so signed requests are RECOMMENDED for relayed delivery — they preserve Verifier audience binding across the relay, losing only origin pre-authorization. An unsigned request has no client_id and so binds only to the nonce. A Verifier that supports relayed delivery validates the returned result by its nonce and, for a signed request, its client_id, accepting that origin pre-authorization was not enforced — the weaker-binding case described in Verifier Binding.
Composing a Request for Both Native and Relayed Fulfillment§
A Verifier that may have its request fulfilled either natively — invoked through navigator.credentials.get() by a Credential Manager enforcing the Digital Credentials API — or by a remote handler that parses the request and generates a result manually MUST compose the request so it is self-contained and verifiable by a party with no prior relationship and no browser context. The governing test is: could a Credential Manager that has never interacted with this Verifier verify the request and produce a correctly bound result from the request bytes alone? A request composed only for the native path can silently fail this test even though a DC-API Credential Manager accepts it. To satisfy both paths, a Verifier:
- SHOULD use a signed request (
openid4vp-v1-signed). It is the only request mode that carries any Verifier binding across a relay (audience viaclient_id), and it is equally valid natively. An unsigned request is fulfillable by a remote handler but binds only to thenonce; see Verifier Binding. - MUST make its identity and request-signing key resolvable from the request alone. The Verifier SHOULD embed its signing certificate chain in the request JWS
x5cheader, or use aclient_idwhose key material is publicly resolvable (for example adid:orhttps:scheme), so a handler with no prior relationship can verify the signature and confirm it matchesclient_idoffline. A Verifier MUST NOT rely on aclient_idscheme or key whose resolution depends on the invoking Web origin or on prior DC-API interaction, because neither exists on the relayed path. - MUST carry every input a fulfiller needs inside the request object: the
nonce, thedcql_query(including anycredential_sets/claim_setsalternatives), the accepted formats and any response-encryption key inclient_metadata, and theexp. A remote handler reads these directly from the requestdata— the signed request object’s claims for a signed request — not from any API surface, so a value referenced only through the DC-API or a same-origin session is unavailable to it. - SHOULD still set the Digital Credentials API transport members —
response_mode(dc_api/dc_api.jwt) andexpected_origins— for the native path, but MUST NOT make correct processing depend on them. A remote handler treats them as not applicable (it returns toreturn_uriand there is no invoking origin to pre-authorize), so a request whose only Verifier binding isexpected_originsdegrades tononce-only when relayed. - SHOULD, when response confidentiality through the intermediary or
return_urihost is required, supply a response-encryption key inclient_metadatarather than relying on thedc_api.jwtresponse mode. The encryption key is honored on both paths, independent ofresponse_mode; the transport-bound encryption ofdc_api.jwtis not used when the result is delivered toreturn_uri. - SHOULD size
expto accommodate relay latency and Holder interaction, which take longer than a same-device interactive flow, and SHOULD rely on the freshness and uniqueness of thenoncefor replay protection rather than on a tightexp; see Verifier State and Stateless Operation. - SHOULD use signature algorithms and credential formats an arbitrary Credential Manager can verify and produce, and SHOULD NOT over-constrain
client_metadatato formats only a specific native Credential Manager implements.
A request composed this way is fulfillable on either path with no change: the native Credential Manager enforces expected_origins and returns through the DC-API, while the remote handler ignores those transport members, verifies the Verifier from the embedded key material, and returns the same { protocol, data } result to return_uri. In both cases the Verifier validates the returned result by its nonce and the binding its request mode provides.
Credential Result Delivery§
This phase of the protocol defines how the Agent packages the Credential Manager’s credential result and presents it back to the Verifier. The Agent either retries the protected route directly with a Result Artifact or uses that same Result Artifact in the OAuth token exchange described in the next leg.
POST /accounts/applications HTTP/1.1
Host: bank.example.com
PROOF-RESPONSE: <base64url-result-artifact-json>
Result Artifact§
After obtaining a Credential Result, the Agent packages it as a Result Artifact for protected-route retry or OAuth token exchange. The Result Artifact carries the credential result either inline or as a Credential Reference.
General Structure§
An inline Result Artifact carries the Digital Credentials API result { protocol, data } directly:
{
"request_id": "proof-template-financial-customer-v1",
"credential_result": {
"protocol": "openid4vp-v1-signed",
"data": "<credential-manager-returned-result>"
}
}
A by-reference Result Artifact carries a URL the Verifier dereferences to fetch the credential result, for results that exceed header size limits or are generated at a remote location:
{
"request_id": "proof-template-financial-customer-v1",
"credential_result_uri": "https://bank.example.com/.well-known/x401/results/abc123",
"expires_at": "2026-05-06T18:50:00Z"
}
Members§
| Name | Definition |
|---|---|
credential_result |
REQUIRED unless credential_result_uri is present. The Credential Result returned by the Credential Manager, as the { protocol, data } object produced by the Digital Credentials API. |
credential_result_uri |
REQUIRED unless credential_result is present. An HTTPS URL the Verifier dereferences with GET to fetch the Credential Result. The Verifier MUST issue a unique credential_result_uri value for each result and MUST NOT reuse one across results. See Result by Reference. |
expires_at |
OPTIONAL. RFC 3339 time after which the credential_result_uri is no longer valid. |
request_id |
OPTIONAL. The x401 request_id, when present. |
agent_id |
OPTIONAL. An Agent Identifier for the HTTP caller, when the deployment binds the Agent to the retry. See Agent Binding Options. |
A Result Artifact MUST contain exactly one of credential_result or credential_result_uri.
The Verifier MUST NOT treat the request_id or agent_id values inside the Result Artifact as authoritative by themselves. They are carried so the Verifier can correlate the retry. Correlation between the result and the issued request is established by the OpenID4VP nonce echoed inside the credential result. The Verifier remains responsible for validating the result binding and, when it binds the Agent, authenticating the Agent Identifier.
Result by Reference§
A credential result can be larger than an HTTP header field comfortably carries, and a remotely generated result may not be held by the Agent at all. For these cases the Result Artifact MAY carry a credential_result_uri instead of an inline credential_result. The Verifier dereferences the URL to obtain the same { protocol, data } credential result it would otherwise have received inline.
When a Result Artifact carries credential_result_uri:
- The
credential_result_uriMUST be anhttpsURL. - The Verifier MUST issue a unique
credential_result_urifor each result and MUST NOT reuse a URI value across results. Uniqueness per result is what lets the Verifier treat the reference as single-use and bind it to one retry. - The Verifier fetches the credential result with an HTTP
GETand processes it exactly as if it had been supplied inline ascredential_result. The fetched result is self-authenticating — it is validated against the issued request’snonceand the binding its request mode provides, like any other — so a substituted or tampered result is rejected by normal proof validation without a separate integrity digest. - The reference SHOULD be short-lived and scoped to the route and retry it serves.
This same mechanism lets a remote fulfillment surface generate a result, publish it at a credential_result_uri, and let the Agent replay the protected route with only the reference. See Remote and Out-of-Band Fulfillment.
x401 Error Object§
When a Verifier reports that a submitted x401 credential result failed or could not be processed, it returns a PROOF-RESULT header whose payload is a base64url-encoded x401 Error Object.
General Structure§
{
"scheme": "x401",
"version": "0.2.0",
"error": "invalid_result",
"error_description": "The credential result did not satisfy the route proof requirement.",
"request_id": "proof-template-financial-customer-v1"
}
Members§
| Name | Definition |
|---|---|
scheme |
REQUIRED. Value MUST be the string "x401". |
version |
REQUIRED. The x401 error object version. |
error |
REQUIRED. A verifier-defined error code. The value SHOULD be a short ASCII token suitable for logs and programmatic handling. |
error_description |
OPTIONAL. Human-readable diagnostic text. |
error_uri |
OPTIONAL. HTTPS URL identifying documentation for the error. |
request_id |
OPTIONAL. The x401 request_id, when the error can be correlated to a proof template. |
The x401 Error Object describes the x401 proof branch only. The HTTP status code continues to describe the overall response.
x401 Token Object§
When an Agent has a Verification Token and the protected route already uses the Authorization request header for existing application authentication, the Agent can pass the Verification Token as an x401 Token Object in a PROOF-RESPONSE header. This allows existing application credentials and x401 proof satisfaction to travel on the same request without overloading Authorization.
General Structure§
{
"scheme": "x401",
"version": "0.2.0",
"token_type": "Bearer",
"access_token": "<x401-verification-token>"
}
Members§
| Name | Definition |
|---|---|
scheme |
REQUIRED. Value MUST be the string "x401". |
version |
REQUIRED. The x401 token object version. |
token_type |
REQUIRED. The token type of the supplied Verification Token. For Verification Tokens defined by this specification, the value is "Bearer". |
access_token |
REQUIRED. The opaque or structured Verification Token value issued to the Agent. |
The x401 Token Object carries proof satisfaction only. It does not replace the route’s ordinary authorization credentials unless the deployment explicitly defines the Verification Token as an upgraded or replacement application token.
Route Retry Headers§
After receiving an x401 proof requirement and obtaining a credential result, the Agent retries the protected route with either direct proof material or a reusable Verification Token.
When retrying with proof material directly, the Agent uses PROOF-RESPONSE:
PROOF-RESPONSE: <base64url-result-artifact-json>
The PROOF-RESPONSE value is the base64url-encoded UTF-8 JSON serialization of the Result Artifact, using the same no-padding encoding as the x401 payload. The Result Artifact carries the credential result inline or as a Credential Reference; for large results the Agent SHOULD use the reference form to keep the header within server and intermediary size limits.
When retrying with a Verification Token, the Agent either uses the route’s normal Authorization request header or carries an x401 Token Object in PROOF-RESPONSE.
A deployment MAY issue a Verification Token that is also the route’s ordinary application authorization credential, or it MAY upgrade the caller’s existing application access token with x401-specific proof satisfaction metadata. In that case, the Agent sends the token using the normal token type for the route. For Verification Tokens defined by this specification, the token type is Bearer:
Authorization: Bearer <verification-token-or-upgraded-application-token>
If the protected route already uses Authorization for existing application authentication and the x401 Verification Token is separate from that application credential, the Agent preserves the existing Authorization value and sends the x401 token separately:
Authorization: Bearer <existing-application-token>
PROOF-RESPONSE: <base64url-x401-token-object>
A protected route MUST process the supplied PROOF-RESPONSE value as a credential-result or proof-token satisfaction attempt. If verification succeeds, the Verifier MAY return the protected resource directly.
Agent Processing Rules§
An Agent receiving an HTTP response with a PROOF-REQUEST proof requirement:
- MUST treat the response as a proof requirement.
- MUST extract the
PROOF-REQUESTfield value and base64url-decode it as a UTF-8 JSON x401 Payload. - MUST validate the decoded payload structure and process the
proofobject. - MUST treat
credential_requirementsas the Verifier-composed credential request and MUST NOT modify any of its entries. - MUST obtain a Credential Result for
credential_requirementsby invoking it through a native credential method, relaying it to a Credential Manager or remote service, or acquiring a remotely generated result. - MAY select among multiple
credential_requirements.digital.requestsentries by protocol or supported credential format when more than one is present. - MAY use the dereferenceable issuer pointers in the request’s DCQL
trusted_authorities(openid_federation,etsi_tl) to filter candidate credentials or guide acquisition; see Credential Acquisition Guidance. - MUST NOT treat any Agent-side interpretation of the request’s
trusted_authoritiesas proof of verifier acceptance. - MUST package the credential result as a Result Artifact, inline or as a Credential Reference.
- MUST retry the same route that produced the x401 proof requirement with one of:
- a Result Artifact in a
PROOF-RESPONSErequest header, - a Verification Token carried as an x401 Token Object in a
PROOF-RESPONSErequest header, or - a Verification Token in the route’s normal
Authorizationrequest header when the deployment specifies that the token is an upgraded or replacement application authorization credential.
- a Result Artifact in a
- MUST NOT replace an existing application
Authorizationcredential with an x401 Verification Token unless the deployment explicitly defines the returned token as valid for that route’s ordinary authorization processing. - MUST treat a
PROOF-RESULTcarrying an x401 Error Object as an x401 proof failure for the route-scoped proof attempt, regardless of the HTTP status code.
Verifier Processing Rules§
A Verifier implementing x401:
- MUST include
PROOF-REQUEST: <base64url-x401-payload>when proof is required or advertised. - MUST include a valid base64url-encoded x401 payload in
PROOF-REQUEST. - MUST use an HTTP status code appropriate for the overall response and MUST NOT rely on the status code alone to convey x401 proof state.
- MUST include a
credential_requirementswhosedigitalmember’s entries are valid OpenID4VP requests for the DC API, usingopenid4vp-v1-signed(RECOMMENDED) oropenid4vp-v1-unsigned. - SHOULD use signed requests and set their
client_idandexpected_origins; when using unsigned requests, MUST account for the weaker binding described in Verifier Binding. - MUST include OAuth token exchange metadata in
oauth. - SHOULD express its accepted issuers as DCQL
trusted_authoritiesinside the request, preferring dereferenceable types (openid_federation,etsi_tl) when it wants Agents to be able to discover acquisition paths. - MUST NOT enumerate verifier-approved issuers inline as an x401-specific payload member.
- MUST validate credential results according to the proof validation rules in this specification and the credential format rules it relies upon, dereferencing a Credential Reference when one is supplied.
- MUST evaluate issuer trust, status, revocation, and policy constraints independently of any Agent-side interpretation of the request’s
trusted_authorities. - MUST accept a Result Artifact in a
PROOF-RESPONSErequest header for protected-route retry, in both its inline and by-reference forms. - MAY issue a Verification Token through the OAuth token endpoint after validating the presented Result Artifact.
- MUST validate Verification Tokens on protected-route retry according to token scope, audience, expiration, any Agent binding, and satisfied requirement metadata, whether the token arrives in
Authorizationor as an x401 Token Object inPROOF-RESPONSE. - MUST bind any Verification Token carried in
PROOF-RESPONSEto the existing application caller, credential, client, key, or Agent Identifier required by the protected route when anAuthorizationheader is also present. - SHOULD return
PROOF-RESULT: <base64url-x401-error-object>if proof is presented but policy satisfaction fails or the result or token cannot be processed. - MUST use
402 Payment Requiredseparately if payment is required and remains unsatisfied.
Proof Validation§
When a Verifier receives a Result Artifact directly on a protected-route retry or through the OAuth token endpoint, it MUST validate the result against the request it composed for the route. When the Result Artifact is a Credential Reference, the Verifier first dereferences credential_result_uri to obtain the credential result, then validates that result exactly as if it had been supplied inline.
The Verifier MUST:
- recover the route, method, and policy context for the request it composed, by whatever stateful or stateless means it uses;
- verify that the result’s proof is cryptographically protected according to the credential and presentation formats in use;
- verify the result’s binding for the request’s mode: for a signed request, that it is bound to the Verifier as relying party through the request signature,
client_id, andexpected_origins; for an unsigned request, that it is bound to the invoking origin the Verifier is willing to accept (see Verifier Binding); - verify that the result’s OpenID4VP
nonceis the value the Verifier issued for the request, applying its freshness and replay policy; - verify that the credentials and disclosed claims satisfy the
dcql_querycarried in the request; - verify issuer trust, credential status, revocation, expiration, assurance, and any additional route policy;
- when the deployment binds the Agent, determine and verify the Agent Identifier for the HTTP caller and reject the retry if it cannot be bound; see Agent Binding Options.
With a signed request the returned result is bound to the Verifier as relying party; with an unsigned request it is bound to the invoking origin and the Verifier’s nonce, which the Verifier SHOULD reinforce as described in Verifier Binding. In either case the result is not, by itself, bound to the Agent: Agent binding is OPTIONAL and, when used, is an additional check layered over this validation.
Access Token Acquisition§
This optional leg of the protocol defines the optional exchange of a verified Result Artifact for a reusable Verification Token. The Agent submits the Result Artifact to the OAuth token endpoint from the x401 payload using OAuth 2.0 Token Exchange, then retries protected routes with the returned token either as the route’s normal authorization credential or as an x401 proof-satisfaction token object in PROOF-RESPONSE.
POST /oauth/token HTTP/1.1
Host: bank.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
subject_token_type=urn:x401:params:oauth:token-type:result_artifact&
subject_token=<base64url-result-artifact-json>
OAuth Token Exchange§
An Agent MAY exchange a Result Artifact for a Verification Token at the OAuth token endpoint supplied in oauth.token_endpoint.
The token request uses OAuth 2.0 Token Exchange. The Agent MUST use:
grant_type=urn:ietf:params:oauth:grant-type:token-exchangesubject_token_type=urn:x401:params:oauth:token-type:result_artifactsubject_token=<base64url-result-artifact-json>
The Agent SHOULD include the resource or audience value from oauth when present. If neither value is present, the Agent MAY use the original protected resource URL as the OAuth resource value.
POST /oauth/token HTTP/1.1
Host: bank.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
subject_token_type=urn:x401:params:oauth:token-type:result_artifact&
subject_token=<base64url-result-artifact-json>&
resource=https%3A%2F%2Fbank.example.com%2Faccounts%2Fapplications
The submitted Result Artifact MAY carry the credential result inline or as a Credential Reference; when it is a reference, the token endpoint dereferences it as described in Result by Reference. The token endpoint MAY require normal OAuth client authentication. If the deployment binds the Agent and client authentication is used, the authenticated OAuth client MUST bind to the same Agent Identifier the deployment requires for the retry.
The token endpoint MUST process the submitted Result Artifact using the same proof validation rules that apply to direct protected-route retry. In particular, it MUST verify that:
- the result is cryptographically valid and bound per the request’s mode — to the Verifier as relying party for a signed request, or to an acceptable invoking origin for an unsigned request;
- the credential material satisfies the
dcql_querycarried in the request for the requested resource; - the result’s OpenID4VP
nonceis one the Verifier issued, applying its freshness and replay policy; - when the deployment binds the Agent, the result and retry bind to the required Agent Identifier.
If verification succeeds and the Verifier chooses token retry, the token endpoint returns an OAuth-compatible successful access token response:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
For Verification Tokens defined by this specification, the token endpoint MUST return token_type with the value Bearer.
{
"access_token": "eyJhbGciOi...",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 300,
"scope": "accounts:open",
"x401": {
"agent_id": "did:web:agent.example",
"verifier_id": "https://bank.example.com",
"request_id": "proof-template-financial-customer-v1",
"satisfied_requirements": [
"urn:example:x401:satisfaction:financial-customer:v1"
],
"resource": "https://bank.example.com/accounts/applications",
"method": "POST",
"expires_at": "2026-05-06T18:50:00Z"
}
}
| Name | Definition |
|---|---|
access_token |
REQUIRED. The opaque or structured Verification Token value issued to the Agent. |
issued_token_type |
RECOMMENDED. Token type of the issued token. For Bearer access tokens, use urn:ietf:params:oauth:token-type:access_token. |
token_type |
REQUIRED. The HTTP authorization scheme the Agent uses with the token. The value defined by this specification is Bearer. |
expires_in |
RECOMMENDED. Lifetime of the token in seconds from the time the response is generated. |
scope |
OPTIONAL. OAuth scope string representing access authorized by the token. |
x401 |
RECOMMENDED. Object containing x401-specific token metadata that helps the Agent and Verifier understand what proof requirements were satisfied. |
Verification Token Retry Placement§
A Verification Token can be placed in either Authorization or PROOF-RESPONSE, depending on how the deployment composes x401 with existing application authorization.
If the Verifier, authorization server, and protected application are integrated, the token endpoint MAY issue a token that is accepted as the route’s normal application authorization credential. This can be a new token or an upgraded version of an existing application token that includes x401-specific proof satisfaction metadata. In that model, the Agent sends the returned token through the normal authorization mechanism for the route:
Authorization: Bearer <verification-token-or-upgraded-application-token>
If the protected route already requires an application token in Authorization, and the x401 Verification Token is separate from that application token, the Agent SHOULD preserve the existing Authorization value and send the Verification Token as an x401 Token Object in PROOF-RESPONSE:
Authorization: Bearer <existing-application-token>
PROOF-RESPONSE: <base64url-x401-token-object>
When processing a PROOF-RESPONSE value that carries an x401 Token Object, the Verifier MUST decode the x401 Token Object, validate the contained Verification Token, and evaluate whether the token satisfies the route’s x401 proof requirement. If an Authorization header is also present, the Verifier MUST ensure that the application credential and the x401 Verification Token are bound to the same Agent Identifier, client, subject, proof-of-possession key, certificate, or other verifier-accepted caller binding for the route. A Verifier MUST reject a request that combines an application credential and x401 Verification Token that cannot be safely bound to the same accepted caller context.
Verification Token Contents§
A Verification Token records the Verifier’s decision that a credential result satisfied an x401 proof requirement. It is not a credential, payment artifact, or new issuer attestation about the credential subject.
A Verifier MAY issue a Verification Token after accepting a Result Artifact. The token:
- when the deployment binds the Agent, MUST be issued to the Agent Identifier bound during the retry, and MUST NOT rely on the credential subject as the token holder identity unless the credential subject is also the Agent;
- MUST be scoped to the Verifier audience and to the route, policy, action, resource, or resource class for which proof was accepted;
- MUST expire, and SHOULD be short-lived;
- SHOULD include a unique token identifier and support replay detection and revocation;
- SHOULD identify the
request_idandsatisfied_requirementsaccepted by the Verifier; - SHOULD identify the Verifier identifier, resource, method, and proof time used to issue the token.
When a Verification Token is represented as a JWT, its exact claim set is deployment-specific. The token SHOULD include:
issidentifying the Verifier or authorization server;subidentifying the Agent, when the deployment binds the Agent;audidentifying the protected resource server or Verifier audience;exp,iat, andjti;client_ididentifying the Agent when useful for OAuth infrastructure;scope,resource, or method/action claims used for access decisions;x401_request_id;x401_satisfied_requirements;x401_query_hashor a verifier-defined reference to the credential query that was satisfied.
Reuse Across Routes§
OpenID4VP state, presentation nonce, and DCQL Credential Query id values are useful for request-response correlation, holder binding, or Credential-Manager-facing query selection inside a single presentation transaction. They are not, by themselves, stable semantic identifiers for cross-route token reuse.
x401 uses request_id and satisfied_requirements for reusable proof semantics. A Verifier MAY accept a Verification Token issued for one route on another route only when:
- the token is valid for the Verifier audience and current protected resource;
- the token has not expired or been revoked;
- when the deployment binds the Agent, the token is issued to the current Agent Identifier;
- the token’s accepted proof requirements cover the later route’s
satisfied_requirements; - any freshness, status, assurance, and policy constraints still hold.
Agents MAY use the x401.satisfied_requirements metadata returned with a Verification Token to decide whether to try the token on a later route. The Verifier remains authoritative and SHOULD return a new x401 proof requirement when the token is valid but does not satisfy the later route.
Examples§
Example 1: Proof Requirement§
Initial Request§
POST /accounts/applications HTTP/1.1
Host: bank.example.com
Response§
HTTP/1.1 401 Unauthorized
PROOF-REQUEST: <base64url-x401-payload>
Cache-Control: no-store
Decoded x401 payload, shown for readability:
{
"scheme": "x401",
"version": "0.2.0",
"credential_requirements": {
"digital": {
"requests": [
{
"protocol": "openid4vp-v1-signed",
"data": {
"request": "eyJhbGciOiJFUzI1NiIsInR5cCI6Im9hdXRoLWF1dGh6LXJlcStqd3QifQ..."
}
}
]
}
},
"oauth": {
"token_endpoint": "https://bank.example.com/oauth/token"
},
"request_id": "proof-template-financial-customer-v1",
"satisfied_requirements": [
"urn:example:x401:satisfaction:financial-customer:v1"
]
}
The signed OpenID4VP request inside credential_requirements.digital.requests[0].data.request, decoded for readability, is authored and signed by the Verifier:
{
"response_type": "vp_token",
"response_mode": "dc_api",
"client_id": "x509_san_dns:bank.example.com",
"expected_origins": ["https://bank.example.com"],
"nonce": "uX7Vq3mZJH6MeN0qz2L7SQ",
"dcql_query": {
"credentials": [
{
"id": "financial_customer",
"format": "jwt_vc_json",
"meta": {
"type_values": ["FinancialCustomerCredential"]
},
"claims": [
{
"path": ["credentialSubject", "assurance_level"],
"values": ["VC-AL2", "VC-AL3"]
}
]
}
]
},
"exp": 1746557100
}
Obtaining the Result§
The Agent invokes the Verifier-composed request directly through the Digital Credentials API, or relays it to a Credential Manager or remote service. The composed request is the digital member argument:
const result = await navigator.credentials.get(payload.credential_requirements);
// result => { protocol: "openid4vp-v1-signed", data: { /* credential result bound to the Verifier */ } }
Successful Retry With Result Artifact§
POST /accounts/applications HTTP/1.1
Host: bank.example.com
PROOF-RESPONSE: <base64url-result-artifact-json>
The decoded Result Artifact carries the credential result inline:
{
"request_id": "proof-template-financial-customer-v1",
"credential_result": {
"protocol": "openid4vp-v1-signed",
"data": "<credential-manager-returned-result>"
}
}
Successful Retry With a Credential Reference§
When the credential result is too large for a header field, or was generated at a remote location, the Result Artifact carries a reference the Verifier dereferences:
{
"request_id": "proof-template-financial-customer-v1",
"credential_result_uri": "https://bank.example.com/.well-known/x401/results/abc123",
"expires_at": "2026-05-06T18:50:00Z"
}
Failed Retry With x401 Error§
HTTP/1.1 401 Unauthorized
PROOF-RESULT: <base64url-x401-error-object>
Cache-Control: no-store
The decoded x401 Error Object describes the failed credential result. The 401 Unauthorized status in this example means the route was not completed because the x401 proof branch failed.
Example 2: OAuth Token Exchange§
After receiving the Credential Manager credential result, the Agent may exchange the Result Artifact for a token.
POST /oauth/token HTTP/1.1
Host: bank.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
subject_token_type=urn:x401:params:oauth:token-type:result_artifact&
subject_token=<base64url-result-artifact-json>&
resource=https%3A%2F%2Fbank.example.com%2Faccounts%2Fapplications
If verification succeeds, the token endpoint returns:
{
"access_token": "eyJhbGciOi...",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 300,
"scope": "accounts:open",
"x401": {
"agent_id": "did:web:agent.example",
"verifier_id": "https://bank.example.com",
"request_id": "proof-template-financial-customer-v1",
"satisfied_requirements": [
"urn:example:x401:satisfaction:financial-customer:v1"
],
"resource": "https://bank.example.com/accounts/applications",
"method": "POST"
}
}
If the returned token is an upgraded or replacement application authorization credential, the Agent retries the original protected route with the token in Authorization:
POST /accounts/applications HTTP/1.1
Host: bank.example.com
Authorization: Bearer eyJhbGciOi...
If the application already requires an existing Authorization token and the x401 Verification Token is separate, the Agent preserves the application token and carries the x401 Verification Token as an x401 Token Object in PROOF-RESPONSE:
POST /accounts/applications HTTP/1.1
Host: bank.example.com
Authorization: Bearer <existing-application-token>
PROOF-RESPONSE: <base64url-x401-token-object>
Composable Agent and Entity Identification§
A returned result is already bound to the Verifier (for a signed request) or to the invoking origin and the Verifier’s nonce (for an unsigned request); see Verifier Binding. x401 does not, by default, require the result to be bound to the Agent, and it does not define a single global agent identity system. Deployments MAY layer additional mechanisms over x401 to authenticate the calling agent, bind an entity identity to the HTTP request, sender-constrain a Verification Token, or carry delegation evidence from a user, organization, workload, or upstream agent.
These mechanisms compose cleanly with x401 when they:
- produce an authenticated caller identifier the Verifier can map to the route’s accepted Agent Identifier policy;
- bind that identifier to the HTTP request being evaluated, including the method, target URI or authority, freshness values, and relevant x401 retry material;
- can be verified before or during x401 proof validation;
- do not alter the composed request in
credential_requirements, the Result Artifact, or the402 Payment Requiredboundary; - allow the Verifier to reject mismatches between the authenticated caller, any Result Artifact
agent_id, and any Verification Token holder identity.
Agent Binding Options§
Agent binding is OPTIONAL in x401. Some deployments — such as a consumer AI client surfacing a verification link to a user — need no agent binding at all; the proof is bound to the Verifier and that is sufficient. Others — such as service-to-service or enterprise deployments — want to additionally bind the proof and retry to the specific calling Agent. This specification does not select a single mechanism. The subsections below describe options that compose with x401; a deployment that binds the Agent picks one (or more) and defines the accepted Agent Identifier schemes in its policy.
When an Agent relays the request to a separate Credential Manager, browser, device, or remote service, or acquires a remotely generated Credential Result, the surface that obtains the result is not necessarily the Agent that retries the route. A deployment that binds the Agent in those topologies MUST choose a binding mechanism that survives the relay — for example, an HTTP-layer or token-layer caller authentication on the retry — rather than relying on the credential result alone.
Web Bot Auth and HTTP Message Signatures§
Web Bot Auth is a natural option for adding request-bound identification to x401. A calling Agent can sign the initial protected-route request, the retry request carrying a Result Artifact or Verification Token, and the OAuth token exchange request using HTTP Message Signatures. The Agent can also use the Signature-Agent header to point the Verifier to an HTTP Message Signatures key directory.
When layered over x401, a Web Bot Auth signature SHOULD cover the method and target, the authority or host, the Signature-Agent header when present, the PROOF-RESPONSE header when retrying with direct proof or proof-token material, the Authorization header when retrying with application or upgraded token material, and Content-Digest when the request has a body. The signature SHOULD include short-lived freshness metadata such as created, expires, and a replay-resistant nonce.
A Verifier MAY use the validated signing key, key directory authority, or derived service identity as the Agent Identifier, or as evidence that maps to an Agent Identifier. The Verifier MUST still validate the Credential Manager result binding for the request mode, the credential query satisfaction, issuer trust, token scope, and payment boundary. Web Bot Auth identifies the calling automation or service; it does not by itself prove the credential subject, satisfy the credential query, or prove end-user delegation.
OAuth Proof-of-Possession and Client Authentication§
Deployments that use the OAuth token exchange leg MAY require additional OAuth client authentication at oauth.token_endpoint. Mutual TLS client authentication and certificate-bound access tokens can bind the token request, and later token use, to a certificate controlled by the Agent. This works well when the Agent Identifier is a certificate-bound identifier, domain-bound client identifier, SPIFFE ID, or other identifier that the Verifier can map from the TLS client certificate.
DPoP can bind OAuth token requests and resource requests to an Agent-controlled key at the application layer. Because this version of x401 defines Bearer Verification Tokens, a DPoP-bound Verification Token retry needs either a deployment-specific profile or a future x401 token retry profile that permits token_type: DPoP and the DPoP proof header. When a deployment binds the Agent and uses DPoP at the token endpoint, the endpoint MUST ensure the DPoP key maps to the Agent Identifier the deployment requires for the retry.
Workload Identity§
In service-to-service deployments, the Agent may be a workload rather than a user-facing application. The Agent Identifier can be derived from a workload identity mechanism such as a SPIFFE ID carried in an X.509-SVID or JWT-SVID, or from WIMSE work on workload identity tokens and HTTP Message Signatures. These mechanisms are useful for infrastructure agents, internal tools, and managed compute environments where the Verifier needs to know which deployed workload made the request.
Workload identity proves the caller’s operational identity. It does not prove that the caller holds the requested credential, that the credential subject is authorized, or that an end user delegated authority to the Agent. Those remain x401 proof validation and policy questions.
Agent Identifier Schemes and HTTP-Layer Binding§
In the composed-request model the proof request never identifies the Agent: for a signed request the client_id identifies the Verifier as relying party, and an unsigned request has no client_id at all. A deployment that binds the Agent therefore does so at the HTTP or token layer rather than through the proof request. The Agent Identifier MAY be a DID, HTTPS origin, domain-bound client identifier, certificate-bound identifier, SPIFFE ID, or other verifier-approved scheme.
The Verifier determines that the protected-route caller is the required Agent Identifier using a verifier-recognized mapping across the HTTP Message Signature identity, mutual TLS certificate, DPoP key, workload identity, or Verification Token holder identity used on the retry. Because the result is bound to the Verifier, this caller binding is what ties the proof to a specific Agent when a deployment requires it.
Delegation and Actor Evidence§
Some deployments need to know not only which Agent made the request, but who or what authorized that Agent to act. Delegation evidence can be carried as an additional credential, a credential disclosed through the request’s dcql_query, an OAuth Token Exchange actor chain, a GNAP grant artifact, a Verifiable Intent credential, or another signed mandate or capability.
Delegation evidence composes best when it is scoped, time-limited, replay-resistant, and bound to the Agent Identifier and requested resource or action. It does not replace caller authentication: the Verifier still needs to know which Agent is presenting the delegation evidence and whether that Agent is the one authorized by the evidence.
Consumer Client Compatibility§
The mechanism in this section is an early experiment in adapting x401 to consumer AI clients and other body-only consumers of HTTP responses. The core x401 protocol — as defined in the preceding sections — is the standard, straightforward way to convey proof requirements: a Verifier returns a PROOF-REQUEST header, and the Agent reads, decodes, and acts on it. The pattern described below is offered as a compatibility supplement for content-bearing responses where header-driven discovery is not viable, and the community is invited to contribute proposals, examples, and reference implementations that improve how x401 reaches these clients.
x401 is fundamentally an HTTP header protocol. A conforming Verifier signals proof requirements through PROOF-REQUEST, and a conforming Agent processes that header. There is, however, a meaningful class of consumers that today cannot reliably access HTTP response headers on a successful (2xx) response with a body. A Verifier that wants its gating requirements to reach those consumers — most notably consumer-facing AI assistants — MAY emit a body-embedded form of the same x401 payload in addition to whatever it carries in PROOF-REQUEST.
Motivation§
Several common client classes do not surface PROOF-REQUEST on a successful response with a body:
- Consumer-facing AI assistants from major platforms typically fetch web content through summarization or browsing tools that surface the response body to the model but drop, hide, or do not propagate the HTTP headers of
2xxresponses. As a result, a proof requirement carried only inPROOF-REQUESTon a successful HTML response will not reach the model. - HTML documents rendered in a browser do not expose response headers to inline content unless application code reads them through JavaScript and re-injects them into the DOM.
- Archival, syndication, and feed-rendering tools often store or render the body without preserving headers.
A Verifier that wants its gating requirements to be discoverable by these consumer AI flows or other body-only clients SHOULD strongly consider embedding the proof requirement in the response body using the form defined below, in addition to setting PROOF-REQUEST on the same response.
Embedded Proof Requirements in HTML Content§
On a non-401 HTML response, the Verifier MAY emit each advertised x401 proof requirement as a single HTML <data> element placed in the document body at, near, or wrapping the content to which the requirement applies. The element:
- MUST use the tag name
data. - MUST set the
valueattribute to the MIME-type expressionapplication/json;x401=proof-required. Thex401parameter identifies the embedded carrier and signals the role of the element’s text content. - MUST set the
hiddenattribute so the element is not visually rendered. - MUST contain a single JSON object as its text content. The JSON object MUST be a valid x401 payload as defined in x401 Payload, and MUST include a
$schemamember whose value is the JSON Schema URL for the x401 request object,https://x401.id/spec/schemas/request.json. The$schemamember is an informational marker that allows AI scrapers, content processors, and validators that retain only the JSON object to recognize it as an x401 proof requirement without prior knowledge of the surrounding HTML carrier.
<data value="application/json;x401=proof-required" hidden>{
"$schema": "https://x401.id/spec/schemas/request.json",
"scheme": "x401",
"version": "0.2.0",
"credential_requirements": {
"digital": {
"requests": [
{
"protocol": "openid4vp-v1-signed",
"data": {
"request": "eyJhbGciOiJFUzI1NiIsInR5cCI6Im9hdXRoLWF1dGh6LXJlcStqd3QifQ..."
}
}
]
}
},
"oauth": {
"token_endpoint": "https://bank.example.com/oauth/token"
}
}</data>
Unlike the PROOF-REQUEST header value, the embedded form is the unencoded JSON object. The <data> element is already a text container, and the $schema member is intended to be directly readable by content processors that retain the object.
Placement and Scope§
A <data> element placed at the document level applies to the page as a whole and SHOULD be used as a body-side mirror of the route-scoped PROOF-REQUEST header so that header-blind clients can still discover the requirement. A response MAY include multiple <data value="application/json;x401=proof-required" hidden> elements when a page surfaces multiple advertised requirements; each element MUST be a complete, independent x401 payload that can be processed without reference to the others.
The embedded form does not extend PROOF-REQUEST semantics. The header continues to carry the route-scoped requirement for the entire response. A <data> element is a body-side disclosure intended for clients that cannot consume the header.
Processing Expectations§
A client that processes embedded <data> elements:
- MUST treat the parsed JSON object as an x401 payload subject to the same structural validation and composed-request processing defined elsewhere in this specification.
- SHOULD treat the embedded requirement as a disclosure of gating intent for the surrounding content and SHOULD complete the protocol by requesting the appropriate protected resource through normal x401 header-driven mechanisms.
A Verifier that emits embedded <data> elements MUST still enforce proof on the protected resource through the normal PROOF-REQUEST / PROOF-RESPONSE exchange. Embedding a requirement in HTML is informational disclosure and does not by itself grant access.
The JSON Schema for the embedded request object is included in Appendix C: x401 Request Object JSON Schema.
Remote and Out-of-Band Fulfillment§
This subsection describes, without adding new normative requirements, how the building blocks defined above let an Agent obtain a result when its own environment cannot invoke the Digital Credentials API — the common case for consumer AI clients. It uses only mechanisms already defined in this specification: the composed credential_requirements, the Credential Reference form of the Result Artifact, and the existing retry. The community is invited to contribute concrete fulfillment and resume patterns.
A consumer AI client may be unable to call navigator.credentials.get() itself. Because the composed request is signed and bound to the Verifier rather than to whoever invokes it, the request can be invoked somewhere else and the result returned to the Agent for replay. A common shape is:
- The Agent receives the x401 proof requirement and cannot invoke
credential_requirementsdirectly. - The Agent surfaces a fulfillment URL — for example, a verifier-hosted page — to the user. Hosting the page on a verifier-controlled origin lets the page’s origin match the
expected_originsof a signed request; an unsigned request avoids theexpected_originsconstraint at the cost described in Verifier Binding. - The page invokes
navigator.credentials.get(credentialRequirements)in its own browser context, on a real user activation, and obtains the credential result. - The page makes the result available for the route retry — for example, by posting it to the Verifier, which holds it at a
credential_result_uri, or by returning a short-lived reference. - The Agent resumes: it acquires the result or its reference and retries the protected route with a Result Artifact carrying the inline result or a Credential Reference.
How the Agent learns that fulfillment is complete and acquires the result or reference — polling a status endpoint, a user-pasted completion code, or a continuation URL — is a deployment choice. None of these resume mechanisms change the wire format defined by this specification; they all converge on the same protected-route retry. Because the result is bound to the Verifier, the surface that invokes the request does not need to be the Agent, and the Agent does not need to read the result it relays.
Security Considerations§
Replay Prevention§
The freshness and replay properties of a result derive from the OpenID4VP nonce the Verifier places in the signed request. Verifiers SHOULD use fresh nonce values and short request expiries and SHOULD reject stale or replayed proofs. How a Verifier recognizes a returned nonce as one it issued, and whether it enforces strict one-time use with shared replay state, are deployment decisions; see Verifier State and Stateless Operation.
Verifier Binding§
x401 binds a result to the Verifier in one of two ways, depending on the request mode the Verifier chose.
Signed requests (openid4vp-v1-signed, RECOMMENDED). The request signature and client_id let the Credential Manager authenticate the Verifier and bind the result’s audience to the Verifier’s identity, and expected_origins lets the Verifier pre-authorize the origin(s) from which the request may be invoked. This resists relay and phishing: a Credential Manager will not produce a result for the Verifier’s request unless it is invoked from an origin the Verifier authorized, so an attacker cannot harvest a result for the Verifier from an arbitrary origin. The cost is that the Verifier must know the invocation origin in advance — which is why, when the Agent’s own origin is not known, the request is invoked from a verifier-controlled fulfillment origin (see Remote and Out-of-Band Fulfillment).
Unsigned requests (openid4vp-v1-unsigned). There is no signature and no client_id; the Credential Manager uses the invoking origin as the relying-party identity and binds the result to that origin and the Verifier’s nonce. This lets the request be fulfilled at an origin the Verifier did not declare — the case signed requests cannot serve — at a real cost: the Credential Manager cannot authenticate the Verifier, and the Verifier cannot pre-authorize the invocation origin. A malicious page or relay that obtains the request can invoke it from its own origin in front of a different holder and relay the result back, so a Verifier that accepts an unsigned result on the strength of the nonce alone is exposed to credential harvesting and relay. A Verifier that uses unsigned requests therefore relies on the freshness and uniqueness of its nonce for replay protection and SHOULD compensate for the missing origin pre-authorization — for example by binding the proof to an authenticated Agent (see Agent Binding Options), by constraining the observed invocation origin, or by trusting the delivery channel.
Relayed delivery. When the request is fulfilled by a remote handler that does not invoke the Digital Credentials API (see Relayed Delivery to a Remote Handler), there is no invoking Web origin, so expected_origins is not enforced regardless of request mode. A signed request still binds the result’s audience to the Verifier through its client_id, so relayed delivery of a signed request preserves Verifier audience binding and loses only origin pre-authorization — strictly stronger than unsigned. A unsigned request relayed this way has no client_id and falls back to nonce-only binding. Either way the Verifier SHOULD compensate for the absent origin pre-authorization as it would for an unsigned request.
In all modes the Verifier MUST validate the binding its request mode and transport provide and MUST reject a result it cannot bind to the request it issued. The nonce provides freshness and correlation throughout; it does not, by itself, authenticate the Verifier or constrain the invocation origin.
Agent Binding§
Agent binding is OPTIONAL. The result is bound to the Verifier, not to the Agent, so a deployment that does not bind the Agent still has a result it can trust. A deployment that additionally wants to tie the proof and retry to a specific Agent layers an HTTP-layer or token-layer caller authentication over x401; see Agent Binding Options. When a deployment binds the Agent, it MUST reject a retry whose caller cannot be bound to the required Agent Identifier. Because the surface that invokes the request may differ from the Agent that retries the route, a binding mechanism that depends only on the credential result is insufficient in relay and remote-fulfillment topologies.
Result by Reference§
A credential_result_uri is a capability-style reference: possession of the URL is enough to retrieve the result. The Verifier MUST issue a unique URI per result, and SHOULD make it https, unguessable, short-lived, single-use, and scoped to the route and retry it serves; the URL SHOULD be treated as sensitive because it can leak through logs, history, and intermediaries. Because the Verifier issues these references, it SHOULD dereference only URIs it recognizes as its own, which also guards against being pointed at arbitrary locations; it SHOULD bound the response size and time of the fetch. A reference that is reused, expired, or unrecognized MUST be rejected. Integrity and authenticity of the fetched bytes do not depend on a separate digest: the result is validated against the issued request’s nonce and the binding its request mode provides, so a substituted or tampered result fails normal proof validation.
Issuer Trust§
Acquisition hints MUST NOT be treated as sufficient trust material. Verifiers MUST apply their own trusted issuer policy and validation logic.
The issuer constraints that must hold at proof time belong in the request’s DCQL trusted_authorities. An Agent MAY follow the dereferenceable entries (openid_federation, etsi_tl) to discover where to acquire a qualifying credential, but that resolution is an acquisition and discovery aid; it does not decide validity. The Verifier MUST independently validate that presented credentials were issued by parties approved under the request it issued and its own issuer policy, and MUST NOT rely on the Agent’s interpretation of trusted_authorities. Because an aki entry is not dereferenceable, a request constrained only by aki offers no acquisition path; this does not weaken enforcement, which depends on the constraint, not on its resolvability.
Proof Result and Token Retry§
Verifiers SHOULD prefer Verification Token retry in multi-step flows to avoid repeatedly transmitting large Result Artifacts. Verifiers that accept direct Result Artifact retry SHOULD consider header size limits and SHOULD use compact or referenced proof formats where possible.
Responses carrying fresh PROOF-REQUEST challenge material or PROOF-RESULT diagnostics SHOULD use Cache-Control: no-store unless the deployment has explicitly made the x401 payload safe to cache. Responses whose selected representation varies based on a PROOF-RESPONSE request header SHOULD use Vary: PROOF-RESPONSE or stronger cache controls.
The x401 payload is visible to the Agent and to intermediaries that can observe decrypted HTTP traffic. A Verifier that encodes state in the request nonce to operate statelessly SHOULD protect that state with a signature, MAC, or authenticated encryption so it cannot be forged or read.
Verification Token Scope§
Verification Tokens SHOULD be short-lived, revocable, and scoped to the accepted x401 proof requirement. When a deployment binds the Agent, a Verifier that issues a token MUST identify the Agent as the token holder and MUST NOT treat the credential subject as the token holder unless the credential subject is also the Agent.
Payment Separation§
Implementations MUST keep proof and payment semantics separate. A proof artifact MUST NOT be treated as payment, and payment satisfaction MUST NOT be treated as proof satisfaction.
Privacy Considerations§
Data Minimization§
Verifiers SHOULD request the minimum attributes or predicates necessary for access control.
Selective Disclosure§
Implementations SHOULD prefer credential formats and proof methods that support selective disclosure or predicate proofs where available.
Correlation Risk§
Repeated use of the same credential or issuer across multiple routes may introduce correlation risk. Implementers SHOULD consider verifier-specific or minimally identifying proof mechanisms where available.
IANA Considerations§
This draft does not yet request any IANA registrations.
Conformance§
A conforming x401 Verifier:
- includes
PROOF-REQUEST: <base64url-x401-payload>when proof is required or advertised - treats the HTTP status code as the overall response status, not as the x401 protocol carrier
- returns a valid base64url-encoded x401 payload in
PROOF-REQUEST - includes a
credential_requirementswhosedigitalmember carries a composed Digital Credentials request whose every entry is a valid OpenID4VP request for the DC API, usingopenid4vp-v1-signed(RECOMMENDED) oropenid4vp-v1-unsigned - for signed requests, sets the
client_idandexpected_originsthat make the Verifier the relying party; for unsigned requests, accounts for the weaker binding per Verifier Binding - includes an OAuth token endpoint for Result Artifact token exchange
- validates credential results for the binding the request mode provides, credential query satisfaction, nonce freshness, issuer trust, status, revocation, and route policy
- accepts Result Artifacts in
PROOF-RESPONSE: <base64url-result-artifact-json>in both inline and by-reference forms, dereferencing a Credential Reference when supplied - accepts Verification Tokens as x401 Token Objects in
PROOF-RESPONSE: <base64url-x401-token-object>when x401 proof satisfaction is separate from existing application authorization - binds x401 Verification Tokens to the same caller context as any existing application authorization credential present on the request
- returns
PROOF-RESULT: <base64url-x401-error-object>when reporting x401 proof or token errors - expresses accepted issuers as DCQL
trusted_authoritiesinside the request, rather than as an x401-specific issuer list - keeps payment separate under
402 Payment Required
A conforming x401 Agent:
- recognizes
PROOF-REQUEST - decodes and processes the x401 payload from the
PROOF-REQUESTfield value - treats
credential_requirementsas the Verifier-composed credential request and does not modify it - obtains a credential result for
credential_requirementsby invoking it through a native credential method, relaying it to a Credential Manager or remote service, or acquiring a remotely generated result - packages the credential result as a Result Artifact, inline or as a Credential Reference
- retries the same protected route that produced the x401 proof requirement with
PROOF-RESPONSEcarrying a Result Artifact,PROOF-RESPONSEcarrying an x401 Token Object, or a deployment-defined upgradedAuthorizationtoken - preserves existing application
Authorizationcredentials when carrying a separate x401 Verification Token as an x401 Token Object inPROOF-RESPONSE - recognizes x401 Error Objects in
PROOF-RESULTas x401 proof failures regardless of the HTTP status code - treats its own interpretation of the request’s
trusted_authoritiesas advisory acquisition guidance until the Verifier validates the result - supports separate handling of
402 Payment Required
Open Questions & Future Additions§
The items below are questions intentionally left open at the time of the initial release of this specification. The community is invited to contribute concrete proposals, start discussions, provide examples, create interop profiles, add test vectors, and submit reference implementations so future versions of x401 can turn the right patterns into a robust protocol that accounts for all necessary concerns.
Additional Credential Request Types§
The credential_requirements member is a CredentialRequestOptions value — the argument to navigator.credentials.get() — so it is structurally able to carry request types beyond the Digital Credentials API, such as the publicKey member used for WebAuthn / passkey assertions or other navigator.credentials.get() members. This version of x401 intentionally specifies processing for the digital member only. The container was named and shaped to make this broadening additive rather than breaking: a future version can define a new member without renaming credential_requirements or restructuring the payload.
What this version does not specify, and what future work should define, includes: how the Credential Result, Result Artifact, and delivery in PROOF-RESPONSE generalize to non-presentation results such as a WebAuthn assertion; how Verifier Binding is expressed for request types with their own binding model (for example WebAuthn’s rpId, challenge, and origin in clientDataJSON); how the OAuth token exchange represents a non-VP proof as the subject token; and how a Verifier signs or otherwise authors each additional request type. Until a future version specifies a given member, a Verifier MUST NOT rely on it and an Agent MUST treat only the digital member as defined by this specification.
Machine Payments and 402§
How should x401 compose with machine-payment protocols that use 402 Payment Required? Future work should define recommended sequencing for proof-first, payment-first, and parallel proof/payment flows while preserving the current boundary that x401 handles proof and 402 handles payment.
Open design questions include how a payment artifact should be bound to the x401 route, method, request nonce, any Agent Identifier, and payment quote; whether a 402 response should reference the same proof context as the earlier x401 response; how proof freshness and payment settlement freshness interact; and how protocols such as x402, AP2, ACP, or UCP should carry or reference x401 proof satisfaction without redefining it.
Fully Autonomous Delegation§
How can a Holder fully delegate identity or proof capability to an Agent so the Agent can satisfy x401 proof requirements without per-request human or Credential Manager interaction? The current working assumption is that this could be addressed by Verifiable Intent or a similar signed delegation artifact that gives an Agent Identifier bounded authority to act within a user-approved scope.
Future work should specify how such a delegation is created, presented, constrained, revoked, audited, and bound to the Agent’s request-signing key. It should also answer whether delegated proof is represented as a credential requested by DCQL, as a companion artifact to the Result Artifact, as OAuth or GNAP delegation state, or as a separate profile layered above x401.
Adding Agent Authentication§
When is agent binding necessary on top of x401? The base protocol binds the returned result to the Verifier as relying party and makes Agent binding OPTIONAL; see Agent Binding Options. Some deployments need no Agent binding at all; others may bind pairwise, ephemeral, enterprise-local, or token-bound Agent Identifiers, or require Web Bot Auth, HTTP Message Signatures, mutual TLS, DPoP, SPIFFE, WIMSE, DID-based authentication, or another caller-authentication profile.
Future work should define when Agent binding is mandatory, how a Verifier advertises acceptable caller-authentication mechanisms in or near the x401 proof requirement, which status codes and headers are used when caller authentication is missing, and how mismatches are reported across HTTP caller identity, Result Artifact agent_id, Verification Token holder identity, and delegation evidence.
References§
Normative§
- RFC 9110: HTTP Semantics
- RFC 2119: Key words for use in RFCs to Indicate Requirement Levels
- RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words
- RFC 4648: The Base16, Base32, and Base64 Data Encodings
- RFC 6749: The OAuth 2.0 Authorization Framework
- RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage
- RFC 7519: JSON Web Token
- RFC 8414: OAuth 2.0 Authorization Server Metadata
- RFC 8693: OAuth 2.0 Token Exchange
- RFC 9101: OAuth 2.0 JWT-Secured Authorization Request (JAR)
- OpenID for Verifiable Presentations 1.0, including its profile for use over the W3C Digital Credentials API
- OpenID for Verifiable Credential Issuance 1.0
- W3C Digital Credentials API
- OpenID Federation 1.0
- ETSI TS 119 612: Trusted Lists
Informative§
- RFC 8705: OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens
- RFC 9421: HTTP Message Signatures
- RFC 9449: OAuth 2.0 Demonstrating Proof of Possession
- RFC 9635: Grant Negotiation and Authorization Protocol
- W3C Verifiable Credentials Data Model
- Web Bot Auth Architecture
- HTTP Message Signatures Directory
- WIMSE Workload-to-Workload Authentication with HTTP Signatures
- SPIFFE X.509-SVID
- SPIFFE JWT-SVID
- Verifiable Intent Specification Overview
Appendix A: Minimal Payload§
{
"scheme": "x401",
"version": "0.2.0",
"credential_requirements": {
"digital": {
"requests": [
{
"protocol": "openid4vp-v1-signed",
"data": {
"request": "eyJhbGciOiJFUzI1NiIsInR5cCI6Im9hdXRoLWF1dGh6LXJlcStqd3QifQ..."
}
}
]
}
},
"oauth": {
"token_endpoint": "https://bank.example.com/oauth/token"
}
}
The example uses the RECOMMENDED signed form; the OpenID4VP request inside credential_requirements.digital carries the credential query (dcql_query), the nonce, and — for a signed request — the client_id and expected_origins. The JSON object is carried in the PROOF-REQUEST header as:
PROOF-REQUEST: <base64url-minimal-x401-payload>
Appendix B: Design Summary§
x401 is best understood as:
- an HTTP route proof requirement protocol
- carrying a composed, Verifier-authored Digital Credentials request at
credential_requirements.digitalrather than a decomposed set of values for the Agent to assemble - making the Verifier the relying party through a signed request (RECOMMENDED), bound via the request signature,
client_id, andexpected_origins, or allowing an unsigned request bound to the invoking origin and nonce when the invocation origin cannot be declared in advance - letting the Agent execute the request natively, relay it to a Credential Manager or remote service, or acquire a remotely generated result and replay the route
- reserving the top level of the payload, alongside the native request, for x401-specific values not yet expressible inside a native Digital Credentials request
- carrying the credential result inline or by reference on retry
- treating VP response encryption and Agent binding as OPTIONAL
- allowing caller authentication, request signing, workload identity, and delegation evidence to layer over the protected-route request
- optionally exchanging verified Result Artifacts for OAuth access tokens
- optionally pointing to OpenID4VCI issuance sources
- remaining orthogonal to payment protocols
- composing with
402 Payment Requiredrather than absorbing it
Appendix C: x401 Request Object JSON Schema§
The JSON Schema below describes the x401 proof requirement payload. The same schema applies whether the payload is carried as a base64url-encoded value in the PROOF-REQUEST header or embedded in HTML inside a <data value="application/json;x401=proof-required" hidden> element. The schema is published at the URL referenced by the $schema member of an embedded <data> element: https://x401.id/spec/schemas/request.json.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://x401.id/spec/schemas/request.json",
"title": "x401 Proof Requirement Payload",
"description": "Schema for an x401 proof requirement payload as defined by the x401 specification.",
"type": "object",
"required": ["scheme", "version", "credential_requirements", "oauth"],
"properties": {
"$schema": {
"type": "string",
"format": "uri",
"description": "Optional informational marker. When the payload is embedded in HTML as a <data> element, this SHOULD be set to https://x401.id/spec/schemas/request.json so that content processors can recognize the object as an x401 proof requirement."
},
"scheme": {
"type": "string",
"const": "x401",
"description": "MUST be the string \"x401\"."
},
"version": {
"type": "string",
"description": "The x401 payload version."
},
"credential_requirements": {
"type": "object",
"required": ["digital"],
"description": "The Verifier-composed credential request, a CredentialRequestOptions value usable as the argument to navigator.credentials.get(). This version of x401 specifies the digital member; additional navigator.credentials.get() request types (e.g. publicKey) may be specified in future versions.",
"properties": {
"digital": {
"type": "object",
"required": ["requests"],
"description": "The composed Digital Credentials request, a DigitalCredentialRequestOptions value (the value the digital member of navigator.credentials.get() takes).",
"properties": {
"requests": {
"type": "array",
"minItems": 1,
"description": "Digital Credentials request entries. Every entry MUST be a valid OpenID4VP request for the DC API.",
"items": {
"type": "object",
"required": ["protocol", "data"],
"properties": {
"protocol": {
"type": "string",
"enum": ["openid4vp-v1-signed", "openid4vp-v1-unsigned"],
"description": "The DC API protocol identifier. openid4vp-v1-signed is RECOMMENDED; openid4vp-v1-unsigned is permitted. See Verifier Binding."
},
"data": {
"type": "object",
"description": "Protocol-specific request data. For openid4vp-v1-signed, an object carrying the signed OpenID4VP request (e.g. { \"request\": \"<JWT>\" }); for openid4vp-v1-unsigned, the OpenID4VP request parameters directly.",
"properties": {
"request": {
"type": "string",
"description": "The signed OpenID4VP request (a JWT-Secured Authorization Request); present for openid4vp-v1-signed."
}
}
}
}
}
}
}
}
}
},
"oauth": {
"type": "object",
"required": ["token_endpoint"],
"properties": {
"token_endpoint": {
"type": "string",
"format": "uri",
"description": "OAuth 2.0 token endpoint where the Agent can exchange a Result Artifact for a Verification Token."
},
"audience": {
"type": "string",
"description": "Optional OAuth token exchange audience value the Agent should request."
},
"resource": {
"type": "string",
"format": "uri",
"description": "Optional OAuth token exchange resource value the Agent should request."
}
},
"additionalProperties": false
},
"request_id": {
"type": "string",
"description": "Optional Agent-visible hint: a stable verifier-defined identifier for the proof template."
},
"satisfied_requirements": {
"type": "array",
"items": { "type": "string" },
"description": "Optional Agent-visible reuse hint: stable verifier-defined identifiers for the reusable proof requirements this proof would satisfy."
},
"return_uri": {
"type": "string",
"format": "uri",
"description": "Optional. Added by a relaying intermediary (never by the Verifier) to tell a remote handler where to POST the credential result. See Relayed Delivery to a Remote Handler."
},
"payment": {
"type": "object",
"description": "Informational hint that payment is additionally required. Does not replace 402 Payment Required.",
"properties": {
"required": {
"type": "boolean",
"description": "Whether payment is additionally required."
},
"scheme_hint": {
"type": "string",
"description": "Hint naming the expected payment protocol."
},
"notes": {
"type": "string",
"description": "Human-readable notes."
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}