HomeGuidesRecipesAPI ReferenceChangelog
Log In
Guides

On-Ramping USD via ACH Pulls (Plaid)

On-Ramping USD via ACH Pulls (Plaid)

This guide explains how to integrate Unblock’s ACH pull flow so your US users can move USD from their bank accounts into your app using Plaid.


🤔 What is ACH Pull?

An ACH Pull lets a user permit Unblock to withdraw funds from a linked bank account. Plaid manages the secure bank connection. Your system never handles bank login credentials.


➡️ USD On-Ramp Flow with ACH Pull

  1. Create ACH profile for the user in Unblock
  2. Create the bank link via API and receive a Plaid Link URL
  3. Load Plaid Link so the user can confirm the bank accounts to link
  4. Verify link status until it is ACTIVE (poll the API or use the linkBankAccount webhook)
  5. Initiate the ACH pull for a USD amount from a specific linked account
  6. Monitor webhooks as the transfer and conversion progress

⚙️ Integration Steps

Step 1: Ensure User Is Ready and Create ACH Profile

The user must be verified as FULL_USER.

API reference:
Create a user profile for linking bank accounts
https://docs.dtr.org/reference/post_user-linked-bank-account-profile

curl --request POST \
  --url https://sandbox.getunblock.com/user/linked-bank-account/profile \
  --header 'Authorization: API-Key <YOUR_API_KEY>' \
  --header 'unblock-session-id: <USER_SESSION_ID>' \
  --header 'content-type: application/json' \
  --data '
{
  "currency": "USD",
  "investment_objective": "INCOME",
  "annual_income_range": "UNDER_25K"
}

Step 2: Create Bank Link and Get Plaid Link URL

Start the bank linking process by calling Unblock

This request returns a Plaid Link URL.
You must load this URL on your frontend in the next step.

API reference:
Link bank account
https://docs.dtr.org/reference/put_user-linked-bank-account-link

curl --request PUT \
  --url https://sandbox.getunblock.com/user/linked-bank-account/link \
  --header 'Authorization: API-Key <YOUR_API_KEY>' \
  --header 'unblock-session-id: <USER_SESSION_ID>' \
  --header 'content-type: application/json' \
  --data '
{
  "currency": "USD",
}

The response includes a Plaid Link URL used to confirm this account

Step 3: Load Plaid Link and Confirm the Account

Open the Plaid Link URL returned in Step 2 on your frontend.

Plaid handles authentication and consent.
Your system does not receive bank credentials.

If the user selects an invalid account (not of type "Checking"), the link fails and the bank account status becomes FAILED. In that case regenerate the Plaid Link URL again and provide it to the user.

Step 4: Verify the Link Status

After Plaid confirmation, the linked bank account moves from PENDING to a final status. You can:

  • Option A – Webhook (recommended): Subscribe to linkBankAccount webhooks (see Webhooks for linked bank account status below). When data.status is ACTIVE, the account is ready for ACH pulls. You also receive data.uuid (the linked bank account UUID) and, on failure, data.error.
  • Option B – Polling: Poll the GET linked bank account endpoint until the account status is ACTIVE.

API reference:
Get linked bank account info
https://docs.dtr.org/reference/get_user-linked-bank-account

Unlink if needed:
https://docs.dtr.org/reference/delete_user-linked-bank-account-unlink

curl --request GET \
  --url https://sandbox.getunblock.com/user/linked-bank-account \
  --header 'Authorization: API-Key <YOUR_API_KEY>' \
  --header 'unblock-session-id: <USER_SESSION_ID>'

Successful response example

{
  "profile_uuid": "5594401c-0072-4df2-be9c-d491c0754c21",
  "status": "ACTIVE",
  "linked_bank_accounts": [
    {
      "uuid": "abcdef01-1234-5678-90ab-cdef01234567",
      "status": "ACTIVE",
      "currency": "USD",
      "account_number": "******7890",
      "routing_number": "021000021",
      "account_type": "CHECKING",
      "bank_name": "Chase Bank"
    }
  ]
}

Step 5: Initiate an ACH Pull

Once the linked bank account status is ACTIVE, initiate the ACH pull for the desired USD amount.

