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 Timing 8 min read
This page defines the sequence and timing your LFI MUST follow for every domestic payment, from the API Hub’s call to your Ozone Connect POST/payments endpoint through to the terminal status recorded on the API Hub. It applies to Single Instant Payment and to every multi-payment type.
Three obligations are timed. Your LFI validates the request and returns 201. Within 3 seconds of that 201, it screens the payment, submits it to the rail, and PATCHes AcceptedSettlementCompleted to the API Hub. When the rail responds, it PATCHes the terminal status immediately. The TPP-facing view of the same timeline is in the TPP Standards Payment Timing page.
The timeline at a glance
The clock for the timed stages starts when your Ozone Connect returns the 201 to the API Hub. That moment is T0 in the table below.
| # | Stage | When | What your LFI does | Status on the API Hub |
|---|---|---|---|---|
| 1 | Respond to POST /payments | Synchronous, ending at T0 | Runs the synchronous validations, creates the payment record, and returns 201 | Pending |
| 2 | Screen, submit, PATCH | By T0 + 3 s | Routes the payment, runs fraud screening, transaction monitoring and AML checks, submits the payment to the rail, and immediately PATCHes AcceptedSettlementCompleted | AcceptedSettlementCompleted |
| 3 | Rail response | Immediately on receipt of the rail’s response | PATCHes the terminal status | AcceptedWithoutPosting (AANI), AcceptedCreditSettlementCompleted (UAEFTS), or Rejected |
A payment booked on your LFI’s own ledger, where the debtor and creditor accounts are both held at your LFI, has no rail and no hand-off to report. Stages 2 and 3 collapse into one: see Intra-bank payments.
Responding to POST /payments
By the time the API Hub calls your Ozone Connect POST/payments, it has already validated the TPP’s access token, confirmed the consent is Authorized, checked the payment against the consent’s control parameters, and validated the request against the OpenAPI schema. Your LFI relies on the API Hub for those checks and does not repeat them.
Your LFI MUST then run the following checks before it creates the payment record. Each failure is returned synchronously, as an error response to the API Hub. No payment record is created and nothing is PATCHed.
| Check | Applies to | On failure |
|---|---|---|
Decrypt the payment PII (PersonalIdentifiableInformation) | All | 400 JWE.DecryptionError |
| Validate the decrypted PII against the payment-time PII schema | All | 400 Body.InvalidFormat |
| The creditor matches the creditor authorised on the consent | All | 400 Consent.FailsControlParameters |
| The debtor account has sufficient available funds | All | 400 GenericError |
The debtor account is not Inactive, Dormant, or Suspended | All | 403 Consent.AccountTemporarilyBlocked |
The debtor account is not Closed, Deceased, or Unclaimed | All | 403 Consent.PermanentAccountAccessFailure |
x-fapi-customer-ip-address is present and a valid IP address | Single Instant Payment, Delegated SCA | 400 Body.InvalidFormat |
No identical payment is already Pending under the consent | Delegated SCA, Fixed On Demand, Variable On Demand | 409 Payment.DuplicateInFlight |
The authoritative rules, including the type-specific checks not listed here, are on each payment type’s Requirements page — for example Single Instant Payment — Payment Execution.
If every check passes, your LFI creates the payment record and returns 201 with the LFI-assigned id and status: Pending. The 201 means the payment record exists. It does not mean the payment has been screened or submitted. Returning it marks T0, and the 3-second window in the next stage starts.
Screen, submit to the rail, PATCH AcceptedSettlementCompleted
Within 3 seconds of returning the 201, your LFI MUST complete its fraud screening, transaction monitoring and AML checks, MUST submit the payment to the rail, and MUST immediately call PATCH/payment-log/{id} with paymentResponse.status: AcceptedSettlementCompleted.
Within the window, your LFI works through four steps in order:
| # | Step | What your LFI does |
|---|---|---|
| 1 | Route | Select the rail. Settle over UAEFTS when UseFTS is true; otherwise submit to AANI, falling back to UAEFTS where your routing rules require it. Assign the payment’s paymentTransactionId. On AANI this is the pacs.008 TxId your LFI assigns, and it MUST start with OFP. On UAEFTS it is the payment’s UAEFTS transaction identifier. Every PATCH on AANI and UAEFTS carries both paymentRail and paymentTransactionId, so routing comes first. |
| 2 | Screen | Run your fraud screening, transaction monitoring and AML checks on the payment. |
| 3 | Submit | Submit the payment to the rail (pacs.008 to AANI or UAEFTS). |
| 4 | PATCH | Immediately on submission, PATCH AcceptedSettlementCompleted with paymentRail and paymentTransactionId. Do not wait for any response from the rail — not the AANI pacs.002, and not the UAEFTS ACK. |
The AcceptedSettlementCompleted PATCH is mandatory on AANI and UAEFTS. It is how the API Hub, and through it the TPP, learns that the payment has cleared your LFI’s checks and been handed to the rail. It is not terminal: a second PATCH always follows in Stage 3.
Example PATCH bodies
{
"paymentResponse.paymentRail": "AANI",
"paymentResponse.status": "AcceptedSettlementCompleted",
"paymentResponse.paymentTransactionId": "OFP7d2c1e9a4b6f4c0e8a1b"
}
{
"paymentResponse.paymentRail": "FTS",
"paymentResponse.status": "AcceptedSettlementCompleted",
"paymentResponse.paymentTransactionId": "<UAEFTS end-to-end transaction identifier>"
}
{
"paymentResponse.paymentRail": "LFI",
"paymentResponse.status": "AcceptedCreditSettlementCompleted"
}
Intra-bank payments
Where the debtor and creditor accounts are both held at your LFI, the payment is booked on your own ledger and never reaches a rail. The same 3-second window applies. Within 3 seconds of returning the 201, your LFI MUST complete its screening, book the payment, and immediately PATCH AcceptedCreditSettlementCompleted with paymentRail: LFI. That status is terminal, so no second PATCH follows. paymentTransactionId is not sent on LFI.
PATCH the terminal status immediately
How long AANI or UAEFTS takes to respond is set by that rail’s own scheme rules, not by the Open Finance standards. The payment stays at AcceptedSettlementCompleted until the rail responds. Your LFI MUST then PATCH the terminal status immediately on receiving the response:
| Rail | Rail response | PATCH paymentResponse.status | Notes |
|---|---|---|---|
| AANI | Positive Account Verification Response (pacs.002 accepted) | AcceptedWithoutPosting | Terminal |
| AANI | pacs.002 rejection | Rejected | Terminal. rejectReasonCode[].code in the AANI. namespace |
| UAEFTS | ACK | — | Technical acceptance only. No PATCH; the status remains AcceptedSettlementCompleted |
| UAEFTS | CB900 Debit Confirmation | AcceptedCreditSettlementCompleted | Terminal |
| UAEFTS | NAK | Rejected | Terminal. rejectReasonCode[].code in the FTS. namespace |
Every terminal PATCH on AANI and UAEFTS carries the same paymentRail and paymentTransactionId as the AcceptedSettlementCompleted PATCH before it. A Rejected PATCH also carries rejectReasonCode, whose message MUST be sanitised for relay to the TPP. The per-rail mapping is in Payment Status.
Example PATCH bodies
{
"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."
}
]
}
What “immediately” requires
Both PATCHes are sent as soon as the triggering event occurs: submission to the rail for the first, and receipt of the rail’s response for the second. In particular, your LFI MUST NOT:
- batch status updates across payments, or hold them for a scheduled job;
- defer a PATCH until the TPP polls GET
/payments/{paymentId}; - skip the
AcceptedSettlementCompletedPATCH because the rail has already responded — send it first, then the terminal status; - send any further status once a terminal status has been PATCHed.
PATCH delivery is durable. Retry a transient failure (5xx, connection error, or timeout) with exponential backoff until it is accepted. Do not retry a 4xx; raise it for operational investigation. Your Ozone Connect GET/payments/{paymentId} MUST return the status most recently PATCHed, so the push and pull views of the payment stay consistent.
PATCH obligations by rail
paymentRail | By T0 + 3 s | Immediately on the rail’s response |
|---|---|---|
AANI | Submit, then PATCH AcceptedSettlementCompleted | PATCH AcceptedWithoutPosting or Rejected |
FTS | Submit, then PATCH AcceptedSettlementCompleted | PATCH AcceptedCreditSettlementCompleted or Rejected (no PATCH on ACK) |
LFI | Book, then PATCH AcceptedCreditSettlementCompleted | — (no rail) |
The field-level contract for each PATCH is in the PATCH /payment-log/{id} API reference. This requirement is recorded in the v2.2 changelog §17.
