Pay via ACH

Pay external bank accounts using the ACH network

Pay via ACH sends funds from a Passport Account to an external bank account over the ACH (Automated Clearing House) network. Use it for vendor payments, payroll disbursements, and refunds. You pay a third party saved as a Contact, or a one-time recipient. For the full field list, jump to the Request reference.

Moving funds to your own bank account is a transfer of your own money, not a payout. See Transfer funds.

Common use cases

  • Pay vendors, suppliers, or service providers
  • Disburse payroll to employees
  • Refund or pay a one-off recipient

Make your first payout

The fastest way to see it work: pay a saved vendor (Contact) with the minimal fields below. The full set of options is in the Request reference.

POST /v1/customer/id/{id}/transaction

{
  "externalId": "ach-first-payout-001",
  "method": "ACH",
  "amount": "1200.00",
  "purpose": "Vendor Payment",
  "source": { "account": { "externalId": "passport-account-001" } },
  "destination": {
    "contact": {
      "externalId": "vendor-contact-001",
      "externalAccount": { "externalId": "vendor-bank-001" }
    }
  }
}

You'll get back a transaction with status: SCHEDULED. Store the returned id and your externalId to track it and for follow-up actions.

📘

Use externalId for safe retries

For Treasury, PCE treats the externalId in the request body as the idempotency key; there's no separate header. Send a unique externalId per payout, and retrying with the same value creates it only once.


Scenarios

Every ACH payout follows the transaction lifecycle. The only thing that changes per scenario is the destination, so pick the one that matches the problem you're solving.

sequenceDiagram
    participant You as Your Application
    participant PCE as PCE
    participant ACH as ACH Network
    participant Bank as Recipient's Bank

    You->>PCE: POST /v1/customer/id/{id}/transaction (method: ACH)
    PCE->>PCE: Validate balance + destination
    PCE-->>You: 201 Created (status: SCHEDULED)
    PCE->>ACH: Submit ACH credit — PROCESSING
    ACH->>Bank: Route funds to beneficiary
    Bank-->>ACH: Credit confirmation
    ACH-->>PCE: Settlement complete
    PCE-->>You: Webhook (status: COMPLETED)

Before you begin (all scenarios)

  • The Passport Account you're paying from is active and has enough balance to cover the payout.

Each scenario below adds one requirement specific to its destination.

Scenario 1: Pay a vendor or contractor you pay regularly

(third-party recipient saved as a Contact)

Use this to pay a vendor, supplier, contractor, or employee you'll pay again. Save their bank details once as a Contact, then reference the contact on every payout.

You'll also need: the recipient saved as a Contact with a linked external bank account.

Request

POST /v1/customer/id/{id}/transaction

{
  "externalId": "ach-contact-2dd0",
  "method": "ACH",
  "type": "REGULAR",
  "amount": "1200.00",
  "purpose": "Vendor Payment",
  "source": { "account": { "externalId": "passport-account-001" } },
  "destination": {
    "contact": {
      "externalId": "vendor-contact-001",
      "externalAccount": { "externalId": "vendor-contact-bank-001" }
    }
  },
  "processingDetail": { "processingMode": "SAME_DAY", "authType": "WRITTEN" }
}

Response

{
  "resourceName": "transaction",
  "url": "/v1/customer/id/4225975/transaction/id/230491510",
  "id": 230491510,
  "externalId": "ach-contact-2dd0",
  "status": "SCHEDULED",
  "statusDate": "06/29/2026 14:20:55",
  "amount": 1200,
  "method": "ACH",
  "purpose": "Vendor Payment",
  "type": "REGULAR",
  "transactionClass": "SEND",
  "statusReason": "On User Request",
  "source": {
    "account": { "resourceName": "account", "id": 9915272, "url": "/v1/customer/id/4225975/account/id/9915272" }
  },
  "destination": {
    "contact": { "resourceName": "contact", "id": 4019600, "url": "/v1/customer/id/4225975/contact/id/4019600" }
  }
}

Scenario 2: Make a one-time payout to a new recipient

(bank details passed inline, nothing saved)

