# MPP on Tempo

Give an AI agent its own wallet and let it pay for things on its own. This recipe pairs Openfort **backend wallets** with the [Machine Payments Protocol (MPP)](https://machinepayments.dev) so an agent can autonomously settle HTTP `402 Payment Required` requests in PathUSD on the [Tempo](https://tempo.xyz) blockchain — no human in the loop.

:::tip
You'll build a Next.js app where you create an agent, fund its wallet with PathUSD from an app treasury, and watch it pay for a weather API over MPP. Openfort holds the keys and signs every payment; the agent never exposes a private key to your code.
:::

<HoverCardLink
  title="View Sample Code"
  subtitle="GitHub Repository"
  description="Complete source code for the MPP agent demo with Openfort backend wallets, Next.js, mppx, and viem."
  href="https://github.com/openfort-xyz/recipes-hub/tree/main/mpp"
  img={{
  src: "/img/icons/github-icon.svg",
  alt: "GitHub Icon",
  className: "rounded-none",
}}
  color="#333"
  external
/>

## What is MPP?

As software becomes agent-driven, payment stops being something that happens *around* a request and becomes part of the request itself. The [Machine Payments Protocol](https://machinepayments.dev) is an open standard for machine-to-machine payments over HTTP: when a service needs to be paid, it answers with `402 Payment Required` and the payment terms; the client signs a payment credential and retries. Settlement happens on Tempo in PathUSD.

That makes it a natural fit for autonomous agents — an agent can discover a price, decide to pay, and complete the call in a single round trip, with no checkout page, API key, or pre-funded account on the provider's side.

## How MPP works

```text
┌─────────────────┐                       ┌─────────────────┐
│   AI Agent      │ ──── GET /weather ───▶ │   MPP Service   │
│  (Openfort      │                        │                 │
│   backend       │ ◀── 402 + payment ──── │                 │
│   wallet)       │     requirements       │                 │
│                 │                        │                 │
│                 │ ──── GET /weather ───▶ │                 │
│                 │      + signed          │                 │
│                 │      credential        │                 │
│                 │ ◀──── 200 + data ───── │                 │
└─────────────────┘                        └─────────────────┘
```

1. The agent requests a resource.
2. The service responds with `402 Payment Required` and the payment requirements (amount, recipient, currency).
3. The agent signs a payment credential with its Openfort backend wallet.
4. The agent retries the request with the signed credential attached.
5. The service verifies the credential, settles on Tempo, and returns the data.

## MPP vs x402

MPP and Coinbase's [x402](/docs/recipes/x402) share the same core idea — pay over HTTP using the `402` status code — but target different deployments.

| | MPP | x402 |
| --- | --- | --- |
| Settlement | Tempo (PathUSD) | EVM chains (USDC) |
| Payment methods | Pluggable (Tempo today, more planned) | On-chain stablecoin transfer |
| Sessions | Offchain vouchers, periodic settlement | Per-request settlement |
| Best for | High-frequency agent payments on Tempo | USDC paywalls on Base and other EVM chains |

Openfort backend wallets work as the signer for **both** — pick the protocol that matches where you want to settle.

## Getting started

:::note
* Node.js 18+ and pnpm
* An [Openfort account](https://dashboard.openfort.io) with a **Secret Key** (`sk_test_...`) and **Wallet Secret**
* A treasury backend wallet funded with PathUSD on the Tempo testnet (Moderato)
:::

:::steps
### Set up your project

Clone the recipe and install dependencies:

```bash
pnpx gitpick openfort-xyz/recipes-hub/tree/main/mpp openfort-mpp
cd openfort-mpp
pnpm install
```

The project is a single Next.js App Router app — the UI and the server routes that hold the wallet secret live together:

```text
mpp/
├── app/
│   ├── page.tsx                       # Landing — create agent
│   ├── agent/[id]/
│   │   ├── fund/page.tsx              # Fund the agent with PathUSD
│   │   └── page.tsx                   # Agent dashboard — run actions
│   └── api/
│       ├── agent/create/route.ts     # Creates an Openfort backend wallet
│       ├── agent/fund/route.ts       # Treasury → agent PathUSD transfer
│       ├── agent/execute/route.ts    # Runs the MPP-paid action
│       ├── agent/balance/route.ts    # Reads PathUSD from the Tempo RPC
│       └── mock-services/weather/    # The MPP-protected service ($0.10)
└── lib/
    ├── openfort.ts                    # Openfort client + wallet creation
    ├── openfort-account.ts            # Backend wallet → viem account adapter
    ├── mpp-client.ts                  # MPP fetch wrapper (signs with Openfort)
    ├── treasury.ts                    # PathUSD funding over Tempo
    └── network.ts                     # Tempo testnet config
```

### Create a treasury wallet

The app funds each agent from a shared treasury. Create a backend wallet with the Openfort Node SDK and note its id and address:

```ts
import Openfort from '@openfort/openfort-node'

const openfort = new Openfort(process.env.OPENFORT_SECRET_KEY, {
  walletSecret: process.env.OPENFORT_WALLET_SECRET,
})

const treasury = await openfort.accounts.evm.backend.create()
console.log(treasury.id)       // acc_... → TREASURY_WALLET_ID
console.log(treasury.address)  // 0x...   → fund this on Tempo
```

### Fund the treasury on Tempo

The treasury needs PathUSD on the Tempo testnet. `viem` ships native Tempo support, including a faucet action:

```ts
import { createClient, http } from 'viem'
import { tempoModerato } from 'viem/chains'
import { Actions } from 'viem/tempo'

const client = createClient({ chain: tempoModerato, transport: http() })
await Actions.faucet.fundSync(client, { account: treasury.address })
```

### Configure your environment

```bash
cp .env.example .env.local
```

Fill in `.env.local`:

```bash
OPENFORT_SECRET_KEY=sk_test_...      # Project secret key
OPENFORT_WALLET_SECRET=...           # Required for backend-wallet signing
TREASURY_WALLET_ID=acc_...           # Treasury backend wallet, funded with PathUSD
MPP_RECIPIENT=0x...                  # Tempo address that receives the agent's payments
MPP_SECRET_KEY=...                   # Secret the seller route uses to HMAC-bind payment challenges
NEXT_PUBLIC_BASE_URL=http://localhost:3000
```

### Run the application

```bash
pnpm dev
```

Visit [http://localhost:3000](http://localhost:3000). Create an agent, fund it with $0.10–$1.00, then run **Fetch Weather** and watch the `402 → sign → 200` payment settle on Tempo.
:::

## How Openfort fits in

Tempo is not a chain Openfort indexes, so Openfort is used purely as a **remote signer**: it holds the keys and signs, while `viem` and `mppx` broadcast directly to the Tempo RPC. Every signing primitive the MPP flow needs maps to the `@openfort/openfort-node` backend-wallet API.

| Step | Openfort call |
| --- | --- |
| Create agent wallet | `openfort.accounts.evm.backend.create()` |
| Load treasury wallet | `openfort.accounts.evm.backend.get({ id })` |
| Sign an MPP payment credential | `account.sign({ hash })` |
| Sign a message / typed data | `account.signMessage(...)` / `account.signTypedData(...)` |

The heart of the recipe is a small adapter that wraps an Openfort backend wallet as a `viem` account. Tempo transactions use a custom format, so `viem` hands `signTransaction` a Tempo serializer — we serialize, hash, sign the hash via Openfort, then re-serialize with the signature attached:

```ts
// lib/openfort-account.ts
import { SignatureEnvelope } from 'ox/tempo'
import { keccak256 } from 'viem'
import { toAccount } from 'viem/accounts'
import { getOpenfortClient } from '@/lib/openfort'

export async function createOpenfortTempoAccount(accountId: string) {
  const account = await getOpenfortClient().accounts.evm.backend.get({ id: accountId })

  return toAccount({
    address: account.address,
    signMessage: ({ message }) => account.signMessage({ message }),
    signTypedData: (typedData) => account.signTypedData(typedData),
    async signTransaction(transaction, options) {
      const serializer = options?.serializer
      const unsigned = await serializer(transaction)
      const signature = await account.sign({ hash: keccak256(unsigned) })
      return serializer(transaction, SignatureEnvelope.from(signature))
    },
  })
}
```

That single adapter is shared by both sides of the demo — the agent paying over MPP and the treasury funding the agent.

## Making a payment

The agent's payment path is a thin wrapper over the `mppx` client. It intercepts the `402`, signs the credential with the Openfort-backed account, and retries automatically:

```ts
// lib/mpp-client.ts
import { Mppx, tempo } from 'mppx/client'
import { createOpenfortTempoAccount } from '@/lib/openfort-account'

export async function executeMppRequest(accountId: string, url: string) {
  const account = await createOpenfortTempoAccount(accountId)
  const mppx = Mppx.create({ polyfill: false, methods: [tempo({ account })] })
  return mppx.fetch(url) // handles the 402 → sign → retry flow
}
```

On the server side, gating a route behind MPP is a one-liner with `mppx/nextjs`:

```ts
// app/api/mock-services/weather/route.ts
import { Mppx, tempo } from 'mppx/nextjs'

const mppx = Mppx.create({
  methods: [tempo.charge({ testnet: true, currency: process.env.MPP_CURRENCY, recipient: process.env.MPP_RECIPIENT })],
})

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

## Funding over Tempo

Funding an agent is a native Tempo token transfer. The treasury account signs through Openfort while `viem` broadcasts to the Tempo RPC:

```ts
// lib/treasury.ts
import { createWalletClient, http, parseUnits } from 'viem'
import { tempoModerato } from 'viem/chains'
import { Actions } from 'viem/tempo'
import { createOpenfortTempoAccount } from '@/lib/openfort-account'

export async function fundAgentWallet(agentAddress: `0x${string}`, amountUsd: number) {
  const account = await createOpenfortTempoAccount(process.env.TREASURY_WALLET_ID!)
  const client = createWalletClient({ account, chain: tempoModerato, transport: http() })

  const hash = await Actions.token.transfer(client, {
    token: '0x20c0000000000000000000000000000000000000', // PathUSD
    to: agentAddress,
    amount: parseUnits(amountUsd.toFixed(6), 6),
  })

  return { hash }
}
```

## Use cases

* **Agent-paid APIs**: let an autonomous agent pay per call for data, search, or inference.
* **Pay-per-use services**: meter access to compute or premium endpoints without API keys.
* **Machine-to-machine commerce**: services that pay each other as part of a workflow.
* **Usage-based settlement**: high-frequency micropayments settled on Tempo.

## Next steps

* [Machine Payments Protocol](https://machinepayments.dev)
* [`mppx` SDK](https://www.npmjs.com/package/mppx)
* [Server Wallets](/docs/products/server)
* [x402 Payments](/docs/recipes/x402)
* [Tempo](https://tempo.xyz)
