ScriptMasterLabs.com · OMNIGATE Starter Kit

How do I add x402 payments to my API and settle through a facilitator?

Copy the middleware for your stack — Express, FastAPI or Hono — set your wallet and your price, and deploy. Your route answers a standards-shaped x402 v2 402 challenge and settles through OMNIGATE, Script Master Labs' facilitator on Base and Arc. No account, no API key, no signup form: your server calls two endpoints, your buyers pay in USDC, and your first settlement lists your resource in OMNIGATE's discovery index automatically. This kit is the integration the seller offer on /settle-through-sml points at.

Get the kit All rails & fees — the seller offer

The deal, in one block

OMNIGATE settles USDC on Base (eip155:8453) and Arc (eip155:5042) under the x402 v2 exact scheme. The facilitator fee is 0.005 USDC per settlement, flat, charged from the first settlement — no free allowance, no monthly minimum, no percentage of your price.

Your buyer pays the fee as a second EIP-3009 authorization attached to the same payment (carried at paymentPayload.fee; the exact shape is published in OMNIGATE's own machine-readable terms at mcp-x402.onrender.com/supported). Your price is paid to your own payTo wallet in full. OMNIGATE broadcasts the two authorizations and holds neither amount — it never takes custody of your money.

Every snippet below was test-run against the live facilitator on 2026-11-04: unpaid calls answer the exact 402 challenge shape a production SML route serves, and a payment attempt missing its fee leg is refused by OMNIGATE's /verify with facilitator_fee_required — nothing broadcast, nothing charged. The dated proof is in the proof section.

What you get

1 · Settlement on Base and Arc, to your own wallet. Two networks, one facilitator endpoint, the standard x402 v2 verify/settle flow. Your server never holds a private key for settlement and never talks to a chain directly.

2 · An automatic discovery listing. Each snippet's 402 challenge carries the bazaar discovery declaration. The first time a payment for your resource settles through OMNIGATE, the resource appears in OMNIGATE's /discovery/resources index — 129 resources at the 2026-11-04 read — because that index is the facilitator's own settlement record. No submission form, no review queue, no separate listing fee.

3 · Federation into the public aggregation. OMNIGATE's index is one of the sources read by Script Master Labs' /x402-directory aggregation — 16,963 resources across all of its sources at the 2026-11-03 read — and the indexes are re-probed daily on the public /x402-index-health page, so a listing that rots gets caught in public, including ours.

How the flow works

  1. Challenge. A buyer calls your route with no payment header. Your server answers HTTP 402 with the challenge — your price, your wallet, the discovery declaration — in the JSON body and base64-encoded in the PAYMENT-REQUIRED header.
  2. Signature. The buyer's x402 v2 client signs an EIP-3009 authorization for your price plus the 0.005 USDC fee leg to the facilitator fee wallet, and retries with the PAYMENT-SIGNATURE header.
  3. Verify. Your server forwards the payload to OMNIGATE's POST /verify — free and read-only. An invalid or fee-less payment is refused here; a refusal broadcasts nothing and consumes neither authorization.
  4. Settle. Your server calls POST /settle. OMNIGATE broadcasts both authorizations: your price lands in your payTo, the 0.005 USDC fee lands in the fee wallet.
  5. Serve + get indexed. Your server returns the resource with a PAYMENT-RESPONSE header carrying the settlement result — and your resource is now a row in the discovery index.

The kit

Three snippets, one per stack, all doing the same five steps. The only values you change are PAY_TO (your wallet) and PRICE (your price in USDC atomic units — 6 decimals, so $0.02 is 20000). Each snippet ships with Base wired; Arc is a second accepts entry away (network eip155:5042, asset 0x3600000000000000000000000000000000000000 — USDC on Arc, per /supported).

Express (Node 18+)

// ─── x402 starter kit: Express ─────────────────────────────────────────────
// One paid route, settled through OMNIGATE (Script Master Labs' facilitator).
// Requires: npm i express   (Node 18+ for global fetch)
// Verified 2026-11-04 against OMNIGATE's live /supported, a live 402
// challenge from a production SML route, and live /verify + /settle calls.
import express from "express";

const FACILITATOR = "https://mcp-x402.onrender.com"; // OMNIGATE
const PAY_TO = "0xYourWalletAddress";   // ← your wallet: settlement lands here
const PRICE = "20000";                  // $0.02 — USDC atomic units (6 decimals)
const USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";

// The payment requirements your route advertises in its 402 challenge.
// Shape copied from a live SML production challenge (2026-11-04).
const REQUIREMENTS = {
  scheme: "exact",
  network: "eip155:8453",               // Base (OMNIGATE also settles Arc, eip155:5042)
  amount: PRICE,
  asset: USDC_BASE,
  payTo: PAY_TO,
  maxTimeoutSeconds: 60,
  extra: { name: "USD Coin", version: "2" },  // EIP-712 domain of Base USDC
};

function challenge(url, description, example) {
  return {
    x402Version: 2,
    error: "payment_required",
    resource: { url, description, mimeType: "application/json" },
    accepts: [REQUIREMENTS],
    extensions: {
      bazaar: {                          // discovery declaration — this is
        discoverable: true,              // what gets you indexed on settle
        info: {
          input: { type: "http", method: "GET" },
          output: { type: "json", example },
        },
      },
    },
  };
}

async function facilitator(path, paymentPayload) {
  const r = await fetch(FACILITATOR + path, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      x402Version: 2,
      paymentPayload,
      paymentRequirements: REQUIREMENTS,
    }),
  });
  return r.json();
}

