Overview
ACH (Automated Clearing House) is the workhorse of US domestic payments. It handles billions of transactions annually for payroll, vendor payments, recurring billing, and general-purpose transfers. OpenFX supports both standard ACH (1-3 business day settlement) and same-day ACH (settlement within hours on the same business day).
ACH payments are created through the POST /payments/ach endpoint with a us_bank payment method on the counterparty.
ACH is a batch-processed network. Payments are grouped and submitted in batches to the Federal Reserve, which is why settlement takes hours or days rather than seconds. Same-day ACH has specific cutoff windows that vary by institution.
ACH types
The achType field controls settlement speed:
| Type | Settlement | Cutoff | Max amount | Use case |
|---|
standard | 1-3 business days | 21:00 UTC | No per-transaction limit | Payroll, vendor payments, recurring transfers |
same_day | Same business day (hours) | Earlier cutoff windows | $1,000,000 per transaction | Urgent payments, time-sensitive disbursements |
If you do not specify achType, the default is standard. Use same_day only when you need faster settlement — it typically incurs a higher fee.
SEC codes
The SEC (Standard Entry Class) code identifies the type of ACH transaction and determines the authorization requirements. The secCode field accepts:
| Code | Name | Description | Typical use |
|---|
WEB | Internet-Initiated | Authorized via the internet | Online payments, e-commerce, consumer transfers |
PPD | Prearranged Payment and Deposit | Consumer transactions with a standing authorization | Direct deposit, recurring consumer payments |
CCD | Corporate Credit or Debit | Business-to-business transactions | Corporate vendor payments, intercompany transfers |
CTX | Corporate Trade Exchange | Business payments with addenda records | Trade payments with invoice data, EDI-linked payments |
Choosing the wrong SEC code can result in NACHA violations. Use CCD or CTX for business-to-business payments and WEB or PPD for consumer payments. If unsure, CCD is the safest choice for corporate payments.
The SEC code determines the authorization model for the ACH transaction. Business-to-business
payments should use CCD (single payment) or CTX (payment with addenda/invoice data).
Consumer-facing payments should use WEB (internet-authorized) or PPD (pre-authorized
recurring). Mismatched SEC codes can result in NACHA rule violations and return code R17
(file record edit criteria).
Prerequisites
Before creating an ACH payment, you need:
- An active customer (KYB approved) with a funded USD account (
demand_deposit type)
- A counterparty with a
us_bank payment method containing:
routingNumber — 9-digit ABA routing number
accountNumber — Bank account number
bankAccountType — checking or savings
Creating an ACH payment
curl -X POST https://sandbox.api.openfx.com/v1/payments/ach \
-H "Authorization: Bearer $API_KEY" \
-H "X-Signature: $SIGNATURE" \
-H "X-Timestamp: $TIMESTAMP" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"sourceAccountId": "acc_01953e1a5f4b7002",
"counterpartyId": "cpt_01953e1a5f4b7004",
"paymentMethodId": "pm_01953e1a5f4b7300",
"amount": {
"currency": "USD", "value": "250000"
},
"achType": "standard",
"secCode": "CCD",
"reference": "Invoice #INV-2026-0087",
"metadata": {
"invoiceId": "INV-2026-0087",
"department": "accounts_payable"
}
}'
The response returns a Payment resource with status: created. The payment will progress through processing and eventually reach completed or returned.
Same-day ACH
To send a same-day ACH payment, set achType to same_day:
curl -X POST https://sandbox.api.openfx.com/v1/payments/ach \
-H "Authorization: Bearer $API_KEY" \
-H "X-Signature: $SIGNATURE" \
-H "X-Timestamp: $TIMESTAMP" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"sourceAccountId": "acc_01953e1a5f4b7002",
"paymentMethodId": "pm_01953e1a5f4b7300",
"amount": {
"currency": "USD", "value": "1500000"
},
"achType": "same_day",
"secCode": "CCD",
"reference": "Urgent vendor payment"
}'
Same-day ACH has a per-transaction limit of $1,000,000 set by NACHA. Payments exceeding this amount must use standard ACH or a different rail like Fedwire.
ACH returns
ACH payments can be returned by the receiving bank for various reasons. When a return occurs, the payment transitions to returned status and the returnCode field on the Payment provides the NACHA return reason.
Common return codes
| Code | Reason | Description | Action |
|---|
R01 | Insufficient funds | Not enough funds in the receiver’s account | Retry later or contact the counterparty |
R02 | Account closed | The receiver’s account is closed | Update the payment method with a new account |
R03 | No account / unable to locate | Routing/account number does not match a valid account | Verify counterparty payment method details |
R04 | Invalid account number | Account number structure is invalid | Correct the account number on the payment method |
R10 | Customer advises unauthorized | The receiver disputes the transaction | Review authorization and respond to dispute |
R29 | Corporate customer advises not authorized | Corporate counterparty disputes the transaction | Review authorization agreement |
ACH returns can arrive up to 2 business days after settlement for standard returns and up to 60 calendar days for unauthorized transaction disputes (R10, R29). Always monitor the payment.returned webhook event for timely notification.
Handling returns
When a payment is returned, you receive a payment.returned webhook and the payment status changes to returned. You can then decide how to respond:
# Get the returned payment to see the return code
curl -X GET https://sandbox.api.openfx.com/v1/payments/ach/pmt_01953e1a5f4b7005 \
-H "Authorization: Bearer $API_KEY" \
-H "X-Signature: $SIGNATURE" \
-H "X-Timestamp: $TIMESTAMP"
Payment lifecycle
created -> processing -> completed
-> returned
-> failed
| Status | Description |
|---|
created | Payment accepted and queued for processing |
processing | Payment submitted to the ACH network |
completed | Funds successfully delivered |
returned | Payment returned by the receiving bank (see return codes above) |
failed | Payment could not be processed (e.g., insufficient funds in source account) |
Provider mapping reference
Under the hood, POST /payments/ach maps to the banking provider’s ACH origination endpoint. The Platform API normalizes provider-specific field names and status values into the OpenFX schema.
Field mapping:
| OpenFX Field | Provider Field | Notes |
|---|
sourceAccountId | deposit_account_id | The funded account to debit |
paymentMethodId | counterparty_us_bank_account_id | The counterparty’s bank account |
amount (decimal string) | amount.value (string cents) + amount.currency="USD" | Amount conversion from OpenFX to provider format |
secCode | sec_code | SEC code passed through directly |
reference | company_discretionary_data | Free-text reference for the counterparty |
Status mapping:
| Provider Status | OpenFX Status |
|---|
PENDING | processing |
SETTLED | completed |
FAILED | failed |
RETURNED | returned |
The Idempotency-Key header is mapped to the provider’s idempotency header automatically.
You do not need to handle provider-specific header formats.
API reference