> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentbot.raveculture.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# MPP payments

> Pay for API requests with crypto using the Machine Payments Protocol on the Tempo blockchain.

# MPP payments

The Machine Payments Protocol (MPP) enables crypto-native, per-request payments on the Tempo blockchain. MPP is an additive payment method alongside [Stripe](/payments/stripe) — you choose which to use on each request.

## How it works

MPP uses an HTTP 402 challenge/response flow:

1. You send a request to the [gateway](/api-reference/gateway).
2. The server returns `402 Payment Required` with a challenge containing the price, token, and recipient.
3. You sign a Tempo transaction matching the challenge.
4. You retry the request with the signed transaction as a credential in the `Authorization` header.
5. The server verifies the payment on-chain and returns the response with a `Payment-Receipt` header.

## Supported networks

| Network       | Chain ID | RPC URL                          | Status |
| ------------- | -------- | -------------------------------- | ------ |
| Tempo Mainnet | 4217     | `https://rpc.tempo.xyz`          | Active |
| Tempo Testnet | 42431    | `https://rpc.moderato.tempo.xyz` | Active |

<Note>Set `TEMPO_TESTNET=true` in your environment to use the testnet during development.</Note>

## Plugin pricing

Each plugin has a fixed per-request price in USD, settled in pathUSD on Tempo.

| Plugin          | Price per request | Description                  |
| --------------- | ----------------- | ---------------------------- |
| `agent`         | \$0.05            | Agent orchestrator           |
| `generate-text` | \$0.01            | LLM text generation          |
| `tts`           | \$0.03            | Text-to-speech synthesis     |
| `stt`           | \$0.02            | Speech-to-text transcription |

## Payment credential

After receiving a 402 challenge, build and sign a credential to send with your retry:

### Authorization header format

```
Authorization: Payment <JSON credential>
```

### Credential structure

```json theme={"dark"}
{
  "scheme": "Payment",
  "transaction": "0x76...",
  "challengeNonce": "a1b2c3d4e5f6..."
}
```

| Field            | Type   | Description                                                             |
| ---------------- | ------ | ----------------------------------------------------------------------- |
| `scheme`         | string | Always `Payment`                                                        |
| `transaction`    | string | Hex-encoded signed Tempo transaction (prefixed with `0x76` type marker) |
| `challengeNonce` | string | The `nonce` value from the 402 challenge (replay protection)            |

## 402 challenge structure

When a payment is required, the server responds with:

```json theme={"dark"}
{
  "error": "payment_required",
  "message": "Payment required for agent. Choose payment method: Stripe or Tempo MPP.",
  "mpp": {
    "scheme": "Payment",
    "amount": "0.05",
    "currency": "0x20c0000000000000000000000000000000000000",
    "recipient": "0xd8fd0e1dce89beaab924ac68098ddb17613db56f",
    "description": "Agent orchestrator request",
    "nonce": "a1b2c3d4e5f6...",
    "expiresAt": 1742472000000
  },
  "stripe": {
    "checkoutUrl": "/api/v1/payments/stripe/create?plugin=agent",
    "amount": "0.05",
    "currency": "usd"
  }
}
```

### Challenge fields

| Field         | Type   | Description                                                                         |
| ------------- | ------ | ----------------------------------------------------------------------------------- |
| `scheme`      | string | Always `Payment`                                                                    |
| `amount`      | string | Price in USD                                                                        |
| `currency`    | string | Token contract address (pathUSD)                                                    |
| `recipient`   | string | Wallet address to pay                                                               |
| `description` | string | Human-readable description of the charge                                            |
| `nonce`       | string | Unique nonce for this challenge (used for replay protection)                        |
| `expiresAt`   | number | Unix timestamp (ms) when the challenge expires. Challenges are valid for 5 minutes. |

## Verification

The server verifies your credential by checking:

1. The transaction is hex-encoded and starts with the `0x76` type marker.
2. The recipient address matches the expected recipient.
3. The token address matches the expected currency (pathUSD).
4. The amount matches the plugin price (within a 0.0001 tolerance).
5. The nonce matches the original challenge nonce.

If verification fails, the server returns an error with a description of the mismatch.

## Receipt

On success, the server returns:

* A `Payment-Receipt` response header containing the transaction hash.
* The `payment.receipt` field in the JSON response body.

## Client usage

Use `mppFetch` to handle the full 402 flow automatically:

```typescript theme={"dark"}
import { mppFetch } from '@agentbot/sdk';

const result = await mppFetch({
  plugin: 'agent',
  body: { messages: [{ role: 'user', content: 'Hello' }] },
  privateKey: '0xYOUR_PRIVATE_KEY',
  baseUrl: 'https://agentbot.sh',
  testnet: false,
});

if (result.success) {
  console.log(result.data);
  console.log('Receipt:', result.receipt);
}
```

### `mppFetch` options

| Option       | Type    | Required | Default               | Description                                            |
| ------------ | ------- | -------- | --------------------- | ------------------------------------------------------ |
| `plugin`     | string  | Yes      | —                     | Plugin ID to call                                      |
| `body`       | object  | Yes      | —                     | Request body forwarded to the plugin                   |
| `privateKey` | string  | Yes      | —                     | Hex-encoded private key for signing Tempo transactions |
| `baseUrl`    | string  | No       | `https://agentbot.sh` | Agentbot instance URL                                  |
| `stream`     | boolean | No       | `false`               | Request a streaming response                           |
| `testnet`    | boolean | No       | `false`               | Use Tempo testnet instead of mainnet                   |

