HTTP Unauthorized: Identity Required

The verifiable identity protocol
for the internet.

HTTP shipped with authentication. It never shipped with proof. x401 is the protocol that completes the challenge layer of the web.

Created by
DB
Daniel Buchner (Proof)
AL
Adam Lemmon (Proof)
DL
Darren Louie (Proof)
in collaboration with
BA
Bhushit Agarwal (Circle)
JL
Jacky Lao (Lightspark)
OT
Oliver Terbu (MATTR)
TL
Tobias Looker (MATTR)
TC
Tim Cappalli (Okta)
NS
Nick Steele (OpenAI)
SS
Steven Sacia (Visa)
TE
Tony England (Visa)
Proof-Requiredx401
StandardDraft 0.1.0
LicenseApache-2.0

Identity negotiation for the agentic web.

HTTP shipped in the nineties with a way to challenge a caller for credentials: 401 Unauthorized, WWW-Authenticate. For thirty years, that was enough. Every system that needed to know who the caller really was wrote its own answer in the app layer.

That model assumed a person at a browser. The agent era breaks the assumption. Calls now come from workloads, autonomous agents, and delegated service identities. The route on the other end needs to know what the caller can prove, not just what token it carries. x401 makes that proof a first-class part of the protocol.

Read the full spec
Use cases

What can x401 do?

Now agents can respond to an identity challenge on behalf of the person they serve.

Banking

Open an account with an identity that satisfies KYC and AML.

Step-up auth

Change wire instructions, require additional proof not just a login.

Prescriptions

Refill a script tied to a verified patient identity, not typed in SSNs.

Organization Credentials

Verify an active board association before joining a meeting.

Age gate

Place a bet by answering “over 18”. No DOB ever leaves the wallet.

Ticketing

Sell concert tickets to people, not bots, with proof of personhood.

Travel

Request identity and a passport when booking travel.

See all demos

Try each scenario against a real 401 challenge.

Trust list

A curated registry, not a research project.

x401 can ask for any claim. The registry curates the issuers the industry agrees to accept, so when a route demands proof there's already a trusted source for it. The same protocol works for age, identity, license, employment, or ownership. One handshake. Proof for anything.

x401 Trust ListSchema · v0.1.0
Open for intake
01 · Legal IdentityA specific verified human.
National IDBank IDPayment IDOpenID networks
Open
02 · EmploymentTied to an employer, workforce, or agency.
Corporate registryWorkforceAgency
Open
03 · MembershipBelonging to an association or community.
Industry bodyAlumni officeProfessional societyTrade group
Open
04 · AccreditationA professional license or qualification.
Medical boardLegal barEngineering councilCPA boardRealtor board
Open
05 · Financial StatusAccredited investor status or beneficial ownership.
Accredited investorBeneficial owner
Open
Are you an issuer?Request to join the trust list →
Protocol flow

When an agent makes a request that requires ID.

Four steps between the request and the response.

01At the verifier

Proof requirement

The route says no, and says why. The response carries the proof requirement in the Proof-Required header: which credentials, which challenge, which trust list.

02At the wallet

Wallet presentation

The agent forwards the requirement to its wallet as an OpenID4VP request, DCQL query intact. The wallet returns a verifiable presentation signed for the agent.

03Back at the verifier

Retry with proof

The agent retries the route with the presentation in the Authorization header. The verifier checks four things: challenge match, agent match, credential satisfaction, issuer trust.

04Optional

Token exchange

The agent trades the presentation for a short-lived Bearer token. Every request after this one skips the proof step entirely.

On the wire.

A verifier returns 401 with a base64url-encoded x401 payload. The agent decodes it, fetches a presentation from its wallet, and retries. The entire handshake runs over plain HTTP request and response headers.

