The 401 handshake.
An agent hits a protected URL. The verifier responds with a set of identity requirements to proceed. The wallet signs a presentation. The agent retries. The whole exchange runs on headers an agent already knows how to read — no SDK, no out-of-band channels, no new transport.
Declare the proof requirement
401 Unauthorized with Proof-Required carrying a base64url-encoded payload. Credentials, predicates, challenge, trust list — all in one header.
Build an OpenID4VP request
Copy dcql_query verbatim, use challenge.value as the OpenID4VP nonce, announce client_id, send to the wallet.
Retry with a VP artifact
Wallet signs a presentation; the agent base64url-encodes the artifact and replays with Proof-Presentation. Verifier checks four things at once.
Or exchange for a bearer
For multi-request flows, exchange the VC presentation for an OAuth token which can be used for the duration of the session.
Declare the proof requirement.
Authorization header, the verifier responds 401 Unauthorized. The full proof requirement rides in a single response header, so no body parsing is required. The payload names the credential type, the predicates the wallet must satisfy, the challenge to bind the presentation to, and the trust list that fixes which issuers count.HTTP/1.1 401 Unauthorized
Proof-Required: <base64url-x401-payload>
Cache-Control: no-store{
"scheme": "x401",
"version": "0.1.0",
"proof": {
"presentation_protocol": "openid4vp",
"dcql_query": {
"credentials": [
{
"id": "board_certification",
"format": "jwt_vc_json",
"meta": { "type_values": ["BoardCertificationCredential"] },
"claims": [
{ "path": ["credentialSubject", "boardCertification", "status"], "values": ["active"] }
]
}
]
},
"challenge": {
"value": "x401:<base64url-utf8-verifier-id>:<nonce>",
"expires_at": "2026-05-06T18:45:00Z"
},
"oauth": { "token_endpoint": "https://research.example.com/oauth/token" },
"issuers": {
"trust_establishment_url": "https://research.example.com/.well-known/x401/trust/board-certified-doctor-v1"
},
"request_id": "proof-template-board-certified-doctor-v1",
"satisfied_requirements": ["urn:example:x401:satisfaction:board-certified-doctor:v1"]
}
}Build an OpenID4VP request.
dcql_query verbatim. Drop the verifier's challenge.value in as the OpenID4VP nonce. Announce client_id — this is the agent identity the verifier back-references on the retry.{
"response_type": "vp_token",
"client_id": "did:web:agent.example",
"nonce": "x401:aHR0cHM6Ly9yZXNlYXJjaC5leGFtcGxlLmNvbQ:uX7Vq3mZJH6MeN0qz2L7SQ",
"dcql_query": { "credentials": [...] },
"response_uri": "https://agent.example/wallet/callback/7c9e"
}Retry with a VP artifact.
The wallet returns a signed verifiable presentation. The agent packages it together with the original challenge and its own agent_id, base64url-encodes the JSON, and replays the request with Proof-Presentation set.
Verifier checks four things at once
- Challenge match
- Agent match
- Credential satisfaction
- Issuer trust
If they all pass, the resource is released on this very request.
{
"agent_id": "did:web:agent.example",
"challenge": "x401:aHR0cHM6Ly9yZXNlYXJjaC5leGFtcGxlLmNvbQ:uX7Vq3mZJH6MeN0qz2L7SQ",
"request_id": "proof-template-board-certified-doctor-v1",
"vp_token": "<wallet-returned-vp-token>"
}GET /papers/medical-study-123 HTTP/1.1
Host: research.example.com
Proof-Presentation: eyJhZ2VudF9pZCI6ImRpZDp3...Or exchange for a bearer.
proof.oauth.token_endpoint using OAuth 2.0 Token Exchange. The verifier returns a short-lived Verification Token. From here the agent can use Authorization: Bearer on subsequent requests until the token expires.POST /oauth/token HTTP/1.1
Host: research.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%2Fresearch.example.com%2Fpapers%2Fmedical-study-123{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 300,
"scope": "papers:read",
"x401": {
"agent_id": "did:web:agent.example",
"verifier_id": "https://research.example.com",
"request_id": "proof-template-board-certified-doctor-v1",
"satisfied_requirements": ["urn:example:x401:satisfaction:board-certified-doctor:v1"],
"resource": "https://research.example.com/papers/medical-study-123",
"method": "GET"
}
}Walk this exact flow against six live demos.
Each demo returns a real HTTP 401 with a real Proof-Required challenge. Pop the diagnostic in the corner to see the headers move, then walk through the full four-exchange flow inline.