TypeScript Server Quickstart

Accept payments for your API in 5 minutes

quickstarttypescriptserverbeginner

Quickstart for Sellers

This guide walks you through integrating with x402 to enable payments for your API or service. By the end, your API will be able to charge buyers and AI agents for access.

Note: This quickstart begins with testnet configuration for safe testing. When you're ready for production, see Running on Mainnet for the simple changes needed to accept real payments on Base (EVM) and Solana networks.

Prerequisites

Before you begin, ensure you have:

  • A crypto wallet to receive funds (any EVM-compatible wallet)
  • Node.js and npm, Go, or Python and pip installed
  • An existing API or server

Note: The Python SDK is currently under development for x402 v2. For immediate v2 support, use TypeScript or Go.

Note
We have pre-configured examples available in our repo for both Node.js and Go. We also have an advanced example that shows how to use the x402 SDKs to build a more complex payment flow.

1. Install Dependencies

Express

Install the x402 Express middleware package.

npm install @x402/express @x402/core @x402/evm

Next.js

Install the x402 Next.js middleware package.

npm install @x402/next @x402/core @x402/evm

Hono

Install the x402 Hono middleware package.

npm install @x402/hono @x402/core @x402/evm

Go

Add the x402 Go module to your project:

go get github.com/coinbase/x402/go

FastAPI

Install the x402 Python package

pip install x402

Note: Python SDK currently uses v1 patterns.

Flask

Install the x402 Python package

pip install x402

Note: Python SDK currently uses v1 patterns.

2. Add Payment Middleware

Integrate the payment middleware into your application. You will need to provide:

  • The Facilitator URL or facilitator client. For testing, use https://x402.org/facilitator which works on Base Sepolia and Solana devnet.
  • The routes you want to protect.
  • Your receiving wallet address.

Express

Full example in the repo here.

import express from "express";
import { paymentMiddleware } from "@x402/express";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { registerExactEvmScheme } from "@x402/evm/exact/server";

const app = express();

// Your receiving wallet address
const payTo = "0xYourAddress";

// Create facilitator client (testnet)
const facilitatorClient = new HTTPFacilitatorClient({
  url: "https://x402.org/facilitator"
});

// Create resource server and register EVM scheme
const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server);

app.use(
  paymentMiddleware(
    {
      "GET /weather": {
        accepts: [
          {
            scheme: "exact",
            price: "$0.001", // USDC amount in dollars
            network: "eip155:84532", // Base Sepolia (CAIP-2 format)
            payTo,
          },
        ],
        description: "Get current weather data for any location",
        mimeType: "application/json",
      },
    },
    server,
  ),
);

// Implement your route
app.get("/weather", (req, res) => {
  res.send({
    report: {
      weather: "sunny",
      temperature: 70,
    },
  });
});

app.listen(4021, () => {
  console.log(`Server listening at http://localhost:4021`);
});

Next.js

Full example in the repo here.

// middleware.ts
import { paymentProxy } from "@x402/next";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { registerExactEvmScheme } from "@x402/evm/exact/server";

const payTo = "0xYourAddress";

const facilitatorClient = new HTTPFacilitatorClient({
  url: "https://x402.org/facilitator"
});

const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server);

export const middleware = paymentProxy(
  {
    "/api/protected": {
      accepts: [
        {
          scheme: "exact",
          price: "$0.01",
          network: "eip155:84532",
          payTo,
        },
      ],
      description: "Access to protected content",
      mimeType: "application/json",
    },
  },
  server,
);

export const config = {
  matcher: ["/api/protected/:path*"],
};

Hono

Full example in the repo here.

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();
const payTo = "0xYourAddress";

const facilitatorClient = new HTTPFacilitatorClient({
  url: "https://x402.org/facilitator"
});

const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server);

app.use(
  paymentMiddleware(
    {
      "/protected-route": {
        accepts: [
          {
            scheme: "exact",
            price: "$0.10",
            network: "eip155:84532",
            payTo,
          },
        ],
        description: "Access to premium content",
        mimeType: "application/json",
      },
    },
    server,
  ),
);

