Initiate Many ACH Transfers
The POST /api/fundsMovement/v2/ACHTransferMany endpoint enables batch processing of ACH (Automated Clearing House) transfers, allowing multiple payments to be dispatched from a single financial account in one API call. Each transfer in the batch is processed and reported individually, so partial success is possible — failed transfers do not block successful ones. This endpoint is secured with a one-time verification code to protect high-value bulk operations.
Endpoint
POST /api/fundsMovement/v2/ACHTransferMany
Authentication
Bearer token required. Obtain via:
POST https://api.banking.netevia.dev/api/auth/v2
Include in header: Authorization: Bearer {token}
Token lifetime: 10 minutes. Refresh via POST /api/auth/refresh.
When to use
Use this endpoint when a business customer needs to send multiple ACH payments in a single operation, such as processing payroll, paying vendors, or settling invoices in bulk. It is more efficient than calling the single-transfer endpoint repeatedly and provides a per-transfer status breakdown in the response. A one-time verification code (oneTimeCode) must be obtained and supplied with each request for security.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
oneTimeCode | string | Yes | 6-digit numeric one-time verification code. Pattern: ^\d{6}$. Obtain via the verification/OTP flow before calling this endpoint. |
secureOperationType | string | Yes | The type of OTP used. One of: ShortMessageCode, TimeBasedCode, LegacyTimeBasedCode. |
transferRequests | array | No | Array of individual ACH transfer request objects. Each item may be a fundsWithdrawalACHTransferRequest or a unifiedTransferRequest (see nested fields below). |
transferRequests[].fromFinancialAccountId | string | Yes (per item) | ID of the Netevia financial account funds are drawn from. |
transferRequests[].toFinancialAccountId | string | Yes (per item) | ID of the destination financial account (ACH-linked external account or Netevia account). |
transferRequests[].amount | integer (int32) | No | Transfer amount in cents (USD). Minimum: 1, Maximum: 4294967295. |
transferRequests[].currencyCode | integer | No | ISO 4217 numeric currency code. Only 840 (USD) is supported. |
transferRequests[].memo | string | No | Optional memo or note attached to the transfer. Maximum 1024 characters. |
transferRequests[].type | string | No | Account type for unified transfer requests. One of: Netevia, ACH. Only present when using unifiedTransferRequest items. |
{
"oneTimeCode": "482901",
"secureOperationType": "ShortMessageCode",
"transferRequests": [
{
"fromFinancialAccountId": "fa_xxxxxxxxxxxxxxxxxx01",
"toFinancialAccountId": "fa_xxxxxxxxxxxxxxxxxx02",
"amount": 150000,
"currencyCode": 840,
"memo": "Vendor payment - Invoice #1042"
},
{
"fromFinancialAccountId": "fa_xxxxxxxxxxxxxxxxxx01",
"toFinancialAccountId": "fa_xxxxxxxxxxxxxxxxxx03",
"amount": 250000,
"currencyCode": 840,
"memo": "Payroll - June 2026",
"type": "ACH"
}
]
}Response
200 OK
Returns an array of transfer response objects, one per item submitted in transferRequests. Each object reports the outcome of its corresponding transfer.
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for this transfer response entry. |
data | object | Payload containing the ACH transfer details (see nested fields below). |
data.initiateFundsWithdrawalACHTransfer | object | Details of the withdrawal leg of the ACH transfer, if applicable. |
data.initiateFundsDepositACHTransfer | object | Details of the deposit leg of the ACH transfer, if applicable. |
data.transfer | object | Consolidated transfer details. |
data.*.id | string | Unique transfer ID assigned by the platform. |
data.*.amount.value | integer (int64) | Transfer amount in cents. |
data.*.amount.currencyCode | string | Currency code string (e.g., "USD"). |
data.*.status.status | string | Transfer status (e.g., PENDING, COMPLETED, FAILED). |
data.*.status.statusReasonCode | string | Reason code qualifying the status, if applicable. |
data.*.fromFinancialAccount.id | string | ID of the source financial account. |
data.*.fromFinancialAccount.name | string | Display name of the source financial account. |
data.*.fromFinancialAccount.last4 | string | Last 4 digits of the source account number. |
data.*.fromFinancialAccount.externalBankAccountDetails | object | External bank account details for the source, if applicable. |
data.*.fromFinancialAccount.externalBankAccountDetails.last4 | string | Last 4 digits of external account number. |
data.*.fromFinancialAccount.externalBankAccountDetails.type | string | External account type (e.g., checking, savings). |
data.*.fromFinancialAccount.externalBankAccountDetails.routingNumber | string | Routing number of the external bank. |
data.*.fromFinancialAccount.externalBankAccountDetails.accountNumber | string | Full account number (sanitize as XXXXXXXXXX in logs). |
data.*.fromFinancialAccount.externalBankAccountDetails.createdAt | string (date-time) | When the external account was linked. |
data.*.fromFinancialAccount.externalBankAccountDetails.updatedAt | string (date-time) | When the external account record was last updated. |
data.*.fromFinancialAccount.provider | string | Provider used to link the external account (e.g., Finicity, Plaid). |
data.*.toFinancialAccount | object | Same structure as fromFinancialAccount for the destination account. |
data.*.errors | array | Array of error objects for this transfer, if it failed. |
data.*.errors[].path | array of strings | JSON path pointing to the field that caused the error. |
data.*.errors[].code | string | Machine-readable error code. |
data.*.errors[].description | string | Human-readable description of the error. |
errorCode | integer | Platform-level error code for this transfer entry; absent or null if no error. |
[
{
"id": "tr_resp_xxxxxxxxxxxxxxxxxx01",
"data": {
"initiateFundsWithdrawalACHTransfer": {
"id": "ach_xxxxxxxxxxxxxxxxxx01",
"amount": {
"value": 150000,
"currencyCode": "USD"
},
"status": {
"status": "PENDING",
"statusReasonCode": null
},
"fromFinancialAccount": {
"id": "fa_xxxxxxxxxxxxxxxxxx01",
"name": "Business Checking",
"last4": "1234",
"externalBankAccountDetails": null,
"provider": null
},
"toFinancialAccount": {
"id": "fa_xxxxxxxxxxxxxxxxxx02",
"name": "Vendor Account",
"last4": "5678",
"externalBankAccountDetails": {
"last4": "5678",
"type": "checking",
"routingNumber": "021000021",
"accountNumber": "XXXXXXXXXX",
"createdAt": "2026-01-15T10:30:00Z",
"updatedAt": "2026-01-15T10:30:00Z"
},
"provider": "Plaid"
},
"errors": []
},
"initiateFundsDepositACHTransfer": null,
"transfer": null
},
"errorCode": null
},
{
"id": "tr_resp_xxxxxxxxxxxxxxxxxx02",
"data": {
"initiateFundsWithdrawalACHTransfer": null,
"initiateFundsDepositACHTransfer": null,
"transfer": {
"id": "ach_xxxxxxxxxxxxxxxxxx02",
"amount": {
"value": 250000,
"currencyCode": "USD"
},
"status": {
"status": "PENDING",
"statusReasonCode": null
},
"fromFinancialAccount": {
"id": "fa_xxxxxxxxxxxxxxxxxx01",
"name": "Business Checking",
"last4": "1234",
"externalBankAccountDetails": null,
"provider": null
},
"toFinancialAccount": {
"id": "fa_xxxxxxxxxxxxxxxxxx03",
"name": "Payroll Account",
"last4": "9012",
"externalBankAccountDetails": null,
"provider": null
},
"errors": []
}
},
"errorCode": null
}
]Error Codes
| Code | When it happens |
|---|---|
| 400 | Missing required fields (oneTimeCode, secureOperationType), invalid OTP format (not 6 digits), or validation error on one or more transfer items |
| 401 | Bearer token missing, expired, or invalid |
| 403 | Caller does not have permission to initiate ACH transfers from the specified financial account |
| 404 | One or more fromFinancialAccountId or toFinancialAccountId values not found |
| 500 | Internal server error |
Common Mistakes
- Submitting an
oneTimeCodethat has already been used or has expired — each request requires a fresh OTP obtained immediately before calling this endpoint. - Using a string value for
currencyCodein the request body — the schema expects the numeric ISO 4217 code840(integer), not the string"USD". - Omitting
fromFinancialAccountIdortoFinancialAccountIdon any item intransferRequests— both fields are required per transfer item and will cause that item (or the entire request) to fail validation. - Assuming that a
200response means all transfers succeeded — always inspect each item in the response array forerrorCodeanddata.*.errorsto confirm individual transfer outcomes. - Setting
amountto0— the minimum allowed value is1(i.e., $0.01 USD). - Sending more transfer items than the account's daily ACH limits allow without verifying limits first.
Related Endpoints
POST /api/fundsMovement/v2/ACHTransfer— Initiate a single ACH transfer from own accountPOST /api/fundsMovement/v2/ACHTransferScheduled— Schedule a single ACH transfer for a future dateGET /api/fundsMovement/v2/transfers— List transfers for a financial accountPOST /api/auth/v2— Obtain a Bearer token for authenticationPOST /api/auth/refresh— Refresh an existing Bearer token
Example
curl -X POST https://api.banking.netevia.dev/api/fundsMovement/v2/ACHTransferMany \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"oneTimeCode": "482901",
"secureOperationType": "ShortMessageCode",
"transferRequests": [
{
"fromFinancialAccountId": "fa_xxxxxxxxxxxxxxxxxx01",
"toFinancialAccountId": "fa_xxxxxxxxxxxxxxxxxx02",
"amount": 150000,
"currencyCode": 840,
"memo": "Vendor payment - Invoice #1042"
},
{
"fromFinancialAccountId": "fa_xxxxxxxxxxxxxxxxxx01",
"toFinancialAccountId": "fa_xxxxxxxxxxxxxxxxxx03",
"amount": 250000,
"currencyCode": 840,
"memo": "Payroll - June 2026",
"type": "ACH"
}
]
}'