Create instant payment links for agents.
Generate a code, share it, get paid in seconds.
No checkout. No integration. Just send and get paid.
Anyone with the link can pay — no account required, no gas.
What this is
Payment Requests let you create a one-time payment link for a specific amount.
The buyer opens it, connects their wallet, approves the payment, and funds settle instantly to your address.
No checkout flow. No merchant account. No manual wallet addresses. Just a link.
Use cases
How it works
Security: The buyer signs an EIP-3009 authorization binding the exact amount and recipient. The facilitator cannot alter the amount or redirect funds.
Example
Anyone with the link can pay. No account. No onboarding. Open, connect wallet, approve.
The code is 8 characters — human-readable, easy to embed in invoices, messages, or QR codes.
Why not just send USDC?
Share anywhere
Payment links are fully shareable — no special integration needed on the buyer side.
Paying
Buyers don't need an account. Payment page is at /pay.
Looking to redeem a one-time use code instead? Redeem Code docs · /redeem
Pay with a code (SDK)
Agents and server-side buyers can pay a merchant's invoice using a pre-generated payment code — no wallet connection required at the time of payment.
This is ideal for AI agents: generate a BLIK-style payment code in advance, then redeem it against any invoice code the merchant shares.
import {
createPayRequest,
payPayRequestWithCode,
createPrivateKeySigner,
generatePaymentCode,
} from '@relai-fi/x402';
const config = { facilitatorUrl: 'https://relai.fi/facilitator' };
// ── Merchant side ──────────────────────────────────────────
const req = await createPayRequest(config, {
to: '0xMerchantWallet',
amount: 5_000_000, // $5.00 USDC (micro-units)
network: 'base-sepolia',
description: 'Order #42',
});
console.log('Invoice code:', req.code); // "SHOP1234"
// ── Buyer side ─────────────────────────────────────────────
// Buyer already has a payment code (generated earlier by an agent)
const result = await payPayRequestWithCode(
config,
'SHOP1234', // merchant's invoice code
'MYBLIK78', // buyer's pre-generated payment code
);
console.log('Success:', result.success);
console.log('Explorer:', result.explorerUrl);
// If the code was worth more than the invoice, you get change back:
if (result.changeCode) {
console.log('Change code:', result.changeCode);
// Pass this to the buyer — they can use it for the next purchase
}Agent-first: No MetaMask. No browser. The buyer only needs a payment code string — the SDK handles everything else server-side. See the Payment Codes docs for how to pre-generate codes.
Change / partial payment
What if the buyer's payment code is worth more than the invoice? The SDK handles this automatically — the merchant receives exactly the invoice amount, and the difference is returned to the buyer as a new payment code.
// Default: change returned as a new payment code (no wallet needed)
const result = await payPayRequestWithCode(config, requestCode, paymentCode);
// result.changeCode = "RESZTA8" — buyer can use this for the next payment
// Option B: change returned atomically to buyer's wallet (on-chain)
const result2 = await payPayRequestWithCode(config, requestCode, paymentCode, {
returnChange: 'wallet',
});
// Strict mode: throw if code value ≠ invoice amount exactly
const result3 = await payPayRequestWithCode(config, requestCode, paymentCode, {
allowOverpayment: false,
});Built-in expiration
ttl in seconds when creating the request (default: 1 hour).expired error — no payment is possible.Track status
Monitor payment completion in real time using the SSE event stream.
createdRequest was registered — link is now shareablepaidPayment confirmed on-chain — tx hash availableexpiredTTL elapsed without payment — create a new request// GET /facilitator/pay-requests/events
// Content-Type: text/event-stream
const es = new EventSource(
'https://api.relai.fi/facilitator/pay-requests/events',
{ headers: { 'X-Service-Key': 'sk_live_...' } }
);
es.onmessage = (e) => {
const { code, status, payTxHash, explorerUrl } = JSON.parse(e.data);
if (status === 'paid') {
console.log('Paid!', explorerUrl);
es.close();
}
};API reference
All endpoints require an X-Service-Key header.
/facilitator/pay-requestsCreate a new one-time payment request. Returns the 8-char code and payUrl.
/facilitator/pay-requestsList all active payment requests for your account.
/facilitator/pay-requests/:codeLook up a specific request — amount, status, expiry.
/facilitator/pay-requests/eventsSSE stream — real-time status updates when a request is paid.
curl -X POST https://api.relai.fi/facilitator/pay-requests \
-H "X-Service-Key: sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"to": "0xYourMerchantWallet",
"amount": 5.00,
"network": "base-sepolia",
"description": "Invoice #42",
"ttl": 3600
}'{
"code": "MW78SGTW",
"expiresIn": 3600,
"payUrl": "https://relai.fi/pay#MW78SGTW"
}Settlement networks
| Network | ID | Gas | Privacy |
|---|---|---|---|
| Base Sepolia | base-sepolia | Sponsored | Public on-chain |
| SKALE Base Sepolia | skale-base-sepolia | Free (SKALE gasless) | 🔒 BITE encrypted |
Error reference
not_foundRequest not foundThe code does not exist or was already consumed.
expiredRequest is no longer validThe TTL elapsed before payment. Create a new request.
already_paidCode was already usedEach code is single-use. Payment was already settled.
amount_mismatchIncorrect payment amountSigned amount does not match the request. Rejected for security.