Payflow Docs
Payments

ACH Transfers

Tokenize ACH customers, preview transfer cost, and create Dwolla-backed ACH transfers through Payflow.

ACH transfers are server-side flows. They require secret API keys, ACH routing, Dwolla configuration, and valid wallet/provider readiness for the target environment.

Endpoints

MethodPathPurpose
POST/client/v1/ach/customer-tokensCreate an encrypted customer token.
POST/client/v1/ach/transfers/previewPreview routing, fees, and total debit.
POST/client/v1/ach/transfersCreate an ACH transfer.

All three endpoints require a secret key.

The examples assume:

export PAYFLOW_CLIENT_API_URL="https://api.ftosllc.co/client/v1"
export PAYFLOW_SECRET_KEY="sk_test_your_key_id_your_secret"

Create a customer token

Customer tokens let Payflow bind customer identity details to an encrypted token before the transfer call.

curl -X POST "$PAYFLOW_CLIENT_API_URL/ach/customer-tokens" \
  -H "Authorization: Bearer $PAYFLOW_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "externalCustomerId": "cus_1001",
    "email": "[email protected]",
    "firstName": "Jane",
    "lastName": "Stone",
    "phone": "+15555550100",
    "metadata": {
      "accountTier": "business"
    }
  }'

Response:

{
  "success": true,
  "data": {
    "customerToken": "eyJ...",
    "tokenType": "JWE",
    "expiresAt": null
  },
  "message": null
}

Preview a transfer

curl -X POST "$PAYFLOW_CLIENT_API_URL/ach/transfers/preview" \
  -H "Authorization: Bearer $PAYFLOW_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amountMinor": "12500",
    "currency": "USD",
    "metadata": {
      "invoiceId": "INV-ACH-1001"
    }
  }'

The preview evaluates ACH routing and returns provider/channel fee information when routing and provider readiness checks pass.

Create a transfer

curl -X POST "$PAYFLOW_CLIENT_API_URL/ach/transfers" \
  -H "Authorization: Bearer $PAYFLOW_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "idempotencyKey": "invoice-INV-ACH-1001-v1",
    "amountMinor": "12500",
    "currency": "USD",
    "customerToken": "eyJ...",
    "routingNumber": "222222226",
    "accountNumber": "123456789",
    "bankAccountType": "checking",
    "bankName": "Test Bank",
    "accountName": "Jane Stone",
    "description": "ACH invoice payment",
    "metadata": {
      "invoiceId": "INV-ACH-1001"
    },
    "remittanceData": {
      "invoice": "INV-ACH-1001"
    }
  }'

Important fields:

FieldNotes
idempotencyKeyRequired, 8 to 160 characters. Reuse it for safe retries of the same business action.
amountMinorPositive smallest currency unit.
currencyCurrently expected to be USD for Dwolla ACH.
customerTokenToken returned by /ach/customer-tokens.
routingNumberNine digits.
accountNumberFour to seventeen digits.
bankAccountTypechecking or savings.
bankNameHuman-readable bank name.
remittanceDataOptional string or object.

Idempotency

ACH references are deterministic for an organization, application environment, and idempotencyKey. If your service retries after a timeout, reuse the same key rather than creating a new one.

Failure cases

ConditionTypical statusNotes
Missing secret key403ACH endpoints require secret credentials.
Invalid body400Field validation failed.
Routing unavailable409ACH routing is disabled or incomplete.
Provider not configured503Dwolla credentials or source funding source are missing.
Insufficient wallet balance409Payflow-held wallet does not have enough available funds.
Provider transfer rejected422Dwolla or provider validation rejected the transfer.

Operational notes

  • Store only the Payflow reference, bank last four, provider references, and status in your app.
  • Do not log full routing or account numbers.
  • Make sure Dwolla source funding source URLs point to a specific /funding-sources/:id resource, not a collection URL.
  • Configure Dwolla webhooks so transfer status changes can update Payflow transactions.

On this page