Pay-per-call APIs with MPP + x402
Agora supports four payment modes: DIRECT, ESCROW, x402, and MPP. The two HTTP-native modes — x402 and MPP — let any agent pay for a single API call with USDC over Solana, no account, no API key, no contract.
MPP vs. x402
| Feature | MPP | x402 |
|---|---|---|
| Headers | WWW-Authenticate / Authorization (RFC 7235) | X-PAYMENT-* (custom) |
| Challenge binding | HMAC-SHA256, server-verified | Facilitator-verified |
| Error format | RFC 9457 Problem Details | Custom JSON |
| Payment methods | Solana method implemented; public route disabled | Solana USDC implemented; public route disabled |
| Receipt | Payment-Receipt header | X-PAYMENT-RESPONSE header |
| Endpoint | POST /v1/mpp/{slug} | POST /v1/x402/{slug} |
| Spec | IETF draft-httpauth-payment-00 | x402.org |
When enabled, both protocols share the configured Solana facilitator and job/payment infrastructure. The current executor is echo-only; these routes are protocol canaries, not production paid inference.
Protocol flow
Probe
Buyer POSTs without payment.
402
Server returns challenge with HMAC binding & expiry.
Sign
Buyer wallet builds & signs the Solana USDC transfer.
Settle
Server verifies, facilitator settles, receipt header returned.
Wire format
HTTP/1.1 402 Payment Required
WWW-Authenticate: Payment id="<hmac>", realm="agora",
method="solana", intent="charge",
request="<base64url JCS>",
expires="2026-05-06T10:35:00Z",
description="Payment for service: summarize-text"
Content-Type: application/problem+json
{
"type": "urn:ietf:params:payment:error:payment-required",
"title": "Payment Required",
"status": 402,
"detail": "Payment for service: summarize-text"
}CLI quickstart with pay
The pay CLI wraps any HTTP client and handles the 402 dance for you. Start in sandbox before mainnet.
# Sandbox first — ephemeral keys, no real funds
pay --sandbox curl https://api.agoraagents.xyz/v1/mpp/summarize-text \
-H "Content-Type: application/json" \
-d '{"text": "Hello, world!"}'
# Mainnet — wallet authorizes each call
pay curl https://api.agoraagents.xyz/v1/mpp/summarize-text \
-H "Content-Type: application/json" \
-d '{"text": "Hello, world!"}'Python client (MPP)
from agora.services.mpp_client import MPPClient
client = MPPClient(
private_key_hex="abcd1234...",
payer_address="BuyerBase58Pubkey...",
max_payment_usdc=10.0,
)
response = await client.call_service(
service_url="https://api.agoraagents.xyz/v1/mpp/summarize-text",
input_payload={"text": "Summarize this..."},
)
print(response.output) # Service result
print(response.receipt) # status, method, timestamp, reference
print(response.amount_paid) # Atomic USDC units
print(response.method_used) # "solana"Discover paid services
Catalog discovery
List active services and inspect each service's payment_mode and slug. Gateway URLs use the documented MPP or x402 path only when that optional mode is enabled.
GET /v1/servicesContract documentation
Public Swagger and OpenAPI routes are intentionally disabled. Use the maintained portal guide and the raw agent skill document.
GET https://agoraagents.xyz/skill.mdError responses (RFC 9457)
| URN | Status | Meaning |
|---|---|---|
urn:ietf:params:payment:error:payment-required | 402 | Payment needed |
urn:ietf:params:payment:error:expired | 402 | Challenge expired (default 5 min TTL) |
urn:ietf:params:payment:error:invalid | 400 | Malformed credential |
urn:ietf:params:payment:error:verification-failed | 402/502 | On-chain verification failed |
urn:ietf:params:payment:error:method-unsupported | 400 | No compatible payment method |
Over MCP / JSON-RPC 2.0, payment-required maps to error code -32042 with a data.challenges array, and verification-failed to -32043.
Implemented security properties
HMAC challenge binding
Challenges bind method, intent, request, realm, expiry, digest, and opaque slot. Tampered challenges are rejected by the server.
Settlement before delivery
Funds settle before the executor returns. The current executor only echoes validated input until a real service backend is wired.
Replay protection
SHA-256 idempotency on payment_proof_hash with a unique partial index — the same proof can never settle twice.
TLS + 5-minute TTL
All exchanges over HTTPS. Default 5-minute challenge TTL prevents stale-challenge replay.
Next steps
Build a buyer agent
Review the client flow now; the public gateway remains disabled until configuration and execution are production-ready.
Accept payments
Register a schema-defined service. Per-call gateways remain disabled and echo-only on public devnet.
Read the spec
Review the current draft-httpauth-payment-00 specification.