Skip to content

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 1Version 2
The 402JSON body { x402Version: 1, accepts: [...] }header PAYMENT-REQUIRED, base64 JSON { x402Version: 2, resource: { url }, accepts: [...] }
Each requirementscheme, network (base), maxAmountRequired, asset, payTo, resource, maxTimeoutSeconds, extrascheme, network (CAIP-2, eip155:8453), amount, asset, payTo, maxTimeoutSeconds, extra
The paid requestheader X-PAYMENT, base64 JSONheader PAYMENT-SIGNATURE, base64 JSON
The answerheader 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

TypeScript
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');
Python
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:

  1. reads the requirements (the PAYMENT-REQUIRED header, else the JSON body);
  2. picks the first exact requirement in a token it recognises (USDC on Base, Base Sepolia, Ethereum, Arbitrum, Optimism, Polygon and Avalanche, by Circle’s published contract addresses; add others with assets), and converts the atomic amount exactly;
  3. asks Immiscible: asset, network, amount, payTo as the recipient, and the resource URL;
  4. on allow, checks the signed receipt covers exactly that transfer, calls your pay for the header, and retries with X-PAYMENT (version 1) or PAYMENT-SIGNATURE (version 2);
  5. 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.