Crypto-to-fiat Payments
A Guide to Crypto-to-fiat Payments, Compliance, and Webhooks with the Request Network API
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 offramp infrastructure. This requires prerequisite compliance (KYC/Agreement) and bank account registration (payment detail) flows.
EasyInvoice includes a reference implementation for this flow.
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:
Create an account on the Request Network API Portal
Generate a Sandbox API key with crypto-to-fiat sandbox access
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
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:
Book a call to request production access
Discuss your use case with our team to ensure the best integration approach
Complete the approval process - we'll work with you to get everything set up
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
Understanding clientUserId
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, email address, database ID, or anything unique per user on your platform.EasyInvoice Example: In the EasyInvoice demo app,
clientUserId
is set to the user's email address.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
Flow Explanation
Submit KYC: The platform collects KYC information from the payer and submits it to the API.
KYC Review: The platform receives webhook updates as the KYC is processed (
compliance.updated
withkycStatus
).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.
Agreement Confirmation: The platform receives a webhook update when the agreement is completed (
compliance.updated
withagreementStatus
).
Relevant Endpoints
POST /payer
: Submit KYC application.GET /payer/{clientUserId}
: Get compliance status for a payer.PATCH /payer/{clientUserId}
: Update agreement status after signature.
Checks compliance status and returns necessary URLs for completing compliance.
API key for authentication
Client User ID
First Name
Last Name
Company Name
Date of birth in YYYY-MM-DD format
^\d{4}-\d{2}-\d{2}$
Address Line 1
Address Line 2
City
State
Postcode
Country
Nationality
Phone in E.164 format
^\+?[1-9]\d{1,14}$
Social Security Number
Source of Funds
Business Activity
POST /v2/payer HTTP/1.1
Host: api.request.network
x-api-key: text
Content-Type: application/json
Accept: */*
Content-Length: 312
{
"clientUserId": "user-123",
"email": "[email protected]",
"firstName": "John",
"lastName": "Doe",
"beneficiaryType": "individual",
"dateOfBirth": "1985-12-12",
"addressLine1": "123 Main Street",
"city": "New York",
"state": "NY",
"postcode": "10001",
"country": "US",
"nationality": "US",
"phone": "+12125551234",
"ssn": "123-45-6789"
}
{
"agreementUrl": "https://core-api.pay.so/v1/public/agreements?email=john.doe%40example.com",
"kycUrl": "https://sumsub.com/idensic/l/#/sbx_VvK9E9P2A23xQPoA",
"status": {
"agreementStatus": "not_started",
"kycStatus": "not_started"
}
}
Retrieves the comprehensive compliance status for a specific user, including KYC and agreement status.
The client user ID to check compliance status for
user-123
API key for authentication
GET /v2/payer/{clientUserId} HTTP/1.1
Host: api.request.network
x-api-key: text
Accept: */*
{
"kycStatus": "completed",
"agreementStatus": "completed",
"isCompliant": true,
"userId": "a25a4274-8f50-4579-b476-8f35b297d4ad"
}
Update the agreement completion status for a user.
The client user ID to update
user-123
API key for authentication
PATCH /v2/payer/{clientUserId} HTTP/1.1
Host: api.request.network
x-api-key: text
Content-Type: application/json
Accept: */*
Content-Length: 27
{
"agreementCompleted": true
}
{
"success": true
}
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
Flow Explanation
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).
Approval: The platform receives a webhook (
payment_detail.updated
) indicating if the payment details are approved, failed, or pending.Create Request: Once approved, the platform creates a new request as usual, but with the
isCryptoToFiatAllowed
flag set totrue
. 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, EasyInvoice intentionally requires the payer to complete KYC first. This decision 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: EasyInvoice integrates payee bank account registration directly into the Create Invoice form. If the user clicks "Create" before the bank account is approved, a loading indicator appears until approval is granted. This avoids the need for a separate bank account management page and keeps the user experience straightforward.
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 separation adds some complexity, so the UI is intentionally kept simple.
Future Flexibility: While some clients may want to allow payees to create requests before payer KYC, this is not currently supported in EasyInvoice to avoid increased complexity. We may revisit this if there is sufficient market demand.
This approach ensures a smooth, compliant, and user-friendly experience, even if it means some technical possibilities are not exposed in the current UI.
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
withisCryptoToFiatAllowed = true
: Create a new crypto-to-fiat request
Create payment details for a user
The client user ID
user-123
API key for authentication
Name of the bank
Name of the account holder
Bank account number
Bank routing number (US)
Type of beneficiary
Three-letter currency code (ISO 4217)
Primary address line
Secondary address line
City name
State or province code
Two-letter country code (ISO 3166-1 alpha-2)
Date of birth in YYYY-MM-DD format
^\d{4}-\d{2}-\d{2}$
Postal or ZIP code
Payment rail type
local
Possible values: UK bank sort code
International Bank Account Number
SWIFT/BIC code
Government-issued ID number
Type of government-issued ID (e.g., passport, driver's license)
Type of bank account
French RIB number
Australian BSB number
New Zealand NCC number
Bank branch code
Bank code
Indian Financial System Code
POST /v2/payer/{clientUserId}/payment-details HTTP/1.1
Host: api.request.network
x-api-key: text
Content-Type: application/json
Accept: */*
Content-Length: 316
{
"bankName": "Chase",
"accountName": "Gordon's Chase Business Account",
"accountNumber": "253009233489",
"routingNumber": "026013356",
"beneficiaryType": "business",
"currency": "usd",
"addressLine1": "24 Theatre St.",
"city": "Paramount",
"state": "CA",
"postalCode": "90723",
"country": "US",
"dateOfBirth": "1985-12-12",
"rails": "local"
}
{
"payment_detail": {
"id": "pd_123456",
"clientUserId": "user-123",
"bankName": "Chase",
"accountName": "Gordon's Chase Business Account",
"currency": "usd",
"beneficiaryType": "business"
}
}
Retrieves the registered bank account details for a user. Optionally filter by payment details ID.
The client user ID to get payment details for
user-123
Optional ID of specific payment details to retrieve
fa898aec-519c-46be-9b4c-e76ef4ff99d9
API key for authentication
GET /v2/payer/{clientUserId}/payment-details HTTP/1.1
Host: api.request.network
x-api-key: text
Accept: */*
{
"paymentDetails": [
{
"id": "fa898aec-519c-46be-9b4c-e76ef4ff99d9",
"userId": "a25a4274-8f50-4579-b476-8f35b297d4ad",
"bankName": "Chase",
"accountName": "Gordon's Chase Business Account",
"beneficiaryType": "business",
"accountNumber": "253009233489",
"routingNumber": "026013356",
"currency": "usd",
"status": "approved",
"rails": "local"
}
]
}
Create a new payment request
API key for authentication
The wallet address of the payer
The wallet address of the payee
The payable amount of the invoice, in human readable format
Invoice Currency ID, from the Request Network Token List e.g: USD
Payment currency ID, from the Request Network Token List e.g: ETH-sepolia-sepolia
Whether crypto-to-fiat payment is available for this request
POST /v2/request HTTP/1.1
Host: api.request.network
x-api-key: text
Content-Type: application/json
Accept: */*
Content-Length: 238
{
"payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7",
"amount": "10",
"invoiceCurrency": "USD",
"paymentCurrency": "ETH-sepolia-sepolia",
"isCryptoToFiatAvailable": false,
"recurrence": {
"startDate": "2025-01-01T00:00:00.000Z",
"frequency": "DAILY"
}
}
{
"paymentReference": "0xb3581f0b0f74cc61",
"requestId": "01e273ecc29d4b526df3a0f1f05ffc59372af8752c2b678096e49ac270416a7cdb"
}
Paying a Crypto-to-Fiat Request
The payer pays in crypto; Request Tech handles offramping and fiat payout.
Payment Flow Diagram
Flow Explanation
Get Payment Calldata: The platform fetches payment calldata for the request.
User Pays: The payer signs and submits the transaction, sending crypto to Request Tech.
Offramp Processing: Request Tech receives the crypto and begins the offramp process.
Status Updates: The platform receives webhook events as the offramp progresses (payment.processing, payment.failed), with subStatus indicating the current offramp stage.
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)
Last updated
Was this helpful?