Requests a resourceOrdinary HTTP — no account, no payment yet
Returns payment termsHTTP 402 Payment Required
Retries with signed paymentSame request, payment payload attached
Verifies, settles, and servesPayment checks out → resource body
Crosshatch is an Effect toolkit for requiring and accepting payment through x402.
No accounts, no API keys, no separate billing system.
$ pnpm add crosshatch
Requests a resourceOrdinary HTTP — no account, no payment yet
Returns payment termsHTTP 402 Payment Required
Retries with signed paymentSame request, payment payload attached
Verifies, settles, and servesPayment checks out → resource body
How Crosshatch fits
Same protocol whether you’re buying or selling: pay as a client layer, charge as route logic.
Wire a payer into your Effect client. On 402, Crosshatch pays and retries automatically — your program stays an ordinary Effect workflow.
Declare a price on the route. Crosshatch returns 402 until paid, settles, then serves — no separate billing product for the charge itself.
Get started
Two short guides — client auto-pay and merchant require-and-settle.
Pay for x402 resources from Effect programs — auto-pay on 402, retry, continue.
Client quickstartAdd payments to your routes — declare price, settle, serve with minimal code.
Merchant quickstartIn code
Highlighted lines are Crosshatch. Payment handling sits in a layer so business logic stays ordinary Effect. The client sample hits one public x402 endpoint; the same composition works for any paid HTTP resource.
Payment disappears into the layer. On 402, pay and retry inside the same Effect program.
Full 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"
// PayerLive: wallet + facilitator client (see quickstart)
// import { PayerLive } from "./payer"
// Any x402 HTTP resource — this endpoint is one public example
const PaidApiLive = 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(PaidApiLive.pipe(
Layer.provide(Http402.layerClient.pipe(
Layer.provide(PayerLive),
)),
)),
Effect.runFork,
)
Return 402 until paid, settle, then serve — the charge lives on the route.
Full merchant quickstart →import {
Facilitator, Http402, KnownAssets,
Payload, Required, Requirements,
} from "crosshatch"
import { Effect } from "effect"
import { HttpServerResponse } from "effect/unstable/http"
const paidRoute = Effect.gen(function* () {
const payload = yield* Payload.Payload
if (!payload) {
const required = yield* Required.make`API access`.pipe(
Required.accept(
Requirements.asset(KnownAssets.USDC, {
amount: 0.01,
// recipients: { eip155: { 8453: payTo } }
}),
),
)
return yield* Http402.require({ required })
}
const settlement = yield* Facilitator.settle({ payload })
return HttpServerResponse.text("The paid resource.").pipe(
Http402.addResponseHeader(settlement),
)
})