You must specify which linked bank account to pull from using source_account_uuid. For some merchants, ip_address (IPv4 or IPv6) is required. When required and missing, the request is rejected. Sending ip_address when not required is optional. Note: only 3 ACH pulls per day are allowed per user (see ACH Pull Limits).

API reference:
Withdraw funds from linked bank account
https://docs.dtr.org/reference/post_user-linked-bank-account-pull

curl --request POST \
  --url https://sandbox.getunblock.com/user/linked-bank-account/pull \
  --header 'Authorization: API-Key <YOUR_API_KEY>' \
  --header 'unblock-session-id: <USER_SESSION_ID>' \
  --header 'content-type: application/json' \
  --data '
{
  "currency": "USD",
  "amount": 100.00,
  "source_account_uuid": "<LINKED_BANK_ACCOUNT_UUID>",
  "ip_address": "203.0.113.42"
}

The response includes a process_uuid that you can use for tracking.


Key Considerations for USD ACH On-Ramps

Prerequisites

  • User must be a FULL_USER
  • Required applicant data:
    • Date of birth
    • Country
    • Phone
    • TIN (SSN)

The user must accept the Terms & Conditions through the SDK
or submit third_party_end_user_agreement_hashes using POST /user.

The ACH profile must be created before starting the bank linking flow.

API reference:
Create a user profile for linking bank accounts
https://docs.dtr.org/reference/post_user-linked-bank-account-profile


Multiple Linked Bank Accounts

A user can link multiple bank accounts.

To remove a linked bank account, use the unlink endpoint with the specific account_uuid:

API reference:
Unlink bank account
https://docs.dtr.org/reference/delete_user-linked-bank-account-unlink

DELETE /user/linked-bank-account/unlink
{
  "account_uuid": "<ACCOUNT_UUID>"
}

You can link additional accounts by repeating the linking flow.


Bank Linking and Unlinking Limits

Users are limited in how many times they can link or unlink bank accounts within a 24-hour period:

  • Maximum 3 bank link events per 24 hours
  • Maximum 3 bank unlink events per 24 hours

If a user exceeds these limits, API requests may return a "LINK LIMIT EXCEEDED" error.


ACH Pull Limits

Users are limited to 3 ACH pulls per day (rolling 24-hour period). If a user exceeds this limit, the pull request may be rejected. Plan your UI and user messaging accordingly (e.g. inform users when they are close to the limit or after a pull is rejected).


Webhooks for linked bank account status

When a user links or unlinks a bank account (e.g. after completing Plaid), Unblock sends linkBankAccount webhooks so you can react to status changes without polling.

Payload shape:

{
  "type": "linkBankAccount",
  "subType": "statusUpdate",
  "uuid": "<user-uuid-or-company-uuid>",
  "data": {
    "uuid": "<linked-bank-account-uuid>",
    "status": "ACTIVE",
    "error": "<optional; when status is FAILED>"
  }
}
FieldDescription
type"linkBankAccount"
subType"statusUpdate"
uuidUser UUID (or company UUID for corporates) — entity that owns the linked bank account.
data.uuidLinked bank account UUID.
data.statusACTIVE, CHECK_COMPLETED, PENDING, FAILED, or DISABLED.
data.errorPresent when status is FAILED; contains the failure reason.

Typical flow:

  • Right after the user completes Plaid, the account is PENDING.
  • When the provider confirms the link, you receive a webhook with status: "ACTIVE" — the account is then ready for ACH pulls (Step 5).
  • If the user selected an invalid account (e.g. not Checking), you receive status: "FAILED" and can use data.error and regenerate the Plaid Link URL.

Configure your webhook endpoint via our API. Full reference: Webhooks.


Settlement Time

ACH transfers take several business days to complete.
You will receive webhook events such as:

  • OUTSIDE_TRANSFER_RECEIVED

These confirm when funds clear and when the conversion process begins.


Error Handling

A FAILED status on the linked bank account usually means that user has selected an invalid account. In that case generate the Plaid Link URL again so the user can link a correct one.


Private Sandbox Testing

Plaid step

  • Select the bank account that ends with 0000

Manual bank account details

Use the following values when creating the bank link:

{
  "currency": "USD",
}