<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Back Alley Coder]]></title><description><![CDATA[Back Alley Coder]]></description><link>https://backalleycoder.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Back Alley Coder</title><link>https://backalleycoder.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 20 Sep 2026 22:37:44 GMT</lastBuildDate><atom:link href="https://backalleycoder.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Introducing x401: Bringing Proof of Identity to the Agentic Web]]></title><description><![CDATA[HTTP has had a payment status code since 1997. It still doesn't have a native identity one. x401 fixes that.


If you've worked with x402, Coinbase's HTTP-native payment protocol built on the long-dor]]></description><link>https://backalleycoder.hashnode.dev/introducing-x401-bringing-proof-of-identity-to-the-agentic-web</link><guid isPermaLink="true">https://backalleycoder.hashnode.dev/introducing-x401-bringing-proof-of-identity-to-the-agentic-web</guid><dc:creator><![CDATA[Daniel Buchner]]></dc:creator><pubDate>Thu, 25 Jun 2026 19:46:12 GMT</pubDate><content:encoded><![CDATA[<p>HTTP has had a payment status code since 1997. It still doesn't have a native identity one. x401 fixes that.</p>
<img src="https://cdn.hashnode.com/uploads/covers/56b260d50a7ca0c6f70c3c80/14c9e435-8adf-4d6a-b7b7-85b5e8174050.png" alt="" style="display:block;margin:0 auto" />

<p>If you've worked with x402, Coinbase's HTTP-native payment protocol built on the long-dormant <code>402 Payment Required</code> status code, x401 is the identity-layer counterpart. Where x402 answers "how does a server tell an agent what to pay?", x401 answers "how does a server tell an agent what identity proof to provide?"</p>
<p>Today, every API that gates access by <em>who you are</em> rather than whether you have a token builds this plumbing from scratch. x401 defines a standard HTTP mechanism for expressing credential requirements, and a standard way for agents to satisfy them automatically.</p>
<p><strong>The problem, concretely</strong></p>
<p>Here's what today's identity-gated API flow looks like for an agent:</p>
<pre><code class="language-http">POST /accounts/applications HTTP/1.1
Host: bank.example.com → 401 Unauthorized
</code></pre>
<p>The 401 just says "no." Nothing in the response tells the agent <em>what it needs</em> — which credential type, from which issuer, expressing which claims. The agent can't self-serve the identity requirement. A human has to get involved.</p>
<p>Compare to x402:</p>
<pre><code class="language-http">GET /api/resource → 402 Payment Required
X-Payment: {"amount": "0.01", "currency": "USDC", ...}
</code></pre>
<p>An agent reads the 402, pays, retries, done — no human in the loop. x401 brings the same pattern to identity.</p>
<p><strong>How x401 works</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/56b260d50a7ca0c6f70c3c80/9a98191a-e1c8-466b-a33e-acaf2faa8909.png" alt="" style="display:block;margin:0 auto" />

