The micropayment revolution is underway
Payment is just another layer.
x402 is an open protocol that lets an HTTP request pay for itself — a few cents of USDC, settled in seconds, no account on either side. Crosshatch brings it to Effect: pay as a layer, charge as a route.
$ pnpm add crosshatch
Effect x402 TypeScript Apache-2.0
Your program sees one request that succeeds — Crosshatch handles the 402 challenge, payment, and settlement. Step through it →
New to x402?
One paid request, step by step
The whole protocol is those four wire moves. Click through what each one carries — real x402 headers, decoded.
Step 1 · Client — your code
An ordinary HTTP call. No account, no key — the client knows nothing about this API yet.
POST /v1/reports HTTP/1.1
host: api.example.com
accept: application/json
content-type: application/json
{ "topic": "eurusd-volatility" }
// no auth header, no api key —
// the server will answer with a price.
you write: client.execute(HttpClientRequest.post("/v1/reports"))
Step 2 · Merchant — Http402.require
Instead of a body, the response carries terms — what this costs and how to pay.
Decoded from the payment-required header:
{
"x402Version": 2,
"resource": { "description": "One market report" },
"accepts": [{
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x8335…2913",
"amount": "10000", // $0.01 — USDC has 6 decimals
"payTo": "0x1c7d…9a42",
"maxTimeoutSeconds": 300
}]
}
merchant writes: yield* Http402.require({ required })
Step 3 · Client — Http402.layerClient
Crosshatch picks an option it can satisfy, signs a transfer authorization, and retries —
your program is still awaiting its first response.
Decoded from the payment-signature header:
{
"x402Version": 2,
"accepted": { "scheme": "exact", "network": "eip155:8453", … },
"payload": {
"signature": "0x74f9…01b3",
"authorization": {
"from": "0x9e04…77c1", // client wallet → merchant
"to": "0x1c7d…9a42",
"value": "10000", "nonce": "0x3fb2…c4d8"
}
}
}
Crosshatch handles: Http402.layerClient + your Payer
Step 4 · Merchant — Facilitator.settle
A facilitator — the settlement service Crosshatch calls for you — verifies
the signature and settles on-chain. Money moves now, not at month-end.
The response is the API body, plus a receipt in payment-response:
{
"success": true,
"transaction": "0xa9c1…e77b", // settlement receipt
"network": "eip155:8453",
"payer": "0x9e04…77c1"
}
// …plus the body your program asked for:
{ "report": "…" }
merchant writes: yield* Facilitator.settle({ payload })
Merchant · charge
From free to paid in one flip
A price in dollars, a typed payload, one yield to settle — that’s the entire paywall. Crosshatch turns HTTP 402 into Effect route logic so USDC moves with the request, not at month-end. Flip the switch.
+import { Facilitator, Http402, KnownAssets, Payload, Required, Requirements } from "crosshatch" import { Effect } from "effect"
import { HttpServerResponse } from "effect/unstable/http"
export const route = Effect.gen(function* () {+ const payload = yield* Payload.Payload+ if (!payload) {+ const required = yield* Required.make`One market report`.pipe(+ Required.accept(Requirements.logical(KnownAssets.Usd.USDC, {+ amount: 0.01,+ recipients: { eip155: { 8453: payTo } },+ })),+ )+ return yield* Http402.require({ required })+ }+ const settlement = yield* Facilitator.settle({ payload }) const report = yield* Reports.generate()
return HttpServerResponse.json(report)+ .pipe(Http402.addResponseHeader(settlement)) })
The green lines are the entire diff. Merchant quickstart →
- API keys
- plan tiers
- usage metering
- invoices
- failed-payment emails
- webhook reconciliation
What’s left is a price on the route.
Client · pay
Pay with a client Layer
Provide Http402.layerClient. On a 402 the client selects an option, signs, and
retries — no payment loop, no API key. Highlighted lines are Crosshatch; the rest is
ordinary Effect.
An LLM call, bought with USDC instead of an API key
No signup in this program — an agent can buy data, tools, or model calls mid-task. Same composition for any x402-priced HTTP API.
Client quickstart →import { OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai-compat"
import { Http402 } from "crosshatch"
import { Effect, Layer } from "effect"
import { LanguageModel } from "effect/unstable/ai"
import { PayerLive } from "./payer" // wallet + signing (see quickstart)
// Any x402-priced API. No account. No key.
const Blockrun = OpenAiLanguageModel.layer({
model: "deepseek/deepseek-chat",
}).pipe(
Layer.provide(OpenAiClient.layer({ apiUrl: "https://blockrun.ai/api/v1" })),
)
LanguageModel.generateText({
prompt: "Hello from Crosshatch.",
}).pipe(
Effect.provide(Blockrun.pipe(
Layer.provide(Http402.layerClient.pipe(Layer.provide(PayerLive))),
)),
Effect.runFork,
)
Why Crosshatch
You write the call. Crosshatch absorbs the rail.
The demos are short because Crosshatch handles the hard parts of x402 — token math, wire format, settlement, and retry.
Requirements.logical(USDC, { amount: 0.01 })
- token addresses
- decimals
- signing domains
Price in dollars — Crosshatch resolves addresses, decimals, and domains on every network it supports, EVM and Solana.
yield* Payload.Payload
- header decoding
- schema validation
- typed failures
Every x402 message and chain identifier is schema-checked — malformed payments die at the boundary, not mid-settle.
Http402.require({ required })
- base64 headers
- response shapes
- versioning
payment-required shapes, headers, and versioning — correct 402s without hand-rolling the wire format.
Facilitator.settle({ payload })
- signature checks
- replay protection
- on-chain settlement
Verify and settle via hosted facilitator or your own — same API either way.
Http402.layerClient
- 402 interception
- option selection
- signing
- retry
Intercept, select, sign, retry as one Effect Layer — typed errors and tracing included.
crosshatch profile add
- key generation
- keychain encryption
- funding
Keygen, keychain encryption, and funding — a dev wallet in one command.
Native to the Effect stack you already use: HttpClient · server middleware · Effect RPC (pay per message) · Effect AI · effect-atom.
Still skeptical?
Crypto, cards, custody
Is this crypto? Do I need to care about blockchains?
There’s no protocol token and nothing to speculate on. x402 settles in stablecoins like
USDC — dollar-denominated tokens designed to stay near $1 — so you can price in cents
and fractions of a cent, machine to machine, in seconds. You think in dollars; for most
apps the crypto surface is KnownAssets.Usd.USDC and an amount.
Why not just use Stripe?
Stripe is built for people with cards and accounts — subscriptions, invoices, a floor around ~30¢ per charge. x402 is for per-request prices in cents or less, paid by programs as well as humans, with no signup on either side. Many products will use both: Stripe for the dashboard, x402 for the API.
Doesn’t paying per request make everything slow?
The 402 exchange adds one round trip before the first paid response. Verifying a payment is a signature check, not a chain confirmation wait. Delivery is your call per route: serve immediately and settle in the background, or hold until settlement confirms — which lands in seconds.
Where does the money actually live?
Clients pay from a wallet — a keypair holding USDC, not an account with a payments
provider (crosshatch profile add makes one for development). Merchants
receive at the address on the payment requirement: yours on settlement, in seconds.
No holding account. No payout schedule.
Get started
Pick a side. Or both.
Pay for x402-priced APIs
Set up a payer, add the client Layer, and call paid APIs from any Effect program — or hand your agent a wallet.
Client quickstartCharge for your routes
Declare a price, return 402 until paid, settle, and serve — in one route handler.
Merchant quickstartnpm package Apache-2.0 x402 you hold the keys
Building with a coding agent? Point it at crosshatch.dev/llms-full.txt — the full docs as one file.