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

one paid HTTP request · x402
POST /v1/reports your program — ordinary HTTP
402 Payment Required price: 0.01 USDC
POST /v1/reportscrosshatch payment: wallet-signed
200 OK settled ✓ · response body

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.

one paid request · decoded 1/4

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.

+13 lines
route.ts free
+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 →

What you’d otherwise be building
  • API keys
  • plan tiers
  • usage metering
  • invoices
  • failed-payment emails
  • webhook reconciliation

What’s left is a price on the route.

Per-call prices cards can’t clear Charge $0.002 for one call — no plan minimum, no monthly commitment, no free-tier gymnastics.
Agents as customers An agent can discover your API and pay for it mid-task — no human in the loop, no signup form. Services and scripts pay the same way.
A paywall on the route Price, settle, serve in the handler you already have. Everything else about your API stays as it is.

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 →
pay.ts auto-pay on 402
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.

Client

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 quickstart
Merchant

Charge for your routes

Declare a price, return 402 until paid, settle, and serve — in one route handler.

Merchant quickstart

npm 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.