// OMNIGATE takes its 0.005 USDC fee as a second EIP-3009 authorization the
// buyer's client attaches at paymentPayload.fee (shape: OMNIGATE /supported).
// Your server never touches it — you forward the payload exactly as received,
// and /verify + /settle refuse a settlement whose fee leg is missing.
export function requirePayment(description, example) {
  return async (req, res, next) => {
    const url = `${req.protocol}://${req.get("host")}${req.originalUrl}`;
    const body = challenge(url, description, example);
    const header = req.get("PAYMENT-SIGNATURE");       // x402 v2 payment header
    if (!header) {
      return res
        .status(402)
        .set("PAYMENT-REQUIRED", Buffer.from(JSON.stringify(body)).toString("base64"))
        .json(body);
    }
    let paymentPayload;
    try {
      paymentPayload = JSON.parse(Buffer.from(header, "base64").toString("utf8"));
    } catch {
      return res.status(402).json(body);
    }
    const verify = await facilitator("/verify", paymentPayload);
    if (!verify.isValid) {
      return res.status(402).json({ ...body, error: verify.invalidReason || "invalid_payment" });
    }
    const settle = await facilitator("/settle", paymentPayload);
    if (!settle.success) {
      return res.status(402).json({ ...body, error: settle.errorReason || "settlement_failed" });
    }
    res.set("PAYMENT-RESPONSE", Buffer.from(JSON.stringify(settle)).toString("base64"));
    next();
  };
}

const app = express();
app.get(
  "/report",
  requirePayment("One full report", { ok: true }),
  (req, res) => res.json({ ok: true, report: "your paid payload here" })
);
app.listen(3000, () => console.log("paid API on :3000 — try: curl -i localhost:3000/report"));

FastAPI (Python)

# ─── x402 starter kit: FastAPI ─────────────────────────────────────────────
# One paid route, settled through OMNIGATE (Script Master Labs' facilitator).
# Requires: pip install fastapi httpx uvicorn
# Verified 2026-11-04 against OMNIGATE's live /supported, a live 402
# challenge from a production SML route, and live /verify + /settle calls.
import base64
import json

import httpx
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

FACILITATOR = "https://mcp-x402.onrender.com"  # OMNIGATE
PAY_TO = "0xYourWalletAddress"  # <- your wallet: settlement lands here
PRICE = "20000"  # $0.02 — USDC atomic units (6 decimals)
USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"

