Skip to main content
Facilitate marketplace payments with fiat onramp, stablecoin purchases, and seamless offramps back to bank accounts. Support escrow via HIFI’s transfer approval workflows to add security and compliance controls to payouts. ⏱️ Time to complete: 30-40 minutes 🎯 What you’ll build: A complete marketplace payment flow where a buyer in the US pays a seller in Hong Kong in USDC. The seller then offramps HKD from their wallet to their bank account.

Use Case Overview

This recipe demonstrates a marketplace payment where:
  • ✅ User A deposits USD from their bank account
  • ✅ USD automatically converts to USDC stablecoin
  • ✅ USDC is transferred to User B wallet
  • ✅ USDC is converted to HKD and sent to User B’s bank account
This flow will also demonstrate the use of transfer approvals to hold funds until transfers are approved.

Prerequisites

Before you start, make sure you have:
  • API keys from the Dashboard (Get API keys)
  • A tool for making API calls (cURL, Postman, or similar)
  • Basic understanding of REST APIs
  • Your sandbox endpoint: https://sandbox.hifibridge.com
All examples in this guide use the sandbox environment. When you’re ready for production, simply replace the sandbox URL with the production endpoint and use your production API keys.

What you’ll build

Here’s what we’ll accomplish in this recipe:
  1. Generate API Keys - Set up authentication for HIFI API
  2. Create Individual User A (Buyer) - Onboard US customer and complete KYC for USD rail
  3. Add Virtual Account - Set up account for USD deposits that convert to USDC
  4. Create Business user B (Seller) - Onboard Hong Kong seller (or use existing business)
  5. Execute Payment - Send USDC from User A wallet to User B wallet
  6. Initiate Offramp - Convert crypto funds to User B HKD bank account
Payment Flow:
USD → User A Bank Account → USDC → User A Wallet → USDC → Business B Wallet → HKG → Business B Bank Account

Generate API Keys

Access to the HIFI API requires authentication via API keys. Generate your keys from the HIFI Dashboard before making any API calls.
Steps:
  1. Navigate to dashboard.hifibridge.com
  2. Log in to your account
  3. Go to Developer → API Keys
  4. Click Generate New Key
  5. Select your environment: Sandbox (for testing) or Production (for live transactions)
  6. Copy the key immediately - it’s only shown once
Important Security Notes:
  • Store your API key securely (environment variables, secrets manager, etc.)
  • Never commit API keys to version control
  • Your key authenticates all HIFI API requests
  • If compromised, immediately revoke in the Dashboard and generate a new one
API keys are shown only once. If you lose your key, you must revoke it and generate a new one. There is no way to retrieve a lost key.
Using Your API Key:Include your API key in the Authorization header of all API requests:
--header 'authorization: Bearer YOUR_API_KEY'
For a complete guide on API authentication, see our Authentication Documentation.

Create User A (Sender)

The buyer needs a verified user account to access fiat rails and make purchases. Every user must accept HIFI’s Terms of Service and complete KYC verification before they can move money between fiat and crypto.
Complete Onboarding Guide: For a comprehensive walkthrough of user onboarding and KYC verification, see our User Guide.

User Creation Flow

Creating a user involves four steps:
  1. Generate Terms of Service Link - Get acceptance link for your customer
  2. Create User - Create the user account after TOS acceptance
  3. Update KYC Information - Provide required personal details
  4. Submit KYC - Submit for verification to unlock USD rail
Once your user has accepted the Terms of Service, create the user account with the Create User endpoint.Request:
curl --request POST \
  --url https://sandbox.hifibridge.com/v2/users \
  --header 'accept: application/json' \
  --header 'authorization: Bearer YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --data '
{
  "type": "individual",
  "firstName": "John",
  "lastName": "Doe",
  "email": "[email protected]",
  "dateOfBirth": "1990-01-01",
  "address": {
    "addressLine1": "123 Park Ave",
    "city": "Kansas City",
    "stateProvinceRegion": "KS",
    "postalCode": "10001",
    "country": "USA"
  },
  "signedAgreementId": "c9c91801-d3a2-4a1c-aa2e-fcb2c473198c",
  "requestId": "705f1f8b-a080-467c-b683-174eca409928"
}
'
Request Fields:
type
string
required
User type. Use individual for personal accounts or business for business accounts.
firstName
string
required
User’s first name.
lastName
string
required
User’s last name.
email
string
required
User’s email address.
dateOfBirth
string
required
User’s date of birth in YYYY-MM-DD format.
address
object
required
User’s residential address.
signedAgreementId
string
required
The signedAgreementId from the Terms of Service acceptance.
requestId
string
required
Unique identifier (UUID) for this request to ensure idempotency.
Response:
{
  "id": "3b77cec8-1bb9-5ea0-91a1-e8c1411dd429",
  "type": "individual",
  "email": "[email protected]",
  "name": "John Doe",
  "wallets": {
    "INDIVIDUAL": {
      "ETHEREUM": {
        "address": "0x062f27D749Db9AdE709FaCF753e71618a0d64eF7"
      },
      "POLYGON": {
        "address": "0x63dAbB631bcE6d6090A4e6c5c7aB20d3D853C338"
      }
    }
  }
}
User created successfully!Response Fields:
id
string
The unique user ID. Save this - you’ll use it for all future operations with this user.
wallets
object
Automatically provisioned wallet addresses on supported blockchain networks.
What you get:
  • User ID (3b77cec8-1bb9-5ea0-91a1-e8c1411dd429) - Save this for all future operations
  • Wallet addresses - Pre-provisioned on multiple blockchains, ready to use immediately
