---
## Documentation
Full documentation, API reference, and guides are available at **[mpp.dev/sdk/typescript](https://mpp.dev/sdk/typescript)**.
Contributors changing Tempo sessions should read the [session design](./src/tempo/session/README.md) before altering credential, recovery, accounting, or transport behavior.
## Install
```bash
npm i mppx
```
## Quick Start
### Server
```ts
import { Mppx, tempo } from 'mppx/server'
const mppx = Mppx.create({
methods: [
tempo({
recipient: '0x742d35Cc6634c0532925a3b844bC9e7595F8fE00',
}),
],
secretKey: process.env.MPP_SECRET_KEY!,
})
export async function handler(request: Request) {
const response = await mppx.charge({ amount: '1' })(request)
if (response.status === 402) return response.challenge
return response.withReceipt(Response.json({ data: '...' }))
}
```
Generate `MPP_SECRET_KEY` with at least 32 bytes, for example: `openssl rand -base64 32`.
### Client
```ts
import { Mppx, tempo } from 'mppx/client'
import { privateKeyToAccount } from 'viem/accounts'
Mppx.create({
methods: [tempo({ account: privateKeyToAccount('0x...') })],
})
// Global fetch now handles 402 automatically
const res = await fetch('https://mpp.dev/api/ping/paid')
```
## Examples
| Example | Description |
| ------------------------------------------------------ | ---------------------------------------------------- |
| [charge](./examples/charge/) | Payment-gated photo generation API |
| [charge-wagmi](./examples/charge-wagmi/) | Payment-gated charge with Wagmi + React |
| [session/multi-fetch](./examples/session/multi-fetch/) | Multiple paid requests over a single payment channel |
| [session/sse](./examples/session/sse/) | Pay-per-token LLM streaming with SSE |
| [stripe](./examples/stripe/) | Stripe SPT charge with automatic client |
```bash
npx gitpick wevm/mppx/examples/charge
```
## CLI
`mppx` includes a basic CLI for making HTTP requests with automatic payment handling. Tempo
session channels are retained and reused automatically until you close them.
```bash
# create account - stored in keychain, autofunded on testnet
mppx account create
# make request - automatic payment handling, curl-like api
mppx example.com
# pay an x402 offer on a server that advertises both protocols
mppx example.com --protocol x402
# open another session instead of reusing the preferred channel
mppx example.com --session new
# inspect and close retained sessions
mppx sessions list
mppx sessions view
mppx sessions close
mppx sessions close --all --yes
# explicitly trust a custom session escrow advertised by the server
mppx example.com -M allowCustomEscrow=true
```
`--session auto` is the default. Pass `new` to open another channel or a channel ID to select one
explicitly.
Tempo session clients accept only the canonical escrow contract by default. A server may advertise
its configured custom escrow in the payment challenge, but the client rejects it unless
`-M allowCustomEscrow=true` is supplied. This opt-in trusts the server-selected address; clients
that do not support custom escrows should leave it unset. See the
[session escrow trust documentation](./src/tempo/session/README.md#escrow-configuration-and-trust).
`--protocol auto` is the default: MPP is preferred when available, and x402 is used otherwise.
Pass `mpp` or `x402` to require one protocol. x402 payments use the same EVM account as EVM
charges, so `MPPX_PRIVATE_KEY` or a stored account works for both.
Payment extensions can enforce policy or prepare funds after challenge selection and confirmation,
immediately before credential creation:
```ts
import { defineConfig, Extension } from 'mppx/cli'
export default defineConfig({
extensions: [
Extension.from({
async preparePayment({ challenge }) {
await prepareFunds(challenge)
},
}),
],
})
```
Extensions run in configuration order. Throwing rejects the payment before Mppx signs it.
You can also install globally to use the `mppx` CLI from anywhere:
```bash
npm i -g mppx
```
## Payments Proxy
`mppx` exports a `Proxy` server handler so that you can create or define a 402-protected payments proxy for any API.
```ts
import { openai, stripe, Proxy } from 'mppx/proxy'
import { Mppx, tempo } from 'mppx/server'
const mppx = Mppx.create({
methods: [tempo()],
secretKey: process.env.MPP_SECRET_KEY!,
})
const proxy = Proxy.create({
services: [
openai({
apiKey: 'sk-...',
routes: {
'POST /v1/chat/completions': mppx.charge({ amount: '0.05' }),
'POST /v1/completions': mppx.tempo.session({
amount: '0.0001',
unitType: 'token',
}),
'GET /v1/models': true,
},
}),
stripe({
apiKey: 'sk-...',
routes: {
'POST /v1/charges': mppx.charge({ amount: '0.01' }),
'GET /v1/customers/:id': true,
},
}),
],
})
createServer(proxy.listener) // Node.js
Bun.serve(proxy) // Bun
Deno.serve(proxy.fetch) // Deno
app.use(proxy.listener) // Express
app.all('*', (c) => proxy.fetch(c.req.raw)) // Hono
app.all('*', (c) => proxy.fetch(c.request)) // Elysia
export const GET = proxy.fetch // Next.js
export const POST = proxy.fetch // Next.js
```
This exposes the following routes:
| Route | Pricing |
| ---------------------------------- | ----------------------------- |
| `POST /openai/v1/chat/completions` | charge **$0.005** |
| `POST /openai/v1/completions` | session **$0.0001 per token** |
| `GET /openai/v1/models` | free |
| `POST /stripe/v1/charges` | charge **$0.01** |
| `GET /stripe/v1/customers/:id` | free |
## Protocol
Built on the ["Payment" HTTP Authentication Scheme](https://datatracker.ietf.org/doc/draft-ryan-httpauth-payment/). See [mpp-specs](https://github.com/tempoxyz/mpp-specs) for the full specification.
## License
MIT
### Accepted Tempo currencies
`tempo()`, `tempo.charge()`, `tempo.session()`, and `tempo.subscription()` offer OUSD first, followed by USDC.e on mainnet or pathUSD on Moderato (`testnet: true`). Each factory returns a group accepted directly by `Mppx.create`. Clients choose one offer; ordering does not trigger an automatic swap.
```ts
import { Mppx, tempo } from 'mppx/server'
import { ousd, usdce } from 'viem/tokens'
const mppx = Mppx.create({
methods: [
tempo.charge({
currencies: [ousd, usdce],
recipient: '0x742d35Cc6634c0532925a3b844bC9e7595F8fE00',
}),
],
})
```
Omit `currencies` for the network defaults, or use `currencies: [ousd]` to accept only OUSD on mainnet. Explicit lists replace the defaults. The deprecated `currency` option also restricts acceptance to one token. Wire requests and handler overrides continue to use singular `currency`.
Use configured handlers such as `mppx.tempo.charge` when composing payments. Code that directly inspects a Method can destructure the group: `const [charge] = tempo.charge({ currencies: [ousd] })`. Existing sessions and subscriptions continue using their originally authorized currency.