Getting Started 5 min read

Quickstart

Accept your first x402 payment in 5 minutes

Prerequisites

  • Node.js 20+ or Rust 1.88+
  • A wallet address to receive payments
  • Your FareSide API key (join the waitlist if you don’t have one)
Note

The versions of x402-* crates shown in this guide are indicative. Please check crates.io for the latest available versions.

Choose Your Stack

The examples below use Hono (TypeScript) and Axum (Rust). x402 is a protocol. While Express, Hono, Next.js, Axum are supported out of the box, adding x402-payment-gating to any other framework or language is relatively straightforward.

TypeScript (Hono)

Step 1: Install Dependencies

We use the scoped @x402 packages for v2 support from x402 reference SDK.

npm install hono @hono/node-server @x402/hono @x402/core @x402/evm

Step 2: Create Your Server

import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { paymentMiddleware } from "@x402/hono";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { registerExactEvmScheme } from "@x402/evm/exact/server";

const app = new Hono();

// 1. Configure the Facilitator Client with your FareSide API key
const facilitatorClient = new HTTPFacilitatorClient({
  url: "https://facilitator.fareside.com/YOUR_API_KEY"
});

// 2. Create the Resource Server and register payment schemes
const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server); // Enable EVM payments

// 3. Configure payment middleware with FareSide facilitator
app.use(
  paymentMiddleware(
    {
      "GET /weather": {
        accepts: [
          {
            scheme: "exact",
            price: "$0.01",           // Price in USD (converted to USDC)
            network: "eip155:84532",  // Base Sepolia (CAIP-2 format)
            payTo: "0xYourWalletAddress",
          },
        ],
        description: "Weather API access",
        mimeType: "application/json",
      },
    },
    server,
  ),
);

// Your paid endpoint
app.get("/weather", (c) => {
  return c.json({
    location: "San Francisco",
    temperature: 68,
    conditions: "Sunny",
  });
});

serve({ fetch: app.fetch, port: 3000 });
console.log("Server running on http://localhost:3000");

Step 3: Test It

Start your server:

npx tsx server.ts

Make a request without payment:

curl -i http://localhost:3000/weather

You’ll get a 402 Payment Required response. The requirements are returned in the Payment-Required header (base64 encoded), which you can decode with:

echo "<Payment-Required header>" | base64 -d | jq

Decoded header payload example:

{
  "x402Version": 2,
  "error": "Payment required",
  "resource": {
    "url": "http://localhost:3000/weather",
    "description": "Weather API access",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:84532",
      "amount": "10000", // 0.01 USDC (6 decimals)
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC contract on Base Sepolia
      "payTo": "0xYourWalletAddress",
      "maxTimeoutSeconds": 300,
      "extra": {
        "name": "USDC",
        "version": "2"
      }
    }
  ]
}

Key Fields:

  • x402Version: Protocol version
  • resource: Metadata about the protected resource
    • url: The resource identifier
    • description: Human-readable description of what is being purchased
    • mimeType: Content type of the resource
  • accepts: Array of acceptable payment options. The client must choose one to proceed
    • scheme: The payment logic to use (e.g., "exact" for a fixed amount transfer)
    • network: The blockchain network identifier in CAIP-2 format
    • amount: The cost in atomic units (e.g., 10000 = 0.01 USDC)
    • asset: The token contract address (e.g., USDC address)
    • payTo: The recipient’s wallet address
    • extra: Scheme-specific parameters (e.g., EIP-712 domain info for EVM tokens)

Rust (Axum)

Step 1: Add Dependencies

[dependencies]
alloy-primitives = "1.4"
axum = "0.8"
serde_json = "1"
tokio = "1"
x402-axum = "1.1"
x402-chain-eip155 = { version = "1.1", features = ["server"] }
x402-types = "1.1"

Step 2: Create Your Server

use axum::{routing::get, Router};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use serde_json::json;
use alloy_primitives::address;
use x402_axum::X402Middleware;
use x402_chain_eip155::{V2Eip155Exact, KnownNetworkEip155};
use x402_types::networks::USDC;

