x402 Payment Flow
Bloomfilter uses x402 for payments — an open protocol that adds native USDC payments to HTTP using the 402 Payment Required status code.
Overview
Section titled “Overview”Every paid request follows the same flow:
- Send request to a paid endpoint
- Receive 402 with payment requirements in the
PAYMENT-REQUIREDheader - Sign payment — create a USDC payment on Base L2
- Retry request with
PAYMENT-SIGNATUREheader - Server settles the payment on-chain and processes your request
Dynamic pricing (registration & renewal)
Section titled “Dynamic pricing (registration & renewal)”Domain registration and renewal use dynamic pricing — the price depends on the TLD and number of years.
Step 1: Initial request
Section titled “Step 1: Initial request”curl -X POST https://api.bloomfilter.xyz/domains/register \ -H "Content-Type: application/json" \ -d '{"domain": "example.io", "years": 1}'Step 2: 402 response
Section titled “Step 2: 402 response”HTTP/1.1 402 Payment RequiredPAYMENT-REQUIRED: eyJhY2NlcHRzIjpb... (base64-encoded JSON)Content-Type: application/json
{ "error": "Payment required", "domain": "example.io", "price_usd": "34.99", "years": 1, "currency": "USDC", "network": "eip155:8453"}The PAYMENT-REQUIRED header contains a base64-encoded JSON object with:
accepts— array of accepted payment schemes (network, asset, price)resource— description of what you’re paying for
Step 3: Retry with payment
Section titled “Step 3: Retry with payment”curl -X POST https://api.bloomfilter.xyz/domains/register \ -H "Content-Type: application/json" \ -H "PAYMENT-SIGNATURE: eyJwYXlsb2FkIj..." \ -d '{"domain": "example.io", "years": 1}'Step 4: Success
Section titled “Step 4: Success”HTTP/1.1 201 Created
{ "transactionId": "01JMXYZ...", "domain": "example.io", "status": "active", "years": 1, "price_usd": "34.99", "registeredAt": "2026-02-21T12:00:00Z", "expiresAt": "2027-02-21T00:00:00Z", "nameservers": ["ns1.bloomfilter.xyz", "ns2.bloomfilter.xyz"], "dnsRecords": []}Static pricing (DNS mutations)
Section titled “Static pricing (DNS mutations)”DNS add, update, and delete operations use a fixed price of $0.10 USDC per operation. The x402 middleware handles this automatically — same flow as above, just with a fixed price.
| Endpoint | Price |
|---|---|
POST /dns/{domain} | $0.10 |
PUT /dns/{domain}/{recordId} | $0.10 |
DELETE /dns/{domain}/{recordId} | $0.10 |
DNS read (GET /dns/{domain}) is free but requires authentication.
Payment details
Section titled “Payment details”| Property | Value |
|---|---|
| Currency | USDC |
| Network | Base L2 (chain ID 8453) |
| CAIP-2 identifier | eip155:8453 |
| Settlement | On-chain, immediate |
| Refunds | Payment only settles on 2xx success. If the operation fails, no payment is taken. |
Using @x402/axios
Section titled “Using @x402/axios”The easiest way to integrate x402 in TypeScript:
import axios from "axios";import { wrapAxiosWithPayment, x402Client } from "@x402/axios";import { ExactEvmScheme } from "@x402/evm";import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");const client = new x402Client().register("eip155:*", new ExactEvmScheme(account));
const http = wrapAxiosWithPayment( axios.create({ baseURL: "https://api.bloomfilter.xyz" }), client);
// Payment happens automatically on 402 responsesconst { data } = await http.post("/domains/register", { domain: "example.io", years: 1,});Insufficient funds
Section titled “Insufficient funds”If your wallet doesn’t have enough USDC, the payment will fail:
{ "error": "Payment verification failed", "message": "insufficient_funds: insufficient balance", "code": "PAYMENT_INSUFFICIENT"}Fund your wallet with USDC on Base before retrying.