Skip to content

Developers · quickstart

From a clone to a settled payment.

Everything runs on your machine first: a local chain, the contracts, a facilitator and a demo provider, with published test keys and no network. The same code then points at Elysium Testnet by changing one option.

What you need

  1. 1

    Node 22 or later and pnpm 10

    The repository is a pnpm workspace; corepack enables the pinned pnpm.
  2. 2

    Foundry, for the local chain

    anvil runs the local chain and forge deploys the contracts. Install from getfoundry.sh. Paying against the testnet needs neither.
  3. 3

    The repository

    The packages are workspace packages; they are not yet published to npm. Clone the repository, run pnpm install, and build the packages once.

Status: Elysium Testnet (chain 99801), pre-audit. Test assets have no value. Mainnet is not deployed.

Run the whole protocol locally

One command starts the chain, deploys the contracts, mints the test asset to the demo agent, and serves a provider with two paid services. A second command plays the agent.

terminalRepository
# Everything local: anvil, the contracts, a facilitator and a demo provider. No funds, no network.
pnpm install
pnpm -r --filter './packages/*' build
pnpm --filter @epp402/cli dev -- dev            # terminal 1
pnpm --filter @epp402/cli dev -- simulate       # terminal 2: discover, quote, session, pay, verify, complete
pnpm --filter @epp402/cli dev -- inspect http://127.0.0.1:4021

simulate prints the discovery check, the session it opened, the signed quote, the 402 and the paid response, the transaction hash, the receipt and its verification against the chain, and the completed session with its history. Add --json for the objects themselves, and --registry http://127.0.0.1:4402 when the local API from pnpm dev is running, to see the session appear in the explorer.

Ports: anvil 8402, the stack's facilitator 4020, the demo provider 4021. pnpm dev at the repository root also starts embedded Postgres on 54402, the API on 4402 and this website on 3402.

Make a paid call

The agent SDK does the whole lifecycle: discover, quote, session, intent, x402 handshake, receipt verification, publication.

agent.tsRepository API
import { privateKeyToAccount } from "viem/accounts";
import { Epp402Agent } from "@epp402/client";

const agent = new Epp402Agent({
  signer: privateKeyToAccount(process.env.AGENT_PRIVATE_KEY as `0x${string}`),
  network: "elysium-testnet",
  registryUrl: "https://epp402-core.fly.dev", // optional: publishes sessions, quotes, intents and receipts
});

const { document } = await agent.discover("https://inference.example.com");
const asset = document.assets[0]!.address;

const session = await agent.openSession({ asset, spendingLimit: "25000000" }); // 25 EPPUSD
const quote = await agent.requestQuote(document, "inference.standard", { text: "Summarise Q3." }, { session });

const paid = await agent.pay(quote, { session, body: { text: "Summarise Q3." } });
console.log(paid.settlement.txHash, paid.receipt?.receiptId, paid.verification?.ok);

await agent.completeSession(session);
  1. 1

    discover

    Fetches /.well-known/epp402.json, validates it, checks the provider's signature and that it settles on your network.
  2. 2

    openSession

    A buyer-side budget in the payment asset's minor units. Everything the agent buys is recorded under it.
  3. 3

    requestQuote

    A provider-signed price for your exact parameters, bound to your address and the session.
  4. 4

    pay

    Checks the policy, signs the intent, drives the x402 402 → PAYMENT-SIGNATURE exchange with the signed intent in EPP-INTENT, verifies the receipt, records the settlement.
  5. 5

    completeSession

    Closes the budget. With a registry URL the session, quote, intent and receipt were published along the way.

The full option tables are on the Agent SDK page.

Sell something

A provider is one configuration object. The SDK serves the discovery document, signs quotes and receipts, and mounts the x402 gate in front of your handler.

server.tsRepository API
import { serve } from "@hono/node-server";
import { privateKeyToAccount } from "viem/accounts";
import { createProvider } from "@epp402/server";

const provider = createProvider({
  providerId: "inference.example.com",
  name: "Example Inference",
  url: "https://inference.example.com",
  network: "elysium-testnet",
  signer: privateKeyToAccount(process.env.PROVIDER_PRIVATE_KEY as `0x${string}`),
  facilitatorUrl: "https://epp402-core.fly.dev/facilitator",
  registryUrl: "https://epp402-core.fly.dev",
  services: [
    {
      id: "inference.standard",
      name: "Standard inference",
      category: "ai-inference",
      endpoint: "/v1/inference",
      method: "POST",
      price: "0.01", // list price in EPPUSD; the quote below prices each request
      quote: {
        price: (req) => 10_000n + 5n * BigInt(String((req.params as { text?: string }).text ?? "").length / 100),
      },
    },
  ],
});

