Machine Payments Protocol and HTTP Authentication
How MPP uses the Payment HTTP authentication scheme, OpenAPI pricing metadata, charge and session intents, and server fulfillment controls.
Machine Payments Protocol, or MPP, borrows HTTP authentication's challenge and credential shape for payment. A server returns 402 Payment Required with a Payment challenge in WWW-Authenticate; after satisfying those terms, the client retries with an Authorization: Payment credential and can receive Payment-Receipt on success.
Stablecoin, card, Lightning, or another registered payment method can use the same core exchange. The route must validate the chosen method; the HTTP fields do not define settlement rules.
This work remains in draft form. The Payment HTTP authentication scheme is an active individual Internet-Draft, not an RFC. The separate Payment Discovery draft 01 has intended status Informational. As of September 9, 2026, the authentication document is draft-ryan-httpauth-payment-01. Pin both documents in the implementation plan. Updating a discovery schema without updating the runtime challenge parser can leave a client comparing two different versions of the transaction.
The runtime challenge controls the purchase
An MPP exchange starts with the resource request. If payment is the primary barrier and the server can provide usable terms, it returns a 402 response like this:
HTTP/1.1 402 Payment Required
WWW-Authenticate: Payment id="ch_123", realm="api.example", method="stripe", intent="charge", request="<base64url-json>"
The required challenge parameters identify the challenge, protection space, payment method, intent, and method-specific request data. Optional parameters can bind the challenge to a request-body digest, set an expiry, or provide a display description. A client shouldn't treat that description as a value to verify. Amount and recipient checks belong to the structured method data.
After fulfilling the method-specific requirement, the client retries:
Authorization: Payment <credential>
The server verifies the credential and settlement rules before releasing the resource. A successful response may include Payment-Receipt, which lets the client retain a structured reference to the accepted payment. Failed payment verification returns a fresh 402 challenge. Ordinary login failures still use 401, and a valid payment followed by a policy denial uses 403.
That distinction is valuable. A payment credential should never become a substitute for product authorization. If a customer must be entitled to a dataset in addition to paying, authenticate first, authorize the account, then issue terms appropriate to that account.
OpenAPI discovery describes payable operations in advance
Runtime challenge handling can work without discovery. Discovery draft 01 provides an optional way for clients to find a paid API before calling it. A participating service publishes an OpenAPI 3.x document at /openapi.json over HTTPS with application/json.
The optional top-level x-service-info object can list service categories and documentation links. Each payable operation carries x-payment-info, using either a single offer or an offers array. New documents should use the array so an operation can advertise alternatives.
{
"x-payment-info": {
"offers": [
{
"intent": "charge",
"method": "stripe",
"amount": "25",
"currency": "usd"
}
]
}
}
An offer requires intent, method, and amount. amount is a string of digits in the currency's smallest denomination, or null when runtime inputs determine the price. currency and a human-readable description are optional fields in the draft.
Discovery is advisory. If the document advertises 25 cents and the live challenge asks for 30, the client must assess the live challenge against the user's approved amount or spending policy. Runtime authority over protocol terms doesn't authorize a price increase. It must not reuse the cached OpenAPI price as payment authority. The mismatch should also produce telemetry because stale discovery can mislead service selection even when the wire protocol remains correct.
Publishing the document reveals routes, request schemas, and prices to unauthenticated readers. That may be intentional for a public API. It is a poor fit for an internal service whose paths or rates are account-specific. Review disclosure, cross-origin access, cache duration, and retirement behavior before placing it at a fixed origin path.
Charge and session intents solve different billing problems
A charge represents a one-time payment. Use it when the server can state the amount for a bounded operation, such as one conversion, one export, or one fixed data query. A retried request needs idempotency so the same business operation doesn't create a second charge or a second result.
A session supports metered use over a continuing interaction. The client authorizes a spending relationship, and the accepted amount can increase as the server delivers units such as tokens or bytes. This can reduce settlement overhead for streaming or high-frequency workloads.
Sessions add state. The service needs a durable store, usage measurement, a maximum authorization, close behavior, reconciliation, and recovery when either party disconnects. A route whose cost is known before execution usually doesn't need that machinery. For a hypothetical streaming response, define what happens when the client reaches its authorized ceiling halfway through a chunk: stop at an agreed unit boundary, preserve usage state, and report the unfinished result. A session should not turn a lost connection into permission to keep spending.
Choose the intent from the product's billing unit, then select an SDK that supports it.
SDK middleware stops at the application boundary
The MPP project publishes SDKs, including mppx for TypeScript and pympp for Python, with adapters for common web frameworks. Middleware can parse challenges and credentials, invoke a configured payment method, and decorate a successful response with its receipt.
The route still owns the work after payment. It must bind the accepted challenge to the exact request, prevent concurrent duplicate execution, decide whether settlement precedes fulfillment, and retain enough state to repair a paid request that failed during delivery.
Use a durable operation key. Store challenge ID, credential outcome, receipt reference, fulfillment state, and the response locator in one record. Don't log complete payment credentials. Lifecycle hooks should record amounts and method identifiers only when policy allows, with secrets and personal data excluded.
Server-side controls also include rate limits before expensive verification, bounded credential sizes, strict method allowlists, body-digest validation for mutating requests, and a refusal to follow payment or metadata URLs supplied by an untrusted client. The draft warns that payment credentials are bearer-like secrets and require TLS.
Define the billing unit
Before annotating an operation in OpenAPI, define what payment buys. For a charge, specify the delivered result, price, completion state and remedy for incomplete fulfillment. For a session, add usage measurement, an authorization ceiling and behavior after disconnect.
Choose payment methods and settlement providers against that contract. The HTTP exchange cannot decide whether a partially generated report counts as delivered or whether a disconnected session should continue accruing charges.
Recover a paid failure
Join the challenge ID, API request, customer account, settlement state and fulfillment result in a durable record. Keep payment credentials out of it. Support staff should be able to find the record and determine whether to resume delivery or apply the agreed remedy.
In a test environment, accept payment and interrupt fulfillment. Retry the request, including concurrently, and confirm that it resolves to the same operation. Exercise expired challenges, changed prices, budget limits and an uncertain settlement response as separate cases.
Document which component reconciles each state. Middleware can validate a payment exchange; the service must still account for the purchase when the ordinary response never reaches the buyer.