Basic 402 Gate
api.stable402.com/gate
What this demonstrates
The x402 basic gate is the simplest possible x402 implementation: a single endpoint that
returns HTTP 402 Payment Required when called without a valid payment, and
200 OK with a payload after payment is verified by the Coinbase CDP facilitator.
The pattern establishes the core x402 V2 contract — a machine-readable
PAYMENT-REQUIRED header encodes everything a client needs to
construct and submit payment autonomously, with no accounts, API keys, or
human intervention. This is what makes x402 suitable for AI agent commerce:
an agent can receive a 402, parse the header, settle via USDC on Base Sepolia,
and retry — all in a single code path.
The payment flow
- Request. Client sends
GET https://api.stable402.com/gatewith no payment header. - 402 response. Worker returns HTTP
402 Payment Requiredwith aPAYMENT-REQUIREDheader containing a Base64-encoded JSON payload describing the payment requirements: network, asset, amount, recipient wallet, and facilitator metadata. - Payment construction. An x402-aware client (SDK or agent) decodes the header,
constructs a signed USDC transfer on Base Sepolia testnet (
eip155:84532), and encodes it as aPAYMENT-SIGNATUREheader. - Facilitator verification. The Worker forwards the payment signature to the
Coinbase CDP facilitator at
https://api.cdp.coinbase.com/platform/v2/x402. The facilitator verifies the on-chain transaction and returns a settlement receipt. - 200 response. Worker returns HTTP
200 OKwith the payload and aPAYMENT-RESPONSEheader containing the Base64-encoded settlement receipt.
PAYMENT-REQUIRED header
The header value is the Base64 encoding of this JSON object:
{
"x402Version": 2,
"accepts": [
{
"scheme": "exact",
"network": "eip155:84532",
"maxAmountRequired": "1000",
"resource": "https://api.stable402.com/gate",
"description": "Access to the x402 basic gate reference endpoint. Returns a demonstration payload.",
"mimeType": "application/json",
"payTo": "0x22F637cF55217cb00252dDCF0c61FC4EfC12682c",
"maxTimeoutSeconds": 60,
"asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"extra": {
"name": "USDC",
"version": "2"
}
}
]
}
Network eip155:84532 is Base Sepolia testnet.
Asset 0x036CbD… is USDC on Base Sepolia.
maxAmountRequired: "1000" is 1000 USDC base units — $0.001 USDC.
The Worker implementation
Full source at stable402/api/src/index.ts. Deployed via
wrangler deploy to the api.stable402.com route.
import { verifyPayment } from '@x402/core';
const PAYMENT_REQUIRED_PAYLOAD = {
x402Version: 2,
accepts: [{
scheme: "exact",
network: "eip155:84532",
maxAmountRequired: "1000",
resource: "https://api.stable402.com/gate",
description: "Access to the x402 basic gate reference endpoint.",
mimeType: "application/json",
payTo: env.WALLET_ADDRESS, // 0x22F637cF...12682c
maxTimeoutSeconds: 60,
asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
extra: { name: "USDC", version: "2" },
extensions: {
bazaar: {
discoverable: true,
outputSchema: {
type: "object",
properties: {
message: { type: "string" },
endpoint: { type: "string" },
protocol: { type: "string" },
facilitator: { type: "string" },
network: { type: "string" },
timestamp: { type: "string" },
documentation: { type: "string" },
}
}
}
}
}]
};
const CORS_HEADERS = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, PAYMENT-SIGNATURE',
};
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
// Handle CORS preflight
if (request.method === 'OPTIONS') {
return new Response(null, { status: 204, headers: CORS_HEADERS });
}
// Route: GET /gate
if (url.pathname === '/gate' && request.method === 'GET') {
return handleGate(request, env);
}
return new Response('Not found', { status: 404 });
},
};
async function handleGate(request: Request, env: Env): Promise<Response> {
const paymentSignature = request.headers.get('PAYMENT-SIGNATURE');
// No payment header — return 402
if (!paymentSignature) {
const encoded = btoa(JSON.stringify(PAYMENT_REQUIRED_PAYLOAD));
return new Response(null, {
status: 402,
headers: {
...CORS_HEADERS,
'PAYMENT-REQUIRED': encoded,
'Content-Type': 'application/json',
},
});
}
// Payment header present — verify with CDP facilitator
try {
const verifyResponse = await fetch(
'https://api.cdp.coinbase.com/platform/v2/x402/verify',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
paymentHeader: paymentSignature,
paymentRequirements: PAYMENT_REQUIRED_PAYLOAD.accepts[0],
}),
}
);
if (!verifyResponse.ok) {
return new Response(JSON.stringify({ error: 'Payment verification failed' }), {
status: 402,
headers: { ...CORS_HEADERS, 'Content-Type': 'application/json' },
});
}
const settlement = await verifyResponse.json();
const settlementEncoded = btoa(JSON.stringify(settlement));
const payload = {
message: 'Payment verified. Welcome to the x402 basic gate.',
endpoint: 'api.stable402.com/gate',
protocol: 'x402 V2',
facilitator: 'Coinbase CDP',
network: 'Base Sepolia (testnet)',
timestamp: new Date().toISOString(),
documentation: 'https://stable402.com/demos/gate',
};
return new Response(JSON.stringify(payload, null, 2), {
status: 200,
headers: {
...CORS_HEADERS,
'Content-Type': 'application/json',
'PAYMENT-RESPONSE': settlementEncoded,
},
});
} catch (err) {
return new Response(JSON.stringify({ error: 'Internal error during payment verification' }), {
status: 500,
headers: { ...CORS_HEADERS, 'Content-Type': 'application/json' },
});
}
}
interface Env {
WALLET_ADDRESS: string;
} Call the live endpoint
Send a request to api.stable402.com/gate and inspect the raw 402 response —
no payment wallet required to see the protocol in action.
Status
PAYMENT-REQUIRED header (decoded)
Raw curl equivalent
curl -i https://api.stable402.com/gate