app.get("/protected-route", (c) => {
  return c.json({ message: "This content is behind a paywall" });
});

serve({ fetch: app.fetch, port: 3000 });

Go (Gin)

Full example in the repo here.

package main

import (
    "net/http"
    "time"

    x402 "github.com/coinbase/x402/go"
    x402http "github.com/coinbase/x402/go/http"
    ginmw "github.com/coinbase/x402/go/http/gin"
    evm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
    "github.com/gin-gonic/gin"
)

func main() {
    payTo := "0xYourAddress"
    network := x402.Network("eip155:84532") // Base Sepolia (CAIP-2 format)

    r := gin.Default()

    // Create facilitator client
    facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
        URL: "https://x402.org/facilitator",
    })

    // Apply x402 payment middleware
    r.Use(ginmw.X402Payment(ginmw.Config{
        Routes: x402http.RoutesConfig{
            "GET /weather": {
                Accepts: x402http.PaymentOptions{
                    {
                        Scheme:  "exact",
                        PayTo:   payTo,
                        Price:   "$0.001",
                        Network: network,
                    },
                },
                Description: "Get weather data for a city",
                MimeType:    "application/json",
            },
        },
        Facilitator: facilitatorClient,
        Schemes: []ginmw.SchemeConfig{
            {Network: network, Server: evm.NewExactEvmScheme()},
        },
        SyncFacilitatorOnStart: true,
        Timeout:    30 * time.Second,
    }))

    // Protected endpoint
    r.GET("/weather", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "weather":     "sunny",
            "temperature": 70,
        })
    })

    r.Run(":4021")
}

FastAPI

Full example in the repo here.

Note: Python SDK currently uses v1 patterns with string network identifiers.

from typing import Any, Dict
from fastapi import FastAPI
from x402.fastapi.middleware import require_payment

app = FastAPI()

# Apply payment middleware to specific routes
app.middleware("http")(
    require_payment(
        path="/weather",
        price="$0.001",
        pay_to_address="0xYourAddress",
        network="base-sepolia",  # v1 string identifier
    )
)

@app.get("/weather")
async def get_weather() -> Dict[str, Any]:
    return {
        "report": {
            "weather": "sunny",
            "temperature": 70,
        }
    }

Flask

Full example in the repo here.

Note: Python SDK currently uses v1 patterns with string network identifiers.

from flask import Flask
from x402.flask.middleware import PaymentMiddleware

app = Flask(__name__)

# Initialize payment middleware
payment_middleware = PaymentMiddleware(app)

# Apply payment middleware to specific routes
payment_middleware.add(
    path="/weather",
    price="$0.001",
    pay_to_address="0xYourAddress",
    network="base-sepolia",  # v1 string identifier
)

Route Configuration Interface:

interface RouteConfig {
  accepts: Array<{
    scheme: string;           // Payment scheme (e.g., "exact")
    price: string;            // Price in dollars (e.g., "$0.01")
    network: string;          // Network in CAIP-2 format (e.g., "eip155:84532")
    payTo: string;            // Your wallet address
  }>;
  description?: string;       // Description of the resource
  mimeType?: string;          // MIME type of the response
  extensions?: object;        // Optional extensions (e.g., Bazaar)
}

When a request is made to these routes without payment, your server will respond with the HTTP 402 Payment Required code and payment instructions.

3. Test Your Integration