### `mppFetch` result

| Field     | Type           | Description                   |
| --------- | -------------- | ----------------------------- |
| `success` | boolean        | Whether the request succeeded |
| `data`    | object         | Response body (non-streaming) |
| `stream`  | ReadableStream | Response stream (streaming)   |
| `receipt` | string         | Payment receipt hash          |
| `error`   | string         | Error message on failure      |

## Check MPP support

You can check whether an endpoint supports MPP payments:

```typescript theme={"dark"}
import { checkMppSupport } from '@agentbot/sdk';

const { supported } = await checkMppSupport(
  'https://agentbot.sh/api/v1/gateway'
);
```

The function sends an `OPTIONS` request and checks for a `WWW-Authenticate: Payment` header.

## Sessions

Payment sessions provide off-chain, per-call billing without an on-chain transaction for every request. This reduces latency to sub-100ms per call while still settling on-chain periodically.

### How sessions work

1. **Open a session** — deposit pathUSD (minimum $1.00, maximum $100.00) into escrow via `POST /api/wallet/sessions`.
2. **Make gateway calls** — include `X-Session-Id` and `X-Wallet-Address` headers on your [gateway](/api-reference/gateway) requests with `X-Payment-Method: session`. The gateway auto-debits your session balance using off-chain vouchers — no 402 round-trip needed.
3. **Automatic settlement** — when accumulated vouchers reach \$5.00 or after one hour, the server batches them into a single on-chain transaction.
4. **Close the session** — call `DELETE /api/wallet/sessions?sessionId=ses_...` to settle any remaining vouchers and return unused funds.

<Tip>You can also submit vouchers manually via `POST /api/wallet/sessions/voucher` if you need fine-grained control. When using the gateway with `X-Payment-Method: session`, voucher creation is handled automatically.</Tip>

### Session configuration

| Parameter        | Value    | Description                                                 |
| ---------------- | -------- | ----------------------------------------------------------- |
| Minimum deposit  | \$1.00   | Minimum amount to open a session                            |
| Maximum deposit  | \$100.00 | Maximum amount per session                                  |
| Settle threshold | \$5.00   | Accumulated voucher total that triggers on-chain settlement |
| Settle interval  | 1 hour   | Maximum time between on-chain settlements                   |

### Session states

| State      | Description                                          |
| ---------- | ---------------------------------------------------- |
| `active`   | Session is open and accepting vouchers               |
| `settling` | Vouchers are being settled on-chain (temporary)      |
| `closed`   | Session has been closed and remaining funds returned |

### Session initialization

When opening a session, you must provide a viem `Account` to the `tempo.session()` call. If no account is provided, the SDK throws immediately with a descriptive error message and an example fix. This prevents cryptic errors during channel close.

```typescript theme={"dark"}
import { tempo } from 'mppx';
import { privateKeyToAccount } from 'viem/accounts';

// Correct — pass an Account object
const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY');
const session = tempo.session({ account });

// Incorrect — throws immediately with a helpful error
const session = tempo.session({}); // Error: No Account provided. Pass { account: privateKeyToAccount('0x...') }
```

### Redis store for session state

By default, session state is stored in memory. For production deployments where you need persistence across restarts or across multiple server instances, use the `Store.redis()` adapter. It works with standard Redis clients including ioredis, node-redis, and Valkey.

```typescript theme={"dark"}
import { Store } from 'mppx';
import Redis from 'ioredis';

const redis = new Redis(process.env.REDIS_URL);
const store = Store.redis(redis);
```

The Redis adapter handles `BigInt` serialization automatically, so session voucher amounts are stored and retrieved without data loss.

<Tip>Use the Redis store in any environment where your server may restart or where you run multiple instances behind a load balancer. Without persistent storage, active sessions are lost on restart.</Tip>

### When to use sessions vs. per-request MPP

* **Sessions** are ideal for high-frequency agent calls where sub-100ms billing latency matters (e.g., chat, real-time orchestration).
* **Per-request MPP** (the standard 402 flow) is better for infrequent or large-value calls where on-chain settlement per request is acceptable.

See the [Wallet API — MPP payment sessions](/api-reference/wallet#mpp-payment-sessions) reference for the full endpoint documentation.

## Troubleshooting

<AccordionGroup>
  <Accordion title="402 response with no MPP challenge">
    The plugin may not have a configured price. Only plugins listed in the pricing table above support MPP payments.
  </Accordion>

  <Accordion title="Credential verification failed">
    * Ensure the `challengeNonce` matches the nonce from the 402 response.
    * Verify the transaction amount matches the challenge amount exactly.
    * Check that you are using the correct network (mainnet vs. testnet).
  </Accordion>

  <Accordion title="Transaction type error">
    The transaction hex must begin with `0x76` (the Tempo transaction type marker). Ensure your signing implementation includes this prefix.
  </Accordion>
</AccordionGroup>
