Pay-per-call API infrastructure, on-chain. Built on the XRP Ledger.
Monetising an API today means SaaS subscriptions, monthly billing keys, or third-party platforms (Stripe, etc.). It is slow to integrate, opaque for buyers, and completely inaccessible to autonomous AI agents.
Ed4l02 lets any developer protect an API in one line of code, and any client — human or AI agent — pay per call, instantly on-chain, with no account, no subscription, and no bank intermediary.
| Ed4l02 | |
|---|---|
| Native pay-per-call | ✓ |
| On-chain, no account | ✓ |
| AI-agent ready | ✓ |
| Credential gate (KYC) | ✓ |
| Multi-asset (XRP/IOU/MPT) | ✓ |
| ISO 20022 export | ✓ |
| 1 line to integrate | ✓ |
Ed4l02 is built on HTTP 402 Payment Required, a standard HTTP code that was never really used — until now.
Client Resource Server Facilitator XRPL
│ │ │ │
│── GET /api/resource ──►│ │ │
│◄── 402 + conditions ───│ │ │
│ │ │ │
│──── X-Payment-Signature (signed tx) ─────────────►│ │
│ │◄── POST /settle ──────────│ │
│ │ │── submitAndWait ──►│
│ │ │◄── confirmed ──────│
│◄── 200 OK + tx_hash ───│◄── settlement response ───│ │
- Client calls a protected API → receives
402with payment conditions - Client signs an XRPL transaction and sends it in the
X-Payment-Signatureheader - The facilitator verifies credentials on-chain, validates the signed tx, then submits it
- On success →
200 OK+ the on-chain transaction hash
No redirect. No cookie. No OAuth. Just HTTP + XRPL.
Ed4l02/
├── ed4l02-x402/ # npm middleware — protect any route in 1 line
│ └── src/
│ ├── middleware.ts # xrpl402() / xrpl402Express()
│ ├── types.ts # TypeScript interfaces
│ ├── vat.ts # VAT computation (FR 20%, CH 8.1%)
│ └── codec.ts # Base64 header encoding
│
├── ed4l02-facilitator/ # Express settlement backend
│ └── src/
│ ├── index.ts # Routes: /health /verify /settle /marketplace
│ ├── settle.ts # 7-step settlement pipeline
│ ├── verify.ts # Offline tx verification (no RPC)
│ ├── credentials.ts # XLS-70 (AND) & XLS-80 (OR) gates
│ ├── marketplace.ts # Merchant & endpoint registry
│ └── db.ts # SQLite operations
│
├── frontend/ # Next.js 16 merchant dashboard
│ ├── app/ # App Router pages
│ ├── components/ # React UI components + shadcn/ui
│ ├── hooks/ # useAuth, useMerchant, useX402, …
│ └── lib/ # camt053, credentialUtils, xrpl-auth, …
│
├── Dockerfile
└── docker-compose.yml
- Node.js ≥ 20
- npm ≥ 10 (or pnpm ≥ 9 for the frontend)
- Docker and Docker Compose (for containerised setup)
- Crossmark, GemWallet, or Xaman browser extension for wallet connection
cp ed4l02-facilitator/.env.example ed4l02-facilitator/.env| Variable | Required | Default | Description |
|---|---|---|---|
PORT |
no | 3000 |
Port the facilitator listens on |
SETTLE_TIMEOUT_MS |
no | 60000 |
Max ms to wait for XRPL confirmation |
XRPL_MAINNET_URL |
no | — | Override mainnet WebSocket URL |
XRPL_TESTNET_URL |
no | — | Override testnet WebSocket URL |
XRPL_DEVNET_URL |
no | — | Override devnet WebSocket URL |
cp frontend/.env.example frontend/.env.local| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_DEFAULT_NETWORK |
yes | XRPL network (testnet / mainnet / devnet) |
FACILITATOR_URL |
yes | URL of the facilitator (http://localhost:3000 locally) |
XRPL_NETWORK |
yes | Same network value, used server-side |
NEXT_PUBLIC_XAMAN_API_KEY |
no | Xaman API key for QR-based wallet login |
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID |
no | WalletConnect project ID |
EDEL_ID_BASE_URL |
no | Edel-ID verifier API base URL |
EDEL_ID_API_KEY |
no | Edel-ID API key |
EDEL_ID_API_SECRET |
no | Edel-ID API secret |
XRPL_ISSUER_ADDRESS |
no | XRPL address of the credential issuer |
XRPL_ISSUER_SEED |
no | Seed of the credential issuer wallet |
XRPL_WSS_ENDPOINT |
no | Custom XRPL WebSocket endpoint |
NEXT_PUBLIC_ISSUER_ADDRESS |
no | Issuer address exposed to the browser |
Never commit
.envor.env.local— they may contain wallet seeds. Both files are already in.gitignore.
docker compose up --build| Service | URL |
|---|---|
| Facilitator | http://localhost:3000 |
| SQLite volume | ./facilitator_data/ |
The SQLite database is persisted in ./facilitator_data/ and survives container restarts.
cd ed4l02-x402
npm install
npm run build
cd ..cd ed4l02-facilitator
npm install
npm run dev # tsx watch — hot reload on port 3000Verify it is running:
curl http://localhost:3000/health
# {"status":"ok","version":"0.1.0"}cd frontend
pnpm install # or: npm install
pnpm dev # or: npm run devOpen http://localhost:3001 in your browser.
The frontend runs on port 3001 by default (Next.js uses 3000 unless it is taken — since the facilitator is already on 3000, Next.js auto-selects the next available port). You can force a port with
pnpm dev -- -p 3001.
| Route | Description | Auth required |
|---|---|---|
/ |
Landing page | No |
/marketplace |
Browse all public x402 APIs | No |
/marketplace/[id] |
API detail page | No |
/checkout/[productId] |
Pay-per-call checkout flow | Wallet |
/merchant |
Merchant dashboard (revenue, transactions, marketplace) | EDEL_MERCHANT |
/merchant/transactions |
Full transaction history with filters + CAMT.053 export | EDEL_MERCHANT |
/merchant/products |
Manage published API endpoints | EDEL_MERCHANT |
/merchant/marketplace-listing |
Publish endpoints to the marketplace | EDEL_MERCHANT |
/user |
User profile, agents, KYC status | Wallet |
/smart-account |
Flare Smart Accounts (FXRP mint/transfer/redeem) | Wallet |
Merchant onboarding: accessing /merchant without the EDEL_MERCHANT credential triggers a 2-step onboarding flow:
- Pay 5 XRP → platform signs a
CredentialCreateon-chain (valid 32 days) - Accept the credential via
CredentialAcceptfrom your wallet
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Liveness probe |
| POST | /verify |
Offline verification of a signed tx |
| POST | /settle |
Full 7-step settlement pipeline |
| GET | /transactions?address=r…&limit=50&offset=0 |
SQLite transaction history for an address |
| POST | /merchants/listing |
Register or update a merchant |
| GET | /marketplace?category=&max_price= |
List all public merchants |
| GET | /marketplace/:merchant_id |
Merchant detail |
- Deduplication —
sha256(tx_blob)in-memory cache for 120s (prevents double-spend) - Offline verification — checks scheme, network, destination, exact amount — no RPC call
- VAT gate — if the route has
vatConfig: verifies the merchant holdsEDEL_TVA_FRorEDEL_TVA_CH - XLS-70 gate — verifies the sender holds all required credentials (AND logic)
- XLS-80 gate — verifies the sender is a member of the configured PermissionedDomain (OR logic)
submitAndWait()— submits the tx to XRPL, waits up to 60s for confirmation- SQLite log — inserts the settled transaction with all VAT columns
Install the package:
npm install @hliosone/ed4l02-x402Protect a route (Express):
import { xrpl402Express } from '@hliosone/ed4l02-x402';
app.get('/premium',
xrpl402Express({
facilitatorUrl: 'https://ed4l02.onrender.com',
network: 'testnet',
asset: 'XRP',
amount: '1000000', // 1 XRP in drops
payTo: 'rYourMerchantAddress...',
// Optional: require on-chain credentials (XLS-70, AND logic)
requiredCredentials: [{ issuer: 'rIssuer...', credentialType: 'KYC_18' }],
// Optional: require PermissionedDomain membership (XLS-80, OR logic)
requiredDomain: { domainId: 'ABCD1234...' },
// Optional: automatic VAT (FR 20% or CH 8.1%)
vatConfig: { jurisdiction: 'FR', ht_amount: '1000000' },
}),
handler
);Supported assets:
| Asset | Format | Notes |
|---|---|---|
XRP |
"1000000" (drops) |
1 XRP = 1,000,000 drops |
IOU |
{ currency, issuer, value } |
Trust lines — RLUSD, EUR, … |
MPT |
{ mpt_issuance_id, value } |
XLS-33 Multi-Purpose Token |
Supported frameworks: Express, Next.js API routes, Hono, Cloudflare Workers — via xrpl402Express (Node.js) and xrpl402 (Web Request/Response).
A merchant can require the payer to hold specific on-chain credentials before the payment is accepted. Example: an adults-only API requires AGE_18 issued by a trusted issuer.
The facilitator checks via account_objects that:
- The credential is
lsfAccepted(accepted by the holder) - The issuer matches
- The credential is not expired (Ripple epoch)
- All listed credentials are present (AND)
If any credential is missing → rejected before submitting on-chain. No fees charged to the client.
The merchant configures a domainId (hash of a PermissionedDomain object on XRPL). The facilitator:
- Fetches the domain object from the ledger → reads its
AcceptedCredentialslist - Verifies the sender holds at least one credential from that list (OR)
One domain can group multiple valid credential types. This is native XLS-80 governance.
The most differentiating feature for the agentic era.
Problem: An AI agent paying a KYC-gated API is not a natural person — it cannot be KYC'd.
Solution: The platform issues two credentials:
KYC_OVER_18→ to the human (proves their KYC)EDEL_AGENT→ to the agent, with the human's XRPL address encoded as hex in theURIfield
When the agent pays a KYC_OVER_18-protected route:
- Facilitator sees the agent does not directly hold
KYC_18 - Automatic fallback: looks for
EDEL_AGENTon the agent (same issuer) - Decodes the hex URI → human's XRPL address
- Verifies the human holds
KYC_OVER_18 - If yes → payment accepted, tx submitted
The agent pays. The platform knows it acts on behalf of a KYC'd human. No human private key is exposed.
For VAT-registered merchants, the middleware automatically computes the VAT-inclusive amount:
- France: 20% — requires
EDEL_TVA_FRcredential - Switzerland: 8.1% — requires
EDEL_TVA_CHcredential
The merchant configures vatConfig: { jurisdiction: 'FR', ht_amount: '1000000' }. The middleware:
- Computes the VAT-inclusive amount using BigInt (no floating-point errors)
- Overrides the requested amount with the total including VAT
- Embeds the ex-VAT / VAT / total breakdown in
PaymentDetails
The facilitator verifies the merchant holds the correct credential before validating. All VAT columns are logged in SQLite.
ISO 20022 CAMT.053 export: VAT transactions are exportable as standard XML, compatible with Swiss and French accounting software.
Merchants publish their x402 endpoints on a built-in marketplace. Each listing shows:
- Merchant name and description
- Available endpoints with prices in XRP
- Destination wallet
Any client (human or agent) can browse the marketplace and call the listed APIs directly. Data is persisted in SQLite and survives restarts.
| Standard | Name | Usage in Ed4l02 |
|---|---|---|
| XLS-70 | On-Chain Credentials | API access gates, merchant credential, KYC, VAT |
| XLS-80 | PermissionedDomains | Domain-level access gate (OR logic) |
| XLS-33 | Multi-Purpose Tokens | Native MPT payment support |
| x402 | HTTP 402 Protocol | Machine-readable pay-per-call protocol |
| Component | Stack |
|---|---|
| Middleware | TypeScript, zero dependencies, ESM/CJS compatible |
| Facilitator | Node.js 20, Express 4, TypeScript, xrpl.js 4.6, better-sqlite3 |
| Frontend | Next.js 16.2, React 18, Tailwind CSS 3.4, shadcn/ui |
| Wallet | xrpl-connect (Crossmark, GemWallet, Xaman) |
| Database | SQLite (WAL mode) — persisted via Docker volume |
| Deployment | Docker / Render (facilitator), Vercel (frontend) |
| Service | URL |
|---|---|
| Facilitator | https://ed4l02.onrender.com |
- Full x402 flow (402 → sign → settle → 200) in XRP, IOU, MPT on testnet
- XLS-70 credential gate (AND logic)
- XLS-80 PermissionedDomain gate (OR logic)
- AI agent delegation via
EDEL_AGENT - Automatic VAT (FR / CH) with merchant credential
- SQLite logging of all settled transactions
- ISO 20022 CAMT.053 client-side export
- Marketplace with SQLite persistence
- Merchant dashboard with
EDEL_MERCHANTcredential gate - Merchant onboarding (pay 5 XRP → receive
EDEL_MERCHANTcredential) - 9 integration tests — all passing
Built for Paris Blockchain Week 2026 in a few days on XRPL testnet. Live infrastructure, open-source code, passing integration tests.
The infrastructure is mainnet-ready — change network: 'mainnet' in the middleware config to go live.