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

Source: https://immiscible.fly.dev/docs/guides/x402

[x402](https://github.com/coinbase/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](https://github.com/coinbase/x402/blob/main/specs/x402-specification-v1.md), [version 2](https://github.com/coinbase/x402/blob/main/specs/x402-specification-v2.md)):

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

```ts
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.