To verify:

  1. Make a request to your endpoint (e.g., curl http://localhost:4021/weather).
  2. The server responds with a 402 Payment Required, including payment instructions in the PAYMENT-REQUIRED header.
  3. Complete the payment using a compatible client, wallet, or automated agent. This typically involves signing a payment payload, which is handled by the client SDK detailed in the Quickstart for Buyers.
  4. Retry the request, this time including the PAYMENT-SIGNATURE header containing the cryptographic proof of payment.
  5. The server verifies the payment via the facilitator and, if valid, returns your actual API response (e.g., { "data": "Your paid API response." }).

4. Enhance Discovery with Metadata (Recommended)

When using the CDP facilitator, your endpoints can be listed in the x402 Bazaar, our discovery layer that helps buyers and AI agents find services. To enable discovery:

{
  "GET /weather": {
    accepts: [
      {
        scheme: "exact",
        price: "$0.001",
        network: "eip155:8453",
        payTo: "0xYourAddress",
      },
    ],
    description: "Get real-time weather data including temperature, conditions, and humidity",
    mimeType: "application/json",
    extensions: {
      bazaar: {
        discoverable: true,
        category: "weather",
        tags: ["forecast", "real-time"],
      },
    },
  },
}

Learn more about the discovery layer in the Bazaar documentation.

5. Error Handling

  • If you run into trouble, check out the examples in the repo for more context and full code.
  • Run npm install or go mod tidy to install dependencies

Running on Mainnet

Once you've tested your integration on testnet, you're ready to accept real payments on mainnet.

1. Update the Facilitator URL

For mainnet, use the CDP facilitator:

Node.js

const facilitatorClient = new HTTPFacilitatorClient({
  url: "https://api.cdp.coinbase.com/platform/v2/x402"
});

Go

facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
    URL: "https://api.cdp.coinbase.com/platform/v2/x402",
})

2. Update Your Network Identifier

Change from testnet to mainnet network identifiers:

Base Mainnet

// Testnet → Mainnet
network: "eip155:8453", // Base mainnet (was eip155:84532)

Solana Mainnet

// Testnet → Mainnet
network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", // Solana mainnet

// For Solana, use a Solana wallet address (base58 format)
payTo: "YourSolanaWalletAddress",

Multi-Network

// Support multiple networks on the same endpoint
{
  "GET /weather": {
    accepts: [
      {
        scheme: "exact",
        price: "$0.001",
        network: "eip155:8453",  // Base mainnet
        payTo: "0xYourEvmAddress",
      },
      {
        scheme: "exact",
        price: "$0.001",
        network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",  // Solana mainnet
        payTo: "YourSolanaAddress",
      },
    ],
    description: "Weather data",
  },
}

3. Register Multiple Schemes (Multi-Network)

For multi-network support, register both EVM and SVM schemes:

Node.js

import { registerExactEvmScheme } from "@x402/evm/exact/server";
import { registerExactSvmScheme } from "@x402/svm/exact/server";

const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server);
registerExactSvmScheme(server);

Go

import (
    evm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
    svm "github.com/coinbase/x402/go/mechanisms/svm/exact/server"
)

r.Use(ginmw.X402Payment(ginmw.Config{
    // ...
    Schemes: []ginmw.SchemeConfig{
        {Network: x402.Network("eip155:8453"), Server: evm.NewExactEvmScheme()},
        {Network: x402.Network("solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"), Server: svm.NewExactSvmScheme()},
    },
}))

4. Update Your Wallet

Make sure your receiving wallet address is a real mainnet address where you want to receive USDC payments.

5. Test with Real Payments

Before going live:

  1. Test with small amounts first
  2. Verify payments are arriving in your wallet
  3. Monitor the facilitator for any issues

Warning: Mainnet transactions involve real money. Always test thoroughly on testnet first and start with small amounts on mainnet.


Network Identifiers (CAIP-2)

x402 v2 uses CAIP-2 format for network identifiers:

NetworkCAIP-2 Identifier
Base Mainneteip155:8453
Base Sepoliaeip155:84532
Solana Mainnetsolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
Solana Devnetsolana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1

See Network Support for the full list.


Next Steps

For questions or support, join our Discord.

Summary

This quickstart covered:

  • Installing the x402 SDK and relevant middleware
  • Adding payment middleware to your API and configuring it
  • Testing your integration
  • Deploying to mainnet with CAIP-2 network identifiers

Your API is now ready to accept crypto payments through x402.

Related Content

Looking for more? Check out our other typescript examples or browse by server content.