> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://mpp.dev/api/mcp` to find what you need.

# Proxy an existing service \[Add payments to any API without changing its code]

Put a payment gate in front of an existing API without modifying it. The proxy sits between buyers and the origin, handles the `402` flow, injects upstream credentials, and forwards paid requests.

## How it works

```mermaid
sequenceDiagram
  participant C as Client
  participant P as Proxy
  participant O as Origin API
  C->>P: GET /my-api/v1/data
  P-->>C: 402 + WWW-Authenticate: Payment
  C->>P: GET /my-api/v1/data + Authorization: Payment
  P->>P: Verify Credential
  P->>O: GET /v1/data + Authorization: Bearer sk-...
  O-->>P: 200 + response body
  P-->>C: 200 + Payment-Receipt + response body

```

The proxy verifies the buyer's payment Credential, then forwards the request to the origin with the real API key injected. The buyer never sees the upstream credentials.

## Prompt mode

Paste this into your coding agent to build the entire guide in one prompt:

```text
Use https://mpp.dev/guides/proxy-existing-service.md as reference.
Create an mppx proxy server that gates an upstream REST API
behind MPP payments. Use Service.from with a bearer token for
upstream auth. Charge $0.01 for the forecast endpoint and allow
the status endpoint for free. Use the Tempo payment method.
```

## Manual mode

::::steps
### Install `mppx`

:::code-group
```bash [npm]
$ npm install mppx viem
```

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

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

### Define the upstream service

Use `Service.from` to describe the API you want to proxy. The proxy injects upstream credentials so buyers never see them.

```ts [server.ts]
import { Proxy, Service } from 'mppx/proxy'
import { Mppx, tempo } from 'mppx/server'

const mppx = Mppx.create({ methods: [tempo.charge({ testnet: true })] })

const proxy = Proxy.create({
  services: [
// [!code focus:start]
    Service.from('weather', {
      baseUrl: 'https://api.weather.example.com',
      bearer: process.env.WEATHER_API_KEY!, // [!code hl]
      description: 'Weather forecast API',
      routes: {
        'GET /v1/forecast': mppx.charge({ amount: '0.01' }),
        'GET /v1/status': true, // [!code hl]
      },
      title: 'Weather API',
    }),
// [!code focus:end]
  ],
})
```

* **`bearer`** injects `Authorization: Bearer` on upstream requests. Use `headers` for APIs that expect a custom header like `X-API-Key`.
* **`true`** marks a route as free passthrough—no payment required, but upstream credentials are still injected.

### Set pricing per route

Each route maps a `"METHOD /pattern"` to a payment intent or `true` for free passthrough. Set different prices per endpoint.

```ts [server.ts]
import { Proxy, Service } from 'mppx/proxy'
import { Mppx, tempo } from 'mppx/server'

const mppx = Mppx.create({ methods: [tempo.charge({ testnet: true })] })

const proxy = Proxy.create({
  services: [
    Service.from('weather', {
      baseUrl: 'https://api.weather.example.com',
      bearer: process.env.WEATHER_API_KEY!,
      description: 'Weather forecast API',
      routes: {
        'GET /v1/forecast': mppx.charge({ amount: '0.01' }), // [!code focus]
        'GET /v1/historical/:date': mppx.charge({ amount: '0.05' }), // [!code focus]
        'GET /v1/status': true, // [!code focus]
      },
      title: 'Weather API',
    }),
  ],
})
```

Requests to routes not listed in the map receive `404`.

### Start the proxy

The proxy returns two handlers for different runtimes.

