Skip to content
LogoLogo

Client

Handle 402 responses automatically

The Client class wraps httpx and intercepts 402 responses—it parses the Challenge, signs a stablecoin transfer, and retries with the Credential.

Quick start

client.py
from mpp.client import Client
from mpp.methods.tempo import tempo, TempoAccount, ChargeIntent
 
account = TempoAccount.from_key("0x...")
 
async with Client(methods=[tempo(account=account, intents={"charge": ChargeIntent()})]) as client:
    response = await client.get("https://api.example.com/resource")
    print(response.json())

Event handling

Register event handlers to record the automatic 402 flow. Use helper methods for named events, event constants for shared wiring, and * for every client event.

client.py
from mpp.client import Client
from mpp.events import PAYMENT_RESPONSE
from mpp.methods.tempo import tempo, TempoAccount, ChargeIntent
 
account = TempoAccount.from_key("0x...")
 
async with Client(methods=[tempo(account=account, intents={"charge": ChargeIntent()})]) as client:
    def log(event: str, data: dict[str, object]) -> None:
        print(event, data)
 
    # Record the selected Challenge before the client creates a Credential.
    client.on_challenge_received(
        lambda payload: log(
            "payment.challenge.received",
            {
                "challenge_id": payload["challenge"].id,
                "intent": payload["challenge"].intent,
                "method": payload["challenge"].method,
            },
        )
    )
 
    # Record the retried response after payment.
    client.on(
        PAYMENT_RESPONSE,
        lambda payload: log(
            "payment.response",
            {"status": payload["response"].status_code},
        ),
    )
 
    # Catch every client payment event in one handler.
    client.on("*", lambda event: log("payment.event", {"name": event.name}))
 
    response = await client.get("https://api.example.com/resource")

on_challenge_received can return a Credential to override the default Credential creation path. Other handlers only record payment handling. Each registration returns an unsubscribe function.

Common methods

MethodDescription
delete(url, **kwargs)DELETE request with payment handling
get(url, **kwargs)GET request with payment handling
post(url, **kwargs)POST request with payment handling
put(url, **kwargs)PUT request with payment handling
request(method, url, **kwargs)Generic request

One-off requests

client.py
from mpp.client import get
from mpp.methods.tempo import tempo, TempoAccount, ChargeIntent
 
response = await get(
    "https://api.example.com/resource",
    methods=[tempo(account=TempoAccount.from_key("0x..."), intents={"charge": ChargeIntent()})],
)

Custom httpx transport

PaymentTransport wraps any httpx.AsyncBaseTransport, so you can compose it with custom transports:

transport.py
import httpx
from mpp.client import PaymentTransport
from mpp.methods.tempo import tempo, TempoAccount, ChargeIntent
 
account = TempoAccount.from_key("0x...")
transport = PaymentTransport(
    methods=[tempo(account=account, intents={"charge": ChargeIntent()})],
    inner=httpx.AsyncHTTPTransport(retries=3),
)
 
async with httpx.AsyncClient(transport=transport) as client:
    response = await client.get("https://api.example.com/resource")

For manual Challenge/Credential handling, see Core types.