Transaction Lifecycle
The 15 transaction states, how payments move between them, and the webhook fired at each step.
Every payment in Cresora follows a deterministic state machine. State values are UPPERCASE on the wire — state: "CAPTURED", never lower-case.
The happy paths
Card sale: INITIATED → CAPTURED → SUBMITTED → SETTLED
Card auth-only: INITIATED → AUTHORIZED → (your capture) → CAPTURED → SUBMITTED → SETTLED
ACH debit: INITIATED → SUBMITTED → SETTLED (or → RETURNED with an R-code)All 16 states
| State | Meaning |
|---|---|
INITIATED | Transaction created; no gateway outcome yet |
THREE_DS_PENDING | A 3-D Secure challenge is outstanding — three_ds.challenge_url is populated, 15-minute TTL. Reserved card-authentication flows only; no current POST /transactions create path issues a step-up |
THREE_DS_FAILED | The challenge failed, was abandoned, or timed out — see three_ds_failure_reason |
AUTHORIZED | Funds held on the card, not yet captured. Reached via an auth-only hosted-page session (capture_mode: authorize) |
CAPTURED | Funds captured; awaiting the settlement batch |
SUBMITTED | ACH: accepted into the ODFI origination batch — clears 1–3 banking days later |
SETTLED | Funds transferred. The gate for refunds |
VOIDED | Cancelled pre-settlement (ledger-only VOID) |
REVERSED | Authorization released via AUTH_REVERSAL (card), or a merchant-initiated NACHA reversal (ACH) |
RETURNED | Terminal: the receiving bank returned the ACH entry with an R-code — reached from SUBMITTED or SETTLED; a transaction.returned webhook fires |
REFUNDED | Fully refunded (cumulative refunds reached the original amount) |
PARTIALLY_REFUNDED | Some but not all of the amount refunded — re-entrant across multiple partial refunds |
FAILED | Declined or failed — discriminate on decline_code |
DISPUTED | The cardholder initiated a dispute |
CHARGED_BACK | The dispute resolved against the merchant |
VERIFIED | Terminal state of a CARD_VERIFICATION ($0 verification) |
Two states you may see in older material do not exist: requires_action (the real 3DS-pending state is THREE_DS_PENDING) and ach_returned (the real state for a bank-returned ACH entry is RETURNED).
Webhook events
| What happened | Event |
|---|---|
| Sale captured | transaction.captured |
| Auth-only hold placed | transaction.authorized |
| Declined / failed | transaction.failed |
| Settled | transaction.settled |
| Voided | transaction.voided |
| Hold released | transaction.auth_reversed |
| Refund created / parent fully refunded | transaction.refund_issued / transaction.refunded |
| ACH accepted into the batch | transaction.ach_submitted |
| ACH returned by the bank | transaction.returned |
| Disputed | transaction.disputed, chargeback.received |
Webhooks are the primary signal for payment state — build on them. Polling GET /api/v1/transactions/{transactionId} is the sanctioned fallback for gaps (missed deliveries, reconciliation), not the main loop.
Retries and state guards
Replay safety is provided by the Idempotency-Key header (24 hours, X-Idempotent-Replay: true on the cached response) — not by the state machine. A new capture request (fresh key) against an already-captured parent is rejected by a state guard (422 invalid_state_transition, transaction_already_settled, parent_not_voidable_in_state, …), it does not echo the existing object. Retry with the same key; don't re-issue operations with new keys expecting a no-op.