v2.2-rc2 is not ratified and MUST NOT be used for production. Current: v2.1. Changelog
This is v2.2-rc2 — a release candidate for the v2.2 standards, published for review and not yet ratified. It MUST NOT be used as the basis for a production implementation. For the current standards, switch to v2.1 using the version selector. See the v2.1 → v2.2-rc2 changelog for every change in this version.
Payment Status 8 min read
Every domestic payment initiated through the API Hub is executed in one of three modes: intra-bank (both debtor and creditor accounts are held at your LFI, so no rail is used), or one of the two domestic payment rails — AANI (the UAE's instant payment rail) and UAEFTS (the UAE Funds Transfer System). Your LFI is the only party that sees the raw execution outcome, so your LFI MUST map that outcome to an Open Finance payment status and propagate it to the Hub via the Consent Manager Payment Log.
This page covers both concerns: how your LFI selects an execution mode, and how outcomes from each mode map to Open Finance statuses. For the consent lifecycle, see the Consent Manager section.
Open Finance payment statuses
The Open Finance payment status enum is defined in the Ozone Connect Bank Service Initiation OpenAPI and aligns with ISO 20022 ExternalPaymentTransactionStatus1Code. Six values are in scope:
| Open Finance status | ISO 20022 | Meaning |
|---|---|---|
Pending | PDNG | Payment accepted for processing; further checks or rail submission outstanding |
AcceptedSettlementCompleted | ACSC | Your LFI has submitted the payment to AANI or UAEFTS and is awaiting the rail’s response — not terminal |
AcceptedWithoutPosting | ACWP | The receiving bank has accepted the payment but your LFI cannot confirm the creditor account has been credited — a settlement state |
AcceptedCreditSettlementCompleted | ACCC | The creditor account has been credited with the funds of the payment |
Rejected | RJCT | The payment was rejected, either by your LFI before submission or by AANI / UAEFTS |
Pending is the initial status your Ozone Connect POST/payments response returns — your LFI does not PATCH a payment to Pending. On AANI and UAEFTS your LFI PATCHes twice: AcceptedSettlementCompleted immediately on submitting the payment to the rail, within 3 seconds of returning the 201, and then the terminal status — AcceptedWithoutPosting, AcceptedCreditSettlementCompleted, or Rejected — immediately on the rail’s response. Both PATCHes are mandatory. For an intra-bank payment there is no hand-off, and your LFI PATCHes the terminal status once. The sequence and timing are set out in Payment Timing.
The Open Finance enum also includes a sixth value, Received (ISO 20022 RCVD), used only for bulk and batch payments where the Hub acknowledges receipt of a file of instructions before processing individual payments. Bulk and batch payments are not yet documented in v2.2 — for the domestic single and multi-payments covered by this page, you can ignore Received.
Once a payment reaches a terminal status (AcceptedWithoutPosting, AcceptedCreditSettlementCompleted, or Rejected), your LFI MUST NOT transition it to a different value.
Rail selection
Your LFI is responsible for choosing the execution mode for every domestic payment. The API Hub does not select a rail on your behalf, and the TPP and customer are not involved in the decision. Apply the following logic on receipt of POST/payments:
- Intra-bank — if both the debtor and creditor accounts are held at your LFI, execute the payment internally. No rail is used, and your LFI has end-to-end visibility of both legs.
- AANI — otherwise, submit the payment to AANI as the primary rail whenever the receiving bank and receiving account are reachable on AANI.
- UAEFTS — if AANI is unavailable or the receiving bank cannot receive via AANI, fall back to UAEFTS. The fall-back is automatic and MUST NOT require TPP or customer intervention.
Your LFI MUST only reject a payment for reachability reasons once both rails have been ruled out — that is, when the receiving bank is reachable on neither AANI nor UAEFTS. As with any rejection raised before submission, it is reported against the rail the payment was routed to and carries that rail’s namespace — see Rejection code prefixes.
Routing comes first in the post-201 sequence. Every PATCH on AANI and UAEFTS carries paymentRail and paymentTransactionId, so your LFI selects the rail and assigns the identifier before it screens the payment. Screening and submission then complete within 3 seconds of the 201 — see Payment Timing.
Intra-bank payments
When the debtor account and creditor account are both held at your LFI, there is no interbank rail involved — the payment is executed by an internal debit-and-credit within your core banking system. Your LFI has full visibility of both legs, so the payment can reach the most specific Open Finance terminal status: AcceptedCreditSettlementCompleted.
The events that trigger a PATCH are:
| Intra-bank outcome | New Open Finance status | Notes |
|---|---|---|
| Debtor debit and creditor credit both posted | AcceptedCreditSettlementCompleted | Terminal for intra-bank — your LFI knows the funds have definitely landed in the creditor account |
| Internal rejection (screening, insufficient funds, account state, etc.) | Rejected | Set rejectReasonCode[].code to LFI.<reasonCode> — the only namespace accepted with paymentRail: LFI |
Every PATCH for an intra-bank payment carries paymentRail: LFI. There is no hand-off to a rail, so AcceptedSettlementCompleted does not apply. Within 3 seconds of returning the 201, your LFI MUST complete its screening, book the payment, and immediately PATCH AcceptedCreditSettlementCompleted. Intra-bank payments have no paymentTransactionId, and the field is not accepted on LFI, so omit it.
AANI status mapping
AANI is the UAE's instant payment rail. An AANI payment either clears to terminal success within seconds and returns a positive Account Verification Response (an ISO 20022 pacs.002 message with an accepted status), or is rejected by the receiving bank with an AANI Reject Reason Code drawn from the AANI Core Service Interface Specification.
Immediately on submitting the payment to AANI — within 3 seconds of returning the 201 — your LFI MUST PATCH AcceptedSettlementCompleted. It does not wait for the AANI response. The payment then remains at AcceptedSettlementCompleted until AANI responds, and your LFI MUST PATCH the terminal status immediately when it does. The events that trigger a PATCH are:
| Event | New Open Finance status | Notes |
|---|---|---|
Payment submitted to AANI (pacs.008 sent) | AcceptedSettlementCompleted | Mandatory. Not terminal |
Positive Account Verification Response (pacs.002 accepted) | AcceptedWithoutPosting | Terminal for AANI |
pacs.002 rejection | Rejected | Terminal. Set rejectReasonCode[].code to AANI.<reasonCode> (for example AANI.AM04) |
AcceptedWithoutPosting is the terminal status for successful AANI payments. Your LFI MUST NOT infer AcceptedCreditSettlementCompleted from an AANI Account Verification Response — AANI does not confirm credit posting at the receiving bank to the originating LFI.
Include paymentTransactionId on every PATCH/payment-log/{id}, including the first. On AANI it is the pacs.008 TxId your LFI assigned to the payment, not an identifier issued by AANI, and it MUST start with OFP. Once set, paymentTransactionId MUST NOT change.
Illustrative AANI Reject Reason Codes
The full list of 40 codes is maintained in the AANI Core Service Interface Specification. The codes you will see most often are:
| AANI code | Meaning |
|---|---|
AC06 | Blocked Account |
AC07 | Closed Creditor Account Number |
AM04 | Insufficient Funds |
AM14 | Amount Exceeds Agreed Limit |
UCRD | Unknown Creditor |
Your LFI MUST relay the code in the AANI. namespace — AANI.AM04 — without transposing it to a different namespace. A PATCH with paymentRail: AANI accepts only codes matching ^AANI\.[A-Za-z0-9]+$.
UAEFTS status mapping
UAEFTS is the UAE Funds Transfer System, the Central Bank of the UAE's domestic interbank funds transfer rail. UAEFTS carries payment messages using the ISO 20022 pacs.008 schema and returns the following acknowledgements:
- ACK — UAEFTS has accepted the message after technical validation
- NAK — UAEFTS has rejected the message with an FTS reason code
- CB900 — Debit Confirmation: settlement through the Central Bank has completed, the debtor account has been debited, and the corresponding credit to the receiving bank has been settled
Because UAEFTS settles through the Central Bank as a centralised interbank clearing, a CB900 implicitly confirms that the creditor side of the payment has been settled as well. The terminal successful status for a UAEFTS payment is therefore AcceptedCreditSettlementCompleted.
Immediately on submitting the payment to UAEFTS — within 3 seconds of returning the 201 — your LFI MUST PATCH AcceptedSettlementCompleted, carrying the payment’s UAEFTS transaction identifier as paymentTransactionId. ACK is technical acceptance only: it does not trigger a PATCH, and the payment remains at AcceptedSettlementCompleted until CB900 or NAK arrives. Your LFI MUST then PATCH the terminal status immediately. The events that trigger a PATCH are:
| Event | New Open Finance status | Notes |
|---|---|---|
Payment submitted to UAEFTS (pacs.008 sent) | AcceptedSettlementCompleted | Mandatory. Not terminal |
| CB900 Debit Confirmation | AcceptedCreditSettlementCompleted | Terminal for UAEFTS |
| NAK received | Rejected | Terminal. Set rejectReasonCode[].code to FTS.<reasonCode> |
Rejection code prefixes
Every Rejected PATCH carries a rejectReasonCode[] array. The first segment of each code is a namespace, and from v2.2 the namespace follows the paymentRail named on the PATCH — not the system in which the check failed:
paymentRail | Covers | code pattern | Example |
|---|---|---|---|
AANI | A payment routed to AANI, whether AANI rejected it or your LFI rejected it before submission (screening, for example) | ^AANI\.[A-Za-z0-9]+$ | AANI.AC06 |
FTS | A payment routed to UAEFTS, whether UAEFTS rejected it or your LFI rejected it before submission | ^FTS\.[A-Za-z0-9]+$ | FTS.AC06 |
LFI | A payment booked on your LFI’s own ledger — every rejection is your LFI’s own | ^LFI\.[A-Za-z0-9]+$ | LFI.InsufficientFunds |
A code from any other namespace does not validate against the PATCH body for the rail named. In particular, a rejection your LFI raises before submitting a payment routed to AANI or UAEFTS MUST carry the AANI. or FTS. prefix, not LFI.. The prefix is relayed to the TPP verbatim and is critical for TPP diagnostics and downstream reconciliation.
On AANI and FTS, a Rejected PATCH also carries paymentTransactionId, as every PATCH on those rails does.
Propagating status to the Hub
The initial Pending status is set by the 201 response your Ozone Connect POST/payments endpoint returns — no PATCH is required at that point.
What your LFI MUST PATCH
paymentRail | On submission to the rail (within 3 s of the 201) | On the rail’s response |
|---|---|---|
AANI | AcceptedSettlementCompleted | AcceptedWithoutPosting or Rejected |
FTS | AcceptedSettlementCompleted | AcceptedCreditSettlementCompleted or Rejected |
LFI | AcceptedCreditSettlementCompleted on booking, within 3 s of the 201 — terminal, no rail | |
Every PATCH in this table is mandatory. Your LFI MUST NOT PATCH any status other than those listed for the rail, and MUST NOT PATCH anything after a terminal status. The full sequence, including what happens between the 201 and submission, is in Payment Timing.
How to PATCH
On each PATCH-triggering event, your LFI MUST immediately:
- Map the outcome to the new Open Finance status.
- Call PATCH
/payment-log/{id}on the API Hub Consent Manager withpaymentResponse.paymentRail, the newpaymentResponse.status,paymentResponse.paymentTransactionId(onAANIandFTS), and, forRejectedonly,paymentResponse.rejectReasonCode. - Update the response your Ozone Connect GET
/payments/{paymentId}endpoint returns so that push and pull views of the payment are consistent.
Your LFI MUST NOT batch status updates or defer them waiting for TPP polling. The Hub relays the new status to the TPP via webhook events as soon as it receives the PATCH.
Example PATCH bodies
{
"paymentResponse.paymentRail": "AANI",
"paymentResponse.status": "AcceptedSettlementCompleted",
"paymentResponse.paymentTransactionId": "OFP7d2c1e9a4b6f4c0e8a1b"
}
{
"paymentResponse.paymentRail": "AANI",
"paymentResponse.status": "AcceptedWithoutPosting",
"paymentResponse.paymentTransactionId": "OFP7d2c1e9a4b6f4c0e8a1b"
}
{
"paymentResponse.paymentRail": "AANI",
"paymentResponse.status": "Rejected",
"paymentResponse.paymentTransactionId": "OFP7d2c1e9a4b6f4c0e8a1b",
"paymentResponse.rejectReasonCode": [
{
"code": "AANI.AM04",
"message": "Payment request cannot be executed as insufficient funds at debtor account."
}
]
}
