Request an ACH Pull
Debits a pull account into the account's USD bank account. The amount shows at once as pending on the balance. For five minutes, until grace_ends_at, the pull can be cancelled; then it is submitted to the bank. See How ACH Pulls Work for what follows.
New in v3: v2 has no equivalent.
A workspace-scoped key names the account with account_id; an account-scoped key acts for its own account.
Authorization:
Every request is signed with your API key: a short-lived JWT, signed with your private key, sent in one header on /v3/* and /api/v2/* alike.
Headers:
Authorization: APIKey {SIGNED_JWT}
How to build and sign the JWT: Authentication.
Endpoint
/v3/banking/ach-pulls
Headers
| Header | Description |
|---|---|
Idempotency-Key | Required, from every caller, API keys included: there is no grace period. One UUID per intended pull, sent again unchanged on every retry of it. A retry answers the first response instead of debiting the holder twice. See API v3 overview. |
Query parameters
| Parameter | Type | Description |
|---|---|---|
account_id | string (cst_…) | The account to fund. Required for a workspace-scoped key; an account-scoped key funds its own account and may omit it. |
Request Body Sample
{
"pull_account_id": "bpa_01k8x4p6sx9t5v7x1z3b5d7f9h",
"amount": "250.00",
"reference": "top-up-2026-10-19",
"description": "Top up"
}
Request Body Schema
| Field | Type | Description |
|---|---|---|
pull_account_id | string (bpa_…) | Required. The pull account to debit. |
amount | string (decimal) | Required. US dollars, greater than zero, in whole cents ("250.00"), as a decimal string, never a number. |
reference | string | Optional. Your own reference, up to 255 characters. |
description | string | Optional. Up to 255 characters. Its first ten characters are the line the holder sees on their bank statement; FUNDING when empty. |
Response Sample
201 Created
{
"id": "ach_01k8x5c4ze2a6b8d0f2h4j6k8m",
"account_id": "cst_01h455vb4pex5vsknk084sn02q",
"pull_account_id": "bpa_01k8x4p6sx9t5v7x1z3b5d7f9h",
"bank_account_id": "bnk_01k8x4m2qv7r3t5w9y1a3c5e7g",
"amount": { "amount": "250.00", "currency": "USD" },
"fee": { "amount": "0.00", "currency": "USD" },
"total": { "amount": "250.00", "currency": "USD" },
"status": "pending",
"status_reason": "Pull requested, waiting for the grace period",
"sec_code": "WEB",
"reference": "top-up-2026-10-19",
"description": "Top up",
"grace_ends_at": "2026-10-19T15:05:00Z",
"created_at": "2026-10-19T15:00:00Z",
"updated_at": "2026-10-19T15:00:00Z"
}
Response Schema
| Field | Type | Description |
|---|---|---|
id | string (ach_…) | The pull. |
account_id | string (cst_…) | The account it funds. |
pull_account_id | string (bpa_…) | The pull account debited. |
bank_account_id | string (bnk_…) | The USD bank account credited: the account's default. |
amount | money | What is debited from the pull account. |
fee | money | What the platform keeps. No fee is charged today. |
total | money | What the bank account is credited. Once the pull completes, all of it but reserved is available. |
reserved | money | The share of total set aside in the account's reserve when the pull completed: held, not spendable. Absent until then, and when nothing was set aside. See The reserve. |
available | money | What of total became spendable when the pull completed: total less reserved. Absent until then. |
source | object | The account debited, as it stood when the pull was requested — kept if that pull account is later edited or deleted: name, bank_name, holder_name, holder_type, routing_number, account_number_last4, account_type. Never the full account number. |
status | string | pending, processing, posted, completed, cancelled, failed or returned. See How ACH Pulls Work. |
status_reason | string | Why the pull is where it is, in words. |
return_code | string | The NACHA return reason (R01, R10, …) once returned. |
sec_code | string | The NACHA class the debit travels under: WEB for a person's account, CCD for a business's. |
reference, description | string | As sent; absent when empty. |
held | boolean | true while the bank reviews the debit before sending it. Absent otherwise. |
grace_ends_at | string (date-time) | Until when the pull can be cancelled. |
posted_at | string (date-time) | When the debit settled. Absent until posted. |
available_at | string (date-time) | When the money becomes available: posted_at, as no hold applies. Absent until posted. |
completed_at | string (date-time) | When the money became available. Absent until completed. |
created_at / updated_at | string (date-time) | RFC 3339, UTC. |
A money value is { "amount": "<decimal string>", "currency": "USD" }.
Errors
| Status | message | When |
|---|---|---|
400 | account_id_required | A workspace-scoped key named no account_id. |
400 | failed to parse request | The body is not valid JSON, amount is not a decimal, or an id has the wrong form. |
400 | invalid_amount | amount is missing, not greater than zero, or has fractions of a cent. |
400 | idempotency_key_required | No Idempotency-Key header. |
400 | idempotency_key_invalid | The Idempotency-Key is longer than 255 characters or not printable ASCII. |
401 | unauthorized | Missing or invalid signature. |
403 | forbidden | An account-scoped key named another account in account_id. |
403 | account_frozen | The account is frozen or blocked. |
404 | feature_not_enabled | ACH pulls are not enabled for the account. |
404 | pull_account_not_found | No such pull account, or it belongs to another account. |
409 | idempotency_in_progress | The same Idempotency-Key is still running. Retry after Retry-After seconds with the same key. |
413 | body_too_large | The body is over 1 MiB. |
422 | pull_account_disabled | A return or a notification of change stopped the pull account. See Update a Pull Account. |
422 | ach_pull_amount_above_limit | amount is above the per-pull limit (Get ACH Pull Limits). |
422 | ach_pull_customer_limit_exceeded | With this pull, the account's open pulls would exceed their limit. |
422 | deposit_account_not_found | The account has no active USD bank account to credit. |
422 | Validation failed | A required field is missing, or a text field is too long; details names it. |
422 | idempotency_key_conflict | The same Idempotency-Key was used for a different request. |
503 | idempotency_unavailable | The key store is unavailable. Nothing ran: retry later with the same key. |
Support and Resources
If you need assistance or have any questions, our support team is here to help. You can contact our support team at support@blockwyre.com.
Stay Updated
Stay up-to-date with the latest news, updates, and features from BlockWyre by following us on social media:
- Instagram: BlockWyre Instagram
- Twitter: BlockWyre Twitter
- Facebook: BlockWyre Facebook
- LinkedIn: BlockWyre LinkedIn
We are excited to have you on board and look forward to seeing how you leverage BlockWyre's powerful tools to enhance your financial operations. Happy integrating!