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

  1. Your Application has the ACH_PULL payment method enabled
  2. The Customer has completed KYC verification (status: VERIFIED)
  3. 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"
    }
}'
ParameterTypeRequiredDescription
redirect_url.successstringNoRedirect URL after successful connection. Supports web links or app deep links
redirect_url.failedstringNoRedirect 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 the ACH_PULL quote — 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
    }
}'
ParameterTypeRequiredDescription
on_behalf_ofstringYesCustomer UUID
quote_idstringYesQuote ID obtained in Step 3
application_transfer_uuidstringYesYour order ID (used for idempotency)
source_linked_bank_account_idstringYesLinked account ID from Step 2 (prefixed with clbacc_)
destinationobjectYesDestination 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

StatusDescription
PENDING_CUSTOMER_TRANSFER_STARTTransfer created, awaiting ACH processing
PENDING_HARBORBeing processed by the bank
COMPLETEDTransfer complete, funds settled
REJECTTransfer rejected
ON_HOLDTransfer temporarily frozen (may require additional review)

We recommend listening for transfer status changes via Webhooks to keep your system updated in real time.