#[tokio::main]
async fn main() {
    // Configure FareSide facilitator
    let x402 = X402Middleware::try_from(
        "https://facilitator.fareside.com/YOUR_API_KEY"
    ).unwrap();

    // Configure USDC on Base Sepolia and your receiving address
    let usdc = USDC::base_sepolia();
    let wallet = address!("0xYourWalletAddress");

    let app = Router::new()
        .route("/weather", get(weather_handler).layer(
            x402.with_price_tag(
                // Create a V2 price tag for 0.01 USDC
                V2Eip155Exact::price_tag(
                    wallet,
                    usdc.amount(10_000u64) // 0.01 USDC (6 decimals)
                )
            )
            // Description is required for v2 resource metadata
            .with_description("Weather API access".to_string())
        ));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    println!("Server running on http://localhost:3000");
    axum::serve(listener, app).await.unwrap();
}

async fn weather_handler() -> impl IntoResponse {
    (StatusCode::OK, axum::Json(json!({
        "location": "San Francisco",
        "temperature": 68,
        "conditions": "Sunny"
    })))
}

Step 3: Test It

cargo run

Make a request without payment:

curl -i http://localhost:3000/weather

You’ll get a 402 Payment Required response. The requirements are returned in the Payment-Required header (base64 encoded), which you can decode with:

echo "<Payment-Required header>" | base64 -d | jq

Decoded header payload example:

{
  "x402Version": 2,
  "error": "Payment-Signature header is required",
  "resource": {
    "url": "http://localhost:3000/weather",
    "description": "Weather API access",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:84532",
      "amount": "10000", // 0.01 USDC (6 decimals)
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC contract on Base Sepolia
      "payTo": "0xYourWalletAddress",
      "maxTimeoutSeconds": 300,
      "extra": {
        "name": "USDC",
        "version": "2"
      }
    }
  ]
}

Key Fields:

  • x402Version: Protocol version
  • resource: Metadata about the protected resource
    • url: The resource identifier
    • description: Human-readable description of what is being purchased
    • mimeType: Content type of the resource
  • accepts: Array of acceptable payment options. The client must choose one to proceed
    • scheme: The payment logic to use (e.g., "exact" for a fixed amount transfer)
    • network: The blockchain network identifier in CAIP-2 format
    • amount: The cost in atomic units (e.g., 10000 = 0.01 USDC)
    • asset: The token contract address (e.g., USDC address)
    • payTo: The recipient’s wallet address
    • extra: Scheme-specific parameters (e.g., EIP-712 domain info for EVM tokens)

Making a Payment (Client Side)

To actually pay for the endpoint, you need an x402-compatible client. Here’s how to test with @x402/fetch:

npm install @x402/fetch @x402/core @x402/evm viem
import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client, x402HTTPClient } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

// 1. Setup your wallet
const signer = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

// 2. Create x402 client and register EVM scheme
const client = new x402Client();
registerExactEvmScheme(client, { signer });

// 3. Wrap fetch
const fetchWithPayment = wrapFetchWithPayment(fetch, client);

// 4. Make request - payment is handled automatically
const response = await fetchWithPayment("http://localhost:3000/weather", {
  method: "GET",
});

const data = await response.json();
console.log("Weather:", data);

// Check payment receipt
if (response.ok) {
  const httpClient = new x402HTTPClient(client);
  const receipt = httpClient.getPaymentSettleResponse(
    (name) => response.headers.get(name)
  );
  console.log("Payment settled:", receipt);
}

For more details, see the Making Payments guide.

What Just Happened?

  1. Client requested /weather without payment
  2. Server returned 402 Payment Required with payment details
  3. Client created a signed payment payload using the registered EVM scheme
  4. Client retried the request with Payment-Signature header
  5. FareSide facilitator verified the payment on-chain
  6. Server delivered the weather data

The payment goes directly to your wallet.

Next Steps