Automatic Wallet Provisioning: Every user automatically receives wallet addresses on supported blockchain networks (Polygon, Ethereum, etc.). These wallets are fully functional immediately - no additional setup required.
Provide the required personal information for KYC verification using the Update KYC endpoint.Request:
curl --request POST \
  --url https://sandbox.hifibridge.com/v2/users/3b77cec8-1bb9-5ea0-91a1-e8c1411dd429/kyc \
  --header 'accept: application/json' \
  --header 'authorization: Bearer YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --data '
{
  "firstName": "John",
  "phone": "+11111111111",
  "taxIdentificationNumber": "678898765",
  "nationality": "USA"
}
'
Request Fields:
firstName
string
User’s first name (can be used to update if needed).
phone
string
required
User’s phone number with country code (E.164 format).
taxIdentificationNumber
string
required
Tax identification number (e.g., SSN for US users).
nationality
string
required
User’s nationality (ISO 3166-1 alpha-3 country code).
Response:
{
  "userId": "3b77cec8-1bb9-5ea0-91a1-e8c1411dd429",
  "kycInfo": {
    "type": "individual",
    "firstName": "John",
    "lastName": "Doe",
    "nationality": "USA",
    "email": "[email protected]",
    "phone": "+11111111111",
    "address": {
      "city": "Kansas City",
      "country": "USA",
      "postalCode": "10001",
      "addressLine1": "123 Park Ave",
      "stateProvinceRegion": "KS"
    },
    "dateOfBirth": "1990-01-01T00:00:00+00:00",
    "taxIdentificationNumber": "678898765",
    "documents": []
  }
}
Personal information updated!
Document Upload: For a complete KYC submission in production, you’ll also need to upload identity documents (passport, driver’s license, etc.). See the Quickstart Guide for detailed document upload instructions.
Submit the KYC application to unlock the USD rail using the Submit KYC endpoint.Request:
curl --request POST \
  --url https://sandbox.hifibridge.com/v2/users/3b77cec8-1bb9-5ea0-91a1-e8c1411dd429/kyc/submissions \
  --header 'accept: application/json' \
  --header 'authorization: Bearer YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --data '
{
  "rails": "USD"
}
'
Request Fields:
rails
string
required
The currency rail to unlock. Use USD for US Dollar operations.
Response:
{
  "currency": "USD",
  "status": "ACTIVE",
  "reviewResult": {
    "reviewAnswer": "APPROVED",
    "reviewRejectType": "",
    "rejectReasons": [],
    "comment": ""
  },
  "details": {
    "identity": {
      "reviewAnswer": "APPROVED",
      "documents": [
        {
          "id": "c00791d0-213a-480a-9e54-1c535673f10c",
          "type": "PASSPORT",
          "subType": "SINGLE_SIDE",
          "reviewAnswer": "APPROVED"
        }
      ]
    },
    "questionnaire": {
      "reviewAnswer": "APPROVED",
      "details": []
    },
    "personalInfo": {
      "reviewAnswer": "APPROVED",
      "details": []
    }
  }
}
KYC approved! USD rail is now active.Response Fields:
status
string
Overall KYC status. ACTIVE means the USD rail is unlocked and the user can now onramp and offramp USD.
reviewResult
object
High-level review outcome.
details
object
Detailed review results broken down by category.
Sandbox Auto-Approval: In sandbox, KYC applications are typically automatically approved within minutes. In production, review typically takes 1-3 business days.

Add Virtual Account (User A)

