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.

ACH Pull Integration Guide

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.


⚠️

The ACH Pull feature is currently available only to Applications registered as US-based companies. Your business operating location and company registered address must be located in a state where OwlPay holds a Money Transmitter License (MTL).

📘

Authorization Email Required

Before executing an ACH Pull transaction, OwlPay will send an authorization email to the Customer requesting their approval. The email will clearly identify which Application is initiating the pull request.

Prerequisites

  1. Your Application has the ACH_PULL payment method enabled
  2. The Customer has completed KYC verification (status: VERIFIED)
  3. The Customer's sub-account (an FBO bank account for on-ramp) has been provisioned (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.

Sandbox Test Credentials

When testing in the Sandbox environment, search for and select MX Bank in the connection widget. Use the following credentials to simulate successful connection scenarios:

1. Basic Connection

  • Search Bank Name: MX Bank
  • Username: mxuser
  • Password: Any value (e.g., password)
  • Expected Result: Successful connection with status CONNECTED.

2. OAuth Connection

  • Search Bank Name: MX Bank (OAuth)
  • Action:
    1. Search for "MX Bank (OAuth)" in the widget.
    2. You will be redirected to a simulated authorization page.
    3. Click the Authorize button.
  • Expected Result: Simulated success and redirection back to your application.

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"
            }
        }
    ]
}
FieldDescription
idUnique identifier of the linked account, prefixed with clbacc_. This ID is required in Step 4 when creating a transfer
nameAccount name
typeAccount type: CHECKING or SAVINGS
connection.institution_nameBanking institution name
connection.statusConnection status

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.

Settlement Time Notice: ACH Pull transfers typically settle in T+2 business days. For more details on fund availability and payment locks, see Settlement and Payment Lock Time.

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": {
        "payment_instrument": {
            "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.payment_instrument.linked_bank_account_idstringYesLinked account ID from Step 2 (prefixed with clbacc_)
destinationobjectYesDestination information (payout address, beneficiary details, etc.)

Response (201 Created)

On success, returns the full Transfer object with an initial status of PENDING_CUSTOMER_TRANSFER_START.

Error Scenarios

HTTP StatusReason
422Insufficient account balance to cover the transfer amount
422Customer has not completed bank compliance onboarding (CRB Onboarding)
422Customer is missing bank_module_app_customer_id
422The bank account has been blocklisted
404Customer not found or does not belong to this Application

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.


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.