Customer's Deposit Account (on-ramp) (Beta)
Learn how to provision dedicated USD bank accounts for automatic fiat-to-stablecoin on-ramp transfers.
The Customer's Deposit Account feature allows you to provision dedicated, virtual fiat bank accounts for your verified customers. This acts as an asynchronous fiat-to-crypto on-ramp service.
Beta FeatureCustomer's Deposit Account is currently in Beta. Some fields, validation rules, or limits might be subject to change. Please contact your OwlPay representative for sandbox access and production onboarding.
When a customer transfers fiat currency (via USD ACH Push or USD Wire Transfer) into their dedicated bank account, Harbor automatically:
- Detects the incoming deposit.
- Converts the fiat amount to cryptocurrency (USDC) minus applicable fees.
- Transfers the stablecoin directly to the specified
destination(either an external blockchain address or a Harbor wallet).
💡 Use Cases
Here are some of the key use cases for Customer's Deposit Account:
- Automated B2B Remittance & Pay-in: Enable your business clients or merchants in the United States to settle payments in traditional fiat. Payer deposits funds into the dedicated bank account, and the platform automatically receives USDC on-chain.
- Recurring Wallet Funding: Provide your users with their own persistent bank routing and account numbers. Users can configure recurring transfers (e.g., payroll deposits) from their traditional banking apps, automatically funding their web3 wallets with USDC.
- Seamless Brokerage & Exchanges: Onboard users into decentralized platforms. Users send fiat from their external bank accounts, and it instantly becomes liquid USDC in their trading account without having to go through a manual checkout flow.
🔄 How It Works (The Lifecycle)
The full integration lifecycle follows five simple phases:
- Provisioning (
provisioning): When you create a deposit account, the API asynchronously provisions a dedicated bank account with the banking partner. The account starts in theprovisioningstatus, anddeposit_instructionsisnull. - Active (
active): Once provisioning completes, the account status flips toactive, anddeposit_instructionspopulated with the routing number, bank name, bank address, account number, and holder name. At this point, Harbor fires thedeposit_accountwebhook with the instructions. - Incoming Deposits: Your end-customer sends traditional fiat (US Dollar via WIRE or ACH Push) to the routing and account number provided in the instructions.
- Auto-Conversion & Settlement:
Upon receipt, Harbor converts the USD to USDC (minus applicable commissions/fees) and transfers it to the defined
destination. - Modification or Termination:
You can retarget the destination address as long as the account status is
active. You can also permanently disable the deposit account. Disabling deletes the underlying bank account and is completely irreversible.
🛑 Key Requirements & Compliance (Travel Rule)
Because this feature transfers fiat to crypto on behalf of customers, the Travel Rule and compliance information must be supplied at creation time under the destination block.
This includes:
- Beneficiary Info: Legal name, date of birth, ID document number, and residential address of the beneficiary.
- Transfer Purpose: A standard enum detailing the purpose (e.g.,
SALARY,FAMILY_MAINTENANCE,PRODUCT_INDEMNITY_INSURANCE, etc.). - Wallet Type & Institution: Flagging whether the wallet is custodial/non-custodial and specifying the receiving wallet provider (e.g., self-hosted, Coinbase, etc.).
💻 Step-by-Step API Walkthrough
Step 1: Create a Customer
Before creating a deposit account, you must have a verified customer in the system. If you haven't created one, refer to Create a Customer.
Step 2: Create a Deposit Account
Create a dedicated deposit account for the customer. Currently, source_country must be US and destination.asset must be USDC.
curl --location --request POST 'https://harbor-sandbox.owlpay.com/api/v1/customers/cus_1234567890/deposit_accounts' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-API-KEY: {YOUR_API_KEY}' \
--header 'X-Idempotency-Key: {YOUR_IDEMPOTENCY_KEY}' \
--data-raw '{
"source_country": "US",
"destination": {
"type": "external_address",
"asset": "USDC",
"chain": "ethereum",
"address": "0x1234567890abcdef1234567890abcdef12345678",
"is_self_transfer": true,
"transfer_purpose": "SALARY",
"beneficiary_receiving_wallet_type": "non_custodial",
"beneficiary_institution_name": "MyPersonalWallet",
"beneficiary_info": {
"beneficiary_name": "John Doe",
"beneficiary_dob": "1990-01-01",
"beneficiary_id_doc_number": "12345678",
"beneficiary_address": {
"street": "123 4th Ave S",
"city": "Minneapolis",
"state_province": "MN",
"postal_code": "55416",
"country": "US"
}
}
},
"application_reference_id": "ref-0001"
}'Response (HTTP 202 Accepted):
{
"data": {
"id": "depacc_1234567890",
"object": "customer_deposit_account",
"customer_id": "cus_1234567890",
"source_country": "US",
"status": "provisioning",
"destination": {
"type": "external_address",
"asset": "USDC",
"chain": "ethereum",
"address": "0x1234567890abcdef1234567890abcdef12345678",
"memo": null,
"beneficiary_info": {
"beneficiary_name": "John Doe",
"beneficiary_dob": "1990-01-01",
"beneficiary_id_doc_number": "12345678",
"beneficiary_address": {
"street": "123 4th Ave S",
"city": "Minneapolis",
"state_province": "MN",
"postal_code": "55416",
"country": "US"
}
},
"transfer_purpose": "SALARY",
"is_self_transfer": true,
"beneficiary_receiving_wallet_type": "non_custodial",
"beneficiary_institution_name": "MyPersonalWallet"
},
"deposit_instructions": null,
"commission": {
"percentage": 0.5,
"amount": 25
},
"application_reference_id": "ref-0001",
"created_at": "2026-06-10T00:00:00+00:00",
"updated_at": "2026-06-10T00:00:00+00:00"
}
}Step 3: Receive Deposit Instructions (via Webhook or Polling)
Since the deposit account is provisioned asynchronously, you will receive real-time notifications by subscribing to the deposit_account webhook (refer to the Webhook Subscriptions guide).
Alternatively, you can poll the GET endpoint to retrieve the status:
curl --location --request GET 'https://harbor-sandbox.owlpay.com/api/v1/deposit_accounts/depacc_1234567890' \
--header 'Accept: application/json' \
--header 'X-API-KEY: {YOUR_API_KEY}'Once active, the deposit_instructions field is populated with banking details:
{
"data": {
"id": "depacc_1234567890",
"object": "customer_deposit_account",
"customer_id": "cus_1234567890",
"source_country": "US",
"status": "active",
"destination": {
"type": "external_address",
"asset": "USDC",
"chain": "ethereum",
"address": "0x1234567890abcdef1234567890abcdef12345678"
},
"deposit_instructions": {
"account_number": "9801234567",
"routing_number": "021214891",
"bank_name": "Cross River Bank",
"bank_address": "2115 Linwood Avenue, Fort Lee, NJ 07024",
"account_holder_name": "OwlTing Inc. FBO John Doe"
},
"commission": {
"percentage": 0.5,
"amount": 25
},
"application_reference_id": "ref-0001",
"created_at": "2026-06-10T00:00:00+00:00",
"updated_at": "2026-06-10T00:00:00+00:00"
}
}Step 4: Retarget or Disable the Account
If you need to change where the funds are routed (only allowed for active status), or permanently close the bank account:
curl --location --request PATCH 'https://harbor-sandbox.owlpay.com/api/v1/deposit_accounts/depacc_1234567890' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-API-KEY: {YOUR_API_KEY}' \
--data-raw '{
"destination": {
"type": "external_address",
"asset": "USDC",
"chain": "arbitrum",
"address": "0x567890abcdef1234567890abcdef1234567890ab",
"is_self_transfer": true,
"transfer_purpose": "SALARY",
"beneficiary_receiving_wallet_type": "non_custodial",
"beneficiary_institution_name": "MyArbitrumWallet",
"beneficiary_info": {
"beneficiary_name": "John Doe",
"beneficiary_dob": "1990-01-01",
"beneficiary_id_doc_number": "12345678",
"beneficiary_address": {
"street": "123 4th Ave S",
"city": "Minneapolis",
"state_province": "MN",
"postal_code": "55416",
"country": "US"
}
}
}
}'To permanently close the bank account (irreversible):
curl --location --request PATCH 'https://harbor-sandbox.owlpay.com/api/v1/deposit_accounts/depacc_1234567890' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-API-KEY: {YOUR_API_KEY}' \
--data-raw '{
"status": "disabled"
}'
Disabling is PermanentOnce disabled, the account cannot be re-enabled or retargeted. This physically deletes the virtual banking link at our provider. To resume, you must create a new deposit account.
Step 5: Simulate Deposit (Sandbox Only)
To test the automatic settlement flow in the Sandbox environment, simulate a payment to your deposit account using the simulation API:
curl --location --request POST 'https://harbor-sandbox.owlpay.com/api/v1/deposit_accounts/depacc_1234567890/simulate_payment' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-API-KEY: {YOUR_API_KEY}' \
--data-raw '{
"amount": 1000,
"method": "WIRE",
"sender_name": "John Doe"
}'Response (HTTP 202):
{
"data": {
"id": "order_1234567890",
"status": "pending"
}
}This triggers the downstream flow: receiving the fiat, initiating a transfer, converting to USDC, and delivering to the defined destination address. You can trace this order status via transfer status hooks and standard transaction logs.
📋 Listing Deposit Accounts
You can list all deposit accounts associated with a specific customer:
curl --location --request GET 'https://harbor-sandbox.owlpay.com/api/v1/customers/cus_1234567890/deposit_accounts?per_page=15' \
--header 'Accept: application/json' \
--header 'X-API-KEY: {YOUR_API_KEY}'This returns a paginated list of accounts, allowing you to manage and audit provisioned accounts easily.
Updated about 2 hours ago