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

# Machine Payments Protocol (MPP)

Enable agents to pay for APIs and content using MPP, an open protocol for machine-to-machine payments over HTTP. Privy's server wallets provide the signing layer, while the `mppx` SDK handles the 402 payment flow automatically.

## What is MPP?

[MPP](https://mpp.dev) (Machine Payments Protocol) is an open protocol for machine-to-machine payments over HTTP. When a resource requires payment, the server responds with `402 Payment Required` and payment details. The client signs a payment credential using the agent's wallet and retries the request. Settlement happens on the [Tempo](https://tempo.xyz) blockchain using PathUSD.

## Installation

```bash theme={"system"}
npm install @privy-io/node mppx viem
```

* `@privy-io/node` provides server-side wallet creation and signing
* `mppx` provides the MPP client that handles 402 payment flows
* `viem` provides Ethereum utilities for creating a compatible account interface

## Creating a Privy-backed signer

MPP's `tempo()` payment method expects a [viem Account](https://viem.sh/docs/accounts/local/toAccount) for signing. Since Privy wallets are server-managed, your app creates a custom viem account that delegates signing to Privy's API.

```typescript theme={"system"}
import {PrivyClient} from '@privy-io/node';
import {toAccount} from 'viem/accounts';
import {keccak256} from 'viem';

const privy = new PrivyClient({
  appId: process.env.PRIVY_APP_ID,
  appSecret: process.env.PRIVY_APP_SECRET
});

function createPrivyAccount(walletId: string, address: `0x${string}`) {
  async function signHash(hash: `0x${string}`): Promise<`0x${string}`> {
    const result = await privy.wallets().ethereum().signSecp256k1(walletId, {params: {hash}});
    return result.signature as `0x${string}`;
  }

  return toAccount({
    address,

    async signMessage({message}) {
      const result = await privy
        .wallets()
        .ethereum()
        .signMessage(walletId, {
          message: typeof message === 'string' ? message : message.raw
        });
      return result.signature as `0x${string}`;
    },

    async signTransaction(transaction, options) {
      const serializer = options?.serializer;
      if (!serializer) {
        throw new Error('Tempo serializer required');
      }

      const unsignedSerialized = await serializer(transaction);
      const hash = keccak256(unsignedSerialized);
      const signature = await signHash(hash as `0x${string}`);

      const {SignatureEnvelope} = await import('ox/tempo');
      const envelope = SignatureEnvelope.from(signature);
      return (await serializer(transaction, envelope as any)) as `0x${string}`;
    },

    async signTypedData(typedData) {
      const result = await privy
        .wallets()
        .ethereum()
        .signTypedData(walletId, {params: typedData as any});
      return result.signature as `0x${string}`;
    }
  });
}
```

The account implements three signing methods:

| Method            | Privy API                              | Purpose                       |
| ----------------- | -------------------------------------- | ----------------------------- |
| `signMessage`     | `wallets().ethereum().signMessage()`   | EIP-191 personal signatures   |
| `signTransaction` | `wallets().ethereum().signSecp256k1()` | Tempo transaction signing     |
| `signTypedData`   | `wallets().ethereum().signTypedData()` | EIP-712 typed data signatures |

<Info>
  Tempo transactions use a custom serialization format. The `signTransaction` method uses the
  Tempo-provided serializer and Privy's raw `signSecp256k1` endpoint rather than the higher-level
  `signTransaction` API.
</Info>

## Making MPP payments

Pass the Privy-backed account to the MPP client's `tempo()` method. The client automatically handles 402 responses, signs payment credentials, and retries requests.

### Using `mppx.fetch`

```typescript {skip-check} theme={"system"}
import {Mppx, tempo} from 'mppx/client';

async function makePayment(walletId: string, address: `0x${string}`, url: string) {
  const account = createPrivyAccount(walletId, address);

  const mppx = Mppx.create({
    polyfill: false,
    methods: [tempo({account})]
  });

  const response = await mppx.fetch(url);
  const data = await response.json();
  return data;
}
```

`mppx.fetch` is a drop-in replacement for `fetch`. When a server returns `402 Payment Required`, the client reads the payment requirements, signs a credential with the Privy wallet, and retries the request automatically.

### Using polyfill mode

Your app can also polyfill the global `fetch` so all HTTP requests handle 402 challenges automatically:

```typescript {skip-check} theme={"system"}
import {Mppx, tempo} from 'mppx/client';

const account = createPrivyAccount(walletId, address);

Mppx.create({
  polyfill: true,
  methods: [tempo({account})]
});

// All fetch calls now handle 402 responses automatically
const response = await fetch('https://api.example.com/weather');
```

## How it works

1. **Agent requests content**: Your app calls `mppx.fetch()` or the polyfilled `fetch()`
2. **Server responds 402**: Returns payment requirements (amount, currency, recipient)
3. **MPP client signs credential**: Uses the Privy-backed account to sign a payment credential
4. **Retry with credential**: Request repeats with the signed credential attached
5. **Server verifies and settles**: Verifies the credential and settles payment on Tempo
6. **Server delivers**: Returns content with `200 OK`

## Creating an MPP-enabled service

The `mppx/nextjs` package provides middleware for adding payment requirements to API routes:

```typescript theme={"system"}
// app/api/weather/route.ts
import {Mppx, tempo} from 'mppx/nextjs';

const mppx = Mppx.create({
  methods: [
    tempo.charge({
      currency: '0x20c0000000000000000000000000000000000000', // PathUSD
      recipient: process.env.MPP_RECIPIENT as `0x${string}`
    })
  ]
});

export const GET = mppx.charge({amount: '0.1'})(() =>
  Response.json({
    temperature: 72,
    condition: 'Sunny',
    location: 'San Francisco, CA'
  })
);
```

When a client calls this route without a payment credential, the middleware responds with `402 Payment Required`. With a valid credential, it verifies the payment, settles on Tempo, and returns the data.

## Full example

```typescript theme={"system"}
import {PrivyClient} from '@privy-io/node';
import {Mppx, tempo} from 'mppx/client';
import {toAccount} from 'viem/accounts';
import {keccak256} from 'viem';

const privy = new PrivyClient({
  appId: process.env.PRIVY_APP_ID!,
  appSecret: process.env.PRIVY_APP_SECRET!
});

// 1. Create a wallet for the agent
const wallet = await privy.wallets().create({chain_type: 'ethereum'});

// 2. Build a viem account backed by Privy
function createPrivyAccount(walletId: string, address: `0x${string}`) {
  async function signHash(hash: `0x${string}`): Promise<`0x${string}`> {
    const result = await privy.wallets().ethereum().signSecp256k1(walletId, {params: {hash}});
    return result.signature as `0x${string}`;
  }

  return toAccount({
    address,
    async signMessage({message}) {
      const result = await privy
        .wallets()
        .ethereum()
        .signMessage(walletId, {
          message: typeof message === 'string' ? message : message.raw
        });
      return result.signature as `0x${string}`;
    },
    async signTransaction(transaction, options) {
      const serializer = options?.serializer;
      if (!serializer) throw new Error('Tempo serializer required');
      const unsignedSerialized = await serializer(transaction);
      const hash = keccak256(unsignedSerialized);
      const signature = await signHash(hash as `0x${string}`);
      const {SignatureEnvelope} = await import('ox/tempo');
      const envelope = SignatureEnvelope.from(signature);
      return (await serializer(transaction, envelope as any)) as `0x${string}`;
    },
    async signTypedData(typedData) {
      const result = await privy
        .wallets()
        .ethereum()
        .signTypedData(walletId, {params: typedData as any});
      return result.signature as `0x${string}`;
    }
  });
}

const account = createPrivyAccount(wallet.id, wallet.address as `0x${string}`);

// 3. Create the MPP client
const mppx = Mppx.create({
  polyfill: false,
  methods: [tempo({account})]
});

// 4. Make a paid request
const response = await mppx.fetch('https://api.example.com/weather');
const weather = await response.json();
```

## Learn more

* [MPP Protocol documentation](https://mpp.dev) - Protocol specification and guides
* [mppx SDK](https://www.npmjs.com/package/mppx) - Client and server SDK for MPP
* [Privy server wallets](/recipes/wallets/agentic-wallets) - Creating and managing server-side wallets
* [Full demo application](https://github.com/privy-io/examples/tree/main/privy-next-mpp-agent-demo) - Complete Next.js example
