ROOKDocs
5 min readUpdated August 2026

Moving Money: ACH, FedNow & Wires

Every financial event in Rook is a transaction, and every movement of funds happens inside one: there is no transfer without a transaction. A transaction holds one or more immutable transfers (the movements of funds), and each transfer posts one book transfer of balanced book entries.

Card authorizations, captures, and refunds are transactions of type CARD; Rook creates their transfers from network events. This guide covers the transaction types you create to move money, and the ones Rook posts for fees, interest, disputes, and rewards.

Transaction types you create

All four are created through their own endpoint and returned as a Transaction (object transaction, type set). The id is the transaction_id used on GET /v1/transactions/{transaction_id}, …/transfers, and …/book-transfers. They share the movement fields (status, amount, wallet_id, financial_account_id, direction, category, memo, events[], transfer_ids[]) and the same Money shape: integer minor units plus an ISO 4217 currency. direction is CREDIT or DEBIT relative to financial_account_id; amount is always non-negative.

type Use when Path
INTERNAL_TRANSFER Both legs are financial accounts on this program /v1/internal-transfers
PAYMENT You originate a payin or payout to an external bank account (ACH, RTP, FedNow) /v1/payments
EXTERNAL_PAYMENT The rail ran off-platform (wire, ACH, check, or transfer) and the books need a matching transaction /v1/external-payments
MANAGEMENT_OPERATION An operator posts a fee, dispute outcome, reward, adjustment, or disbursement on one financial account (single-leg) /v1/management-operations

GET /v1/transactions?type=… lists the same objects. Pick the type that matches how the funds actually move: do not record an originated ACH as an external payment, and do not use a management operation to move money between two financial accounts.

Transaction types Rook posts for you — FEE, INTEREST, DISPUTE, REWARD — appear on GET /v1/transactions and in the activity feed with the same shape.

Program caps for ACH, wire, RTP, FedNow, and internal transfers live on Transfer limits.

Every creating or lifecycle POST requires Idempotency-Key. Replay of the same method, path, and body within 24 hours returns the original status and payload.

Shared object

status is the transaction status (one set for every type). events[] is the transaction’s lifecycle log, chronological; transfers themselves never change. Discriminate each item on type:

type Meaning
CREATED The request was accepted.
SETTLED The amount posted to the ledger.
DECLINED The amount never posted.
RETURNED An ACH return unwound a settlement.
REVERSED An API reverse unwound a settlement.
RELEASED A pending external-payment hold was dropped without posting.
CANCELED A pending external payment was voided.

Each event has amount (integer minor units), result (APPROVED or DECLINED), financial_account_id, and created_at.

Internal transfers

Move funds between two financial accounts on the program. The source (from_financial_account_id) is debited; the destination (to_financial_account_id) is credited. On the object, financial_account_id equals the source and direction is DEBIT.

category selects the reason; subtype is the subtype. Valid pairs:

category subtype
TRANSFER COLLECTION, PAYMENT
ADJUSTMENT BILLING_ERROR, BILLING_ERROR_REVERSAL, CORRECTION, GOODWILL
BALANCE_OR_FUNDING AVAILABLE_TO_RESERVE, RESERVE_TO_AVAILABLE, AVAILABLE_TO_SUSPENSE, SUSPENSE_TO_AVAILABLE
FEE LATE, NSF, ANNUAL, ATM, FOREIGN_TRANSACTION, FEE_REVERSAL
REWARD CASH_BACK, CASH_BACK_REVERSAL, PROMO, PROMO_REVERSAL
PROGRAM_FUNDING PROGRAM_FUNDING, PROGRAM_REPAYMENT

A mismatched pair returns 400 invalid_request with param: subtype. Use TRANSFER / COLLECTION to pull from a cardholder issuing financial account onto the program operating account. Use BALANCE_OR_FUNDING to reclassify available, reserve, and suspense on the same program.

Status subset: PENDING, SETTLED, DECLINED, REVERSED.

stateDiagram-v2
  [*] --> SETTLED: create approved
  [*] --> DECLINED: create declined
  DECLINED --> SETTLED: retry approved
  DECLINED --> DECLINED: retry declined
  SETTLED --> REVERSED: reverse
  create approved ──► SETTLED ── reverse ──► REVERSED

  create declined ──► DECLINED ─┬─ retry approved ──► SETTLED
                                └─ retry declined ─► DECLINED
  • Create: POST /v1/internal-transfers
  • Reverse: POST /v1/internal-transfers/{internal_transfer_id}/reverse on SETTLED
  • Retry: POST /v1/internal-transfers/{internal_transfer_id}/retry on DECLINED