verifier reply · 401JSON
HTTP/1.1 401 Unauthorized
Proof-Required: eyJ2ZXJzaW9uIjoiMC4xLjAi...
Cache-Control: no-store
decoded payloadJSON
{
  "scheme": "x401",
  "version": "0.1.0",
  "proof": {
    "presentation_protocol": "openid4vp",
    "dcql_query": {
      "credentials": [{
        "id": "identity",
        "format": "dc+sd-jwt",
        "meta": { "vct_values": ["https://credentials.example.com/identity"] },
        "claims": [
          { "path": ["given_name"] },
          { "path": ["family_name"] },
          { "path": ["address"] },
          { "path": ["birth_date"] },
          { "path": ["kyc_status"], "values": ["verified"] }
        ]
      }]
    },
    "challenge": {
      "value": "x401:aHR0cHM6Ly9yZXN0L2Nx...",
      "expires_at": "2026-05-08T18:45:00Z"
    },
    "oauth": {
      "token_endpoint": "https://research.example.com/oauth/token"
    },
    "issuers": {
      "trust_establishment_url":
        "https://research.example.com/.well-known/x401/trust/identity-..."
    }
  }
}
agent · fetch with x401TS
import { fetchWithX401 } from "@x401/client";

const res = await fetchWithX401("https://api.fintech.example/v1/accounts", {
  method: "POST",
  body: JSON.stringify({ plan: "business" }),
  agentId: "did:web:agent.example",
  wallet,                          // any OpenID4VP-capable wallet
  trustList: "auto",               // resolve issuers from proof.issuers
});

// Auto-handles: decode payload → build OpenID4VP request →
//               wallet presentation → retry with VP Artifact.
console.log(await res.json());
retryHTTP
POST /v1/accounts HTTP/1.1
Host: api.fintech.example
Proof-Presentation: eyJhZ2VudF9pZCI6ImRpZDp3ZWI6YWdlbnQuZXhhbXBsZS..."
Where x401 fits

It composes. It doesn't compete.

x401 is a response format. It carries existing credential standards into a single HTTP challenge, so a route can declare its proof requirement. No new stack to invent.

PresentationOpenID4VPThe wallet replies with a Verifiable Presentation that satisfies the route's challenge.
Query languageDCQLThe challenge tells the wallet exactly which credentials and claims to surface. No guessing.
Credential formatSD-JWT · mDoc · JWT-VCFormat-agnostic. The route declares which formats it accepts; x401 stays out of the credential body.
Browser handoffDC APIWhen the caller is a browser, the OS-mediated wallet picker handles user consent natively.
Token & identityOAuth 2.0 · DIDsAfter proof, the route returns a route-scoped token. The caller identity is bound to a specific agent DID.
Trust rootsDIF CTEThe challenge points at a Credential Trust Establishment URL so the wallet knows which issuers the route honors.
The response format is the contract. Everything underneath is whatever standard you already trust.
Sibling protocols

x401 + x402.

Two protocols. One boundary. x402 lets agents pay. x401 lets agents prove who's paying. Stack the two and a single HTTP exchange handles every regulated payment flow on the web. KYC'd onboarding. Travel Rule transfers. Age-gated commerce. Sanctions screening. Anywhere money has to know your name.

401 UnauthorizedProof

Who are you?

The route asks the agent to reveal identity, organizational membership, or another credential. The agent presents a verifiable credential, the verifier validates it, the route opens.

HTTP/1.1 401 Unauthorized
Proof-Required: <base64url-x401-payload>
402 Payment RequiredPayment

Can you pay?

The route asks the agent to settle a payment. The agent presents a payment artifact, the verifier confirms it, the route opens.

HTTP/1.1 402 Payment Required
Payment-Required: payment-object
x401+x402

Run them in sequence: prove first, then pay. Run them in parallel: both required, in any order. Or use either on its own. Neither artifact is accepted as the other.

Get in touch

Meet the creators

x401 is built in the open by Proof and a group of contributors from across the identity ecosystem. Want to talk through your use case, collaborate on the spec, or just say hello? The team would love to hear from you.

Created by