Now that User A has passed KYC, create a Virtual Account for onramping. This is a bank account number automatically created by HIFI that User A can deposit USD into. When fiat arrives, it’s automatically converted to USDC and sent to their wallet.
Complete Virtual Account Guide: For more details on virtual accounts and onramping, see our Virtual Accounts Guide.
A Virtual Account enables User A to deposit USD from their bank, which automatically converts to USDC on Polygon.Request:
curl --request POST \
  --url https://sandbox.hifibridge.com/v2/users/3b77cec8-1bb9-5ea0-91a1-e8c1411dd429/virtual-accounts \
  --header 'accept: application/json' \
  --header 'authorization: Bearer YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --data '
{
  "sourceCurrency": "usd",
  "destinationCurrency": "usdc",
  "destinationChain": "POLYGON"
}
'
Request Fields:
sourceCurrency
string
required
Fiat currency to deposit. Use usd for US Dollars.
destinationCurrency
string
required
Stablecoin to receive after conversion. Use usdc for USD Coin.
destinationChain
string
required
Blockchain network for the stablecoin. Options: POLYGON, ETHEREUM
Response:
{
  "id": "c48d1dd1-b5cd-5bc7-b85e-df1b0f17c1c8",
  "status": "activated",
  "provider": "CROSS_RIVER",
  "userId": "3b77cec8-1bb9-5ea0-91a1-e8c1411dd429",
  "source": {
    "paymentRails": ["ach", "wire", "rtp"],
    "currency": "usd"
  },
  "destination": {
    "chain": "POLYGON",
    "currency": "usdc",
    "walletAddress": "0x63dAbB631bcE6d6090A4e6c5c7aB20d3D853C338"
  },
  "depositInstructions": {
    "bankName": "Cross River Bank",
    "bankAddress": "885 Teaneck Road, Teaneck, NJ 07666",
    "beneficiary": {
      "name": "John Doe",
      "address": "123 Park Ave, Kansas City, KS, 10001, US"
    },
    "ach": {
      "routingNumber": "021214891",
      "accountNumber": "372232122120"
    },
    "wire": {
      "routingNumber": "021214891",
      "accountNumber": "372232122120"
    },
    "instruction": "Please deposit USD to the bank account provided. Ensure that the beneficiary name matches the account holder name provided, or the payment may be rejected."
  }
}
Virtual account created!Response Fields:
id
string
The unique virtual account ID. Save this for monitoring deposits.
status
string
Account status. activated means it’s ready to receive deposits.
source
object
Source configuration for fiat deposits.
destination
object
Destination configuration for crypto conversion.
depositInstructions
object
Most Important: Bank account details User A needs to send money to.
How to Use: Provide these deposit instructions to User A through your app/website. When they send money to this account via their bank, HIFI automatically detects the deposit, converts it to USDC, and sends it to their wallet. For monitoring deposits, subscribe to ONRAMP.CREATE webhook events.

Create Business B (HK Seller)

The recipient business (Business B in Hong Kong) needs a wallet address to receive the payment. Follow the same business creation flow as User A, but complete KYB for the appropriate Hong Kong rail.
Using an Existing Business: If Business B already exists in your system, you can skip business creation and just ensure they have an active wallet address and Hong Kong bank account for later offramping. You’ll need either their userid or walletaddress for the crypto transfer.

Required Information

To complete this recipe, you’ll need:
  • Business B’s userId - From creating their business account
  • Business B’s walletAddress - Provisioned upon creating the business user
  • Business B’s HKD accountId - From adding their Hong Kong bank account
For the complete flow of creating Business B and adding their HKD bank account, follow the same steps as User A but:
  1. Use Hong Kong business information and address details
  2. Submit KYB for the Hong Kong rail
  3. Add an Offramp Account (Hong Kong bank account) instead of a Virtual Account
For detailed instructions on adding offramp accounts for international recipients, see our Wallet Guide.

Execute Payment

Now you can send the wallet transfer payment from User A (US) to Business B (Hong Kong). Create a crypto transfer that sends USDC from User A’s wallet to Business B’s wallet.
Transfers can be sent to other HIFI users or external wallet addresses. For this example, we will send to another HIFI user (Business B).
For this recipe, we will demonstrate the use of Transfer Approvals. This is a two-step process:
  1. Create crypto transfer request - Mark requireApproval as true in the request fields (User A)
  2. Accept the transfer approval - Accept the approval through the Approve a transfer endpoint (Business B)
