> ## Documentation Index
> Fetch the complete documentation index at: https://docs.payai.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Python Server Manual Flow

> Step-by-step guide to building the x402 PAYMENT-REQUIRED response manually in Python without server SDK or middleware.

## Manual x402 server flow (Python)

This page shows how to respond with **402 Payment Required** and build the **PAYMENT-REQUIRED** header by hand in Python—no `x402` server middleware. You decide when payment is required, construct the payment-requirements payload, base64-encode it, and send it in the response.

For production you’ll usually use the [Flask](/x402/servers/python/flask) or [FastAPI](/x402/servers/python/fastapi) quickstarts; this tutorial is for agents or environments that need a minimal implementation or want to understand the protocol from the server’s perspective.

<Warning>
  This is a **402 response-construction example**, not a payment-processing server.
  It never delivers a paid resource. Requests carrying a payment header receive
  501 until a real verification, settlement and recovery implementation replaces
  the demonstration handler. Header presence is not proof of payment.
</Warning>

***

## 1. When to return 402

When a request hits a protected route:

* If the request **does not** include a valid **PAYMENT-SIGNATURE** header (or the payment is invalid/expired), respond with **402** and a **PAYMENT-REQUIRED** header so the client knows how to pay.
* If the request **does** include a valid payment, you (or your facilitator) verify/settle it and then respond with **200** and the resource. Verification and settlement are typically done via the [PayAI Facilitator](/x402/facilitators/introduction) or your own backend; this page focuses only on building the 402 response.

***

## 2. Build the payment-requirements payload

The **PAYMENT-REQUIRED** header must contain **base64-encoded JSON**. The JSON object has this shape (see [x402 Reference §5.1](/x402/reference)):

| Field | Type | Description |
| - | - | - |
| `x402Version` | `number` | Protocol version; use **2** |
| `error` | `string` | Human-readable message (e.g. why payment is required) |
| `resource` | `object` | `url`, `description`, and optional `mimeType` for the protected resource |
| `accepts` | `array` | List of payment options (scheme, network, amount, asset, payTo, maxTimeoutSeconds, optional extra) |
| `extensions` | `object` | Optional extension data; this example advertises none (`{}`). |

Each item in **accepts** describes one payment option. This example uses **Base Sepolia and Solana devnet**, not mainnet. The client echoes its chosen requirement in `accepted` within PAYMENT-SIGNATURE.

***

## 3. Example: building the payload in Python

Define the payload dict, then encode it as base64 and set the header. Set your own
testnet recipient addresses in `EVM_ADDRESS` and `SVM_ADDRESS`. Set `SVM_FEE_PAYER`
from the selected facilitator's current devnet configuration; check
[supported networks](/x402/supported-networks). Missing configuration stops the
example; it never substitutes somebody else's recipient. Address syntax checks
do not prove ownership or network compatibility.

```python theme={null}
import base64
import json
import os
import re

def required_address(name: str, pattern: str) -> str:
    value = os.environ.get(name)
    if not value or not re.fullmatch(pattern, value):
        raise ValueError(f"Configure {name}")
    return value

EVM_PAY_TO = required_address("EVM_ADDRESS", r"0x[0-9a-fA-F]{40}")
SVM_PAY_TO = required_address("SVM_ADDRESS", r"[1-9A-HJ-NP-Za-km-z]{32,44}")
SVM_FEE_PAYER = required_address("SVM_FEE_PAYER", r"[1-9A-HJ-NP-Za-km-z]{32,44}")

# Example: protected resource URL and metadata
resource_url = "https://api.example.com/weather"
resource_description = "Weather data"
resource_mime_type = "application/json"

payment_required = {
    "x402Version": 2,
    "error": "PAYMENT-SIGNATURE header is required",
    "resource": {
        "url": resource_url,
        "description": resource_description,
        "mimeType": resource_mime_type,
    },
    "accepts": [
        {
            "scheme": "exact",
            "network": "eip155:84532",
            "amount": "10000",
            "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
            "payTo": EVM_PAY_TO,
            "maxTimeoutSeconds": 60,
            "extra": {"name": "USDC", "version": "2"},
        },
        {
            "scheme": "exact",
            "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
            "amount": "1000000",
            "asset": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
            "payTo": SVM_PAY_TO,
            "maxTimeoutSeconds": 60,
            "extra": {"feePayer": SVM_FEE_PAYER},
        },
    ],
    "extensions": {},
}

payment_required_b64 = base64.b64encode(json.dumps(payment_required).encode()).decode()
```

***

## 4. Send the 402 response with PAYMENT-REQUIRED

Set the **PAYMENT-REQUIRED** header to the base64 string and return status **402**.

```python theme={null}
def send_402(start_response, payment_required_b64: str) -> list[bytes]:
    status = "402 Payment Required"
    headers = [
        ("Content-Type", "application/json"),
        ("PAYMENT-REQUIRED", payment_required_b64),
    ]
    start_response(status, headers)
    return [b'{"error":"Payment required"}']
```

The following demonstration handlers return 501 when a payment header is supplied.
Do not expose them as payable services. A real server must validate against its
own requirements and follow the selected scheme's verification/settlement
sequence before delivering a resource. Preserve uncertain-payment state and
follow [recovery guidance](/x402/facilitators/capacity-and-limits).

Example with Flask (requires Flask):

```python theme={null}
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/weather")
def weather():
    if request.headers.get("PAYMENT-SIGNATURE") is None:
        return (
            jsonify({"error": "Payment required"}),
            402,
            {"PAYMENT-REQUIRED": payment_required_b64},
        )
    return jsonify({"error": "Payment processing is not implemented in this example"}), 501
```

Example with FastAPI (requires FastAPI):

```python theme={null}
from fastapi import FastAPI, Request, Response

app = FastAPI()

@app.get("/weather")
def weather(request: Request):
    if request.headers.get("payment-signature") is None:
        return Response(
            content='{"error":"Payment required"}',
            status_code=402,
            media_type="application/json",
            headers={"PAYMENT-REQUIRED": payment_required_b64},
        )
    return Response(
        content='{"error":"Payment processing is not implemented in this example"}',
        status_code=501,
        media_type="application/json",
    )
```

***

## Summary

| Step | Action |
| - | - |
| 1 | Decide when payment is required (no or invalid PAYMENT-SIGNATURE). |
| 2 | Build the payment-requirements dict: `x402Version`, `error`, `resource`, `accepts`, `extensions`. |
| 3 | Base64-encode `json.dumps(payment_required)` and set the **PAYMENT-REQUIRED** header. |
| 4 | Respond with status **402**. |

For exact field types and facilitator behavior, see the [x402 Reference](/x402/reference). For a ready-made server, use the [Flask](/x402/servers/python/flask) or [FastAPI](/x402/servers/python/fastapi) quickstarts.

## Need help?

<Card title="Join our Community" icon="discord" href="https://discord.gg/eWJRwMpebQ">
  Have questions or want to connect with other developers? Join our Discord server.
</Card>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.