List financial_account_id matches source or destination.

Linking bank accounts

A Payment needs a linked external bank account whose state is ENABLED and whose verification_state is ENABLED. Link the bank before you originate ACH. Reads return last_four and the ABA routing_number. The full account number is never returned.

Create with POST /v1/external-bank-accounts. Send either bank details or a processor token, not both. account_number and processor_token are write-only.

Body Required fields Typical verification_method
Bank details routing_number, account_number, type (CHECKING or SAVINGS), owner, owner_type (PERSON or BUSINESS) MANUAL, MICRO_DEPOSIT, or PRENOTE
Processor token processor_token, owner, owner_type EXTERNALLY_VERIFIED

MICRO_DEPOSIT and PRENOTE also need address plus date_of_birth (person) or tax_id (business). tax_id and date_of_birth are write-only.

state is the origination lifecycle. verification_state is ownership proof. Both must be ENABLED to originate.

state Meaning
ENABLED May originate when verification also passed.
PAUSED Origination declined until unpause.
CLOSED Terminal. No origination.
verification_state Meaning
PENDING Ownership proof running (micro-deposits or prenote).
ENABLED Passed.
FAILED_VERIFICATION Rejected. Retry or change method.
INSUFFICIENT_FUNDS A micro-deposit could not post. Retry after the bank can accept a credit.
stateDiagram-v2
  [*] --> PENDING: MICRO_DEPOSIT or PRENOTE create
  [*] --> ENABLED: MANUAL or EXTERNALLY_VERIFIED create
  PENDING --> ENABLED: amounts match / prenote succeeds
  PENDING --> FAILED_VERIFICATION: mismatch or prenote return
  PENDING --> INSUFFICIENT_FUNDS: micro-deposit cannot post
  FAILED_VERIFICATION --> PENDING: retry or set method
  INSUFFICIENT_FUNDS --> PENDING: retry
  create MANUAL / EXTERNALLY_VERIFIED ──► verification_state ENABLED

  create MICRO_DEPOSIT / PRENOTE ──► PENDING ─┬─ success ──► ENABLED
                                              ├─ mismatch ─► FAILED_VERIFICATION ── retry ──► PENDING
                                              └─ NSF ──────► INSUFFICIENT_FUNDS ── retry ──► PENDING
  • Verify micro-deposits: POST .../micro-deposits with the two credit amounts (Money: integer cents plus USD). Both must match.
  • Retry micro-deposits: POST .../micro-deposits/retry posts a new pair.
  • Retry prenote: POST .../prenote/retry sends a new zero-dollar ACH prenotification.
  • Set method: POST .../verification-method to switch onto MICRO_DEPOSIT or PRENOTE, or to pick a new method after failure.
  • Pause / unpause: POST .../pause and POST .../unpause toggle state between ENABLED and PAUSED. They do not change verification.

PATCH /v1/external-bank-accounts/{external_bank_account_id} updates nickname, owner, address, and user_defined_id only. List filters: wallet_id, owner_type, state, verification_state.

Subscribe to external_bank_account.updated for state and verification changes.

Payments

Originate an ACH payin or payout against an external bank account that is ENABLED with verification_state ENABLED. PAYIN credits financial_account_id (pull from the bank). PAYOUT debits it (push to the bank). method is ACH_NEXT_DAY, ACH_SAME_DAY, RTP, or FEDNOW. sec_code is CCD (corporate), PPD (consumer), or WEB (internet-initiated). category is ACH, RTP, or FEDNOW, derived from method; sec_code is null on instant rails.

Status: PENDING, SETTLED, DECLINED, RETURNED, REVERSED. return_reason_code is a NACHA code (R01 insufficient funds, R02 wallet closed, and the rest of the closed set) when status is RETURNED, and null otherwise.

