How To: Implement ACH Payments
How To: Implement ACH Payments
Developers can implement ACH payments with Coinflow via checkout link, SDK, or API, including API order-type enforcement and authorization records.
🏦 ACH Overview
ACH payments settle to your chosen settlement location (bank account or USDC).
Flow:
- Merchant submits transaction.
- User initiates ACH purchase.
- Coinflow processes ACH (≈3 business days).
- On completion, Coinflow settles to your configured settlement location.
Implementing ACH via SDK (React)
- Generate session key
- Tokenize checkout parameters
- Implement
<CoinflowPurchase>Component
Advanced: Settlement to a contract
Merchants settling to a contract may pass a transaction prop. See the advanced settlement docs for details.
<CoinflowPurchase> already shows an ACH-authorization checkbox before the purchase button for existing bank accounts. Checking it creates a one_time_purchase ACH authorization record (via the shopper’s session key) and the SDK forwards the resulting achAuthorizationId/orderType on checkout automatically — no additional props or integration changes needed. See ACH Order Type Enforcement and Authorization Records below for what this backs and when it’s enforced.
Implementing ACH via API
- Tokenize payment data
- Get totals
- Add customer bank account
You can also have the customer link their own bank account through Coinflow’s bank authentication UI, which returns the same reusable token — see How To: Link a Bank Account for ACH Checkout.
- Get tokenized bank account
5.Send ACH checkout
token is required — it’s the customer’s bank account token, returned by Step 4 above at customer.bankAccounts[].token or by either bank-linking flow in How To: Link a Bank Account for ACH Checkout.
If your merchant account has allowedOrderTypes configured, add orderType and achAuthorizationId to this request body — see the next section. achAuthorizationId must reference an authorization record you created before this checkout call; there’s no way to attach one after the fact.
orderType is becoming required for all API integrations
Coinflow will soon require orderType on every ACH checkout for all merchants integrating via the API, regardless of whether allowedOrderTypes is configured. If you haven’t already, start passing orderType on this request now to avoid disruption when this takes effect.
ACH Order Type Enforcement and Authorization Records
Some ACH use cases — recurring debits, balance top-ups, limit orders, and fair-market-value fills — need a complete, auditable record of what the customer agreed to at the time they authorized the debit: the exact disclosure text they saw, when they consented, and the structured terms of the order (frequency and amount for a subscription, bid price and quantity for a limit order, and so on).
ACH Authorization Records capture that consent with Coinflow before the debit happens, and let you report status changes (like the order being fully filled or archived) — or revoke the record entirely — as things change afterward. Coinflow reconciles the authorization against the real customer and payment server-side, so the record can’t drift from what was actually charged.
Is this required for my integration?
orderType will soon be required on every ACH checkout for all API integrations — see the notice above. Separately, the allowedOrderTypes merchant setting restricts which orderType values are accepted, and configuring it (with at least one value) also requires a valid achAuthorizationId on every ACH checkout. It’s opt-in — contact your Coinflow integration representative to enable it for your account.
<CoinflowPurchase> handles the one_time_purchase case automatically: its existing ACH-authorization checkbox creates the authorization record (via the shopper’s session key) as soon as it’s checked, and the SDK forwards orderType/achAuthorizationId on checkout — no integration changes required. Recurring subscriptions, balance top-ups, limit orders, and fair-market-value fills remain API-only; <CoinflowPurchase> does not expose orderType/achAuthorizationId as props for those cases, so merchants needing them must integrate directly against /checkout/ach/{merchantId} (Step 1 below) as described in this section.
How It Works
Step 1: Create the Authorization Record
Before initiating checkout, create the authorization record from wherever you’re capturing consent — this must happen first, since the record has to exist before the checkout call that references it.
Save the _id from the response — that’s the achAuthorizationId you’ll pass to every checkout call this authorization backs, starting with Step 2.
Two ways to authenticate this call
- Customer session key — pass the same session key your integration already uses for checkout. The customer is resolved from the session; omit
customerId. - Admin-scoped API key — pass
customerIdexplicitly in the body. Never expose your admin API key to a client.
routingNumber and accountLastFour are derived server-side from the bank account behind the token you send — they’re not part of the request.
idempotencyKey is required
Pass a string unique to this intended checkout (a UUID you generate is fine). Calling this endpoint again with the same key returns the original authorization unchanged instead of creating a duplicate, so retrying after a network failure is always safe.
Step 2: Complete ACH Checkout Referencing the Authorization
Pass the authorization’s _id from Step 1 as achAuthorizationId, along with orderType, on POST /checkout/ach/{merchantId}. If your account has allowedOrderTypes configured, orderType must be one of the configured values, and achAuthorizationId is required and must reference an authorization belonging to this customer and merchant.
On success, Coinflow links this call’s paymentId onto the authorization record as its founding payment.
Step 3 (Optional): Report Lifecycle Status Events
As the order’s status changes on your side — filled, archived, reopened, or expired without shipment — report it so the authorization record stays current for audits.
Every event you report is appended to the record’s status history — nothing is overwritten, so the full timeline of an authorization is preserved.
Step 4: Reference the Same Authorization for Later Debits
For order types with more than one debit against the same authorization — recurring subscriptions, balance top-ups, limit orders, and fair-market-value fills — pass the same achAuthorizationId from Step 1 on each subsequent ACH checkout call so it’s traceable back to the original consent record.
Coinflow validates achAuthorizationId against the real authorization record at checkout time on every call, not just the founding one — it doesn’t re-link the payment once one is already set.
Step 5 (Optional): Revoke an Authorization
If the customer’s consent should no longer be honored — they withdraw authorization, or you determine the record was created in error — revoke it by ID. Once revoked, Coinflow rejects any further checkout call that references this achAuthorizationId, even if it was previously valid.
Revoking is permanent — there’s no way to un-revoke an authorization through this endpoint. Revoking an already-revoked authorization succeeds as a no-op and doesn’t change its original revokedAt timestamp.
Order Types and Structured Terms
structuredTerms is shaped by orderType — send only the fields for the order type you’re submitting.
All amount fields (subscriptionAmount, topUpTriggerAmount, topUpMaxAmount, bidPrice, maximumTotal, minFairValue, maxFairValue) are cents objects, e.g. {"cents": 5000}.
Status Event Types
Error Handling
404 — customerId not found for this merchant
When creating an authorization with an admin-scoped API key (Step 1), the customerId you passed doesn’t match any customer on your merchant account.
400 — customerId is required
Creating an authorization with an admin-scoped API key requires customerId in the body — there’s no session to resolve a customer from.
400 — customerId does not match the authenticated customer
You passed a customer session key and a customerId in the body, and they disagree. Omit customerId when authenticating with a session key — the customer is already resolved from it.
400 — Could not resolve the bank account for this token
Coinflow couldn’t find the bank account behind the token you sent when creating the authorization (Step 1). Double-check the token came from a completed bank-linking step for this customer.
400 — ACH authorization is required before checkout
Your merchant account has allowedOrderTypes configured, and the checkout call omitted achAuthorizationId. Create an authorization record (Step 1) before this checkout call and pass its _id.
400 — achAuthorizationId does not reference a known ACH authorization for this merchant
The achAuthorizationId you passed at checkout doesn’t match any authorization record on your merchant account. Double-check you’re passing the _id returned from Step 1, not a paymentId.
400 — achAuthorizationId does not belong to this customer
The authorization record exists, but it was created for a different customer than the one checking out. Authorizations can’t be reused across customers.
400 — orderType is required and must be one of: ...
Your merchant account has allowedOrderTypes configured, and the ACH checkout request either omitted orderType or sent a value outside the configured list. Pass one of the allowed order types.
404 — No ACH authorization found for this authorizationId and merchant
Reporting a status event (Step 3) or revoking an authorization (Step 5) requires that an authorization record already exists for the given id, and that it belongs to your merchant account. Double-check you’re using the _id from Step 1, not a paymentId.
400 — This ACH authorization has been revoked
The achAuthorizationId you passed at checkout has been revoked (Step 5) and can no longer back any payment. Create a new authorization record if the customer wants to proceed.