Use this for a one-off payment when you don't want to save the recipient, such as a refund or a single disbursement. You pass the bank details directly in the request.

You'll also need: the recipient's holderName, routingNumber, and accountNumber, verified.

Request

POST /v1/customer/id/{id}/transaction

{
  "externalId": "ach-onetime-91be",
  "method": "ACH",
  "type": "REGULAR",
  "amount": "750.00",
  "purpose": "Refund",
  "source": { "account": { "externalId": "passport-account-001" } },
  "destination": {
    "externalAccount": {
      "holderName": "Jane Doe",
      "accountNumber": "987654321098",
      "routingNumber": "021000089",
      "type": "CHECKING",
      "holderType": "CONSUMER"
    }
  },
  "processingDetail": { "processingMode": "FORWARD", "authType": "WRITTEN" }
}

Response

{
  "resourceName": "transaction",
  "url": "/v1/customer/id/4225975/transaction/id/230491521",
  "id": 230491521,
  "externalId": "ach-onetime-91be",
  "status": "SCHEDULED",
  "statusDate": "06/29/2026 16:04:30",
  "amount": 750,
  "method": "ACH",
  "purpose": "Refund",
  "type": "REGULAR",
  "transactionClass": "SEND",
  "statusReason": "On User Request",
  "source": {
    "account": { "resourceName": "account", "id": 9915272, "url": "/v1/customer/id/4225975/account/id/9915272" }
  },
  "destination": {
    "externalAccount": { "resourceName": "externalAccount", "id": 4019508, "url": "/v1/customer/id/4225975/externalAccount/id/4019508" }
  }
}

Scenario 3: Refund a payout you already sent

(reverse a completed payout)

Use this to return funds from a payout that has already completed, for example reversing a vendor payment. You reference the original transaction, and the source and destination are derived from it, so you don't pass them again. Set type to REFUND.

You'll also need: the original payout to exist and be COMPLETED (you can't refund one that's still processing).

Request

POST /v1/customer/id/{id}/transaction

{
  "externalId": "ach-refund-4c2e",
  "method": "ACH",
  "type": "REFUND",
  "amount": "5.00",
  "purpose": "Card Refund",
  "reason": "ON_CUSTOMER_REQUEST",
  "processingDetail": {
    "parent": { "id": "3317" },
    "achRefundCompanyName": "AMEX LTD",
    "achRefundCompanyDescription": "Refund"
  }
}

Response

{
  "resourceName": "transaction",
  "url": "/v1/customer/id/4225975/transaction/id/230491533",
  "id": 230491533,
  "externalId": "ach-refund-4c2e",
  "status": "SCHEDULED",
  "statusDate": "06/29/2026 20:02:50",
  "amount": 5,
  "method": "ACH",
  "type": "REFUND",
  "purpose": "Card Refund",
  "statusReason": "On User Request",
  "processingDetail": {
    "parent": { "id": 230491490 }
  }
}

Track the transaction

Subscribe to webhooks or GET the transaction to follow it to settlement. The statuses, the webhook payload, and the event types all live in Transaction lifecycle.


Request reference

The complete set of fields for the create-transaction request. Scenarios above use subsets of these.

Core parameters

ParameterRequiredDescription
methodSet to ACH
externalIdRecommendedYour own reference ID, also the idempotency key. Retrying with the same value won't create a duplicate. Maximum 45 characters.
typeOptionalType of transaction. Possible values: REGULAR (a standard payout). Use REFUND only when refunding a completed payout, see Scenario 3.
source.account.id (or externalId)The Passport Account to debit
destinationThe bank account to credit. See Destination types for the supported shapes.
amountAmount to send (in USD)
purposePurpose of the transaction. Maximum 128 characters.

Destination types

The destination object takes one of two shapes, matching the two scenarios above.