<p>The protocol uses three dedicated HTTP header fields:</p>
<ul>
<li><p><code>PROOF-REQUIRED</code> (server → agent): carries the proof requirement</p>
</li>
<li><p><code>PROOF-PRESENTATION</code> (agent → server): carries the credential presentation</p>
</li>
<li><p><code>PROOF-RESPONSE</code> (server → agent): carries verification results and error details</p>
</li>
</ul>
<p>Step 1 — Initial request:</p>
<p>http</p>
<pre><code class="language-http">POST /accounts/applications HTTP/1.1
Host: bank.example.com
</code></pre>
<p>Step 2 — Server returns a response with <code>PROOF-REQUIRED</code>:</p>
<pre><code class="language-http">HTTP/1.1 401 Unauthorized
PROOF-REQUIRED: &lt;base64url-x401-payload&gt;
Cache-Control: no-store
</code></pre>
<p>The <code>PROOF-REQUIRED</code> value is a base64url-encoded JSON payload. Decoded:</p>
<pre><code class="language-json">{
  "scheme": "x401",
  "version": "0.2.0",
  "credential_requirements": {
    "digital": {
      "requests": [
        {
          "protocol": "openid4vp-v1-signed",
          "data": {
            "request": "eyJhbGciOiJFUzI1NiIsInR5cCI6Im9hdXRoLWF1dGh6LXJlcStqd3QifQ..."
          }
        }
      ]
    }
  },
  "oauth": {
    "token_endpoint": "https://bank.example.com/oauth/token"
  },
  "trust_establishment": "https://bank.example.com/.well-known/x401/trust/financial-customer-v1",
  "request_id": "proof-template-financial-customer-v1"
}
</code></pre>
<p>The key field is <code>credential_requirements</code>. It contains a complete, Verifier-authored Digital Credentials API request — specifically an OpenID4VP request the agent can execute directly:</p>
<pre><code class="language-javascript">const result = await navigator.credentials.get(payload.credential_requirements);
// result =&gt; { protocol: "openid4vp-v1-signed", data: { /* signed VP */ } }
</code></pre>
<p>The Verifier authors and signs this request. The agent does not compose it — it only executes it or relays it to a wallet. Inside the signed request is the actual credential query, expressed in DCQL (Digital Credentials Query Language):</p>
<pre><code class="language-json">{
  "response_type": "vp_token",
  "response_mode": "dc_api",
  "client_id": "x509_san_dns:bank.example.com",
  "expected_origins": ["https://bank.example.com"],
  "nonce": "uX7Vq3mZJH6MeN0qz2L7SQ",
  "dcql_query": {
    "credentials": [
      {
        "id": "financial_customer",
        "format": "jwt_vc_json",
        "meta": { "type_values": ["FinancialCustomerCredential"] },
        "claims": [
          {
            "path": ["credentialSubject", "assurance_level"],
            "values": ["VC-AL2", "VC-AL3"]
          }
        ]
      }
    ]
  },
  "exp": 1746557100
}
</code></pre>
<p>Step 3 — Agent obtains a presentation:</p>
<p>The agent passes <code>credential_requirements</code> to a credential wallet. The wallet constructs an OpenID4VP authorization request, selects the matching credential, and returns a signed Verifiable Presentation bound to the Verifier as relying party.</p>
<p>Step 4 — Retry with <code>PROOF-PRESENTATION</code>:</p>
<pre><code class="language-http">POST /accounts/applications HTTP/1.1
Host: bank.example.com
PROOF-PRESENTATION: &lt;base64url-vp-artifact-json&gt;
</code></pre>
<p>The <code>PROOF-PRESENTATION</code> value is a "VP Artifact":</p>
<pre><code class="language-json">{
  "request_id": "proof-template-financial-customer-v1",
  "response": {
    "protocol": "openid4vp-v1-signed",
    "data": "&lt;wallet-returned-presentation-result&gt;"
  }
}
</code></pre>
<p>Step 5 — Verifier validates and grants access:</p>
<p>The Verifier checks the presentation cryptographically — no shared secrets, no PII in transit. Either the credential validates against the issuer's public keys, or it doesn't. On success, the Verifier returns the protected resource.</p>
<p>If something fails, you get an x401 Error Object back in <code>PROOF-RESPONSE</code>:</p>
<pre><code class="language-json">{
  "scheme": "x401",
  "version": "0.2.0",
  "error": "invalid_presentation",
  "error_description": "Credential from untrusted issuer."
}
</code></pre>
<p><strong>The optional OAuth leg</strong></p>
<p>Rather than submitting a full VP Artifact on every request, the agent can exchange a VP for a short-lived Verification Token via standard OAuth 2.0 Token Exchange:</p>
<pre><code class="language-http">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&amp;
subject_token_type=urn:x401:params:oauth:token-type:vp_artifact&amp;
subject_token=&lt;base64url-vp-artifact-json&gt;
</code></pre>
<p>On success, the Verifier returns a Bearer token usable on subsequent requests without re-presenting credentials.</p>
<p><strong>What are Verifiable Credentials (and why not JWTs)?</strong></p>
<p>Verifiable Credentials are W3C-standardized cryptographically-signed assertions. Unlike ordinary JWTs or session tokens, VCs are:</p>
<ul>
<li><p><strong>Issued by an authoritative third party</strong> — not self-asserted by the agent or the application</p>
</li>
<li><p><strong>Verifiable without a live roundtrip to the issuer</strong> — the issuer's public key is sufficient</p>
</li>
<li><p><strong>Revocable</strong> via a credential status endpoint</p>
</li>
<li><p><strong>Selectively disclosable</strong> — the holder presents only the claims required</p>
</li>
</ul>
<p>x401 doesn't define a new credential format. It works with any format expressible in OpenID4VP: <code>jwt_vc_json</code>, <code>mso_mdoc</code>, <code>sd-jwt</code>, and others.</p>
<p><strong>Status code independence</strong></p>
<p>One design decision worth noting: x401 does not require the server to return <code>401</code>. The <code>PROOF-REQUIRED</code> header is the protocol carrier, not the status code. A server can return <code>200 OK</code> with <code>PROOF-REQUIRED</code> if the response body is still useful without proof, or any <code>4xx</code> when the operation can't proceed. This means x401 composes cleanly with routes that already use <code>WWW-Authenticate</code> for other auth schemes.</p>
<p>Payment and identity stay separate: if payment is also required after proof is satisfied, the Verifier returns <code>402 Payment Required</code> and the agent runs the x402 flow separately.</p>
<p><strong>What's live now</strong></p>
<p>The spec is published at <a href="https://x401.proof.com/spec/latest/">x401.proof.com/spec/latest</a> (v0.2.0). It covers the full protocol: payload structure, header semantics, presentation requirements, VP Artifact format, OAuth token exchange, agent binding options, and security/privacy considerations.</p>
<p>It's an open spec — read it, open issues on GitHub at <a href="https://github.com/x401-protocol/x401">x401-protocol/x401</a>, or reply here if you're working on agent infrastructure and want to coordinate.</p>
<p><em>Proof is the identity provider that authored x401. Learn more at</em> <a href="https://proof.com"><em>proof.com</em></a><em>.</em></p>
]]></content:encoded></item></channel></rss>