Equinox logo

Equinox

Equinox lets AI agents automatically pay for APIs using USDG on Robinhood Chain.

the idea
AI Agent
   ↓
Equinox
   ↓
Paid API
   ↓
x402
   ↓
USDG on Robinhood Chain

Give an AI a budget and let it safely buy useful things on the internet. Your agent says "use this paid API, but don't spend more than $0.05". Equinox detects the x402 payment request, checks your spending rules, signs a USDG payment on Robinhood Chain, retries the request and hands back the data.

  • MCP server: plug it into OpenAI Codex or any MCP client.
  • TypeScript SDK: equinox.fetch() works like fetch(), but can pay.
  • Local REST API + web console: for other agent frameworks, and for you to watch the money.
  • Demo paid API: GET /demo/weather for $0.001, so you can try the whole buyer + seller loop.

Download the project
The complete source: MCP server, SDK, REST API, demo API, this website, tests and config templates. Wallet keys are never included.

Download equinox402-source.zip

Five-minute setup

You need Node.js 20+ (nodejs.org).

1. Unzip and install

Unzip equinox402-source.zip, open a terminal in the equinox402 folder and run:

terminal
cd equinox402
npm install

2. Create a wallet

terminal
npm run wallet:new

This writes a fresh buyer wallet and a demo seller wallet into .env and prints only the public addresses. The private key never appears on screen.

3. Fund it on Robinhood Chain

Equinox runs on Robinhood Chain mainnet (chain id 4663) with the real USDG, so this is real money. The demo costs $0.001 per call; keep only a few dollars in the wallet.
WhatWhereWhy
USDG → buyerSend from any wallet or exchange that supports Robinhood ChainThis is what your agent spends
A little ETH → buyerRobinhood Chain ETH (bridge or send)Gas for the one-time Permit2 approval only
A little ETH → demo sellerSameThe demo seller settles payments on-chain and pays that gas

4. Approve USDG once

terminal
npm run approve

USDG on Robinhood Chain has no built-in gasless approval, so x402 pays it through Permit2. This one transaction lets Permit2 move USDG when you sign. Every payment after it is a gasless signature.

5. Start Equinox

terminal
npm run dev:api

Open the console at http://localhost:4020/app.html and hit Fetch & pay on the prefilled /demo/weather URL. You just paid $0.001 USDG.

6. Add it to Codex

The project includes .codex/config.toml, so running codex inside the folder picks it up. To use Equinox from any project, see Codex config. Then ask Codex:

"Get the current weather from http://localhost:4020/demo/weather. You can spend up to $0.03 if required."

Codex calls x402_inspect, sees $0.001, calls x402_fetch, Equinox pays, and Codex answers.

How a payment works

flow
Codex ─▶ Equinox MCP ─▶ HTTP request ─▶ API answers 402
                                           │
                             read PAYMENT-REQUIRED (x402 V2)
                                           │
                   check policy: price, daily budget, domain, auto-pay
                                           │
                  sign USDG Permit2 transfer (official @x402/evm scheme)
                                           │
                     retry request with PAYMENT-SIGNATURE header
                                           │
               seller's facilitator verifies + settles on Robinhood Chain
                                           │
                   PAYMENT-RESPONSE (tx signature) + the data
                                           │
                               result returned to Codex

Equinox uses the official x402 V2 packages (@x402/core, @x402/evm, @x402/express). Legacy X-PAYMENT headers are not supported.

MCP tools

ToolWhat it does
x402_fetchFetch a URL. If it needs an x402 payment within policy, pay and return the data. Free URLs are returned as-is.
x402_inspectReturn the price, asset, network, recipient and scheme without paying.
wallet_balanceETH and USDG balance of the payment wallet, and whether the Permit2 approval is done (never keys).
spending_statusspentTodayUsd, dailyLimitUsd, remainingTodayUsd.
payment_historyPast payments: timestamp, domain, amount, asset, recipient, tx signature, status.

x402_fetch input

input
{ "url": "https://example.com/api/research", "method": "GET", "maxPriceUsd": 0.05 }

Output

output
{
  "success": true,
  "paid": true,
  "amountUsd": 0.01,
  "network": "robinhood",
  "transaction": "5Kx…",
  "data": {}
}

Above the auto-pay threshold

output
{ "success": false, "approvalRequired": true, "amountUsd": 0.08, "resource": "…", "recipient": "…" }

If the MCP client supports elicitation, Equinox asks the human directly instead. The agent itself can never approve.

Instructions Equinox gives the agent

  • Use x402_inspect when you need to determine the cost of a paid endpoint.
  • Use x402_fetch when the user has authorized sufficient spending.
  • Never intentionally split transactions to bypass a payment limit.
  • Never increase the requested spending limit.
  • Never reveal wallet secrets.

