What This Reference Covers
This comprehensive guide provides everything you need to handle payment statuses correctly:- Status lifecycles: The complete flow from creation to terminal states
- The pending threshold: Why
pendingstatus is the point of no return - Failed vs reversed: Critical distinction between pre-funding and post-funding failures
- ACH return codes: All R-codes and S-codes with their meanings and timing
- Status reasons: Complete catalog of failure reasons and their sources
- Status/Source/Reason matrix: Valid combinations showing what actually occurs
- Implementation patterns: Code examples for monitoring and handling status changes
Payment Lifecycle Overview
The happy path for payments follows this progression:1
Created
Payment successfully created and awaiting verification
2
Scheduled
Payment passed verifications and risk scoring, queued for processing
3
Pending
Payment sent to network - CANNOT BE STOPPED
4
Paid
Payment successfully funded
Status Values
Complete Status Reference
*Only user-initiated holds can be released by the user; Watchtower holds require Straddle authorization
†Can still transition to
reversed if return occurs post-funding
Understanding Failed vs Reversed
Status Sources
Sources identify where status changes originate:Status/Source/Reason Matrix
Success States
Hold States
Failed States (Pre-Funding)
Reversed States (Post-Funding)
Cancelled States
Cancellation only occurs via user action in the dashboard or API
Resubmit Failed Payments
Retry any failed, reversed, or cancelled charge or payout by creating a new payment linked to the original. The new payment carries over the original bank information and payment details. Optional: provide a newpayment_date, description, or external_id. Otherwise, Straddle generates these from the original payment.
The new payment inherits the amount, paykey, currency, and metadata from the original. Each payment can only be resubmitted once, but if a resubmitted payment also fails, resubmit that one to create a chain.
Resubmit a charge with an updated payment date and description.
Refund Paid Charges
Return funds to a customer for a paid charge directly from the dashboard or API. The refund is sent back to the customer’s original bank account using the same paykey as the source charge, so there’s no manual lookup or destination-account work. To issue a refund from the dashboard, see the step-by-step walkthrough in the help center. Each charge can be refunded once, full or partial. The refund amount must be greater than $0 and no more than the original charge amount. When you create a refund:- A new payout is created with
payment_type: "payout"and a description ofRefund for Charge ID: {id}. If you supplied your owndescription, it’s appended after the system prefix. - The original charge stays in Paid status. Refunds are tracked separately as payouts and linked through
related_payments. - The new refund payout follows normal payout statuses (Pending → Paid).
related_payments lists the new refund payout, and the refund payout’s related_payments lists the source charge. Each entry includes the linked payment’s id, relationship, and payment_type.
Relationship values:
original: the linked payment is the source. Appears on the refund payout, pointing back to the charge.refund: the linked payment is a refund. Appears on the original charge, pointing to the new refund payout.
payout.* webhook events fire for the refund lifecycle. Existing payout handlers track refunds without changes.
When You Can’t Refund
- The charge is not in
paidstatus. Use Resubmit for failed, reversed, or cancelled charges. - The charge has already been refunded. Each charge can have one refund; check
related_paymentsbefore retrying. - Two requests reach the API at the same time. Straddle uses optimistic concurrency to prevent duplicate refunds — one request succeeds, the other gets a 422 with a clear message.
Important: Same Reason, Different Sources
Some reason codes can originate from different sources depending on when they’re detected:insufficient_funds
Key distinction:
- Watchtower: Proactive check using real-time balance data — payment never leaves Straddle
- Bank Decline: Reactive return from the ACH network after the payment was sent
Status Reasons
Active Reason Codes
These reason codes are actively used by the system:ACH Return Codes
Standard ACH Returns (R-Codes)
Straddle Internal Codes (S-Codes)
Status Details Structure
Every payment includes detailed status information:Handling Status Changes
Monitoring for Status Changes
Handling Specific Failure Reasons
Common Scenarios
Successful Payment Flow
created→system/ok- Payment initializedscheduled→system/ok- Verification passed, risk approvedpending→system/ok- Sent to ACH networkpaid→system/ok- Funds successfully transferred
Pre-Submission Balance Check Failure
created→system/ok- Balance check detects insufficient funds
failed→watchtower/insufficient_funds- No money moved, payment never sent to bank
NSF After Submission (ACH Return)
created→scheduled→pending- Bank returns with R01
failed→bank_decline/insufficient_funds/ code: R01- No money moved (failed before funding)
Dispute After Funding
created→scheduled→pending→paid- Customer disputes with their bank
- Bank sends R10 return
reversed→customer_dispute/disputed/ code: R10- Money was moved, then returned
Risk Hold and Release
created→system/ok- Payment initialized- Watchtower flags for review
on_hold→watchtower/risk_review- Manual review in dashboard
- If approved: released →
scheduled→ normal flow - If declined: →
failedorcancelled
User-Initiated Hold
created→system/ok- User calls hold API
on_hold→user_action/user_request- User can release when ready
- Released → continues processing
Best Practices
Monitor Pending Status
Once pending, payments cannot be stopped. Plan accordingly.
Check Both Source and Reason
The same reason can come from different sources with different implications.
Handle Failed vs Reversed
Different workflows for pre-funding failures vs post-funding reversals.
Track Return Codes
Log ACH return codes for reconciliation and pattern analysis.
Next Steps
Webhook Events
Set up real-time status notifications
Testing Statuses
Test various status scenarios in sandbox
Charges Guide
Learn about charge-specific flows
Payouts Guide
Understand payout-specific scenarios