// Discovery, quotes, receipts and the x402 gate are already mounted; add your handler.
const app = provider.app();
app.post("/v1/inference", async (c) => c.json(await infer(await c.req.json())));

serve({ fetch: app.fetch, port: 4031 }, () => provider.publish());

Options, the service shape and what the gate does are on the Provider SDK page. The CLI scaffolds this file with epp402 init.

Without an SDK

The wire format is x402 v2. Any client that can sign an EIP-3009 authorization can pay; the EPP-402 objects arrive in headers it may ignore.

terminalIllustrative
# 1. Ask without paying: the gate answers 402 with its terms.
curl -i https://inference.example.com/v1/inference -X POST -d '{"text":"hi"}'
#   HTTP/1.1 402 Payment Required
#   PAYMENT-REQUIRED: eyJ4NDAyVmVyc2lvbiI6MiwicmVzb3VyY2Ui...   (base64 JSON)

# 2. Sign the EIP-3009 authorization the terms describe, then retry with it.
curl -i https://inference.example.com/v1/inference -X POST -d '{"text":"hi"}' \
  -H "PAYMENT-SIGNATURE: eyJ4NDAyVmVyc2lvbiI6MiwiYWNjZXB0ZWQi..."
#   HTTP/1.1 200 OK
#   PAYMENT-RESPONSE: eyJzdWNjZXNzIjp0cnVlLCJ0cmFuc2FjdGlvbiI6IjB4...
#   EPP-RECEIPT: eyJwcm90b2NvbCI6ImVwcC00MDIiLCJyZWNlaXB0SWQi...

What a plain x402 client gets, and what an EPP-402 client adds, is on the x402 compatibility page.

Hosted endpoints

The facilitator and the registry run as one service. Point the SDKs at them to leave the local stack.

Endpoints
5
  • Service root

    https://epp402-core.fly.dev/

    Network, contract addresses and assets as the service sees them.
  • Facilitator

    https://epp402-core.fly.dev/facilitator

    x402 v2: /supported, /verify, /settle; EPP-402 adds /status/:transactionId and /health.
  • Registry

    https://epp402-core.fly.dev/v1

    Providers, services, sessions, quotes, intents, settlements, receipts, stats, network, events.
  • Health

    https://epp402-core.fly.dev/health

    Database, RPC and the facilitator's gas balance.
  • This site

    https://epp402.vercel.app

    Explorer, receipt verification, documentation.

Contract addresses are read from contracts/deployments/99801.json. Until that manifest exists the facilitator settles directly on the configured token and the indexer stays idle; the network page shows the current state.

Packages

What each workspace package is for. Import from the specific package; @epp402/sdk re-exports the protocol, network, client and server surfaces for convenience.

Workspace packages
11
  • @epp402/protocol

    schemas, signatures

    The normative objects as zod schemas, EIP-712 typed data, canonical hashing, ids, error codes, header codecs.
  • @epp402/elysium

    network adapter

    Verified network parameters, the viem chain, explorer links, deployment manifests.
  • @epp402/authorization

    buyer policy

    The spending policy engine and Payment Intent construction and binding.
  • @epp402/session

    buyer state

    The session state machine with in-memory and Postgres stores.
  • @epp402/receipts

    proof

    Issue a receipt as a provider; verify one against signature and chain as anyone.
  • @epp402/client

    agent SDK

    Epp402Agent, discovery and quote helpers, PaymentFailed.
  • @epp402/server

    provider SDK

    createProvider: discovery document, quotes, the x402 gate, receipts, on-chain registration.
  • @epp402/facilitator

    settlement

    An x402 facilitator with replay protection and the EPP402Settlement relay.
  • @epp402/registry

    index

    Crawler, chain indexer, the /v1 API and the worker.
  • @epp402/cli

    tooling

    epp402: init, dev, inspect, simulate, publish, registry, receipts, session, doctor.
  • @epp402/sdk

    umbrella

    Re-exports protocol, elysium, authorization, session, receipts, client and server.