Discovery
Let clients automatically discover your API's pricing
Overview
MPP's discovery system lets clients and agents learn what your endpoints cost before making a request. You serve a standard OpenAPI 3.1 document at /openapi.json with x-payment-info extensions that advertise one or more payment offers for each paid operation. Registries aggregate these documents so agents can find paid APIs automatically, and provide value-added services like reputation, search, and analytics.
Registries
Registries aggregate discovery documents from multiple services, making it easy for clients and agents to find paid APIs.
| Registry | Description | How to add |
|---|---|---|
| MPPScan | Public registry of MPP-enabled services with search and analytics | Manually register in one click |
| MPP Services directory | Curated list of live services on mpp.dev | Submit a PR to add your service |
Quick start
The mppx SDK generates discovery documents from your route configuration. Add discovery() to your server and it serves /openapi.json automatically.
import { Hono } from 'hono'
import { Mppx, discovery } from 'mppx/hono'
import { tempo } from 'mppx/server'
const app = new Hono()
const mppx = Mppx.create({
methods: [
tempo({
currency: '0x20c0000000000000000000000000000000000000',
recipient: '0x...',
testnet: true,
}),
],
secretKey: process.env.MPP_SECRET_KEY,
})
app.get('/v1/fortune', mppx.charge({ amount: '0.01' }), (c) => c.json({ fortune: 'You will be rich' }))
discovery(app, mppx, {
auto: true,
info: { title: 'Fortune API', version: '1.0.0' },
})
This generates a GET /openapi.json endpoint with canonical x-payment-info.offers[] entries on each paid route.
Express
import express from 'express'
import { Mppx, discovery } from 'mppx/express'
import { tempo } from 'mppx/server'
const app = express()
const mppx = Mppx.create({
methods: [
tempo({
currency: '0x20c0000000000000000000000000000000000000',
recipient: '0x...',
testnet: true,
}),
],
secretKey: process.env.MPP_SECRET_KEY,
})
const pay = mppx.charge({ amount: '0.01' })
app.get('/v1/fortune', pay, (req, res) => res.json({ fortune: 'You will be rich' }))
discovery(app, mppx, {
info: { title: 'Fortune API', version: '1.0.0' },
routes: [{ handler: pay, method: 'get', path: '/v1/fortune' }],
})
Next.js
In Next.js, discovery() returns a route handler you export from an API route.
import { discovery } from 'mppx/nextjs'
import { mppx, pay } from '../fortune/route'
export const GET = discovery(mppx, {
info: { title: 'Fortune API', version: '1.0.0' },
routes: [{ handler: pay, method: 'get', path: '/api/fortune' }],
})
How it works
Your server exposes a GET /openapi.json endpoint that returns an OpenAPI document. Paid operations include an x-payment-info extension with one or more payment offers, and the document root can include x-service-info for service-level metadata.
{
"openapi": "3.1.0",
"info": { "title": "My API", "version": "1.0.0" },
"x-service-info": {
"categories": ["ai"],
"docs": {
"homepage": "https://example.com",
"apiReference": "https://example.com/docs",
"llms": "/llms.txt"
}
},
"paths": {
"/v1/generate": {
"post": {
"x-payment-info": {
"offers": [
{
"amount": "1000000",
"currency": "0x20c0000000000000000000000000000000000001",
"description": "Generate text with Tempo",
"intent": "charge",
"method": "tempo"
},
{
"amount": "100",
"currency": "usd",
"description": "Generate text with Stripe",
"intent": "charge",
"method": "stripe"
}
]
},
"responses": {
"200": { "description": "Successful response" },
"402": { "description": "Payment Required" }
}
}
},
"/v1/models": {
"get": {
"responses": {
"200": { "description": "Successful response" }
}
}
}
}
}x-payment-info
Add this extension to any operation that requires payment. Prefer the canonical multi-offer shape:
| Field | Type | Description |
|---|---|---|
offers | Offer[] | Ordered list of payment offers the client can choose from |
offers[]
| Field | Type | Description |
|---|---|---|
amount | string | null | Payment amount in base units |
currency | string | Currency code or token address |
description | string | Human-readable description of the charge |
intent | string | Payment intent (charge or session) |
method | string | Payment method identifier (tempo, stripe) |
x-service-info
Optional root-level metadata about the service:
| Field | Type | Description |
|---|---|---|
categories | string[] | Free-form service categories (for example, ai, payments) |
docs.homepage | string | Link to the service homepage |
docs.apiReference | string | Link to API documentation |
docs.llms | string | Link to an llms.txt file for AI consumption |
Build manually
You can author a discovery document by hand following the discovery specification. The document is a standard OpenAPI 3.1 file with two extensions:
Create the OpenAPI skeleton
Start with a standard OpenAPI 3.1 document:
{
"openapi": "3.1.0",
"info": {
"title": "My API",
"version": "1.0.0"
},
"paths": {}
}Add x-payment-info to paid operations
For each endpoint that requires payment, add the x-payment-info extension with an offers array. Add more objects to offers[] when the client can choose between alternative payment methods or currencies. Amounts are in base units (for example, 1000000 for $1.00 with 6 decimals).
{
"paths": {
"/v1/generate": {
"post": {
"summary": "Generate text",
"x-payment-info": {
"offers": [
{
"amount": "1000000",
"currency": "0x20c0000000000000000000000000000000000001",
"intent": "charge",
"method": "tempo"
}
]
},
"responses": {
"200": { "description": "Successful response" },
"402": { "description": "Payment Required" }
}
}
}
}
}CLI
Generate a static discovery document from a config module:
$ npx mppx discover generate ./discovery.config.tsValidate an existing discovery document from a file or URL:
$ npx mppx discover validate https://example.com/openapi.jsonValidation
Common validation issues:
| Issue | Severity | Description |
|---|---|---|
Missing 402 response | Error | Operations with x-payment-info must include a 402 response |
| Invalid amount format | Error | Each offers[].amount value must be a non-negative integer string |
Missing requestBody | Warning | POST/PUT/PATCH operations without a requestBody definition |
| Invalid URI in docs | Error | docs links must be valid URIs or absolute paths |