Bankhaus

x402

Pay the 402. Keep the figure.

x402 is how a server asks to be paid over HTTP: a 402, a payment, a retry. Bankhaus answers it with a confidential transfer, and puts in the X-PAYMENT header the one thing the server needs: a letter that opens that transfer to the server alone. The server learns the figure it asked for. The chain learns there was a transfer. Nobody learns your balance.

  • bankhaus-sealed: a confidential transfer to a handle, the letter as the payload
  • bankhaus-mandate: an agent pays from its vault, caps and allowlist enforced first
  • The payee verifies offline; no facilitator, no key on the server, no account with us
one request, three headers

GET /report/2026-10

402 Payment Required

PAYMENT-REQUIRED bankhaus-sealed · eip155:4663 · 4.20 USDG · bob.bankhaus

confidentialTransfer(bob, Δsender, Δrecipient) no amount in calldata

GET /report/2026-10

X-PAYMENT base64(letter: 4.20 USDG, r, pk_bob, Δrecipient)

200 OK

X-PAYMENT-RESPONSE success · tx 0x7c1e…9b04 · payer gwen.bankhaus

no facilitatorverified offlinefigure off chain

Try one

A 402 you can pay here.

The box on the right is a 402 from a server that wants 4.20 USDG for a report, paid to bob.bankhaus with the sealed scheme. Pay it from your paper account. The X-PAYMENT header appears, and the check under it is exactly what bob's server runs. Then edit the amount in the 402, or a digit in the header, and check again.

  • The payee learns the figure it asked for and nothing else about you
  • The chain sees a transfer with two deltas and no amount
  • The header is a letter: verifiable offline, no key on the server
402 Payment Required · api.exampleeditable
X-PAYMENT · gwen.bankhaus → bob.bankhausempty until you pay

Metered x402, on paper

Pay an API by the second.

The server answers 402 with a stream instead of a price: 0.05 a second for two minutes. Your agent opens the stream with one proof and retries with the letter that opens the rate to the server. The server checks the letter against the stream the chain holds and serves. The meter runs on chain; the server releases what accrued whenever it likes, and the rate stays sealed.

  • One proof per session, not one per request
  • The server learns the rate it asked for and nothing about your balance
  • Stop paying by cancelling: what accrued is theirs, the rest comes back sealed
402 Payment Required · bankhaus-streamno stream yet

Who learns what

01

The payee

The figure it asked for, from a letter it can check itself, and the transfer reference. Not your balance, not your other payments.

02

The chain

That one handle paid another, in a transfer carrying two curve points. There is no amount field to read.

03

Everyone else

An X-PAYMENT header is for one payee: it names them, opens one transfer, and proves nothing to anyone the letter does not name.

The server, live

A paid endpoint runs on this site. Without payment it answers 402 with Bankhaus requirements; with an X-PAYMENT it opens the envelope, verifies the letter offline, then checks on Robinhood Chain that the transfer exists, went to the house and carries exactly that delta, and that it has not paid before.

curl -i https://www.bankhaus.app/api/report
HTTP/1.1 402 Payment Required
PAYMENT-REQUIRED: eyJ4NDAyVmVyc2lvbiI6MSwiYWNjZXB0cyI6W3sic2NoZW1lIjoiYmFua2hhdXMtc2VhbGVkIi…

curl -i -H "X-PAYMENT: $(bankhaus x402 pay 402.json)" https://www.bankhaus.app/api/report
HTTP/1.1 200 OK
X-PAYMENT-RESPONSE: eyJzdWNjZXNzIjp0cnVlLCJ0cmFuc2FjdGlvbiI6IjB4…
import { x402 } from 'bankhaus-sdk';   // any framework, plain data in and out
const need = x402.paymentRequired({ payTo: 'bankhaus', usdg: '0.25', resource: url });
const r = await x402.settle(req.headers['x-payment'], need.body.accepts[0], { viewKey, used });
r.ok && r.onChain ? serve(r.headers) : reply(402, need);

The agent

Through bankhaus-mcp, with keys that stay on its machine. The mandate path asks the contract first and never retries a Refused.

x402_requirements(required)        // what the 402 asks, in cents, and the invoice id
x402_pay(required)                 // sealed: confidential transfer → X-PAYMENT
route(agentId, payee, amount)      // or, under a mandate:
x402_pay(required, mandate, agentId)   // payInvoice from the vault → X-PAYMENT
x402_verify(payment, required)     // the payee's check, and the response header
npx -y bankhaus-mcp34 toolsworks before deployment