Guides
x402 payments
Pay x402 resources only when Immiscible allows it. The SDK reads the 402, asks with the payment requirements, and calls your x402 signer only on allow.
x402 is an open protocol for paying for HTTP resources with stablecoins. A server answers 402 Payment Required with what it accepts; the client signs a payment authorisation and asks again with it; a facilitator verifies and settles it on chain.
The signature is made by the client, so that is where Immiscible sits: between the 402 and the signature. Immiscible never signs and never holds the key; your x402 client does, after an allow.
#What the protocol looks like
From the specifications (version 1, version 2):
| Version 1 | Version 2 | |
|---|---|---|
| The 402 | JSON body { x402Version: 1, accepts: [...] } | header PAYMENT-REQUIRED, base64 JSON { x402Version: 2, resource: { url }, accepts: [...] } |
| Each requirement | scheme, network (base), maxAmountRequired, asset, payTo, resource, maxTimeoutSeconds, extra | scheme, network (CAIP-2, eip155:8453), amount, asset, payTo, maxTimeoutSeconds, extra |
| The paid request | header X-PAYMENT, base64 JSON | header PAYMENT-SIGNATURE, base64 JSON |
| The answer | header X-PAYMENT-RESPONSE, base64 { success, transaction, network, payer } | header PAYMENT-RESPONSE, the same |
Amounts are strings in the token’s atomic units: "10000" is 0.01 USDC, which has six decimals.
#The wrapper
import { Immiscible, x402Fetch } from '@immiscible/sdk';
const immiscible = new Immiscible();
const pay = x402Fetch(immiscible, {
pay: ({ requirements, paymentRequired }) => myX402Client.createPaymentHeader(requirements, paymentRequired), // your signer
provenance: [{ source: 'user', detail: 'the analyst asked for this report' }],
});
const res = await pay('https://api.tidewater-data.example/v1/quotes');from immiscible import Immiscible
from immiscible.crypto import x402_request
status, headers, body = x402_request(Immiscible(), "https://api.tidewater-data.example/v1/quotes",
pay=lambda info: my_x402_client.payment_header(info["requirements"]))On a 402 it:
- reads the requirements (the
PAYMENT-REQUIREDheader, else the JSON body); - picks the first
exactrequirement in a token it recognises (USDC on Base, Base Sepolia, Ethereum, Arbitrum, Optimism, Polygon and Avalanche, by Circle’s published contract addresses; add others withassets), and converts the atomic amount exactly; - asks Immiscible: asset, network, amount,
payToas the recipient, and the resource URL; - on allow, checks the signed receipt covers exactly that transfer, calls your
payfor the header, and retries withX-PAYMENT(version 1) orPAYMENT-SIGNATURE(version 2); - reports the transaction hash from the response header when it settles the action.
A refusal raises ImmiscibleDeniedError and pay is never called; a payment held for a person waits, as guard does.
The resource’s website becomes the payment’s merchant, so a workspace block list applies to it, and the payTo address is checked like any other: a lookalike of an address the agent has paid is stopped.
#Tested against
A fake x402 server that answers in the version 1 and version 2 shapes above, in test/crypto-wallets.test.js (TypeScript, against the real server) and packages/immiscible-py/tests/test_crypto.py. A real facilitator and a real chain are not part of the tests.
#Provenance
The price came from the server, but the decision to buy came from the agent’s task. Declare what led to it honestly: with no provenance, Immiscible assumes untrusted content did, and the Rule of Two asks a person about every payment.