Payment Flow¶
The mcp-pay payment flow is built on HTTP 402 and is compatible with x402.
The reference server implements it end to end with a facilitator-backed
verifier.
Discovery¶
- The client fetches
/.well-known/mcp/pay.json. - The client parses pricing for the desired tools.
- The client selects a preferred payment rail from
accepts.
Request Diagram¶
Agent MCP Server Facilitator
| | |
| GET /api/forecast | |
|------------------------------->| |
| | |
| 402 Payment Required | |
| X-PAYMENT-REQUIRED: {...} | |
|<-------------------------------| |
| | |
| [Create payment proof] | |
| | |
| GET /api/forecast | |
| X-PAYMENT: {...} | |
|------------------------------->| |
| | POST /verify |
| |----------------------------->|
| | |
| | { valid: true } |
| |<-----------------------------|
| | |
| 200 OK | |
| X-PAYMENT-RESPONSE: {...} | |
| { forecast: ... } | |
|<-------------------------------| |
Payment Required (HTTP 402)¶
When a client calls a paid tool without payment:
- The server responds with
HTTP 402 Payment Required. - The response includes an
X-PAYMENT-REQUIREDheader (base64 JSON). - The response body contains the payment requirements:
{
"x402_version": 2,
"accepts": [{
"scheme": "exact",
"network": "eip155:8453",
"max_amount_required": "0.003",
"resource": "get_forecast",
"pay_to": "0x...",
"asset": "USDC",
"max_timeout_seconds": 60
}]
}
The reference server uses x402_version: 2, scheme: "exact", and a fixed
max_timeout_seconds of 60. If a CONTRACT is configured, it is attached
to each requirement under an extra object as { "contract": "0x…" }.
Payment Submission¶
- The client constructs a payment proof.
- The client sends the request again with an
X-PAYMENTheader (base64 JSON). - The server verifies the payment with the facilitator.
- On success the server returns
200 OKwithX-PAYMENT-RESPONSE. - On failure the server returns
402 Payment RequiredwithX-PAYMENT-ERROR.
X402PaymentPayload Shape¶
The reference verifier expects this payload (after base64 decoding):
{
"x402_version": 2,
"scheme": "exact",
"network": "eip155:8453",
"payload": {
"signature": "0x…",
"authorization": "…",
"amount": "0.003",
"nonce": "…",
"expiry": 1717678800
}
}
signature is required; authorization, amount, nonce, and expiry are
optional.
Server-Side Verification¶
X402Verifier::verify does the following:
- Base64-decode and JSON-parse the
X-PAYMENTpayload. - Reject the request if the payload network does not match the server's
configured
NETWORK. - POST the payload to
<X402_FACILITATOR>/verify. - Reject if the facilitator returns a non-success HTTP status or
{ "valid": false }. - Return a
PaymentReceiptcontaining the facilitator-providedtx_hash, asettled_atepoch second, plus the request's amount, currency, rail ("x402"), and network.
The receipt is serialised into the X-PAYMENT-RESPONSE header on the success
response.
Error Responses¶
When verification fails the handler returns:
HTTP/1.1 402 Payment Required
Content-Type: application/json
X-PAYMENT-ERROR: <message>
{
"error": "payment_invalid",
"message": "<verifier error>"
}
Errors surfaced by the verifier include:
InvalidPayload— base64 or JSON parsing failed.VerificationFailed— network mismatch or facilitator rejection.NetworkError— couldn't reach the facilitator.InvalidSignature— facilitator returnedvalid: false.
Security Considerations¶
From the specification:
- Payment data is public — the manifest contains no secrets.
- HTTPS required — payment endpoints MUST use HTTPS.
- Rate limiting — servers SHOULD rate-limit manifest requests.
- Payment verification — always verify payments with the facilitator.