```ts [server.ts]
import { Proxy, Service } from 'mppx/proxy'
import { Mppx, tempo } from 'mppx/server'

const mppx = Mppx.create({ methods: [tempo.charge({ testnet: true })] })

const proxy = Proxy.create({
  description: 'Payment-gated weather data', // [!code hl]
  services: [
    Service.from('weather', {
      baseUrl: 'https://api.weather.example.com',
      bearer: process.env.WEATHER_API_KEY!,
      description: 'Weather forecast API',
      routes: {
        'GET /v1/forecast': mppx.charge({ amount: '0.01' }),
        'GET /v1/historical/:date': mppx.charge({ amount: '0.05' }),
        'GET /v1/status': true,
      },
      title: 'Weather API',
    }),
  ],
  title: 'Weather Proxy', // [!code hl]
})

// [!code focus:start]
// Bun / Deno
export default { fetch: proxy.fetch }

// Node.js
// import { createServer } from 'node:http'
// createServer(proxy.listener).listen(3000)
// [!code focus:end]
```

### Test via the `mppx` CLI

```bash [test.sh]
# Validate the paid route
$ npx mppx validate http://localhost:3000

# Create and fund a testnet account
$ npx mppx account create --network testnet
$ npx mppx account fund --network testnet

# Free route — no payment required
$ npx mppx http://localhost:3000/weather/v1/status --network testnet

# Paid route — handles 402 automatically
$ npx mppx http://localhost:3000/weather/v1/forecast --network testnet
```

Requests route to `/{serviceId}/`—for example, `/weather/v1/forecast` proxies to `https://api.weather.example.com/v1/forecast`.
::::

## Multiple upstream services

Gate several APIs behind a single proxy by passing multiple services. Each mounts at its own path prefix.

```ts [server.ts]
import { Proxy, Service, openai } from 'mppx/proxy'
import { Mppx, tempo } from 'mppx/server'

const mppx = Mppx.create({ methods: [tempo.charge({ testnet: true })] })

const proxy = Proxy.create({
  description: 'Multi-service paid API proxy',
  services: [
// [!code focus:start]
    openai({ // [!code hl]
      apiKey: process.env.OPENAI_API_KEY!,
      routes: {
        'GET /v1/models': true,
        'POST /v1/chat/completions': mppx.charge({ amount: '0.05' }),
      },
    }),
    Service.from('internal', { // [!code hl]
      baseUrl: 'https://api.internal.example.com',
      headers: { 'X-API-Key': process.env.INTERNAL_API_KEY! }, // [!code hl]
      routes: {
        'POST /v1/analyze': mppx.charge({ amount: '0.10' }),
      },
    }),
// [!code focus:end]
  ],
  title: 'My Proxy',
})
```

* `/openai/v1/chat/completions` → `https://api.openai.com/v1/chat/completions`
* `/internal/v1/analyze` → `https://api.internal.example.com/v1/analyze`

Built-in service presets (`openai`, `anthropic`, `stripe`) handle upstream auth conventions automatically. Use `Service.from` for everything else.

## Recover interrupted delivery

`Proxy.create` limits request bodies to 10 MiB on paid and free routes; set `maxRequestBodySize` when an upstream needs a different limit. Enable `replay` with a shared atomic store to let buyers collect a paid response using the same Credential and exact request after a connection loss.

For non-idempotent upstream operations, set `replay.maxAttempts: 1`: the default of three attempts can execute a failed operation again without charging again. In-progress retries return `202`; streamed or oversized responses aren't retained and later return `410`. See [paid-response recovery](/sdk/typescript/proxy#recover-a-paid-response) for configuration and limits.

## Discovery

The proxy generates OpenAPI and `llms.txt` documents so coding agents and CLI tools can find available services and pricing.

```bash [test.sh]
# Human-readable overview
$ curl http://localhost:3000/llms.txt

# OpenAPI 3.1 document with all service routes
$ curl http://localhost:3000/openapi.json
```

See the [Proxy reference](/sdk/typescript/proxy#discovery-endpoints) for discovery endpoint details.

:::info[Go live]
Before going live, remove `testnet: true` and fund a mainnet account.
:::

## Next steps

[Charge per request](/guides/one-time-payments) — One payment per request, settled on-chain

[Proxy reference](/sdk/typescript/proxy): Full Proxy SDK API documentation

[Tempo Wallet CLI](https://wallet.tempo.xyz) — Managed MPP client with built in spend controls and service discovery
