# Batch Payments Source: https://docs.request.network/api-features/batch-payments Process multiple payments in a single transaction for gas optimization ## Overview Batch payments enable you to process multiple payment requests efficiently in a single blockchain transaction, reducing gas costs and simplifying multi-recipient workflows. **EVM-only.** Batch payments — both inbound (one transaction paying multiple payees) and outbound (one signed batch payout) — are supported on EVM chains only. The API rejects Tron batch requests with `Batch payments are not supported for TRON networks. Please submit individual payment requests.` For Tron, submit single-recipient payments or payouts instead. **Two Types of Batch Payments:** ### Batch Pay Invoices Process previously created requests using their request IDs and receive payment calldata that can be executed on-chain to pay multiple requests simultaneously. ### Batch Payouts Submit new payment requests that are immediately processed, creating requests and returning payment calldata in a single API call for instant multi-recipient payments. ## Key Benefits * **Gas Efficiency**: Significantly reduce transaction costs by batching multiple payments * **Simplified UX**: Process up to 200 payments in a single transaction * **Mixed Payment Types**: Support ERC20, native tokens, and conversion payments in the same batch * **Atomic Execution**: All payments succeed or fail together, ensuring consistency **Single Network Limitation**: All requests in a batch must be on the same blockchain network. ## Batch Processing Limits The theoretical limit for batch payments is **100-200 payments per transaction**, depending on: * Payment complexity (ERC20 vs native tokens vs conversions) * Available block gas limit on the target network * Smart contract computational requirements For optimal performance, we recommend starting with smaller batches (10-50 payments) and scaling based on your network conditions. ## Batch Payment Workflow ```mermaid theme={null} sequenceDiagram participant Payer participant App participant RequestAPI as Request Network API participant Blockchain Payer->>App: Initiate Batch Payment App->>RequestAPI: POST /v2/payouts/batch {requests or requestIds} RequestAPI-->>App: 200 OK {batchPaymentTransaction, ERC20ApprovalTransactions} RequestAPI-)RequestAPI: Start listening for batch payments opt if needs ERC20 approvals App->>Payer: Prompt for approval signatures Payer-->>App: Sign approval transactions App->>Blockchain: Submit approval transactions end App->>Payer: Prompt for batch payment signature Payer-->>App: Sign batch payment transaction App->>Blockchain: Submit batch payment transaction RequestAPI->>RequestAPI: Batch payments detected RequestAPI->>App: POST {"payment.confirmed" events for each request} App-->>Payer: Batch Payment Complete ``` ## Endpoints ### Pay multiple requests in one transaction Pays multiple payment requests in one transaction by either creating new requests or using existing request IDs. All requests must be on the same network. Supports mixed ERC20, native, and conversion requests. Endpoint reference: [POST /v2/payouts/batch](https://api.request.network/open-api/#tag/v2payouts/POST/v2/payouts/batch) ## Implementation Examples The following examples demonstrate how to implement batch payment calldata execution in your application. The API returns unsigned transaction calldata, and your application sends those transactions on-chain. #### Batch Pay Invoices Example ```typescript theme={null} import { ethers } from 'ethers'; // Get unsigned calldata to pay existing requests by their IDs const batchPayResponse = await fetch('https://api.request.network/v2/payouts/batch', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-client-id': 'your-client-id' }, body: JSON.stringify({ requestIds: [ "01e273ecc29d4b526df3a0f1f05ffc59372af8752c2b678096e49ac270416a7cdb", "02f384fdd39e5c627e04b1f2e6fd60593783b8863c3c789197f5bd381527b8ecd" ], payer: "0x2e2E5C79F571ef1658d4C2d3684a1FE97DD30570" }) }); if (!batchPayResponse.ok) { throw new Error(`API error: ${batchPayResponse.status}`); } const { batchPaymentTransaction, ERC20ApprovalTransactions } = await batchPayResponse.json(); // Your app must implement sending these transactions to the blockchain const provider = new ethers.providers.Web3Provider(window.ethereum); const signer = provider.getSigner(); // 1. Handle ERC20 approvals if needed for (const approval of ERC20ApprovalTransactions) { const tx = await signer.sendTransaction(approval); await tx.wait(); } // 2. Send the batch payment transaction const batchTx = await signer.sendTransaction(batchPaymentTransaction); await batchTx.wait(); ``` #### Batch Payouts Example ```typescript theme={null} // Create new requests and process them immediately const batchPayResponse = await fetch('https://api.request.network/v2/payouts/batch', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-client-id': 'your-client-id' }, body: JSON.stringify({ requests: [ { payee: "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", amount: "10", invoiceCurrency: "USD", paymentCurrency: "USDC-sepolia" }, { payee: "0xb07D2398d2004378cad234DA0EF14f1c94A530e4", amount: "25.50", invoiceCurrency: "EUR", paymentCurrency: "DAI-sepolia" } ], payer: "0x2e2E5C79F571ef1658d4C2d3684a1FE97DD30570" }) }); if (!batchPayResponse.ok) { throw new Error(`API error: ${batchPayResponse.status}`); } const { batchPaymentTransaction, ERC20ApprovalTransactions } = await batchPayResponse.json(); // Your app must implement the blockchain transaction execution // (same pattern as Batch Pay Invoices example above) ``` ## Supported Payment Types Batch payments support mixing different payment types in a single transaction: * **ERC20 Token Payments**: Standard token transfers * **Native Token Payments**: ETH, MATIC, etc. * [**Conversion Payments**](/api-features/conversion-payments): Requests denominated in one currency but paid in another (e.g., USD invoices paid with USDC) ## Key Implementation Notes ### Your Responsibility * **API Call**: Your application calls the Request Network API to get transaction data * **Blockchain Execution**: Your application executes the returned transaction data on the blockchain * **Error Handling**: Your application handles transaction failures and retries ### Best Practices 1. **Validate Addresses**: Always validate recipient addresses before submitting batch payments 2. **Test on Testnets**: Start with small batches on test networks before production deployment 3. **Handle Failures Gracefully**: Implement proper error handling for transaction failures 4. **Gas Estimation**: Consider gas costs when determining optimal batch sizes 5. **User Experience**: Provide clear progress indicators for multi-step approval processes ### Error Handling Common error scenarios and their solutions: * **Network Mismatch**: Ensure all requests use the same blockchain network * **Insufficient Funds**: Verify payer has sufficient balance for all payments plus gas * **Invalid Addresses**: Validate all payee addresses before batch submission * **Gas Limit Exceeded**: Reduce batch size if hitting network gas limits ## Related For detailed information on all available endpoints and their parameters, see the full [Request Network API Reference](https://api.request.network/open-api). For an end-to-end walkthrough of paying many recipients at once, see [Batch payouts](/use-cases/batch-payouts). # Client ID Management Source: https://docs.request.network/api-features/client-id-management Create and manage Client IDs for frontend authentication, domain whitelisting, and orchestrator flows ## Overview Client IDs enable frontend/browser-side authentication with the Request Network API. Unlike API keys (which are for server-to-server calls), Client IDs are designed to be used in client-side code with domain restrictions. **Key differences from API keys:** | | API Key | Client ID | | ---------------------- | -------------------- | -------------------------- | | **Use case** | Backend integrations | Frontend/browser apps | | **Auth header** | `x-api-key` | `x-client-id` + `Origin` | | **Domain restriction** | None | Optional whitelist | | **Fee configuration** | Per-request | Configurable per client ID | ## Backend vs Frontend Client IDs * **Frontend Client IDs** — have `allowedDomains` set. The API validates the `Origin` header against the whitelist. * **Backend Client IDs** — have empty `allowedDomains`. No domain validation is performed. Useful for server-side orchestrators that need client ID scoping without browser restrictions. ## Orchestrator Pattern When a Client ID is bound to a [payee destination](/api-features/payee-destinations), requests created with that Client ID automatically resolve the payee from the destination. This enables orchestrator flows where a third party creates payment requests on behalf of a merchant without knowing the merchant's wallet details. ``` Orchestrator (with Client ID) → POST /v2/request (no payee field) → API resolves payee from Client ID's bound destination → Payment goes to merchant's configured wallet ``` ## Two ways to manage Client IDs | Method | Best for | | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | | **Dashboard** ([dashboard.request.network](https://dashboard.request.network)) | Most users — sign in with your wallet, generate a Client ID, set allowed domains, copy the value | | **Auth API** (`POST /v1/client-ids`) | Automation, dynamic provisioning, multi-tenant orchestration | Both paths use the same Client ID — there is no functional difference. ## CRUD Operations Client IDs can be managed through the auth API (session-based) or the request API (API key-based). ### Create a Client ID ```bash theme={null} curl -X POST "https://auth.request.network/v1/client-ids" \ -H "Cookie: session=YOUR_SESSION_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "label": "My Checkout Widget", "allowedDomains": ["https://mystore.com", "https://staging.mystore.com"], "payeeDestinationId": "0x6923...C7D7@eip155:8453#ABCD1234:0x8335...2913" }' ``` Display name for the Client ID (1-100 characters). List of allowed origins (max 10). Must be HTTPS, except `http://localhost` and `http://127.0.0.1`. Leave empty for backend Client IDs. Default fee percentage for requests created with this Client ID (0-100). Fee recipient address. Required when `feePercentage` is set. ERC-7828 destination ID to bind to this Client ID. Enables the orchestrator pattern. Optional Ethereum address of a smart wallet with delegated operator permissions. Used in commerce payment (authorize/capture) flows. Default pre-approval duration in seconds. Overrides per-request value when set. Default authorization duration in seconds. Overrides per-request value when set. ```json Response (201) theme={null} { "id": "01HXEXAMPLE123", "clientId": "cli_abc123def456", "label": "My Checkout Widget", "allowedDomains": ["https://mystore.com", "https://staging.mystore.com"], "feePercentage": null, "feeAddress": null, "operatorWalletAddress": null, "defaultPreApprovalExpiry": null, "defaultAuthorizationExpiry": null, "payeeDestinationId": "0x6923...C7D7@eip155:8453#ABCD1234:0x8335...2913", "status": "active", "createdAt": "2026-03-15T10:00:00.000Z" } ``` ### List Client IDs ```bash theme={null} curl -X GET "https://auth.request.network/v1/client-ids" \ -H "Cookie: session=YOUR_SESSION_TOKEN" ``` ### Get a Client ID ```bash theme={null} curl -X GET "https://auth.request.network/v1/client-ids/01HXEXAMPLE123" \ -H "Cookie: session=YOUR_SESSION_TOKEN" ``` ### Update a Client ID ```bash theme={null} curl -X PUT "https://auth.request.network/v1/client-ids/01HXEXAMPLE123" \ -H "Cookie: session=YOUR_SESSION_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "allowedDomains": ["https://mystore.com", "https://new-domain.com"], "label": "Updated Label" }' ``` All fields are optional. You can update `label`, `allowedDomains`, `feePercentage`, `feeAddress`, `payeeDestinationId`, `operatorWalletAddress`, `defaultPreApprovalExpiry`, `defaultAuthorizationExpiry`, and `status`. ### Revoke a Client ID ```bash theme={null} curl -X DELETE "https://auth.request.network/v1/client-ids/01HXEXAMPLE123" \ -H "Cookie: session=YOUR_SESSION_TOKEN" ``` Revoking a Client ID is permanent and cannot be undone. ## Webhook Scoping Webhooks can be scoped to specific Client IDs. When a webhook is created with a `clientId`, it only receives events for requests created with that Client ID. See [Webhooks](/api-features/webhooks-events) for details. ## Related Pages How to use Client IDs for API authentication. Create destinations to bind to Client IDs. # Commerce Payments (Preview) Source: https://docs.request.network/api-features/commerce-payments Authorize-capture-void escrow payment flow for e-commerce platforms Commerce payments are in **preview** and may not be available on all accounts. Contact support if you're interested in this feature. ## Overview Commerce payments implement an authorize-capture-void escrow pattern for e-commerce use cases. Funds are held in escrow after authorization and only transferred to the merchant upon capture. This is useful for: * **Marketplace platforms** — Hold funds until order fulfillment, then capture or void * **Subscription trials** — Authorize a payment, capture only if the user converts * **Pre-orders** — Reserve funds and capture when the product ships ## Payment Lifecycle ```mermaid theme={null} stateDiagram-v2 [*] --> Authorized: authorize Authorized --> Captured: capture Authorized --> Voided: void Captured --> Refunded: refund Captured --> [*] Voided --> [*] Refunded --> [*] ``` ## Endpoints All commerce payment endpoints are under `/v2/commerce-payments`. ### Authorize Generate escrow authorization calldata and execute it. 1. `POST /v2/commerce-payments/authorize/calldata` — Get authorization transaction calldata 2. `POST /v2/commerce-payments/:requestId/authorize` — Execute authorization via smart account ### Capture Transfer authorized funds to the merchant. 1. `POST /v2/commerce-payments/:requestId/capture/calldata` — Get capture transaction calldata 2. `POST /v2/commerce-payments/:requestId/capture` — Execute capture ### Void Cancel an authorized payment and release escrowed funds. 1. `POST /v2/commerce-payments/:requestId/void/calldata` — Get void transaction calldata 2. `POST /v2/commerce-payments/:requestId/void` — Execute void ### Check Status `GET /v2/commerce-payments/:requestId/status` — Get the current state of a commerce payment. ## Authentication All endpoints require `x-api-key` or `x-client-id` authentication. ## Related Pages Hosted payment experience with smart account support. Receive real-time notifications for payment lifecycle events. # Conversion Payments Source: https://docs.request.network/api-features/conversion-payments Requests denominated in one currency and settled in another with conversion at payment time ## Overview Conversion payments let you denominate a request in one currency (for example USD) and collect payment in a different currency (for example USDC or ETH). This is useful when you want stable invoicing amounts while still collecting crypto on-chain. ## How It Works Create the request with [POST /v2/request](https://api.request.network/open-api/#tag/v2request/POST/v2/request): * `invoiceCurrency`: currency you want to denominate the request in (for example `USD`) * `paymentCurrency`: currency you want to receive on-chain (for example `USDC-base`) The API stores both values and computes payable amounts using the configured rate source. Use [GET /v2/request//pay](https://api.request.network/open-api/#tag/v2request/GET/v2/request/\{requestId}/pay) to get transaction payloads, then execute on-chain. At payment time, the converted amount is applied so the request can be settled in `paymentCurrency`. Use [GET /v2/request/](https://api.request.network/open-api/#tag/v2request/GET/v2/request/\{requestId}) to inspect status fields such as: * `amountInUsd` * `conversionRate` * `rateSource` * `conversionBreakdown` (for multi-payment or partial-payment scenarios) When a request is paid in multiple parts, conversion details are returned in the breakdown instead of a single conversion rate value. ## Conversion Behavior * Single-payment requests can expose a direct `conversionRate` * Multi-payment or partial-payment requests return detailed `conversionBreakdown` * Rate source metadata is returned by the status endpoint ## Supported Currencies and Chains Use [Request Network Token List](/resources/token-list) and [Supported Chains and Currencies](/resources/supported-chains-and-currencies) to choose valid `invoiceCurrency` and `paymentCurrency` pairs. Conversion behavior depends on the selected currency pair and network support. ## Used In Professional invoices in fiat Cross-chain e-commerce pricing ## API Reference For full endpoint schemas and examples, see [Request Network API Reference](https://api.request.network/open-api). # Create Requests Source: https://docs.request.network/api-features/create-requests Request creation workflows and configuration for invoices and payment collection ## Overview Request creation forms the foundation of Request Network operations, enabling structured payment collection through invoice generation and payment request workflows. ## What You Can Do At its core, the Request Network API empowers you to: * **Create Requests:** Define payment requests with information such as payee, payer (optional), amount, currency, and recurrence (optional). * **Facilitate Payments:** Return transaction calldata, ready to be signed by end-users and sent to the blockchain for secure and transparent value transfer. * **Deliver Webhook Notifications:** Receive instant updates on payment status changes, enabling your application to react dynamically to completed transactions. * **Partial Payment Support:** Pay a portion of a request instead of the full amount at once. This unlocks powerful use cases such as: * **Split payment:** Split a payment 50% USDC on Base and 50% with USDT on Optimism. * **Gradual payment plans:** Allow users to pay large invoices in smaller chunks. * **Risk mitigation:** Test with small amounts before completing large payments. The API automatically tracks payment progress, showing `partially_paid` status until the request is fully paid, and prevents overpayment by capping amounts to the remaining balance. ## Workflows ### Invoice-first Workflow Create a payment request first, then allow customers to pay at their convenience. **Flow:** 1. Create request with payee, amount, and currency 2. Share request ID or payment reference with customer 3. Customer retrieves payment calldata 4. Customer executes transaction 5. Receive webhook confirmation **Use Cases:** Professional invoicing, B2B payments, subscription billing ### Payment-first Workflow Send payments directly without creating a request first using the `/payouts` endpoint. **Flow:** 1. Call `/payouts` with payee and amount 2. Receive transaction calldata immediately 3. Execute transaction 4. Request is created and paid in one step **Use Cases:** Vendor payments, contractor payouts, immediate transfers ## How It Works The following diagram illustrates the typical flow for creating and paying requests using the Request Network API: ```mermaid theme={null} sequenceDiagram actor User participant App participant Request Network API participant Blockchain User->>App: Create Request App->>Request Network API: POST /request {apiKey, payee, payer?, amount, invoiceCurrency, paymentCurrency} Request Network API-->>App: 201 Created {requestId, paymentReference} User->>App: Pay Request App->>Request Network API: GET /request/{requestId}/pay {apiKey} Request Network API-->>App: 200 OK {transactions[calldata], metadata{stepsRequired, needsApproval, approvalTransactionIndex}} Request Network API-)Request Network API: Start listening {paymentReference} opt if needs approval App->>User: Prompt for approval signature User-->>App: Sign approval transaction App->>Blockchain: Submit approval transaction end App->>User: Prompt for payment signature User-->>App: Sign payment transaction App->>Blockchain: Submit payment transaction Request Network API->>Request Network API: Payment detected, stop listening {paymentReference} Request Network API->>App: POST {"payment.confirmed", requestId, paymentReference, explorer link, timestamp} App-->>User: Payment Complete ``` ## Request Properties ### Core Information * **Payee:** The wallet address of the payee (Ethereum 0x... or TRON T...). Required for all requests except crypto-to-fiat. * **Payer:** The wallet address of the payer (optional) * **Amount:** The payable amount of the invoice, in human readable format * **Invoice Currency:** Invoice Currency ID, from the [Request Network Token List](/resources/token-list) e.g: USD * **Payment Currency:** Payment currency ID, from the [Request Network Token List](/resources/token-list) e.g: ETH-sepolia-sepolia ### Optional Configuration * **Recurrence:** For recurring payments, specify start date and frequency (DAILY, WEEKLY, MONTHLY, YEARLY) * **Fee Settings:** Specify fee percentage and fee address for platform fee collection ### Supported Chains and Currencies See [Supported Chains and Currencies](/resources/supported-chains-and-currencies) for the complete list of available networks and tokens. ## Quick Example Here's a simple example of creating a request: ```javascript theme={null} const response = await fetch('https://api.request.network/v2/request', { method: 'POST', headers: { 'x-client-id': process.env.RN_CLIENT_ID, 'Content-Type': 'application/json' }, body: JSON.stringify({ payee: '0x...', amount: '100', invoiceCurrency: 'USD', paymentCurrency: 'USDC-base-base' }) }); const { requestId, paymentReference } = await response.json(); ``` Then get the payment calldata: ```javascript theme={null} const payResponse = await fetch( `https://api.request.network/v2/request/${requestId}/pay`, { headers: { 'x-client-id': process.env.RN_CLIENT_ID } } ); const { transactions, metadata } = await payResponse.json(); // Execute transactions with your wallet... ``` For a complete working example, see the [Integration Tutorial](/api-setup/integration-tutorial). ## Used In Business invoice generation via the Dashboard Payment collection at checkout via the API Complete implementation example Subscription and billing workflows ### Key Features * **Human-readable amounts:** Send amounts in standard format (e.g., "0.1"), no BigNumber conversions needed * **Automatic payment tracking:** Real-time status updates via webhooks * **Flexible currencies:** Request in one currency, pay in another with automatic conversion * **Partial payments:** Track multiple payments against a single request See [API Reference](https://api.request.network/open-api) for complete technical documentation with OpenAPI specs. # Crosschain Payments Source: https://docs.request.network/api-features/crosschain-payments Multi-network payment routing with automatic bridging via LiFi **Breaking change (March 2026):** The crosschain payment flow has been redesigned. The old payment-intent signing flow (`POST /v2/request/payment-intents/:paymentIntentId`) has been removed. Crosschain payments now return executable transaction calldata directly. See updated steps below. ## Overview Crosschain payments allow users to pay a request using a stablecoin from a different blockchain network than the one specified on the request. For example, a payer can pay a request for USDC on Base using USDT from their Optimism wallet. Crosschain routing is powered by [LiFi](https://li.fi/), which aggregates bridges and DEXs to find optimal routes. Cross-chain routing uses the Across bridge (LiFi routes are restricted to Across for predictable, supported settlement). A circuit breaker automatically degrades gracefully if the LiFi routing service is impaired—routes may temporarily be unavailable during an outage, but payments remain detectable and the API stays responsive. ## Benefits * **Flexibility:** Payers can pay with their preferred stablecoin on any supported chain. * **Cost-Effective:** Automated routing balances cost and speed. * **Time-Saving:** Payers don't need to swap or bridge tokens manually. * **Simplified UX:** Payment settlement requires only 1 or 2 transactions from the payer. ## Crosschain Payments Supported Chains and Currencies For crosschain (and samechain) payments, the Request Network API supports USDC and USDT on 5 chains. Bridged USDC (USDC.e) is **not** supported for crosschain payments. Only native USDC is supported. ### Supported Chains * Ethereum * Arbitrum One * Base * OP Mainnet * Polygon ### Supported Currencies Crosschain payments work only with mainnet funds (real money). Test networks are not supported. * USDC * USDT ## How It Works Create a request with a `paymentCurrency` in the supported stablecoins and networks. The `amount` must be greater than 1 USD equivalent (e.g., at least 1.01 USDC) — crosschain routes are not available for amounts of \$1 or less due to bridge minimums. Create the request via [POST /v2/request](https://api.request.network/open-api/#tag/v2request/POST/v2/request). Fetch available routes with [GET /v2/request//routes](https://api.request.network/open-api/#tag/v2request/GET/v2/request/\{requestId}/routes). **Required query parameters:** * `wallet` — the payer's wallet address **Optional query parameters:** * `feePercentage` and `feeAddress` — for platform fee inclusion in route calculations The API returns routes ranked by fees and speed. Each route includes: * `chain` and `token` — the source chain and token * `fee` — total fee as a decimal * `feeBreakdown[]` — detailed fee components (gas, crosschain, platform) * `speed` — `"FAST"` for same-chain, seconds estimate for crosschain The API may also return samechain routes when the payer has funds on the same chain as `paymentCurrency`. ```json Example routes response theme={null} { "routes": [ { "id": "REQUEST_NETWORK_PAYMENT", "fee": 0, "feeBreakdown": [], "speed": "FAST", "chain": "BASE", "token": "USDC" }, { "id": "ARBITRUM_BASE_USDT_USDC", "fee": 0.001, "feeBreakdown": [ { "type": "crosschain", "stage": "sending", "provider": "lifi", "amount": "0.001", "amountInUSD": "0.001", "currency": "USDT" } ], "speed": 300, "chain": "ARBITRUM", "token": "USDT" } ] } ``` Once the payer selects a route, fetch executable transaction calldata with [GET /v2/request//pay](https://api.request.network/open-api/#tag/v2request/GET/v2/request/\{requestId}/pay). **Query parameters:** * `wallet` — the payer's wallet address * `chain` — the source chain from the selected route (e.g., `ARBITRUM`) * `token` — the source token from the selected route (e.g., `USDT`) Both `chain` and `token` must be provided together for crosschain payments. Omit both for same-chain payments. The API returns a `transactions` array with ready-to-execute calldata: ```json Example crosschain response theme={null} { "transactions": [ { "data": "0x095ea7b3...", "to": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", "value": "0x0" }, { "data": "0xabcdef...", "to": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE", "value": "0x0" } ], "metadata": { "stepsRequired": 2, "needsApproval": true, "approvalTransactionIndex": 0, "paymentTransactionIndex": 1, "routeType": "crosschain", "quoteExpiresAt": 1742205771, "hasEnoughBalance": true, "sourceAmount": "10.02" } } ``` **Metadata fields:** * `stepsRequired` — number of transactions to execute (1 or 2) * `needsApproval` — whether a token approval transaction is needed first * `approvalTransactionIndex` — index of the approval tx in the array (or `null`) * `paymentTransactionIndex` — index of the payment/bridge tx * `routeType` — `"crosschain"` or `"samechain"` * `quoteExpiresAt` — unix timestamp when the route quote expires * `hasEnoughBalance` — whether the payer has sufficient funds * `sourceAmount` — the amount the payer needs to send on the source chain (includes bridge fees) The API always includes approval transactions in crosschain calldata responses, even if the payer already has sufficient token allowance. This ensures USDT-style tokens (which require resetting allowance to zero before setting a new one) work correctly. Send each transaction in the `transactions` array as a standard `eth_sendTransaction`. If `needsApproval` is true, execute the approval transaction first and wait for confirmation before sending the payment transaction. ```typescript theme={null} import { createWalletClient, createPublicClient, custom, http } from "viem"; import { arbitrum } from "viem/chains"; const walletClient = createWalletClient({ chain: arbitrum, transport: custom(window.ethereum), }); const publicClient = createPublicClient({ chain: arbitrum, transport: http(), }); const [account] = await walletClient.getAddresses(); // Step 1: Send approval transaction (if needed) if (metadata.needsApproval && metadata.approvalTransactionIndex != null) { const approvalTx = transactions[metadata.approvalTransactionIndex]; const approvalHash = await walletClient.sendTransaction({ account, to: approvalTx.to, data: approvalTx.data, value: BigInt(approvalTx.value || 0), }); // Wait for confirmation before proceeding await publicClient.waitForTransactionReceipt({ hash: approvalHash }); } // Step 2: Send payment/bridge transaction const paymentTx = transactions[metadata.paymentTransactionIndex]; const paymentHash = await walletClient.sendTransaction({ account, to: paymentTx.to, data: paymentTx.data, value: BigInt(paymentTx.value || 0), }); ``` After the payer broadcasts the crosschain transaction, payment detection happens automatically. The API monitors the bridge execution and sends [webhook notifications](/api-features/webhooks-events) when the payment is confirmed on the destination chain. ## Custom fee configuration Custom fee configuration is available. See [Platform Fees](/api-features/platform-fees) for setup details (`feePercentage`, `feeAddress`) and implementation examples. # Crypto-to-fiat Payments Source: https://docs.request.network/api-features/crypto-to-fiat-payments Pay requests in crypto while payees receive fiat in bank accounts # Crypto-to-fiat Payments Crypto-to-fiat payments allow a Payer to pay a Request in cryptocurrency, while the Payee receives fiat currency directly in their bank account. This is achieved by combining the Request Network crypto payment with [Request Tech](https://www.request.finance/tech) offramp infrastructure. This requires prerequisite compliance (KYC/Agreement) and bank account registration (payment detail) flows. Crypto-to-fiat payments are only available in version 2 of the Request Network API. ## Getting Started with Crypto-to-fiat Payments ### Sandbox Access - Get Started Today All developers can immediately access the **Crypto-to-fiat Sandbox** to build and test their integration: 1. **Sign in** to the [Request Dashboard](https://dashboard.request.network) with your wallet 2. **Generate a Sandbox Client ID** with crypto-to-fiat sandbox access 3. **Start building** with Sepolia testnet USDC, simulated KYC, and mock bank accounts The sandbox provides a complete testing environment where you can: * Test the full crypto-to-fiat flow without real funds * Simulate payer KYC verification using [mock documents](https://docs.sumsub.com/docs/verification-document-templates) * Work with mock bank account data and fiat payment status **Important: Other Payment Types Use Real Funds** The "Crypto-to-fiat Sandbox" setting for API keys only affects crypto-to-fiat payments. Other payment types can process *real* funds with any API key, even Sandbox API keys. ### Production Access - Launch When Ready When you're ready to go live with real transactions: 1. [**Get in touch**](https://2deywy.share-eu1.hsforms.com/2b92phs9LR_eJdeZoxzmoMA?utm_source=request.network\&utm_medium=docs\&utm_campaign=evergreen\&utm_content=get_in_touch) to request production access 2. **Discuss your use case** with our team to ensure the best integration approach 3. **Complete the approval process** - we'll work with you to get everything set up 4. **Generate Production API keys** once approved Production access includes: * Real USDC transactions on mainnet * Actual KYC verification for payers * Live bank account validation * Fiat deposits to real bank accounts ### Crypto-to-fiat Supported Chains and Currencies For Crypto-to-fiat Payments, the Request Network API supports USDC on Ethereum, Polygon, Arbitrum One, and Sepolia. **Crypto-to-fiat Payment with non-USDC currencies:** While crypto-to-fiat requests must be created in USDC, users can pay in alternative currencies through a four-step process, using [crosschain-payments](/api-features/crosschain-payments): 1. **Create Crosschain Request**: Create a request to swap the payer's currency (e.g., ETH on Ethereum) to USDC on the target chain (e.g., USDC on Polygon). Note: Don't specify a payer address during request creation to allow flexibility in who can pay. 2. **Pay Crosschain Request**: Execute the crosschain payment to fulfill the first request 3. **Create Crypto-to-Fiat Request**: Create a separate request for the USDC offramp to fiat and bank deposit 4. **Pay Crypto-to-Fiat Request**: Execute the crypto-to-fiat payment to complete the flow This four-step approach is required due to current API limitations - more streamlined flows are not yet implemented. ## Understanding `clientUserId` Many `/payer` endpoints in the Request Network API require a `clientUserId` as a path parameter. This value is an **arbitrary identifier** chosen by your platform to represent a user (the payer) in your own system. * **You control the format:** The `clientUserId` can be any unique string that makes sense for your application. It can be a UUID, a database ID, or anything unique per user on your platform. * **Common pattern:** Many integrations set `clientUserId` to an internal user ID from their own system. * **Why is this useful?** This approach allows you to integrate the Request Network API without having to change your existing user management logic. You simply pass your own identifier to the API, and all payer-related compliance, agreement, and payment detail records will be associated with that value. **Example usage:** ``` GET /v2/payer/{clientUserId} PATCH /v2/payer/{clientUserId} POST /v2/payer/{clientUserId}/payment-details GET /v2/payer/{clientUserId}/payment-details ``` In each case, replace `{clientUserId}` with your chosen identifier for the user. ## Compliance & Payer Onboarding Before a payer can use crypto-to-fiat, they must complete compliance steps: * **KYC**: The payer must submit a KYC application. * **Agreement**: The payer must sign a compliance agreement (via an iframe flow). * **Bank Account**: The payee's bank account must be associated with a payer for compliance reasons, even though the payee owns the account. ### **Compliance Flow Diagram** ```mermaid theme={null} sequenceDiagram participant Payer participant Platform participant RequestAPI participant RequestTech Payer->>Platform: Submit KYC data via KYC Form Platform->>RequestAPI: POST /v2/payer (KYC data) RequestAPI->>RequestTech: Forward KYC RequestTech-->>RequestAPI: KYC status update (webhook) Note over Platform: Wait for KYC "approved" RequestAPI-->>Platform: webhook: compliance.updated (kycStatus: pending/approved/rejected/failed) Platform->>Payer: Show iframe for agreement signature Payer->>Platform: Completes signature Platform->>RequestAPI: PATCH /v2/payer/{clientUserId} (agreement_status: completed) RequestAPI-->>Platform: webhook: compliance.updated (agreementStatus: completed) ``` ### **Flow Explanation** 1. **Submit KYC**: The platform collects KYC information from the payer and submits it to the API. 2. **KYC Review**: The platform receives webhook updates as the KYC is processed (`compliance.updated` with `kycStatus`). 3. **Agreement Signature**: The platform displays an iframe for the payer to sign the compliance agreement. Once signed, the platform calls the API to update the agreement status. 4. **Agreement Confirmation**: The platform receives a webhook update when the agreement is completed (`compliance.updated` with `agreementStatus`). ### Relevant Endpoints * `POST /v2/payer`: Submit KYC application. * `GET /v2/payer/{clientUserId}`: Get compliance status for a payer. * `PATCH /v2/payer/{clientUserId}`: Update agreement status after signature. ## Create compliance data for a user > Checks compliance status and returns necessary URLs for completing compliance. Endpoint reference: [POST /v2/payer](https://api.request.network/open-api/#tag/v2payer/POST/v2/payer) ## Get compliance status for a user > Retrieves the comprehensive compliance status for a specific user, including KYC and agreement status. Endpoint reference: [GET /v2/payer/](https://api.request.network/open-api/#tag/v2payer/GET/v2/payer/\{clientUserId}) ## Update agreement status > Update the agreement completion status for a user. Endpoint reference: [PATCH /v2/payer/](https://api.request.network/open-api/#tag/v2payer/PATCH/v2/payer/\{clientUserId}) ## Setting Up a Crypto-to-Fiat Request (Payee Flow) Before a payer can pay in crypto and the payee can receive fiat, the platform must: * **Submit the payee’s bank account details** (associated with a payer for compliance). * **Wait for approval** of those payment details (usually less than 60 seconds, confirmed via webhook). * **Create a new request** with `isCryptoToFiatAllowed = true`. ### **Payment Details Flow Diagram** ```mermaid theme={null} sequenceDiagram participant Payee participant Platform participant RequestAPI participant RequestTech Payee->>Platform: Submit bank info via Bank Account Form Platform->>RequestAPI: POST /v2/payer/{clientUserId}/payment-details (bank info) RequestAPI->>RequestTech: Forward payment details RequestTech-->>RequestAPI: payment_detail_update (webhook) Note over Platform: Wait for payment detail "approved" RequestAPI-->>Platform: webhook event: payment_detail.updated (status: approved/failed/pending) Platform->>RequestAPI: POST /request (isCryptoToFiatAllowed = true) RequestAPI-->>Platform: Request created ``` ### **Flow Explanation** 1. **Submit Bank Account**: The platform submits the payee’s bank account details, associating them with a payer. The Request Network API forwards these details to the offramp provider (Request Tech). 2. **Approval**: The platform receives a webhook (`payment_detail.updated`) indicating if the payment details are approved, failed, or pending. 3. **Create Request**: Once approved, the platform creates a new request as usual, but with the `isCryptoToFiatAllowed` flag set to `true`. This signals that the request is eligible for crypto-to-fiat payment. ### **Design Rationale & UX Constraints** While it is technically possible to create a crypto-to-fiat request before the payer has completed KYC, the recommended pattern is to require KYC first. This is based on several practical and UX considerations: * **Bank Account Association:** The payee's bank account ("payment details") must be linked to a specific payer, which can only be done after the payer completes KYC. This ensures compliance and accurate association of payment details. * **Validation Complexity:** Although the payee could submit their bank account details in advance, the platform cannot validate or approve these details until the payer's KYC is complete. This would introduce additional communication steps and potential confusion. * **UI Simplicity:** Embedding payee bank account registration directly in the request creation form (with a pending state until approval) keeps the UX straightforward and avoids a separate bank account management page. * **Protocol Fit:** The crypto-to-fiat feature is integrated at the API level, not at the Request Network protocol level. Creating a request on the protocol does not require bank account details, because the protocol itself only handles crypto payments. The additional bank account and offramp logic is layered on top via the API, which transfers crypto to Request Tech, who then executes the offramp and sends fiat to the payee's bank account. This approach ensures a smooth, compliant, and user-friendly experience. ### Relevant Endpoints * `POST /payer/{clientUserId}/payment-details`: Create payment details (register bank account) for a payee. * `GET /payer/{clientUserId}/payment-details`: Get payment details (bank accounts) for a payee. * `POST /v2/request` with `isCryptoToFiatAllowed = true`: Create a new crypto-to-fiat request ## Create payment details > Create payment details for a user Endpoint reference: [POST /v2/payer//payment-details](https://api.request.network/open-api/#tag/v2payer/POST/v2/payer/\{clientUserId}/payment-details) ## Get payment details for a user > Retrieves the registered bank account details for a user. Optionally filter by payment details ID. Endpoint reference: [GET /v2/payer//payment-details](https://api.request.network/open-api/#tag/v2payer/GET/v2/payer/\{clientUserId}/payment-details) ## Create a new request > Create a new payment request Endpoint reference: [POST /v2/request](https://api.request.network/open-api/#tag/v2request/POST/v2/request) ## Paying a Crypto-to-Fiat Request The payer pays in crypto; Request Tech handles offramping and fiat payout. ### **Payment Flow Diagram** ```mermaid theme={null} sequenceDiagram participant Payer participant Platform participant RequestAPI participant RequestTech participant Blockchain participant PayeeBank as Payee Bank Platform->>RequestAPI: GET payment calldata for request/{requestId}/pay Platform->>Payer: Prompt to sign and send transaction Payer->>Blockchain: Send crypto to Request Tech Blockchain-->>RequestTech: Payment received RequestTech-->>RequestAPI: offramp_update (webhook) RequestAPI-->>Platform: webhook: payment.processing/payment.failed (subStatus: initiated, ongoing_checks, sending_fiat, fiat_sent, failed, etc.) RequestTech->>PayeeBank: Offramp and deposit fiat RequestTech-->>RequestAPI: offramp_update (fiat sent) RequestAPI-->>Platform: webhook: payment.processing (subStatus: fiat_sent) RequestAPI-->>Platform: webhook: payment.confirmed (fiat delivered) ``` **Flow Explanation** 1. **Get Payment Calldata**: The platform fetches payment calldata for the request. 2. **User Pays**: The payer signs and submits the transaction, sending crypto to Request Tech. 3. **Offramp Processing**: Request Tech receives the crypto and begins the offramp process. 4. **Status Updates**: The platform receives webhook events as the offramp progresses (`payment.processing`, `payment.failed`), with `subStatus` indicating the current offramp stage. 5. **Fiat Delivered**: When the offramp is complete, the platform receives a final webhook (`payment.processing` with `subStatus: fiat_sent`), and then a `payment.confirmed` event. ## Crypto-to-fiat Webhook Event Reference | | | | | ------------------------ | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | Event | Description | subStatus values (if any) | | `compliance.updated` | KYC/Agreement status updates | `kycStatus`: `initiated`, `pending`, `approved`, `rejected`, `failed`; `agreementStatus`: `not_started`, `pending`, `completed`, `rejected`, `failed` | | `payment_detail.updated` | Payment detail (bank account) status | `approved`, `failed`, `pending` | | `payment.processing` | Offramp in progress | `initiated`, `pending_internal_assessment`, `ongoing_checks`, `sending_fiat`, `fiat_sent`, `bounced`, `retry_required` | | `payment.failed` | Offramp or payment failed | `failed`, `bounced` | | `payment.confirmed` | Payment fully settled (fiat delivered) | | # Fee Breakdowns Source: https://docs.request.network/api-features/fee-breakdowns Where to read fee details in routes, payment search, and status responses ## Overview Fee breakdowns provide itemized cost details returned by reconciliation and routing endpoints. Use this page to understand where fee data appears and how to consume it reliably. ## Where Fee Breakdowns Appear ### Payment Routes * Endpoint: [GET /v2/request//routes](https://api.request.network/open-api/#tag/v2request/GET/v2/request/\{requestId}/routes) * Fields include: * `fee` * `feeBreakdown[]` Route-level fee breakdowns are useful before execution (quote/comparison stage). ### Payment Search * Endpoint: [GET /v2/payments](https://api.request.network/open-api/#tag/v2payments/GET/v2/payments) * Field includes: * `fees[]` Payment-level fee breakdowns are useful after execution (reconciliation/reporting stage). ### Request Status (when applicable) * Endpoint: [GET /v2/request/](https://api.request.network/open-api/#tag/v2request/GET/v2/request/\{requestId}) * May include fee information in enriched status outputs. ## Fee Types Common fee `type` values in API responses: * `protocol` * `gas` * `platform` * `crosschain` * `crypto-to-fiat` * `offramp` ## Route Fee Stages For route responses, `feeBreakdown` can include stage-level attribution: * `sending` * `receiving` * `proxying` * `refunding` * `overall` ## How to Use in Reconciliation Persist fee arrays exactly as returned (`fees[]` or `feeBreakdown[]`) before deriving reporting values. Group by `type`, `provider`, and currency to build accounting-friendly summaries. When replaying jobs or webhooks, deduplicate with stable identifiers (`requestId`, `paymentReference`, tx hash, delivery IDs). ## Example Shapes ### From routes endpoint ```json theme={null} { "fee": 0.0021, "feeBreakdown": [ { "type": "gas", "stage": "sending", "provider": "request-network", "amount": "0.0012", "currency": "USDC" }, { "type": "crosschain", "stage": "overall", "provider": "lifi", "amount": "0.0009", "currency": "USDC" } ] } ``` ### From payments endpoint ```json theme={null} { "fees": [ { "type": "protocol", "provider": "request-network", "amount": "0.05", "currency": "USDC" }, { "type": "platform", "provider": "request-network", "amount": "0.50", "currency": "USDC" }, { "type": "gas", "provider": "ethereum", "amount": "0.002", "currency": "ETH" } ] } ``` ## Related Pages Configure integrator fees with feePercentage and feeAddress. Understand protocol-level fee policy, rate, and cap. ## API Reference For complete schemas and examples, see [Request Network API Reference](https://api.request.network/open-api). # Partial Payments Source: https://docs.request.network/api-features/partial-payments Split payments across multiple transactions and funding sources ## Overview Partial payments let you settle a request over multiple transactions instead of one full payment. This is useful for installments, split settlement, and staged collections. ## How It Works Create a request with [POST /v2/request](https://api.request.network/open-api/#tag/v2request/POST/v2/request). Fetch payment payload with [GET /v2/request//pay](https://api.request.network/open-api/#tag/v2request/GET/v2/request/\{requestId}/pay) and pass `amount` in query parameters to pay only part of the request. The `amount` query parameter is human-readable and must be greater than 0. Execute additional partial payments as needed until the request reaches full settlement. Check [GET /v2/request/](https://api.request.network/open-api/#tag/v2request/GET/v2/request/\{requestId}) for status and conversion/payment breakdown fields. ## Key Behavior * Partial payments are applied incrementally against the same request * Request status reflects progress until full settlement * For conversion flows, breakdown fields expose paid vs remaining values ## Use Cases Break large payments into smaller amounts Group payments or shared expenses ## Supported Payment Types Partial settlement can be used in request-based payment flows where payment payloads are fetched via `GET /v2/request/{requestId}/pay`. Commonly used with: * Native & ERC20 payments * Conversion payments * Crosschain payments ## Used In Large invoice installments via the Dashboard Flexible payment options via the API ## API Reference For full endpoint schemas and examples, see [Request Network API Reference](https://api.request.network/open-api). # Payee Destinations Source: https://docs.request.network/api-features/payee-destinations Register and manage receiving routes using ERC-7828 interop addresses ## Overview Payee destinations define where payments are received. Each destination encodes a wallet address, chain, and token into a single **ERC-7828 interop address**, creating a unique receiving route. Destinations are used by: * **Secure payments** — to resolve the payee, chain, and token from a `destinationId` * **Client IDs** — to bind a receiving route to a client ID for orchestrator flows ## ERC-7828 Address Format A destination ID combines the interop address with the token address: ``` {walletAddress}@eip155:{chainId}#{checksum}:{tokenAddress} ``` **Example:** ``` 0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7@eip155:8453#ABCD1234:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 ``` This encodes: * **Wallet:** `0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7` * **Chain:** Base (chainId `8453`) * **Checksum:** `ABCD1234` (auto-generated by the API for address verification) * **Token:** USDC (`0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`) The checksum portion (`#ABCD1234`) is generated automatically by the API when you create a destination. You do not need to compute it yourself. ## How It Works Destinations require a SIWE wallet session. See [Wallet Authentication](/api-reference/wallet-authentication) for the challenge/verify flow. Call `POST /v1/payee-destination` with the token address and chain ID. The API generates the full destination ID with interop address. EVM and Tron destinations use the same endpoint and shape — only the address format and chain ID differ. ```bash theme={null} curl -X POST "https://auth.request.network/v1/payee-destination" \ -H "Cookie: session=YOUR_SESSION_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "chainId": 8453 }' ``` ```bash theme={null} curl -X POST "https://auth.request.network/v1/payee-destination" \ -H "Cookie: session=YOUR_SESSION_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "tokenAddress": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "chainId": 728126428 }' ``` The returned `destinationId` can be used in: * `POST /v2/secure-payments` — as `requests[].destinationId` * Client ID creation — as `payeeDestinationId` to bind the receiving route ## Endpoints All endpoints require a SIWE wallet session (httpOnly cookie). ### POST /v1/payee-destination Create a new payee destination or reactivate an existing one. ERC20 token contract address (EVM `0x...` or Tron `T...` format). Chain ID for the receiving network (e.g., `8453` for Base, `1` for Ethereum). Optional KYT (Know Your Transaction) compliance policy applied to payments into this destination. When set, every payer wallet that connects to a payment link backed by this destination is screened before the payment can proceed. See [Compliance-gated payments](/use-cases/compliance-gated-payments) for the full guide. `off` (default): no screening. `kyt_all_wallets`: every payer wallet is screened. KYT provider used for screening. Required when `mode` is `kyt_all_wallets` (must be omitted or `null` when `mode` is `off`). `merklescience` (Merkle Science) is available where enabled for your account. When `true`, payment metadata is hidden in the payer UI until the wallet passes screening. When `true`, the payee address is masked in the payer UI (no copy / no explorer link). ```json Response (201) theme={null} { "id": "01HXEXAMPLE123", "destinationId": "0x6923...C7D7@eip155:8453#ABCD1234:0x8335...2913", "walletAddress": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "chainId": 8453, "accessPolicy": { "mode": "kyt_all_wallets", "screeningProvider": "hypernative", "hideUntilApproved": true, "hidePayeeAddress": true }, "binaryInteropAddress": "0x...", "humanReadableInteropAddress": "0x6923...C7D7@eip155:8453#ABCD1234", "claimed": true, "active": true, "createdAt": "2026-03-15T10:00:00.000Z" } ``` ### PUT /v1/payee-destination Update the token address and/or chain on the active destination. New ERC20 token contract address. New chain ID. Returns the updated destination object (same format as POST response). ### GET /v1/payee-destination Returns the active payee destination for the authenticated wallet, or `null` if none exists. ### GET /v1/payee-destination/payout-lookup Retrieve the active destination preference for a payout **recipient** wallet. Returns only the routing fields needed to prefill payout creation, or `null` if the recipient has no active destination. Like the other endpoints on this page, this requires a SIWE wallet session (httpOnly cookie). Unlike the other endpoints, the `walletAddress` query parameter is the *recipient* you are looking up — not the authenticated caller — so a payout initiator can resolve where a recipient wants to be paid. Recipient wallet address to check for an active destination (Ethereum or Tron format). ```json Response (200) theme={null} { "destinationId": "0x742d...8f44e@eip155:1#4CA88C9C:0xa0b8...6eb48", "walletAddress": "0x742d35cc6634c0532925a3b844bc454e4438f44e", "tokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "chainId": 1, "claimed": true } ``` ### GET /v1/payee-destination/lookup Lookup a destination by its composite ID. The full destination ID in ERC-7828 format. ### PATCH /v1/payee-destination/deactivate Deactivate the current destination. The destination ID to deactivate. ```json Response (200) theme={null} { "success": true, "message": "Destination deactivated" } ``` ## Related Pages Bind destinations to client IDs for orchestrator flows. Use destination IDs when creating secure payments. # Payment Detection Source: https://docs.request.network/api-features/payment-detection Automatic reference-based payment detection system for blockchain transactions ## Overview The Request Network API uses a **reference-based payment detection system** that automatically monitors blockchain transactions to detect when payments are made to your requests. This works across supported blockchains and handles payment matching automatically. ## How It Works ```mermaid theme={null} graph TD A[Create Request] --> B[Payment Reference Generated] B --> C[Payer Sends Payment with Reference] C --> D[Subgraph Detects Transaction] D --> E[Validate Payment Details] E --> F[Update Request Status] F --> G[Trigger Webhook] ``` ### 1. Payment Reference Generation When you create a request, the API automatically generates a unique **payment reference** (16-character identifier). This reference is what links on-chain payment transactions to your request. **Example:** `0x1234567890abcdef` ### 2. Blockchain Monitoring The API continuously monitors supported blockchains using subgraphs that scan for transactions containing payment references. This happens automatically in the background. **Monitoring includes:** * transaction scanning * payment reference matching * amount and currency validation ### 3. Automatic Detection When someone makes a payment and includes the payment reference in their transaction, the system: * **Detects** the transaction * **Validates** payment details (amount, currency, recipient) * **Updates** request status (for example partially paid or fully paid) * **Triggers** your configured webhooks ### 4. Real-time Status Updates Once a payment is detected, your request status is updated and you can retrieve the latest information via: * **API Queries:** `GET /v2/request/{requestId}` * **Webhooks:** receive updates on your configured endpoints For the latest chain and currency support, see [Supported Chains and Currencies](/resources/supported-chains-and-currencies). ## Crosschain Payment Detection All crosschain payments using Request Network API use the **ERC-20 Fee Proxy contract** as the last payment leg, so payment detection works out of the box. **How it works:** 1. Payer initiates payment on source chain (e.g., Polygon) 2. Crosschain bridge transfers funds to destination chain (e.g., Base) 3. Final payment uses ERC-20 Fee Proxy with payment reference 4. Payment detection system identifies the transaction 5. Request status updated automatically ### LiFi Fallback Settlements In rare cases, a crosschain bridge may deliver funds to the destination chain but fail to execute the final payment contract call. When this happens, the system detects the LiFi fallback settlement and confirms the payment automatically. Fallback-confirmed payments include: * `detectionSource` set to `"lifi"` * A `note` field explaining that funds were delivered via fallback settlement ## Webhook Notifications Configure webhooks to receive real-time notifications for payment events: Full payment received and confirmed on blockchain Partial payment received (less than expected amount) Payment transaction failed or reverted Payment was refunded to the payer This allows your application to react immediately to payment events without constantly polling the API. ## Integration Benefits Payment detection happens automatically Works across supported chains Fast detection and status updates Built on blockchain indexing infrastructure ## What's Next? Configure webhook notifications for payment events Manually check payment status via API View all supported networks and currencies # Payment Types Overview Source: https://docs.request.network/api-features/payment-types-overview Choose the right Request Network payment type for your integration ## Overview Request Network supports multiple payment types. This page helps you choose the right one for your integration. ## Payment Types Core payment types available in the API: Same-currency payments with native tokens and ERC20 tokens Fiat-denominated requests paid in crypto Pay from a different chain and token than the request currency Process multiple payments in one transaction Subscription-style scheduled payments ## Choosing a Payment Type Use Native/ERC20 for same-currency flows, Conversion for fiat pricing, and Crosschain when payer and request chains differ. Use Batch for multi-recipient execution and Recurring for scheduled payments. ## API Reference For full endpoint schemas and examples, see [Request Network API Reference](https://api.request.network/open-api). # Payouts Source: https://docs.request.network/api-features/payouts Initiate single, batch, and recurring payments directly via the API ## Overview Payouts let you initiate payments without a separate request creation step. The API creates the request and returns executable transaction calldata in a single call. **Payout modes:** * **Single** — Pay one recipient * **Batch** — Pay multiple recipients in one transaction (same network, EVM) * **Recurring** — Automated payment schedules with ERC20 permit signatures * **Multicall** — Combine multiple existing payout links into one bundle, including across chains and on Tron ## Single Payout Create a payment request and get transaction calldata in one call. Single payouts work on **all 8 supported networks (EVM and Tron)**. ```bash theme={null} curl -X POST "https://api.request.network/v2/payouts" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": "100", "invoiceCurrency": "USD", "paymentCurrency": "USDC-base", "payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", "reference": "PAYOUT-001" }' ``` ```bash theme={null} curl -X POST "https://api.request.network/v2/payouts" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": "100", "invoiceCurrency": "USD", "paymentCurrency": "USDT-tron", "payee": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8", "reference": "PAYOUT-001" }' ``` **Required fields:** * `amount` — Human-readable amount * `invoiceCurrency` — Invoice currency (e.g., `"USD"`) * `paymentCurrency` — Payment currency ID (e.g., `"USDC-base"`, `"USDT-tron"`) * `payee` — Recipient wallet address (EVM `0x...` or Tron `T...`) **Optional fields:** * `reference` — Merchant reference for reconciliation * `feePercentage` and `feeAddress` — Platform fee configuration * `customerInfo` — optional structured metadata to attach to the request * `payer` — Payer wallet address (required for recurring) The response includes `requestId` and a `transactions` array with executable calldata. ## Batch Payout Pay multiple recipients in a single blockchain transaction. All payments must be on the same network. **This endpoint is EVM-only.** `POST /v2/payouts/batch` settles a single same-network EVM transaction and rejects Tron with `Batch payments are not supported for TRON networks. Please submit individual payment requests.` To batch Tron payouts, use [multicall payouts](#multicall-payouts) with the `tron_batch` execution kind instead; single Tron payouts via `POST /v2/payouts` also remain available. ```bash theme={null} curl -X POST "https://api.request.network/v2/payouts/batch" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "requests": [ { "amount": "50", "invoiceCurrency": "USD", "paymentCurrency": "USDC-base", "payee": "0xb07d2398d2004378cad234da0ef14f1c94a530e4" }, { "amount": "25", "invoiceCurrency": "USD", "paymentCurrency": "USDC-base", "payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7" } ], "payer": "0x1234567890123456789012345678901234567890" }' ``` The response includes approval transactions and a batch payment transaction to execute. ## Multicall payouts Multicall payouts combine multiple **existing** outgoing payout links into a single hosted link the payer settles as one bundle. Unlike [batch payouts](#batch-payout) — which are same-network and EVM-only — multicall supports cross-chain routing and Tron. Create a multicall link from previously created secure-payout tokens: ```bash theme={null} curl -X POST "https://api.request.network/v2/secure-payments/multicall-payouts" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "childTokens": ["01JZ4PC7EXAMPLECHILD000001", "01JZ4PC7EXAMPLECHILD000002"] }' ``` The response returns a parent `token` and `securePaymentUrl`. Share the URL with whoever signs payouts; they review every recipient and settle the whole bundle in one flow. **Execution kinds:** * `evm_same_chain` — all recipients on one network and currency, settled as a single batch transaction. * `evm_cross_chain` — recipients span chains/currencies (or the payer pays from a different source); each leg is routed via Li.Fi. * `tron_batch` — all recipients on Tron, settled via the Tron batch contract. When you omit `requestedExecutionKind`, it is derived from the payer's source selection at payment time. The default cap is 150 children (20 for cross-chain). See [Multicall payouts in the Secure Payments reference](/api-reference/secure-payments#post-v2secure-paymentsmulticall-payouts) for the full request/response schema, eligibility rules, and validation errors. ## Recurring Payouts Create automated payment schedules where the payer authorizes a series of payments with a single EIP-712 signature. See [Recurring Payments](/api-features/recurring-payments) for the full lifecycle (create, authorize, monitor, manage). **Endpoints:** * `POST /v2/payouts` with `recurrence` object — Create a recurring schedule * `POST /v2/payouts/recurring/:id` — Submit the payer's permit signature to activate * `GET /v2/payouts/recurring/:id` — Check status and next payment date * `PATCH /v2/payouts/recurring/:id` — Cancel or unpause ## Error Handling | Status | Meaning | | ------ | ------------------------------------------------------------- | | `400` | Invalid request body — check required fields and currency IDs | | `401` | Authentication failed — verify your `x-client-id` header | | `404` | Request or recurring payment not found | | `429` | Rate limited — back off and retry | | `500` | Server error — safe to retry with exponential backoff | For batch payouts, a `400` may indicate that payments span multiple networks (all must be on the same chain). ## Endpoint Reference Create a single or recurring payout. Create a batch payout with multiple recipients. # Platform Fees Source: https://docs.request.network/api-features/platform-fees Configure platform fees with feePercentage and feeAddress ## Overview Platform fees let your product collect an additional fee during payment execution. To configure a platform fee, pass: * `feePercentage` * `feeAddress` Use this page for setup and integration patterns. For protocol-level fees charged by Request Network, see [Protocol Fees](/api-features/protocol-fees). ## Required Parameters Fee percentage to apply at payment time (for example `"2.5"` for 2.5%). Wallet address that receives the platform fee. `feePercentage` and `feeAddress` must be provided together. If one is missing, validation fails. ## Validation Rules * `feePercentage` must be a number between `0` and `100` * `feeAddress` must be a valid blockchain address * On `GET /v2/request/{requestId}/pay` these are **query** parameters; on `POST /v2/payouts` and `POST /v2/payouts/batch` they're **body** parameters ## Endpoint Usage Use platform fee parameters on these endpoints: * [GET /v2/request//pay](https://api.request.network/open-api/#tag/v2request/GET/v2/request/\{requestId}/pay) * [POST /v2/payouts](https://api.request.network/open-api/#tag/v2payouts/POST/v2/payouts) * [POST /v2/payouts/batch](https://api.request.network/open-api/#tag/v2payouts/POST/v2/payouts/batch) ## How to Add Platform Fees Set the percentage and receiver address used by your platform. Add `feePercentage` and `feeAddress` to the payment endpoint call. The API returns payment payloads that include fee handling. Your app executes the returned transactions as usual. ## Integration Examples ### Request-based payment ```bash cURL theme={null} curl -X GET 'https://api.request.network/v2/request/{requestId}/pay?feePercentage=2.5&feeAddress=0x742d35CC6634c0532925a3B844BC9e7595f8fA40' \ -H 'x-api-key: YOUR_API_KEY' ``` ### Direct payout ```bash cURL theme={null} curl -X POST 'https://api.request.network/v2/payouts' \ -H 'x-api-key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", "amount": "100", "invoiceCurrency": "USD", "paymentCurrency": "USDC-base", "feePercentage": "2.5", "feeAddress": "0x742d35CC6634c0532925a3B844BC9e7595f8fA40" }' ``` ### Batch payout ```bash cURL theme={null} curl -X POST 'https://api.request.network/v2/payouts/batch' \ -H 'x-api-key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "payer": "0x2e2E5C79F571ef1658d4C2d3684a1FE97DD30570", "feePercentage": "2.5", "feeAddress": "0x742d35CC6634c0532925a3B844BC9e7595f8fA40", "requests": [ { "payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", "amount": "10", "invoiceCurrency": "USD", "paymentCurrency": "USDC-base" } ] }' ``` ## Related Pages Understand Request Network protocol-level fees. Inspect fee line items returned by API responses. ## API Reference For full schemas and examples, see [Request Network API Reference](https://api.request.network/open-api). # Protocol Fees Source: https://docs.request.network/api-features/protocol-fees Protocol fee model applied by Request Network API payments ## Protocol Fee Request Network charges a protocol fee on all payments processed through the API. The fee applies to every payment type: * ERC20 token payments * Native currency payments (ETH, POL, etc.) * Conversion payments * Batch payments * Crosschain payments * Subscription (coming soon) The protocol fee rate can change over time, so it isn't published here. Read the **current** rate that applies to any payment from the `metadata.protocolFee` field returned in every API response (see [Fee Information In Responses](#fee-information-in-responses) below), or [contact the Request Network team](https://request.network/discord) for current pricing. ## Who Pays the Fee? By default, the payer bears the protocol fee. It is added on top of the invoice amount, so the payee receives the full invoice amount. The exact fee for a given payment is returned in the API response before the payer pays, and a cap applies to large stablecoin payments. ## Shifting the Fee to the Payee If you want the payee to bear the protocol fee instead of the payer, reduce the invoice amount by the protocol fee percentage before creating the request. The payer then pays approximately the original invoice amount, while the payee effectively absorbs the fee. Use `metadata.protocolFee.percentage` from the response to calculate the exact reduction. ## Platform Fees You can add your own platform fee on top of the protocol fee. Both fees are handled automatically when you configure a platform fee in your request. To set up platform fees, include the following parameters in your payment request: * `feePercentage`: Your platform fee percentage (e.g., "2.5"). * `feeAddress`: The wallet address to receive your platform fees. When both protocol and platform fees are configured, the API automatically batches them into a single transaction for the payer. ### Fee Information In Responses Every API response includes the fee details in the metadata. Read the live protocol fee rate from `protocolFee.percentage` rather than hardcoding it: ```json theme={null} { "metadata": { "protocolFee": { "percentage": "...", "address": "0x..." }, "platformFee": { "percentage": "2.5", "address": "0x..." } } } ``` ## Tron-specific fees Transaction fees on Tron are paid in TRX and can be high when a wallet burns TRX directly for energy and bandwidth. To prevent you from needing TRX to broadcast a transaction, Request Network covers the required Tron resources through dedicated providers such as [CatFee](https://catfee.io/) and [Tron Energy Rent](https://tronenergyrent.com/). This provider-based flow makes Tron transactions cost less than if you burned TRX directly. To cover these sponsored resource costs, Request Network applies a fixed \$3.99 fee to every Tron payment. This Tron-specific fee is charged in addition to the standard protocol fee and is lower than the TRX amount you would typically spend when paying Tron network resources directly. # Query Payments Source: https://docs.request.network/api-features/query-payments Advanced payment search and filtering with the GET /v2/payments endpoint ## Overview Use [GET /v2/payments](https://api.request.network/open-api/#tag/v2payments/GET/v2/payments) to search payments with filters such as transaction hash, wallet, request identifiers, currency, type, and date range. This endpoint is designed for wallet-level reconciliation, payment history search, and operational reporting. ## Core Endpoint * [GET /v2/payments](https://api.request.network/open-api/#tag/v2payments/GET/v2/payments) ## How It Works `GET /v2/payments` requires at least one of these filters: * `txHash` * `walletAddress` * `paymentReference` * `requestId` * `reference` * `type` * `invoiceCurrency` * `paymentCurrency` If none are provided, validation fails. Optional filters include: * `fromDate`, `toDate` (ISO 8601 UTC) * `limit`, `offset` Date ranges are validated (`toDate` must be after or equal to `fromDate`). The response returns: * `payments` array with payment and request-linked metadata * `pagination.total` * `pagination.limit` * `pagination.offset` * `pagination.hasMore` ## Query Parameters At least one search parameter is required. ### Identity and Transaction Filters Transaction hash (66 chars: `0x` + 64 hex). Returns all payments in that transaction. Wallet address (EVM `0x...` or Tron `T...`). Returns payments where the wallet is payer or payee. Payment reference hex identifier. Request Network request ID. Custom merchant reference string. ### Type and Currency Filters Payment type. Values: `direct`, `conversion`, `crosschain`, `recurring`. Invoice currency (e.g., `USD`, `EUR`). Payment currency ID (e.g., `USDC-base`, `ETH-mainnet`). ### Date Range and Pagination Start date in ISO 8601 UTC format (e.g., `2026-01-01T00:00:00.000Z`). End date in ISO 8601 UTC format. Must be >= `fromDate`. Results per page. Pagination offset. ## Response Schema ```json Example response theme={null} { "payments": [ { "id": "01HXAMPLE1234567890ABCDEF", "amount": "100.00", "sourceNetwork": "base", "destinationNetwork": "base", "sourceTxHash": "0x1234...abcdef", "destinationTxHash": null, "timestamp": "2026-01-15T10:30:00.000Z", "type": "direct", "currency": "USD", "paymentCurrency": "USDC", "detectionSource": "request-network", "note": null, "fees": [ { "type": "gas", "stage": "sending", "amount": "0.002", "amountInUSD": "0.005", "currency": "ETH", "provider": "request-network" } ], "request": { "requestId": "01e273ecc29d4b526df3a0f1f05ffc59372af8752c2b678096e49ac270416a7cdb", "paymentReference": "0xb3581f0b0f74cc61", "hasBeenPaid": true, "customerInfo": null, "reference": "ORDER-2024-001234" } } ], "pagination": { "total": 157, "limit": 20, "offset": 0, "hasMore": true } } ``` ### Payment fields | Field | Type | Description | | -------------------- | -------------- | ------------------------------------------------------------------------------------- | | `id` | string | Unique payment identifier | | `amount` | string | Human-readable payment amount | | `sourceNetwork` | string | Originating blockchain network | | `destinationNetwork` | string | Receiving blockchain network | | `sourceTxHash` | string \| null | Source chain transaction hash | | `destinationTxHash` | string \| null | Destination chain tx hash (crosschain) | | `timestamp` | string | ISO 8601 timestamp | | `type` | string | `direct`, `conversion`, `crosschain`, `recurring` | | `currency` | string | Invoice currency | | `paymentCurrency` | string | Payment currency | | `fees` | array | Fee breakdown with type, amount, currency, provider | | `detectionSource` | string \| null | How the payment was confirmed: `"request-network"` or `"lifi"` | | `note` | string \| null | Additional context for non-standard settlements (e.g., LiFi fallback) | | `recurringPaymentId` | string \| null | Recurring payment ID if applicable | | `request` | object | Linked request with requestId, paymentReference, hasBeenPaid, customerInfo, reference | ## Practical Notes * Search parameters are combined with AND semantics. * Searching by `txHash` or `walletAddress` can return multiple rows from batch transactions. * Keep your reconciliation workers idempotent in case the same payment appears across repeated queries. ## Related Pages Read request-level status and metadata. Understand automatic detection and payment matching. Build real-time event-driven reconciliation. ## API Reference For full schemas and examples, see [Request Network API Reference](https://api.request.network/open-api). # Query Requests Source: https://docs.request.network/api-features/query-requests Request status monitoring, lifecycle management, and information retrieval ## Overview Use query endpoints to retrieve the latest status for a specific request. These endpoints are the main reconciliation surface for request-level state (paid/not paid, payment references, transaction hash, and optional metadata). ## Core Endpoints * [GET /v2/request/](https://api.request.network/open-api/#tag/v2request/GET/v2/request/\{requestId}) - get request status/details * [GET /v2/payments](https://api.request.network/open-api/#tag/v2payments/GET/v2/payments) - wallet-level payment search and reconciliation ## How It Works Call [GET /v2/request/](https://api.request.network/open-api/#tag/v2request/GET/v2/request/\{requestId}) to get current request-level status fields. Typical fields include: * `hasBeenPaid` * `paymentReference` * `txHash` * `isListening` * `requestAmount` — the original requested amount in invoice currency * `detectionSource` — how the payment was confirmed (`"request-network"` or `"lifi"`) * `note` — additional context for non-standard settlements (e.g., LiFi fallback) * `payerAddress` — the resolved payer wallet address for the payment (`null` when it can't be determined, e.g. for some contract-mediated flows) * `payerEoaAddress` — the payer's connected wallet address (`null` when unavailable). It can differ from `payerAddress` when a smart account is used. * `paidAmount`, `receivedAmount`, `excessAmount` — the amount paid by the payer, the raw amount received by the payment route, and any route-delivered amount above what was applied to the request * optional metadata such as `customerInfo` and `reference` Use [Webhooks & Events](/api-features/webhooks-events) for push updates, and use query endpoints as source-of-truth reads. For wallet-level reconciliation views, use [GET /v2/payments](https://api.request.network/open-api/#tag/v2payments/GET/v2/payments). ## Request Status Query `GET /v2/request/{requestId}` is the canonical request-level status endpoint for: * request payment completion checks (`hasBeenPaid`) * transaction linkage (`txHash`) * request identification (`requestId`, `paymentReference`) * conversion-related status fields when applicable (`amountInUsd`, `conversionRate`, `conversionBreakdown`) ## Reconciliation Pattern React to events in real time and update app state immediately. Use query endpoints to confirm latest status and backfill missed events. ## Practical Notes * Use `requestId` for deterministic lookup. * Keep idempotent reconciliation logic in case the same request is processed multiple times by your workers. * The `GET /v2/request` list endpoint accepts an optional `walletAddress` query parameter to scope results to a single payee wallet. Omit it to list across the authenticated identity's requests. ## Related Pages Understand automatic detection and status updates. Search and reconcile payment-level events. Build real-time event-driven reconciliation. ## API Reference For full schemas and examples, see [Request Network API Reference](https://api.request.network/open-api). # Recurring Payments Source: https://docs.request.network/api-features/recurring-payments Automated payment schedules for subscriptions and recurring billing ## Overview The Recurring Payments feature allows you to create and manage subscription-like payments on the blockchain. The API handles the scheduling and triggering of these payments, providing a reliable way to automate regular transfers. ## Core functionality * **Create Recurring Schedules:** Define a payment schedule with a start date, frequency (daily, weekly, monthly, yearly), and total number of payments. The system will generate a payment permit that encapsulates all the payment details. * **Payer Authorization:** To authorize the payment series, the payer signs the payment permit with an EIP-712 signature. This single authorization allows the system to trigger all subsequent payments in the schedule without further interaction from the payer. For the first payment, the payer may also need to approve a token allowance for the recurring payment contract if they have not already. * **Automated Payments:** Once the payer has authorized the schedule, Request Network API backend systems automatically trigger the payments at the specified intervals. You can rely on the API to handle the entire lifecycle of the recurring payments. * **Status Tracking and Webhooks:** You can monitor the status of each recurring payment, including processed payments, failures, and completion status (for example active, paused, completed). Webhook notifications are sent for key events like `payment.confirmed` and `payment.failed`, allowing your application to react in real time. * **Flexible Management:** The API provides the ability to manage the lifecycle of a recurring payment. You can cancel a recurring payment schedule to stop future payments. If a payment fails (for example due to insufficient funds), the schedule is paused, and you can unpause it once the issue is resolved. Unpausing a recurring payment after issues are resolved allows the subscription to catch up on missed payments. ## Security and Trust Recurring payments are built on a non-custodial smart contract with several security measures to protect payer funds and ensure predictable behavior. The core principle is that all payment parameters are defined upfront and cryptographically signed by the payer, preventing unauthorized changes. Key security features provided by the smart contract: * **Signature-Protected Payments:** Payments cannot be triggered without a valid EIP-712 signature from the payer. The smart contract verifies the signature for every payment attempt. * **Immutable Recipient:** The recipient address is part of the signed data. Funds can only be sent to this specified address and cannot be altered after the schedule is authorized. * **Fixed Payment Amount:** The amount for each payment is fixed in the signed permit. The smart contract transfers only this exact amount. * **Strict Payment Timing:** Payments cannot be triggered before their scheduled time. The contract calculates the due date for each payment and rejects premature attempts. * **No-Repeat Payments:** The contract tracks payments, making it impossible to process the same payment more than once. * **Enforced Payment Limit:** The total number of payments is defined in the signed permit. The smart contract enforces this limit and does not allow extra payments. * **Sequential Payments:** Payments must be triggered in strict order (payment #1, then #2, then #3). Out-of-order attempts fail. * **Signature Expiration:** Each recurring payment schedule has a `deadline`. If the signature expires, no further payments can be triggered. ## Recurring payment workflow ```mermaid theme={null} sequenceDiagram actor User as User (Payer) participant App participant API as Request Network API participant Blockchain App->>API: Create recurring payment schedule activate API API-->>App: Returns payment permit for signing deactivate API alt If token allowance is needed App->>User: Prompt for one-time token allowance approval User->>Blockchain: Approves allowance transaction end App->>User: Prompt to sign payment permit User->>App: Provides signature for the permit App->>API: Submit signed permit to activate schedule activate API API-->>App: Confirms activation deactivate API loop For each scheduled payment API->>Blockchain: Triggers payment automatically Blockchain-->>API: Payment result API->>App: Webhook notification (payment.confirmed / payment.failed) end ``` ## Supported Networks Recurring payments rely on ERC-20 permit (`EIP-2612`) signatures and are therefore **EVM-only**. **Mainnet:** * Ethereum * Arbitrum One * Optimism * Base * Polygon * BNB Smart Chain **Testnet:** * Sepolia Tron does not support ERC-20 permit semantics, so recurring payments are not available on Tron. For Tron, send single payouts on a schedule from your own backend. ## Supported currencies Recurring payments support ERC20 currencies available on the supported networks. See [Supported Chains and Currencies](/resources/supported-chains-and-currencies). ## How it works Create a recurring schedule with [POST /v2/payouts](https://api.request.network/open-api/#tag/v2payouts/POST/v2/payouts). For recurring payments, include a `recurrence` object with: * `startDate` * `frequency` (`DAILY`, `WEEKLY`, `MONTHLY`, `YEARLY`) * `totalPayments` * `payer` The response includes a payment permit payload (EIP-712 typed data) for signature and, when required, transactions for token allowance approval. The payer must: * approve the recurring payment contract to spend the required token amount (if not already approved) * sign the payment permit with an EIP-712 compatible wallet ```javascript theme={null} import { Wallet, providers } from "ethers"; const privateKey = "WALLET_PRIVATE_KEY"; const provider = new providers.JsonRpcProvider("RPC_URL"); const wallet = new Wallet(privateKey, provider); const recurringPaymentPermit = ...; // from API response const signature = await wallet._signTypedData( recurringPaymentPermit.domain, recurringPaymentPermit.types, recurringPaymentPermit.values, ); ``` Activate the recurring payment by submitting the permit signature with [POST /v2/payouts/recurring/](https://api.request.network/open-api/#tag/v2payouts/POST/v2/payouts/recurring/\{id}). A successful response confirms activation. The schedule is now active and payments are executed automatically at the configured intervals. Retrieve status, processed payments, next payment date, and related details with [GET /v2/payouts/recurring/](https://api.request.network/open-api/#tag/v2payouts/GET/v2/payouts/recurring/\{id}). Manage recurring payments with [PATCH /v2/payouts/recurring/](https://api.request.network/open-api/#tag/v2payouts/PATCH/v2/payouts/recurring/\{id}). * **cancel**: stops all future payments * **unpause**: resumes a paused recurring payment For full endpoint schemas and request/response examples, see [Request Network API Reference](https://api.request.network/open-api). # Safe Multisig Payments Source: https://docs.request.network/api-features/safe-multisig-payments Pay a secure payment from a Gnosis Safe multisig: request Safe-ready calldata, execute on your Safe, and track settlement via the intent endpoint. ## Overview When the wallet paying a secure payment is an existing [Gnosis Safe](https://safe.global/) multisig, the payment cannot be broadcast in a single signature — it has to be proposed on the Safe, signed by the required owners, and then executed. Request Network supports this flow directly: you ask the calldata endpoint for **Safe-ready** transactions, execute them on your Safe off-platform, and then hand the resulting Safe transaction hash back to the API so it can track settlement. This is distinct from the [smart-account path](/api-features/secure-payment-pages#smart-account-payments) on the hosted secure payment page, which derives its own ERC-4337 account automatically. Use the flow on this page when **your own payer wallet is a Safe** and you execute the transaction yourself. Safe multisig payments are **EVM-only**. They are not supported on Tron — requesting Safe calldata for a Tron network returns a 400. ## How it works Call `GET /v2/secure-payments/:token/pay` with `isSafe=true` and `wallet` set to the **Safe address**. The API returns calldata sized and priced for a Safe (it includes the required approval transactions and skips the EOA native-gas balance check). ```bash theme={null} curl -X GET "https://api.request.network/v2/secure-payments/01ABC123DEF456GHI789JKL/pay?wallet=0xSAFE_ADDRESS&isSafe=true" \ -H "x-client-id: YOUR_CLIENT_ID" ``` `isSafe=true` is mutually exclusive with `eoaWallet`, and `wallet` is required. The `wallet` value must be the Safe address that will execute the payment. Propose the returned transactions to your Safe, collect the required owner signatures, and execute. This happens entirely on your side using your Safe tooling — Request Network does not custody keys or co-sign. Once the Safe transaction is created, tell the API to start tracking it by calling `POST /v2/secure-payments/:token/intent` with `safeTxHash` (instead of `txHash`). ```bash theme={null} curl -X POST "https://api.request.network/v2/secure-payments/01ABC123DEF456GHI789JKL/intent" \ -H "x-client-id: YOUR_CLIENT_ID" \ -H "Content-Type: application/json" \ -d '{ "safeTxHash": "0xSAFE_TX_HASH", "chain": "POLYGON", "token": "USDC", "safePaymentDeadline": 1749760800 }' ``` Exactly one of `txHash` or `safeTxHash` must be provided. `safePaymentDeadline` is optional (Unix seconds) and can only be sent alongside `safeTxHash`. While the Safe transaction is pending execution, `GET /v2/secure-payments/:token` returns **HTTP 423 (Locked)** with the message `Secure payment is in progress` and a `requestStatuses[]` array carrying `safeTxHash`, `chainId`, and `safePaymentDeadline`. Request Network resolves the Safe transaction in the background: once executed it records the real on-chain transaction hash and confirms the payment; if it fails or the deadline passes, the payment is marked failed. ## Request fields ### `GET /v2/secure-payments/:token/pay` The Safe address that will execute the payment. Used for balance and approval calculation. Set to `true` to prepare calldata for a Gnosis Safe multisig payer. Mutually exclusive with `eoaWallet`; `wallet` must be set to the Safe address. Source chain for cross-chain payments. Values: `BASE`, `OPTIMISM`, `ARBITRUM`, `ETHEREUM`, `POLYGON`, `BNB`. Provide together with `token`. Source currency for cross-chain payments. Values: `USDC`, `USDT`. Provide together with `chain`. ### `POST /v2/secure-payments/:token/intent` The Safe transaction hash to track. Provide **either** `safeTxHash` (Safe multisig flow) **or** `txHash` (already-broadcast on-chain hash) — exactly one. Unix timestamp (seconds) — the hard on-chain execution deadline for the Safe + cross-chain route. Only valid alongside `safeTxHash`; the API stops tracking once this passes. Source chain. Values: `BASE`, `OPTIMISM`, `ARBITRUM`, `ETHEREUM`, `POLYGON`, `BNB`. Source token. Values: `USDC`, `USDT`. Optional address of the wallet making the payment. Echoed back on the response. ```json 200 OK (intent recorded) theme={null} { "intentId": "01HXEXAMPLE123", "paymentReference": "0xb3581f0b0f74cc61", "safeTxHash": "0xSAFE_TX_HASH", "safePaymentDeadline": 1749760800, "isListening": true } ``` ## Status while a Safe payment is in progress ```json 423 Locked theme={null} { "message": "Secure payment is in progress", "status": "pending", "requestStatuses": [ { "requestId": "01e273ecc29d4b526df3a0f1f05ffc59372af8752c2b678096e49ac270416a7cdb", "hasBeenPaid": false, "safeTxHash": "0xSAFE_TX_HASH", "chainId": 137, "safePaymentDeadline": 1749760800 } ] } ``` Treat `423` as "keep polling" — the Safe transaction has been recorded but not yet executed on-chain. Once Request Network detects execution, the request flips to paid (and `GET /v2/secure-payments/:token` returns the completed state); a `payment.confirmed` webhook fires as usual. ## Related Full request/response schemas for the secure payment endpoints. The automatic ERC-4337 path on the hosted page (for EOA payers). # Secure Payment Integration Guide Source: https://docs.request.network/api-features/secure-payment-integration-guide Integrate a secure payment experience via a secured link (redirect) ## Overview This guide shows how to integrate Secure Payment Pages in a production-ready way. The recommended pattern is: 1. Create secure payment links with `POST /v2/secure-payments` 2. Store returned `requestIds` in your system 3. Redirect the payer to the secure page 4. Use webhooks as the source of truth for payment status updates ## Prerequisites Before you integrate, make sure you have: * An API key or a Client ID linked to your integration domain * A webhook endpoint registered via `POST /v1/webhook` on the [Auth API](https://auth.request.network/open-api/#tag/webhook) with your `x-client-id` * Your webhook signing secret stored securely on your backend For setup details, see: * [Authentication](/api-reference/authentication) * [Webhooks](/api-reference/webhooks) ## Quick start Call `POST /v2/secure-payments` and store the returned `requestIds`. ```bash cURL theme={null} curl -X POST "https://api.request.network/v2/secure-payments" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "requests": [ { "payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", "amount": "10", "invoiceCurrency": "USDC-base", "paymentCurrency": "USDC-base" } ] }' ``` ```json 201 Created theme={null} { "requestIds": [ "01e273ecc29d4b526df3a0f1f05ffc59372af8752c2b678096e49ac270416a7cdb" ], "securePaymentUrl": "https://pay.request.network/?token=01ABC123DEF456GHI789JKL", "token": "01ABC123DEF456GHI789JKL" } ``` Store at least: * your internal metadata * returned `requestIds` * `token` * `securePaymentUrl` This mapping lets you reconcile webhook events back to your internal records. Redirect in the same tab or open the secure URL in a new tab. Handle payment events from webhooks and update your order/payment state from those events. ## Integration pattern: generated URL + redirect ### Backend example (Node.js/Express) ```javascript server.js theme={null} import express from "express"; const app = express(); app.use(express.json()); app.post("/api/checkout/secure-payment", async (req, res) => { const { orderId, payee, amount, currencyId } = req.body; const apiResponse = await fetch("https://api.request.network/v2/secure-payments", { method: "POST", headers: { "x-api-key": process.env.REQUEST_API_KEY, "content-type": "application/json", }, body: JSON.stringify({ requests: [ { payee, amount, invoiceCurrency: currencyId, paymentCurrency: currencyId, }, ], }), }); if (!apiResponse.ok) { const errorBody = await apiResponse.text(); return res.status(apiResponse.status).json({ error: errorBody }); } const securePayment = await apiResponse.json(); // Persist in your DB // Example payload: // { // orderId, // requestIds: securePayment.requestIds, // token: securePayment.token, // securePaymentUrl: securePayment.securePaymentUrl, // status: "pending" // } return res.status(200).json({ orderId, securePaymentUrl: securePayment.securePaymentUrl, }); }); ``` ### Frontend redirect examples ```javascript Same tab theme={null} window.location.href = securePaymentUrl; ``` ```javascript New tab theme={null} window.open(securePaymentUrl, "_blank", "noopener,noreferrer"); ``` ## Payment status updates with webhooks Use webhook events as your payment status source of truth. Typical mapping: * `payment.confirmed` -> mark order as paid * `payment.partial` -> mark order as partially paid * `payment.failed` -> mark order as failed ### Webhook handler example (signature verification + reconciliation) ```javascript webhook.js theme={null} import crypto from "node:crypto"; import express from "express"; const app = express(); app.use( express.raw({ type: "application/json", verify: (req, _res, buf) => { req.rawBody = buf; }, }), ); app.post("/webhooks/request", async (req, res) => { const signature = req.headers["x-request-network-signature"]; const secret = process.env.REQUEST_WEBHOOK_SECRET; const expectedSignature = crypto .createHmac("sha256", secret) .update(req.rawBody) .digest("hex"); if (!signature) { return res.status(401).json({ error: "Missing signature" }); } try { const isValid = crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expectedSignature), ); if (!isValid) { return res.status(401).json({ error: "Invalid signature" }); } } catch { return res.status(401).json({ error: "Invalid signature format" }); } const event = JSON.parse(req.rawBody.toString("utf8")); const requestId = event.requestId || event.requestID; // Find internal record by requestId in your DB, then update order status. // Example: // const checkout = await db.findCheckoutByRequestId(requestId) // if (event.event === "payment.confirmed") await db.markPaid(checkout.orderId) return res.status(200).json({ received: true }); }); ``` ## Using destination IDs Instead of specifying `payee`, `invoiceCurrency`, and `paymentCurrency` separately, you can use a `destinationId` that encodes all three in a single ERC-7828 composite ID: ```json theme={null} { "requests": [ { "destinationId": "0x6923...C7D7@eip155:8453#ABCD1234:0x8335...2913", "amount": "10" } ] } ``` When using a Client ID bound to a [payee destination](/api-features/payee-destinations), the `destinationId` can be omitted entirely — the API resolves it from the Client ID configuration. ## Crosschain payments on secure pages Secure payment pages support crosschain payments automatically. When the payer connects a wallet, the page checks balances across supported chains and shows payment options. The payer selects a route, and the page handles the rest. The two-endpoint flow is: 1. `GET /v2/secure-payments/:token` — returns metadata and `paymentOptions` with balance info per chain 2. `GET /v2/secure-payments/:token/pay?wallet=&chain=&token=` — returns executable calldata for the selected route For crosschain tracking, after the payer broadcasts the source-chain transaction, the page calls `POST /v2/secure-payments/:token/intent` with the transaction hash. See the [Secure Payments API Reference](/api-reference/secure-payments) for full endpoint details. ## Expiry handling Secure payment links expire after one week by default. If a payer opens an expired link, create a new secure payment link and redirect again. ## Troubleshooting * Verify your `x-api-key` or `x-client-id` header * If using Client ID in browser, verify the request origin is in allowed domains * The token may be expired * Create a fresh secure payment link and retry * Payment is already completed * Show a paid/completed state in your app instead of retrying payment * Verify HMAC signature validation uses raw request body * Ensure your endpoint returns `2xx` after successful processing * Confirm your DB lookup maps incoming `requestId`/`requestID` to stored request IDs ## Related docs Full request and response schema details. Event types, signing, retries, and payload details. API key and Client ID setup. # Secure Payment Pages Source: https://docs.request.network/api-features/secure-payment-pages Create hosted secure payment links and let payers complete payments from a dedicated secure flow. ## Overview Secure Payment Pages let you generate a hosted payment URL from the API and redirect the payer to a dedicated payment experience. This feature is useful when you want to reduce frontend tampering risk and separate payment execution from your checkout UI. ## Built-in contract safety check Before signing, the secure page validates that the transaction targets official Request Network contracts. The payer-facing status copy is: * "This is a safe smart contract" * "The smart contract you are interacting with is an official Request Network smart contract, it is audited and valid." If validation fails, the secure page warns the payer and prevents continuing with unsafe contract interactions. ## KYT wallet screening Secure Payment Pages can enforce KYT wallet screening when the payment destination has a compliance gate enabled. See [Compliance-gated payments](/use-cases/compliance-gated-payments) for configuration details and [Hypernative Standard Screening Policy](/use-cases/hypernative-standard-screening-policy) for the default policy categories and thresholds. ## How the flow works Call `POST /v2/secure-payments` with one or more requests. The API creates Request records and returns: * `requestIds` * `token` * `securePaymentUrl` Send the payer to `securePaymentUrl`. The hosted page loads the payment details and prepares the required transaction flow. The payer signs the returned transaction set from their wallet. Depending on token approvals, this can be one or more transactions. ## Authentication Both secure payment endpoints accept: * `x-api-key`, or * `x-client-id` with browser `Origin` See [Authentication](/api-reference/authentication) for implementation options. ## API Reference Create a secure payment entry and return a hosted secure payment URL. View the complete endpoint documentation with request/response schemas and examples. ## Crosschain Support Secure payment pages support crosschain payments. When a payer connects their wallet, the page fetches balance information across supported chains via the `paymentOptions` field. The payer can select which chain and token to pay from, and the page fetches executable calldata for the selected route. **Supported crosschain currencies:** USDC, USDT **Supported crosschain chains:** Ethereum, Arbitrum, Base, OP Mainnet, Polygon, BNB Chain See [Crosschain Payments](/api-features/crosschain-payments) for details on crosschain routing via LiFi. ## Smart Account Payments For supported EVM payments, the secure payment page can execute through an ERC-4337 [Safe](https://safe.global/) smart account derived automatically from the payer's connected wallet, bundled through [Pimlico](https://pimlico.io/). The whole payment — approval, funding, and settlement — runs as a single UserOperation. **How it works:** 1. The payer authorizes the token spend. For tokens that support [EIP-2612 `permit`](#gasless-token-approvals-eip-2612) (such as USDC), this is an off-chain signature with no separate approval transaction; for other tokens (such as USDT) it is a one-time on-chain approval. 2. The smart account batches the `permit`/approval, the funding transfer from the payer's wallet, and the payment transaction into a single UserOperation. 3. The bundler submits the UserOperation and the page surfaces the resulting on-chain transaction hash on the success screen. **Supported chains for smart account payments:** Ethereum, Base, Arbitrum, Optimism, Polygon, and BNB Chain (plus Sepolia testnet). Native-currency payments and Tron payments do not use the smart-account path. Smart account payments are an automatic client-side behavior of the hosted secure payment page. There is no API field to enable them — the same `GET /v2/secure-payments/:token/pay` endpoint provides the transaction calldata, and the page decides whether to route through a smart account. To pay from an existing **Gnosis Safe multisig** programmatically, see [Safe multisig payments](/api-features/safe-multisig-payments). ### Gasless token approvals (EIP-2612) ERC-20 payments normally need a separate `approve()` transaction before the transfer, which costs the payer extra gas. On the smart-account path, the secure payment page checks each token at runtime for [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) `permit` support: * **Permit-enabled tokens (e.g. USDC, EURC)** — the payer signs an off-chain `permit` instead of sending an approval transaction. No approval gas is required, and the approval gas line item is hidden in the payer's cost breakdown. * **Non-permit tokens (e.g. USDT, DAI)** — the page falls back to a standard one-time on-chain `approve()`, which costs native gas as usual. The approval is a one-time authorization that stays in effect until revoked (the page exposes a revoke control), and it applies to both single payments and [multicall payouts](/api-features/payouts#multicall-payouts). Gasless approvals are available on the same chains as smart-account payments listed above. Whether a token is gasless is determined per token at payment time by probing the token contract, not from a fixed list — any EIP-2612-compliant token gets the gasless signature path. USDC/EURC and USDT/DAI are cited here only as common examples. ## Status outcomes * `200`: token is valid and payable * `403`: token expired or status is not payable * `404`: token not found * `409`: payment already completed ## Next pages Endpoint details, request and response schemas, and error codes. Check supported chain and currency coverage before creating links. # Secure Payment Supported Networks and Currencies Source: https://docs.request.network/api-features/secure-payment-supported-networks-and-currencies Chain and currency coverage for Secure Payment Pages. ## Coverage model Secure Payment Pages support the same 8 networks as Request API destinations: | Type | Networks | | ------- | ---------------------------------------------------------------------------------------------- | | EVM | Ethereum (`mainnet`), Arbitrum One, Optimism, Base, Polygon (`matic`), BNB Smart Chain (`bsc`) | | Non-EVM | **Tron** | | Testnet | Sepolia | Stablecoins (USDC, USDT) are supported on every mainnet, including **Tron** (USDT and USDC, TRC-20). **USDT0** is additionally available as a payment currency (an alias of USDT) on Arbitrum One, Optimism, and Polygon. FAU is available on Sepolia for testing. For a payer's perspective, a Secure Payment link can be paid from any of these chains — when the source chain differs from the merchant's destination chain, the swap is routed through Li.Fi (EVM source chains only; Tron payments are same-chain). Batch secure payment links — multiple payees in a single hosted link — are EVM-only. Tron Secure Payment links are single-recipient. For the canonical chain × currency table and feature-support matrix, see [Supported Chains and Currencies](/resources/supported-chains-and-currencies). ## Supported currencies Currencies are determined by the Request token list and available per network. Use `GET /v2/currencies` to query support by network. ```bash cURL theme={null} curl -X GET "https://api.request.network/v2/currencies?network=sepolia" \ -H "x-api-key: YOUR_API_KEY" ``` ```json Example theme={null} [ { "id": "FAU-sepolia", "name": "FAU", "symbol": "FAU", "decimals": 18, "network": "sepolia", "type": "ERC20", "chainId": 11155111 } ] ``` ## How to verify support * Query `GET /v2/currencies` with `network` and `symbol` filters for discovery * Validate currency IDs before calling `POST /v2/secure-payments` * Use the same network and currency validation rules you apply to normal request flows For an at-a-glance list of ecosystem support, see [Supported Chains and Currencies](/resources/supported-chains-and-currencies). ## Preflight checklist * Confirm each currency ID exists in `GET /v2/currencies` * Confirm each request network is supported by your target flow * Confirm your selected currencies are available on those networks # Standard Native & ERC20 Payments Source: https://docs.request.network/api-features/standard-payments Simple crypto-to-crypto payments using native currencies and ERC20 tokens ## Overview Standard payments are same-currency payments where `invoiceCurrency` and `paymentCurrency` match. Use this type when you want straightforward crypto settlement without conversion or crosschain routing. ## Native Currency Payments Pay with network native currencies (for example ETH on Ethereum-compatible networks). **Characteristics:** * no ERC20 allowance step * one payment transaction in most cases * payer covers gas in native token ## ERC20 Token Payments Pay with ERC20 tokens such as USDC, USDT, and DAI (depending on chain support). **Characteristics:** * may require approval before payment * API can return approval calldata when needed * payment then executes with token transfer transaction(s) ## How It Works Use either: * [POST /v2/request](https://api.request.network/open-api/#tag/v2request/POST/v2/request) for request-first flows * [POST /v2/payouts](https://api.request.network/open-api/#tag/v2payouts/POST/v2/payouts) for payment-first flows For standard payments, set matching values for `invoiceCurrency` and `paymentCurrency`. Fetch payment payload with [GET /v2/request//pay](https://api.request.network/open-api/#tag/v2request/GET/v2/request/\{requestId}/pay) when using request-first flow. The response includes: * payment transaction calldata * approval calldata when token approval is required * metadata such as `stepsRequired` and `needsApproval` For native payments, execute the payment transaction. For ERC20 payments, execute approval first if needed, then execute the payment transaction. ## Supported Networks & Tokens Choose valid chain/token pairs from the supported currencies and chains catalog. Supports native currency payments and ERC20 token payments. Use [Supported Chains and Currencies](/resources/supported-chains-and-currencies) and [Request Network Token List](/resources/token-list) for current availability. ## Used In Simple crypto invoices via the Dashboard Server-side payment links via the API ## API Reference For full endpoint schemas and examples, see [Request Network API Reference](https://api.request.network/open-api). # Webhooks & Events Source: https://docs.request.network/api-features/webhooks-events Real-time notifications for payment lifecycle events and request status changes ## Overview Webhooks provide real-time notifications when payment and request events occur, enabling immediate response to status changes without constant polling. ## Event Categories The Request Network webhook system emits **13 event types** across five categories: | Category | Events | | -------------------------------------- | ---------------------------------------------------------------------------- | | **Payment** (core) | `payment.confirmed`, `payment.partial`, `payment.failed`, `payment.refunded` | | **Payment** (Client ID-scoped) | `payment.confirmed.client_id`, `payment.partial.client_id` | | **Payment** (Checkout-scoped) | `payment.confirmed.checkout`, `payment.partial.checkout` | | **Processing** (crypto-to-fiat) | `payment.processing` (with `subStatus`) | | **Request** | `request.recurring` | | **Compliance / Bank** | `compliance.updated`, `payment_detail.updated` | | **Secure Payment Page** (payer funnel) | `secure_payment.user_event` (with `userEvent`) | The `.client_id` and `.checkout` variants are emitted in addition to the base `.confirmed` / `.partial` events when the request was created via a Client ID or as a checkout / secure payment, respectively. They include extra metadata (`clientId`, `origin`). Payment webhook payloads include `payerAddress`, the address used to make the payment, and `payerEoaAddress`, the payer's connected wallet address. These can differ when a smart account is used. Both are `null` when unavailable. See the [Webhooks reference](/api-reference/webhooks) for the full payload schema. For full payload schemas and headers, see the [Webhooks reference](/api-reference/webhooks). ## How It Works ```mermaid theme={null} graph LR A[Event Occurs] --> B[HMAC Signed POST] B --> C[Your Endpoint] C --> D[Verify & Process] D --> E[Return 200 OK] ``` **Process:** 1. **Event occurs:** Payment confirmed, request created, compliance updated 2. **Secure delivery:** HMAC SHA-256 signed POST to your configured endpoint 3. **Your processing:** Verify `x-request-network-signature`, update application state 4. **Reliable delivery:** 3 retries (1s, 5s, 15s delays) with 5-second timeout ## Key Features ### Reliability * **Idempotency support:** Use `x-request-network-delivery` header for duplicate detection * **Delivery confirmation:** Monitor `x-request-network-retry-count` header to track attempts ### Security * **HMAC SHA-256 signatures:** Every webhook includes `x-request-network-signature` header * **HTTPS required:** Production endpoints must use secure connections * **Test webhook identification:** `x-request-network-test` header for development ### Development Tools * **Test deliveries:** Fire test events via `POST /v1/webhook/test` (Auth API) — see [Webhooks reference](/api-reference/webhooks#testing) * **ngrok integration:** Receive webhooks locally during development * **Comprehensive logging:** Request API logs all delivery failures with attempt details ## Common Use Cases * **Invoice systems:** Automatically mark invoices as paid when `payment.confirmed` received * **Order fulfillment:** Release goods or services immediately after payment confirmation * **Subscription management:** Handle `request.recurring` for automatic billing renewals * **Compliance workflows:** Update user permissions when `compliance.updated` shows KYC approval * **Real-time dashboards:** Display live payment status using `payment.processing` subStatus values * **Payer-funnel visibility:** Track wallet connection and signature progress on the Secure Payment Page via `secure_payment.user_event` ## Implementation Complete technical documentation with setup, payloads, and code examples Working webhook handlers with Express.js and Next.js POST /v1/webhook to create, GET/PUT/DELETE to manage, /test to fire test deliveries # Authentication Source: https://docs.request.network/api-reference/authentication How to authenticate Request Network API calls with API keys or Client ID ## Overview Request Network API supports two authentication modes: * `x-api-key` for server-side integrations * `x-client-id` for browser/client integrations (with `Origin` header) Use this page as the canonical auth reference. Client IDs are managed in the [Request Dashboard](https://dashboard.request.network). Webhooks can be managed no-code from the [Dashboard](/tools/dashboard#webhooks) or programmatically via the [Auth API](https://auth.request.network/open-api/#tag/webhook) (`POST /v1/webhook` with the `x-client-id` header). ## Choose the Right Method | Method | Best for | Header(s) | | --------- | -------------------------------------------------------- | -------------------------- | | API Key | Backend services, cron jobs, trusted server environments | `x-api-key` | | Client ID | Browser/front-end calls where client auth is required | `x-client-id` (+ `Origin`) | ## API Key Authentication Use API keys for backend calls. ### Example ```bash cURL theme={null} curl -X GET 'https://api.request.network/v2/request/{requestId}' \ -H 'x-api-key: YOUR_API_KEY' ``` ## Client ID Authentication Use Client ID when your integration needs browser-side authentication flow. ### Example ```bash cURL theme={null} curl -X GET 'https://api.request.network/v2/request/{requestId}' \ -H 'x-client-id: YOUR_CLIENT_ID' \ -H 'Origin: https://your-app.example' ``` For browser-based requests, `Origin` is part of the request context and is required for Client ID auth. ## Header Reference * `x-api-key`: API key used for server-side auth * `x-client-id`: Client identifier used for client-side auth * `Origin`: required with Client ID flows in browser contexts ## Rate limits Request Network enforces rate limits on `x-client-id` requests to keep the API responsive for every integrator. By default, authenticated `x-client-id` integrators get an SLA of **300 requests per minute (5 requests per second)** in production. Several tiers exist. Most integrators run on **Default**; contact us to move to a higher-throughput tier if you need one: | Tier | Rate | | ---------------------- | -------------------------- | | Basic | 2 req/sec (120 req/min) | | Default (standard SLA) | 5 req/sec (300 req/min) | | Premium | 10 req/sec (600 req/min) | | High-volume | 20 req/sec (1,200 req/min) | Tiers are listed from lowest to highest throughput; `Default` is the standard SLA applied to authenticated integrators unless you have been assigned another tier. Rate limits are scoped per client ID and IP address, so usage from one client ID on one IP doesn't affect your limit on another IP. If you sustain a breach of your limit, requests from that client ID and IP are temporarily blocked (5 minutes by default) before being allowed again. Some endpoints, such as client ID management, use a more relaxed limit than the general default shown above. The production default is 5 req/sec. Non-production environments (development and test) run at a higher limit to support testing. Contact us if your integration needs a higher tier than the production default. ## Common Authentication Errors ### 401 Unauthorized * Missing auth header * Invalid/expired API key or client ID ### 403 Forbidden * Credentials are valid but not allowed for the requested operation * Client ID is revoked or restricted ### 429 Too Many Requests * Request rate exceeded for your credentials. See [Rate limits](#rate-limits). ## Security Guidance * Keep API keys server-side and out of frontend bundles * Store credentials in environment variables or secret managers * Rotate compromised credentials immediately * Verify webhook signatures independently (webhook signing uses a separate secret) ## Related Pages Create credentials and manage webhook configuration. Signature verification and delivery behavior. Full endpoint authentication requirements. # Create a new client ID Source: https://docs.request.network/api-reference/client-ids/create-a-new-client-id /api-reference/openapi.v2.json post /v2/client-ids Create a new client ID for frontend applications with domain restrictions # Get a specific client ID Source: https://docs.request.network/api-reference/client-ids/get-a-specific-client-id /api-reference/openapi.v2.json get /v2/client-ids/{id} Get details of a specific client ID # List all client IDs Source: https://docs.request.network/api-reference/client-ids/list-all-client-ids /api-reference/openapi.v2.json get /v2/client-ids Get all client IDs for the authenticated platform # Revoke a client ID Source: https://docs.request.network/api-reference/client-ids/revoke-a-client-id /api-reference/openapi.v2.json delete /v2/client-ids/{id} Revoke a client ID (cannot be reactivated) # Update a client ID Source: https://docs.request.network/api-reference/client-ids/update-a-client-id /api-reference/openapi.v2.json put /v2/client-ids/{id} Update client ID settings including domains and rate limits # Get conversion routes for a specific currency Source: https://docs.request.network/api-reference/currencies/get-conversion-routes-for-a-specific-currency /api-reference/openapi.v2.json get /v2/currencies/{currencyId}/conversion-routes Get a list of currency objects (with all details) that can be converted to from the specified currency. Optionally filter by network using the 'network' query parameter. # Get currencies Source: https://docs.request.network/api-reference/currencies/get-currencies /api-reference/openapi.v2.json get /v2/currencies Get a list of all available tokens, or filter by network, symbol, or id. # Endpoints Overview Source: https://docs.request.network/api-reference/endpoints-overview Quick index of v2 endpoint groups ## Overview Use this page as the index for v2 endpoints in the docs-integrated OpenAPI reference. ## v2 Endpoint Groups v2 request endpoints for create, status, pay calldata, and routes. v2 payout endpoints for direct, batch, and recurring operations. v2 payment search endpoint for reconciliation and reporting. v2 payer endpoints for KYC, agreement, and payment details. ## Requests (v2) * [v2 Request endpoints](https://api.request.network/open-api/#tag/v2request) * [POST /v2/request](https://api.request.network/open-api/#tag/v2request/POST/v2/request) * [GET /v2/request/](https://api.request.network/open-api/#tag/v2request/GET/v2/request/%7BrequestId%7D) * [GET /v2/request//pay](https://api.request.network/open-api/#tag/v2request/GET/v2/request/%7BrequestId%7D/pay) * [GET /v2/request//routes](https://api.request.network/open-api/#tag/v2request/GET/v2/request/%7BrequestId%7D/routes) ## Payouts (v2) * [v2 Payout endpoints](https://api.request.network/open-api/#tag/v2payouts) * [POST /v2/payouts](https://api.request.network/open-api/#tag/v2payouts/POST/v2/payouts) * [POST /v2/payouts/batch](https://api.request.network/open-api/#tag/v2payouts/POST/v2/payouts/batch) * [POST /v2/payouts/recurring/](https://api.request.network/open-api/#tag/v2payouts/POST/v2/payouts/recurring/%7Bid%7D) ## Payments (v2) * [GET /v2/payments](https://api.request.network/open-api/#tag/v2payments/GET/v2/payments) ## Payer / Compliance (v2) * [v2 Payer endpoints](https://api.request.network/open-api/#tag/v2payer) * [POST /v2/payer](https://api.request.network/open-api/#tag/v2payer/POST/v2/payer) * [GET /v2/payer/](https://api.request.network/open-api/#tag/v2payer/GET/v2/payer/%7BclientUserId%7D) * [PATCH /v2/payer/](https://api.request.network/open-api/#tag/v2payer/PATCH/v2/payer/%7BclientUserId%7D) # Get the status of a recurring payment Source: https://docs.request.network/api-reference/pay/get-the-status-of-a-recurring-payment /api-reference/openapi.v2.json get /v2/payouts/recurring/{id} Retrieve the current status and execution details of a recurring payment. Returns information about executed payments, remaining executions, next payment date, and overall status. This endpoint is useful for monitoring recurring payment progress and checking if payments are being executed as expected. Note: Customer information (PII) is not included in the response for security reasons. # Initiate a payment Source: https://docs.request.network/api-reference/pay/initiate-a-payment /api-reference/openapi.v2.json post /v2/payouts Initiate a payment without having to create a request first. Supports both one-time and recurring payments. For recurring payments, specify the recurrence object with start date, frequency, total executions, and payer address. The system will create a recurring payment schedule and return the necessary transactions for allowance approval and signature submission. Optionally includes customer information (firstName, lastName, email, address) and a merchant reference field for checkout widget implementations and receipt tracking. # Pay multiple requests in one transaction Source: https://docs.request.network/api-reference/pay/pay-multiple-requests-in-one-transaction /api-reference/openapi.v2.json post /v2/payouts/batch Pays multiple payment requests in one transaction by either creating new requests or using existing request IDs. All requests must be on the same network. Supports mixed ERC20, Native, and conversion requests. # Submit a recurring payment signature Source: https://docs.request.network/api-reference/pay/submit-a-recurring-payment-signature /api-reference/openapi.v2.json post /v2/payouts/recurring/{id} Submit a signature for a recurring payment permit to activate the recurring payment schedule. This endpoint is called after creating a recurring payment and obtaining the permit data. The signature authorizes the recurring payment contract to execute payments on behalf of the payer according to the schedule. Once activated, payments will be executed automatically at the specified intervals. # Update a recurring payment Source: https://docs.request.network/api-reference/pay/update-a-recurring-payment /api-reference/openapi.v2.json patch /v2/payouts/recurring/{id} Update a recurring payment by cancelling it or unpausing it. When cancelling, optionally returns a transaction to decrease allowance. When unpausing, resumes execution of a paused recurring payment. # Create compliance data for a user Source: https://docs.request.network/api-reference/payer/create-compliance-data-for-a-user /api-reference/openapi.v2.json post /v2/payer Checks compliance status and returns necessary URLs for completing compliance. # Create payment details Source: https://docs.request.network/api-reference/payer/create-payment-details /api-reference/openapi.v2.json post /v2/payer/{clientUserId}/payment-details Create payment details for a user # Get compliance status for a user Source: https://docs.request.network/api-reference/payer/get-compliance-status-for-a-user /api-reference/openapi.v2.json get /v2/payer/{clientUserId} Retrieves the comprehensive compliance status for a specific user, including KYC and agreement status. # Get payment details for a user Source: https://docs.request.network/api-reference/payer/get-payment-details-for-a-user /api-reference/openapi.v2.json get /v2/payer/{clientUserId}/payment-details Retrieves the registered bank account details for a user. Optionally filter by payment details ID. # Update agreement status Source: https://docs.request.network/api-reference/payer/update-agreement-status /api-reference/openapi.v2.json patch /v2/payer/{clientUserId} Update the agreement completion status for a user. # Create a new request Source: https://docs.request.network/api-reference/request/create-a-new-request /api-reference/openapi.v2.json post /v2/request Create a new payment request # Get payment calldata Source: https://docs.request.network/api-reference/request/get-payment-calldata /api-reference/openapi.v2.json get /v2/request/{requestId}/pay Get the calldata needed to pay a request. For same-chain payments, returns transaction calldata that can be directly executed. For crosschain payments (when chain and token parameters are provided and differ from the request's native chain), returns a payment intent that needs to be signed and processed through the crosschain bridge. For off-ramp payments, use the query parameters clientUserId and paymentDetailsId. Note: Crosschain requests with an expectedAmount less than 1 are rejected. # Get payment routes Source: https://docs.request.network/api-reference/request/get-payment-routes /api-reference/openapi.v2.json get /v2/request/{requestId}/routes Get available payment routes for a request. This endpoint analyzes the payer's wallet balance across supported chains and returns possible payment methods. Routes include direct same-chain payments and crosschain bridging options when the payer has sufficient balance on different chains than the request's native chain. # Get request status Source: https://docs.request.network/api-reference/request/get-request-status /api-reference/openapi.v2.json get /v2/request/{requestId} Get the status of a payment request # Send a payment intent Source: https://docs.request.network/api-reference/request/send-a-payment-intent /api-reference/openapi.v2.json post /v2/request/payment-intents/{paymentIntentId} Send a payment intent # Update a recurring request Source: https://docs.request.network/api-reference/request/update-a-recurring-request /api-reference/openapi.v2.json patch /v2/request/{requestId} Update a recurring request # Secure Payments API Reference Source: https://docs.request.network/api-reference/secure-payments Generate secure payment URLs, retrieve payment metadata, and get executable calldata. ## Endpoints * `POST /v2/secure-payments` — Create a secure payment (incoming, single or batch on EVM; single only on Tron) * `POST /v2/secure-payments/payouts` — Create a hosted secure payout link (outgoing payment to a recipient) * `GET /v2/secure-payments` — Lookup by request ID * `GET /v2/secure-payments/:token` — Get payment metadata * `GET /v2/secure-payments/:token/pay` — Get payment calldata * `POST /v2/secure-payments/:token/intent` — Record crosschain payment intent * `POST /v2/secure-payments/multicall-payouts` — Combine multiple existing payout links into one multicall link * `GET /v2/secure-payments/multicall-payouts/:token` — Get multicall payout details (operator/session auth) Batch incoming and batch payouts are EVM-only. Tron requests with multiple `requests[]` items return a 400 with `Batch payments are not supported for TRON networks`. ## Authentication | Endpoint | Supported auth | | -------------------------------------------------- | ------------------------------------------------------------------ | | `POST /v2/secure-payments` | `x-api-key`, `x-client-id` + Origin, session, or orchestrator pair | | `GET /v2/secure-payments` | Session only | | `GET /v2/secure-payments/:token` | `x-client-id` (+ Origin) | | `GET /v2/secure-payments/:token/pay` | `x-client-id` (+ Origin) | | `POST /v2/secure-payments/:token/intent` | `x-client-id` (+ Origin) | | `POST /v2/secure-payments/multicall-payouts` | `x-api-key`, `x-client-id` + Origin, session, or orchestrator pair | | `GET /v2/secure-payments/multicall-payouts/:token` | Session only | *** ## POST /v2/secure-payments Create a secure payment entry and return a hosted payment URL. ### Request fields Array of payment requests. One item creates a single payment. Multiple items create a batch payment. ERC-7828 composite destination ID encoding payee wallet, chain, and token. Format: `{interopAddress}:{tokenAddress}`. Optional when the authenticated client ID has a bound payee destination. Human-readable payment amount (e.g., `"10.50"`). Must be greater than 0. Optional fee percentage from `0` to `100` (e.g., `"2.5"` for 2.5%). Optional fee recipient address. Required when `feePercentage` is set. Optional merchant reference for reconciliation (max 255 chars). Optional payer identifier (max 255 chars). Optional `http(s)` URL displayed as a button on the success screen. After a successful payment the payer can click the button to return to your site — there is no auto-redirect. Only safe URLs are accepted: scheme must be `http` or `https`, and the value must not contain HTML/script payload characters (`<`, `>`, `"`, `'`, `` ` ``, whitespace). Optional button label for the redirect (1–255 chars). Defaults to **"Go Back and Close"** when omitted. Cannot include HTML control characters (`<`, `>`, `&`, `"`, `'`, `` ` ``). **Cannot be set without `redirectUrl`** — the API rejects with 400 `redirectLabel cannot be provided without redirectUrl`. ```bash cURL theme={null} curl -X POST "https://api.request.network/v2/secure-payments" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "requests": [ { "destinationId": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7@eip155:11155111#80B12379:0x370DE27fdb7D1Ff1e1BaA7D11c5820a324Cf623C", "amount": "10" } ], "feePercentage": "2.5", "feeAddress": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", "reference": "ORDER-2024-001", "redirectUrl": "https://merchant.example.com/order/2024-001/thank-you", "redirectLabel": "Back to merchant" }' ``` ```json 201 Created theme={null} { "requestIds": [ "01e273ecc29d4b526df3a0f1f05ffc59372af8752c2b678096e49ac270416a7cdb" ], "securePaymentUrl": "https://pay.request.network/?token=01ABC123DEF456GHI789JKL", "token": "01ABC123DEF456GHI789JKL" } ``` ### Error responses * `400`: invalid body or unsupported secure payment configuration * `401`: unauthorized * `429`: rate limited *** ## POST /v2/secure-payments/payouts Create a hosted **secure payout link** — an outgoing single-recipient payment URL the payer (you) opens to sign and broadcast the transaction. Useful for sending payments to contractors, vendors, or any external recipient when you want a hosted UI instead of executing calldata yourself. ### Request fields Recipient wallet address. EVM `0x...` or Tron `T...` format. Wallet that created the payout (typically the payer wallet). Destination network. Values: `mainnet`, `arbitrum-one`, `optimism`, `base`, `matic`, `bsc`, `tron`, `sepolia`. Payment currency in `-` form, e.g. `USDC-base`, `USDT-tron`, `FAU-sepolia`. Human-readable amount (e.g., `"100"`). Optional merchant reference (max 255 chars). Optional recipient identifier (max 255 chars). Optional fee percentage from `0` to `100`. Optional fee recipient address. Required when `feePercentage` is set. Optional `http(s)` URL rendered as a button on the success screen for the signer to return to your app. Same validation rules as on `POST /v2/secure-payments` — see [above](#post-v2secure-payments). Optional button label (1–255 chars). Defaults to **"Go Back and Close"**. Cannot be set without `redirectUrl`. ```bash cURL (EVM payout) theme={null} curl -X POST "https://api.request.network/v2/secure-payments/payouts" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "recipient": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", "creatorWalletAddress": "0x2e2E5C79F571ef1658d4C2d3684a1FE97DD30570", "network": "base", "currency": "USDC-base", "amount": "250", "reference": "INVOICE-2026-042", "redirectUrl": "https://merchant.example.com/payouts/done" }' ``` ```bash cURL (Tron payout) theme={null} curl -X POST "https://api.request.network/v2/secure-payments/payouts" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8", "creatorWalletAddress": "TKb9mFPjUgCbrQpenCm4Z3T7ELNrLWurfm", "network": "tron", "currency": "USDT-tron", "amount": "250", "reference": "INVOICE-2026-042" }' ``` ```json 201 Created theme={null} { "requestIds": [ "01e273ecc29d4b526df3a0f1f05ffc59372af8752c2b678096e49ac270416a7cdb" ], "securePaymentUrl": "https://pay.request.network/?token=01PAYOUT123ABCDEF456", "token": "01PAYOUT123ABCDEF456" } ``` ### Error responses * `400`: invalid body, unsupported network/currency, or batch attempt on Tron * `401`: unauthorized * `429`: rate limited *** ## GET /v2/secure-payments Lookup a secure payment by request ID. Requires a SIWE wallet session. ### Query parameters The request ID to look up. ```json 200 OK theme={null} { "token": "01ABC123DEF456GHI789JKL", "securePaymentUrl": "https://pay.request.network/?token=01ABC123DEF456GHI789JKL", "status": "pending", "paymentType": "single", "createdAt": "2026-03-15T10:00:00.000Z", "expiresAt": "2026-03-22T10:00:00.000Z" } ``` ### Error responses * `404`: secure payment not found for the given request ID *** ## GET /v2/secure-payments/:token Retrieve payment metadata and display information. Returns amounts, destination info, status, and optionally crosschain payment options — but **not** executable transaction calldata. Use `/pay` for calldata. ### Path parameters Secure payment token returned from `POST /v2/secure-payments`. ### Query parameters Payer wallet address. When provided, the response includes `paymentOptions` with balance information across supported chains. Optional for Tron payments (the API uses a fallback address if omitted). ```bash cURL theme={null} curl -X GET "https://api.request.network/v2/secure-payments/01ABC123DEF456GHI789JKL?wallet=0x1234567890123456789012345678901234567890" \ -H "x-api-key: YOUR_API_KEY" ``` ```json 200 Single payment theme={null} { "paymentType": "single", "payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", "network": "base", "amount": "10000000000000000000", "paymentCurrency": "USDC-base", "isNativeCurrency": false, "status": "pending", "destination": { "destinationId": "0x6923...C7D7@eip155:8453#ABCD1234:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "payeeAddress": "0x6923...C7D7@eip155:8453", "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "walletAddress": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", "network": "base" }, "reference": "ORDER-2024-001", "paymentOptions": { "BASE": { "USDC": { "balance": "150.00", "hasEnoughBalance": true, "neededAmount": "10.00" } }, "ARBITRUM": { "USDC": { "balance": "25.00", "hasEnoughBalance": true, "neededAmount": "10.02" } } } } ``` ```json 200 Batch payment theme={null} { "paymentType": "batch", "payees": [ "0xb07d2398d2004378cad234da0ef14f1c94a530e4", "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7" ], "network": "sepolia", "amounts": ["50", "10"], "paymentCurrencies": ["FAU-sepolia", "FAU-sepolia"], "isNativeCurrency": [false, false], "status": "pending", "destinations": [ { "destinationId": "...", "payeeAddress": "...", "tokenAddress": "...", "walletAddress": "0xb07d2398d2004378cad234da0ef14f1c94a530e4", "network": "sepolia" } ], "reference": null } ``` ### Error responses * `403`: token expired or not payable * `404`: token not found * `409`: secure payment already completed * `423`: a Safe multisig payment is in progress — keep polling (see [Safe multisig payments](/api-features/safe-multisig-payments)) * `429`: rate limited *** ## GET /v2/secure-payments/:token/pay Retrieve executable transaction calldata for the secure payment. For crosschain payments, provide `chain` and `token` query parameters to select the source route. The `:token` in the URL path is the secure payment token (a ULID identifier). The `token` query parameter is the source currency symbol (`USDC` or `USDT`) for crosschain route selection. These are different values. For **Tron** secure payments, do not pass `chain` or `token` query parameters — the calldata is generated for the Tron network directly. Tron payments are single-recipient and same-chain only. ### Path parameters Secure payment token (ULID returned from `POST /v2/secure-payments`). ### Query parameters Payer wallet address. Used for approval and balance checks. Optional for Tron payments (the API uses a fallback address if omitted). Source chain for crosschain payments. Values: `BASE`, `OPTIMISM`, `ARBITRUM`, `ETHEREUM`, `POLYGON`, `BNB`. Must be provided together with the `token` query parameter. Crosschain swap-to-pay is EVM-source only. Source currency for crosschain payments. Values: `USDC`, `USDT`. Must be provided together with `chain`. Set to `true` when the payer wallet is a Gnosis Safe multisig. Returns Safe-ready calldata; `wallet` must be the Safe address and `eoaWallet` must not be set. See [Safe multisig payments](/api-features/safe-multisig-payments). Externally-owned account address used to fund a smart-account payment. Mutually exclusive with `isSafe`. ```bash cURL (same-chain) theme={null} curl -X GET "https://api.request.network/v2/secure-payments/01ABC123DEF456GHI789JKL/pay?wallet=0x1234..." \ -H "x-api-key: YOUR_API_KEY" ``` ```bash cURL (crosschain) theme={null} curl -X GET "https://api.request.network/v2/secure-payments/01ABC123DEF456GHI789JKL/pay?wallet=0x1234...&chain=ARBITRUM&token=USDT" \ -H "x-api-key: YOUR_API_KEY" ``` ```json 200 Single payment calldata theme={null} { "transactions": [ { "to": "0x370DE27fdb7D1Ff1e1BaA7D11c5820a324Cf623C", "data": "0x...", "value": 0 } ], "metadata": { "stepsRequired": 1, "needsApproval": false, "paymentTransactionIndex": 0, "hasEnoughBalance": true, "hasEnoughGas": true } } ``` ```json 200 Crosschain calldata theme={null} { "transactions": [ { "to": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", "data": "0x095ea7b3...", "value": "0x0" }, { "to": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE", "data": "0xabcdef...", "value": "0x0" } ], "metadata": { "stepsRequired": 2, "needsApproval": true, "approvalTransactionIndex": 0, "paymentTransactionIndex": 1, "routeType": "crosschain", "quoteExpiresAt": 1742205771, "hasEnoughBalance": true, "sourceAmount": "10.02" } } ``` ```json 200 Batch payment calldata theme={null} { "ERC20ApprovalTransactions": [], "batchPaymentTransaction": { "to": "0x399F5EE127ce7432E4921a61b8CF52b0af52cbfE", "data": "0x...", "value": 0 }, "metadata": { "hasEnoughBalance": true, "hasEnoughGas": true } } ``` ### Error responses * `400`: invalid calldata request or unsupported crosschain configuration * `403`: token expired or not payable * `404`: token not found * `409`: secure payment already completed * `429`: rate limited *** ## POST /v2/secure-payments/:token/intent Record a crosschain payment intent after the payer broadcasts the source-chain LiFi transaction. This allows the system to track the bridge execution and trigger payment detection on the destination chain. ### Path parameters Secure payment token. ### Request fields The source-chain transaction hash (66 characters: `0x` + 64 hex chars). Provide **either** `txHash` **or** `safeTxHash` — exactly one. A Gnosis Safe transaction hash to track instead of an already-broadcast `txHash`. Used for [Safe multisig payments](/api-features/safe-multisig-payments). Provide exactly one of `txHash` or `safeTxHash`. Unix timestamp (seconds) — the hard on-chain execution deadline for the Safe route. Only valid alongside `safeTxHash`. The source chain. Values: `BASE`, `OPTIMISM`, `ARBITRUM`, `ETHEREUM`, `POLYGON`, `BNB`. The source token. Values: `USDC`, `USDT`. Optional address of the wallet making the payment. Echoed back on the response. ```bash cURL theme={null} curl -X POST "https://api.request.network/v2/secure-payments/01ABC123DEF456GHI789JKL/intent" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "txHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", "chain": "ARBITRUM", "token": "USDT" }' ``` ```json 200 OK theme={null} { "intentId": "01HXEXAMPLE123", "paymentReference": "0xb3581f0b0f74cc61", "txHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", "isListening": true } ``` ### Error responses * `400`: invalid or unsupported crosschain payload * `403`: token expired or not payable * `404`: token not found * `409`: secure payment already completed * `429`: rate limited *** ## POST /v2/secure-payments/multicall-payouts Combine multiple existing **outgoing payout** secure-payment links into a single multicall link the payer settles as one bundle. You pass the child secure-payment tokens; the API returns a parent token and hosted URL. This is a distinct mechanism from a [batch payment](#post-v2secure-payments) (multiple `requests[]` in one create call). Multicall composes already-created payout links and supports cross-chain and Tron execution. ### Request fields Ordered list of existing secure-payment tokens to combine. Minimum 2, maximum 1000 (the deployed default cap is 150 children). Duplicates are rejected. How the bundle executes: `evm_same_chain`, `evm_cross_chain`, or `tron_batch`. When omitted, the execution kind is derived at payment time from the payer's source selection (Tron-only children → `tron_batch`; a chosen source chain/token → `evm_cross_chain`; otherwise `evm_same_chain`). ```bash cURL theme={null} curl -X POST "https://api.request.network/v2/secure-payments/multicall-payouts" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "childTokens": [ "01JZ4PC7EXAMPLECHILD000001", "01JZ4PC7EXAMPLECHILD000002" ] }' ``` ```json 201 Created theme={null} { "type": "multicall", "token": "01JZ4PCMULTICALLPARENT0001", "securePaymentUrl": "https://pay.request.network/?token=01JZ4PCMULTICALLPARENT0001", "status": "pending", "expiresAt": "2026-06-22T10:00:00.000Z", "createdAt": "2026-06-15T10:00:00.000Z", "items": [ { "securePaymentToken": "01JZ4PC7EXAMPLECHILD000001", "requestId": "01e273...", "position": 0 }, { "securePaymentToken": "01JZ4PC7EXAMPLECHILD000002", "requestId": "02f384...", "position": 1 } ] } ``` The parent's `expiresAt` is the earliest expiry among its children. ### Execution kinds | Kind | When | Behavior | | ----------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | `evm_same_chain` | All children on one network and one currency | Settled as a single bundled batch transaction (plus any ERC-20 approvals) | | `evm_cross_chain` | Children span chains/currencies, or the payer pays from a different source | One cross-chain (Li.Fi) route is fetched per child and aggregated into one bundle (default cap 20 children) | | `tron_batch` | All children on Tron (same token) | Settled via the Tron batch payment contract | Supported payer source chains for cross-chain selection: `BASE`, `OPTIMISM`, `ARBITRUM`, `ETHEREUM`, `POLYGON`, `BNB`. Supported source tokens: `USDC`, `USDT`. ### Validation errors Eligibility failures return a structured envelope: ```json 400 Bad Request theme={null} { "message": "Multicall validation failed", "code": "multicall_validation_failed", "failures": [ { "securePaymentToken": "01JZ4PC7EXAMPLECHILD000099", "reason": "already_paid" } ] } ``` `failures[].reason` includes values such as `already_paid`, `secure_payment_expired`, `not_outgoing`, `compliance_failed`, and `not_payable_child`. Each `failures[]` entry also carries `messageKey`, `context`, and `recoveryAction` to help you present and resolve the failure. When a count limit is exceeded, the envelope instead carries a `cap` object together with a `failures` entry whose reason is `too_many_children` (child-count cap) or `cap_exceeded`: ```json 400 Bad Request (cap exceeded) theme={null} { "message": "Multicall validation failed", "code": "multicall_validation_failed", "failures": [ { "securePaymentToken": "*", "reason": "too_many_children" } ], "cap": { "type": "child_count", "maximum": 150, "actual": 180, "reduceBy": 30 } } ``` ### Paying a multicall link The payer settles the parent token through the standard payer routes: * `GET /v2/secure-payments/:token` returns the multicall details (children, totals, per-child eligibility). * `GET /v2/secure-payments/:token/pay` returns multicall calldata, including `executionKind`, the `children[]` (each with its own `paymentReference`), the top-level `references[]` array (one per child, in order), and — for cross-chain — a per-child `destinationCall`. *** ## GET /v2/secure-payments/multicall-payouts/:token Retrieve passive details for a multicall payout parent — children, totals, and per-child eligibility. This endpoint does **not** return quotes, calldata, or execution data; use `GET /v2/secure-payments/:token/pay` for calldata. Requires a SIWE wallet session (operator/Dashboard-facing). ### Path parameters Multicall parent token returned from `POST /v2/secure-payments/multicall-payouts`. ```json 200 OK theme={null} { "paymentType": "multicall", "token": "01JZ4PCMULTICALLPARENT0001", "status": "pending", "canExecute": true, "expiresAt": "2026-06-22T10:00:00.000Z", "createdAt": "2026-06-15T10:00:00.000Z", "children": [ { "securePaymentToken": "01JZ4PC7EXAMPLECHILD000001", "requestId": "01e273...", "position": 0, "executable": true, "blockReason": null, "amount": "50", "payee": "0xb07d2398d2004378cad234da0ef14f1c94a530e4" } ], "totals": { "childCount": 2, "blockedChildCount": 0 } } ``` The example is abbreviated. Each child also carries `messageKey`, `context`, `recoveryAction`, and `feePlan`, and `totals` also includes `byDestinationCurrency` and `amountSummary`. ### Error responses * `403`: not authorized for this multicall parent * `404`: token not found * `429`: rate limited # Create payee destination Source: https://docs.request.network/api-reference/v2payee-destination/create-payee-destination /api-reference/openapi.v2.json post /v2/payee-destination Create a payee destination with EIP-712 signature verification # Deactivate payee destination Source: https://docs.request.network/api-reference/v2payee-destination/deactivate-payee-destination /api-reference/openapi.v2.json delete /v2/payee-destination/{destinationId} Deactivate a payee destination with EIP-712 signature verification. Sets active=false without deleting the record. # Get active payee destination for wallet Source: https://docs.request.network/api-reference/v2payee-destination/get-active-payee-destination-for-wallet /api-reference/openapi.v2.json get /v2/payee-destination Retrieve the active payee destination for a specific wallet address. Returns null if no active destination exists. # Get EIP-712 signing data with nonce Source: https://docs.request.network/api-reference/v2payee-destination/get-eip-712-signing-data-with-nonce /api-reference/openapi.v2.json get /v2/payee-destination/signing-data Generate a nonce and return the complete EIP-712 signing data structure for payee destination creation or deactivation # Get payee destination by ID Source: https://docs.request.network/api-reference/v2payee-destination/get-payee-destination-by-id /api-reference/openapi.v2.json get /v2/payee-destination/{destinationId} Retrieve a payee destination using the format network:walletAddress:tokenAddress # Search payments with advanced filtering Source: https://docs.request.network/api-reference/v2payments/search-payments-with-advanced-filtering /api-reference/openapi.v2.json get /v2/payments Search for payments by transaction hash, wallet address, payment reference, request ID, or merchant reference. Supports filtering by payment type, currencies, date ranges, and pagination. Returns complete payment details including customer information and fees. When searching by transaction hash or wallet address, returns ALL payments from batch transactions. Most search parameters are optional, but at least one must be provided and they default to an AND relationship. # Broadcast signed TRON secure payment transaction Source: https://docs.request.network/api-reference/v2secure-payment/broadcast-signed-tron-secure-payment-transaction /api-reference/openapi.v2.json post /v2/secure-payments/{token}/tron/broadcast Relays a locally signed TRON transaction for this secure payment through the configured CatFee Seamless Energy node. The backend rebuilds the expected secure-payment TRON transaction and only forwards the signed transaction if the payload matches. # Create a batch secure payment for multiple outgoing payouts Source: https://docs.request.network/api-reference/v2secure-payment/create-a-batch-secure-payment-for-multiple-outgoing-payouts /api-reference/openapi.v2.json post /v2/secure-payments/batch-payouts Creates a single batch payout link from multiple existing pending single payout tokens. The individual tokens are invalidated and a new batch payment URL is returned. All payouts must target the same network. # Create a multicall payout link Source: https://docs.request.network/api-reference/v2secure-payment/create-a-multicall-payout-link /api-reference/openapi.v2.json post /v2/secure-payments/multicall-payouts Creates a multicall Secure Payment Page link from selected outgoing payout secure-payment tokens. This is distinct from legacy secure-payment batch links and persists only the parent token plus ordered child references. # Create a secure payment entry Source: https://docs.request.network/api-reference/v2secure-payment/create-a-secure-payment-entry /api-reference/openapi.v2.json post /v2/secure-payments Creates a secure payment entry with a token. Accepts an array of payment requests using destination IDs (composite ERC-7828 payee address + token address). The server resolves chain, wallet, and currency from each destination ID. Single item creates a single incoming payment. Multiple items preserve the legacy incoming-payment batch shape and are unrelated to multicall payout parents; create multicall payout parents only through /v2/secure-payments/multicall-payouts. All requests must resolve to the same network. Returns a secure payment URL. # Create a secure payment for an outgoing payout Source: https://docs.request.network/api-reference/v2secure-payment/create-a-secure-payment-for-an-outgoing-payout /api-reference/openapi.v2.json post /v2/secure-payments/payouts Creates a single-payment secure-payment link for an outgoing payout. The caller provides the raw recipient details (wallet, network, currency, amount); the API resolves or creates the wallet/network/currency destination tuple and links the payout to that tuple. Returns the same response shape as POST /v2/secure-payments/ so the resulting link loads on the secure payment page without a separate code path. # Find secure payment by request ID Source: https://docs.request.network/api-reference/v2secure-payment/find-secure-payment-by-request-id /api-reference/openapi.v2.json get /v2/secure-payments Looks up the secure payment associated with a given request ID. Returns the payment link URL, status, and metadata. Requires a SIWE session. # Get multicall payout state Source: https://docs.request.network/api-reference/v2secure-payment/get-multicall-payout-state /api-reference/openapi.v2.json get /v2/secure-payments/multicall-payouts/{token} Retrieves the passive multicall payout details payload for Secure Payment Page rendering. It does not return quotes, calldata, wallet routes, or execution data. # Get secure payment calldata by token Source: https://docs.request.network/api-reference/v2secure-payment/get-secure-payment-calldata-by-token /api-reference/openapi.v2.json get /v2/secure-payments/{token}/pay Retrieves executable payment calldata for a secure payment token. When chain and token are provided, the backend prepares a crosschain payment for the selected source asset; otherwise it returns the existing same-chain or batch payment calldata. # Get secure payment data by token Source: https://docs.request.network/api-reference/v2secure-payment/get-secure-payment-data-by-token /api-reference/openapi.v2.json get /v2/secure-payments/{token} Retrieves secure payment display data and resolved destination info. The token must be valid, not expired, and the payment must be in 'pending' status. # Preview the fee plan for a payment Source: https://docs.request.network/api-reference/v2secure-payment/preview-the-fee-plan-for-a-payment /api-reference/openapi.v2.json post /v2/secure-payments/fees/preview Calculation-only preview of the fee plan that would apply to a payment with the given amount, currency, and flow. Uses the same fee resolver as creation. Has no side effects: does not create a secure payment, payment request, or persist any snapshot. # Record secure payment intent Source: https://docs.request.network/api-reference/v2secure-payment/record-secure-payment-intent /api-reference/openapi.v2.json post /v2/secure-payments/{token}/intent Records source-side execution metadata after the payer broadcasts a transaction. Single crosschain payments create or reuse a LiFi tracking intent. Multicall parent tokens create or reuse an audit-only execution receipt keyed by parent token and UserOperation hash; receipt rows are not consulted for settlement state. # Refresh bridge step transactionRequest Source: https://docs.request.network/api-reference/v2secure-payment/refresh-bridge-step-transactionrequest /api-reference/openapi.v2.json post /v2/secure-payments/{token}/refresh-step-transaction Re-stamps the bridge deadline to 'now' by calling LI.FI /advanced/stepTransaction for a previously selected route step. Call this in parallel for all steps immediately before bundling and submitting the UserOp to avoid deadline expiry. # Wallet Authentication (SIWE) Source: https://docs.request.network/api-reference/wallet-authentication Sign-In with Ethereum and Tron wallet authentication for session-based API access ## Overview Wallet authentication uses the [Sign-In with Ethereum (SIWE)](https://eips.ethereum.org/EIPS/eip-4361) standard to authenticate users via their wallet signature. After verification, the API sets an httpOnly session cookie for subsequent requests. This is the authentication method used by the [Request Dashboard](https://dashboard.request.network) and is required for managing [payee destinations](/api-features/payee-destinations) and [client IDs](/api-features/client-id-management). ## Supported Wallets * **EVM wallets** — MetaMask, WalletConnect, Coinbase Wallet, and any wallet supporting `personal_sign` * **Tron wallets** — addresses starting with `T...` (see [Recommended Tron wallets](#recommended-tron-wallets)) The API auto-detects the wallet type from the address format. Both EVM and Tron wallets are first-class authentication methods for the Dashboard and the auth API. ### Recommended Tron wallets We rank these in order of recommendation, based on team testing: 1. **Trust** browser extension 2. **Trust** mobile app via WalletConnect (scan the QR) 3. **TronLink** browser extension 4. **Guarda** browser extension 5. **OKX** browser extension MetaMask is not recommended for Tron — its Tron support is partial. ## Challenge/Verify Flow Call `POST /v1/auth/wallet/challenge` with the wallet address. The API returns a SIWE-formatted message to sign. ```bash theme={null} curl -X POST "https://auth.request.network/v1/auth/wallet/challenge" \ -H "Content-Type: application/json" \ -d '{ "address": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7" }' ``` ```json Response (201) theme={null} { "challengeId": "01HXEXAMPLE123", "nonce": "a1b2c3d4e5f6", "message": "auth.request.network wants you to sign in with your Ethereum account:\n0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7\n\nSign in to Request Network.\n\nURI: https://auth.request.network\nVersion: 1\nNonce: a1b2c3d4e5f6\nIssued At: 2026-03-15T10:00:00.000Z\nExpiration Time: 2026-03-15T10:05:00.000Z", "expiresAt": "2026-03-15T10:05:00.000Z" } ``` The challenge expires after 5 minutes. Sign the `message` field using the wallet's signing capability. **EVM (viem):** ```typescript theme={null} const signature = await walletClient.signMessage({ account, message: challenge.message, }); ``` **Tron (TronLink):** ```typescript theme={null} const signature = await tronWeb.trx.signMessageV2(challenge.message); ``` Submit the signature back to complete authentication. The API sets a session cookie on success. ```bash theme={null} curl -X POST "https://auth.request.network/v1/auth/wallet/verify" \ -H "Content-Type: application/json" \ -c cookies.txt \ -d '{ "challengeId": "01HXEXAMPLE123", "nonce": "a1b2c3d4e5f6", "message": "auth.request.network wants you to sign in with your Ethereum account:...", "signature": "0x1234...abcdef" }' ``` On success, the response sets an httpOnly session cookie. Use this cookie for subsequent API calls. ## Session Management * **Session type:** httpOnly, secure, sameSite=lax cookie * **Wallet session timeout:** 15 minutes idle timeout * **Logout:** `POST /v1/auth/logout` clears the session cookie ## Email/Password Authentication For programmatic access without a wallet, the API also supports email/password authentication: * `POST /v1/auth/register` — Create an account with email and password (8-100 chars) * `POST /v1/auth/login` — Login with email and password * `POST /v1/auth/logout` — Clear the session Email/password sessions have a 30-day expiry. ## Related Pages API key and Client ID authentication for API integrations. Manage receiving routes (requires wallet session). # Webhooks Source: https://docs.request.network/api-reference/webhooks Complete webhook implementation guide with event types, security, and retry configuration ## Overview Webhooks deliver real-time notifications when payment and request events occur. Configure your endpoints to receive HMAC-signed POST requests with automatic retry logic and comprehensive event data. ## Webhook Configuration Webhooks are managed **programmatically** via the Auth API at `auth.request.network` — there is no Dashboard UI for webhook CRUD. Each webhook is scoped to the Client ID that creates it; events for any payment link or request created with that Client ID are delivered to that webhook. ### Create a webhook ```bash theme={null} curl -X POST "https://auth.request.network/v1/webhook" \ -H "Content-Type: application/json" \ -H "x-client-id: YOUR_CLIENT_ID" \ -d '{ "url": "https://yourapp.com/webhooks/request-network" }' ``` **Response (201 Created):** ```json theme={null} { "id": "01KJC2WX8EH4MP3DHZB2YQ7N9G", "secret": "f3c189a4b5e6d7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2" } ``` The `secret` is only returned once at creation. Store it securely — you cannot retrieve it again. Use HTTPS in production. `localhost` URLs are accepted for local testing. ### Manage webhooks All endpoints accept `x-client-id` and operate on the webhooks owned by that Client ID. | Method | Path | Purpose | | -------- | ------------------------ | ------------------------------------------------------------------ | | `GET` | `/v1/webhook` | List webhooks for this Client ID | | `PUT` | `/v1/webhook/:webhookId` | Toggle active / inactive | | `DELETE` | `/v1/webhook/:webhookId` | Permanently delete | | `POST` | `/v1/webhook/test` | Body `{ "eventType": "payment.confirmed" }` — fire a test delivery | Open the [Auth API Scalar docs](https://auth.request.network/open-api/#tag/webhook) to call these interactively with your wallet session — signing in to the [Dashboard](https://dashboard.request.network) sets the session cookie that's shared across all `*.request.network` services. ### Local Development Use [ngrok](https://ngrok.com/docs/traffic-policy/getting-started/agent-endpoints/cli) to receive webhooks locally, then pass the public URL to `POST /v1/webhook`: ```bash theme={null} ngrok http 3000 # Use the HTTPS URL (e.g., https://abc123.ngrok.io/webhook) as the webhook URL ``` ## Event Types See [Payload Examples](#payload-examples) below for detailed webhook structures. ### Payment Events (core) | Event | Description | Context | Primary Use | | ------------------- | ------------------------------------ | ---------------------------------------------- | ------------------------------------------------ | | `payment.confirmed` | Payment fully completed and settled | After blockchain confirmation | Complete fulfillment, release goods | | `payment.partial` | Partial payment received for request | Installments, partial orders | Update balance, allow additional payments | | `payment.failed` | Payment execution failed | Recurring payments, cross-chain transfers | Notify failure, retry logic, pause subscriptions | | `payment.refunded` | Payment has been refunded to payer | Cross-chain payment failures, refund scenarios | Update order status, notify customer | ### Payment Events (Client ID-scoped) Emitted **in addition to** the core events when the originating request was created with a Client ID. Payload includes extra `clientId` and `origin` fields. | Event | Description | | ----------------------------- | -------------------------------------------------- | | `payment.confirmed.client_id` | Same as `payment.confirmed`, scoped to a Client ID | | `payment.partial.client_id` | Same as `payment.partial`, scoped to a Client ID | ### Payment Events (Checkout / Secure Payment-scoped) Emitted **in addition to** the core events when the request was created via a Secure Payment / checkout flow. | Event | Description | | ---------------------------- | ------------------------------------------------------------------- | | `payment.confirmed.checkout` | Same as `payment.confirmed`, originating from a Secure Payment link | | `payment.partial.checkout` | Same as `payment.partial`, originating from a Secure Payment link | ### Processing Events | Event | Description | Context | Primary Use | | -------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | | `payment.processing` | Crypto-to-fiat payment in progress | **subStatus values:** initiated, pending\_internal\_assessment, ongoing\_checks, sending\_fiat, fiat\_sent, bounced | Track crypto-to-fiat payment status, update UI | ### Request Events | Event | Description | Context | Primary Use | | ------------------- | ------------------------------- | ----------------------------------------- | ------------------------------------------ | | `request.recurring` | New recurring request generated | Subscription renewals, scheduled payments | Send renewal notifications, update billing | ### Compliance Events | Event | Description | Context | Primary Use | | ------------------------ | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | | `compliance.updated` | KYC or agreement status changed | **kycStatus values:** not\_started, pending, approved, rejected, retry\_required
**agreementStatus values:** not\_started, pending, completed, rejected, failed | Update user permissions, notify status | | `payment_detail.updated` | Bank account verification status updated | States: approved, failed, pending | Enable fiat payments, update profiles | ### Secure Payment Page Events (payer funnel) | Event | Description | Context | Primary Use | | --------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | | `secure_payment.user_event` | Payer progressed through a step of the Secure Payment Page | **userEvent values:** wallet\_connected, payment\_sent\_to\_wallet, payment\_approved\_in\_wallet | Real-time payer-funnel visibility, drop-off analytics | Sent to the same registered webhook endpoints as every other event — same Client ID scoping, `x-request-network-signature` HMAC verification, delivery headers, timeout, and 1s/5s/15s retry semantics described elsewhere on this page. The `userEvent` field distinguishes the 3 funnel steps: | `userEvent` | Meaning | | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `wallet_connected` | The payer successfully connected a wallet on the secure payment page | | `payment_sent_to_wallet` | The payment transaction was handed to the payer's wallet for signature | | `payment_approved_in_wallet` | The payer approved/signed the payment in their wallet. `properties` includes the submission id (e.g. tx hash / user-operation hash) | `securePaymentToken` is the platform's correlation key, returned when the secure payment was created. `requestId` is present only when exactly one request is linked to the secure payment (see `requestIds` for the full list). `timestamp` is server-stamped on receipt. `occurredAt` and `properties` are **client-reported telemetry from the payer's browser** — useful for analytics, but not authoritative. `secure_payment.user_event` is best-effort browser telemetry. Navigation, network errors, or browser extensions can prevent the API from receiving it. Webhook retries begin only after the API accepts the event. Do not treat an absent event as evidence that the payer did not take the step; use `payment.confirmed` for settlement and reconciliation. When the Secure Payment Page includes wallet information in `properties`, it uses `wallet_address_hashed` rather than a raw wallet address. ## Security Implementation ### Signature Verification Every webhook includes an HMAC SHA-256 signature in the `x-request-network-signature` header: ```javascript theme={null} import crypto from "node:crypto"; function verifyWebhookSignature(rawBody, signature, secret) { const expectedSignature = crypto .createHmac("sha256", secret) .update(rawBody) .digest("hex"); try { return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expectedSignature) ); } catch { return false; } } // Usage in your webhook handler app.post("/webhook", (req, res) => { const signature = req.headers["x-request-network-signature"]; if (!verifyWebhookSignature(req.rawBody, signature, WEBHOOK_SECRET)) { return res.status(401).json({ error: "Invalid signature" }); } // Parse JSON after verification const body = JSON.parse(req.rawBody.toString("utf8")); // Process webhook... res.status(200).json({ success: true }); }); ``` ### Security Requirements * **HTTPS only:** Production webhooks require HTTPS endpoints * **Always verify signatures:** Never process unverified webhook requests * **Keep secrets secure:** Store signing secrets as environment variables * **Return 2xx for success:** Any 2xx status code confirms successful processing ## Request Headers Each webhook request includes these headers: | Header | Description | Example | | ------------------------------- | --------------------------- | ---------------------------- | | `x-request-network-signature` | HMAC SHA-256 signature | `a1b2c3d4e5f6...` | | `x-request-network-delivery` | Unique delivery ID (ULID) | `01ARZ3NDEKTSV4RRFFQ69G5FAV` | | `x-request-network-retry-count` | Current retry attempt (0-3) | `0` | | `x-request-network-test` | Present for test webhooks | `true` | | `content-type` | Always JSON | `application/json` | ## Retry Logic ### Automatic Retries * **Max attempts:** 3 retries (4 total attempts) * **Retry delays:** 1s, 5s, 15s * **Trigger conditions:** Non-2xx response codes, timeouts, connection errors * **Timeout:** 5 seconds per request ### Response Handling ```javascript theme={null} // ✅ Success - no retry res.status(200).json({ success: true }); res.status(201).json({ created: true }); // ❌ Error - triggers retry res.status(401).json({ error: "Unauthorized" }); res.status(404).json({ error: "Resource not found" }); res.status(500).json({ error: "Internal server error" }); ``` ### Error Logging Request API logs all webhook delivery failures with: * Endpoint URL * Attempt number * Error details * Final failure after all retries ## Payload Examples All payment events include an `explorer` field linking to [Request Scan](https://scan.request.network) for transaction details. **Common Fields:** * `requestId` / `requestID`: Unique identifier for the payment request * `paymentReference`: Short reference, also unique to a request, used to link payments to the request * `timestamp`: ISO 8601 formatted event timestamp * `paymentProcessor`: Either `request-network` (crypto) or `request-tech` (fiat) * `payerAddress`: Resolved payer wallet — the on-chain sender for plain direct payments, or the resolved payer for recurring and intent-based flows (Secure Payment Page, LiFi, Safe, ERC-4337, multicall). `null` when it cannot be determined. Included on `payment.confirmed` and `payment.partial` events (and their `.client_id` / `.checkout` variants). * `payerEoaAddress`: The payer's connected wallet address. It can differ from `payerAddress` when a smart account is used. `null` when unavailable. Included on `payment.confirmed` and `payment.partial` events (and their `.client_id` / `.checkout` variants). ### Payment Confirmed ```json theme={null} { "event": "payment.confirmed", "requestId": "0151b394e3c482c5aebaa04eb04508a8db70595470760293f1b258ed96d1fafa93", "requestID": "0151b394e3c482c5aebaa04eb04508a8db70595470760293f1b258ed96d1fafa93", "paymentReference": "0x2c3366941274c34c", "explorer": "https://scan.request.network/request/0151b394e3c482c5aebaa04eb04508a8db70595470760293f1b258ed96d1fafa93", "amount": "100.0", "totalAmountPaid": "100.0", "expectedAmount": "100.0", "timestamp": "2025-10-03T14:30:00Z", "txHash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", "payerAddress": "0x92Fc3406Fc6BB7A76aC63b2E8b9d02b1B9C3e4d5", "payerEoaAddress": "0x7A1F20C4D58E9B0A3C6D4E2F1B8A5C7D9E0F1234", "network": "ethereum", "currency": "USDC", "paymentCurrency": "USDC", "isCryptoToFiat": false, "subStatus": "", "paymentProcessor": "request-network", "fees": [ { "type": "network", "amount": "0.02", "currency": "ETH" } ] } ``` ### Payment Processing ```json theme={null} { "event": "payment.processing", "requestId": "0151b394e3c482c5aebaa04eb04508a8db70595470760293f1b258ed96d1fafa93", "requestID": "0151b394e3c482c5aebaa04eb04508a8db70595470760293f1b258ed96d1fafa93", "paymentReference": "0x2c3366941274c34c", "offrampId": "offramp_test123456789", "timestamp": "2025-10-03T14:35:00Z", "subStatus": "ongoing_checks", "paymentProcessor": "request-tech", "rawPayload": { "status": "ongoing_checks", "providerId": "provider_test123" } } ``` ### Payment Partial ```json theme={null} { "event": "payment.partial", "requestId": "0151b394e3c482c5aebaa04eb04508a8db70595470760293f1b258ed96d1fafa93", "requestID": "0151b394e3c482c5aebaa04eb04508a8db70595470760293f1b258ed96d1fafa93", "paymentReference": "0x2c3366941274c34c", "explorer": "https://scan.request.network/request/0151b394e3c482c5aebaa04eb04508a8db70595470760293f1b258ed96d1fafa93", "amount": "50.0", "totalAmountPaid": "50.0", "expectedAmount": "100.0", "timestamp": "2025-10-03T14:30:00Z", "txHash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", "payerAddress": "0x92Fc3406Fc6BB7A76aC63b2E8b9d02b1B9C3e4d5", "payerEoaAddress": "0x7A1F20C4D58E9B0A3C6D4E2F1B8A5C7D9E0F1234", "network": "ethereum", "currency": "USDC", "paymentCurrency": "USDC", "isCryptoToFiat": false, "subStatus": "", "paymentProcessor": "request-network", "fees": [] } ``` ### Payment Failed ```json theme={null} { "event": "payment.failed", "requestId": "0151b394e3c482c5aebaa04eb04508a8db70595470760293f1b258ed96d1fafa93", "requestID": "0151b394e3c482c5aebaa04eb04508a8db70595470760293f1b258ed96d1fafa93", "paymentReference": "0x2c3366941274c34c", "subStatus": "insufficient_funds", "paymentProcessor": "request-network" } ``` ### Compliance Updated ```json theme={null} { "event": "compliance.updated", "clientUserId": "user_test123456789", "kycStatus": "approved", "agreementStatus": "completed", "isCompliant": true, "timestamp": "2025-10-03T14:30:00Z", "rawPayload": { "verificationLevel": "full", "documents": "verified" } } ``` ### Secure Payment User Event ```json theme={null} { "event": "secure_payment.user_event", "userEvent": "payment_approved_in_wallet", "securePaymentToken": "spt_3fk29ax7...", "requestId": "01JD3E6JD46KY4KKV7X9V0MZ7W", "requestIds": ["01JD3E6JD46KY4KKV7X9V0MZ7W"], "orchestratorId": "orch_12345", "occurredAt": "2026-08-05T14:03:21.512Z", "timestamp": "2026-08-05T14:03:22.104Z", "properties": { "wallet_provider": "metamask", "payment_submission_id": "0x6a4f...e21b", "payment_submission_id_type": "evm_tx_hash", "selected_source_chain": "base", "payment_type": "single" } } ``` ## Implementation Examples For a complete working example, see [Webhook reconciliation](/use-cases/webhook-reconciliation) which implements webhook handling for payment notifications. ```javascript theme={null} import express from "express"; import crypto from "node:crypto"; const app = express(); const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET; // Use raw body parser to capture exact request bytes for signature verification app.use( express.raw({ type: "application/json", verify: (req, _res, buf) => { req.rawBody = buf; }, }) ); app.post("/webhook/payment", async (req, res) => { try { // Verify signature against raw body const signature = req.headers["x-request-network-signature"]; const rawBody = req.rawBody; const expectedSignature = crypto .createHmac("sha256", WEBHOOK_SECRET) .update(rawBody) .digest("hex"); if (!signature || !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) { return res.status(401).json({ error: "Invalid signature" }); } // Parse JSON only after verifying signature const body = JSON.parse(rawBody.toString("utf8")); const isTest = req.headers["x-request-network-test"] === "true"; if (isTest) { console.log("Received test webhook"); } // Process webhook based on event type const { event, requestId } = body; switch (event) { case "payment.confirmed": await handlePaymentConfirmed(body); break; case "payment.processing": await handlePaymentProcessing(body); break; case "compliance.updated": await handleComplianceUpdate(body); break; default: console.log(`Unhandled event: ${event}`); } return res.status(200).json({ success: true }); } catch (error) { console.error("Webhook processing error:", error); return res.status(500).json({ error: "Processing failed" }); } }); ``` ```javascript theme={null} // app/api/webhook/route.ts import crypto from "node:crypto"; import { NextResponse } from "next/server"; export async function POST(request: Request) { try { // Read raw body for signature verification const rawBody = await request.text(); const signature = request.headers.get("x-request-network-signature"); const expectedSignature = crypto .createHmac("sha256", process.env.WEBHOOK_SECRET!) .update(rawBody) .digest("hex"); if (!signature || !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) { return NextResponse.json({ error: "Invalid signature" }, { status: 401 }); } // Parse JSON after verifying signature const body = JSON.parse(rawBody); // Process webhook const { event, requestId } = body; // Your business logic here await processWebhookEvent(event, body); return NextResponse.json({ success: true }, { status: 200 }); } catch (error) { console.error("Webhook error:", error); return NextResponse.json( { error: "Internal server error" }, { status: 500 } ); } } ``` ## Testing ### Test deliveries Fire a test webhook from the Auth API: ```bash theme={null} curl -X POST "https://auth.request.network/v1/webhook/test" \ -H "Content-Type: application/json" \ -H "x-client-id: YOUR_CLIENT_ID" \ -d '{ "eventType": "payment.confirmed" }' ``` Or call it interactively from the [Auth API Scalar docs](https://auth.request.network/open-api/#tag/webhook/POST/v1/webhook/test). Test deliveries arrive at all active webhooks for that Client ID and include the `x-request-network-test: true` header so handlers can branch on test vs real. ### Test Webhook Identification Test webhooks include the `x-request-network-test: true` header: ```javascript theme={null} app.post("/webhook", (req, res) => { const isTest = req.headers["x-request-network-test"] === "true"; if (isTest) { console.log("Received test webhook"); // Handle test scenario } // Process normally... }); ``` ## Best Practices ### Error Handling * **Implement idempotency:** Use delivery IDs to prevent duplicate processing * **Graceful degradation:** Handle unknown event types without errors ### Performance * **Timeout management:** Complete processing within 5 seconds ## Troubleshooting ### Common Issues **Signature verification fails:** * Check your signing secret matches the value returned by `POST /v1/webhook` at creation * Ensure you're using the raw request body for signature calculation * Verify HMAC SHA-256 implementation **Webhooks not received:** * Confirm endpoint URL is accessible via HTTPS * Verify endpoint returns 2xx status codes * Confirm the webhook is `active` via `GET /v1/webhook` (toggle with `PUT /v1/webhook/:id`) ### Debugging Tips * Use ngrok request inspector to see raw webhook data * Monitor retry counts in headers to identify issues * Fire test deliveries via `POST /v1/webhook/test` ## Related Documentation High-level webhook concepts and workflow Complete webhook implementation example API credential setup and webhook security Manage Client IDs and payment destinations (webhooks are managed via the Auth API above) # Getting Started Source: https://docs.request.network/api-setup/getting-started Quick setup guide to get your Client ID and start building with Request Network ## Welcome to Request Network Get started with Request Network in just a few minutes. This guide will walk you through setting up your account, obtaining your Client ID, and making your first payment. ## Quick Setup Sign up for a free Request Network account at [dashboard.request.network](https://dashboard.request.network) Generate your Client ID from the Dashboard Use the API to create a payment request Get payment calldata and execute the transaction ## Account Setup ### Request Dashboard Registration Sign up at [dashboard.request.network](https://dashboard.request.network) to get started. All accounts include: * Free API access with generous limits * API documentation and tools * Community support ### Client ID Generation 1. Log in to [Request Dashboard](https://dashboard.request.network) 2. **Create a payment destination first** if you haven't — the Client IDs section is only available once a destination exists. From the Home page, click **Set up payment destination** and pick the chain + token you want to receive on. 3. Open **Manage Destination → Client IDs** 4. Click **Generate your first Client ID** (or **Generate New Client ID** if you already have one) 5. Copy and securely store the Client ID **Security Best Practices** * Store your Client ID and webhook secret in environment variables * Never commit credentials to version control * Rotate credentials regularly ## Your First Integration Let's create a simple Node.js server that integrates with the Request Network API to create payments and track their status. ### Project Setup Create a new project and install dependencies: ```bash theme={null} mkdir request-api-demo cd request-api-demo npm init -y npm install dotenv ``` Create a `.env` file: ```bash theme={null} RN_CLIENT_ID=your_client_id_here RN_API_URL=https://api.request.network/v2 ``` `RN_CLIENT_ID` is used server-side only — it's sent as the `x-client-id` header on API requests and should never be exposed in client-side code. ### Create a Payment Create an `index.js` file: ```javascript theme={null} require('dotenv').config(); async function createPayment() { try { const response = await fetch(`${process.env.RN_API_URL}/payouts`, { method: 'POST', headers: { 'x-client-id': process.env.RN_CLIENT_ID, 'Content-Type': 'application/json' }, body: JSON.stringify({ payee: '0x...', // Your wallet address amount: '0.1', invoiceCurrency: 'ETH-sepolia-sepolia', paymentCurrency: 'ETH-sepolia-sepolia' }) }); if (!response.ok) { const errorText = await response.text(); console.error('API Error:', errorText); return; } const data = await response.json(); console.log('Payment created:', { requestId: data.requestId, paymentReference: data.paymentReference, transactions: data.transactions, metadata: data.metadata }); return data; } catch (error) { console.error('Error:', error.message); } } createPayment(); ``` Run it: ```bash theme={null} node index.js ``` The response will include: * `requestId` — Unique identifier for the request * `paymentReference` — Used to track the payment * `transactions` — Array of transaction calldata to execute * `metadata` — Additional info like `stepsRequired` and `needsApproval` ### Understanding the Response ```json theme={null} { "requestId": "011d9f76e07a678b8321ccfaa300efd4d80832652b8bbc07ea4069ca71006210b5", "paymentReference": "0xe23a6b02059c2b30", "transactions": [ { "data": "0xb868980b000000000000000000000000...", "to": "0xe11BF2fDA23bF0A98365e1A4c04A87C9339e8687", "value": { "type": "BigNumber", "hex": "0x02c68af0bb140000" } } ], "metadata": { "stepsRequired": 1, "needsApproval": false, "paymentTransactionIndex": 0 } } ``` **Note:** The `amount` is in human-readable format. No BigNumber conversions needed! ### Setting Up Webhooks To track payment status in real-time, set up a webhook endpoint. Always verify against the **raw** request body, before it's parsed as JSON — re-serializing the parsed body will produce a different signature and fail verification. ```javascript theme={null} const crypto = require('crypto'); const express = require('express'); const app = express(); // Capture the raw body alongside JSON parsing so we can verify against // the exact bytes Request Network signed. app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } })); // Webhook handler app.post('/webhooks', async (req, res) => { const signature = req.headers['x-request-network-signature']; const webhookSecret = process.env.RN_WEBHOOK_SECRET; // Verify signature against the RAW body (not JSON.stringify(req.body)) const expectedSignature = crypto .createHmac('sha256', webhookSecret) .update(req.rawBody) .digest('hex'); const expected = Buffer.from(expectedSignature, 'hex'); const received = Buffer.from(signature || '', 'hex'); const isValid = expected.length === received.length && crypto.timingSafeEqual(expected, received); if (!isValid) { return res.status(401).send({ error: 'Invalid signature' }); } const { requestId, event } = req.body; console.log(`Webhook: ${event} for request ${requestId}`); // Handle different events switch (event) { case 'payment.confirmed': console.log('Payment confirmed!'); // Update your database, send email, etc. break; case 'payment.partial': console.log('Partial payment received...'); break; case 'payment.processing': console.log('Payment processing...'); break; } res.send({ code: 200, message: 'Webhook received' }); }); ``` #### Testing Webhooks Locally Since webhooks can't reach your local server directly, use [ngrok](https://ngrok.com/): ```bash theme={null} ngrok http 3000 ``` Copy the HTTPS URL (e.g., `https://abc123.ngrok.io/webhooks`) and register it via the Auth API: ```bash theme={null} curl -X POST "https://auth.request.network/v1/webhook" \ -H "Content-Type: application/json" \ -H "x-client-id: $RN_CLIENT_ID" \ -d '{ "url": "https://abc123.ngrok.io/webhooks" }' ``` The response includes a one-time `secret` — copy it to your `.env`: ```bash theme={null} RN_WEBHOOK_SECRET=your_webhook_secret_here ``` ## Environment Configuration Set up environment variables for secure credential management: ```bash .env theme={null} # Request Network Configuration RN_CLIENT_ID=your_client_id_here RN_API_URL=https://api.request.network/v2 # Webhook Configuration (optional) RN_WEBHOOK_SECRET=your_webhook_secret_here ``` ```javascript config.js theme={null} module.exports = { requestNetwork: { clientId: process.env.RN_CLIENT_ID, apiUrl: process.env.RN_API_URL || 'https://api.request.network/v2', webhookSecret: process.env.RN_WEBHOOK_SECRET, } }; ``` ## What's Next? Now that you've made your first API call, explore more features: Learn about different payment types and features Understand how payments are tracked Complete tutorial with backend + frontend ## Troubleshooting ### Common Issues * Verify Client ID is correct * Check that you're using the right header: `x-client-id` * Ensure the Client ID hasn't been revoked * Check required fields: `payee`, `amount`, `invoiceCurrency`, `paymentCurrency` * Ensure `amount` is a string (e.g., "0.1") * Verify currency IDs are valid * Verify webhook URL is publicly accessible * Check webhook signature verification * Ensure the webhook is `active` (toggle via `PUT /v1/webhook/:id` on the Auth API) **You're all set!** You've created your first payment request with Request Network. For a complete working example with frontend, check out the [Integration Tutorial](/api-setup/integration-tutorial). # Integration Tutorial Source: https://docs.request.network/api-setup/integration-tutorial Complete step-by-step guide to integrate Request Network API with a Node.js backend and React frontend We will be creating a simple node server integrating the Request Network API to create payments and track their status. We are going to use `fastify` as our server and use `drizzle` with `SQLite` to store our payment data. Additionally, we'll be creating a simple React web application to interact with the API and execute payments. View the entire codebase on [Code Sandbox](https://codesandbox.io/p/github/RequestNetwork/integration-demo/main?workspaceId=ws_ADdDA8Vov3FqF921ntkFf). ## Backend In this section we'll create your API that integrates Request Network's API to create and track payments. After we are done with it, we'll jump over and create a web app connecting to your API and put everything together! ### Setup As mentioned, we are using `fastify` and `drizzle` for this demo, you can of course choose whatever suits you best. Create a new project. In it create a folder called `rn-test-backend` inside and copy over this `package.json` file to `rn-test-backend`. ```json theme={null} { "name": "request-api-demo", "version": "1.0.0", "description": "Request API demo", "main": "dist/index.js", "scripts": { "build": "tsc", "start": "node dist/index.js", "dev": "ts-node src/index.ts", "dev:watch": "ts-node-dev --respawn --transpile-only src/index.ts", "db:push": "drizzle-kit push", "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", "db:studio": "drizzle-kit studio" }, "keywords": [ "fastify", "typescript", "node" ], "author": "", "license": "MIT", "devDependencies": { "@types/node": "^20.0.0", "ts-node": "^10.9.0", "ts-node-dev": "^2.0.0", "typescript": "^5.0.0" }, "dependencies": { "@fastify/cors": "^11.1.0", "@types/better-sqlite3": "^7.6.13", "better-sqlite3": "^12.2.0", "dotenv": "^17.2.1", "drizzle-kit": "^0.31.4", "drizzle-orm": "^0.44.5", "fastify": "^5.5.0" } } ``` The folder structure for the demo is going to be simple: Folder structure ```typescript theme={null} // src/db/index.ts import Database from 'better-sqlite3'; import { drizzle } from 'drizzle-orm/better-sqlite3'; import * as schema from './schema'; const sqlite = new Database('database.sqlite'); export const db = drizzle(sqlite, { schema }); ``` ```typescript theme={null} // src/db/schema.ts import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'; export const payments = sqliteTable('payments', { id: integer('id').primaryKey({ autoIncrement: true }), requestId: text('request_id').notNull(), status: text('status').notNull(), }); export type Payment = typeof payments.$inferSelect; ``` ```typescript theme={null} // src/index.ts import 'dotenv/config'; import Fastify, { FastifyRequest, FastifyReply } from 'fastify'; const fastify = Fastify({ logger: true }); fastify.get('/', async (request: FastifyRequest, reply: FastifyReply) => { return { message: 'Hello World!' }; }); const start = async () => { try { const port = 3000; const host = 'localhost'; await fastify.register(require('@fastify/cors'), { origin: true, // change to your frontend URL in production methods: ['GET', 'POST', 'PATCH'], }); await fastify.listen({ port, host }); console.log(`Server listening on http://${host}:${port}`); } catch (err) { fastify.log.error(err); process.exit(1); } }; start(); ``` ```typescript theme={null} // drizzle-config.ts import { defineConfig } from 'drizzle-kit'; export default defineConfig({ schema: './src/db/schema.ts', out: './drizzle', dialect: 'sqlite', dbCredentials: { url: './database.sqlite', }, }); ``` ```json theme={null} // tsconfig.json { "compilerOptions": { "target": "ES2020", "module": "commonjs", "lib": ["ES2020"], "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "declaration": true, "declarationMap": true, "sourceMap": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } ``` Then run `npm install` and when that's done, run `npm run db:push`. ### Get your API credentials Sign in to the [Request Dashboard](https://dashboard.request.network) with your wallet and create a Client ID. The Client ID is your API credential — copy it into your `.env` file. ``` // .env RN_CLIENT_ID= RN_API_URL=https://api.request.network/v2 ``` ### Create your first payment Let's create two new endpoints, one for creating a payment on our API and the other to fetch all of the payments users have made on our API. ```typescript theme={null} // src/index.ts import 'dotenv/config'; import Fastify, { FastifyRequest, FastifyReply } from 'fastify'; import { db } from './db'; import { payments } from './db/schema'; import { eq } from 'drizzle-orm'; const fastify = Fastify({ logger: true }); fastify.get('/', async (request: FastifyRequest, reply: FastifyReply) => { return { message: 'Hello World!' }; }); interface PaymentBody { payee: string; amount: string; invoiceCurrency: string; paymentCurrency: string; } fastify.post('/payments', async (request: FastifyRequest<{ Body: PaymentBody }>, reply: FastifyReply) => { try { const { payee, amount, invoiceCurrency, paymentCurrency } = request.body; if (!payee || !amount || !invoiceCurrency || !paymentCurrency) { return reply.status(400).send({ error: 'Missing required fields: payee, amount, invoiceCurrency, paymentCurrency' }); } const response = await fetch(`${process.env.RN_API_URL}/payouts`, { method: 'POST', headers: { 'x-client-id': process.env.RN_CLIENT_ID, 'Content-Type': 'application/json' }, body: JSON.stringify({ payee, amount, invoiceCurrency, paymentCurrency }) }); if (!response.ok) { const errorText = await response.text(); fastify.log.error(`Request Network API error: ${response.status} - ${errorText}`); return reply.status(response.status).send({ error: 'Failed to create payment with Request Network API', details: errorText }); } const rnApiResponse: any = await response.json(); console.log('Request Network API response:', JSON.stringify(rnApiResponse, null, 2)); const [savedPayment] = await db.insert(payments).values({ requestId: rnApiResponse.requestId, status: 'pending' }).returning(); console.log('Payment saved to database:', savedPayment); return { payment: savedPayment, calldata: { transactions: rnApiResponse.transactions, metadata: rnApiResponse.metadata } }; } catch (error) { console.error('Error creating payment:', error); return reply.status(500).send({ error: 'Internal server error', details: error instanceof Error ? error.message : 'Unknown error' }); } }); interface UpdatePaymentStatusBody { status: string; } // we will use this endpoint later on, don't think too much about it right now! fastify.patch('/payments/:id', async (request: FastifyRequest<{ Params: { id: string }; Body: UpdatePaymentStatusBody }>, reply: FastifyReply) => { try { const { id } = request.params; const { status } = request.body; if (!status) { return reply.status(400).send({ error: 'Status is required' }); } const updatedPayment = await db.update(payments) .set({ status }) .where(eq(payments.id, parseInt(id))) .returning(); if (!updatedPayment.length) { return reply.status(404).send({ error: 'Payment not found' }); } console.log('Payment status updated:', updatedPayment[0]); return { payment: updatedPayment[0] }; } catch (error) { console.error('Error updating payment status:', error); return reply.status(500).send({ error: 'Internal server error', details: error instanceof Error ? error.message : 'Unknown error' }); } }); fastify.get('/payments', async (request: FastifyRequest, reply: FastifyReply) => { try { const allPayments = await db.select().from(payments); return { payments: allPayments }; } catch (error) { console.error('Error fetching payments:', error); return reply.status(500).send({ error: 'Failed to fetch payments', details: error instanceof Error ? error.message : 'Unknown error' }); } }); const start = async () => { try { const port = 3000; const host = 'localhost'; await fastify.register(require('@fastify/cors'), { origin: true, // change to your frontend URL in production methods: ['GET', 'POST', 'PATCH'], }); await fastify.listen({ port, host }); console.log(`Server listening on http://${host}:${port}`); } catch (err) { fastify.log.error(err); process.exit(1); } }; start(); ``` Note: the `amount` our API receives is human readable, so just send over the amount in `invoiceCurrency` you wish, no BigNumbers needed! ### Let's try it out! Call our `/payments` endpoint with the right data to create a payout and let's see what we get back. ```bash theme={null} curl -X POST http://localhost:3000/payments \ -H "Content-Type: application/json" \ -d '{ "payee": "", "amount": "0.2", "invoiceCurrency": "ETH-sepolia-sepolia", "paymentCurrency": "ETH-sepolia-sepolia" }' ``` The response should look something like the following object ([full API reference](https://api.request.network/open-api/#tag/v2payouts/POST/v2/payouts)): ```json theme={null} { "requestId": "011d9f76e07a678b8321ccfaa300efd4d80832652b8bbc07ea4069ca71006210b5", "paymentReference": "0xe23a6b02059c2b30", "transactions": [ { "data": "0xb868980b00000000000000000000000029eab540117632a112ea29ba8be686a1b66467a700000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dead0000000000000000000000000000000000000000000000000000000000000008e23a6b02059c2b30000000000000000000000000000000000000000000000000", "to": "0xe11BF2fDA23bF0A98365e1A4c04A87C9339e8687", "value": { "type": "BigNumber", "hex": "0x02c68af0bb140000" } } ], "metadata": { "stepsRequired": 1, "needsApproval": false, "paymentTransactionIndex": 0 } } ``` Now you can check your database with `npm run db:studio` and assert that the payment is there. Database Studio ### Setting up webhooks In order for your app to make use of our payment tracking easily and in real-time, we provide webhook support. You just provide the endpoint and the Request Network API does the rest. Let's create a new route for handling webhook calls. ```typescript theme={null} // Add this to src/index.ts import crypto from "node:crypto"; fastify.post('/webhooks', async (request: FastifyRequest, reply: FastifyReply) => { let webhookData: Record = {}; try { const body = request.body as Record; webhookData = body; const signature = request.headers['x-request-network-signature'] as string; const webhookSecret = process.env.RN_WEBHOOK_SECRET; if (!webhookSecret) { fastify.log.error('RN_WEBHOOK_SECRET is not set'); return reply.status(500).send({ error: 'Webhook secret not configured' }); } const expectedSignature = crypto.createHmac('sha256', webhookSecret) .update(JSON.stringify(body)) .digest('hex'); if (signature !== expectedSignature) { fastify.log.error('Invalid webhook signature'); return reply.status(401).send({ error: 'Invalid signature' }); } const { requestId, event } = body; console.log(`Webhook received: ${event} for request ${requestId}`, { webhookData: body }); // Log the event console.log(`Webhook event: ${event}`); console.log('Full webhook data:', JSON.stringify(body, null, 2)); return reply.send({ code: 200, message: 'Webhook received' }); } catch (error) { console.error('Webhook error:', { error, requestId: webhookData?.requestId, event: webhookData?.event, }); return reply.status(500).send({ error: 'Internal server error' }); } }); ``` We'll go into more detail on how to get the `RN_WEBHOOK_SECRET` in the next subsection. #### Testing webhooks locally As you may know, it's impossible for our webhooks to call your locally running server. In order to test them, use a tool like [ngrok](https://ngrok.com/). Install it and run `ngrok http 3000` in your terminal. In a few moments, you should see something similar to the screenshot below and copy the URL. ngrok terminal Next up, register the webhook via the Auth API. The URL is the ngrok URL from above with `/webhooks` appended (e.g. `https://34c701d1d7f9.ngrok-free.app/webhooks`): ```bash theme={null} curl -X POST "https://auth.request.network/v1/webhook" \ -H "Content-Type: application/json" \ -H "x-client-id: $RN_CLIENT_ID" \ -d '{ "url": "https://34c701d1d7f9.ngrok-free.app/webhooks" }' ``` The response includes a one-time `secret` value — copy it and add to your `.env` file, then restart the app. ``` // .env RN_CLIENT_ID= RN_API_URL=https://api.request.network/v2 RN_WEBHOOK_SECRET= ``` If you want to test it out, fire a test delivery and observe your server's logs: ```bash theme={null} curl -X POST "https://auth.request.network/v1/webhook/test" \ -H "Content-Type: application/json" \ -H "x-client-id: $RN_CLIENT_ID" \ -d '{ "eventType": "payment.confirmed" }' ``` Your output should look something like the following: ``` Webhook received: payment.confirmed for request req_test123456789abcdef { webhookData: { event: 'payment.confirmed', requestId: 'req_test123456789abcdef', requestID: 'req_test123456789abcdef', paymentReference: '0x1234567890abcdef1234567890abcdef12345678', explorer: 'https://scan.request.network/request/req_test123456789abcdef', amount: '100.0', totalAmountPaid: '100.0', expectedAmount: '100.0', timestamp: '2025-08-28T12:25:45.995Z', txHash: '0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890', network: 'ethereum', currency: 'USDC', paymentCurrency: 'USDC', isCryptoToFiat: false, subStatus: '', paymentProcessor: 'request-network', fees: [ [Object] ] } } Webhook event: payment.confirmed Full webhook data: { "event": "payment.confirmed", "requestId": "req_test123456789abcdef", "requestID": "req_test123456789abcdef", "paymentReference": "0x1234567890abcdef1234567890abcdef12345678", "explorer": "https://scan.request.network/request/req_test123456789abcdef", "amount": "100.0", "totalAmountPaid": "100.0", "expectedAmount": "100.0", "timestamp": "2025-08-28T12:25:45.995Z", "txHash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", "network": "ethereum", "currency": "USDC", "paymentCurrency": "USDC", "isCryptoToFiat": false, "subStatus": "", "paymentProcessor": "request-network", "fees": [ { "type": "network", "amount": "0.02", "currency": "ETH" } ] } ``` #### Testing webhooks live Once your application is deployed, register a new webhook via `POST /v1/webhook` using your deployment's webhook route — same `curl` as above with the production URL. Then copy the returned secret to your deployment's environment variables and you can test your handlers just as we did above. #### Responding to payment confirmation events To make use of payment tracking, we need to map different event types to handlers. For demo purposes, let's create a new handler that will update the status of a payment in your API to `confirmed` when it's been confirmed by Request Network. ```typescript theme={null} // Update our handler in src/index.ts fastify.post('/webhooks', async (request: FastifyRequest, reply: FastifyReply) => { let webhookData: Record = {}; try { const body = request.body as Record; webhookData = body; const signature = request.headers['x-request-network-signature'] as string; const webhookSecret = process.env.RN_WEBHOOK_SECRET; if (!webhookSecret) { fastify.log.error('RN_WEBHOOK_SECRET is not set'); return reply.status(500).send({ error: 'Webhook secret not configured' }); } const expectedSignature = crypto.createHmac('sha256', webhookSecret) .update(JSON.stringify(body)) .digest('hex'); if (signature !== expectedSignature) { fastify.log.error('Invalid webhook signature'); return reply.status(401).send({ error: 'Invalid signature' }); } const { requestId, event } = body; console.log(`Webhook received: ${event} for request ${requestId}`, { webhookData: body }); // Log the event console.log(`Webhook event: ${event}`); console.log('Full webhook data:', JSON.stringify(body, null, 2)); switch (event) { // handling the payment.confirmed event case "payment.confirmed": await db.update(payments) .set({ status: 'confirmed' }) .where(eq(payments.requestId, requestId as string)); break; } return reply.send({ code: 200, message: 'Webhook received' }); } catch (error) { console.error('Webhook error:', { error, requestId: webhookData?.requestId, event: webhookData?.event, }); return reply.status(500).send({ error: 'Internal server error' }); } }); ``` This is it for the API, now to properly test this, we're going to build a simple frontend app that will interact with the newly created API! ## Frontend We cannot test out the entire flow without a user actually paying a request. For testing purposes, I will use a [Metamask](https://metamask.io/) wallet. In order for you to properly test this, I advise using a wallet and giving yourself some test Sepolia ETH from a faucet like [Google](https://cloud.google.com/application/web3/faucet/ethereum/sepolia). If you really want to check out what happens to your funds, create two accounts in your wallet. We'll be using Request Network to move funds from one to another. ### Setup We'll be using [Vite](https://vite.dev/) to create a simple React app. Move to the root directory in the created project and run `npm create vite@latest rn-test-frontend -- --template react-ts` in the terminal. Then move to the created directory `rn-test-frontend`, run `npm install`. *NOTE*: We are not going to be using any advanced patterns or libraries here, we'll try to keep it as simple as possible and let you build in your own way. Vite setup Next up, let's scaffold our app. Create a folder called `components`, and then create two files `CreatePayment.tsx` and `ViewPayments.tsx`. ```tsx theme={null} // src/components/create-payment/index.tsx import React from 'react'; const CreatePayment: React.FC = () => { return (