Spending policy

.env
EQUINOX402_MAX_PAYMENT_USD=0.10     # any single payment above this is rejected
EQUINOX402_DAILY_LIMIT_USD=5.00     # total per UTC day
EQUINOX402_AUTO_PAY_LIMIT_USD=0.02  # at/below: pays automatically; above: approvalRequired
EQUINOX402_ALLOWED_DOMAINS=api.example.com,data.example.com   # optional allowlist
  • The caller's maxPriceUsd can only tighten these limits, never loosen them.
  • In-flight payments are reserved against the daily budget, so firing many small requests in parallel can't sneak past it.
  • Allowlist entries match the host and its subdomains. Redirects are followed manually and every hop is re-checked. A payment credential is never forwarded across a redirect.
  • Version 1 pays USDG only, exact scheme only, on Robinhood Chain only.

.env reference

This is the full .env.example. npm run wallet:new copies it to .env and fills in the wallet.

.env.example
# ─── Network ──────────────────────────────────────────────
# Robinhood Chain. Internally mapped to CAIP-2:
#   mainnet = eip155:4663   (default — real USDG)
#   testnet = eip155:46630  (no official USDG: set EQUINOX402_TOKEN_ADDRESS)
EQUINOX402_NETWORK=mainnet
# Optional custom RPC (recommended for mainnet)
EQUINOX402_RPC_URL=

# ─── Wallet (buyer) ───────────────────────────────────────
# `npm run wallet:new` fills this in (0x-prefixed hex EVM private key).
# NEVER share this file. Equinox never returns this value through MCP/REST/logs.
EQUINOX402_PRIVATE_KEY=
# …or point at a file containing the key instead:
EQUINOX402_KEY_PATH=

# ─── Payment token ────────────────────────────────────────
# Default: USDG 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 via Permit2.
EQUINOX402_TOKEN_ADDRESS=

# ─── Spending policy ──────────────────────────────────────
EQUINOX402_MAX_PAYMENT_USD=0.10
EQUINOX402_DAILY_LIMIT_USD=5.00
EQUINOX402_AUTO_PAY_LIMIT_USD=0.02

# ─── Domain protection ────────────────────────────────────
# Comma-separated. If set, payments only go to these hosts (and subdomains).
EQUINOX402_ALLOWED_DOMAINS=

# ─── Local REST API + console ─────────────────────────────
EQUINOX402_API_PORT=4020
EQUINOX402_API_HOST=127.0.0.1

# ─── Storage ──────────────────────────────────────────────
# Payment history, idempotency cache and logs. Default: ~/.equinox402
EQUINOX402_DATA_DIR=

# ─── Demo seller (GET /demo/weather, $0.001 USDG) ─────────
# Seller key: receives demo payments and pays settlement gas (ETH).
EQUINOX402_DEMO_SELLER_PRIVATE_KEY=
EQUINOX402_DEMO_PAY_TO=
EQUINOX402_DEMO_PORT=4021
# Optional: a hosted standard x402 facilitator instead of the built-in one.
EQUINOX402_FACILITATOR_URL=

Codex config

Inside the project, .codex/config.toml is picked up automatically. To use Equinox from any project, add this to ~/.codex/config.toml with your own absolute path:

~/.codex/config.toml
[mcp_servers.equinox402]
command = "node"
args = ["--import", "tsx", "/absolute/path/to/equinox402/src/mcp/server.ts"]
startup_timeout_sec = 20
tool_timeout_sec = 120

# Optional per-agent overrides (these win over .env).
# The wallet key stays in equinox402/.env — never put it here.
[mcp_servers.equinox402.env]
EQUINOX402_MAX_PAYMENT_USD = "0.05"
EQUINOX402_DAILY_LIMIT_USD = "2.00"
EQUINOX402_AUTO_PAY_LIMIT_USD = "0.02"
EQUINOX402_ALLOWED_DOMAINS = "localhost,api.example.com"

On Windows, write the path with forward slashes, e.g. C:/Users/you/equinox402/src/mcp/server.ts.

Security

  • The private key is loaded from .env into a signer that only exposes getAddress() and signPayment(). It is never returned by any tool, response or log.
  • Wallets implement a small interface, so a remote, KMS or hardware signer can be dropped in later.
  • Replay protection: every authorization is hashed (SHA-256) and stored. A used authorization is never re-sent, and a settlement receipt already on record isn't counted as a new purchase.
  • Idempotency: paid requests carry an Idempotency-Key. Pass the same idempotencyKey again and you get the stored result, with no second payment.
  • Logs (~/.equinox402/equinox402.log): timestamp, URL, domain, amount, token, network, recipient, tx signature, settlement status and latency. Never keys or raw authorizations.
  • The REST API binds to 127.0.0.1, rejects non-localhost Host headers and sends no CORS headers, so other websites can't drive your wallet.
