# Use x402 and MPP

x402 and the Machine Payments Protocol (MPP) allow an agent to discover and pay
for a service over HTTP. When the agent calls a paid endpoint, the service
answers with HTTP 402 (Payment Required) and a payment challenge. The agent then
pays the challenge in USDC and sends the request again. For example, if your
agent calls Wren's Weather and the service answers with a 402 challenge, your
agent will pay the challenge and repeat the call to get the response.

We record each of these payments as a send from an account that the agent's
[policy](https://docs.catena.com/policies.md) lets it send from. This means the same counterparty list,
spending limits, and approvals apply to x402 and MPP payments as to any other
send.

We support these payments in the [CLI](https://docs.catena.com/connect/cli.md) and the SDK. Note that the
[MCP connector](https://docs.catena.com/connect/mcp.md) does not support x402 or MPP payments.

## Requirements

Before your agent can pay an endpoint, you'll need the following:

- **A wallet account on the challenge's network:** That is Base where enabled,
  or Base Sepolia in the sandbox. Arc support is rolling out.
- **A send capability:** The agent's policy must allow sends from that account.
- **A counterparty for the service:** The address the service asks to be paid
  (`payTo`) must be saved as a counterparty wallet rail.

We do not add `payTo` as a [counterparty](https://docs.catena.com/counterparties.md) for you, because a
402 response is untrusted. A service's address can change, so you may want to
consider letting the agent request new counterparties.

## Pay with the CLI

To pay an x402 endpoint, pass the URL and the wallet account the payment draws
from:

```sh
catena x402 --url https://api.example.com/paid \
  --account acct_... --max-amount 0.25
```

For MPP, run `catena mpp` with the same options:

| Option                   | Use                                                                                                                |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| `--url`                  | The endpoint to call.                                                                                              |
| `--account`              | The wallet account the payment draws from.                                                                         |
| `--max-amount`           | A price ceiling in USD. The CLI refuses a larger challenge before it pays anything. Pass it whenever you have one. |
| `--data`                 | A request body. Implies POST and JSON.                                                                             |
| `--header "Name: Value"` | An extra request header.                                                                                           |

In the example above, `--max-amount 0.25` means the CLI will refuse any
challenge over $0.25 before it pays anything. The
[CLI reference](https://docs.catena.com/cli.md#catena-x402) lists every option.

## Read the result

The command prints JSON. When it needs something from you, it will exit
non-zero, and the JSON names the problem. Below are the results that a run may
return:

| Field                   | Meaning                                                                                                  | Next step                                                                                                  |
| ----------------------- | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `.paid`                 | The payment went through. `.payment` has the intent ID, amount, and recipient. `.body` has the response. | Use the response.                                                                                          |
| `.counterpartyNotFound` | The recipient is not a saved counterparty. Nothing was charged.                                          | Read `.nameIsPlaceholder`, create the counterparty with `.createCommand`, then run the same command again. |
| `.approvalPending`      | The payment is at or above the approval threshold.                                                       | Ask an admin to approve it, then run the same command again. It uses the approval and pays once.           |
| `.networkMismatch`      | The account cannot pay on the challenge's network. Nothing was charged.                                  | Run again with `--account` set to a wallet on one of `.networkMismatch.requiredNetworks`.                  |

When `.nameIsPlaceholder` is `false`, the suggested name is the one the service
gave for itself, and nobody has verified it, so confirm that you trust it before
you create the counterparty. When it is `true`, you'll need to replace the
placeholder name yourself.

If `.bodyTruncated` is `true`, you can read the full body from the file at
`.bodyPath`.

## Never fetch the URL again to see more

> [!WARNING] **Each run can pay again**
>
> Every run of the command can pay again, even for the same URL and price. Do
> not run the command a second time to read more of a response, and do not pay
> again after an unclear result.

To recover a payment that finished after an interruption, run
`catena intents get <id>`. For x402, `.data.paymentCredential.value` holds the
`PAYMENT-SIGNATURE` header that you can retry with while it is still valid.

## Pay with the SDK

In the SDK, you wrap `fetch` so that it pays challenges from a wallet account:

```ts
import { createCatenaClient } from "@catena/sdk"
import { wrapFetchWithX402Payment } from "@catena/sdk/x402"

const client = createCatenaClient()
const fetchWithPayment = wrapFetchWithX402Payment(client, {
  accountId: walletAccountId,
  maxAtomicAmount: 1_000_000n, // 1 USDC
})

const response = await fetchWithPayment("https://api.example.com/paid-thing")
```

`maxAtomicAmount` is in atomic USDC units, so `1_000_000n` is 1 USDC. Responses
without a payable challenge pass through unchanged. For MPP, use
`wrapFetchWithMppPayment` from `@catena/sdk/mpp`. See the [SDK reference](https://docs.catena.com/sdk.md)
for details.

## Evidence from sellers

Some x402 sellers sign their offer and receipt. When they do, Catena verifies
both and keeps them on the intent (as evidence for an audit or a dispute). They
do not change the payment.

## Related

- [Pay for an API call](https://docs.catena.com/recipes/pay-for-an-api-call.md): Your agent pays an x402 endpoint in USDC from a wallet account, and it won't pay more than the price ceiling you set.
- [Manage counterparties and vendors](https://docs.catena.com/counterparties.md): Add the people and businesses you pay, request their payment details, and find the rail ID your agent sends to.
- [Set policies](https://docs.catena.com/policies.md): Decide what each agent can do with its policy, from the accounts it can read and the counterparties it can pay to its spending rules and temporary overrides.
- [SDK reference](https://docs.catena.com/sdk.md): Setup, methods, intent actions, errors, and client options for @catena/sdk, the TypeScript client for the agent API.