# The payment requirements your route advertises in its 402 challenge.
# Shape copied from a live SML production challenge (2026-11-04).
REQUIREMENTS = {
    "scheme": "exact",
    "network": "eip155:8453",  # Base (OMNIGATE also settles Arc, eip155:5042)
    "amount": PRICE,
    "asset": USDC_BASE,
    "payTo": PAY_TO,
    "maxTimeoutSeconds": 60,
    "extra": {"name": "USD Coin", "version": "2"},  # EIP-712 domain of Base USDC
}


def challenge(url: str, description: str, example: dict) -> dict:
    return {
        "x402Version": 2,
        "error": "payment_required",
        "resource": {"url": url, "description": description,
                     "mimeType": "application/json"},
        "accepts": [REQUIREMENTS],
        "extensions": {
            "bazaar": {  # discovery declaration — what gets you indexed on settle
                "discoverable": True,
                "info": {
                    "input": {"type": "http", "method": "GET"},
                    "output": {"type": "json", "example": example},
                },
            }
        },
    }


def b64(obj: dict) -> str:
    return base64.b64encode(json.dumps(obj).encode()).decode()


async def facilitator(path: str, payment_payload: dict) -> dict:
    body = {"x402Version": 2, "paymentPayload": payment_payload,
            "paymentRequirements": REQUIREMENTS}
    async with httpx.AsyncClient(timeout=30) as client:
        r = await client.post(FACILITATOR + path, json=body)
        return r.json()


# OMNIGATE takes its 0.005 USDC fee as a second EIP-3009 authorization the
# buyer's client attaches at paymentPayload.fee (shape: OMNIGATE /supported).
# Your server never touches it — forward the payload exactly as received;
# /verify and /settle refuse a settlement whose fee leg is missing.
async def require_payment(request: Request, description: str, example: dict):
    """Returns (settle_result, None) when paid, or (None, 402 response)."""
    body = challenge(str(request.url), description, example)
    header = request.headers.get("PAYMENT-SIGNATURE")  # x402 v2 payment header
    if not header:
        return None, JSONResponse(body, status_code=402,
                                  headers={"PAYMENT-REQUIRED": b64(body)})
    try:
        payment_payload = json.loads(base64.b64decode(header))
    except Exception:
        return None, JSONResponse(body, status_code=402)
    verify = await facilitator("/verify", payment_payload)
    if not verify.get("isValid"):
        body["error"] = verify.get("invalidReason") or "invalid_payment"
        return None, JSONResponse(body, status_code=402)
    settle = await facilitator("/settle", payment_payload)
    if not settle.get("success"):
        body["error"] = settle.get("errorReason") or "settlement_failed"
        return None, JSONResponse(body, status_code=402)
    return settle, None


app = FastAPI()


@app.get("/report")
async def report(request: Request):
    settle, err = await require_payment(request, "One full report", {"ok": True})
    if err:
        return err
    return JSONResponse(
        {"ok": True, "report": "your paid payload here"},
        headers={"PAYMENT-RESPONSE": b64(settle)},
    )


# run: uvicorn main:app --port 3000   then: curl -i localhost:3000/report

Hono (TypeScript, Node)

// ─── x402 starter kit: Hono ────────────────────────────────────────────────
// One paid route, settled through OMNIGATE (Script Master Labs' facilitator).
// Requires: npm i hono @hono/node-server   (Node 18+ for global fetch)
// Verified 2026-11-04 against OMNIGATE's live /supported, a live 402
// challenge from a production SML route, and live /verify + /settle calls.
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import type { Context, Next } from "hono";

const FACILITATOR = "https://mcp-x402.onrender.com"; // OMNIGATE
const PAY_TO = "0xYourWalletAddress";   // ← your wallet: settlement lands here
const PRICE = "20000";                  // $0.02 — USDC atomic units (6 decimals)
const USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";

// The payment requirements your route advertises in its 402 challenge.
// Shape copied from a live SML production challenge (2026-11-04).
const REQUIREMENTS = {
  scheme: "exact",
  network: "eip155:8453",               // Base (OMNIGATE also settles Arc, eip155:5042)
  amount: PRICE,
  asset: USDC_BASE,
  payTo: PAY_TO,
  maxTimeoutSeconds: 60,
  extra: { name: "USD Coin", version: "2" },  // EIP-712 domain of Base USDC
};

