HTTP 402 · Payment Required402

The internet has had a payment status code since 1997.
Nobody used it until agents needed to

scheme: upi
network: upi:in
settled via Razorpay
PAYMENT-REQUIRED
{
  "x402Version": 2,
  "accepts": [
    {
      "scheme": "upi",
      "network": "upi:in",
      "amount": "100",
      "asset": "INR",
      "payTo": "merchant@upi",
      "maxTimeoutSeconds": 180
    }
  ]
}
scroll

the old way

01

KYC verification

Identity checks before an agent can transact.

Manual
02

API key provisioning

Manual credential exchange, per integration.

Manual
03

Recurring subscriptions

Standing authorization instead of per-request consent.

Manual

raw protocol → rupee-settled payment

The Bridge

One HTTP 402 response. One scan. The agent never leaves the request.

PAYMENT-REQUIRED → UPI

PAYMENT-REQUIRED

{
  "x402Version": 2,
  "accepts": [{
    "scheme": "upi",
    "network": "upi:in",
    "amount": "10000",
    "asset": "INR",
    "payTo": "merchant@upi",
    "maxTimeoutSeconds": 180
  }]
}

₹100.00

merchant@upi
upi://pay?pa=merchant@upi&pn=fiat402%20Demo%20Merchant&am=100.00&cu=INRUPI Intent
TravelBotflagged for review

Confirm
Declined

live proof

The real control tower, replayed

recorded demo

Agent console

Replays a captured run -- no live request is sent from this page.

Idle -- run a persona above to see it live.

State machine

created → pending → approved|declined|expired → settled|failed

No requests yet. The rail lights up as soon as one enters "pending".
Created
created
Payment Link
pending
Payer outcome
approved
declined
expired
Terminal
settled
failed

Decision

No request in flight

Not available on the live event stream -- the deterministic/AI decision is returned in the facilitator's /verify response, not published to fiat402:events.

Raw x402 traffic

No request observed yet

No PAYMENT-REQUIRED / PAYMENT-SIGNATURE / PAYMENT-RESPONSE headers observed on the live feed. This panel decodes those HTTP headers; the dashboard's live feed is the Redis event channel, which doesn't carry them.

the actual protocol

Two lines, either side

For merchants
import { withX402Payment } from "../../../lib/x402-middleware";

export async function GET(request: Request): Promise<Response> {
  return withX402Payment(request, RESOURCE, () =>
    Response.json({
      data: "premium market data",
      generatedAt: new Date().toISOString(),
    }),
  );
}
For agents
// x402-upi-client/src/index.ts
export function registerUpiScheme(client: x402Client, options?: UpiSchemeClientOptions): x402Client {
  return client.register("upi:in", new UpiSchemeClient(options));
}

// real usage (x402-upi-client/test/demo.ts):
const client = new x402Client();
client.setSpendControls({ allowedAssets: true });
registerUpiScheme(client, {
  payerVpa: process.env.DEMO_PAYER_VPA,
  agentMetadata: { taskContext },
});

fiat402