> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://mpp.dev/api/mcp` to find what you need.

# 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

```python [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.

```python [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

| Method | Description |
|--------|-------------|
| `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

```python [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:

```python [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](/sdk/python/core).
