x401: HTTP Proof Requirement Protocol§
Status:
Version: 0.1.0
- Editors:
- Daniel Buchner - Proof
- Darren Louie - Proof
- 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-REQUIREDHTTP header field to carry proof requirements - the
PROOF-PRESENTATIONHTTP header field to carry proof presentations or reusable proof-satisfaction tokens - the
PROOF-RESPONSEHTTP 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
- Digital Credentials Query Language (DCQL), directly or through OpenID4VP
scopealiases, to describe the credential requirements for a protected route - OpenID for Verifiable Presentations (OpenID4VP) for the Agent-created presentation request
- OAuth 2.0 for optional exchange of a verified presentation for an access token
- DIF Credential Trust Establishment for verifier-approved issuer policy and non-authoritative acquisition guidance
- OpenID for Verifiable Credential Issuance (OpenID4VCI) for resolving credential issuer metadata when the referenced trust document identifies OpenID4VCI issuers
The x401 payload is not a fully composed OpenID4VP Authorization Request. The Verifier returns the presentation protocol, credential query requirement, Verifier Challenge value, issuer trust reference, and OAuth token exchange metadata the Agent needs in order to create its own OpenID4VP Authorization Request for a Wallet.
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, and OpenID4VCI define interoperable mechanisms for requesting presentations, evaluating credential requirements, 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 credential query requirement for the route, expressed as either a DCQL query or an OpenID4VP
scopevalue that represents a DCQL query - carries a Verifier Challenge value composed from the Verifier’s identifier and a cryptographic nonce
- gives the Agent the information required to create a valid OpenID4VP Authorization Request for its Wallet
- includes OAuth token exchange metadata for Agents that want a reusable access token after proving
- optionally includes a DIF Credential Trust Establishment URL that can help Agents discover acceptable credential issuers
- composes with, but does not subsume, payment protocols
In the typical flow, an Agent receives an x401 proof requirement from a Verifier, creates a wallet-facing Presentation Request that includes the Verifier’s Credential Query Requirement and Verifier Challenge, receives a verifiable presentation from a Wallet, and retries the original protected route. An Issuer Trust List can help the Agent discover credentials or issuers, 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.
- Allow Verifiers to send proof requirements without composing the Agent’s OpenID4VP Authorization Request.
- Require the Agent to bind the wallet presentation to its own identifier while preserving a verifier-originated Verifier Challenge.
- Reuse OpenID4VP credential query mechanisms, DCQL, OAuth, and OpenID4VCI.
- Remain separate from payment semantics.
- Allow issuer discovery by reference to verifier trust policy without listing issuers inline in the x401 payload.
- Support stateless verifier deployments by allowing Verifier Challenge correlation context to be encoded in verifier-protected nonce values.
- Allow additional caller authentication, request signing, and delegation artifacts to compose with x401 without making any one agent identity system mandatory.
Non-Goals§
x401 does not:
- define a new credential format
- replace OpenID4VP
- replace OpenID4VCI
- define the transport used to deliver an OpenID4VP request to a Wallet
- define a payment protocol
- require all Verifiers to maintain server-side session state
- define a universal agent authentication protocol
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, creates a wallet-facing OpenID4VP presentation request, receives proof material from a Wallet, and retries the protected route. The Agent uses an Agent Identifier for client binding.
- Agent Identifier:
- An identifier for the Agent that the Verifier can bind to the HTTP caller. This specification does not register a single Agent Identifier scheme; a Verifier policy MUST define which schemes it accepts, including any accepted DID, HTTPS origin, domain-bound client identifier, or certificate-bound identifier schemes.
- Holder:
- The subject or entity that possesses credentials and can authorize a Wallet to present proof.
- Wallet:
- Software capable of receiving an OpenID4VP presentation request from an Agent and returning a verifiable presentation result authorized by a Holder.
- Presentation Request:
- An OpenID4VP Authorization Request created by the Agent. It includes the Credential Query Requirement from the x401 payload as either
dcql_queryorscope, uses the Verifier Challenge value asnonce, and identifies the Agent as the OpenID4VP client. - Credential Query Requirement:
- The verifier-supplied credential selection requirement in the x401 payload. It is expressed as exactly one of
proof.dcql_queryorproof.scope. - DCQL Requirement:
- A Credential Query Requirement expressed directly as
proof.dcql_query. - Scope Requirement:
- A Credential Query Requirement expressed as
proof.scope. A Scope Requirement is an OpenID4VPscopestring whose scope value, or values, represent well-defined DCQL queries according to OpenID4VP and an applicable profile, ecosystem, Wallet configuration, or verifier policy recognized by the Wallet and Verifier. - Verifier Challenge:
- A nonce-bearing object generated by the Verifier. It contains a challenge value and expiry. The challenge value is composed from, or bound to, the Verifier’s identifier and a fresh cryptographic nonce. The Agent uses this exact value as the OpenID4VP
noncein its wallet-facing Presentation Request. - Issuer Trust List:
- A verifier-controlled DIF Credential Trust Establishment document that identifies the issuers, authorities, roles, activities, credential schemas, or credential types the Verifier accepts for a proof requirement. The same document can also help Agents discover where qualifying credentials may be obtained.
- VP Artifact:
- A retry artifact containing the Wallet’s presentation result and the x401 metadata needed by the Verifier to validate proof fulfillment for an Agent Identifier, encoded for use in a
PROOF-PRESENTATIONrequest header or OAuth token exchange. - 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 VP 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-PRESENTATIONwhen the route already usesAuthorizationfor existing application authentication. - x401 Token Object:
- A JSON object carried as a base64url value in
PROOF-PRESENTATIONto 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-REQUIREDheader field. - x401 Error Object:
- A JSON object carried as a base64url value in the
PROOF-RESPONSEheader 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, the Agent turns the requirements into a wallet-facing OpenID4VP request, the Agent presents the Wallet’s presentation 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-REQUIRED 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-REQUIRED: <base64url-x401-payload>
Cache-Control: no-store
The Agent decodes the x401 payload and creates its own OpenID4VP Authorization Request for the Wallet. It preserves the Verifier’s credential query requirement, as either dcql_query or scope, and uses the exact Verifier Challenge value as the OpenID4VP nonce; see Agent-Generated Verifiable Presentation for details.
{
"response_type": "vp_token",
"client_id": "decentralized_identifier:did:web:agent.example",
"nonce": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"dcql_query": {
"credentials": [
{
"id": "financial_customer",
"format": "jwt_vc_json"
}
]
},
"response_uri": "https://agent.example/wallet/callback/7c9e"
}
After the Wallet returns a presentation result, the Agent packages it as a VP Artifact for protected-route retry. The VP Artifact carries the Agent Identifier, Verifier Challenge, and Wallet-returned proof material; see Verifiable Presentation Delivery for details.
{
"agent_id": "did:web:agent.example",
"challenge": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"vp_token": "<wallet-returned-vp-token>"
}
The Agent can submit the same VP 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-PRESENTATION 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:vp_artifact&
subject_token=<base64url-vp-artifact-json>
Primary Flow§
In the primary x401 flow, the Agent is the HTTP caller and is assumed to have access to a Wallet, 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-REQUIRED: <base64url-x401-payload>
- The Agent decodes the x401 payload and extracts the OpenID4VP presentation protocol, Credential Query Requirement, Verifier Challenge, and OAuth token endpoint.
- The Agent creates a wallet-facing OpenID4VP Presentation Request. The request MUST use the x401 Verifier Challenge as its
noncevalue, MUST include the x401 Credential Query Requirement using the same OpenID4VP query parameter name supplied by the x401 payload, and MUST identify the Agent as the OpenID4VP client. - The Wallet returns a presentation result to the Agent.
- The Agent retries the same protected route that produced the x401 proof requirement with one of:
- a VP Artifact in a
PROOF-PRESENTATIONrequest header, - a Verification Token carried as an x401 Token Object in a
PROOF-PRESENTATIONrequest 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 VP Artifact in a
- The Verifier validates the VP Artifact or Verification Token.
- 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 VP 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, Agent-generated verifiable presentation, verifiable presentation 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-REQUIRED 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-REQUIRED 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-REQUIRED: <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-REQUIRED |
Proof is required, advertised, or not yet satisfied for the route | Decode the x401 payload from the PROOF-REQUIRED field value |
Any response with PROOF-RESPONSE 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-REQUIRED |
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-REQUIRED |
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-REQUIRED: <base64url-x401-payload>. The PROOF-REQUIRED 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-REQUIRED: <base64url-x401-payload>
Cache-Control: no-store
An x401 proof requirement carried in PROOF-REQUIRED 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-REQUIRED 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-RESPONSE: <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
- presentation audience does not match the Agent Identifier
- challenge value does not match the expected verifier identifier and nonce
- 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 and reserves Verifier Challenge for the nonce-bearing proof.challenge object.
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-REQUIRED: <base64url-json>
PROOF-PRESENTATION: <base64url-json>
PROOF-RESPONSE: <base64url-json>
The defined x401 proof header fields are:
| Header field | Direction | Decoded payload |
|---|---|---|
PROOF-REQUIRED |
Verifier to Agent | x401 Payload |
PROOF-PRESENTATION |
Agent to Verifier | VP Artifact or x401 Token Object |
PROOF-RESPONSE |
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-PRESENTATION carries a VP Artifact, the request is a direct proof presentation. When it carries an x401 Token Object, the request is presenting a reusable proof-satisfaction token issued after prior verification.
PROOF-RESPONSE is the Verifier-to-Agent response information channel for x401-specific results and diagnostics. This specification defines the x401 Error Object for failed proof presentation 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-REQUIRED and PROOF-RESPONSE 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-PRESENTATION value.
When browser-based JavaScript needs to read PROOF-REQUIRED or PROOF-RESPONSE from a cross-origin response, the response needs CORS exposure for those fields. When browser-based JavaScript sends PROOF-PRESENTATION cross-origin, the server needs to allow the PROOF-PRESENTATION request header through CORS preflight.
x401 Payload§
A x401 payload is a single JSON object encoded in the PROOF-REQUIRED 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.1.0",
"proof": {},
"payment": {}
}
Member Definitions§
| Name | Definition |
|---|---|
scheme |
REQUIRED. Value MUST be the string "x401". |
version |
REQUIRED. The x401 payload version. |
proof |
REQUIRED. Contains the presentation protocol, Credential Query Requirement, Verifier Challenge, issuer trust reference, OAuth token exchange metadata, and reusable requirement identifiers. |
payment |
OPTIONAL. Describes that payment is additionally required, without replacing 402 semantics. |
Proof Object§
The proof object gives the Agent the verifier-supplied values it needs to create its own Presentation Request and later retry the protected route.
General Structure§
{
"presentation_protocol": "openid4vp",
"dcql_query": {
"credentials": [
{
"id": "financial_customer",
"format": "jwt_vc_json",
"meta": {
"type_values": ["FinancialCustomerCredential"]
},
"claims": [
{
"path": ["credentialSubject", "assurance_level"],
"values": ["VC-AL2", "VC-AL3"]
}
]
}
]
},
"challenge": {
"value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"expires_at": "2026-05-06T18:45:00Z"
},
"oauth": {
"token_endpoint": "https://bank.example.com/oauth/token"
},
"issuers": {
"trust_establishment_url": "https://bank.example.com/.well-known/x401/trust/financial-customer-v1"
},
"request_id": "proof-template-financial-customer-v1",
"satisfied_requirements": [
"urn:example:x401:satisfaction:financial-customer:v1"
]
}
The same proof object can instead use an OpenID4VP scope value when an applicable profile, Wallet configuration, or verifier policy recognized by the Wallet and Verifier defines that scope as a credential query:
{
"presentation_protocol": "openid4vp",
"scope": "financial_customer",
"challenge": {
"value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"expires_at": "2026-05-06T18:45:00Z"
},
"oauth": {
"token_endpoint": "https://bank.example.com/oauth/token"
},
"issuers": {
"trust_establishment_url": "https://bank.example.com/.well-known/x401/trust/financial-customer-v1"
},
"request_id": "proof-template-financial-customer-v1",
"satisfied_requirements": [
"urn:example:x401:satisfaction:financial-customer:v1"
]
}
Members§
| Name | Definition |
|---|---|
presentation_protocol |
REQUIRED. Identifies the wallet-facing presentation protocol the Agent MUST use. For this version of x401, the value MUST be "openid4vp". |
dcql_query |
OPTIONAL. The explicit DCQL Requirement for the protected route. The Agent MUST include this value as the dcql_query in its Presentation Request when dcql_query is present. Exactly one of dcql_query or scope MUST be present. |
scope |
OPTIONAL. The Scope Requirement for the protected route. The Agent MUST include this value as the OpenID4VP scope in its Presentation Request when scope is present. Exactly one of dcql_query or scope MUST be present. |
challenge |
REQUIRED. The Verifier Challenge object. The Agent MUST include challenge.value as the OpenID4VP nonce in its Presentation Request. |
oauth |
REQUIRED. OAuth token exchange metadata. It gives the Agent the endpoint, and any optional resource or audience values, needed to exchange a VP Artifact for a Verification Token. |
issuers |
OPTIONAL. References verifier-approved issuer trust policy for the proof requirement. When present, this object MUST identify an Issuer Trust List and MUST NOT list approved issuers inline. |
request_id |
OPTIONAL. A stable verifier-defined identifier for the proof template. This value can be reused across Verifier Challenge instances and routes when they ask for the same proof requirement. |
satisfied_requirements |
OPTIONAL. An array of stable verifier-defined identifiers for the reusable proof requirements that will be marked satisfied if this proof is fulfilled. |
The proof object MUST contain exactly one Credential Query Requirement: either dcql_query or scope. It MUST NOT contain both, and it MUST NOT omit both.
When scope is used, each scope value MUST be an alias for a well-defined DCQL query in the applicable OpenID4VP profile, ecosystem, Wallet configuration, or verifier policy recognized by the Wallet and Verifier. If a single scope string contains multiple scope values, the DCQL Credential Query id values and claim id values represented by those scope values MUST be unique across the combined query. The Verifier MUST be able to validate the returned credentials against the credential policy represented by the scope value and MUST NOT treat the returned scope string itself as proof satisfaction.
Verifier Challenge Members§
| Name | Definition |
|---|---|
value |
REQUIRED. The exact value the Agent MUST use as the OpenID4VP nonce in its Presentation Request. |
expires_at |
REQUIRED. The time after which the Verifier will reject the Verifier Challenge. The value MUST be an RFC 3339 timestamp. |
OAuth Members§
| Name | Definition |
|---|---|
token_endpoint |
REQUIRED. OAuth 2.0 token endpoint where the Agent can exchange a VP 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.
Issuers Members§
| Name | Definition |
|---|---|
trust_establishment_url |
REQUIRED when issuers is present. An HTTPS URL for a DIF Credential Trust Establishment document that describes the issuers, authorities, roles, activities, credential schemas, or credential types the Verifier approves for this proof requirement. |
Verifier-approved issuers MUST NOT be enumerated inline in the x401 payload. A Verifier that wants to expose issuer approval policy to Agents SHOULD publish a DIF Credential Trust Establishment document and provide its URL in proof.issuers.trust_establishment_url.
The Agent MAY use the Issuer Trust List to select candidate credentials or guide acquisition. The Verifier MUST enforce issuer trust during proof validation and MUST NOT rely on the Agent’s interpretation of the Issuer Trust List.
Proof Object Example§
{
"presentation_protocol": "openid4vp",
"dcql_query": {
"credentials": [
{
"id": "financial_customer",
"format": "jwt_vc_json",
"meta": {
"type_values": ["FinancialCustomerCredential"]
}
}
]
},
"challenge": {
"value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"expires_at": "2026-05-06T18:45:00Z"
},
"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 Challenge Construction and Correlation§
When a Verifier creates a Verifier Challenge, it MUST bind the Verifier Challenge instance to the protected resource context needed to evaluate the retry. This context includes the requested method, route or resource identifier, Credential Query Requirement, Verifier identifier, cryptographic nonce, expiration time, expected Agent Identifier rules, and accepted retry mechanisms.
This binding MAY be stored server-side, but x401 does not require that storage. A Verifier MAY instead encode the binding into the nonce segment as verifier-protected Verifier Challenge state.
Verifier Challenge State and Verification§
A Verifier MUST be able to determine that a returned challenge value is one it issued for the protected route being retried. Matching the x401 prefix and decoded Verifier identifier is not sufficient.
The Verifier MUST support at least one of the following Verifier Challenge correlation models:
- Stored Verifier Challenge state. The nonce segment is an opaque handle to a Verifier Challenge record stored by the Verifier. The record MUST include or reference the route, method, Credential Query Requirement, policy, Verifier identifier, nonce, expiration time, expected Agent Identifier rules, accepted retry mechanisms, and replay status needed to evaluate the retry.
- Verifier-protected nonce state. The nonce segment is a self-contained or referencing value protected by the Verifier with a signature, MAC, or authenticated encryption. It MUST allow the Verifier to authenticate the returned challenge value and reconstruct the expected route, method, Credential Query Requirement, policy, Verifier identifier, nonce, expiration time, expected Agent Identifier rules, and replay requirements.
In both models, the nonce segment MUST contain or be bound to at least 128 bits of cryptographic randomness. The Agent treats the nonce segment as opaque and MUST NOT parse it.
When validating a returned VP Artifact or OAuth token exchange request, the Verifier MUST use the nonce segment to recover or authenticate the expected Verifier Challenge context. It MUST reject the returned artifact if the returned challenge value is not exactly the issued or reconstructed challenge value, if the Verifier Challenge is expired, if the nonce is unknown or fails verifier-protected authentication, if the route or method does not match, or if replay policy fails.
Verifier Challenge Format§
The proof.challenge object carries the value the Agent gives to the Wallet and the time after which the Verifier will reject it:
{
"value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"expires_at": "2026-05-06T18:45:00Z"
}
The canonical value is:
x401:<base64url-utf8-verifier-id>:<nonce>
The Verifier Challenge value has three colon-delimited segments:
| Name | Segment | Definition |
|---|---|---|
| Prefix | x401 |
REQUIRED. Domain-separates x401 Verifier Challenge values from other nonce or challenge values a Wallet may receive. |
| Verifier identifier | <base64url-utf8-verifier-id> |
REQUIRED. The Verifier identifier encoded as UTF-8 and then base64url without padding. The decoded value is typically an HTTPS origin, domain-based identifier, or DID controlled by the Verifier. |
| Nonce | <nonce> |
REQUIRED. An opaque verifier-generated nonce segment encoded as base64url without padding. It MUST either identify stored Verifier Challenge state or carry verifier-protected Verifier Challenge state. It MUST contain or be bound to at least 128 bits of cryptographic randomness. |
The Verifier constructs the entire value, including the x401: prefix. The encoded verifier identifier and nonce MUST omit padding. The nonce segment MUST let the Verifier triangulate back to the exact requirements presented to the Agent, including route, method, Credential Query Requirement, policy, expiration, expected Agent Identifier rules, accepted retry mechanisms, and replay status. The payload does not duplicate the verifier identifier or nonce as separate fields. The Verifier MUST reject a presentation if the challenge value, embedded or expected verifier identifier, nonce, or expiration does not match the Verifier Challenge expected for the protected route.
The Agent MUST use proof.challenge.value exactly as the OpenID4VP nonce in its Presentation Request. The Agent MUST NOT add the x401: prefix, re-encode, hash, trim, normalize, or transform this value before giving it to the Wallet.
Stateless Continuation§
x401 deployments MAY make the interaction between the protected resource and the token endpoint stateless by making the challenge value nonce-state-encapsulating.
In a stateless deployment:
- The x401 payload carried in
PROOF-REQUIREDcontains only information the Agent needs to continue, such as the Credential Query Requirement, Verifier Challenge, issuer trust reference, token exchange metadata, and payment hints. - The nonce segment of the Verifier Challenge MUST carry or reference the verifier-protected Verifier Challenge state needed to validate a VP Artifact or issue a Verification Token.
- A Verification Token, when issued, MUST be verifier-protected and carry or reference the route, policy, Agent binding, expiration, and satisfied requirements needed for later protected-route evaluation.
- The protected resource server and token endpoint MAY be separate components if they share the keys, policies, or verification services needed to validate these artifacts.
Stateless processing does not remove every need for storage. Verifiers MAY still keep server-side state for replay detection, token revocation, audit, rate limiting, or one-time Verifier Challenge enforcement. If a deployment requires strict one-time-use Verifier Challenges, it generally needs replay state shared by the components that accept returned challenge values.
Credential Acquisition Guidance§
When a Verifier wants to disclose which issuers, authorities, roles, activities, credential schemas, or credential types can satisfy a proof requirement, it provides a proof.issuers.trust_establishment_url.
That URL points to a Issuer Trust List. The Agent MAY use the referenced document to discover candidate credentials or credential issuers, and the Wallet MAY use it to help select credentials that are likely to satisfy the Verifier.
If the referenced Issuer Trust List identifies OpenID4VCI Credential Issuer Identifiers, Agents resolve those issuer identifiers using the OpenID4VCI issuer metadata discovery rules. See OpenID4VCI Section 12.2.2: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-final.html.
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.
Agent-Generated Verifiable Presentation§
This leg defines how the Agent turns the x401 payload into a wallet-facing OpenID4VP Authorization Request. The Agent keeps the Verifier’s Credential Query Requirement and Verifier Challenge value intact, uses the Verifier Challenge value as the OpenID4VP nonce, and identifies itself as the OpenID4VP client.
{
"response_type": "vp_token",
"client_id": "decentralized_identifier:did:web:agent.example",
"nonce": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"dcql_query": {
"credentials": [
{
"id": "financial_customer",
"format": "jwt_vc_json"
}
]
},
"response_uri": "https://agent.example/wallet/callback/7c9e"
}
When the x401 payload contains proof.scope instead of proof.dcql_query, the Agent uses that value as the OpenID4VP scope and does not include dcql_query:
{
"response_type": "vp_token",
"client_id": "decentralized_identifier:did:web:agent.example",
"nonce": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"scope": "financial_customer",
"response_uri": "https://agent.example/wallet/callback/7c9e"
}
OpenID4VP Boundary and Reuse§
x401 stays intentionally narrow. It defines the HTTP proof requirement at the protected route and the payload that carries proof requirements, Verifier Challenge material, issuer trust references, token exchange metadata, and payment hints. It does not define the Wallet transport or require the Verifier to become the Wallet-facing presentation requester.
The protocol boundary is:
- x401 governs the protected-route exchange that carries the
PROOF-REQUIREDheader containing the x401 payload. - The x401 payload supplies a Credential Query Requirement and Verifier Challenge. It is not a complete OpenID4VP Authorization Request and MUST NOT be treated as one.
- The Agent creates the wallet-facing Presentation Request as an OpenID4VP Authorization Request.
- The Agent’s Presentation Request MUST include the exact x401 Credential Query Requirement as either
dcql_queryorscope, matching the query member present in the x401 payload, and MUST use the exact x401 Verifier Challenge value asnonce. - The Agent’s Presentation Request MUST identify the Agent as the OpenID4VP client.
- x401 resumes when the Agent retries the original protected route with a VP Artifact or Verification Token.
proof.issuerscan help Agents discover acceptable issuers, but it never delegates verification behavior to the Agent. When the referenced Issuer Trust List identifies OpenID4VCI issuers, Agents resolve those issuers using OpenID4VCI issuer metadata discovery.
If a deployment uses a Wallet, browser, device, or application that is separate from the Agent, that deployment MUST use an OpenID4VP response delivery mechanism that returns the presentation result to the Agent. The response delivery endpoint, such as an OpenID4VP redirect_uri or response_uri, MUST be controlled by the Agent.
Agent-Created Presentation Request§
The Agent MUST provide the Wallet with a valid OpenID4VP Authorization Request. The Agent-created request MUST include at least:
- a valid
response_typefor returning a verifiable presentation; - a
client_idthat identifies the Agent Identifier used with the protected-route request; - the exact x401
proof.challenge.valueas the OpenID4VPnonce; - the exact x401 Credential Query Requirement as the OpenID4VP
dcql_queryorscope, matching the query member present in the x401 payload; - a
response_uriorredirect_urithat delivers the Wallet’s presentation result to the Agent; - any required
client_metadata, key material, supported VP formats, encryption preferences, or request signing information required by the Wallet or by the OpenID4VP profile in use; - any Agent-generated
statevalue needed for Agent-side correlation.
The x401 payload does not contain a separate presentation_request object. The fixed request construction rules above are part of the protocol and are derived from proof.presentation_protocol; repeating them in each x401 payload would be redundant. Verifier-specific proof requirements should be expressed through the Credential Query Requirement, Verifier Challenge, OAuth metadata, or the Verifier’s normal validation policy.
The Agent MAY add Wallet UX metadata, request signing, encryption parameters, or stricter local handling requirements, but it MUST NOT weaken or omit the x401 Credential Query Requirement and MUST NOT alter the x401 Verifier Challenge value.
The Verifier’s identifier appears inside the Verifier Challenge. The Agent MUST NOT use the Verifier’s identifier as the Wallet-facing client_id unless the Agent and Verifier are the same entity for that presentation transaction.
OpenID4VP Request Construction Rules§
x401 Agents:
- MUST create an OpenID4VP Authorization Request that is valid under OpenID4VP.
- MUST preserve the exact OpenID4VP parameter names and MUST NOT define x401 aliases for
response_uri,redirect_uri,response_mode,nonce,state,dcql_query,scope, orclient_metadata. - MUST include an OpenID4VP
client_idfor the Agent. - MUST include a valid OpenID4VP
response_typefor the chosen flow. - MUST include exactly one OpenID4VP credential query parameter:
dcql_querycontaining the exact x401proof.dcql_querywhen present, orscopecontaining the exact x401proof.scopewhen present. - MUST use
noncecontaining the exact x401proof.challenge.value. - MUST arrange for the Wallet response to return to the Agent.
- SHOULD use short expiry windows when a signed request object is used.
- MAY use
request_urior signed request objects for the Agent-created Presentation Request, but those objects are created by the Agent and are not supplied by the x401 payload.
Verifiable Presentation Delivery§
This phase of the protocol defines how the Agent packages the Wallet’s presentation result and presents it back to the Verifier. The Agent either retries the protected route directly with a VP Artifact or uses that same VP Artifact in the OAuth token exchange described in the next leg.
POST /accounts/applications HTTP/1.1
Host: bank.example.com
PROOF-PRESENTATION: <base64url-vp-artifact-json>
VP Artifact§
After the Wallet returns a presentation result, the Agent packages the result as a VP Artifact for protected-route retry or OAuth token exchange.
General Structure§
{
"agent_id": "did:web:agent.example",
"challenge": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"request_id": "proof-template-financial-customer-v1",
"vp_token": "<wallet-returned-vp-token>",
"presentation_submission": {}
}
Members§
| Name | Definition |
|---|---|
agent_id |
REQUIRED. The Agent Identifier represented by the OpenID4VP client_id in the Agent-created Presentation Request. |
challenge |
REQUIRED. The exact proof.challenge.value from the x401 payload. |
request_id |
OPTIONAL. The x401 proof.request_id, when present. |
vp_token |
REQUIRED. The verifiable presentation material returned by the Wallet. |
presentation_submission |
OPTIONAL. The OpenID4VP presentation submission metadata returned by the Wallet when applicable. |
state |
OPTIONAL. Agent-generated correlation state returned by the Wallet when applicable. |
The Verifier MUST NOT treat the agent_id, challenge, or request_id values inside the VP Artifact as authoritative by themselves. They are carried so the Verifier can correlate the presentation with its expected Verifier Challenge context. The Verifier remains responsible for authenticating the Agent Identifier and validating the presentation binding.
x401 Error Object§
When a Verifier reports that an x401 proof presentation failed or could not be processed, it returns a PROOF-RESPONSE header whose payload is a base64url-encoded x401 Error Object.
General Structure§
{
"scheme": "x401",
"version": "0.1.0",
"error": "invalid_presentation",
"error_description": "The presentation did not satisfy the route proof requirement.",
"request_id": "proof-template-financial-customer-v1",
"challenge": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ"
}
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 proof.request_id, when the error can be correlated to a proof template. |
challenge |
OPTIONAL. The Verifier Challenge value, when the error can be safely correlated to a challenge. |
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-PRESENTATION 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.1.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 presentation 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-PRESENTATION:
PROOF-PRESENTATION: <base64url-vp-artifact-json>
The PROOF-PRESENTATION value is the base64url-encoded UTF-8 JSON serialization of the VP Artifact, using the same no-padding encoding as the x401 payload.
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-PRESENTATION.
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-PRESENTATION: <base64url-x401-token-object>
A protected route MUST process the supplied PROOF-PRESENTATION value as a proof presentation 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-REQUIRED proof requirement:
- MUST treat the response as a proof requirement.
- MUST extract the
PROOF-REQUIREDfield value and base64url-decode it as a UTF-8 JSON x401 Payload. - MUST validate the decoded payload structure and process the
proofobject. - MUST verify that
proof.presentation_protocolisopenid4vp. - MUST create a wallet-facing OpenID4VP Presentation Request that includes the x401 Credential Query Requirement as
dcql_querywhenproof.dcql_queryis present or asscopewhenproof.scopeis present. - MUST use the exact x401
proof.challenge.valueas the Presentation Request’snonce. - MUST identify itself to the Wallet using an Agent Identifier that can also be bound to the protected-route retry.
- MAY use
proof.issuers.trust_establishment_url, when present, to filter candidate credentials or guide acquisition. - MUST NOT treat any Agent-side interpretation of the Issuer Trust List as proof of verifier acceptance.
- MUST send the OpenID4VP Presentation Request to a Wallet or local presentation subsystem to fulfill the proof requirement.
- MUST package the Wallet’s presentation result as a VP Artifact.
- MUST retry the same route that produced the x401 proof requirement with one of:
- a VP Artifact in a
PROOF-PRESENTATIONrequest header, - a Verification Token carried as an x401 Token Object in a
PROOF-PRESENTATIONrequest 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 VP 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-RESPONSEcarrying 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-REQUIRED: <base64url-x401-payload>when proof is required or advertised. - MUST include a valid base64url-encoded x401 payload in
PROOF-REQUIRED. - 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 set
proof.presentation_protocoltoopenid4vp. - MUST include exactly one Credential Query Requirement: either
proof.dcql_queryorproof.scope. - MUST include a Verifier Challenge in
proof.challenge. - MUST include OAuth token exchange metadata in
proof.oauth. - SHOULD provide
proof.issuers.trust_establishment_urlwhen issuer approval policy is useful to disclose to Agents. - MUST NOT enumerate verifier-approved issuers inline in the x401 payload.
- MUST bind the Verifier Challenge to the route, method, policy, expiration, and expected nonce, and MUST either store or reconstruct enough Verifier Challenge context to recognize the challenge value on return.
- MUST validate VP Artifacts according to the proof validation rules in this specification and the credential format rules it relies upon.
- MUST evaluate issuer trust, status, revocation, and policy constraints independently of any Agent-side interpretation of the Issuer Trust List.
- MUST accept a VP Artifact in a
PROOF-PRESENTATIONrequest header for protected-route retry. - MAY issue a Verification Token through the OAuth token endpoint after validating the presented VP Artifact.
- MUST validate Verification Tokens on protected-route retry according to token scope, audience, expiration, Agent binding, and satisfied requirement metadata, whether the token arrives in
Authorizationor as an x401 Token Object inPROOF-PRESENTATION. - MUST bind any Verification Token carried in
PROOF-PRESENTATIONto the existing application caller, credential, client, key, or Agent Identifier required by the protected route when anAuthorizationheader is also present. - SHOULD return
PROOF-RESPONSE: <base64url-x401-error-object>if proof is presented but policy satisfaction fails or the presentation or token cannot be processed. - MUST use
402 Payment Requiredseparately if payment is required and remains unsatisfied.
Proof Validation§
When a Verifier receives a VP Artifact directly on a protected-route retry or through the OAuth token endpoint, it MUST validate the presentation using the route’s expected x401 Verifier Challenge context.
The Verifier MUST:
- recover the expected Verifier Challenge context, including route, method, policy, Credential Query Requirement, Verifier identifier, nonce, challenge value, expiration, Agent Identifier rules, and replay requirements;
- determine the Agent Identifier for the HTTP caller using the route’s accepted Agent Identifier policy;
- reject the presentation if it cannot bind the HTTP caller to an Agent Identifier acceptable for the route;
- verify that the VP Artifact’s
challengeexactly matches the issued or reconstructed Verifier Challenge; - verify that the Wallet’s presentation proof is cryptographically protected according to the credential and presentation formats in use;
- verify that the presentation is bound to the authenticated Agent Identifier as its audience, client, or intended target;
- verify that the presentation’s OpenID4VP
nonceequals the exactproof.challenge.valuefrom the x401 payload; - verify that the credentials and disclosed claims satisfy the route’s Credential Query Requirement; when the requirement is a
scope, the Verifier MUST validate against the credential policy represented by that scope, not merely the presence of the scope value; - verify issuer trust, credential status, revocation, expiration, assurance, and any additional route policy;
- enforce replay, one-time-use, and expiration requirements for the Verifier Challenge.
The Verifier MUST NOT accept a presentation that is bound only to the Verifier unless the Verifier is also the Agent for the wallet-facing presentation transaction. In the default x401 model, the Wallet presents to the Agent, and the Verifier authorizes resource access by checking that the presentation was intended for the same Agent that is retrying the protected route.
Access Token Acquisition§
This optional leg of the protocol defines the optional exchange of a verified VP Artifact for a reusable Verification Token. The Agent submits the VP 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-PRESENTATION.
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:vp_artifact&
subject_token=<base64url-vp-artifact-json>
OAuth Token Exchange§
An Agent MAY exchange a VP Artifact for a Verification Token at the OAuth token endpoint supplied in proof.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:vp_artifactsubject_token=<base64url-vp-artifact-json>
The Agent SHOULD include the resource or audience value from proof.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:vp_artifact&
subject_token=<base64url-vp-artifact-json>&
resource=https%3A%2F%2Fbank.example.com%2Faccounts%2Fapplications
The token endpoint MAY require normal OAuth client authentication. If client authentication is used, the authenticated OAuth client MUST bind to the same Agent Identifier that appears as the intended audience of the Wallet presentation.
The token endpoint MUST process the submitted VP Artifact using the same proof validation rules that apply to direct protected-route retry. In particular, it MUST verify that:
- the VP is cryptographically valid and bound to the authenticated Agent Identifier;
- the credential material satisfies the Credential Query Requirement for the requested resource;
- the Verifier Challenge value contains or resolves to the token endpoint’s Verifier identifier and the expected cryptographic nonce;
- the Verifier Challenge is not expired, replayed, or outside the route, method, policy, or resource context requested.
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",
"challenge": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"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-PRESENTATION, 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-PRESENTATION:
Authorization: Bearer <existing-application-token>
PROOF-PRESENTATION: <base64url-x401-token-object>
When processing a PROOF-PRESENTATION 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 presentation 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 VP Artifact. The token:
- MUST be issued to the Agent Identifier that was bound during proof validation;
- 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 Challenge, 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;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_challengeor a digest of the challenge value;x401_query_hashor a verifier-defined reference to the Credential Query Requirement that was satisfied.
Reuse Across Routes§
OpenID4VP state, presentation nonce, DCQL Credential Query id values, and OpenID4VP scope values are useful for request-response correlation, holder binding, or wallet-facing query selection inside a single presentation transaction. They are not, by themselves, stable semantic identifiers for cross-route token reuse.
x401 uses proof.request_id and proof.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;
- the token is issued to the current Agent Identifier;
- the token’s accepted proof requirements cover the later route’s
proof.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-REQUIRED: <base64url-x401-payload>
Cache-Control: no-store
Decoded x401 payload, shown for readability:
{
"scheme": "x401",
"version": "0.1.0",
"proof": {
"presentation_protocol": "openid4vp",
"dcql_query": {
"credentials": [
{
"id": "financial_customer",
"format": "jwt_vc_json",
"meta": {
"type_values": ["FinancialCustomerCredential"]
},
"claims": [
{
"path": ["credentialSubject", "assurance_level"],
"values": ["VC-AL2", "VC-AL3"]
}
]
}
]
},
"challenge": {
"value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"expires_at": "2026-05-06T18:45:00Z"
},
"oauth": {
"token_endpoint": "https://bank.example.com/oauth/token"
},
"issuers": {
"trust_establishment_url": "https://bank.example.com/.well-known/x401/trust/financial-customer-v1"
},
"request_id": "proof-template-financial-customer-v1",
"satisfied_requirements": [
"urn:example:x401:satisfaction:financial-customer:v1"
]
}
}
Agent-Created Presentation Request Fragment§
The Agent creates an OpenID4VP Authorization Request. The fragment below shows the x401-required OpenID4VP fields; deployments add the remaining fields required by the selected OpenID4VP response mode, client identifier scheme, and signing or encryption profile.
{
"response_type": "vp_token",
"client_id": "decentralized_identifier:did:web:agent.example",
"nonce": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t: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"]
}
]
}
]
},
"response_uri": "https://agent.example/wallet/callback/7c9e"
}
Scope-Based Payload Variant§
The same route can express the credential query using proof.scope when the value is defined as an OpenID4VP scope query by an applicable profile, Wallet configuration, or verifier policy recognized by the Wallet and Verifier:
{
"scheme": "x401",
"version": "0.1.0",
"proof": {
"presentation_protocol": "openid4vp",
"scope": "financial_customer",
"challenge": {
"value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"expires_at": "2026-05-06T18:45:00Z"
},
"oauth": {
"token_endpoint": "https://bank.example.com/oauth/token"
},
"issuers": {
"trust_establishment_url": "https://bank.example.com/.well-known/x401/trust/financial-customer-v1"
},
"request_id": "proof-template-financial-customer-v1",
"satisfied_requirements": [
"urn:example:x401:satisfaction:financial-customer:v1"
]
}
}
The Agent-created OpenID4VP Authorization Request carries the same scope value and omits dcql_query:
{
"response_type": "vp_token",
"client_id": "decentralized_identifier:did:web:agent.example",
"nonce": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"scope": "financial_customer",
"response_uri": "https://agent.example/wallet/callback/7c9e"
}
Successful Retry With VP Artifact§
POST /accounts/applications HTTP/1.1
Host: bank.example.com
PROOF-PRESENTATION: <base64url-vp-artifact-json>
The decoded VP Artifact contains the Wallet’s vp_token, the Agent Identifier, and the x401 Verifier Challenge correlation values.
Failed Retry With x401 Error§
HTTP/1.1 401 Unauthorized
PROOF-RESPONSE: <base64url-x401-error-object>
Cache-Control: no-store
The decoded x401 Error Object describes the failed proof presentation. 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 Wallet presentation result, the Agent may exchange the VP 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:vp_artifact&
subject_token=<base64url-vp-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-PRESENTATION:
POST /accounts/applications HTTP/1.1
Host: bank.example.com
Authorization: Bearer <existing-application-token>
PROOF-PRESENTATION: <base64url-x401-token-object>
Composable Agent and Entity Identification§
x401 requires the Verifier to bind the Wallet presentation to the Agent Identifier used by the HTTP caller, but 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 x401 Credential Query Requirement, Verifier Challenge, VP Artifact, or
402 Payment Requiredboundary; - allow the Verifier to reject mismatches between the authenticated caller, the OpenID4VP
client_id, the VP Artifactagent_id, and any Verification Token holder identity.
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 VP 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-PRESENTATION 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 x401 Verifier Challenge, Wallet presentation binding, Credential Query Requirement 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 Requirement, 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 proof.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. Even when DPoP is used only at the token endpoint, the endpoint MUST ensure the DPoP key maps to the same Agent Identifier that the Wallet presentation targets.
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.
DID, Signed Request Objects, and Wallet-Facing Client Binding§
An Agent MAY use a DID, HTTPS origin, domain-bound client identifier, certificate-bound identifier, or other verifier-approved scheme as its OpenID4VP client_id. If the Agent signs its OpenID4VP request object, that signature helps the Wallet bind the presentation to the Agent’s key and metadata.
This wallet-facing binding is necessary but not always sufficient. The Verifier also needs an HTTP-layer or token-layer way to determine that the protected-route caller is the same Agent Identifier the Wallet presentation targeted. Deployments can accomplish this by using the same key, a linked key, or a verifier-recognized mapping across the OpenID4VP client identifier, HTTP Message Signature identity, mutual TLS certificate, DPoP key, workload identity, or Verification Token holder identity.
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 VP disclosed through the x401 Credential Query Requirement, 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-REQUIRED 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-REQUIRED, 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-REQUIRED.
Motivation§
Several common client classes do not surface PROOF-REQUIRED 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-REQUIREDon 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-REQUIRED 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.1.0",
"proof": {
"presentation_protocol": "openid4vp",
"dcql_query": {
"credentials": [
{
"id": "financial_customer",
"format": "jwt_vc_json",
"meta": {
"type_values": ["FinancialCustomerCredential"]
}
}
]
},
"challenge": {
"value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"expires_at": "2026-05-06T18:45:00Z"
},
"oauth": {
"token_endpoint": "https://bank.example.com/oauth/token"
}
}
}</data>
Unlike the PROOF-REQUIRED 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-REQUIRED 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-REQUIRED 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, Verifier Challenge handling, and Credential Query Requirement 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-REQUIRED / PROOF-PRESENTATION 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.
Security Considerations§
Replay Prevention§
Verifier Challenges used within x401 SHOULD include fresh nonce values and short expiries. Verifiers SHOULD reject stale or replayed proofs.
Stateless deployments SHOULD use short expiration windows and verifier-protected nonce state. Strict one-time-use Verifier Challenge enforcement requires replay tracking or shared replay-prevention state. Verifiers MUST NOT treat a well-formed challenge value as proof that the Verifier Challenge was issued by that Verifier unless the value is present in stored Verifier Challenge state or authenticates through verifier-protected nonce state.
Agent Binding§
Returned presentations MUST be bound to the Agent Identifier used by the Agent for the protected-route retry. The Verifier MUST reject a VP Artifact if the Wallet presentation is not cryptographically bound to the Agent Identifier it is communicating with.
The Verifier Challenge binds the presentation to the Verifier by embedding the Verifier identifier and expected nonce in the challenge value. The Agent client binding and Verifier Challenge binding are both required.
Issuer Trust§
Acquisition hints MUST NOT be treated as sufficient trust material. Verifiers MUST apply their own trusted issuer policy and validation logic.
When a x401 payload includes proof.issuers.trust_establishment_url, that URL identifies the DIF Credential Trust Establishment document for the proof requirement. The document can help Agents and Wallets choose likely acceptable credentials, but it does not replace verifier-side enforcement. The Verifier MUST independently validate that presented credentials were issued by parties approved under the applicable Issuer Trust List or by the Verifier’s internal issuer policy when no list is disclosed.
Proof Presentation and Token Retry§
Verifiers SHOULD prefer Verification Token retry in multi-step flows to avoid repeatedly transmitting large VP Artifacts. Verifiers that accept direct VP retry SHOULD consider header size limits and SHOULD use compact or referenced proof formats where possible.
Responses carrying fresh PROOF-REQUIRED challenge material or PROOF-RESPONSE 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-PRESENTATION request header SHOULD use Vary: PROOF-PRESENTATION or stronger cache controls.
The x401 payload is visible to the Agent and to intermediaries that can observe decrypted HTTP traffic. Sensitive Verifier Challenge state SHOULD be omitted, stored server-side, or placed only in verifier-protected nonce state.
Verification Token Scope§
Verification Tokens SHOULD be short-lived, revocable, and scoped to the accepted x401 proof requirement. 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-REQUIRED: <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-REQUIRED - sets
proof.presentation_protocoltoopenid4vp - includes a Credential Query Requirement as either
proof.dcql_queryorproof.scoperather than a fully composed OpenID4VP Authorization Request - includes a Verifier Challenge composed from its verifier identifier and a fresh cryptographic nonce
- includes an OAuth token endpoint for VP Artifact token exchange
- validates VP Artifacts for Agent client binding, Verifier Challenge correctness, Credential Query Requirement satisfaction, issuer trust, status, revocation, and route policy
- accepts VP Artifacts in
PROOF-PRESENTATION: <base64url-vp-artifact-json> - accepts Verification Tokens as x401 Token Objects in
PROOF-PRESENTATION: <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-RESPONSE: <base64url-x401-error-object>when reporting x401 proof presentation or token errors - issues Verification Tokens to the Agent when token exchange is used
- optionally includes a DIF Credential Trust Establishment URL for issuer trust and acquisition guidance
- keeps payment separate under
402 Payment Required
A conforming x401 Agent:
- recognizes
PROOF-REQUIRED - decodes and processes the x401 payload from the
PROOF-REQUIREDfield value - requires
proof.presentation_protocolto beopenid4vp - creates its own wallet-facing OpenID4VP Presentation Request
- uses the x401 Verifier Challenge as the Presentation Request
nonce - includes the x401 Credential Query Requirement as the Presentation Request
dcql_queryorscope, matching the query member present in the x401 payload - identifies itself as the OpenID4VP client for the Wallet presentation
- packages the Wallet result as a VP Artifact
- retries the same protected route that produced the x401 proof requirement with
PROOF-PRESENTATIONcarrying a VP Artifact,PROOF-PRESENTATIONcarrying 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-PRESENTATION - recognizes x401 Error Objects in
PROOF-RESPONSEas x401 proof failures regardless of the HTTP status code - treats Issuer Trust List interpretation as advisory until the Verifier validates the presentation
- 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.
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, Verifier Challenge, Agent Identifier, and payment quote; whether a 402 response should reference the same Verifier Challenge 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 Wallet 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 VP Artifact, as OAuth or GNAP delegation state, or as a separate profile layered above x401.
Adding Agent Authentication§
Is adding agent authentication on top of x401 required? The base protocol requires the Verifier to bind the returned presentation to an Agent Identifier, but it does not require a globally authenticated public agent identity for every deployment. Some deployments may accept pairwise, ephemeral, enterprise-local, or token-bound Agent Identifiers; others may 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 stronger agent authentication 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, OpenID4VP client identity, VP 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
- OpenID for Verifiable Credential Issuance 1.0
- DIF Credential Trust Establishment 1.0
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
- W3C Digital Credentials API
- 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.1.0",
"proof": {
"presentation_protocol": "openid4vp",
"dcql_query": {
"credentials": [
{
"id": "proof",
"format": "jwt_vc_json"
}
]
},
"challenge": {
"value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"expires_at": "2026-05-06T18:45:00Z"
},
"oauth": {
"token_endpoint": "https://bank.example.com/oauth/token"
}
}
}
The minimal proof object can use scope instead of dcql_query when the value is a defined OpenID4VP scope query:
{
"scheme": "x401",
"version": "0.1.0",
"proof": {
"presentation_protocol": "openid4vp",
"scope": "proof",
"challenge": {
"value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ",
"expires_at": "2026-05-06T18:45:00Z"
},
"oauth": {
"token_endpoint": "https://bank.example.com/oauth/token"
}
}
}
The selected JSON object is carried in the PROOF-REQUIRED header as:
PROOF-REQUIRED: <base64url-minimal-x401-payload>
Appendix B: Design Summary§
x401 is best understood as:
- an HTTP route proof requirement protocol
- carrying verifier proof requirements rather than a completed OpenID4VP Authorization Request
- requiring the Agent to create the wallet-facing OpenID4VP presentation request
- binding Wallet presentations to the Agent as the OpenID4VP client and to the Verifier through the Verifier Challenge value
- allowing caller authentication, request signing, workload identity, and delegation evidence to layer over the protected-route request
- optionally exchanging verified VP 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-REQUIRED 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", "proof"],
"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."
},
"proof": {
"type": "object",
"required": ["presentation_protocol", "challenge", "oauth"],
"properties": {
"presentation_protocol": {
"type": "string",
"const": "openid4vp",
"description": "Identifies the wallet-facing presentation protocol. For this version of x401 the value MUST be \"openid4vp\"."
},
"dcql_query": {
"type": "object",
"description": "The explicit DCQL Requirement for the protected route. Exactly one of dcql_query or scope MUST be present."
},
"scope": {
"type": "string",
"description": "The Scope Requirement for the protected route, expressed as an OpenID4VP scope string. Exactly one of dcql_query or scope MUST be present."
},
"challenge": {
"type": "object",
"required": ["value", "expires_at"],
"properties": {
"value": {
"type": "string",
"pattern": "^x401:[A-Za-z0-9_-]+:[A-Za-z0-9_-]+$",
"description": "Verifier Challenge value: the literal prefix \"x401\", the base64url-encoded verifier identifier, and an opaque verifier-generated nonce, separated by colons."
},
"expires_at": {
"type": "string",
"format": "date-time",
"description": "RFC 3339 timestamp after which the Verifier will reject the Verifier Challenge."
}
},
"additionalProperties": false
},
"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 VP 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
},
"issuers": {
"type": "object",
"required": ["trust_establishment_url"],
"properties": {
"trust_establishment_url": {
"type": "string",
"format": "uri",
"description": "HTTPS URL for a DIF Credential Trust Establishment document that describes verifier-approved issuers, authorities, roles, activities, credential schemas, or credential types for this proof requirement."
}
},
"additionalProperties": false
},
"request_id": {
"type": "string",
"description": "Stable verifier-defined identifier for the proof template."
},
"satisfied_requirements": {
"type": "array",
"items": { "type": "string" },
"description": "Stable verifier-defined identifiers for the reusable proof requirements that will be marked satisfied if this proof is fulfilled."
}
},
"oneOf": [
{
"required": ["dcql_query"],
"not": { "required": ["scope"] }
},
{
"required": ["scope"],
"not": { "required": ["dcql_query"] }
}
],
"additionalProperties": false
},
"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
}