Create Payment

This will be a form to create new payments

); }; export default CreatePayment; ``` ```tsx theme={null} // src/components/view-payments/index.tsx import React from 'react'; const ViewPayments: React.FC = () => { return (

View Payments

This will show all payments from the database

); }; export default ViewPayments; ``` Next up, let's modify our `App.tsx` file to display two tabs. ```tsx theme={null} // src/App.tsx import { useState } from 'react' import './App.css' import ViewPayments from './components/ViewPayments' import CreatePayment from './components/CreatePayment' type TabType = 'view' | 'create'; function App() { const [activeTab, setActiveTab] = useState('view'); return (

Request Network Demo

{activeTab === 'view' && } {activeTab === 'create' && }
) } export default App ``` ```css theme={null} // src/App.css #root { max-width: 1280px; margin: 0 auto; padding: 2rem; text-align: center; } .app { max-width: 800px; margin: 0 auto; padding: 20px; } .tabs { display: flex; gap: 10px; margin-bottom: 30px; } .tab-button { border-radius: 0px; padding: 12px 24px; border: none; background: none; cursor: pointer; font-size: 16px; border-bottom: 3px solid transparent; transition: all 0.2s; } .tab-button:hover { background-color: #f5f5f5; } .tab-button.active { border-bottom-color: #11c9a0; color: #11c9a0; font-weight: 600; } .tab-content { min-height: 400px; } h1 { text-align: center; margin-bottom: 40px; } ``` The final result should look something like this: Initial UI ### Connecting the user's wallet We'll be using [wagmi](https://wagmi.sh/) to enable wallet connection. To do that we need to do a few things: 1. Install `wagmi` and its dependencies `npm install wagmi viem @tanstack/react-query --save` 2. Create a wagmi config at `src/config/wagmi.ts` ```typescript theme={null} // src/config/wagmi.ts import { createConfig, http } from 'wagmi' import { sepolia } from 'wagmi/chains' import { injected } from 'wagmi/connectors' export const config = createConfig({ chains: [sepolia], connectors: [ injected(), ], transports: { [sepolia.id]: http(), }, }) ``` 3. Update `main.tsx` to include the new providers ```tsx theme={null} // src/main.tsx import React from 'react' import ReactDOM from 'react-dom/client' import { WagmiProvider } from 'wagmi' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { config } from './config/wagmi' import App from './App.tsx' import './index.css' const queryClient = new QueryClient() ReactDOM.createRoot(document.getElementById('root')!).render( , ) ``` 4. Create a new component at `src/components/wallet-connect/index.tsx` ```tsx theme={null} // src/components/wallet-connect/index.tsx import { useAccount, useConnect, useDisconnect } from 'wagmi' import './styles.css'; const WalletConnect: React.FC = () => { const { address, isConnected } = useAccount() const { connect, connectors } = useConnect() const { disconnect } = useDisconnect() if (isConnected) { return (
Connected: {address?.slice(0, 6)}...{address?.slice(-4)}
) } return (
) } export default WalletConnect ``` ```css theme={null} // src/components/wallet-connect/styles.css .connect-btn { background: #646cff; color: white; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; } .connect-btn:hover { background: #5145d4; } .disconnect-btn { background: #dc2626; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; } .disconnect-btn:hover { background: #b91c1c; } ``` 5. Render this component from our `App` component ```tsx theme={null} // src/App.tsx import { useState } from 'react' import './App.css' import ViewPayments from './components/view-payments' import CreatePayment from './components/create-payment' import WalletConnect from './components/wallet-connect'; type TabType = 'view' | 'create'; function App() { const [activeTab, setActiveTab] = useState('view'); return (