const b64encode = (obj: unknown) =>
  Buffer.from(JSON.stringify(obj)).toString("base64");
const b64decode = (s: string) =>
  JSON.parse(Buffer.from(s, "base64").toString("utf8"));

function challenge(url: string, description: string, example: unknown) {
  return {
    x402Version: 2,
    error: "payment_required",
    resource: { url, description, mimeType: "application/json" },
    accepts: [REQUIREMENTS],
    extensions: {
      bazaar: {                          // discovery declaration — this is
        discoverable: true,              // what gets you indexed on settle
        info: {
          input: { type: "http", method: "GET" },
          output: { type: "json", example },
        },
      },
    },
  };
}

async function facilitator(path: string, paymentPayload: unknown) {
  const r = await fetch(FACILITATOR + path, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      x402Version: 2,
      paymentPayload,
      paymentRequirements: REQUIREMENTS,
    }),
  });
  return r.json();
}

// OMNIGATE takes its 0.005 USDC fee as a second EIP-3009 authorization the
// buyer's client attaches at paymentPayload.fee (shape: OMNIGATE /supported).
// Your server never touches it — forward the payload exactly as received;
// /verify and /settle refuse a settlement whose fee leg is missing.
export function requirePayment(description: string, example: unknown) {
  return async (c: Context, next: Next) => {
    const body = challenge(c.req.url, description, example);
    const header = c.req.header("PAYMENT-SIGNATURE");   // x402 v2 payment header
    if (!header) {
      return c.json(body, 402, { "PAYMENT-REQUIRED": b64encode(body) });
    }
    let paymentPayload: unknown;
    try {
      paymentPayload = b64decode(header);
    } catch {
      return c.json(body, 402);
    }
    const verify = await facilitator("/verify", paymentPayload);
    if (!verify.isValid) {
      return c.json({ ...body, error: verify.invalidReason ?? "invalid_payment" }, 402);
    }
    const settle = await facilitator("/settle", paymentPayload);
    if (!settle.success) {
      return c.json({ ...body, error: settle.errorReason ?? "settlement_failed" }, 402);
    }
    c.header("PAYMENT-RESPONSE", b64encode(settle));
    await next();
  };
}

const app = new Hono();
app.get("/report", requirePayment("One full report", { ok: true }), (c) =>
  c.json({ ok: true, report: "your paid payload here" })
);
serve({ fetch: app.fetch, port: 3000 });

Test it before you trust it

Call your route unpaid — curl -i localhost:3000/report — and expect HTTP 402 with the challenge in the body and the PAYMENT-REQUIRED header, the same shape a production SML route served under probe on 2026-11-04. After your first real settlement, find your resource URL in OMNIGATE's discovery index. If a buyer's client forgets the fee leg, nothing breaks and nothing settles: /verify answers facilitator_fee_required and your route re-issues the 402 — the snippets already handle that path.

The proof, dated

CheckResultWhen
OMNIGATE /supported — kinds, fee, allowancex402 v2 exact on Base + Arc; fee 0.005 USDC flat (5000 atomic); free_allowance_per_day: 0; settlement_proven: true2026-11-04, live read
Unpaid probe of a production SML paid routeHTTP 402; challenge keys x402Version / error / resource / accepts / extensions; the snippets emit this exact shape2026-11-04, live probe
Starter-kit test runs (all three snippets, local servers)Unpaid → 402 with matching PAYMENT-REQUIRED header; payment attempt with no fee leg → live /verify refusal facilitator_fee_required, surfaced as a fresh 402; nothing broadcast2026-11-04
OMNIGATE discovery index size129 resources2026-11-04, live read
Failure-mode audit of SML's listed resources (all public indexes, deduplicated)224 of 241 (93%) answered a clean, parseable 4022026-11-03, full audit