Saved Contact (pay a third party you've saved, Scenario 1)

ParameterRequiredDescription
destination.contact.id or externalIdUnique identifier of the Contact
destination.contact.externalAccount.id or externalIdUnique identifier of the External Account linked to the Contact

Inline bank details (one-time payout, nothing saved, Scenario 2)

ParameterRequiredDescription
destination.externalAccount.holderNameName of the bank account holder as it appears on bank records
destination.externalAccount.accountNumberThe bank account number to be credited
destination.externalAccount.routingNumber9-digit routing number of the account
destination.externalAccount.typeSAVING or CHECKING
destination.externalAccount.holderTypeCONSUMER or CORPORATE
📘

Paying your own account?

Referencing a saved External Account by id sends funds to an account you own. That's a transfer, covered on Transfer funds, not here.

Processing detail

Optional as a whole, but certain fields may be required depending on your program's configuration (set by your Program Manager, the platform partner you onboarded through).

ParameterRequiredDescription
processingDetail.processingModeRecommendedFORWARD (default): settles next business day (end-of-day batch). SAME_DAY: settles same day if submitted before cutoff, otherwise next business day.
processingDetail.companyNameRecommendedThe payee name populated in the NACHA file. Defaults to your program's configuration if not provided. Maximum 16 characters.
processingDetail.companyDescriptionRecommendedShort description shown on the recipient's bank statement, such as PAYROLL or PURCHASE. Defaults to your program's configuration if not provided. Maximum 10 characters. Per NACHA rules (effective March 20, 2026), set PAYROLL for PPD credit transactions for wages or salaries, and PURCHASE for WEB debit transactions authorized for online consumer purchases.
processingDetail.authTypeConditionalHow authorization was obtained: WRITTEN (signed), PHONE (recorded call), or ONLINE (digital/web).
processingDetail.addendaRecommendedReference data for reconciliation, such as an invoice number, customer reference ID, or order ID. Maximum 80 characters.
processingDetail.location.idUnique identifier of a location to link to the transaction, assigned by Passport.

Refund fields

These apply only when type is REFUND (see Scenario 3). Source and destination are derived from the parent transaction, so you don't pass them.

ParameterRequiredDescription
typeSet to REFUND
amountAmount to refund, in USD
purposePurpose of the refund
processingDetail.parent.idThe id of the original transaction being refunded
processingDetail.achRefundCompanyNameCompany name populated in the NACHA file for the refund
processingDetail.achRefundCompanyDescriptionCompany description populated in the NACHA file for the refund
reasonReason for the refund, for example ON_CUSTOMER_REQUEST

Validation

The prerequisites above are the validations, and PCE enforces them when you submit. A submission is checked synchronously; if a check fails, the request returns an error and no transaction is created. Submission checks:

  • Required fields are present and correctly formatted.
  • The source Passport Account is active and has enough balance to cover the payout.
  • The destination resolves (a saved Contact, or valid one-time bank details).

Other conditions can only be judged once the payout is in flight, such as an inactive or blocked account, a compliance hold, or a bank return. These surface after creation as PENDING or FAILED; see Transaction lifecycle. For error codes, see Error Codes and Messages.


Statuses

Pay via ACH uses the shared transaction lifecycle. See it for every status, the reasons, and how to track a transaction.


Go live

The shared pre-production checklist (production credentials, webhook subscription and signature validation, idempotency, and sandbox testing, including failure and bank-return paths) is in Getting Started. Specific to paying via ACH, also confirm:

  • Settlement timing (FORWARD versus SAME_DAY cutoffs) is verified for your program.

Best practices

General practices (set a unique externalId, subscribe to webhooks, test in sandbox) are in Getting Started. Specific to paying via ACH:

PracticeDescription
Verify destination detailsFor one-time payments, ensure the account and routing numbers are accurate to avoid failures or misrouted funds.
Use the correct authorization typePass processingDetail.authType (WRITTEN, PHONE, ONLINE) to reflect how authorization was obtained.
Set meaningful NACHA descriptorsPopulate processingDetail.companyName and companyDescription for clear identification on the recipient's bank statement.
Use addenda for reconciliationInclude invoice numbers or reference IDs via processingDetail.addenda to simplify reconciliation.

Next steps

See also



Did this page help you?
.readme-logo { display: none !important; }