> ## 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.

# TypeScript Server Manual Flow

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

## Manual x402 server flow (TypeScript)

This page shows how to respond with **402 Payment Required** and build the **PAYMENT-REQUIRED** header by hand in TypeScript—no `@x402/express` or other server SDK. 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 [Express](/x402/servers/typescript/express), [Hono](/x402/servers/typescript/hono), or [Next.js](/x402/servers/typescript/nextjs) 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 TypeScript

Define the payload object, 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 below do not prove ownership or network compatibility.

```ts theme={null}
import type { IncomingMessage, ServerResponse } from "node:http";

function b64Encode(s: string): string {
  return Buffer.from(s, "utf-8").toString("base64");
}

function requiredAddress(name: string, pattern: RegExp): string {
  const value = process.env[name];
  if (!value || !pattern.test(value)) throw new Error(`Configure ${name}`);
  return value;
}

const EVM_PAY_TO = requiredAddress("EVM_ADDRESS", /^0x[0-9a-fA-F]{40}$/);
const SVM_PAY_TO = requiredAddress("SVM_ADDRESS", /^[1-9A-HJ-NP-Za-km-z]{32,44}$/);
const SVM_FEE_PAYER = requiredAddress("SVM_FEE_PAYER", /^[1-9A-HJ-NP-Za-km-z]{32,44}$/);

// Example: protected resource URL and metadata
const resourceUrl = "https://api.example.com/weather";
const resourceDescription = "Weather data";
const resourceMimeType = "application/json";

const paymentRequired = {
  x402Version: 2,
  error: "PAYMENT-SIGNATURE header is required",
  resource: {
    url: resourceUrl,
    description: resourceDescription,
    mimeType: resourceMimeType,
  },
  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: {},
};

const paymentRequiredB64 = b64Encode(JSON.stringify(paymentRequired));
```

***

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

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

```ts theme={null}
function send402(res: ServerResponse, paymentRequiredB64: string): void {
  res.writeHead(402, {
    "Content-Type": "application/json",
    "PAYMENT-REQUIRED": paymentRequiredB64,
  });
  res.end(JSON.stringify({ error: "Payment required" }));
}
```

Demonstration handler: missing headers get the 402 response; supplied headers
stop at 501. This is not a deployed payment error policy. Do not expose it as a
payable service. A real server must validate against its own requirements and
follow the selected scheme's verification/settlement sequence before delivering
the resource. Preserve uncertain-payment state and follow
[recovery guidance](/x402/facilitators/capacity-and-limits).

```ts theme={null}
function handleGetWeather(req: IncomingMessage, res: ServerResponse): void {
  if (req.headers["payment-signature"] === undefined) {
    send402(res, paymentRequiredB64);
    return;
  }
  res.writeHead(501, { "Content-Type": "application/json" });
  res.end(JSON.stringify({ error: "Payment processing is not implemented in this example" }));
}
```

***

## Summary

| Step | Action |
| - | - |
| 1 | Decide when payment is required (no or invalid PAYMENT-SIGNATURE). |
| 2 | Build the payment-requirements object: `x402Version`, `error`, `resource`, `accepts`, `extensions`. |
| 3 | Base64-encode `JSON.stringify(paymentRequired)` 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 [Express](/x402/servers/typescript/express), [Hono](/x402/servers/typescript/hono), or [Next.js](/x402/servers/typescript/nextjs) 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.