Overview
Every payment in OpenFX follows a defined lifecycle from creation to a terminal state. The
lifecycle is the same whether the payment was created via the unified POST /payments endpoint
or a rail-specific endpoint. Understanding these states is critical for building reliable
payment integrations.
State Machine
Status Definitions
Provider status mapping
Payment statuses are normalized from provider-native values. The Platform API abstracts
provider-specific status semantics into a consistent lifecycle:
| Provider Status | OpenFX Payment Status | Notes |
|---|
PENDING | processing | Payment submitted to the rail, awaiting settlement |
SETTLED | completed | Funds delivered to the counterparty |
FAILED | failed | Payment could not be processed |
RETURNED | returned | Funds returned by the receiving institution |
This mapping ensures a consistent payment lifecycle regardless of the underlying banking
provider. If providers change underneath (new provider, new capabilities), the status
semantics remain stable for API consumers.
Active states
These statuses indicate the payment is still in progress and may transition further.
| Status | Description | What to do |
|---|
created | Payment has been accepted and is being prepared for processing. Compliance screening may be in progress. | Wait for payment.processing webhook. |
processing | Payment is actively being processed on the payment rail. Funds have been debited from the source account. | Wait for payment.completed or payment.failed webhook. |
in_review | Payment is undergoing manual review (compliance or operations). This is a temporary hold — no action is needed from you. | Wait for the review to complete. The payment will resume processing or transition to completed or failed. |
Terminal states
These statuses are final. The payment will not transition again (except completed which can lead to returned or reversed).
| Status | Description | Financial impact |
|---|
completed | Payment was successfully delivered to the counterparty. | Funds have left your account and arrived at the destination. This is the happy path. |
failed | Payment could not be completed. | Funds that were held are released back to the source account. Check failureReason for details. |
returned | Payment was delivered but subsequently returned by the receiving institution (e.g., ACH return codes R01-R51). | Returned funds are credited back to the source account. |
reversed | Payment was reversed after completion (e.g., due to fraud or error). | Reversed funds are credited back to the source account. |
The completed status means the payment was delivered but it is not necessarily a terminal
state in all cases. A completed payment can still transition to returned or reversed
if a post-delivery event occurs. Build your integration to handle these transitions
even after a payment reaches completed.
Cancellation state
The cancellationState field is orthogonal to payment status. It tells you whether a
payment can be canceled at its current point in the lifecycle, regardless of its status.
| Cancellation State | Meaning |
|---|
cancelable | The payment can be canceled via POST /payments/{paymentId}/cancel |
not_cancelable | The payment has progressed past the point where cancellation is possible |
A payment in created status is typically cancelable. Once it reaches processing, it
becomes not_cancelable because funds have been submitted to the payment rail.
Webhook Events
Subscribe to these webhook events to react to payment status changes in real time.
| Event | Triggered when | Payment status |
|---|
payment.created | Payment is accepted and created | created |
payment.processing | Payment has entered active processing | processing |
payment.in_review | Payment is under manual review | in_review |
payment.completed | Payment was successfully delivered | completed |
payment.failed | Payment could not be completed | failed |
payment.returned | Delivered payment was returned | returned |
payment.reversed | Delivered payment was reversed | reversed |
At a minimum, subscribe to payment.completed, payment.failed, and payment.returned.
These three events cover the most common integration scenarios.
Lifecycle Timestamps
Every payment includes timestamps for key lifecycle events. These are null until the corresponding transition occurs.
| Field | Set when |
|---|
createdAt | Payment is created (always present) |
submittedAt | Payment enters processing |
completedAt | Payment reaches completed |
failedAt | Payment reaches failed |
returnedAt | Payment is returned |
reversedAt | Payment is reversed |
Failure Details
When a payment fails, the failure object provides structured information about what went wrong:
{
"failure": {
"code": "insufficient_funds",
"message": "Source account has insufficient balance for this payment.",
"railCode": null,
"railMessage": null,
"retryable": true
}
}
| Field | Description |
|---|
code | Machine-readable failure code: insufficient_funds, account_closed, invalid_account, compliance_rejected, rail_rejected, provider_error, timeout, unknown |
message | Human-readable explanation |
railCode | Raw error code from the payment rail (e.g., ACH return code). Null if failure occurred before rail submission. |
railMessage | Raw error message from the payment rail |
retryable | Whether the payment can be retried with the same parameters |
Return Details
When a completed payment is returned, the returnInfo object explains the return:
{
"returnInfo": {
"code": "R01",
"reason": "Insufficient funds",
"returnedBy": "Receiving Bank",
"originalRail": "ach"
}
}
ACH return window: ACH returns can arrive up to 2 business days after settlement for
standard returns and up to 60 calendar days for unauthorized transaction disputes (R10, R29).
Build your integration to handle returns arriving long after a payment reaches completed status.
Tracking Identifiers
Once a payment enters processing, OpenFX populates trackingIdentifiers with rail-native
reference numbers for end-to-end traceability.
| Field | Rail(s) | Description |
|---|
uetr | SWIFT | Universal End-to-End Transaction Reference (UUID format) |
traceNumber | ACH | Trace number assigned by the originating bank |
imad | Fedwire | Input Message Accountability Data |
omad | Fedwire | Output Message Accountability Data |
txHash | Crypto | Blockchain transaction hash |
These identifiers are null until populated by the rail and are only present for the relevant
rail type.
Recommended Integration Pattern
1. Create payment (POST /payments)
|
2. Subscribe to webhooks:
| payment.completed
| payment.failed
| payment.returned
|
3. On payment.completed:
| Record success, update your system
|
4. On payment.failed:
| Record failure, notify stakeholders
| Check failureReason for retry guidance
|
5. On payment.returned:
| Evaluate return reason
| Take appropriate action
API Reference