Request Network Demo

{activeTab === 'view' && } {activeTab === 'create' && }
) } export default App ``` ```css theme={null} // src/App.css, add this class in .header { display: flex; justify-content: space-between; gap: 32px; align-items: center; margin-bottom: 40px; } ``` The final result should look something like this with the wallet connection working. Wallet connected Wallet disconnected ### Viewing payments Since we have created a few payments via `cURL` before, we can implement viewing of payments first. Let's create a `.env` file and add the following to it: ``` // .env VITE_API_URL=http://localhost:3000 ``` Next up, let's modify the `ViewPayments` component. ```tsx theme={null} // src/components/view-payments/index.tsx import React, { useState, useEffect } from 'react'; import './styles.css'; interface Payment { id: number; requestId: string; status: string; } const ViewPayments: React.FC = () => { const [payments, setPayments] = useState([]); const [isLoading, setIsLoading] = useState(true); const fetchPayments = async () => { try { const response = await fetch(`${import.meta.env.VITE_API_URL}/payments`); if (response.ok) { const data = await response.json(); setPayments(data.payments || []); } else { console.error('Failed to fetch payments'); } } catch (error) { console.error('Error fetching payments:', error); } finally { setIsLoading(false); } }; useEffect(() => { fetchPayments(); const interval = setInterval(fetchPayments, 3000); return () => clearInterval(interval); }, []); const getStatusClass = (status: string) => { switch (status.toLowerCase()) { case 'pending': return 'status-pending'; case 'in-progress': return 'status-in-progress'; case 'confirmed': return 'status-confirmed'; case 'failed': return 'status-failed'; default: return 'status-pending'; } }; if (isLoading && payments.length === 0) { return (

