Zunivo Docs

zunivo · the payment layer for ai agents on arc

x402 payments on Arc

Charge USDC per API call. Let AI agents pay for it autonomously — no card, no account, no human. Standard x402 on Arc's USDC-native chain.

server middlewareagent clientEIP-3009 facilitatorArc Testnet

What is this

x402 is the HTTP-native payment standard that revives the dormant 402 Payment Required status code. A server answers a request with 402 and a machine-readable price; a client attaches a signed payment and retries; the resource is served. It's governed by the Linux Foundation and backed by Coinbase, Circle, Visa, Stripe, and Google — tens of millions of agent payments already run on it.

But its facilitators cover Base, Polygon, Arbitrum, and Solana — not Arc. zunivo-x402-arc is that missing piece: it lets an x402-speaking agent pay for an API on Arc, using Arc's USDC-native gas and sub-second finality. Same standard the agent already knows; a new chain it can now pay on.

How x402 works

Request. An agent calls a priced endpoint like any HTTP request.
402. The server replies 402 Payment Required with PaymentRequirements: price, token, recipient, network.
Pay. The agent pays in USDC on Arc — either by settling a Zunivo order through our verified router, or (advanced) by signing an EIP-3009 authorization.
Retry with proof. The agent resends the request with an X-PAYMENT header.
Serve. The server verifies settlement, returns the data, and adds an X-PAYMENT-RESPONSE receipt header.

Quickstart

# install
npm install zunivo-x402-arc

# run a paid API (needs a Zunivo API key from https://api.zunivo.io)
ZUNIVO_API=https://api.zunivo.io ZUNIVO_KEY=zk_… PAY_TO=you.zunivo npm run example:server

# in another terminal, an agent pays it — one command, one function
AGENT_PK=0x… npm run example:agent

Server — charge USDC per call

Three lines protect any Express route:

import express from "express";
import { paymentRequired } from "zunivo-x402-arc";

const app = express();
const pay = paymentRequired({
  price: "0.05",              // human USDC
  payTo: "you.zunivo",         // a .zunivo name or 0x address
  zunivoApi: "https://api.zunivo.io",
  zunivoKey: process.env.ZUNIVO_KEY,
});

app.get("/v1/data", pay, (req, res) => res.json({ premium: "…" }));

Unpaid requests receive a spec-compliant 402. Paid requests pass through with an X-PAYMENT-RESPONSE receipt header. Each payment is consumed exactly once.

The 402 a client sees

{
  "x402Version": 1,
  "accepts": [{
    "scheme": "exact",
    "network": "arc-testnet",
    "maxAmountRequired": "50000",        // 0.05 USDC in 6-decimal base units
    "asset": "0x3600000000000000000000000000000000000000",
    "payTo": "you.zunivo",
    "maxTimeoutSeconds": 120,
    "extra": { "name": "USDC", "decimals": 6, "chainId": 5042002 }
  }]
}

Agent — pay automatically

The whole dance — discover price, pay on Arc, retry with proof — is one wrapped fetch:

import { createX402Fetch } from "zunivo-x402-arc/client";

const x402fetch = createX402Fetch({
  privateKey: process.env.AGENT_PK,
  maxPrice: "1",                  // refuse anything pricier (optional guard)
  onEvent: (e) => console.log(e.type),
});

const res = await x402fetch("https://api.example.dev/v1/data");
const data = await res.json();   // paid for, on-chain, no human

Two settlement backends

The middleware's verify is pluggable. Two paths ship:

BackendHow it settlesStatus
Zunivo orders (default)Payment settles through our verified Arc router 0x4210…Ea55. X-PAYMENT payload is { zunivoOrderId }.works today
EIP-3009 facilitatorAgent signs a TransferWithAuthorization; the facilitator submits it to Arc's USDC. Fully standard, gasless.reference
why twoThe Zunivo-order backend needs no new on-chain code and is verified end-to-end today. The EIP-3009 path is the purest x402 scheme; it's provided as a reference so standard agents interoperate, pending on-chain confirmation of Arc USDC's 3009 selectors.

EIP-3009 (gasless, advanced)

Arc's USDC is issued by Circle and supports EIP-3009 (TransferWithAuthorization) — the same mechanism x402 uses elsewhere. The agent signs off-chain; a facilitator submits on-chain; no prior approval, no gas for the agent.

import { signPayment, createFacilitator } from "zunivo-x402-arc/facilitator";

// agent side — sign, no gas, no transaction
const payload = await signPayment({ privateKey, to: "you.zunivo", price: "0.05" });

// facilitator side — verify signature, then submit to Arc USDC
const fac = createFacilitator({ submitterKey });
const { settled, txHash } = await fac.settle(payload);
before productionConfirm Arc's USDC (0x3600…0000) exposes the transferWithAuthorization / receiveWithAuthorization selectors and its EIP-712 domain (name, version) on-chain. Until then, use the Zunivo-order backend as the tested path.

Decimals on Arc (read this)

criticalArc's native USDC gas token uses 18 decimals; the USDC ERC-20 interface uses 6 decimals. x402 amounts are ERC-20 amounts → always 6 decimals. Mixing them mis-scales a payment by 10¹². This library keeps the two paths strictly separate; if you compute amounts yourself, use toUsdcBaseUnits() / fromUsdcBaseUnits().

Arc Testnet constants

FieldValue
Chain ID5042002
RPChttps://rpc.testnet.arc.network
Explorerhttps://testnet.arcscan.app
USDC (ERC-20, 6 dp)0x3600000000000000000000000000000000000000
Zunivo router0x4210D40a9899e42b4946B9dC7E0C35d3cf14Ea55
x402 network idarc-testnet
Faucetfaucet.circle.com