Link a Bank Account
This document describes how to integrate the ACH Pull (direct bank account debit) feature via the OwlPay Harbor API, allowing your end users to initiate deposits directly from their linked U.S. bank accounts.
Prerequisites
- Your Application has the
ACH_PULLpayment method enabled - The Customer has completed KYC verification (status:
VERIFIED) - The Customer has completed bank compliance onboarding (CRB Bank Onboarding status:
ONBOARDED)
Flow Overview
1. Connect Bank → User links a bank account via Widget
2. List Accounts → Retrieve the list of linked bank accounts
3. Get Quote → Get an ACH_PULL quote
4. Create Transfer → Submit a transfer with the linked account ID
5. Email Notification → OwlPay sends a notification email to the account holder
Important: When an ACH Pull transfer is created, OwlPay will send a notification email to the bank account holder informing them of the upcoming debit. Please ensure the user is aware of this process.
Step 1: Connect Bank Account
First, generate a bank connection Widget URL for the customer and redirect them to complete bank account authorization.
Request
curl --location --request POST 'https://harbor-sandbox.owlpay.com/api/v1/customers/{{CUSTOMER_UUID}}/bank-connections/widget-url' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-API-KEY: {{API_KEY}}' \
--data-raw '{
"redirect_url": {
"success": "https://yourapp.com/bank-connected",
"failed": "https://yourapp.com/bank-failed"
}
}'| Parameter | Type | Required | Description |
|---|---|---|---|
redirect_url.success | string | No | Redirect URL after successful connection. Supports web links or app deep links |
redirect_url.failed | string | No | Redirect URL on failure. Supports web links or app deep links |
Response (200)
{
"data": {
"widget_url": "https://bank-connection.owlpay.com/connect?token=..."
}
}Integration
Present the returned widget_url to the user via redirect or iframe. After the user completes bank login and account authorization in the Widget, the system will automatically sync account information.
Step 2: List Linked Accounts
After the user completes Widget authorization, call this API to retrieve the list of linked bank accounts.
Request
curl --location --request GET 'https://harbor-sandbox.owlpay.com/api/v1/customers/{{CUSTOMER_UUID}}/linked-bank-accounts' \
--header 'Accept: application/json' \
--header 'X-API-KEY: {{API_KEY}}'Response (200)
{
"data": [
{
"id": "clbacc_45b93a4affca4bc2818dc189264b6e07",
"name": "Checking Account",
"type": "CHECKING",
"connection": {
"institution_name": "Chase Bank",
"status": "CONNECTED",
"is_syncing": false,
"last_synced_at": "2026-03-21T08:30:00Z"
}
}
]
}To query a single account, use:
GET /api/v1/customers/{customer_uuid}/linked-bank-accounts/{linked_bank_account_id}
Step 3: Get ACH Pull Quote
Before initiating a transfer, get a quote to confirm the exchange rate, fees, and settlement amount.
Request
curl --location --request POST 'https://harbor-sandbox.owlpay.com/api/v2/transfers/quotes' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-API-KEY: {{API_KEY}}' \
--data-raw '{
"on_behalf_of": "{{CUSTOMER_UUID}}",
"source_amount": "100.00",
"source_currency": "USD",
"destination_currency": "USDC"
}'Response (200)
The response includes quotes for multiple payment methods. Select the item where payment_method is ACH_PULL.
[
{
"id": "quote_f03AirZ3ZY...KBtFQfZtPDYv",
"payment_method": "ACH_PULL",
"source_amount": "100.00",
"source_currency": "USD",
"destination_amount": "99.50",
"destination_currency": "USDC",
"fee": "0.50",
"quote_expire_date": "2026-03-21T09:30:00Z"
}
]Save the
id(i.e.,quote_id) from theACH_PULLquote — it is required in the next step to create the transfer.
Step 4: Create ACH Pull Transfer
Use the clbacc_ ID from Step 2 and the quote_id from Step 3 to create the transfer.
Request
curl --location --request POST 'https://harbor-sandbox.owlpay.com/api/v2/transfers' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-API-KEY: {{API_KEY}}' \
--header 'Idempotency-Key: {{Idempotency-Key}}' \
--data-raw '{
"on_behalf_of": "{{CUSTOMER_UUID}}",
"quote_id": "{{QUOTE_ID}}",
"application_transfer_uuid": "{{YOUR_ORDER_ID}}",
"source_linked_bank_account_id": "clbacc_45b93a4affca4bc2818dc189264b6e07",
"destination": {
"beneficiary_info": {
"...": "Fill according to quote requirements"
},
"payout_instrument": {
"address": "0x1234567890abcdef1234567890abcdef12345678"
},
"transfer_purpose": "SALARY",
"is_self_transfer": true
}
}'| Parameter | Type | Required | Description |
|---|---|---|---|
on_behalf_of | string | Yes | Customer UUID |
quote_id | string | Yes | Quote ID obtained in Step 3 |
application_transfer_uuid | string | Yes | Your order ID (used for idempotency) |
source_linked_bank_account_id | string | Yes | Linked account ID from Step 2 (prefixed with clbacc_) |
destination | object | Yes | Destination information (payout address, beneficiary details, etc. |
Email Notification
When an ACH Pull transfer is successfully created, OwlPay will automatically send a notification email to the bank account holder containing:
- Debit amount and currency
- Transaction details
This email ensures the account holder is aware of the upcoming ACH debit, in compliance with regulatory requirements. No additional action is needed from the Application side for this notification.
Complete Flow Diagram
Transfer Status Flow
| Status | Description |
|---|---|
PENDING_CUSTOMER_TRANSFER_START | Transfer created, awaiting ACH processing |
PENDING_HARBOR | Being processed by the bank |
COMPLETED | Transfer complete, funds settled |
REJECT | Transfer rejected |
ON_HOLD | Transfer temporarily frozen (may require additional review) |
We recommend listening for transfer status changes via Webhooks to keep your system updated in real time.
Updated about 4 hours ago