View Payments

Loading payments...
); } return (

View Payments

{payments.length === 0 ? (
No payments found
) : ( payments.map((payment) => (
Payment ID: {payment.id} {payment.status}
)) )}
); }; export default ViewPayments; ``` ```css theme={null} // src/components/view-payments/styles.css .view-payments { max-width: 600px; margin: 0 auto; } .payments-container { display: flex; flex-direction: column; gap: 12px; margin-top: 20px; } .payment-item { display: flex; justify-content: space-between; align-items: center; padding: 16px; border: 1px solid #4a4a4a; border-radius: 8px; background-color: #2a2a2a; transition: background-color 0.2s; } .payment-item:hover { background-color: #333333; } .payment-id { font-weight: 500; color: #e5e7eb; } .status-pill { padding: 6px 14px; border-radius: 20px; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; border: 1px solid transparent; } .status-pending { background-color: #451a03; color: #fcd34d; border-color: #92400e; } .status-in-progress { background-color: #1e3a8a; color: #93c5fd; border-color: #3b82f6; } .status-confirmed { background-color: #064e3b; color: #6ee7b7; border-color: #10b981; } .status-failed { background-color: #7f1d1d; color: #fca5a5; border-color: #ef4444; } .no-payments { text-align: center; color: #9ca3af; font-style: italic; padding: 40px 20px; background-color: #1f1f1f; border-radius: 8px; border: 1px solid #4a4a4a; } .loading { text-align: center; color: #9ca3af; padding: 20px; background-color: #1f1f1f; border-radius: 8px; border: 1px solid #4a4a4a; } ``` It should look something like this: View payments ### Creating payments Let's update our `CreatePayment` component. It's going to do the following: 1. The user inputs payment information - the payee address, amount, invoice currency and payment currency 2. After submitting the form, we create a payment on the API, receive the response and use the `transactions` property to execute the payment with our connected wallet. 3. Immediately after that succeeds, we update the payment status on the backend to `in-progress` ```tsx theme={null} // src/components/create-payment/index.tsx import React, { useState } from 'react'; import { useSendTransaction, useAccount } from 'wagmi'; import './styles.css'; interface PaymentForm { payee: string; amount: string; invoiceCurrency: string; paymentCurrency: string; } const CreatePayment: React.FC = () => { const [formData, setFormData] = useState({ payee: '', amount: '', invoiceCurrency: 'ETH-sepolia-sepolia', paymentCurrency: 'ETH-sepolia-sepolia' }); const [isExecuting, setIsExecuting] = useState(false); const { sendTransactionAsync } = useSendTransaction(); const { isConnected } = useAccount(); const currencyOptions = [ { value: 'ETH-sepolia-sepolia', label: 'ETH (Sepolia)' }, { value: 'FAU-sepolia', label: 'FAU (Sepolia)' }, { value: 'fUSDC-sepolia', label: 'fUSDC (Sepolia)' } ]; const handleInputChange = (e: React.ChangeEvent) => { const { name, value } = e.target; setFormData(prev => ({ ...prev, [name]: value })); }; const updatePaymentStatus = async (paymentId: number, status: string) => { try { const response = await fetch(`${import.meta.env.VITE_API_URL}/payments/${paymentId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ status }), }); if (!response.ok) { console.error('Failed to update payment status'); } else { console.log(`Payment ${paymentId} status updated to: ${status}`); } } catch (error) { console.error('Error updating payment status:', error); } }; const executeTransactions = async (transactions: Array<{ to: string; data: string; value: { hex: string } }>, paymentId: number) => { if (!isConnected) { alert('Please connect your wallet first'); return; } try { for (let i = 0; i < transactions.length; i++) { const tx = transactions[i]; console.log(`Executing transaction ${i + 1}/${transactions.length}:`, tx); const txHash = await sendTransactionAsync({ to: tx.to as `0x${string}`, data: tx.data as `0x${string}`, value: BigInt(tx.value.hex) }); // As soon as we start sending transactions, update status to 'in-progress' await updatePaymentStatus(paymentId, 'in-progress'); console.log(`Transaction ${i + 1} sent with hash:`, txHash); } alert('All transactions executed successfully!'); } catch (error) { console.error('Transaction execution failed:', error); alert(`Transaction failed: ${error instanceof Error ? error.message : 'Unknown error'}`); await updatePaymentStatus(paymentId, 'failed'); throw error; } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setIsExecuting(true); try { const response = await fetch(`${import.meta.env.VITE_API_URL}/payments`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formData), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || 'Failed to create payment'); } const data = await response.json(); console.log('Backend response:', data); if (data.calldata && data.calldata.transactions) { await executeTransactions(data.calldata.transactions, data.payment.id); } else { throw new Error('No transaction data received from backend'); } } catch (error) { console.error('Error in payment flow:', error); alert(`Payment failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } finally { setIsExecuting(false); } }; return (

