PaymentOracle · 4 rails · ES256K signed

Verifiable payments.
Four rails. One signed receipt.

PaymentOracle proves what your agent paid. Every receipt is ES256K-signed against the same key, follows the same schema, and is verifiable from any host without trusting us.

4
live rails
signed receipts
ES256K
signature alg
x402
protocol
The rails

Four payment rails. One receipt schema.

Each rail uses the chain's native finality and is verified read-only against its public RPC. Receipts share the same ES256K signing key and JSON shape regardless of rail.

Rail Asset Chain Status First receipt
base-usdc-x402 USDC
6 decimals
Base mainnet
chain id 8453
live r_a603fc71…
xrpl-xrp XRP
drops, native
XRPL mainnet live r_9c941c43…
xrpl-rlusd RLUSD
issued asset / IOU
XRPL mainnet beta r_bb5960a6…
base-eurc EURC
6 decimals
Base mainnet
chain id 8453
beta r_f48be831…

Live rail status: /payments/proof/latest · well-known: /.well-known/payment-oracle.json

Anatomy of a receipt

Every receipt looks like this.

Same schema across rails. The payment.rail and payment.asset_ref fields tell an agent which rail settled the payment.

Live receipt · rail: base-eurc · 1.272801 EURC open raw →
loading live receipt…
Verify any receipt

Don’t trust us. Verify.

The signing key is published as a JWK at /.well-known/payment-oracle.json. Any receipt can be checked offline with three steps: recompute the receipt hash, fetch the JWK, verify the ES256K signature.

# pip install cryptography
import json, hashlib, base64, urllib.request
from cryptography.hazmat.primitives.asymmetric import ec, utils
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend

def canon(o): return json.dumps(o, sort_keys=True, separators=(',', ':'), ensure_ascii=False)

# 1. fetch the public JWK and a receipt
po  = json.load(urllib.request.urlopen("https://tooloracle.io/.well-known/payment-oracle.json"))
rcp = json.load(urllib.request.urlopen("https://tooloracle.io/payments/receipt/r_f48be831573596ad3ebba1e04dc45311"))
jwk = po["receipts"]["public_jwk"]
r   = rcp["receipt"]

# 2. recompute the receipt hash
body = {k: v for k, v in r.items() if k not in ("receipt_hash", "signature")}
assert "sha256:" + hashlib.sha256(canon(body).encode()).hexdigest() == r["receipt_hash"]

# 3. verify ES256K signature
sig = base64.urlsafe_b64decode(r["signature"]["sig"] + "==")
x = int.from_bytes(base64.urlsafe_b64decode(jwk["x"] + "=="), "big")
y = int.from_bytes(base64.urlsafe_b64decode(jwk["y"] + "=="), "big")
pub = ec.EllipticCurvePublicNumbers(x, y, ec.SECP256K1()).public_key(default_backend())
so  = dict(r); so["signature"] = {"alg": r["signature"]["alg"], "kid": r["signature"]["kid"]}
der = utils.encode_dss_signature(int.from_bytes(sig[:32], "big"), int.from_bytes(sig[32:], "big"))
pub.verify(der, canon(so).encode(), ec.ECDSA(hashes.SHA256()))
print("verified")
Quick start

Quote → pay → verify.

Three HTTP calls. The agent quotes, pays on-chain, and exchanges the tx hash for a signed receipt. Replay protection is enforced server-side: a tx hash can be claimed at most once across all rails.

01

Quote

Tell PaymentOracle which rail your agent prefers. You get an intent_hash, an exact amount, and the destination wallet for that chain.

POST /payments/quote
02

Pay on-chain

Send the asset to the quoted wallet on the quoted chain. Base rails: ERC-20 transfer. XRPL rails: native Payment with the intent hash in a Memo.

on-chain transfer
03

Verify & collect receipt

Submit the tx hash with the intent hash. PaymentOracle reads the chain, confirms delivery, and issues an ES256K-signed receipt.

POST /payments/verify → /payments/receipt
# 1. quote
curl -s https://tooloracle.io/payments/quote \
  -H 'content-type: application/json' \
  -d '{"product":"rank","tool":"keyword_research",
       "tool_args":{"keyword":"hello"},
       "agent_did":"did:web:my-agent.example",
       "preferred_rails":["base-eurc"]}'

# 2. agent sends EURC to the returned payTo on Base mainnet

# 3. verify and collect receipt
curl -s https://tooloracle.io/payments/verify \
  -H 'content-type: application/json' \
  -d '{"intent_hash":"sha256:…",
       "payload":{"tx_hash":"0x…"}}'

curl -s https://tooloracle.io/payments/receipt \
  -H 'content-type: application/json' \
  -d '{"verification_id":"ver_…"}'
Endpoints

Read-only by default.

Discovery and proof endpoints are open. Quote / verify / receipt are POST and accept JSON.

GET/.well-known/payment-oracle.json

Service descriptor. Includes the public JWK for receipt verification, scope, supported rails, and pricing surface.

GET/payments/proof/latest

Most recent verified payment, ES256K-signed. Self-contained proof an external observer can re-verify offline.

POST/payments/quote

Open a payment intent for a tool call on a chosen rail. Returns intent hash, amount, payTo wallet, and execution hint.

POST/payments/verify

Submit the on-chain tx hash. PaymentOracle checks delivery, recipient, asset, and temporal binding against the intent.

POST/payments/receipt

Issue the ES256K-signed receipt for a verified payment. Idempotent: same verification yields the same receipt.

GET/payments/receipt/{id}

Fetch any issued receipt by id. Same JSON the issuer signed; safe to mirror, hash, and re-verify.

GET/payments/verify/{id}

Fetch any verification row, including the ones that failed and why. Useful for agent debugging.

GET/payments/rails/xrpl/status

XRPL-specific readiness info: account funding, RLUSD trustline, memo policy.

Built for machines

Discoverable without a human in the loop.

Agents find PaymentOracle the same way they find anything in the OracleNet mesh: well-known files, the catalog, the MCP registry. No login, no API key handshake.

Service descriptor

/.well-known/payment-oracle.json

Public JWK, scope, supported rails, endpoint URLs, pricing surface. Read first, then call.

Latest proof

/payments/proof/latest

Self-contained, signed snapshot of the most recent verified payment. The fastest way to confirm the service is alive and signing.

Mesh snapshot

/.well-known/agent-pulse

PaymentOracle surfaces in the live OracleNet mesh snapshot alongside every other oracle.

Capability catalog

/catalog

The full ToolOracle catalog. Browse or filter by category. PaymentOracle sits under the settlement layer.

Want the full story? Read the deep-dive: Four Rails. One Signed Receipt.