stateDiagram-v2
  [*] --> PENDING: create accepted
  [*] --> DECLINED: create declined
  PENDING --> SETTLED: ACH settles
  DECLINED --> PENDING: retry
  SETTLED --> RETURNED: return
  SETTLED --> REVERSED: network reverse
                    ┌─────────────────────────────────┐
  create accepted ─►│            PENDING              │
                    └──────────────┬──────────────────┘
                                   │ ACH settles
                                   v
                                SETTLED ─┬─ return ──────────► RETURNED
                                         └─ network reverse ─► REVERSED

  create declined ──► DECLINED ── retry ──► PENDING
  • Create: POST /v1/payments
  • Retry: POST /v1/payments/{payment_id}/retry on DECLINED
  • Return: POST /v1/payments/{payment_id}/return on SETTLED with return_reason_code

Use this family only when Rook originates the ACH. A wire or ACH that already happened at the bank is an external payment.

External payments

Record a movement that originated outside Rook so balances stay correct. category is EXTERNAL_WIRE, EXTERNAL_ACH, EXTERNAL_CHECK, or EXTERNAL_TRANSFER. Set direction relative to financial_account_id. effective_date is the business date (YYYY-MM-DD).

On create, omit progress_to to leave the object PENDING. Pass SETTLED or RELEASED to skip the hold.

Status: PENDING, SETTLED, RELEASED, CANCELED, REVERSED.

stateDiagram-v2
  [*] --> PENDING: create
  [*] --> SETTLED: create progress_to SETTLED
  [*] --> RELEASED: create progress_to RELEASED
  PENDING --> SETTLED: settle
  PENDING --> RELEASED: release
  PENDING --> CANCELED: cancel
  SETTLED --> REVERSED: reverse
                    ┌─────────────────────────────────┐
  create ──────────►│            PENDING              │
                    └───┬──────────┬──────────┬───────┘
                        │          │          │
                   settle     release      cancel
                        │          │          │
                        v          v          v
                     SETTLED    RELEASED   CANCELED

                     reverse
                        v
                     REVERSED

  create progress_to SETTLED  ──► SETTLED
  create progress_to RELEASED ──► RELEASED
  • Settle: POST .../settle posts pending funds (amount may be a partial)
  • Release: POST .../release drops the hold without posting
  • Cancel: POST .../cancel voids a pending record
  • Reverse: POST .../reverse undoes a settlement

Settle when the off-platform movement landed. Release when it will not land and the hold should drop. Cancel when the record was created in error. Reverse when a settled record must be unwound.

Management operations

Post an operator adjustment on a single financial account. Set direction relative to that financial account. category and subtype must match:

category subtype
MANAGEMENT_FEE LATE, NSF, ANNUAL, ATM, FOREIGN_TRANSACTION, OTHER
MANAGEMENT_DISPUTE CHARGEBACK, REPRESENTMENT, WON, LOST
MANAGEMENT_REWARD CASH_BACK, PROMO, POINTS_REDEMPTION
MANAGEMENT_ADJUSTMENT GOODWILL, CORRECTION, CHARGE_OFF, WRITE_OFF
MANAGEMENT_DISBURSEMENT DISBURSEMENT

Status subset: PENDING, SETTLED, DECLINED, REVERSED.

stateDiagram-v2
  [*] --> SETTLED: create approved
  [*] --> DECLINED: create declined
  SETTLED --> REVERSED: reverse
  create approved ──► SETTLED ── reverse ──► REVERSED
  create declined ──► DECLINED
  • Create: POST /v1/management-operations
  • Reverse: POST /v1/management-operations/{management_operation_id}/reverse on SETTLED

Use an internal transfer when two financial accounts exchange funds. Use a management operation when one financial account should move without a book counterparty (for example a late fee or goodwill credit).

Books

Every settled transaction posts one book transfer per transfer it holds, to the books of the financial accounts involved. A book transfer is the collection of balanced book entries (debits and credits) one transfer posts; book entries are never presented on their own. Read a transaction’s transfers with GET /v1/transactions/{transaction_id}/transfers, its book transfers with GET /v1/transactions/{transaction_id}/book-transfers, the books of a financial account with GET /v1/financial-accounts/{financial_account_id}/books, and the book transfers posted to them with GET /v1/financial-accounts/{financial_account_id}/book-transfers?transaction_id=….

Activity feed

Every transaction that touches a financial account appears once on GET /v1/financial-accounts/{financial_account_id}/activity, with transaction_id and a category that mirrors the transaction type: CARD, INTERNAL_TRANSFER, PAYMENT, FEE, INTEREST, DISPUTE, and ADJUSTMENT for management operations. The feed is a view; the transaction is the source of truth.

Was this page helpful?