Create Payment

{!isConnected && (

Please connect your wallet to create payments

)}
); }; export default CreatePayment; ``` ```css theme={null} // src/components/create-payment/styles.css .create-payment { max-width: 500px; margin: 0 auto; } .payment-form { display: flex; flex-direction: column; gap: 20px; } .form-group { display: flex; flex-direction: column; gap: 6px; } .form-group label { font-weight: 600; color: #374151; font-size: 14px; } .form-group input, .form-group select { padding: 12px; border: 2px solid #e5e7eb; border-radius: 6px; font-size: 16px; transition: border-color 0.2s; background-color: inherit; color: inherit; } .form-group input:focus, .form-group select:focus { outline: none; border-color: #11c9a0; box-shadow: 0 0 0 3px rgba(100, 108, 255, 0.1); } .form-group select { cursor: pointer; appearance: none; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e"); background-position: right 12px center; background-repeat: no-repeat; background-size: 16px; padding-right: 40px; } .form-group select option { background-color: #1a1a1a; color: #ffffff; padding: 8px 12px; } .form-group select option:hover { background-color: #11c9a0; } .submit-btn { background: #11c9a0; color: white; border: none; padding: 14px 24px; border-radius: 6px; font-size: 16px; font-weight: 600; cursor: pointer; transition: background-color 0.2s; margin-top: 10px; } .submit-btn:hover { background: #5145d4; } .submit-btn:active { background: #4338ca; } input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } input[type="number"] { appearance: textfield; -moz-appearance: textfield; } ``` The end result is a form that looks like the following: Create payment form ### Trying everything out We recommend using two different Metamask accounts you own. That way you will be able to confirm that the funds were moved on your very own. *NOTE*: For this demo, we recommend inputting your second account for the `Payee address` value and use the same invoice and payment currencies. 1. Let's create a payment from the client, moving 0.02 Sepolia ETH to our second account Create payment step 1 2. Create the payment and sign the transaction Sign transaction 1 Sign transaction 2 3. Navigate to the `View payments tab` , verify that the last payment is `In progress` and let's wait for the transaction to go through. You can patiently watch your server's logs to check when the webhook is called. Payment in progress 4. In a few moments the payment's status should be set to `Confirmed` . Payment confirmed This is it, you have successfully built a basic application integrating our API to move actual test funds between two wallets. **Happy building** 🎉 # LLM Integration Guide Source: https://docs.request.network/api-setup/llm-integration-guide The canonical reference for AI agents integrating Request Network payments: credentials, endpoints, and webhook verification. This page is the single source an AI coding agent should read to integrate Request Network payments correctly. You'll build a flow that accepts a crypto or stablecoin payment through a hosted payment link and confirms it via webhook. This flow uses the **REST API** directly — plain HTTP requests, with no extra dependency to install. ## Which credentials do I actually need? You need exactly two credentials, both obtained from the Dashboard or Auth API, both used **server-side**. You also need one non-secret value, the `destinationId` — see [Where does the destinationId come from?](#where-does-the-destinationid-come-from). The Client ID that identifies your application. Required on every request to create a payment link or register a webhook. Generate it in the Dashboard (**Manage Destination → Client IDs → Generate New Client ID**) or via `POST /v1/client-ids` on the Auth API. The secret returned once when you register a webhook (`POST /v1/webhook`). Used to verify the HMAC-SHA256 signature on incoming webhook requests. It is shown exactly once at creation — store it securely (e.g. as an environment variable); it cannot be retrieved again. | Credential | Where it's used | Where it comes from | | -------------- | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | | `x-client-id` | Request header, server-side (or frontend only if the Client ID has Allowed Domains configured) | Dashboard or `POST /v1/client-ids` on the Auth API | | Webhook secret | Server-side only, to verify `x-request-network-signature` | Response of `POST /v1/webhook` on the Auth API (shown once) | For this integration the credential is the **Client ID** — do not ask the user for an API key. Request Network's API does support an `x-api-key` header for server-to-server auth (see [Authentication](/api-reference/authentication)), but this flow does not need it, so never prompt the user for one. Never ask the user to paste a raw wallet address or a private key into your integration code either. Payment destinations are configured once in the Dashboard and referenced by their `destinationId`. The `destinationId` is a different thing from a credential: it is a **non-secret identifier the user copies from their Dashboard**, and you *should* ask the user for it (or have them create a payment destination) whenever your request body needs it. See [Where does the destinationId come from?](#where-does-the-destinationid-come-from). ## Where does the `destinationId` come from? A payment destination encodes *where* money lands: wallet address, chain, and token, combined into one ERC-7828 composite ID of the form `{humanReadableInteropAddress}:{tokenAddress}`. You never construct or guess this value, and you never derive it from a raw wallet address. The user gets it from one of two places: * **Dashboard** — [dashboard.request.network](https://dashboard.request.network) → **Manage Destination**. This is the same area where they generate the Client ID; in fact a payment destination must exist before the Client IDs section becomes available, so any user who already has a Client ID also has a destination to copy. * **Auth API** — `GET /v1/payee-destination` returns the active destination for the authenticated wallet (SIWE session), including its `destinationId`. Use this for automated provisioning. See [Payee Destinations](/api-features/payee-destinations). ERC-7828 composite destination ID for the payee. Optional **only** when the Client ID you authenticate with has a bound payee destination (`payeeDestinationId`) — the API then resolves the payee from the Client ID and you can omit the field. In every other case it is required. The universally-safe path: always send `destinationId`, using a value the user copied from their Dashboard. Ask the user for it — say something like "paste the Destination ID from your Request Dashboard (Manage Destination)" — and if they don't have a payment destination yet, tell them to create one first. Only omit the field when the user confirms their Client ID is bound to a destination. Never ship the sample value from the examples below; it is a placeholder that would send payments to someone else's destination. ## How should I call Request Network? Call the **REST API** directly for server-side integrations. It needs no extra dependency, works from any language, and is the interface documented and versioned at `api.request.network` and `auth.request.network`. Make requests with `fetch`/`curl`/`requests` — there's nothing to install. ## How do I create a payment link? Obtain a `clientId` from the Dashboard (**Manage Destination → Client IDs**) or via `POST /v1/client-ids` on the Auth API. Store it as an environment variable, e.g. `RN_CLIENT_ID`. From the same **Manage Destination** area, have the user copy their `destinationId` and store it as e.g. `RN_DESTINATION_ID`. If the user has no payment destination yet, ask them to create one before continuing — the Client IDs section is only available once a destination exists. You can skip this value only if the user's Client ID is bound to a payee destination. `POST https://api.request.network/v2/secure-payments` with the `x-client-id` header and a `requests` array containing at least one `{ destinationId, amount }` item. Substitute the user's own `destinationId` — the value in the examples below is a placeholder. The response includes a `securePaymentUrl`. Redirect the payer there, or embed it as a link/button. The link expires after 7 days or once paid, whichever comes first. ```bash cURL theme={null} # RN_DESTINATION_ID is the user's own Destination ID, copied from the Dashboard. # It looks like this (do not reuse this sample value): # 0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7@eip155:11155111#1f969856:0x370DE27fdb7D1Ff1e1BaA7D11c5820a324Cf623C curl -X POST 'https://api.request.network/v2/secure-payments' \ -H "x-client-id: $RN_CLIENT_ID" \ -H 'Content-Type: application/json' \ -d '{ "requests": [ { "destinationId": "'"$RN_DESTINATION_ID"'", "amount": "1" } ] }' ``` ```typescript TypeScript theme={null} const response = await fetch("https://api.request.network/v2/secure-payments", { method: "POST", headers: { "x-client-id": process.env.RN_CLIENT_ID!, "Content-Type": "application/json", }, body: JSON.stringify({ requests: [ { // The user's own Destination ID from the Dashboard, e.g. // "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7@eip155:11155111#1f969856:0x370DE27fdb7D1Ff1e1BaA7D11c5820a324Cf623C" destinationId: process.env.RN_DESTINATION_ID!, amount: "1", }, ], }), }); if (!response.ok) { throw new Error(`Failed to create payment link: ${await response.text()}`); } const { requestIds, securePaymentUrl, token } = await response.json(); console.log({ requestIds, securePaymentUrl, token }); ``` **Response (201 Created):** ```json theme={null} { "requestIds": ["01de2a889ee629c15b71b5d7964e3a7e87638c886be75bf1b9d2c1fbe64cf855fb"], "securePaymentUrl": "https://pay.request.network/?token=01KJRA0M9QG8MA4X887908T8A4", "token": "01KJRA0M9QG8MA4X887908T8A4" } ``` | Field | Type | Description | | ------------------ | ---------- | --------------------------------------------------------------------------- | | `requestIds` | `string[]` | IDs of the created payment requests — use these to correlate webhook events | | `securePaymentUrl` | `string` | Shareable URL for the payer to complete payment | | `token` | `string` | Unique token for this payment session | The `destinationId` in the request body is a composite value: `{humanReadableInteropAddress}:{tokenAddress}`. It comes from a payment destination already configured in the Dashboard — you do not construct it from a raw wallet address. Ask the user for their value rather than hardcoding one. Omit the field only when the user's Client ID is bound to a payee destination. ## How do I confirm a payment? The webhook is the source of truth for payment confirmation — do not treat a successful `POST /v2/secure-payments` call or a payer's browser redirect as proof of payment. `POST https://auth.request.network/v1/webhook` with `x-client-id` and a `{ "url": "https://yourdomain.com/webhook" }` body. The response includes a one-time `secret` — store it as `RN_WEBHOOK_SECRET`. Compute `HMAC-SHA256(rawBody, webhookSecret)` and compare it to the `x-request-network-signature` header using a constant-time comparison. You must use the **raw** request body — verifying against a re-serialized (e.g. `JSON.stringify(req.body)`) body will produce a mismatch and silently break verification. A fully paid request fires `payment.confirmed`; a partial payment fires `payment.partial`. Match the `requestId` in the payload to the `requestIds` you received when creating the payment link. Fulfill the order only after `payment.confirmed` — `payment.partial` is an intermediate state, not a completed payment. ```typescript theme={null} import { createHmac, timingSafeEqual } from "node:crypto"; function verify(rawBody: string, signature: string | undefined, secret: string) { if (!signature) return false; const expected = createHmac("sha256", secret).update(rawBody).digest("hex"); const a = Buffer.from(expected, "hex"); const b = Buffer.from(signature, "hex"); return a.length === b.length && timingSafeEqual(a, b); } ``` Always verify against the raw request body, before any JSON parsing. Frameworks like Express parse and re-serialize the body by default (`express.json()`); if you re-stringify `req.body` instead of using the original raw bytes, the computed HMAC will not match and every verification will fail (or worse, be skipped entirely if the check is disabled to "fix" it). **Fallback (optional polling):** `GET https://api.request.network/v2/request/{requestId}` returns `{ hasBeenPaid: boolean, ... }`. Send the `x-client-id` header — the call is unauthenticated without it: ```bash theme={null} curl -X GET "https://api.request.network/v2/request/$REQUEST_ID" \ -H "x-client-id: $RN_CLIENT_ID" ``` Use this only as a fallback if a webhook delivery is missed — it is not a replacement for webhook handling. This call runs from your server, so it needs a **backend** Client ID — one created with no Allowed Domains. A Client ID that has Allowed Domains set is validated against the `Origin` header, and a server-side request sends no `Origin`, so it will be rejected. See [Client ID Management](/api-features/client-id-management). ## How do I handle cross-chain payments? When a payer pays from a different chain than your payment destination, settlement is not instantaneous. Your thank-you or confirmation page must treat the payment as **pending** until the webhook confirms it — never mark a payment as complete purely because the payer's browser reported a successful transaction submission or redirected back to your site. Drive the final "paid" state exclusively from the `payment.confirmed` webhook (or the `hasBeenPaid` polling fallback), not from front-end submission success. ## Common mistakes * Asking the user for an API key, a private key, or a raw wallet address. For this flow you need only the Client ID, the webhook secret, and the user's Destination ID. * Hardcoding the sample `destinationId` from the docs instead of asking the user for theirs — payments would go to the sample destination, not to the user. * Verifying the webhook signature against a re-serialized body (e.g. `JSON.stringify(req.body)`) instead of the raw request body. * Assuming a cross-chain payment is final as soon as the payer's browser confirms submission, instead of waiting for the `payment.confirmed` webhook. * Adding an unnecessary dependency instead of calling the documented REST API directly. # FAQ Source: https://docs.request.network/faq Frequently Asked Questions and Common Misconceptions If your question is not answered below, please consider posting it to the [Request Network Discussions](https://github.com/orgs/RequestNetwork/discussions) page on Github. No. Request Network is not a blockchain, smart contract platform, or scaling solution. Rather, it's a protocol for storing payment requests, facilitating onchain payments, and reconciling those payments with the requests. It stores payment requests in [IPFS](https://www.ipfs.com/) and CID hashes on [Gnosis Chain](https://www.gnosis.io/). It uses [The Graph](https://thegraph.com/) for onchain event indexing. It processes payments across a variety of [supported payment chains](/resources/supported-chains-and-currencies). Request Network is an open-source protocol. Request Finance is a product built on top of Request Network. Request Finance has spun off from Request Network, and they are now two separate teams. No. Requests are created on Gnosis Chain (or Sepolia for testing), even if the payment will happen on a different chain. Payment(s) can occur on any of our [supported payment chains](/resources/supported-chains-and-currencies). To help builders get started quickly, the Request Network Foundation operates Request Node Gateways that are free for anyone to use. These gateways offer endpoints for creating and retrieving requests. Requests created on Gnosis Chain are "real" and will exist forever. Requests created on Sepolia are "test" requests and will exist only as long as Sepolia continues to operate. It can be, but not necessarily. The [Signer Identity](/glossary#signer-identity) that signs to create a request is defined by the `signer` parameter. This is separate from the [Payment Recipient](/glossary#payment-recipient) which is defined by the `paymentNetwork.parameters.paymentAddress` parameter. They can be the same or different. This design allows for a single payee identity to have potentially multiple payment recipient addresses. See [Parties of a Request](/glossary#parties-of-a-request) for details. No. Request Network is a hybrid onchain/offchain protocol storing the majority of request contents in IPFS. Only the content-addressable ID (CID) is stored onchain, on Gnosis Chain. Yes. Payments are linked to requests via a [Payment Reference](/glossary#payment-reference) derived from the request contents. Therefore, it is possible to calculate the `paymentReference` and execute a payment *before* creating the corresponding request. Yes and No. Requests can be *denominated* in fiat currencies like USD, EUR, etc. ([ISO 4217 currencies](https://en.wikipedia.org/wiki/ISO_4217)) but our payment smart contracts only support payments in cryptocurrencies. We call these [Conversion Payment](/glossary#conversion-payment)s, in which the requested fiat amount is converted to the appropriate cryptocurrency amount using onchain price feeds at the moment of payment. It is possible to implement fiat payments using Declarative Requests, where the payer declares that the payment was sent and the payee declares that the payment was received. Crypto-to-fiat (off-ramp) payments are supported via the Request Network API — see [Crypto-to-fiat Payments](/api-features/crypto-to-fiat-payments). Fiat-to-crypto (on-ramp) payments are not currently supported by the API. No. A Request Node cannot change a request's contents before persisting it to IPFS and onchain because doing so would invalidate the signature. This is true for private, encrypted requests as well. The Request Node cannot forge the end-user's signature. Yes. It is possible to request access to a user's Request Finance invoices using the add-stakeholder web component which is just a thin wrapper around the [Request Finance Add Stakeholders API](https://docs.request.finance/faq#i-am-integrating-the-request-network.-can-i-get-access-to-users-data-on-request-finance). They display a dialog that prompts the end-user to grant access to 1 invoice at a time. Details: * Request Finance invoices are encrypted. * Request Network Foundation cannot grant access to encrypted requests in Request Finance. Yes, via the **Request Network API only**. Crosschain payments are not supported by the protocol directly. When using the API, payers can fund requests using assets on different chains and tokens, and the payee receives the requested asset on the request's chain. For example, you can pay a USDC-on-Base request using USDT from Optimism. See [Crosschain Payments](/api-features/crosschain-payments) for implementation details. No. It is not currently possible to create a request via a smart contract call. However, [RequestNetwork/public-issues#15](https://github.com/RequestNetwork/public-issues/issues/15) is in our roadmap to make this possible. The recommended way to create a request is using the Request Network API. # Glossary Source: https://docs.request.network/glossary Definitions of Request Network terms — parties, protocol, payments, and cryptography concepts. ## Parties of a Request ### Payee Identity The Payee Identity is the EVM address that uniquely identifies the payee. It can be but is not necessarily the address that will receive the payment. It is authorized to make certain updates to the request after it is created. It is one of the owners of the request data. The Payee Identity is defined by the `payee` field when creating a request. ### Payer Identity The Payer Identity is the EVM address that uniquely identifies the payer. It can be but is not necessarily the address that will send the payment. It is authorized to make certain updates to the request after it is created. It is one of the owners of the request data. The Payer Identity is defined by the `payer` field when creating a request. ### Signer Identity The Signer Identity is the EVM address that provides the signature to create a request. It must be either the Payee Identity or Payer Identity. ### Payment Recipient The EVM address that receives the payment. It is defined by the `paymentNetwork.parameters.paymentAddress` field when creating a request. ### Payment Sender The EVM address that sends the payment. Anyone can pay a given request. The payment sender address is NOT stored in the request contents. ### Additional Stakeholder An EVM address that has been granted view access to an encrypted request. ### Declarative Delegate An EVM address that has been granted authorization to declare payments sent and payments received on behalf of either the Payee Identity or Payer Identity of a given request. ## Request Protocol ### Action An action is signed data added by a request's stakeholder into the Request Protocol that creates or updates the state of a request. A request can be represented by a list of actions. For example, the creation of a request is an action. ### Balance When using a payment network, the balance is the current amount paid for a request. The balance is determined by the payment detection method of the payment network used. A request with no payment network provided doesn't have a balance. ### Confirmed/Pending action Request relies on other blockchain technologies to ensure data immutability. Most blockchains don't offer transaction instant finality. This means that when performing an action on the request, this action can't directly be confirmed as effective. As long as the action hasn't persisted and is not confirmed, the action is marked as "pending". The "pending" state helps have a fast response and a good user experience. Until the request is Confirmed, it should not be relied upon. ### Signature Provider A signature provider is an abstraction of identity management and action signatures. Depending on use cases, it allows you to give your user complete control or handle some parts for them. ### Decryption Provider A decryption provider is an abstraction of the mechanism that handles the decryption of a request. Depending on use cases, it allows you to give your user complete control or handle some parts for them. It is not used for clear requests. ### Extension An extension is a set of actions that extends the feature of a request. A request without extension is a fundamental request for payment with a payee, a currency, and a requested amount. The extension allows for more advanced features. ### Identity The identity defines a stakeholder of a request that allows signing or encrypting the request actions. The identity is the public data that identifies the stakeholder. ### Request ID The request ID is the number that uniquely identifies a request. This number is computed from the hash of the request creation action. ### Request Data (aka. Request Contents) The request data is the current state of a request, the data of the request after having applied all the confirmed actions on it. ### Stakeholder A request stakeholder is a party involved with the request. Stakeholders are generally the payer and the payee of the request or any other third-party allowed to perform actions on it. For encrypted requests, stakeholders are any party interested in reading the request content. ### Topic A topic is a string that is used to index a request. This topic is used for request retrieval. Several requests can share the same topic. Every request has its request id and payee identity as topics (and the payer identity if it is defined). Any custom topic can be appended to a request. ## Payments ### Payment Detection Payment detection is a method defined by the payment network to determine the current balance of a request. ### Payment Network (aka Payment Extension) A payment network is a predefined set of rules to agree on the balance of a request. The payment network is defined during the creation of the request. A payment network is generally related to one currency, but it's not always the case (the Declarative payment network is currency agnostic). ### Payment Reference In the Reference-based Payment Networks, Payments are linked to Requests via a `paymentReference` which is derived from the `requestId` and payment recipient address. For details see [Payment Detection](/api-features/payment-detection). ### Conversion Payment A "conversion" request is denominated in one currency but paid in another currency. This is facilitated by on-chain price feeds provided by oracles. The typical use case is to denominate a request in fiat like USD and pay the request in stablecoins like USDC or DAI. For details see [Conversion Payments](/api-features/conversion-payments). ### Swap-to-pay Payment A "swap-to-pay" payment is where the payment sender sends one currency but the payment recipient receives a different currency. For details see [Crosschain Payments](/api-features/crosschain-payments). ### Swap-to-Conversion Payment A "swap-to-conversion" payment is where the request is denominated in currency A, the payer sends currency B and the payee receives currency C. For details see [Crosschain Payments](/api-features/crosschain-payments). ## Ecosystem ### Request Client The Request Client is a Javascript library that interacts directly with the Request Protocol. The Request Client connects to a Request Node. ### Request Node Request Nodes are HTTP servers exposing an API that allows the Request Client to communicate with the Request Protocol. These servers abstract the complexity of IPFS and Ethereum used by the Request Protocol. ### Request Protocol The Request Protocol is the underlying protocol that powers Request. It defines how requests are stored on a distributed ledger and how to interpret actions performed on them. ## Blockchain, Cryptography ### Confirmation Confirmation means that the network has verified the blockchain transaction. This happens through a process known as mining in a proof-of-work system (e.g., Bitcoin). Once a transaction is confirmed, it cannot be reversed. ### Ether Ether is the native token of the Ethereum blockchain, which is used to pay for transaction fees, block proposer rewards, and other services on the network. ### IPFS The Inter-Planetary File System (IPFS) is a protocol and a peer-to-peer network for storing and sharing data in a distributed file system. IPFS uses content-addressing to uniquely identify each file in a global namespace connecting all computing devices. The Request Protocol uses IPFS to ensure data accessibility. ### Multi-signature Multi-signature (multisig) wallets allow multiple parties to require more than one key to authorize a transaction. The needed number of signatures is agreed upon at the creation of the wallet. Multi-signature addresses have a much greater resistance to theft. ### Account Abstraction (ERC-4337) Account Abstraction lets a smart contract act as a wallet (a "smart account"), enabling features such as transaction batching and gas sponsorship that externally-owned accounts (EOAs) cannot do natively. The Secure Payment page can execute supported EVM payments through an ERC-4337 smart account so approval, funding, and settlement run as a single operation. See [Smart account payments](/api-features/secure-payment-pages#smart-account-payments). ### EIP-2612 Permit [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) lets a token holder approve a spender with an off-chain signature instead of an on-chain `approve()` transaction, removing the gas cost of the approval step. Tokens such as USDC support it; tokens such as USDT do not. See [Gasless token approvals](/api-features/secure-payment-pages#gasless-token-approvals-eip-2612). ### Gnosis Safe A [Safe](https://safe.global/) is a widely used smart-contract multisig wallet that requires multiple owner signatures to execute a transaction. Request Network supports paying a secure payment from a Safe via the [Safe multisig payments](/api-features/safe-multisig-payments) flow. ### Private Key A private key is a large number that allows you to sign or decrypt messages. Private keys can be thought of as a password; private keys must never be revealed to anyone but you, as they allow you to spend the funds from your wallet through a cryptographic signature. # Client ID linking Source: https://docs.request.network/orchestrators/client-id-linking Link platform Client IDs to your orchestrator — directly or via onboarding link intents — so your fees and branding apply to their payments. ## Overview To apply your [orchestrator fees](/orchestrators/fees) and [branding](/orchestrators/whitelabel-branding) to a platform's payments, link that platform's [Client ID](/api-features/client-id-management) (`cli_*` token) to your orchestrator. There are two ways to link: directly, when you already have the client ID, or via an onboarding **link intent**, when a recipient creates and links the client ID themselves. All linking endpoints use the `x-orchestrator-key` header. Linking is **one-to-one from the client ID side**: a client ID can be actively linked to at most one orchestrator. Re-linking a client ID that is already linked to a *different* orchestrator is rejected; re-linking to the *same* orchestrator is idempotent. ## List linked client IDs ```bash theme={null} curl -X GET "https://api.request.network/v2/orchestrators/client-ids?page=1&limit=20" \ -H "x-orchestrator-key: orc_YOUR_ORCHESTRATOR_KEY" ``` Results are paginated with `page` and `limit`. ## Link an existing client ID When the platform has already shared its `cli_*` token with you, link it directly: ```bash theme={null} curl -X POST "https://api.request.network/v2/orchestrators/client-ids" \ -H "x-orchestrator-key: orc_YOUR_ORCHESTRATOR_KEY" \ -H "Content-Type: application/json" \ -d '{ "clientId": "cli_PLATFORM_CLIENT_ID" }' ``` ## Onboard with a link intent When you want a recipient to create and link a client ID themselves, create a **link intent**. It returns a single-use onboarding URL you hand to the recipient; redeeming it creates a client ID under the name you chose and links it to your orchestrator. ```bash theme={null} curl -X POST "https://api.request.network/v2/orchestrators/client-id-link-intents" \ -H "x-orchestrator-key: orc_YOUR_ORCHESTRATOR_KEY" \ -H "Content-Type: application/json" \ -d '{ "clientIdName": "Acme Store" }' ``` The response contains an onboarding `url` carrying a single-use access token. Share it with the recipient to complete onboarding. A link intent is single-use and expires. Once redeemed, the new client ID is linked to your orchestrator and appears in `GET /v2/orchestrators/client-ids`. ## Unlink a client ID ```bash theme={null} curl -X DELETE "https://api.request.network/v2/orchestrators/client-ids/cli_PLATFORM_CLIENT_ID" \ -H "x-orchestrator-key: orc_YOUR_ORCHESTRATOR_KEY" ``` Unlinking revokes the active link. The client ID itself continues to exist and work; it simply no longer carries your orchestrator's fees and branding. ## Related Apply fees and per-client-ID overrides to linked client IDs. How platforms create and manage the `cli_*` client IDs you link. # Orchestrator fees Source: https://docs.request.network/orchestrators/fees Configure orchestrator fees and per-client-ID overrides: rates, caps, fee bearer, flow direction, and how fee layers resolve. ## Overview As an [orchestrator](/orchestrators/overview), you can attach fees to the payments made under the client IDs you've linked. Fees are configured with the `x-orchestrator-key` header on the `/v2/orchestrators/fee-configs` endpoints. Orchestrator fees are separate from — and stack with — the Request Network [protocol fee](/api-features/protocol-fees) and any per-request [platform fee](/api-features/platform-fees). On the orchestrator API you manage **orchestrator fees only**; the protocol fee is administered by Request Network. ## Fee layers and precedence A payment can be subject to up to three fee layers. They resolve from most to least specific: 1. **Per-client-ID override** — an orchestrator fee scoped to a single linked client ID. Wins its slot over the orchestrator default. 2. **Orchestrator default** — an orchestrator fee that applies to all of the orchestrator's linked client IDs. 3. **Protocol fee** — set by Request Network (falls back to the standard protocol default when none is configured). There is no per-client-ID protocol fee — client IDs can only override orchestrator fees. ## Fee configuration fields Fee rate in basis points, `0`–`10000` (`10000` = 100%). For example, `250` is 2.5%. Optional maximum fee in USD (as a string). Caps the fee for large payments. Who absorbs the fee: `payer` (added on top of what the payer pays) or `recipient` (deducted from what the recipient receives). Which payment direction the fee applies to: `incoming` (get-paid flows) or `outgoing` (pay/payout flows). **Immutable** — see below. EVM address that receives the fee. At least one of `evmRecipientAddress` or `tronRecipientAddress` is required. Tron address that receives the fee. At least one of `evmRecipientAddress` or `tronRecipientAddress` is required. A fee config's identity is its combination of fee type, `flow`, and `feeBearer`. These are **immutable** — to change `flow` or `feeBearer`, disable the existing config and create a new one. You can update `bps`, `usdCap`, recipient addresses, and `status`. ## Fee bearer and flow semantics * **`flow: incoming`** applies to payments you receive ("get paid"); its default bearer is the recipient. * **`flow: outgoing`** applies to payouts you send ("pay"); its default bearer is the payer. * **`feeBearer: payer`** adds the fee on top of the amount the payer pays. * **`feeBearer: recipient`** deducts the fee from the amount the recipient receives. A recipient-borne fee cannot exceed the gross amount. ## Managing fee configs ```bash theme={null} # Create an orchestrator default fee (2.5%, payer-borne, on incoming payments) curl -X POST "https://api.request.network/v2/orchestrators/fee-configs" \ -H "x-orchestrator-key: orc_YOUR_ORCHESTRATOR_KEY" \ -H "Content-Type: application/json" \ -d '{ "bps": 250, "feeBearer": "payer", "flow": "incoming", "evmRecipientAddress": "0x742d35CC6634c0532925a3B844BC9e7595f8fA40" }' ``` | Operation | Endpoint | | ------------------------------------------ | ------------------------------------------ | | Create a fee or per-client-ID override | `POST /v2/orchestrators/fee-configs` | | List fee configs (filter with `?clientId`) | `GET /v2/orchestrators/fee-configs` | | Update a fee config | `PATCH /v2/orchestrators/fee-configs/:id` | | Disable a fee config | `DELETE /v2/orchestrators/fee-configs/:id` | To scope a fee to a single linked client ID (an **override** rather than the orchestrator-wide default), provide that `clientId` when creating, listing, or disabling the config. ## Related Request Network's protocol-level fee, rate, and cap. Per-request integrator fees via feePercentage/feeAddress. Where fee line items appear in API responses. # Orchestrators overview Source: https://docs.request.network/orchestrators/overview Orchestrators are fee and branding partners in Request Network: link client IDs, configure fees, and apply whitelabel branding across your platform. ## What is an orchestrator? An **orchestrator** is a fee and branding partner in Request Network. It is a first-class account that can: * **Link client IDs** — associate the developer [Client IDs](/api-features/client-id-management) (`cli_*` tokens) of the platforms you serve with your orchestrator. * **Configure fees** — set [orchestrator fees](/orchestrators/fees) (and per-client-ID overrides) that apply to payments made under those client IDs. * **Apply branding** — give the hosted [Secure Payment](/tools/secure-payments) experience your own [whitelabel branding](/orchestrators/whitelabel-branding). This is aimed at platforms, PSPs, and partners who orchestrate payments on behalf of multiple downstream merchants and want consistent fees and branding across them. "Orchestrator" here is the formal fee/branding partner account described on this page. It builds on — but is broader than — the lightweight "orchestrator pattern" of binding a single Client ID to a payee destination described in [Client ID Management](/api-features/client-id-management#orchestrator-pattern). ## Getting provisioned Orchestrator accounts and their API keys are provisioned by Request Network — they are not self-service. [Get in touch](https://request.network) to have an orchestrator created for you. Once provisioned, you authenticate to the orchestrator endpoints with a secret key sent in the `x-orchestrator-key` header: ```bash theme={null} curl -X GET "https://api.request.network/v2/orchestrators/client-ids" \ -H "x-orchestrator-key: orc_YOUR_ORCHESTRATOR_KEY" ``` Your orchestrator key (prefixed `orc_`) is a server-side secret. The full key value is shown only once, at creation time. Store it in a secret manager, never expose it in client-side code, and contact Request Network to rotate it if it is compromised. ## The orchestrator API surface All partner-facing orchestrator endpoints live under `/v2/orchestrators` and are authenticated with the `x-orchestrator-key` header: | Endpoint | Purpose | | ----------------------------------------------- | ---------------------------------------------------- | | `GET /v2/orchestrators/client-ids` | List the client IDs linked to your orchestrator | | `POST /v2/orchestrators/client-ids` | Link an existing `cli_*` client ID | | `DELETE /v2/orchestrators/client-ids/:clientId` | Unlink a client ID | | `POST /v2/orchestrators/client-id-link-intents` | Create an onboarding link intent | | `POST /v2/orchestrators/fee-configs` | Create an orchestrator fee or per-client-ID override | | `GET /v2/orchestrators/fee-configs` | List your fee configs (optionally `?clientId`) | | `PATCH /v2/orchestrators/fee-configs/:id` | Update a fee config | | `DELETE /v2/orchestrators/fee-configs/:id` | Disable a fee config | See [Client ID linking](/orchestrators/client-id-linking) and [Orchestrator fees](/orchestrators/fees) for details. ## How orchestrators apply at payment time **Fees** are applied only when the secure payment is created with **paired authentication** — both your `x-orchestrator-key` and the platform's `x-client-id` headers. A client-ID-only call cannot carry an orchestrator key (it is rejected), so it receives no orchestrator fee. The orchestrator's active fee configuration is resolved and baked into the payment at creation time: ```bash theme={null} curl -X POST "https://api.request.network/v2/secure-payments" \ -H "x-orchestrator-key: orc_YOUR_ORCHESTRATOR_KEY" \ -H "x-client-id: cli_PLATFORM_CLIENT_ID" \ -H "Content-Type: application/json" \ -d '{ "requests": [ { "destinationId": "...", "amount": "100" } ] }' ``` **Branding** is resolved from the active client-ID → orchestrator link when the hosted Secure Payment page loads, so your branding applies to a linked client ID's payments even without paired authentication. ## Related Configure orchestrator fees and per-client-ID overrides. Link client IDs directly or via onboarding link intents. Theme the Secure Payment page with your branding. # Whitelabel branding Source: https://docs.request.network/orchestrators/whitelabel-branding Theme the hosted Secure Payment page with your own colors, logo, and legal links as an orchestrator or per linked client ID. ## Overview As an [orchestrator](/orchestrators/overview), you can whitelabel the hosted [Secure Payment](/tools/secure-payments) page with your own colors, logo, and legal links. Branding can be set at the orchestrator level (applies to all your linked client IDs) or overridden per linked client ID. Branding is resolved server-side and delivered embedded in the `GET /v2/secure-payments/:token` response, so the hosted page renders your theme without any extra call. ## Color tokens Branding defines five color tokens. Each must be a 3- or 6-digit hex color. The page derives additional shades (hovers, borders, modal colors, and a light/dark scheme) automatically from these five values. | Token | Default | Used for | | ------------------- | --------- | ------------------------------ | | `pageBackground` | `#f3f4f6` | Page background | | `surfaceBackground` | `#ffffff` | Cards and panels | | `primaryAction` | `#00d395` | Primary buttons (Pay, Approve) | | `primaryText` | `#475569` | Headings and amounts | | `secondaryText` | `#94a3b8` | Labels and helper text | These are the theme-token names applied by the Secure Payment page. The underlying stored branding fields use `Color`-suffixed names — in particular `surfaceBackground` is stored as `cardBackgroundColor` (and `pageBackground` → `pageBackgroundColor`, `primaryAction` → `primaryActionColor`, and so on). ## Logo and legal links | Field | Rules | | ------------- | ---------------------------------------------------------------------------------------------------------- | | `logoPath` | Footer/icon image. Must be a raster image: `.png`, `.jpg`, `.jpeg`, `.webp`, or `.avif` (SVG is rejected). | | `termsPath` | Terms link. Must end in `.html`. | | `privacyPath` | Privacy link. Must end in `.html`. | All three are **relative paths that must start with `/branding/`** — they cannot be absolute URLs or protocol-relative, cannot contain a query (`?`) or hash (`#`), and cannot contain path traversal (`..`). Asset files must be **bundled with the deployed Secure Payment app** under `public/branding/{brand}/`. There is no remote-URL fetch — a path that points to a missing file renders as broken branding, not a fallback. Coordinate logo/legal asset bundling with Request Network when onboarding your branding. ## Request Network attribution `displayRequestBranding` (boolean) controls whether the footer shows **"Made easy by Request Network"**. It defaults to off for orchestrator-branded payments. Set it to `true` to keep the attribution. ## Resolution priority Branding is selected **one row at a time**, most specific wins: 1. **Client branding** — if a linked client ID has its own branding row, it is used. Any field left unset in that row falls back to the **Secure Payment defaults** (not to the orchestrator's values). 2. **Orchestrator branding** — used only when the client ID has no branding row of its own. 3. **Secure Payment defaults** (and the platform's default legal links) — used when neither is set. In other words, defining client-level branding replaces the orchestrator branding entirely for that client ID, so set every field you want at the level you use. ## Setting branding Branding is managed through your orchestrator surface using the `x-orchestrator-key` header. Provide a `clientId` to set a per-client-ID override; omit it to set the orchestrator-level branding that applies to all your linked client IDs. Because logo and legal asset files must be bundled into the deployed app (see warning above), coordinate branding setup with Request Network during onboarding. ## Related The hosted page your branding themes. How orchestrators, client IDs, fees, and branding fit together. # Release Notes Source: https://docs.request.network/release-notes/index Stay up to date with the latest updates and improvements to Request Network products # Release Notes Stay informed about the latest features, improvements, and bug fixes across Request Network products. Our release notes provide detailed information about what's new and what's changed in each version. ## Product Release Notes Track updates to the Request Network API, including new endpoints, breaking changes, and performance improvements. ## How to Stay Updated We follow semantic versioning and provide advance notice of breaking changes. Major version updates will be announced at least 30 days in advance with migration guides. * **Critical Updates**: Subscribe to our status page for system-wide notifications * **Feature Updates**: Follow our blog for detailed feature announcements * **Developer Updates**: Join our Discord community for real-time discussions * **Current Version**: Full support with regular updates * **Previous Version**: Security updates and critical bug fixes for 6 months * **Legacy Versions**: Limited support, migration assistance available ## Quick Links * [API Status Page](https://status.request.network) - Real-time system status * [Developer Blog](https://request.network/blog) - In-depth feature explanations * [Discord Community](https://request.network/discord) - Ask questions and get support * [GitHub Repository](https://github.com/RequestNetwork) - Source code and issue tracking ## Release Schedule We typically release updates on the following schedule: * **Patch releases** (bug fixes): As needed * **Minor releases** (new features): Monthly * **Major releases** (breaking changes): Quarterly Sign up for our developer newsletter to receive release notes directly in your inbox. # Request API Release Notes Source: https://docs.request.network/release-notes/request-api Track recent updates and changes to the Request Network API This page tracks notable changes to the Request Network v2 API. ## Recent updates ### New Features * **Payer wallet details in reconciliation** — request status responses and payment webhook payloads include `payerAddress`, the address used to make the payment, and `payerEoaAddress`, the payer's connected wallet address. These can differ when a smart account is used. Both are `null` when unavailable. See [Query requests](/api-features/query-requests) and [Webhooks & events](/api-features/webhooks-events). * **List requests by wallet (optional)** — the request list endpoint (`GET /v2/request`) now accepts an optional `walletAddress` filter to scope results to a single payee wallet; omit it to list across the authenticated identity scope. * **Tron batch payouts** — Tron payouts can now be bundled via multicall payouts using the `tron_batch` execution kind. See [Multicall payouts](/api-features/payouts#multicall-payouts). ### Improvements * **Rate-limit SLA** — authenticated integrators (via `x-client-id`) are served at a 300 requests/min SLA, with higher tiers available on request. Limits are scoped per client ID and IP. See [Authentication](/api-reference/authentication). * **LiFi routing hardened** — cross-chain routing is now restricted to the Across bridge, and a circuit breaker degrades gracefully when LiFi is impaired so payments remain detectable. See [Cross-chain payments](/api-features/crosschain-payments). ### New Features * **Orchestrators** — a fee and branding partner layer. Provisioned by Request Network and authenticated with an `x-orchestrator-key`, orchestrators link platform Client IDs (`POST /v2/orchestrators/client-ids` and onboarding link intents), configure orchestrator fees and per-client-ID overrides (`/v2/orchestrators/fee-configs`), and apply whitelabel branding. See [Orchestrators](/orchestrators/overview). * **Configurable orchestrator fee model** — basis-point rates, optional USD caps, `feeBearer` (payer/recipient), and `flow` (incoming/outgoing), resolving as per-client-ID override → orchestrator default → protocol fee. See [Orchestrator fees](/orchestrators/fees). ### New Features * **Multicall payouts** — `POST /v2/secure-payments/multicall-payouts` combines multiple existing payout links into one hosted link settled as a single bundle. Three execution kinds: `evm_same_chain`, `evm_cross_chain` (per-child Li.Fi routing), and `tron_batch`. See [Multicall payouts](/api-features/payouts#multicall-payouts). ### New Features * **Safe multisig payments** — pay a secure payment from an existing Gnosis Safe: request Safe-ready calldata with `isSafe=true`, execute on your Safe, and track settlement by recording `safeTxHash` on the intent endpoint (`423 Locked` while in progress). See [Safe multisig payments](/api-features/safe-multisig-payments). * **Gasless token approvals (EIP-2612)** — on the smart-account path, permit-enabled tokens (e.g. USDC) authorize via an off-chain signature with no separate approval transaction. See [Gasless token approvals](/api-features/secure-payment-pages#gasless-token-approvals-eip-2612). ### New Features * **USDT0** is supported as a payment currency (alias of USDT) on Arbitrum One, Optimism, and Polygon, and is part of the cross-chain currency set. See [Supported Chains and Currencies](/resources/supported-chains-and-currencies). * **Merkle Science** is available as a multi-chain KYT screening provider alongside the default Hypernative — select it with `screeningProvider` on a destination's access policy. See [Compliance-gated payments](/use-cases/compliance-gated-payments). ### New Features * **Whitelabel branding** for the Secure Payment page — five color tokens plus logo and legal links, set at the orchestrator level or per client ID and delivered with the secure-payment metadata. See [Whitelabel branding](/orchestrators/whitelabel-branding). ### Improvements * **Tron gas fees** exposed in payment breakdowns. The `fees` array now includes Tron network gas costs (energy/bandwidth converted to TRX-equivalent USD) so payers and accountants can reconcile total spend across EVM and Tron payments uniformly. ### New Features * **Secure payout links** — `POST /v2/secure-payments/payouts` returns a hosted URL the payer opens to send a single-recipient payment. Works on EVM and Tron. Pairs with the new `payout` flag on secure payments to distinguish incoming/outgoing flows when listing. * **Secure payment filtering** — `GET /v2/secure-payments` accepts filters to scope listings by request ID, payout vs incoming, and creation timestamp. * **Secure payments without bound destinations** — Client IDs without a bound `payeeDestinationId` can still create secure payments by supplying `requests[].destinationId` inline. ### New Features * **Tron** is a first-class destination network. Wallet sign-in via TronLink/Guarda/Trust/WalletConnect-Tron, USDT and USDC TRC-20 destinations, single-recipient payments, single-recipient payouts. Network ID `tron`, chain ID `728126428`. * **Network fee estimation for Tron** — readiness checks return whether the payer's wallet has enough TRX, energy, and bandwidth to broadcast the transaction. * **Max-allowance approval pattern** for Tron USDT (which does not allow approve-from-non-zero, similar to mainnet USDT). ### Limitations * **Batch payments are EVM-only.** Tron requests with multiple `requests[]` entries return a 400 with `Batch payments are not supported for TRON networks.`. Submit single-recipient payments instead. ### Improvements * **Paid / received / excess amount fields** on the Payment model and webhook payloads. `totalAmountPaid` reflects the cumulative amount across multiple inbound transactions; the API now also returns `excessAmount` when a payer overpays. ### New Features * **`POST /v2/secure-payments/{token}/calldata`** generates executable transaction calldata for a secure payment after the payer has selected a chain/token. Replaces previous payment-intent flow for the calldata path. ## Endpoint coverage today The v2 API exposes endpoints across these resource groups (see [Endpoints (V2)](/api-reference/endpoints-overview) for the OpenAPI-driven reference): * **Requests** (`/v2/request`) — create, query, update, get calldata * **Payments** (`/v2/payments`) — search and reconcile * **Payouts** (`/v2/payouts`, `/v2/payouts/batch`, `/v2/payouts/recurring`) — single, batch (EVM-only), recurring * **Secure Payments** (`/v2/secure-payments`, `/v2/secure-payments/payouts`, `/v2/secure-payments/multicall-payouts`) — incoming, outgoing, and multicall hosted links * **Orchestrators** (`/v2/orchestrators`) — link client IDs and configure orchestrator fees (orchestrator-key auth) * **Currencies** (`/v2/currencies`) — supported tokens and conversion routes * **Commerce Payments** (`/v2/commerce-payments`) — authorize/capture/void/refund (preview) * **Payer** (`/v2/payer`) — crypto-to-fiat KYC and bank account flows * **Journey** (`/v2/journey`) — multi-hop payment tracing * **Client IDs** (`/v2/client-ids`) — see also auth API at `/v1/client-ids` For client ID, webhook, and payee destination management, see the [Auth API](https://auth.request.network/open-api). ## Getting help * 📚 [API Reference](/api-reference/authentication) * 💬 [Discord Community](https://request.network/discord) # Lifecycle of a Request Source: https://docs.request.network/resources/lifecycle-of-a-request The typical lifecycle of a request is as follows: Typical Lifecycle of a Request ## Create a request * The payer or payee signs the request which contains the payee, payer, currency, amount, payment details, and arbitrary content data. * The request can be optionally encrypted such that only the payee, payer, and approved 3rd parties can view the request contents. * The request is persisted in IPFS. * The IPFS Content-addressable ID (CID) is stored in a smart contract on Gnosis chain Requests are *created* by storing their CIDs on Gnosis, but this doesn't mean *payment* must occur on Gnosis. *Payment* can occur on any of the supported chains — EVM-compatible chains and Tron. ## Update a request * The payee can optionally cancel the request or increase/decrease the expected amount. * The payer can optionally accept the request, indicating that they intend to pay it. * Both payee and payer can add third-party stakeholders if the request is encrypted. ## Pay a request * The payer derives a paymentReference from the request contents. * The payer calls a function on the payment network smart contract, passing in the token address, to address, amount, and paymentReference. * An event is emitted containing the token address, to address, amount, and paymentReference. Most requests are "reference-based" meaning that a paymentReference derived from the request contents is logged on-chain via a smart contract that emits an event. Nothing gets written back to IPFS when paying a "reference-based" request. The exception is when paying a "declarative" request, in which case, data *is* written back to IPFS. This includes when the payer declares that the payment was sent and the payee declares that the payment was received. ## Retrieve a request / Detect a payment * The event is indexed by the payments subgraph * An app can retrieve the request contents from IPFS and calculate the balance based on events from the payments subgraph. The request balance is calculated by adding up all the on-chain payment events with the same paymentReference. Partial payments are possible. All of these steps are facilitated by the Request Network API. See the [Quickstart](/use-cases/quickstart) for the end-to-end flow. # Supported Chains and Currencies Source: https://docs.request.network/resources/supported-chains-and-currencies Supported chains and currency coverage across Request Network API payment types ## Request Network API Supported Chains and Currencies Request Network supports payment destinations on **8 networks** — 7 EVM chains and **Tron**. EVM and Tron are co-equal first-class networks for sign-in, payment destinations, payment links, and single payouts. The one current asymmetry: **batch payments (incoming or outgoing) are EVM-only** — the API rejects Tron batch requests. ## Supported networks ### Mainnet | Network | Chain ID | Network ID | USDC | USDT | Notes | | --------------- | ----------- | -------------- | ---- | ---- | ------------------------------------- | | Ethereum | `1` | `mainnet` | ✓ | ✓ | | | Arbitrum One | `42161` | `arbitrum-one` | ✓ | ✓ | USDT0 payment currency also available | | Optimism | `10` | `optimism` | ✓ | ✓ | USDT0 payment currency also available | | Base | `8453` | `base` | ✓ | ✓ | | | Polygon | `137` | `matic` | ✓ | ✓ | USDT0 payment currency also available | | BNB Smart Chain | `56` | `bsc` | ✓ | ✓ | | | **Tron** | `728126428` | `tron` | ✓ | ✓ | TRC-20 tokens, `T...` addresses | ### Testnet | Network | Chain ID | Network ID | Tokens | | ------- | ---------- | ---------- | --------------- | | Sepolia | `11155111` | `sepolia` | FAU, USDC, USDT | All token addresses above are mainnet contracts. Use them as the `tokenAddress` when creating a payment destination via the [Auth API](https://auth.request.network/open-api/#tag/payee-destination). **USDT0** is supported as a payment currency (an alias of USDT) on **Arbitrum One**, **Optimism**, and **Polygon** — for example `USDT0-arbitrum-one`, `USDT0-optimism`, `USDT0-matic`. It is also part of the cross-chain currency set (USDC, USDT, USDT0). USDT0 is a payment/cross-chain currency, not a separate payee-destination token — receiving destinations are registered in USDC or USDT. ## Feature support per chain | Feature | EVM (7 chains) | Tron | | -------------------------------------- | -------------- | ---- | | Wallet sign-in (Dashboard) | ✓ | ✓ | | Payment destinations (ERC-7828) | ✓ | ✓ | | Single incoming payments | ✓ | ✓ | | **Batch incoming payments** | ✓ | ✗ | | Single outgoing payouts | ✓ | ✓ | | **Batch outgoing payouts** | ✓ | ✗ | | Conversion payments (fiat-denominated) | ✓ | ✓ | | Cross-chain swap-to-pay (Li.Fi) | ✓ | ✓ | | Recurring payments | ✓ | ✓ | Batch payments (one transaction paying multiple recipients, or one signed batch payout) are supported on EVM only. Submitting a batch with Tron payees returns a 400 with: `Batch payments are not supported for TRON networks. Please submit individual payment requests.` ## Tron specifics * **Mainnet only** — no Tron testnet support * **Native token:** TRX (used for energy/bandwidth fees) * **Stablecoins:** USDT (`TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t`), USDC (`TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8`) * **Address format:** Base58, prefix `T` * **Wallets:** TronLink, Guarda, Trust, WalletConnect-Tron Tron wallet addresses are not interchangeable with EVM addresses. The API uses ERC-7828 `humanReadableInteropAddress` to disambiguate — Tron destinations resolve under the `eip155:728126428` namespace. ## ERC20 and Native Payments Supported Currencies For ERC20 and native payments, Request Network exposes a broader catalog of 500+ tokens (the Request Network Token List). The destinations and secure payment flows above use the curated subset on the canonical 8 networks. Access the full token catalog with token IDs, symbols, and network mapping. The token list is a superset and may include tokens on chains outside the destination flow. For payment destinations and secure payment links, use the 8 networks listed above. ## Conversion Payments Supported Currencies For Conversion Payments, supported **invoice currencies** include: * USD * EUR * CNY * GBP * JPY For Conversion Payments, supported **payment currencies** include: * USDC * USDT * DAI * FAU (Sepolia) To fetch supported payment currencies for an invoice currency: Get payment currency options available for a given invoice currency. ## Crosschain Payments Supported Currencies View the supported chain/currency matrix for crosschain payments. ## Crypto-to-fiat Payments Supported Currencies View supported chains and currencies for crypto-to-fiat flows. ## Currencies API Endpoints The Currencies API lets you discover available currencies and conversion routes. ### Key Features * **Payment request integration**: get currency IDs required for request creation * **Payment integration**: retrieve token/network metadata for settlement logic * **Currency validation**: verify supported currency IDs before creating requests * **Multi-chain support**: discover tokens across supported chains ### Currency Object Fields Typical fields include: * `id` (for example `USDC-mainnet`) * `name` * `symbol` * `decimals` * `address` * `network` * `type` * `chainId` ## Currency Codes and Examples ```javascript Native and ERC20 examples theme={null} "ETH-mainnet" "USDC-mainnet" "USDC-base" "USDT-arbitrum-one" "USDT0-arbitrum-one" "USDT0-optimism" "USDT0-matic" ``` ```javascript Tron examples theme={null} "USDT-tron" "USDC-tron" ``` ```javascript Fiat invoice currency examples theme={null} "USD" "EUR" "GBP" ``` ## API Query Examples ```bash Get all currencies theme={null} curl -X GET 'https://api.request.network/v2/currencies' \ -H 'x-api-key: YOUR_API_KEY' ``` ```bash Filter by network and symbol theme={null} curl -X GET 'https://api.request.network/v2/currencies?network=tron&symbol=USDT&firstOnly=true' \ -H 'x-api-key: YOUR_API_KEY' ``` ```bash Get conversion routes for invoice currency theme={null} curl -X GET 'https://api.request.network/v2/currencies/USD/conversion-routes' \ -H 'x-api-key: YOUR_API_KEY' ``` ## Endpoints List currencies and filter by network, symbol, or id. List payment currencies available for a given invoice currency. ## Related Pages Full token catalog with IDs and chain mapping. Learn where each currency flow is used. Build with supported currencies and chains. # Request Network Token List Source: https://docs.request.network/resources/token-list A curated, standardized list of tokens supported by Request Network products — address, symbol, name, decimals, and chainId for each. ## Usage Access the latest published token list JSON. You can fetch the token list directly in your application: ```typescript theme={null} const tokenList = await fetch( "https://requestnetwork.github.io/request-token-list/latest.json" ).then((res) => res.json()); ``` ## Token List vs Currencies API Use the token list for static token metadata and broad catalog browsing. Use the Currencies API when you need runtime filtering by network/symbol/id or conversion-route discovery. Query currencies with optional filters (`network`, `symbol`, `id`). Fetch payment currencies available for a specific invoice currency. ## Token List Structure Each token in the list contains the following information: ```json theme={null} { "id": "USDC-mainnet", "name": "USD Coin", "symbol": "USDC", "decimals": 6, "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "network": "mainnet", "type": "ERC20", "hash": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "chainId": 1 } ``` Tron tokens use the same shape — `network: "tron"`, `chainId: 728126428`, `type: "TRC20"`, and base58 addresses (`T...`): ```json theme={null} { "id": "USDT-tron", "name": "Tether USD", "symbol": "USDT", "decimals": 6, "address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "network": "tron", "type": "TRC20", "chainId": 728126428 } ``` | Field | Description | | ---------- | ------------------------------------------------------------------------------------------------------------------------------ | | `id` | Unique identifier, typically `SYMBOL-network` (e.g., `USDC-mainnet`) | | `name` | Human-readable token name | | `symbol` | Token symbol | | `decimals` | Number of decimal places | | `address` | Token contract address | | `network` | Network name. Supported destination networks: `mainnet`, `arbitrum-one`, `optimism`, `base`, `matic`, `bsc`, `tron`, `sepolia` | | `type` | Currency type (e.g., `ERC20`, `ETH`, `ISO4217`) | | `hash` | For ERC20 tokens, same as `address`. For native tokens, a calculated hash. | | `chainId` | Chain ID of the network | ## Adding a New Token We welcome community contributions! To add a new token to the list: Fork the [request-token-list](https://github.com/RequestNetwork/request-token-list) repository on Github Add your token information to `tokens/token-list.json` Make sure your token meets our requirements (see [CONTRIBUTING.md](https://github.com/RequestNetwork/request-token-list/blob/main/CONTRIBUTING.md)) Run tests locally: `npm test` Create a Pull Request # Dashboard Source: https://docs.request.network/tools/dashboard dashboard.request.network — sign in with your wallet to manage payment destinations, Client IDs, incoming payment requests, and outgoing payouts. The **Request Network Dashboard** ([dashboard.request.network](https://dashboard.request.network)) is the no-code home for everything you do with Request Network as a receiver: setting up where you want to be paid, generating API credentials, sending and receiving payment links. Sign in with an EVM or Tron wallet — no email, no password. Dashboard home with active payment destination ## Sign-in The login screen offers EVM and Tron as parallel tabs. Connect MetaMask, Coinbase Wallet, or WalletConnect. Sign the SIWE message when prompted to start your session. Dashboard sign-in (EVM) Connect TronLink, Guarda, Trust, or WalletConnect-Tron. Sign the message when prompted to start your session. Dashboard sign-in (Tron) Your session is a wallet-bound cookie with a 15-minute idle timeout, shared across `dashboard.request.network`, `auth.request.network`, and `api.request.network` — sign in once and you're authorised across all three. ## Payment destinations A **payment destination** registers where you want to receive payments. It's a single chain × token × wallet combo, encoded as an [ERC-7828 humanReadableInteropAddress](/api-features/payee-destinations). Create payment destination dialog Supported chain × token combinations in the Dashboard UI: | Chain | Tokens shown in the UI | | -------------------------------------- | ---------------------- | | Ethereum, Optimism, Base, Polygon, BSC | USDC, USDT | | Arbitrum One | USDC, USDT0 | | **Tron** | USDT (TRC-20) | | Sepolia (testnet) | FAU, USDC, USDT | The underlying API also supports USDC on Tron and additional tokens on some chains; the Dashboard UI exposes the curated set above. To use a token not in the UI, create the destination via the [Auth API](https://auth.request.network/open-api/#tag/payee-destination) directly. You can switch your active destination at any time via **Manage Destination → Update Receiving Route**. ## Client ID management Client IDs are the credential the Dashboard (and your own apps) use to call the API on your behalf. The Dashboard's **Generate New Client ID** dialog collects: * A **label** (display name) * **Allowed Domains** — one or more HTTPS origins (leave empty for backend-only Client IDs) Manage destination and Client IDs Generate Client ID dialog Advanced fields — `feePercentage`, `feeAddress`, `operatorWalletAddress`, `payeeDestinationId`, `defaultPreApprovalExpiry`, `defaultAuthorizationExpiry` — are only configurable via the [Auth API](https://auth.request.network/open-api/#tag/client-ids). Existing Client IDs created from the Dashboard can be edited (allowed domains) or revoked from the same screen. For the full field reference, see [Client ID Management](/api-features/client-id-management). ## Webhooks The Dashboard manages webhooks from the **Webhooks** section on the **Manage Destination** page. Each webhook endpoint is tied to a specific Client ID. From this section you can: * **Add a webhook** — enter the endpoint URL and choose the Client ID it belongs to. The Dashboard shows the signing secret once, immediately after creation — save it, since it isn't displayed in full again afterward. * **Enable or disable** an existing webhook with a toggle, without deleting it. * **Delete** a webhook endpoint. See [Webhooks](/api-features/webhooks-events) for the payload format and how to verify signatures. Webhooks can also be managed programmatically via the [Auth API](https://auth.request.network/open-api/#tag/webhook). ## Get Paid The **Get Paid** tab is your incoming-payments view. Create a request — amount, currency, optional reference and payer identifier — and the Dashboard generates a `pay.request.network` link to share. Get Paid list Each request shows status (Pending / Paid), the on-chain transaction hash once paid, and a copyable payment URL. Works identically for EVM and Tron destinations. Request detail with payment link ## Pay (outgoing) The **Pay** tab is your outgoing-payments view. Create a single-recipient payout, sign the resulting transaction, and the Dashboard tracks settlement. Pay list (outgoing payouts) The Dashboard supports single-recipient payouts on both EVM and Tron, and lets you select multiple pending payouts and settle them together as a [multicall payout](/api-features/payouts#multicall-payouts) — reviewing every recipient before signing. For fully programmatic batch payouts, you can also call the API directly — see [Batch payouts](/use-cases/batch-payouts). ## What the Dashboard does *not* do * It is not an admin / merchant control panel for downstream platforms — it's a user dashboard for your own wallet. * It does not host the payer-side checkout — that's [Secure Payment](/tools/secure-payments) at `pay.request.network`. ## Open the Dashboard Sign in with your EVM or Tron wallet to start. ## Related End-to-end walkthrough using the Dashboard. The payer-facing companion at pay.request.network. # Secure Payment Source: https://docs.request.network/tools/secure-payments pay.request.network — the hosted payment link app payers open to settle a Request Network payment. Defense-in-depth, multi-chain, EVM + Tron. **Secure Payment** ([pay.request.network](https://pay.request.network)) is the hosted page your payers land on when they click a payment link. It's a defense-in-depth checkout: trusted contract addresses are hardcoded, every transaction is decoded and verified client-side, and the payer can pay from any of 8 supported chains in any supported wallet. Secure Payment options view ## Why it exists The Secure Payment page is intentionally separated from the merchant frontend so a compromised merchant site or API endpoint can't trick a payer into signing the wrong transaction. The page: * Loads payment metadata from the Request API using a one-time token * Validates every contract address against a hardcoded trusted set * Decodes the transaction calldata and shows the payer what they're about to sign * Resolves payee addresses to ENS where available * Refuses to broadcast if anything looks off ## What the payer sees The payer clicks a `pay.request.network/?token=...` URL you generated via [`POST /v2/secure-payments`](/api-reference/secure-payments). The page loads payment details (amount, recipient, reference). The connect modal lists EVM and Tron wallets side by side. Connect wallet modal — EVM When the destination is on Tron, the same modal shows Tron wallets (TronLink, Guarda, Trust, WalletConnect-Tron) instead of the EVM list — try a [Tron-destination payment link](https://pay.request.network) to see it. The page reads the connected wallet's balances across supported chains and surfaces every chain × token combo with a sufficient balance. The same-chain options view is shown at the top of this page; for cross-chain payments the layout is identical except the banner reads "You'll be paying across chain via Li.Fi" (in pink) instead of "You'll be paying on the same chain as your recipient" (in slate). For ERC-20s, an approval transaction precedes the payment. The page shows a stepper (connect → confirm → setup → pay) and an optional **Advanced** view that decodes the calldata for inspection. Advanced view with decoded calldata The success screen shows the source and destination transaction hashes, with explorer links per chain. For cross-chain payments, a Li.Fi badge is displayed. If you set a `redirectUrl` when creating the payment link, an extra button appears at the bottom of the screen — defaulting to **"Go Back and Close"**, or whatever you set as `redirectLabel` — that takes the payer back to your site. Payment success screen ## Wallet support The connect modal offers a curated wallet list — only the wallets explicitly registered in the app are surfaced; unrecognised injected providers are filtered out (this is what filters TronLink's EVM injection out of the EVM list). | EVM wallets | Tron wallets | | --------------- | ------------------ | | MetaMask | TronLink | | Coinbase Wallet | Guarda | | WalletConnect | Trust Wallet | | Ledger | WalletConnect-Tron | | Phantom | | | Rabby | | EVM and Tron are presented as co-equal columns. The first 5 wallets render up-front; the rest appear behind a "show more" toggle. TronLink also injects an EVM provider that converts Tron addresses to EVM-style. Secure Payment **filters this out** of the EVM wallet list because it would mis-route Tron payments — Tron is handled exclusively via the TronWeb integration. ## Cross-chain via Li.Fi When the payer's selected source chain differs from the destination chain, Secure Payment routes through Li.Fi. The UI displays: * "You'll be paying across chain via [Li.Fi](https://li.fi/)" on the options view * The bridge fee in the cost breakdown * A Li.Fi badge on the success screen Supported source chains for Li.Fi swap-to-pay: Ethereum, Arbitrum One, Optimism, Base, Polygon, BSC, Tron. ## Tron support Tron is fully supported as both a **source** and a **destination** for single-recipient payments: | Direction | Same-chain | Cross-chain (Li.Fi) | | ----------------------- | ---------- | ------------------- | | Tron → Tron (USDT/USDC) | ✓ | — | | EVM → Tron | — | ✓ | | Tron → EVM | — | ✓ | **Multi-recipient Tron Secure Payments are not supported.** A Secure Payment link with multiple `requests[]` entries pointing at Tron destinations is rejected at creation time with a 400. EVM batches up to 200 payees per link work as expected. ## Multicall payouts When several outgoing payout links are combined into a [multicall payout](/api-features/payouts#multicall-payouts), the payer opens one Secure Payment link that lists every recipient and settles the whole bundle in a single flow. The page shows a per-recipient breakdown (including each payout's reference), totals, and — for cross-chain bundles — the Li.Fi routing, then a single confirm step. Bundles settle same-chain, across chains, or as a Tron batch depending on the recipients and the payer's chosen source. ## Compliance-gated payments (KYT) When the merchant has enabled a Know Your Transaction policy on the receiving destination, the secure payment app screens the connected wallet before showing payment options. Wallets that fail the screen see a policy-failure view and cannot reach the sign step. Optional privacy flags can also keep the payment amount and the payee address masked until the screening passes. See [Compliance-gated payments](/use-cases/compliance-gated-payments) for the merchant-side configuration and [Hypernative Standard Screening Policy](/use-cases/hypernative-standard-screening-policy) for the default KYT categories and thresholds. ## Smart accounts & gasless approvals (EVM) For supported EVM payments, Secure Payment can route through an [ERC-4337](https://www.alchemy.com/overviews/what-is-account-abstraction) smart account derived from the payer's connected wallet and bundled via Pimlico, so the approval, funding, and payment execute as a single operation. The flow is invisible to the payer beyond the signature step. On this path, ERC-20 approvals are **gasless** for tokens that support [EIP-2612 `permit`](/api-features/secure-payment-pages#gasless-token-approvals-eip-2612) (such as USDC) — the payer signs instead of broadcasting a separate approval transaction. Tokens without permit (such as USDT) use a one-time on-chain approval. See [Secure payment pages](/api-features/secure-payment-pages#smart-account-payments) for the full spec. Smart-account routing is EVM-only; Tron payments use the standard TronWeb path. To pay from an existing **Gnosis Safe multisig**, use the [Safe multisig payments](/api-features/safe-multisig-payments) API flow. ## Error states The Secure Payment app handles a few well-defined error states with clear copy: | State | Message | | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | | Link expired (>7 days, or already paid expired) | "You are coming too late. Please contact the recipient to send you a new payment link." | | Token not found / invalid URL | "This URL is invalid. Please check the link and try again, or contact the recipient of this payment." | | Already paid | "If that is not the case, please check the link and try again, or contact the recipient of this payment." | | Calldata validation fails | "There is an issue with this payment call data. Please contact the recipient to send you a new payment link." | ## What Secure Payment does *not* do * It does **not** persist transaction history beyond the immediate confirmation screen — explorers and webhook events are the source of truth. * It does **not** provide a payee-side dashboard — that's [Dashboard](/tools/dashboard) at `dashboard.request.network`. * It does **not** accept payments without a backing Request — the link must originate from `POST /v2/secure-payments`. ## Open Secure Payment Try a payment link in the live app. ## Related The payer experience and how it routes across chains. The API call that mints these links. Full request/response schemas. The payee-side companion. # Batch payouts (EVM) Source: https://docs.request.network/use-cases/batch-payouts Pay many recipients in one signed transaction. Marketplaces paying vendors, agencies paying contractors, refund campaigns, payroll-adjacent flows. ## What you'll build A workflow for paying many recipients at once with a single payer signature — atomic, on EVM. Either hand the payer a hosted batch URL on `pay.request.network` (recommended), or pay programmatically by getting calldata and broadcasting it yourself. **Audience:** marketplaces paying out sellers, agencies paying contractors, refund campaigns, payroll-adjacent flows, any scenario where you'd otherwise sign N transactions back-to-back. **EVM-only.** Tron does not support batch payments. Submitting a batch with Tron destinations returns a 400 with: `Batch payments are not supported for TRON networks. Please submit individual payment requests.` For Tron recipients, send single payouts in a loop using [`POST /v2/payouts`](/api-features/payouts) or [`POST /v2/secure-payments/payouts`](/api-reference/secure-payments#post-v2secure-paymentspayouts). ## Two modes | Mode | Endpoint | What you get back | | ----------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | **Hosted Batch Link** (recommended) | [`POST /v2/secure-payments`](/api-reference/secure-payments) with multiple `requests[]` | A `pay.request.network` URL the payer opens, signs once, and pays everyone | | **Direct Execution** (advanced) | [`POST /v2/payouts/batch`](/api-features/payouts#batch-payout) | Approval calldata + a single batch payment transaction you broadcast from your wallet | Pick **hosted batch link** for most scenarios — the payer reviews recipients and the transaction on the Secure Payment Page before signing. Pick **direct execution** only when your backend already signs payouts in fully automated environments with appropriate security controls. **Bundling payouts across chains or on Tron?** [Multicall payouts](/api-features/payouts#multicall-payouts) combine multiple existing payout links into one link with cross-chain (Li.Fi) routing or Tron batch execution — a separate mechanism from the same-network batch endpoints below. See the [Secure Payments reference](/api-reference/secure-payments#post-v2secure-paymentsmulticall-payouts). ## Prerequisites Same as the [Quickstart](/use-cases/quickstart) — you need a Client ID. Webhooks are optional but recommended so you get notified when each leg of the batch settles. ## Mode 1 — Hosted Batch Link (Recommended & Safer Approach) The Secure Payment Page, hosted on a decentralized protocol, allows operators to review and verify transactions before execution, reducing operational risk. The Hosted Batch Link supports **cross-chain execution from a single wallet connection**. Users can pay batches across the top supported EVM chains without switching environments or executing separate transactions per chain. The Secure Payment Page handles routing and cross-chain execution automatically, allowing operators to initiate payouts from one connected destination while supporting payments across multiple EVM networks. Mint a hosted link with multiple `requests[]` and share it with whoever signs payouts: ```typescript theme={null} const response = await fetch( "https://api.request.network/v2/secure-payments", { method: "POST", headers: { "Content-Type": "application/json", "x-client-id": process.env.RN_CLIENT_ID!, }, body: JSON.stringify({ requests: contractors.map((c) => ({ destinationId: c.destinationId, amount: c.amount, })), reference: `payroll-${period}`, }), }, ); const { securePaymentUrl } = await response.json(); // Send securePaymentUrl to whoever signs payouts ``` The Secure Payment Page shows the full payee list, total amount, and a single approval-then-pay flow. The payer signs once. ## Mode 2 — Direct Execution (Advanced & Self-Secure) Direct execution removes the transaction review layer and may increase operational risk in case of calldata manipulation or wallet compromise. Recommended only for advanced automated environments with appropriate security controls. Direct execution currently supports **same-chain batch payouts only**. For example, Ethereum-to-Ethereum payouts require execution directly on Ethereum. To execute payouts across multiple chains, operators must execute one transaction per chain and interact with the corresponding contract deployment on each network individually. `POST /v2/payouts/batch` accepts up to 200 payment requests. All must be on the same EVM network. Mixed payment types (ERC-20 + native) are allowed within the batch. ```typescript theme={null} const response = await fetch( "https://api.request.network/v2/payouts/batch", { method: "POST", headers: { "Content-Type": "application/json", "x-client-id": process.env.RN_CLIENT_ID!, }, body: JSON.stringify({ requests: [ { payee: "0xb07d2398d2004378cad234da0ef14f1c94a530e4", amount: "50", invoiceCurrency: "USD", paymentCurrency: "USDC-base", }, { payee: "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", amount: "75", invoiceCurrency: "USD", paymentCurrency: "USDC-base", }, // ...up to 200 recipients ], payer: process.env.PAYOUT_WALLET!, }), }, ); const { ERC20ApprovalTransactions, batchPaymentTransaction } = await response.json(); ``` The response contains: * `ERC20ApprovalTransactions[]` — zero or more approval transactions you must broadcast first (one per token) * `batchPaymentTransaction` — the single transaction that settles all payments at once Sign and broadcast in order. If the wallet already has sufficient allowance, the approvals array is empty. ```typescript theme={null} import { ethers } from "ethers"; const signer = wallet.connect(provider); for (const approvalTx of ERC20ApprovalTransactions) { const tx = await signer.sendTransaction(approvalTx); await tx.wait(); } const batchTx = await signer.sendTransaction(batchPaymentTransaction); await batchTx.wait(); ``` ## Wallet infrastructure compatibility The hosted Secure Payment Page and Dashboard execute through the payer's connected wallet (including an automatic [smart account](/api-features/secure-payment-pages#smart-account-payments) on supported EVM chains), so you cannot connect an existing Gnosis Safe multisig directly in the UI. To pay from a Safe multisig, use the API: request Safe-ready calldata and track settlement with the [Safe multisig payments](/api-features/safe-multisig-payments) flow, executing the transaction on your own Safe. For broader corporate treasury and operational wallet infrastructure, Request Network is also compatible with WalletConnect-based vault providers such as Utila, DFNS, or Fireblocks. ## Real-world examples ### Marketplace seller payouts ```typescript theme={null} // Pay every seller their week's earnings in one tx const sellers = await db.sellers.findWithUnpaidBalance(); await fetch("https://api.request.network/v2/payouts/batch", { method: "POST", headers: { "Content-Type": "application/json", "x-client-id": clientId }, body: JSON.stringify({ requests: sellers.map((s) => ({ payee: s.walletAddress, amount: s.balanceUsdc.toString(), invoiceCurrency: "USD", paymentCurrency: "USDC-base", })), payer: marketplaceWallet, }), }); ``` ### Refund campaign ```typescript theme={null} // Refund 30 customers from a botched promo await fetch("https://api.request.network/v2/payouts/batch", { method: "POST", headers: { "Content-Type": "application/json", "x-client-id": clientId }, body: JSON.stringify({ requests: refundList.map((r) => ({ payee: r.walletAddress, amount: r.refundAmount, invoiceCurrency: "USD", paymentCurrency: "USDC-arbitrum-one", })), payer: refundWallet, }), }); ``` ### Contractor payroll ```typescript theme={null} // Run end-of-month payroll for the team await fetch("https://api.request.network/v2/payouts/batch", { method: "POST", headers: { "Content-Type": "application/json", "x-client-id": clientId }, body: JSON.stringify({ requests: contractors.map((c) => ({ payee: c.walletAddress, amount: c.monthlyRate.toString(), invoiceCurrency: "USD", paymentCurrency: "USDC-optimism", })), payer: payrollWallet, }), }); ``` ## Webhook events Each request inside the batch produces its own `payment.confirmed` event when settled. For a batch of 50, expect 50 webhook deliveries — each carries its own `requestId` and `paymentReference`. Use the `reference` you set on creation (e.g. `"payroll-2026-04"`) to group them on your side. See [Webhook reconciliation](/use-cases/webhook-reconciliation) for the full handler pattern. ## Limits * **Up to 200 recipients per batch transaction**, depending on payment type complexity * **Same network only** — all `requests[]` must target the same chain. No mixing Base + Arbitrum in one batch. * **EVM only** — see warning at top * **Mixed currency types within a network are OK** — ERC-20 + native ETH/MATIC/BNB in one batch is supported ## Related Single, batch, and recurring payout endpoints. Wire batch confirmations into your accounting / DB. # Compliance-gated payments (KYT) Source: https://docs.request.network/use-cases/compliance-gated-payments Screen every payer wallet for sanctions and risk before accepting a payment. Built into payment destinations — opt-in per receiving route. ## What you'll build A receiving setup where every wallet that tries to pay you is screened against sanctions and risk lists before the payment can settle. If a wallet fails screening, the payment is blocked — the payer never reaches the sign step. You configure this **per payment destination**, so you can keep some flows compliance-gated and others permissive. **Audience:** regulated fintech, marketplaces with compliance obligations, B2B platforms with KYC/AML requirements, any merchant who needs to refuse payments from sanctioned or high-risk addresses. This is a **Know Your Transaction (KYT)** policy, not a Know Your Customer (KYC) flow. There's no document upload or identity verification on the payer side — just a wallet-address screening. ## How it works When you create a payment destination via the [Auth API](https://auth.request.network/open-api/#tag/payee-destination), you can attach a **payment access policy** that turns KYT screening on for that destination: Before the payment options view loads, Request Network's compliance gate screens the connected wallet (and, for smart-account payments, the parent EOA as well). The payment continues normally. The secure payment page shows a policy-failure view and the payer cannot reach the sign step. You can also choose how much information is visible to the payer until the wallet has passed screening — see [Privacy options](#privacy-options) below. ## Configure a KYT-gated destination Pass an `accessPolicy` object when creating a payee destination: ```bash theme={null} curl -X POST "https://auth.request.network/v1/payee-destination" \ -H "Cookie: session=YOUR_SESSION_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "tokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "chainId": 1, "accessPolicy": { "mode": "kyt_all_wallets", "screeningProvider": "hypernative", "hideUntilApproved": true, "hidePayeeAddress": true } }' ``` | Field | Type | Description | | ------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `mode` | `"off"` \| `"kyt_all_wallets"` | `off` is the default (no screening). `kyt_all_wallets` screens every payer wallet that connects. | | `screeningProvider` | `"hypernative"` \| `"merklescience"` | KYT provider used for screening. **Required when `mode` is `kyt_all_wallets`** (omit it when `mode` is `off`). Choose `hypernative` or `merklescience` (Merkle Science, a multi-chain option). | | `hideUntilApproved` | `boolean` | When `true`, the payment metadata (amount, currency, recipient) is hidden until the payer's wallet has passed screening. | | `hidePayeeAddress` | `boolean` | When `true`, the payee wallet address is masked throughout the payer flow — no ENS resolution, no copy button, no explorer link. | Both privacy flags are independent of `mode` — you can show payment details upfront and still gate the actual payment behind screening, or hide everything until the gate clears. Existing payment destinations without an `accessPolicy` keep behaving exactly as before — there is no implicit screening. KYT only activates when you opt a destination in. ## Privacy options `hideUntilApproved` and `hidePayeeAddress` solve two different concerns: * **`hideUntilApproved`** — useful when the payment terms themselves are sensitive (commercial pricing, B2B contracts). The payer connects a wallet, gets screened, and only sees the amount/recipient if their wallet clears. * **`hidePayeeAddress`** — useful when you want to keep your receiving wallet from being scraped and re-used by the payer outside this payment flow. The payer can still pay (the secure payment app builds the transaction with the real address), but they don't see the address copy/explorer-link affordances. You can combine both for the strictest setup, or use either independently. ## What payers experience | Scenario | Payer view | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | Wallet passes screening | Standard payment flow — connect, pick chain/token, sign. | | Wallet fails screening | Policy-failure view; sign button is disabled. The payer is told the payment cannot proceed and to contact you for an alternative. | | Screening in progress | Brief loading state between wallet connect and payment options view. | For **smart-account payments** on EVM, the gate screens both the connected EOA and the smart-account wallet — both must pass before payment proceeds. ## Update or remove a policy Change the policy on an existing destination by re-issuing the create call (the active destination is updated in place) or by calling `PUT /v1/payee-destination`: ```bash theme={null} curl -X PUT "https://auth.request.network/v1/payee-destination" \ -H "Cookie: session=YOUR_SESSION_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "tokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "chainId": 1, "accessPolicy": { "mode": "off", "hideUntilApproved": false, "hidePayeeAddress": false } }' ``` Setting `mode: "off"` reverts the destination to standard, unscreened behavior. Existing payment links you've already created against that destination will use whatever policy is in effect at the moment a payer connects. ## When KYT screening doesn't replace your own checks KYT screens individual **wallet addresses** against external sanctions and risk lists. It does not: * Verify payer identity (use KYC tooling for that). * Prove origin of funds — addresses can pass screening but still be linked to off-platform behavior you'd flag yourself. * Replace transaction-monitoring on your side after the fact. Treat KYT as a first-line filter that blocks the most obvious cases, layered with whatever else your compliance program requires. ## Hypernative Standard policy Request Network offers two KYT screening providers: **Hypernative** and **Merkle Science** (a multi-chain option). When `mode` is `kyt_all_wallets`, you must set `screeningProvider` to one of them — there is no default. The **Hypernative Standard** policy defines the high-risk categories and related exposure thresholds applied when you screen with Hypernative. See [Hypernative Standard Screening Policy](/use-cases/hypernative-standard-screening-policy) for the full category list, exposure thresholds, and limitations. ## Related Review the default KYT policy categories, thresholds, and limitations. Full payee-destination reference, including the `accessPolicy` field. # Hypernative KYT Policy Source: https://docs.request.network/use-cases/hypernative-standard-screening-policy Understand the default Hypernative wallet-screening policy used for Request Network KYT checks. ## Hypernative Standard Screening Policy Request Network integrates with [Hypernative](https://www.hypernative.io) to provide optional blockchain wallet screening for incoming payments and outgoing payouts. When the “Hypernative Standard” policy is enabled, payer wallets and recipient wallets are screened against Hypernative’s risk intelligence database across supported blockchains before a transaction is authorized. The purpose of this screening is to help reduce exposure to wallets associated with sanctions, illicit finance activity, scams, ransomware, mixers, darknet activity, terrorism financing, and other high-risk categories. Under this policy: * Incoming payer wallets identified as high risk may be prevented from transacting with you. * Outgoing payouts to wallets identified as high risk may also be blocked when payout screening is enabled. * Screening can be enabled at the destination level and, where supported, overridden at transaction creation level. Users may choose to opt out of screening checks. By disabling or bypassing screening, the user acknowledges and accepts the risks associated with transacting with unscreened wallets. The Hypernative Standard policy uses market-standard rules and exposure thresholds maintained and periodically updated by Hypernative. These rules are intended to provide a baseline level of automated wallet risk protection only. ### Direct High-Risk Categories Wallets directly associated with the following categories are classified as high risk: * Sanctions * Phishing * Scam * Attacker Wallets * Mixing Services * Sanctioned Jurisdiction Exchanges * Terrorism Financing * CSAM * Darknet Marketplaces * Drugs Trade * Weapon Trade * Illicit Goods and Services * Special Measures * Ransomware ### Related Exposure Thresholds Indirect exposure may also trigger a high-risk classification when the following thresholds are exceeded: | Category | Threshold | | --------------------------------- | ------------------------------------- | | Sanctions | > \$10 exposure | | Sanctioned Jurisdiction Exchanges | > \$10 exposure | | Terrorism Financing | > \$10 exposure | | CSAM | > \$10 exposure | | Darknet Marketplaces | > \$10 exposure | | Drugs Trade | > \$10 exposure | | Weapon Trade | > \$10 exposure | | Illicit Goods and Services | > \$10 exposure | | Special Measures | > \$10 exposure | | Ransomware | > \$10 exposure | | Scam | > 5% incoming exposure | | Attacker Wallets | > 10% exposure | | Mixing Services | > 30% exposure or > \$10,000 exposure | Neither Request Network nor Hypernative provides compliance, legal, regulatory, or risk management services through this feature, and screening results do not constitute legal advice, a guarantee of wallet safety, or a determination that funds are free from illicit exposure or regulatory risk. Users remain solely responsible for assessing the suitability of transactions and complying with applicable laws, sanctions programs, AML obligations, and internal compliance policies. Request Network acts solely as a technology provider facilitating access to third-party screening infrastructure. Hypernative acts solely as a screening technology and data provider. Users requiring tailored compliance rules should [contact the Request Network team](https://request.network) to discuss customized screening configurations. # Multi-chain checkout Source: https://docs.request.network/use-cases/multi-chain-checkout Let payers pay from any chain and any token. Receive on your preferred chain. Cross-chain swaps are routed through Li.Fi. ## Why multi-chain matters When you create a Secure Payment link, you decide on the **destination** — the chain and token you want to receive on. The payer doesn't have to match it. They can hold USDC on Optimism, USDT on Polygon, or USDT on Tron, and Request Network's Secure Payment app will route the payment through Li.Fi to land in your destination token. This means your customers don't need to bridge anything before paying. They open the link, the app shows them every chain/token combination they hold a balance in, and they pick one. **Audience:** any merchant/integrator whose customers don't know or don't want to know about chains. E-commerce, SaaS, marketplaces. ## Supported source chains Cross-chain swap-to-pay routes through Li.Fi. Source chains the Secure Payment app can pay *from*: * Ethereum (`ETHEREUM`) * Arbitrum One (`ARBITRUM`) * Optimism (`OPTIMISM`) * Base (`BASE`) * Polygon (`POLYGON`) * BNB Smart Chain (`BNB`) * **Tron** (paid same-chain or as a cross-chain leg) The destination chain can be any of the above plus Sepolia (testnet). USDC and USDT are the standard payment currencies; Sepolia adds FAU. The Secure Payment UI displays a Li.Fi banner on the payment options screen when the payer's selected chain differs from your destination — so customers know exactly when a swap is happening. ## What the payer experiences Customer clicks the `pay.request.network` URL. Connect wallet modal (EVM) EVM wallets — MetaMask, Coinbase Wallet, WalletConnect, Ledger, Phantom, Rabby — **or** Tron wallets — TronLink, Guarda, Trust, WalletConnect-Tron. The modal lists both alongside; when the destination is Tron, Tron wallets replace the EVM list. The app shows all the payer's holdings across supported chains, with USD-equivalent balances. When the payer's selected source chain matches the destination, the banner reads "You'll be paying on the same chain as your recipient". When they differ, it reads "You'll be paying across chain via Li.Fi" (in pink) — that's the cross-chain swap path. For ERC-20s, an approval transaction precedes the payment. Both transactions are signed by the payer in their wallet. For cross-chain, the source-chain bridge transaction is signed; the destination-chain settlement is monitored by the API and reported via webhook. The success screen shows the source and destination transaction hashes (linked to the appropriate explorer per chain) and a Li.Fi badge for cross-chain payments. Payment success screen ## Tron specifics Tron is supported as both a source and a destination for single-recipient payments: | Direction | Supported? | Notes | | --------------------------- | ---------- | ----------------------------- | | Tron → Tron (USDT/USDC) | ✓ | Same-chain, no swap | | EVM → Tron (USDT/USDC) | ✓ | Routed via Li.Fi | | Tron → EVM (USDT/USDC) | ✓ | Routed via Li.Fi | | **Multi-recipient on Tron** | ✗ | Batch is EVM-only — see below | Multi-recipient (batch) Secure Payment links are EVM-only. If your destination is Tron and you submit multiple `requests[]`, the API returns a 400 with `Batch payments are not supported for TRON networks.` ## What you don't have to do * **No bridging UX of your own** — Li.Fi is integrated end-to-end inside the Secure Payment page * **No quote management** — quotes are fetched per session, surfaced in the UI, refreshed if they expire before the payer signs * **No source-chain config** — supplying a destination is enough; the Secure Payment app derives the rest from the payer's wallet ## Code: nothing changes Creating a multi-chain-capable link uses the same `POST /v2/secure-payments` call as same-chain — there are no extra parameters. The payer choosing a different source chain happens in the hosted UI, not in your API call. ```typescript theme={null} await fetch("https://api.request.network/v2/secure-payments", { method: "POST", headers: { "Content-Type": "application/json", "x-client-id": process.env.RN_CLIENT_ID!, }, body: JSON.stringify({ requests: [ { destinationId: process.env.MERCHANT_DESTINATION!, amount: "100", }, ], reference: order.id, }), }); ``` For the full request schema, see [`POST /v2/secure-payments`](/api-reference/secure-payments). ## Related Full feature reference for pay.request.network. The API call that mints these links. # No-code payment links Source: https://docs.request.network/use-cases/no-code-payment-links Generate payment links from the Dashboard UI — no API integration required. Best for freelancers, SMBs, and ops teams. ## What you'll build A repeatable workflow for sending one-off crypto payment links to customers, contractors, or partners — without writing a line of code. Sign in to the Dashboard with your wallet, set up your receiving destination once, then generate a fresh payment link each time you need to be paid. **Audience:** freelancers, SMBs, ops teams, anyone receiving low-to-medium volume crypto invoices who doesn't want to host their own checkout. **Apps used:** * [Dashboard](https://dashboard.request.network) — sign in, create destination + Client ID, generate links * [Secure Payment](https://pay.request.network) — what your customer sees when they open the link ## Prerequisites * An EVM wallet (MetaMask, Coinbase Wallet, WalletConnect) **or** a Tron wallet (TronLink, Guarda, Trust) * The wallet that will receive payments ## The flow Open [dashboard.request.network](https://dashboard.request.network), connect your wallet, and sign the auth message. Both EVM and Tron wallets work — the Dashboard auto-detects the type and gives you parallel sign-in tabs. Dashboard sign-in (EVM) On the home page, click **Set up payment destination**. Pick the chain (one of 7 EVM chains plus Tron) and the token (USDC and/or USDT — exact catalog varies by chain; see [Supported Chains and Currencies](/resources/supported-chains-and-currencies)). Confirm. Create payment destination dialog The Dashboard generates a **destination ID** in the ERC-7828 format. You only need to do this once per chain/token combo you want to receive on. Open **Manage Destination → Client IDs → Generate New Client ID**. Give it a label (e.g. "freelance invoices"). For dashboard-only use, leave **Allowed Domains** empty. Save the generated value. Generate Client ID dialog The Client ID is the credential the Dashboard uses on your behalf when you create payment links. Click **Get Paid → New Request**. Fill in: * **Amount** — what you want to be paid (e.g. `500`) * **Reference** — your invoice number or any tracking string * **Payer identifier** (optional) — your internal order or invoice ID Create new request form Click **Create**. The Dashboard returns a hosted payment URL on `pay.request.network`. Send the URL to your customer over email, Telegram, Slack, or paste it into your invoice PDF. They open it in any browser, connect any supported wallet (EVM or Tron, including cross-chain via Li.Fi), and pay. The **Get Paid** list shows status (Pending / Paid) for every link you've created, with on-chain transaction hashes for paid ones. Get Paid list ## Tron parity The exact same flow works for Tron. Sign in with TronLink (or Guarda, Trust, WalletConnect-Tron) instead of MetaMask, pick the **Tron** chain when creating the destination, and pick **USDT** or **USDC**. Customers can pay your Tron link from any supported wallet — including paying *to* a Tron destination *from* an EVM wallet via Li.Fi swap-to-pay. Tron secure payment links are **single-recipient only**. To pay multiple Tron addresses, generate a separate link per recipient. ## When to graduate from no-code This flow stays low-friction up to \~50 links per month. Beyond that — or if any of the below applies — switch to [programmatic payment links](/use-cases/programmatic-payment-links): * You want links generated automatically from your invoicing or e-commerce app * You need to attribute payments to internal user IDs * You want webhook-driven order fulfillment / accounting (see [Webhook reconciliation](/use-cases/webhook-reconciliation)) * You're paying many recipients at once (see [Batch payouts](/use-cases/batch-payouts)) ## Related Full feature reference for dashboard.request.network. Same flow with API and webhook details for when you outgrow no-code. # Programmatic payment links Source: https://docs.request.network/use-cases/programmatic-payment-links Generate Secure Payment links on demand from your backend. Best for e-commerce, marketplaces, SaaS, and any app that bills programmatically. ## What you'll build A backend that creates a Secure Payment link in response to your app's events — a customer hitting checkout, an invoice being approved, a subscription renewing. The customer opens the link, pays from any chain/token in any supported wallet, and your webhook fires when the payment confirms. **Audience:** developers integrating Request Network into an e-commerce app, marketplace, SaaS, or accounting product. **Apps used:** * [Auth API](https://auth.request.network/open-api) — Client IDs, webhooks * [Request API](https://api.request.network/open-api) — `POST /v2/secure-payments` and `POST /v2/secure-payments/payouts` * [Secure Payment](https://pay.request.network) — what your customer sees when they open the link ## Prerequisites Complete steps 1–4 of the [Quickstart](/use-cases/quickstart): 1. Sign in to the Dashboard with your wallet 2. Create a payment destination 3. Create a Client ID (with allowed domains for browser apps, empty for backend-only) 4. Register a webhook URL pointing at your handler You'll need: * `clientId` — passed as `x-client-id` on every API call * `destinationId` — composite ID combining `humanReadableInteropAddress` + `tokenAddress` * Webhook signing secret — for verifying webhook payloads ## Create an incoming payment link Use `POST /v2/secure-payments` to mint a link the customer pays into. ```typescript Node.js theme={null} const response = await fetch("https://api.request.network/v2/secure-payments", { method: "POST", headers: { "Content-Type": "application/json", "x-client-id": process.env.RN_CLIENT_ID!, }, body: JSON.stringify({ requests: [ { destinationId: "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7@eip155:8453#ABCD1234:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", amount: "100", }, ], reference: order.id, payerIdentifier: customer.id, }), }); const { securePaymentUrl, requestIds, token } = await response.json(); // Send securePaymentUrl to the customer (redirect, email, etc.) ``` ```python Python theme={null} import os import requests response = requests.post( "https://api.request.network/v2/secure-payments", headers={ "Content-Type": "application/json", "x-client-id": os.environ["RN_CLIENT_ID"], }, json={ "requests": [ { "destinationId": ( "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7" "@eip155:8453#ABCD1234:" "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" ), "amount": "100", } ], "reference": order.id, "payerIdentifier": customer.id, }, ) data = response.json() secure_payment_url = data["securePaymentUrl"] ``` ```curl cURL theme={null} curl -X POST "https://api.request.network/v2/secure-payments" \ -H "Content-Type: application/json" \ -H "x-client-id: $RN_CLIENT_ID" \ -d '{ "requests": [ { "destinationId": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7@eip155:8453#ABCD1234:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "100" } ], "reference": "ORDER-2026-042", "payerIdentifier": "user_12345" }' ``` The response includes `securePaymentUrl` — share it with the customer. They open it on `pay.request.network`, connect a wallet, and pay. ## Send the customer back to your site after payment Pass `redirectUrl` (and optionally `redirectLabel`) when creating the payment link. After a successful payment, the success screen renders a button that opens your URL — the payer clicks it to return to your site. There is no auto-redirect; the button is an explicit user action. ```typescript theme={null} const response = await fetch("https://api.request.network/v2/secure-payments", { method: "POST", headers: { "Content-Type": "application/json", "x-client-id": process.env.RN_CLIENT_ID!, }, body: JSON.stringify({ requests: [{ destinationId: process.env.MERCHANT_DESTINATION!, amount: "100" }], reference: order.id, redirectUrl: `https://yourshop.com/orders/${order.id}/thank-you`, redirectLabel: "Back to your order", }), }); const { securePaymentUrl } = await response.json(); // Send securePaymentUrl to the customer (redirect, email, etc.) ``` **Validation rules:** * `redirectUrl` must be `http(s)`. Other schemes (`javascript:`, `data:`, etc.) and HTML/script payload characters are rejected with `400 redirectUrl must be a safe http(s) URL with no script/HTML payload`. * `redirectLabel` is 1–255 chars and rejects HTML control characters (`<`, `>`, `&`, `"`, `'`, `` ` ``). * `redirectLabel` cannot be set without `redirectUrl` — the API rejects with `400 redirectLabel cannot be provided without redirectUrl`. * If you don't pass `redirectLabel`, the button reads **"Go Back and Close"**. The same fields are accepted on `POST /v2/secure-payments/payouts` for hosted payout links. ## EVM and Tron destinations side-by-side Tron is a drop-in replacement for any EVM destination as long as you submit a single recipient per call. The shape is identical; only the `destinationId` and addresses change format. ```json theme={null} { "requests": [ { "destinationId": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7@eip155:8453#ABCD1234:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "100" } ] } ``` ```json theme={null} { "requests": [ { "destinationId": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8@eip155:728126428#5F89A3B2:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "amount": "100" } ] } ``` Submitting multiple `requests[]` items where any destination is on Tron returns a 400 with `Batch payments are not supported for TRON networks. Please submit individual payment requests.` See [Batch payouts](/use-cases/batch-payouts) for the EVM-only batch flow. ## Send a payout link (outgoing) To *pay* someone (a contractor, vendor, refund recipient) via a hosted link they open and sign, use `POST /v2/secure-payments/payouts`. Same shape, single recipient, EVM or Tron. ```typescript theme={null} const response = await fetch( "https://api.request.network/v2/secure-payments/payouts", { method: "POST", headers: { "Content-Type": "application/json", "x-client-id": process.env.RN_CLIENT_ID!, }, body: JSON.stringify({ recipient: "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", creatorWalletAddress: process.env.PAYOUT_WALLET!, network: "base", currency: "USDC-base", amount: "250", reference: "INVOICE-2026-042", }), }, ); const { securePaymentUrl } = await response.json(); ``` ```typescript theme={null} const response = await fetch( "https://api.request.network/v2/secure-payments/payouts", { method: "POST", headers: { "Content-Type": "application/json", "x-client-id": process.env.RN_CLIENT_ID!, }, body: JSON.stringify({ recipient: "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8", creatorWalletAddress: process.env.PAYOUT_WALLET_TRON!, network: "tron", currency: "USDT-tron", amount: "250", reference: "INVOICE-2026-042", }), }, ); ``` The hosted URL the response returns is opened by *you* (or whoever signs payouts). The signer's wallet must match `creatorWalletAddress`. ## Receive payment notifications Your webhook endpoint receives signed POSTs as the payment lifecycle progresses. Verify the HMAC-SHA256 signature against your webhook secret before parsing the body. ```typescript theme={null} import { createHmac, timingSafeEqual } from "node:crypto"; import express from "express"; const app = express(); // Capture raw body for signature verification app.use( "/webhook", express.raw({ type: "application/json" }), (req, res) => { const signature = req.headers["x-request-network-signature"] as string; const expected = createHmac("sha256", process.env.WEBHOOK_SECRET!) .update(req.body) .digest("hex"); const sigBuf = Buffer.from(signature, "hex"); const expBuf = Buffer.from(expected, "hex"); const ok = sigBuf.length === expBuf.length && timingSafeEqual(sigBuf, expBuf); if (!ok) return res.status(401).send("invalid signature"); const event = JSON.parse(req.body.toString("utf8")); if (event.event === "payment.confirmed") { // Mark the order paid in your DB, fire fulfillment, etc. } res.status(200).send("ok"); }, ); ``` For the full webhook spec — all 12 events, retry policy, headers — see [Webhook reconciliation](/use-cases/webhook-reconciliation) and the [Webhooks reference](/api-reference/webhooks). ## Next steps What the customer sees when they pay across chains. Pay many EVM recipients in one signed transaction. The reliable, signature-verified way to detect payments. Full request/response schemas for every endpoint. # Quickstart Source: https://docs.request.network/use-cases/quickstart End-to-end walkthrough — sign in to the Dashboard, create a payment destination, register a webhook, and create a hosted payment link. This guide walks through the canonical flow for receiving payments via Request Network: creating a payment destination and Client ID in the Dashboard, registering a webhook for payment notifications, and creating a payment link. Every other use-case page links back to specific steps here. ## Overview The flow involves three services: 1. **Dashboard** ([dashboard.request.network](https://dashboard.request.network)) — Sign in with your wallet, create payment destinations, and generate Client IDs 2. **Auth API** ([auth.request.network](https://auth.request.network/open-api)) — Programmatic alternative for Client IDs, and the home of webhooks 3. **Request API** ([api.request.network](https://api.request.network/open-api)) — Create payment links (secure payments) ``` ┌──────────────────────┐ │ 1. Sign in with │ dashboard.request.network │ wallet │ └──────────┬───────────┘ │ ▼ ┌──────────────────────┐ │ 2. Create Payee │ dashboard.request.network │ Destination │ └──────────┬───────────┘ │ ▼ ┌──────────────────────┐ │ 3. Create Client ID │ dashboard.request.network │ │ (or auth.request.network/open-api) └──────────┬───────────┘ │ ▼ ┌──────────────────────┐ │ 4. Register Webhook │ auth.request.network/open-api │ │ (POST /v1/webhook with x-client-id) └──────────┬───────────┘ │ ▼ ┌──────────────────────┐ │ 5. Create Secure │ api.request.network/open-api │ Payment (link) │ └──────────────────────┘ ``` Your wallet session cookie (`session_token`) is shared across `dashboard.request.network`, `auth.request.network`, and `api.request.network` — so signing in once on the Dashboard gives you access to the API docs as well. ## Prerequisites * An EVM wallet (e.g. MetaMask) **or** a Tron wallet (e.g. TronLink) that will receive payments * The ability to sign messages with that wallet ## Step 1: Sign in with Your Wallet Go to [dashboard.request.network](https://dashboard.request.network). Connect your EVM or Tron wallet and sign the authentication message when prompted. Once signed in, you have an active wallet session. This sets a `session_token` cookie in your browser that is shared across Request Network services. Wallet sessions expire after 15 minutes of idle time. If your session expires during the following steps, return to the Dashboard and sign in again. ## Step 2: Create a Payment Destination A payment destination registers where you want to receive payments — it links your wallet address to a specific token on a specific chain. In the Dashboard, navigate to the payment destination setup. Select the **chain** and **token** you want to receive payments in. Confirm the creation. The Dashboard returns a **`destinationId`** (also shown as `humanReadableInteropAddress`). Save this value — you'll need it in Step 5. The `destinationId` follows the ERC-7828 format: ``` {walletAddress}@eip155:{chainId}#{checksum} ``` ### Supported Chains and Tokens | Network | Chain ID | USDC | USDT | | ------------ | ----------- | --------------------------------------------------------------- | ---------------------------------------------------- | | Ethereum | `1` | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` | `0xdAC17F958D2ee523a2206206994597C13D831ec7` | | Arbitrum One | `42161` | `0xaf88d065e77c8cC2239327C5EDb3A432268e5831` | `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9` (USDT0) | | Optimism | `10` | `0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85` | `0x94b008aA00579c1307B0EF2c499aD98a8ce58e58` | | Base | `8453` | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | `0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2` | | Polygon | `137` | `0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359` | `0xc2132D05D31c914a87C6611C10748AEb04B58e8F` | | BSC | `56` | `0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d` | `0x55d398326f99059ff775485246999027b3197955` | | **Tron** | `728126428` | — (API-only via Auth API: `TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8`) | `TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t` | The Dashboard UI exposes USDT for Tron destinations. To create a USDC-on-Tron destination, call `POST /v1/payee-destination` on the Auth API directly with the USDC token address. **Testnet:** | Network | Chain ID | Tokens | | ------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | Sepolia | `11155111` | FAU: `0x370DE27fdb7D1Ff1e1BaA7D11c5820a324Cf623C`, USDC: `0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238`, USDT: `0xF046b3CA5ae2879c6bAcC4D42fAF363eE8379F78` | ## Step 3: Create a Client ID A Client ID identifies your application and is required to create payment links and register webhooks. ### Option A — Dashboard (recommended) Go to [dashboard.request.network](https://dashboard.request.network). Open your payment destination settings. Open the **Client IDs** section. Click **Generate New Client ID**. Enter a human-readable name. Add **Allowed Domains** if the Client ID will be used from a frontend application. Leave empty for backend/server-side usage only. Confirm to generate the Client ID. Copy and save the generated `clientId` — you'll use it in Steps 4 and 5. Allowed domains must use `https://` (except for `localhost`, `127.0.0.1`, or `::1` which allow `http://`). No path, query, or fragment allowed. ### Option B — Auth API (for automation) Open the Scalar docs at [auth.request.network/open-api](https://auth.request.network/open-api/#tag/client-ids/POST/v1/client-ids) and call: ```http theme={null} POST https://auth.request.network/v1/client-ids Content-Type: application/json { "label": "My Client ID", "allowedDomains": ["https://mydomain.com"], "feePercentage": null, "feeAddress": null, "operatorWalletAddress": null, "payeeDestinationId": "" } ``` Your wallet session from Step 1 is sent automatically via cookie. **Example response (201 Created):** ```json theme={null} { "id": "01KJBN4KR5PFG4NAQG60EHR2Y0", "clientId": "cli_nz1bj41szV2fvjm9pbxdIhro3ld4x4", "label": "My Client ID", "allowedDomains": ["https://mydomain.com"], "feePercentage": null, "feeAddress": null, "operatorWalletAddress": null, "defaultPreApprovalExpiry": null, "defaultAuthorizationExpiry": null, "status": "active", "createdAt": "2026-03-02T19:42:37 GMT+0000" } ``` Save the `clientId` value (e.g. `cli_nz1bj41szV2fvjm9pbxdIhro3ld4x4`) — you'll use it in Steps 4 and 5. ## Step 4: Register a Webhook Webhooks let you receive real-time notifications when a payment is completed (or partially paid) for your payment links — no polling required. Webhooks are scoped to the Client ID that creates them. Any payment link created with that Client ID will trigger the webhook. The webhook payload includes the `clientId` field so you can identify which Client ID the payment was associated with. ### Creating a webhook Open [auth.request.network/open-api](https://auth.request.network/open-api/#tag/webhook/POST/v1/webhook) and call: ```http theme={null} POST https://auth.request.network/v1/webhook x-client-id: Content-Type: application/json { "url": "https://mydomain.com/webhook" } ``` **Example response (201 Created):** ```json theme={null} { "id": "01KJC2WX8EH4MP3DHZB2YQ7N9G", "secret": "f3c189a4b5e6d7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2" } ``` The `secret` is only returned once at creation. Store it securely — you cannot retrieve it again. Use HTTPS in production. `localhost` URLs are accepted for local testing. ### Managing webhooks Use the same Scalar docs page to list, toggle, or delete webhooks (all accept `x-client-id`): | Method | Path | Purpose | | -------- | ------------------------ | ------------------------------------------------------------------ | | `GET` | `/v1/webhook` | List webhooks for this Client ID | | `PUT` | `/v1/webhook/:webhookId` | Toggle active / inactive | | `DELETE` | `/v1/webhook/:webhookId` | Permanently delete | | `POST` | `/v1/webhook/test` | Body `{ "eventType": "payment.confirmed" }` — fire a test delivery | ### Verifying webhook signatures Every webhook request includes a signature header so you can verify it came from Request Network: | Header | Description | | ------------------------------- | ----------------------------------------------------------------- | | `x-request-network-signature` | HMAC-SHA256 of the raw JSON body, signed with your webhook secret | | `x-request-network-delivery` | Unique delivery ID — use as an idempotency key | | `x-request-network-retry-count` | Retry attempt number (`0`–`3`) | | `x-request-network-test` | `true` only for test deliveries via `/v1/webhook/test` | To verify, compute `HMAC-SHA256(rawBody, webhookSecret)` and compare it to `x-request-network-signature` using a constant-time comparison. ```typescript theme={null} import { createHmac, timingSafeEqual } from "node:crypto"; function verify(rawBody: string, signature: string | undefined, secret: string) { if (!signature) return false; const expected = createHmac("sha256", secret).update(rawBody).digest("hex"); const a = Buffer.from(expected, "hex"); const b = Buffer.from(signature, "hex"); return a.length === b.length && timingSafeEqual(a, b); } ``` Always verify against the **raw** request body before parsing. **Retries:** up to 3 retries (4 attempts total), default delays 1s / 5s / 15s, triggered on any non-2xx response, timeout, or connection error. ### Webhook events for payment links When a payer completes a payment on a payment link you created, your webhook receives a `payment.confirmed` event (or `payment.partial` for partial payments). For Client ID-scoped variants you'll also receive `payment.confirmed.client_id` / `payment.partial.client_id` with extra `clientId` and `origin` fields. **Example `payment.confirmed` payload:** ```json theme={null} { "event": "payment.confirmed", "requestId": "01de2a889ee629c15b71b5d7964e3a7e87638c886be75bf1b9d2c1fbe64cf855fb", "paymentReference": "0xabc123...", "payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", "amount": "10.0", "totalAmountPaid": "10.0", "expectedAmount": "10.0", "timestamp": "2026-03-02T20:15:00.000Z", "txHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", "network": "sepolia", "currency": "FAU", "paymentCurrency": "FAU", "clientId": "cli_nz1bj41szV2fvjm9pbxdIhro3ld4x4", "origin": "https://mydomain.com" } ``` Key fields to look for: | Field | Description | | ----------------- | -------------------------------------------------------------------------------------------------------- | | `event` | `payment.confirmed` (fully paid) or `payment.partial` (partial payment) | | `requestId` | The request ID from when you created the payment link | | `clientId` | The Client ID used to create the payment link — use this to route events if you have multiple Client IDs | | `amount` | The amount paid in this transaction | | `totalAmountPaid` | Cumulative amount paid so far | | `expectedAmount` | The total amount expected | | `txHash` | On-chain transaction hash | | `network` | The blockchain network | | `currency` | The token used for payment | ### All supported webhook events | Event | Description | | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `payment.confirmed` | Payment fully confirmed | | `payment.partial` | Partial payment received | | `payment.confirmed.client_id` | Client ID-scoped variant of `payment.confirmed` | | `payment.partial.client_id` | Client ID-scoped variant of `payment.partial` | | `payment.confirmed.checkout` | Secure-payment-scoped variant of `payment.confirmed` | | `payment.partial.checkout` | Secure-payment-scoped variant of `payment.partial` | | `payment.failed` | Payment failed | | `payment.refunded` | Payment refunded | | `payment.processing` | Offramp processing started | | `request.recurring` | A recurring request fired | | `payment_detail.updated` | Payment detail metadata changed | | `compliance.updated` | Compliance status changed | | `secure_payment.user_event` | Payer progressed through a Secure Payment Page step (`userEvent`: `wallet_connected`, `payment_sent_to_wallet`, `payment_approved_in_wallet`) — funnel telemetry, not a settlement signal | `secure_payment.user_event` is best-effort browser telemetry. The payer's browser can fail to reach the API, and retries begin only once the API has accepted the event — so a missing event is not evidence that the payer skipped the step. Use `payment.confirmed` for settlement and reconciliation. ## Step 5: Create a Secure Payment (Payment Link) With the `destinationId` from Step 2 and the `clientId` from Step 3, you can now create a payment link. When the payment is completed, the webhook from Step 4 will be notified automatically. Open the Request API docs: [api.request.network/open-api](https://api.request.network/open-api/#tag/v2secure-payment/POST/v2/secure-payments). Set the `x-client-id` header to the `clientId` value from Step 3. ```json theme={null} { "requests": [ { "destinationId": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7@eip155:11155111#1f969856:0x370DE27fdb7D1Ff1e1BaA7D11c5820a324Cf623C", "amount": "1" } ] } ``` Click **Send**. ### Constructing the `destinationId` The `destinationId` in the request body is a composite value that combines the payee destination's `humanReadableInteropAddress` (from Step 2) with the `tokenAddress`, separated by `:`: ``` {humanReadableInteropAddress}:{tokenAddress} ``` For example: ``` 0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7@eip155:11155111#1f969856:0x370DE27fdb7D1Ff1e1BaA7D11c5820a324Cf623C └──────────── humanReadableInteropAddress ─────────────────┘ └──────────── tokenAddress ──────────────────────┘ ``` ### Request body fields | Field | Type | Required | Description | | -------------------------- | -------- | -------- | -------------------------------------------------------------------------------------- | | `requests` | `array` | Yes | Array of payment request items (at least 1; multiple items create a batch on EVM only) | | `requests[].destinationId` | `string` | Yes | Composite ID: `humanReadableInteropAddress:tokenAddress` | | `requests[].amount` | `string` | Yes | Amount in human-readable format (e.g. `"10"` for 10 USDC) | | `feePercentage` | `string` | No | Fee percentage (0–100). If set, `feeAddress` is required. | | `feeAddress` | `string` | No | Address to receive fees. Required if `feePercentage` is set. | | `reference` | `string` | No | Merchant reference (≤ 255 chars) | | `payerIdentifier` | `string` | No | Payer identifier (≤ 255 chars) | Submitting multiple `requests[]` items where any destination is on **Tron** returns a 400 with `Batch payments are not supported for TRON networks. Please submit individual payment requests.` Tron secure payments are single-recipient. EVM batches up to 200 payees per link work as expected. **Example response (201 Created):** ```json theme={null} { "requestIds": [ "01de2a889ee629c15b71b5d7964e3a7e87638c886be75bf1b9d2c1fbe64cf855fb" ], "securePaymentUrl": "https://pay.request.network/?token=01KJRA0M9QG8MA4X887908T8A4", "token": "01KJRA0M9QG8MA4X887908T8A4" } ``` | Field | Type | Description | | ------------------ | ---------- | ----------------------------------------------- | | `requestIds` | `string[]` | IDs of the created payment requests | | `securePaymentUrl` | `string` | Shareable URL for the payer to complete payment | | `token` | `string` | Unique token for this payment session | Share the `securePaymentUrl` with the payer. They can open it in their browser to complete the payment. The payment link expires after 7 days or once it has been paid, whichever comes first. ## Step 6: Check Payment Status (Optional) In addition to receiving webhook notifications (Step 4), you can also poll for payment status using the `requestId` from Step 5. **Endpoint:** `GET https://api.request.network/v2/request/{requestId}` You can call this from the [Request API docs](https://api.request.network/open-api) using the same wallet session, or programmatically with the `x-client-id` header. **Example response (200 OK — paid):** ```json theme={null} { "hasBeenPaid": true, "requestId": "01de2a889ee629c15b71b5d7964e3a7e87638c886be75bf1b9d2c1fbe64cf855fb", "payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", "isListening": false, "txHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" } ``` **Example response (200 OK — not yet paid):** ```json theme={null} { "hasBeenPaid": false, "requestId": "01de2a889ee629c15b71b5d7964e3a7e87638c886be75bf1b9d2c1fbe64cf855fb", "payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", "isListening": true, "txHash": null } ``` | Field | Type | Description | | ------------- | --------- | -------------------------------------------------- | | `hasBeenPaid` | `boolean` | Whether the request has been fully paid | | `requestId` | `string` | The request ID | | `payee` | `string` | The payee's wallet address | | `isListening` | `boolean` | Whether the system is still listening for payment | | `txHash` | `string` | Transaction hash of the payment (`null` if unpaid) | ## Quick Reference | Step | Action | Where | | ---- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 1 | Sign in with wallet | [dashboard.request.network](https://dashboard.request.network) | | 2 | Create payment destination | [dashboard.request.network](https://dashboard.request.network) | | 3 | Create Client ID | [dashboard.request.network](https://dashboard.request.network) (or [auth.request.network/open-api](https://auth.request.network/open-api/#tag/client-ids/POST/v1/client-ids)) | | 4 | Register webhook | [auth.request.network/open-api](https://auth.request.network/open-api/#tag/webhook/POST/v1/webhook) | | 5 | Create payment link | [api.request.network/open-api](https://api.request.network/open-api/#tag/v2secure-payment/POST/v2/secure-payments) | | 6 | Check payment status | [api.request.network/open-api](https://api.request.network/open-api) (or via webhook) | ## Troubleshooting | Issue | Solution | | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | `401 Unauthorized` on Dashboard / Auth API calls | Your wallet session has likely expired (15 min TTL). Go back to [dashboard.request.network](https://dashboard.request.network) and sign in again. | | `Origin header is required` | When using `x-client-id` from a browser, include an `Origin` header matching one of the Client ID's allowed domains. | | `Invalid destination ID format` | The `destinationId` must be `humanReadableInteropAddress:tokenAddress`. Make sure both values are joined with `:` as the separator. | | `Batch payments are not supported for TRON networks` | Tron requests must be single-recipient. Submit one `requests[]` item per Tron destination. | | Webhook signature doesn't match | Verify against the **raw** request body (no re-serialization), use HMAC-SHA256, and compare with a constant-time check. | | Webhook secret lost | Secrets are only shown at creation. Delete the webhook (`DELETE /v1/webhook/:id`) and create a new one. | | Payment link expired | Payment links expire after 7 days or once paid. Create a new one if needed. | ## What's next Skip the code — generate payment links from the Dashboard UI. Server-side payment link creation with TS / Python / cURL examples. Let payers pay from any chain/token; receive on your preferred one. Automated payment notifications wired into your accounting/order systems. # Webhook reconciliation Source: https://docs.request.network/use-cases/webhook-reconciliation Real-time payment notifications wired into your accounting, fulfillment, and order systems. The reliable way to detect payments without polling. ## What you'll build A webhook handler that receives signed payment events from Request Network, verifies the signature, and triggers your downstream systems — order fulfillment, invoice closeout, accounting entries, customer email. Polling-free, idempotent, retry-safe. **Audience:** any backend integrating Request Network where payment events drive state changes downstream. ## The 13 events | Category | Event | When it fires | | ------------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Payment (core) | `payment.confirmed` | Payment fully settled on-chain | | | `payment.partial` | Partial payment received, more expected | | | `payment.failed` | Payment execution failed (recurring, cross-chain) | | | `payment.refunded` | Payment refunded to payer | | Payment (Client ID) | `payment.confirmed.client_id` | Same as `payment.confirmed`, request was created via Client ID — payload includes `clientId` and `origin` | | | `payment.partial.client_id` | Client ID-scoped partial | | Payment (Checkout) | `payment.confirmed.checkout` | Same as `payment.confirmed`, request originated from a Secure Payment link | | | `payment.partial.checkout` | Secure Payment-scoped partial | | Processing | `payment.processing` | Crypto-to-fiat offramp in progress (with detailed `subStatus`) | | Request | `request.recurring` | A new recurring billing cycle fired | | Compliance | `compliance.updated` | KYC or agreement status changed | | Bank details | `payment_detail.updated` | Bank account verification status changed | | Secure Payment Page | `secure_payment.user_event` | Payer progressed through a step of the Secure Payment Page (`userEvent`: `wallet_connected`, `payment_sent_to_wallet`, `payment_approved_in_wallet`) — funnel telemetry, **not** a settlement signal | `secure_payment.user_event` is best-effort browser telemetry, and the stream is intentionally incomplete: the payer's browser can fail to reach the API, and retries begin only once the API has accepted the event. A missing event is not evidence that the payer skipped the step, so do not drive drop-off, notification, or reconciliation logic off its absence. For the full payload schemas, see the [Webhooks reference](/api-reference/webhooks). ## Setup Complete steps 1–3 of the [Quickstart](/use-cases/quickstart). Note your `clientId`. `POST https://auth.request.network/v1/webhook` with header `x-client-id: ` and body `{ "url": "https://yourapp.com/webhooks/request-network" }`. Save the returned `secret` immediately — it's only shown once. Fire a test event from the [auth API docs](https://auth.request.network/open-api/#tag/webhook/POST/v1/webhook/test) with body `{ "eventType": "payment.confirmed" }`. The request will arrive with header `x-request-network-test: true`. ## Handler — reference implementation A signature-verifying Express handler. It verifies against the **raw** body, uses constant-time comparison, passes the delivery ID to business handlers as their idempotency key, and lets Request Network retry a failed handler. ```typescript theme={null} import { createHmac, timingSafeEqual } from "node:crypto"; import express from "express"; const app = express(); const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!; app.post( "/webhooks/request-network", express.raw({ type: "application/json" }), async (req, res) => { const signature = req.headers["x-request-network-signature"] as string; const deliveryId = req.headers["x-request-network-delivery"] as string; if (!signature || !deliveryId) { return res.status(400).send("missing headers"); } // 1. Verify signature against the RAW body — never re-stringify const expected = createHmac("sha256", WEBHOOK_SECRET) .update(req.body) .digest("hex"); const sigBuf = Buffer.from(signature, "hex"); const expBuf = Buffer.from(expected, "hex"); if ( sigBuf.length !== expBuf.length || !timingSafeEqual(sigBuf, expBuf) ) { return res.status(401).send("invalid signature"); } // 2. Parse and route. Each business operation uses deliveryId as an // idempotency key in its own durable store. const event = JSON.parse(req.body.toString("utf8")); try { await handleEvent(event, deliveryId); return res.status(200).send("ok"); } catch (err) { console.error("handler failed", err); return res.status(500).send("handler error"); } }, ); async function handleEvent(event: any, deliveryId: string) { switch (event.event) { case "payment.confirmed": case "payment.confirmed.client_id": case "payment.confirmed.checkout": await markOrderPaid(event.requestId, event.txHash, deliveryId); break; case "payment.partial": case "payment.partial.client_id": case "payment.partial.checkout": await recordPartialPayment( event.requestId, event.amount, event.totalAmountPaid, deliveryId, ); break; case "payment.failed": await flagFailedPayment(event.requestId, deliveryId); break; case "request.recurring": await onRecurringInvoice(event.originalRequestId, event.requestId, deliveryId); break; case "compliance.updated": await syncKycStatus(event.clientUserId, event.kycStatus, deliveryId); break; // Payer-funnel telemetry from the Secure Payment Page. Never reconcile // money off this — a payer can approve in their wallet and still have the // transaction fail on-chain. Wait for payment.confirmed for settlement. case "secure_payment.user_event": await recordFunnelStep( event.securePaymentToken, event.userEvent, deliveryId, ); break; // ... others } } ``` Webhook delivery is at least once, not exactly once. Each business operation must atomically record the delivery ID with the state it changes, then make a repeat delivery a successful no-op. If an operation calls another service, pass the delivery ID as that service's idempotency key too. A process can fail after a side effect but before it returns `200`. ## Headers reference | Header | Description | | ------------------------------- | --------------------------------------------- | | `x-request-network-signature` | HMAC-SHA256 of the raw JSON body, hex-encoded | | `x-request-network-delivery` | ULID — use as idempotency key | | `x-request-network-retry-count` | `0`–`3`, current retry attempt | | `x-request-network-test` | `true` only for `/v1/webhook/test` deliveries | ## Retry policy | Attempt | Delay | Cumulative time | | ----------- | ----- | --------------- | | 0 (initial) | — | t=0 | | 1 | 1s | t+1s | | 2 | 5s | t+6s | | 3 | 15s | t+21s | After 4 total attempts (initial + 3 retries) the delivery is dropped. Triggers: any non-2xx response, timeout, connection error. Default request timeout is 5s. ## Common patterns ### Idempotency The same `payment.confirmed` event might arrive twice (network blip, retry overlap). Use `x-request-network-delivery` as the idempotency key. Record it atomically with the business update in your durable store; do not use a check-then-act cache lookup, because overlapping deliveries can both pass the check. For a local database update, add a `webhook_deliveries` table with a unique `delivery_id` column, then insert that ID in the same transaction as the business update: ```typescript theme={null} async function markOrderPaid( requestId: string, txHash: string, deliveryId: string, ) { await db.transaction(async (tx) => { const order = await tx.orders.findOne({ where: { requestId } }); if (!order) return; // not ours const claim = await tx.execute( `INSERT INTO webhook_deliveries (delivery_id) VALUES ($1) ON CONFLICT (delivery_id) DO NOTHING`, [deliveryId], ); if (claim.rowCount === 0) return; // already applied await tx.orders.update({ where: { id: order.id }, data: { paidAt: new Date(), txHash }, }); }); } ``` ### Routing by Client ID If your platform has many merchants, give each their own Client ID. The webhook payload includes `clientId` so you can route events to the right tenant. ### Slack alerts on failure ```typescript theme={null} case "payment.failed": await fetch(SLACK_WEBHOOK, { method: "POST", body: JSON.stringify({ text: `:warning: Payment failed for request ${event.requestId}`, }), }); break; ``` ### Crypto-to-fiat status tracking The `payment.processing` event includes a `subStatus` field that progresses through `initiated → pending_internal_assessment → ongoing_checks → sending_fiat → fiat_sent`. Surface this in your UI so the payee sees real-time offramp progress. ## Local development Use [ngrok](https://ngrok.com) to expose localhost during development: ```bash theme={null} ngrok http 3000 # Pass the https://xxxxx.ngrok-free.app URL to POST /v1/webhook (above) ``` Local URLs (`localhost`, `127.0.0.1`) are accepted by the auth API for testing. HTTPS is required in production. ## Related Full payload schemas for every event type. High-level concepts and event categories. # Request Network Docs Source: https://docs.request.network/use-cases/welcome Send and reconcile crypto payments across EVM chains and Tron with payment links and instant webhook confirmations.

