MPP-Tempo¶
Pay a paywalled API with PathUSD or USDC on the Tempo L2 using the MPP
(Machine Payment Protocol) with method=tempo.
Prerequisites¶
| Requirement | Notes |
|---|---|
| Tempo wallet | Any eth_account.LocalAccount — same key format as x402 |
| PathUSD balance (testnet) | Request from faucet.tempo.xyz for Moderato testnet |
| USDC balance (mainnet) | Bridge or acquire USDC on the Tempo mainnet (chain ID 42430) |
| MPP-Tempo endpoint | A server that sends WWW-Authenticate: Payment method="tempo" on 402 |
Environment variables¶
export TEMPO_PRIVATE_KEY=0x<your-private-key>
export TEMPO_MERCHANT_ENDPOINT=https://your-api.com/paid
Testnet: Tempo Moderato¶
Moderato is Tempo's public testnet (chain ID 42431). PathUSD is the faucet-funded stablecoin.
import asyncio
import os
from eth_account import Account
from routeweiler import Routeweiler, Funding
from routeweiler.trace.sink_sqlite import TraceSink
async def main():
wallet = Account.from_key(os.environ["TEMPO_PRIVATE_KEY"])
async with Routeweiler(
funding=[Funding.tempo_pathusd_moderato(wallet=wallet)],
trace_sink=TraceSink.sqlite("rw.db"),
agent_id="tempo-agent",
) as client:
response = await client.get(os.environ["TEMPO_MERCHANT_ENDPOINT"])
print(f"HTTP {response.status_code}: {response.json()}")
asyncio.run(main())
Mainnet: Tempo with USDC¶
async with Routeweiler(
funding=[Funding.tempo_usdc(wallet=wallet)], # chain ID 42430, USDC
trace_sink=TraceSink.sqlite("rw.db"),
) as client:
response = await client.get("https://your-api.com/paid-endpoint")
Custom RPC or token¶
Use TempoFundingSource directly when you need a non-default RPC URL or token:
from routeweiler.funding.tempo import TempoFundingSource, EthAccountTempoSigner
from eth_account import Account
wallet = Account.from_key(os.environ["TEMPO_PRIVATE_KEY"])
funding = TempoFundingSource(
signer=EthAccountTempoSigner(wallet=wallet, chain_id=42431),
network="tempo-moderato",
asset="pathusd",
rpc_url="https://rpc.moderato.tempo.xyz", # or your own RPC
)
What the flow looks like¶
GET https://your-api.com/paid
← 402 Payment Required
WWW-Authenticate: Payment id="live-abc123",
realm="api.example.com",
method="tempo",
intent="charge",
request="<b64url JCS payload>",
expires="2026-01-01T00:00:00Z"
Routeweiler:
1. Parses MPP-Tempo challenge (charge_id, amount, recipient, chain_id)
2. Checks policy and budget
3. Builds a Tempo 0x76 transaction (EIP-2718 type 0x76)
4. Signs it locally with EthAccountTempoSigner
5. Sends Authorization: Payment <b64url credential>
GET https://your-api.com/paid
Authorization: Payment <b64url credential with signed tx>
← 200 OK
Payment-Receipt: <b64url JCS receipt with tx hash>
The 0x76 transaction type is specific to Tempo. The merchant submits it to the
Tempo RPC (eth_sendRawTransaction) and returns the receipt once the tx is mined.
What to check in the trace¶
import sqlite3, json
conn = sqlite3.connect("rw.db")
rows = conn.execute(
"SELECT * FROM trace_events WHERE selected_rail = 'mpp-tempo'"
).fetchall()
conn.close()
for row in rows:
p = json.loads(row["payload"])
payment = p["payment"]
print(f"Tx hash: {payment['proofValue']}") # 0x<64 hex>
print(f"Amount: {payment['amountNative']} base units")
print(f"Currency: {payment['amountNativeCurrency']}") # CAIP-19 token address
print(f"Settlement latency: {payment['settlementLatencyMs']} ms")
proofType will be "txid". The proofValue is a valid Tempo on-chain tx hash.
You can verify it at explore.testnet.tempo.xyz
(Moderato testnet) or the Tempo mainnet explorer.
Error handling¶
from routeweiler import (
SigningError,
MppChargeFailedError,
MppReceiptVerificationError,
NoFeasibleRailError,
)
try:
response = await client.get(url)
except MppChargeFailedError as exc:
print(f"Merchant rejected the charge: {exc}")
except MppReceiptVerificationError as exc:
print(f"Settlement receipt invalid: {exc}")
except SigningError as exc:
print(f"Tempo transaction signing failed: {exc}")
except NoFeasibleRailError:
print("No MPP-Tempo funding source matched this challenge.")
See also¶
- Tempo documentation
TempoFundingSource,EthAccountTempoSigner,Funding.tempo_pathusd_moderatoin API Reference- Concepts: Payment Rails
- Live e2e test:
packages/routeweiler/tests/rails/test_mpp_tempo_e2e_live.py