Create a crypto transfer request that requires approval by setting requireApproval: true in the Create a Crypto Transfer endpoint.Request:
curl --request POST \
  --url https://sandbox.hifibridge.com/v2/wallets/transfers \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '
{
  "requestId": "86dbb25a-ef63-496a-a26c-8bdeaf277568",
  "currency": "usdc",
  "amount": 10,
  "chain": "POLYGON",
  "source": {
    "userId": "3b77cec8-1bb9-5ea0-91a1-e8c1411dd429"
  },
  "destination": {
    "userId": "e06caa8a-4c0f-58d7-95a5-baf676e1e39a",
    "requireApproval": true
  }
}
'
Request Fields:
requestId
string
required
Unique identifier (UUID) for this transfer request to ensure idempotency.
currency
string
required
Source cryptocurrency (e.g., usdc, usdt).
amount
number
required
Amount of USDC to send from User A’s wallet.
chain
string
required
Blockchain network (e.g., POLYGON, ETHEREUM).
source
object
required
Source of the transfer (User A).
destination
object
required
Destination of the transfer (Business B)
Response:
{
  "transferType": "WALLET.TRANSFER",
  "transferDetails": {
    "id": "ec2702f3-fe0e-46b0-9fd8-24a12dbd33a3",
    "requestId": "c776758a-3246-4347-badf-6a6aa97f0529",
    "createdAt": "2025-11-25T18:08:28.924Z",
    "updatedAt": "2025-11-25T18:08:28.924Z",
    "chain": "POLYGON",
    "currency": "usdc",
    "contractAddress": "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582",
    "status": "PENDING_APPROVAL",
    "failedReason": null,
    "source": {
      "userId": "3b77cec8-1bb9-5ea0-91a1-e8c1411dd429",
      "walletAddress": "0x63dAbB631bcE6d6090A4e6c5c7aB20d3D853C338",
      "walletType": "INDIVIDUAL",
      "user": {
        "email": "[email protected]",
        "lastName": "Doe",
        "firstName": "John"
      }
    },
    "destination": {
      "userId": "e06caa8a-4c0f-58d7-95a5-baf676e1e39a",
      "walletAddress": "0x8356d234077d4b7c71E35c95B075ed4ba5FF7681",
      "user": {
        "email": "[email protected]",
        "businessName": "HKG Test Business"
      }
    },
    "amount": 10,
    "approval": {
      "id": "528f0c38-6a82-41b1-8e35-c07c7b6cc556",
      "status": "PENDING",
      "createdAt": "2025-11-25T18:08:28.976103+00:00"
    }
  }
}
Response Fields:
transferDetails.id
string
Unique crypto transfer transaction ID.
transferDetails.type
string
Transfer type.
transferDetails.status
string
Current status. PENDING_APPROVAL means you have an active transfer approval waiting to be accepted.
transferDetails.source
object
Source details showing User A’s wallet.
transferDetails.destination
object
Destination details showing Business B’s wallet info.
transferDetails.amount
number
Amount of USDC being transferred.
transferDetails.approval
object
Transfer approval details.

Understanding Transfer Approvals

After submitting a transfer request with requireApproval: true:
  • The transfer status becomes PENDING_APPROVAL and does not execute
  • Dashboard admins receive email notifications about the pending approval
  • Authorized admins review and either approve or reject the transfer
  • If approved → transfer executes normally. If rejected, transfer is cancelled
For detailed instructions on the approval workflow, see:
👉 Transfer Approvals Guide

Execute Offramp

With the approved transfer, Business B now has everything they need to transfer the USDC funds from their wallet into their HKD bank account. This is a two-step process:
  1. Create offramp request - Get a quote for the conversion
  2. Accept the quote - Execute the payment

Required Information

In order to execute the offramp and complete this recipe, follow these steps:
  1. Create an offramp request with the source (wallet, chain, amount) and destination (fiat account), optionally including developer fees.
  2. Review the returned quote—rate, fees, send/receive amounts, and expiration—in transferDetails.quoteInformation.
  3. Confirm status is OPEN_QUOTE and save transferDetails.id to track and accept the quote.
  4. Accept the quote via POST /v2/offramps/{id}/quote/accept to convert stablecoins to fiat and initiate payout.
  5. Monitor statuses through crypto and fiat phases (e.g., CRYPTO_INITIATED → completion) using the receipt fields or webhooks.
For detailed instructions on creating an offramp transaction, see our Offramp Guide.

🎉 Congratulations!

You’ve successfully completed a marketplace payment! By following this recipe, you can now:
  • ✅ Generate API keys and authenticate with HIFI
  • ✅ Onboard users and complete KYC for multiple currency rails
  • ✅ Create virtual accounts for fiat-to-crypto conversion
  • ✅ Execute marketplace payments with transfer approvals
  • ✅ Provide transparent fee structures and quote expiration

Next Steps

Ready to build a full marketplace payment platform? Here’s what to explore next:
  • Webhooks - Set up real-time notifications for transaction status updates and user engagement
  • Offramp Guide - Deep dive into offramp transactions and compliance requirements
  • Onchain Transfers - Learn how to initiate and track onchain wallet-to-wallet transfers
  • Transfer Approvals - Configure optional approvals for higher-control transfer workflows
  • Error Handling - Implement robust error handling for failed transactions and expired quotes
  • API Reference - Explore all available endpoints and parameters for advanced features