Understanding the transaction lifecycle helps you build robust payment flows, handle edge cases, and provide accurate status updates to your customers.
A single status field represents whether the transaction reached its destination:
Status
Description
PENDING
Quote is pending confirmation
EXPIRED
Quote wasn’t executed before the expiry window
PROCESSING
Executing the quote after receiving funds (checking internal balances, or push/pull to/from external account)
COMPLETED
Payout successfully reached the destination account
FAILED
Something went wrong — accompanied by a failureReason
EXPIRED and FAILED are terminal states, but COMPLETED is not always final — a bank can return a payment after it was marked COMPLETED, moving it to FAILED. Always continue processing webhook events for transactions even after they reach COMPLETED.
If a refund fails, contact support to resolve the issue manually.
Payment initially succeeds but the bank returns it:
OUTGOING_PAYMENT.PENDING
OUTGOING_PAYMENT.PROCESSING
OUTGOING_PAYMENT.COMPLETED
OUTGOING_PAYMENT.FAILED
OUTGOING_PAYMENT.REFUND_PENDING
OUTGOING_PAYMENT.REFUND_COMPLETED
Bank returns can happen days after initial completion. Continue processing webhook events for transactions even after they reach COMPLETED.
Payment is cancelled while still processing, and the refund succeeds:
OUTGOING_PAYMENT.PENDING
OUTGOING_PAYMENT.PROCESSING
OUTGOING_PAYMENT.REFUND_PENDING
OUTGOING_PAYMENT.REFUND_COMPLETED
OUTGOING_PAYMENT.COMPLETED
In the manual cancellation flow, OUTGOING_PAYMENT.COMPLETED fires after the refund settles. This indicates the payout provider ultimately delivered the original payment despite the cancellation attempt. Your system should check the transaction’s refund object to determine whether the payment was refunded or delivered.
Post-transaction processing at counterparty failed
Contact support
When a transaction fails, a refund is initiated automatically. Track the refund via the refund object on the transaction and OUTGOING_PAYMENT.REFUND_* webhook events. See Refund Object above.
Translate technical statuses to user-friendly messages:
function getUserMessage(webhookType, data) { switch (webhookType) { case 'OUTGOING_PAYMENT.PENDING': return 'Payment processing...'; case 'OUTGOING_PAYMENT.PROCESSING': return 'Payment in progress...'; case 'OUTGOING_PAYMENT.COMPLETED': return 'Payment delivered!'; case 'OUTGOING_PAYMENT.FAILED': return 'Payment failed. Please try again or contact support.'; case 'OUTGOING_PAYMENT.REFUND_PENDING': return 'Refund in progress...'; case 'OUTGOING_PAYMENT.REFUND_COMPLETED': return 'Refund completed. Funds returned to your account.'; case 'OUTGOING_PAYMENT.REFUND_FAILED': return 'Refund failed. Please contact support.'; default: return 'Payment status updated.'; }}