Honest limits. OMNIGATE's published settlement proof is SELF-PROOF — the operator's own wallet settling to the operator's own recipient (the Arc canary is transaction 0xa9a2d63d92834026a3c9cbc13df18e67a16a608810ca30970bcc1e6993adaff2, published in /supported). SELF-PROOF proves the rails settle end-to-end and charge exactly the published fee. It does not prove outside adoption, and no external seller has settled through OMNIGATE yet — the 129 resources in its index today are Script Master Labs' own fleet. This kit is the open door; the index will show the first outside resource the day one settles. Presence in an index is also not demand: the metric that matters is unique paying wallets over time, which no index publishes and which Script Master Labs does not manufacture with self-payments.

Copy the kit Integration questions — tim@scriptmasterlabs.com

Selling on XRPL, Xahau or Solana?

Those rails settle through 402Proof, Script Master Labs' second facilitator, and that door is partner-keyed by design: the XRPL fee accrues per settlement instead of being collected inside the settlement, so keys are issued to known sellers. The full fee table for every rail is on /settle-through-sml. To request a partner key, email tim@scriptmasterlabs.com with your service's base URL, the rail (XRPL RLUSD, XRPL USDC, Xahau XAH, or Solana), and your receiving wallet address. Keys are issued by hand — expect a reply within one business day.

Frequently asked questions

What does it cost to settle through OMNIGATE?

0.005 USDC per settlement, flat, charged from the first settlement. There is no free settlement allowance (OMNIGATE publishes free_allowance_per_day: 0), no monthly minimum and no percentage of your price. Your buyer pays the fee as a second EIP-3009 authorization attached to the same payment, at paymentPayload.fee; your price is paid to your payTo wallet in full. Terms verified live on 2026-11-04 at https://mcp-x402.onrender.com/supported.

Do I need an account, an API key or a signup to use OMNIGATE?

No. On Base and Arc the door is open self-serve: your resource server answers the 402 challenge and calls OMNIGATE's POST /verify and POST /settle at https://mcp-x402.onrender.com. The starter kit on this page is the whole integration. (The XRPL, Xahau and Solana rails run through SML's second facilitator, 402Proof, and are partner-keyed — see /settle-through-sml.)

Does OMNIGATE ever hold my money?

Never. Settlement pays your own payTo wallet directly. The buyer signs two EIP-3009 authorizations — one to your payTo for your price, one for exactly 0.005 USDC to the facilitator fee wallet — and OMNIGATE broadcasts them. It holds neither amount at any point.

How does my API get listed in the discovery index?

Settle. Each snippet's 402 challenge carries the bazaar discovery declaration; when a payment for your resource settles through OMNIGATE, the resource appears in OMNIGATE's /discovery/resources index automatically — the index is the facilitator's settlement record. There is no submission form and no separate listing fee. OMNIGATE's index also feeds Script Master Labs' public /x402-directory aggregation. At the 2026-11-04 read the OMNIGATE index listed 129 resources and the /x402-directory aggregation read 16,963 resources across all of its sources at its 2026-11-03 read.

What does my buyer need to pay me?

An x402 v2 client that signs EIP-3009 transfer authorizations and, for OMNIGATE settlements, attaches the facilitator fee leg at paymentPayload.fee in the shape published at /supported. If a client skips the fee leg, OMNIGATE's /verify answers facilitator_fee_required and nothing is broadcast or charged — your route simply re-issues the 402, which the starter-kit snippets already do.

Can I settle on Arc as well as Base?

Yes — same facilitator, same 0.005 USDC fee. Add a second entry to the accepts array with network eip155:5042 and asset 0x3600000000000000000000000000000000000000 (USDC on Arc), per OMNIGATE's /supported. The snippets ship with Base wired and Arc marked in the comments.

I sell on XRPL, Xahau or Solana instead. Is there a kit for me?

Those rails settle through 402Proof, Script Master Labs' second facilitator, and onboarding there is partner-keyed by design. The full fee table for every rail and the request-a-partner-key block are on /settle-through-sml: email tim@scriptmasterlabs.com with your service's base URL, the rail, and your receiving wallet address; keys are issued by hand with a reply inside one business day.