Request Network Docs

Request Network is a protocol for creating and settling payments directly between two parties on EVM chains and Tron. Generate a payment link, route the payment across chains, and get a webhook the moment it settles. No custodial hold, no manual matching against your invoices.

Three Products

We ship Request Network as three products: the Dashboard, the Secure Payment Page, and the API.

No-code home for payment destinations, Client IDs, and incoming/outgoing payment links. EVM and Tron sign-in. Hosted payment links at pay.request.network. Cross-chain swap-to-pay. Tron and EVM payer wallets. Programmatic creation of payment destinations, Client IDs, secure payments, payouts, and webhooks.

Get Started

End-to-end: sign in to the Dashboard, create a payment destination, register a webhook, ship a payment link. No-code links, programmatic links, multi-chain checkout, batch payouts, webhook reconciliation, KYT-screened payments. Payment types, secure payment pages, payouts, webhooks, fees, and more.
Supported chains (8 networks including Tron), currencies, smart contracts, and feature matrix.
# Request Network vs Stripe, PayPal & crypto payment tools Source: https://docs.request.network/use-cases/why-request-network How Request Network compares to Stripe, PayPal, and other crypto payment tools on fees, settlement time, custody, chargebacks, and stablecoin support. Businesses accepting stablecoin or crypto payments often start by asking how Request Network compares to the processors they already know. This page lays out the differences in fees, fund custody, dispute handling, and currency support. ## How do the fees compare? Request Network charges a small, percentage-based transaction fee — a fraction of what traditional card processors like Stripe and PayPal charge, which is typically a percentage of every transaction plus a fixed per-transaction fee. For the current Request Network rate, [contact the team](https://request.network/discord). | | Request Network | Stripe / PayPal | | --------- | --------------------------------------- | ---------------------------------------------------------- | | Fee model | Small, percentage-based transaction fee | Percentage of each transaction + fixed per-transaction fee | ## What does non-custodial mean for me? With Request Network, funds move **directly from payer to recipient** on-chain. There is no intermediary holding period. Traditional processors hold funds for **2-7 days** before releasing them to the merchant. That hold introduces: * Counterparty risk — the processor could freeze the account or go bankrupt while holding your funds * Delayed access to money you've already been paid * Exposure to fund freezing or seizure Because Request Network never takes custody of funds, none of these risks apply. The recipient has full control and immediate access as soon as the transaction settles on-chain. ## What about chargebacks? Crypto payments made through Request Network are **final by default** — there are no chargebacks. This means: * No reversals appearing 60-90 days after a payment was made * No per-dispute chargeback fees like those card processors charge * Fewer losses from chargeback fraud, which is a common problem for international card payments and digital goods Card processors carry chargeback risk on every transaction: a merchant can lose the product *and* the payment, then pay a dispute fee on top. For businesses that still want buyer protection, Request Network supports **escrow-based flows**. Funds are held in a smart contract until agreed conditions are met, which enables refunds when needed without reintroducing custodial risk. ## Which is better for crypto-native businesses? Request Network is built for crypto from the ground up, rather than adding crypto support to a fiat-first system. * **553+ currencies** supported across EVM chains (Ethereum, Polygon, BSC, and others) plus Tron * **No forced fiat conversion** — funds can stay in crypto end-to-end * **Native wallet integration** — connects directly with MetaMask, WalletConnect, and other Web3 wallets * **Crosschain payments** — a payer can pay in one token on one chain and the recipient receives a different token on a different chain Traditional processors typically support a handful of assets (often just BTC, ETH, and USDC), require crypto-to-fiat conversion with variable fees, and treat crypto as an add-on to a fiat-first architecture. ## How does reconciliation compare? Both Request Network and traditional processors offer reconciliation, but the underlying guarantees differ. | | Request Network | Stripe / PayPal | | ---------------- | ----------------------------------------------------- | ---------------------------------------------- | | Record integrity | Cryptographically guaranteed, on-chain | Stored in a company database | | Retention | No retention limit — records are permanent | Typically limited (for example, 7 years) | | Status updates | Real-time via webhooks | Real-time via webhooks | | Durability | Independent of any single company staying in business | Depends on the processor remaining operational | Because Request Network payment records live on-chain, they can't be altered or deleted, and they don't depend on a vendor's continued existence to remain accessible. ## Full comparison at a glance | | Request Network | Stripe / PayPal | | ------------------------- | ----------------------------------------------------- | ---------------------------------------------------------- | | Fee model | Small, percentage-based transaction fee | Percentage of each transaction + fixed per-transaction fee | | Settlement | Direct payer → recipient, on-chain | 2-7 day hold before payout | | Custody | Non-custodial | Processor holds funds during settlement | | Chargebacks | None by default; optional escrow for buyer protection | Standard; per-dispute fees apply | | Crypto/stablecoin support | 553+ currencies, native | Limited (typically BTC, ETH, USDC) | | Chains | EVM chains + Tron | N/A (fiat-first, crypto bolted on) | | Crosschain payments | Pay in one token, receive in another | Not supported |