> Feedback: If these docs are stale, missing, or confusing, post sanitized feedback to `https://docs.tempo.xyz/api/feedback` with `source: "mcp"`, a short `message`, and any relevant `toolName`, `relatedResource`, or `client`.
# Accept pay-as-you-go payments

Build a payment-gated photo gallery API that charges $0.01 per photo using `mppx` sessions. The server returns random photos from [Picsum](https://picsum.photos) behind a paywall.

:::info
Unlike [one-time payments](/docs/guide/machine-payments/one-time-payments), sessions open a payment channel once and use off-chain vouchers for each subsequent request — vouchers are processed in pure CPU-bound signature checks, not bottlenecked by blockchain throughput.
:::

## How sessions work

<MermaidDiagram
  chart={`sequenceDiagram
  participant Client
  participant Server
  participant Tempo
  Client->>Tempo: (1) Deposit tokens
  Tempo-->>Client: Channel created
  Client->>Server: (2) Open credential
  Note over Server: Verify on-chain deposit
  Server-->>Client: 200 OK (session established)
  loop Per request
      Client->>Server: (3) Request + voucher
      Note over Server: ecrecover only
      Server-->>Client: 200 OK + Receipt
  end
  Note over Server: (4) Periodic settlement
  Server->>Tempo: settle(channelId, voucher)
  Client->>Server: (5) Close
  Server->>Tempo: close(channelId, voucher)
  Tempo-->>Client: Refund remaining deposit
`}
/>

1. **Open** — Client deposits funds into an on-chain reserve contract, creating a payment channel
2. **Session** — Client signs EIP-712 vouchers with increasing cumulative amounts as service is consumed
3. **Top up** — If the channel runs low, the client deposits additional tokens without closing the channel
4. **Close** — Either party closes the channel, settling the final balance on-chain and refunding unused deposit

## Server setup

::::steps

### Install dependencies

:::code-group

```bash [npm]
npm install mppx viem
```

```bash [pnpm]
pnpm add mppx viem
```

```bash [bun]
bun add mppx viem
```

:::

### Set up `Mppx` instance

Set up an `Mppx` instance with the `tempo` method.

* `recipient` is the address where you receive payments.
* `currency` is the token address for payments (in this case, `pathUSD`).

```ts
import { Mppx, tempo } from 'mppx/server'

const mppx = Mppx.create({
  methods: [tempo({
    currency: '0x20c0000000000000000000000000000000000000',
    recipient: '0xa726a1CD723409074DF9108A2187cfA19899aCF8',
  })],
})
```

### Add a session-gated route

Add payment verification using `mppx.session` as route middleware. The handler only runs after payment is verified.

:::code-group

```ts [Next.js]
import { Mppx, tempo } from 'mppx/nextjs'

const mppx = Mppx.create({
  methods: [tempo({
    currency: '0x20c0000000000000000000000000000000000000',
    recipient: '0xa726a1CD723409074DF9108A2187cfA19899aCF8',
  })],
})

export const GET =
  mppx.session({ amount: '0.01', unitType: 'photo' })
  (async () => {
    const res = await fetch('https://picsum.photos/200/200')
    return Response.json({ url: res.url })
  })
```

```ts [Hono]
import { Hono } from 'hono'
import { Mppx, tempo } from 'mppx/hono'

const app = new Hono()

const mppx = Mppx.create({
  methods: [tempo({
    currency: '0x20c0000000000000000000000000000000000000',
    recipient: '0xa726a1CD723409074DF9108A2187cfA19899aCF8',
  })],
})

app.get(
  '/api/sessions/photo',
  mppx.session({ amount: '0.01', unitType: 'photo' }),
  async (c) => {
    const res = await fetch('https://picsum.photos/200/200')
    return c.json({ url: res.url })
  },
)
```

```ts [Express]
import express from 'express'
import { Mppx, tempo } from 'mppx/express'

const app = express()

const mppx = Mppx.create({
  methods: [tempo({
    currency: '0x20c0000000000000000000000000000000000000',
    recipient: '0xa726a1CD723409074DF9108A2187cfA19899aCF8',
  })],
})

app.get(
  '/api/sessions/photo',
  mppx.session({ amount: '0.01', unitType: 'photo' }),
  async (req, res) => {
    const response = await fetch('https://picsum.photos/200/200')
    res.json({ url: response.url })
  },
)
```

```ts [Fetch API]
import { Mppx, tempo } from 'mppx/server'

const mppx = Mppx.create({
  methods: [tempo({
    currency: '0x20c0000000000000000000000000000000000000',
    recipient: '0xa726a1CD723409074DF9108A2187cfA19899aCF8',
  })],
})

Bun.serve({
  async fetch(request) {
    const result = await mppx.session({
      amount: '0.01',
      unitType: 'photo',
    })(request)

    if (result.status === 402) return result.challenge

    const res = await fetch('https://picsum.photos/200/200')
    return result.withReceipt(Response.json({ url: res.url }))
  },
})
```

:::

### Test the endpoint

```bash
# Create account funded with testnet tokens
$ npx mppx account create

# Make a paid request
$ npx mppx http://localhost:3000/api/sessions/photo
```

::::

## Client setup

When using sessions from a client, set `maxDeposit` to enable automatic channel management. This is the maximum amount of tokens the client locks into the payment channel's reserve contract. Any unspent deposit is refunded when the channel closes.

```ts
import { Mppx, tempo } from 'mppx/client'
import { privateKeyToAccount } from 'viem/accounts'

const mppx = Mppx.create({
  methods: [tempo({
    account: privateKeyToAccount('0x...'),
    maxDeposit: '1', // Lock up to 1 pathUSD per channel
  })],
})

// Each fetch automatically manages the session lifecycle:
// 1st request: opens channel on-chain, sends initial voucher
// 2nd+ requests: sends off-chain vouchers (no on-chain tx)
const res = await fetch('http://localhost:3000/api/sessions/photo')
```

* **`maxDeposit: '1'`** — Locks up to 1 pathUSD into the payment channel. At $0.01/photo, this covers up to 100 requests before the channel runs out.
* The client handles the full session lifecycle automatically: channel open, voucher signing, and retry after `402` responses.
* If the server sets `suggestedDeposit`, the client uses `min(suggestedDeposit, maxDeposit)`.

## Next steps

<Cards>
  <Card icon="lucide:zap" title="Accept streamed payments" description="Per-token billing over Server-Sent Events" to="/docs/guide/machine-payments/streamed-payments" />

  <Card icon="lucide:server" title="Server quickstart" description="Framework middleware reference" to="/docs/guide/machine-payments/server" />

  <Card icon="lucide:book-open" title="Full session reference" description="Complete tempo.session API documentation" to="https://mpp.dev/payment-methods/tempo/session" />
</Cards>