src/chain/wallet.ts
interface PaymentSigner {
  getAddress(): Promise<string>;
  signPayment(requirements: PaymentRequirements, ctx): Promise<PaymentPayload>;
}
Use a dedicated wallet that holds only what you're willing to let agents spend. Never share your .env file.

SDK

example.ts
import { Equinox } from "equinox402";

const equinox = new Equinox({
  maxPaymentUsd: 0.05,
  dailyLimitUsd: 5,
});

const response = await equinox.fetch("https://example.com/api", { maxPriceUsd: 0.01 });
console.log(await response.json());
console.log(response.payment); // { paid, amountUsd, transaction, … }

equinox.fetch behaves like native fetch. Policy refusals throw EquinoxPaymentError (check .approvalRequired / .code). equinox.request() returns the structured result without throwing. Also available: inspect(), balance(), spendingStatus(), history().

Try it: npm run demo in one terminal, npm run example in another.

REST API

npm run dev:api serves it at http://localhost:4020.

MethodPathBody
POST/fetch or /api/fetch{ url, method?, maxPriceUsd?, headers?, body?, idempotencyKey?, approved? }
POST/inspect{ url, method? }
GET/balance · /spending · /history?limit=50 · /statusnone
terminal
curl -X POST http://localhost:4020/fetch \
  -H "content-type: application/json" \
  -d '{"url":"http://localhost:4020/demo/weather","maxPriceUsd":0.05}'

approved: true is how a host app (like the console's approval dialog) confirms a payment above the auto-pay limit. Don't give that power to an agent.

Demo paid API

EndpointPricePurpose
GET /demo/weather$0.001 USDGAuto-pays under the default policy
GET /demo/premium$0.05 USDGAbove the auto-pay limit, so it shows the approval flow
terminal
curl -i http://localhost:4020/demo/weather   # → HTTP 402 + PAYMENT-REQUIRED

After payment:

response
{ "city": "Miami", "temperature": 82, "conditions": "Sunny" }

Built with the official @x402/express middleware and settled by the official @x402/evm facilitator running inside Equinox: it verifies the buyer's Permit2 signature and submits the transfer on Robinhood Chain, paying gas from the demo seller wallet. It's mounted inside npm run dev:api, or runs standalone on port 4021 with npm run demo.

Network

.env
EQUINOX402_NETWORK=mainnet          # → eip155:4663 (default)
EQUINOX402_RPC_URL=https://your-rpc-provider   # optional

Robinhood Chain testnet (eip155:46630) has no official USDG. To test there, deploy or pick a test token and set EQUINOX402_NETWORK=testnet plus EQUINOX402_TOKEN_ADDRESS.

Use a fresh wallet holding only what you're willing to let agents spend, and keep the limits tight.

Project files

equinox402/
src/
  mcp/        server.ts (stdio), tools.ts (tools + agent instructions)
  x402/       client.ts (payment flow), parser.ts (V2 headers), payment.ts
  chain/      wallet.ts (PaymentSigner), permit2.ts, balance.ts, connection.ts
  policy/     spending-policy.ts, domain-policy.ts
  storage/    payments.ts (history, replay + idempotency store)
  api/        server.ts (REST + web console)
  sdk/        index.ts (Equinox class)
  demo/       server.ts (paid /demo/*), setup.ts
  config.ts
web/          this website (static, no build step)
examples/     simple-fetch.ts, codex-example.md
scripts/      new-wallet.ts, pack-source.ts, mcp-smoke.ts
test/         offline end-to-end tests
.codex/       config.toml
AGENTS.md  .env.example  package.json  README.md  LICENSE

Scripts

CommandWhat it does
npm run wallet:newCreate a buyer wallet and demo seller in .env
npm run approveOne-time Permit2 approval for USDG
npm run dev:apiREST API, console and demo API on :4020
npm run demoDemo seller only, on :4021
npm run mcpMCP server on stdio
npm run exampleSDK example
npm testOffline tests
npm run mcp:smokeStart the MCP server and call its tools
npm run buildCompile to dist/
npm run pack:sourceRebuild the downloadable zip (keys excluded)

Roadmap

Version 1 is deliberately small: Robinhood Chain, USDG, x402 V2, exact payments, MCP, fetch, budgets, history and a demo API. The core is shaped so these can be added without rewriting it:

  • x402 upto payments for metered AI inference and variable usage
  • Paid MCP tools and agent-to-agent payments
  • Pay-per-search, pay-per-dataset, pay-per-compute
  • Service discovery and API marketplaces
  • Per-agent wallets and organization spending policies
  • Remote and hardware signers

License: MIT.