All proposals
OFP-013 DraftHigh impact

Return every Confirmation of Payee endpoint a bank operates

An IBAN carries a bank code, not a segment. Banks run separate Retail, SME and Corporate instances on the API Hub, and POST /discovery can only name one of them — so a Confirmation of Payee check on a corporate IBAN is sent to the retail estate and comes back “IBAN not recognised” for an account that is real, active, and verifiable. This proposal makes the discovery response an array of every instance that can answer, and has the TPP try each until one does.

Proposed by
Nebras
Author
Thomas Catchpole
Target
V2.2
Opened
16 Sep 2026
Closes
7 Oct 2026
Decision

Cast your vote

Sign in with the Trust Framework to vote — For, Against, or Abstain — recorded in the open with your reasoning. Your organisation and name come from your directory profile, and each person may vote once. Two questions are attached to this vote — set out in section 07 below — and they carry as much weight as the tally: they decide whether a breaking change lands at V2.2, and what a fanned-out check should cost.

§ BallotOFP-013Closes 7 Oct 2026 ·
Where do you stand?
Pick a stance to add your name & a comment.
0%
in favour
0
votes cast
0
For
0
Against
0
Abstain
Voting not yet open
Voting opens 16 Sep 2026
The proposal
01 · Background

An IBAN names a bank, not an estate

Confirmation of Payee is the one flow where the API Hub chooses the LFI on the TPP’s behalf. Everywhere else the customer picks their own institution and the consent is raised against the authorisation server they chose. Here the subject of the check is the payee — not the TPP’s customer, not present, and not able to be asked anything — so the only input is the destination IBAN, and POST /discovery exists to turn it into somewhere to send the request.

A UAE IBAN carries the bank code, and the Hub resolves it correctly. What the IBAN does not carry is the segment — whether the account sits on the bank’s retail, SME, or corporate estate — and those estates are separate Open Finance instances with their own lfiCode, their own authorisation server, and their own API Hub resource server. That separation is not an edge case — it is the ordinary way a bank with more than one customer base is onboarded:

How one bank code can map to several instances
  • A retail and a business estate. One instance (bank-retail) carries personal current and savings accounts; a second (bank-sme) carries the SME book. Two lfiCodes, two authorisation servers, one bank code in the IBAN.
  • A three-way split. bank-retail, bank-sme, and bank-corp each onboarded separately, typically because each sits on a different core platform with its own release cycle.
  • A split that has nothing to do with segment. A digital-only brand run alongside the parent bank, or an estate part-way through a migration, produces two instances under one bank code for reasons no segment field would describe.

AEConfirmationDiscoveryResponse.Data is a single object holding one DiscoveryEndpointUrl and one ResourceServerUrl. One bank code, one answer. With no segment to discriminate on and no room to return more than one, the Hub returns the retail instance — correct when the payee holds a retail account, and wrong in every other case. The TPP is given no way to tell which of the two it received.

Today — the corporate payee that cannot be found
# Today — one bank code resolves to one instance

POST /discovery
  { "Data": { "SchemeName": "IBAN", "Identification": "AE070331234567890123456" } }

200
  { "Data": { "DiscoveryEndpointUrl": "https://auth1.bank-retail.apihub.openfinance.ae/.well-known/openid-configuration",
              "ResourceServerUrl":    "https://rs1.bank-retail.apihub.openfinance.ae" } }

# The bank code resolved. The segment did not — an IBAN does not carry one — so
# the retail instance is returned, because a single-valued Data has no room for
# the alternative.

POST https://rs1.bank-retail.apihub.openfinance.ae/open-finance/confirmation-of-payee/v2.1/confirmation
204   # "IBAN is not recognised"

# The account exists on the business estate, which was not queried. The
# response is identical to the one returned for a mistyped IBAN.

The outcome is not reported as an error. A 204 is a valid CoP response meaning the IBAN is not recognised, so the request succeeds, no fault is raised, and neither the TPP nor the Hub has a signal that the check was sent to an instance that was never going to hold the account. The TPP reports to the payer that the account could not be verified, for an account that would have matched had the request reached the instance that holds it.

The result is a negative that is indistinguishable, to the TPP and to the payer, from a mistyped or fraudulent IBAN — which is the outcome Confirmation of Payee exists to identify.

02 · Recommendation

Return them all, and let the TPP ask each in turn

Data becomes an array. The Hub resolves the IBAN to a bank code as it does today, and returns every instance registered under that bank code that serves Confirmation of Payee — each as the same DiscoveryEndpointUrl and ResourceServerUrl pair the response already carries. A bank running one instance returns an array of one. A bank running retail, SME, and corporate returns three.

Proposed — the shape, and what the TPP does with it
# Proposed — one bank code resolves to every instance that can answer

200
  { "Data": [
      { "DiscoveryEndpointUrl": "https://auth1.bank-retail.apihub.openfinance.ae/.well-known/openid-configuration",
        "ResourceServerUrl":    "https://rs1.bank-retail.apihub.openfinance.ae" },
      { "DiscoveryEndpointUrl": "https://auth1.bank-sme.apihub.openfinance.ae/.well-known/openid-configuration",
        "ResourceServerUrl":    "https://rs1.bank-sme.apihub.openfinance.ae" },
      { "DiscoveryEndpointUrl": "https://auth1.bank-corp.apihub.openfinance.ae/.well-known/openid-configuration",
        "ResourceServerUrl":    "https://rs1.bank-corp.apihub.openfinance.ae" }
    ],
    "Links": { "Self": "..." },
    "Meta":  { } }

# Which entries to call is the TPP's choice: one it has grounds to identify,
# the entries in turn, or all of them. What may be concluded is not:
#
#   200 from an entry      -> authoritative. The account was found. Stop.
#   204 from an entry      -> this instance does not hold the account.
#   204 from EVERY entry   -> the IBAN is not recognised.
#   204 from some entries  -> inconclusive. Report "could not verify",
#                             which is NOT the same as "not recognised".
#   4xx/5xx from an entry  -> that instance did not answer. Inconclusive on
#                             the same terms.

How many of the entries a TPP calls is the TPP’s choice. One that already knows where the payee’s account sits — from a previous successful check against the same IBAN, or from what it knows of the payee — may go straight to that entry. One with nothing to go on attempts the entries in turn, or attempts them all.

What the choice does not change is what may be concluded. A 200 carrying ConfirmationOfPayee.Yes, Partial, or No is authoritative, because the account was found and the submitted name was evaluated against it. A 204 means only that the instance queried does not hold the account. The IBAN may be reported as not recognised only once every entry in the array has returned 204 — a TPP that called some of the entries and received 204 from each has an inconclusive result, not a negative one.

Nothing changes at the LFI. Ozone Connect keeps the same POST /customers/action/cop-query contract, and each instance answers for the accounts it holds exactly as it does now. Both URLs in every entry are Hub-hosted — auth1.{lfiCode} and rs1.{lfiCode} — so this is the Hub returning more of what it already knows, from directory data it already holds. This is the reason for preferring this shape to the alternatives set out in section 06.

03 · Scope and behaviour

What goes in the array, and what the TPP does with it

Returning more than one endpoint only helps if it is unambiguous which responses end the search, which ones continue it, and what the TPP is entitled to tell the payer at the end.

Proposed rules
  • Data MUST be an array of one or more source objects. Each object keeps the current schema unchanged — DiscoveryEndpointUrl and ResourceServerUrl, both required. A bank with a single instance returns an array of one; there is no special case and no object form to fall back to.
  • The Hub MUST include only instances that serve Confirmation of Payee. An instance that does not advertise the confirmation API family is left out, so a TPP is never sent to an endpoint that cannot answer the question it is being asked.
  • The order MUST be stable for a given bank code, and the Hub SHOULD order entries by how likely each is to hold an arbitrary account — in practice retail first. Position carries no assertion about the payee: the first entry is the most likely instance, not the identified one.
  • How many entries to call is the TPP’s choice. A TPP that has grounds to identify the instance holding the payee’s account MAY call that entry alone; a TPP without them MAY call the entries in turn or call them all. Nothing in this proposal requires a fixed number of attempts.
  • A 200 is authoritative and ends the check. The account was found and the submitted name was evaluated against it, so Yes, Partial, and No are all final. The TPP MUST NOT continue to other entries looking for a better answer.
  • A 204 MUST NOT be reported as “not recognised” unless every entry returned one. A 204 states only that the instance queried does not hold the account. Where the TPP called a subset, or where any entry answered 4xx or 5xx, the result is inconclusive and the outcome reported to the payer is could not verify — a different statement from not recognised, and the user journeys must keep the two apart.
  • Each entry is authenticated on its own terms. The access token for a /confirmation call comes from the token endpoint published at that entry’s DiscoveryEndpointUrl; a token issued by one instance is not valid at another. TPPs SHOULD cache tokens per instance and reuse them across checks rather than re-authenticating on every attempt.
  • Where more than one entry is attempted, the attempts SHOULD be sequential. Calling every entry in parallel incurs a charge for each one and multiplies load across the ecosystem for a single payee check, whereas sequential attempts stop at the first 200.
  • The request is unchanged. POST /discovery still takes the IBAN alone — no segment hint, no account-type parameter. The TPP does not know the payee’s segment either, so asking it to declare one would only move the guess.
  • No change to /confirmation, and none at the LFI. The confirmation request and response schemas, the match indicators, and the Ozone Connect cop-query contract are all untouched.
  • Out of scope: every other flow. Data Sharing and Service Initiation select an authorisation server from the directory with the customer’s involvement, so segmentation is already handled there. Insurance is unaffected.
04 · Technical changes

What changes

One schema field changes cardinality. The implementation work falls on the API Hub and on TPPs. No LFI changes anything.

Who implements what
  • LFI — nothing. No new endpoint, no schema change, and no change to Ozone Connect. POST /customers/action/cop-query keeps its current contract, and each instance answers for the accounts it holds exactly as it does now. An LFI does not see this change: it never learns whether the Hub named it alone or alongside its other instances, because the array is assembled after the Hub has resolved the IBAN and before any LFI is called at all. Nothing is required of an LFI to be conformant at V2.2.
  • API Hub — the response. The schema change, and assembling the array from directory data the Hub already holds. It calls no one it does not call today.
  • TPP — parsing and reporting. Read an array rather than an object, decide which entries to call, and apply the reporting rule: “not recognised” only once every entry has returned 204.
01 · Standards specification

In uae-confirmation-of-payee-openapi.yaml, change AEConfirmationDiscoveryResponse.Data from a $ref to AEConfirmationSourceProperties into an array of it, with minItems: 1. AEConfirmationSourceProperties itself is unchanged — both fields stay required, so each entry is exactly the object TPPs parse today. The request schema, Links, and Meta are untouched, and /confirmation does not move. Targets V2.2.

02 · API Hub

Assemble the response from every authorisation server registered under the resolved bank code whose instance advertises the confirmation API family, in a stable order with the retail instance first. The Hub holds all of this in the Trust Framework directory already — lfiCode, issuer, discoveryUri, apiFamilies — so the change is in how the response is built, not in what the Hub knows or who it has to ask.

03 · Documentation

Rewrite steps 5 to 10 of the Confirmation of Payee API Guide so discovery yields a list and the token-and-confirm sequence is shown as a loop over it, with the stop conditions from section 03 stated as code rather than prose. Update the CoP sequence diagram, the capability landing page, and the user journeys so “not recognised” and “could not verify” are presented to the payer as the different outcomes they are. Record the change in the version changelog.

04 · Functional certification and Postman

Add a Confirmation of Payee scenario in which an IBAN belonging to a multi-instance bank resolves to an array of more than one entry, the first attempt returns 204, and a later entry returns a match. The assertion is on what the TPP reports, not on how many entries it called: a single 204 must not be surfaced to the payer as “not recognised”. Update the CoP Postman collection to iterate the returned entries.

05 · Separate prerequisite — onboarding the non-retail instances

This one is not part of the change above, is not required for conformance at V2.2, and is not scheduled by this proposal — but without it the array returns nothing beyond the retail instance. For a corporate payee to be verifiable, the SME and corporate instances have to serve CoP, advertise the confirmation API family, and accept the confirmation-of-payee scope, and TPPs have to be able to authenticate against them. Where an LFI has not implemented cop-query on those instances, that is LFI work — but it is the work of extending an existing capability to another estate, not anything this proposal introduces.

05 · Versioning and migration

A breaking change that is safe in a minor version

The change is breaking — Data stops being an object — so it is proposed for V2.2, at a version boundary, rather than as an erratum against V2.1. What makes a minor version sufficient is that nothing about this endpoint forces two participants to migrate together.

Why the two versions can run side by side
  • The API Hub controls the endpoint end to end.POST /discovery is answered by the Hub alone, from directory data, with no LFI in the path. The Hub can therefore serve /open-finance/confirmation-of-payee/v2.1/discovery and /v2.2/discovery concurrently, returning the single object at one and the array at the other.
  • The call is client credentials, not consented. There is no consent bound to a version, no authorisation journey, and no stored state that outlives the request. Each discovery call stands alone, so the version is a property of the request the TPP makes and of nothing else.

Together these mean a TPP migrates when it chooses to. One that has not moved keeps calling the V2.1 path and keeps receiving the single-object response — including where the LFI holding the payee’s account has already migrated, because the LFI’s version has no bearing on the shape the Hub returns to a TPP. The V2.1 request and response examples stay correct for as long as V2.1 is served.

This is what separates it from a breaking change in Data Sharing or Service Initiation, where a consent is created at a version and held for its lifetime, so one participant’s migration pulls on the other’s. Here there is no such coupling: a TPP picks up the segment fix at the point it moves to V2.2, and until then behaves exactly as it does today.

06 · Alternatives considered

Two other ways to find the right estate

Both resolve the segment more precisely than fan-out does. Both were set aside — the first deliberately rather than permanently.

01 · An LFI-hosted CoP discovery endpoint — deferred

The LFI exposes an endpoint that resolves one of its own IBANs to the estate that holds it, and the Hub calls it before answering /discovery. This is the accurate answer: one request, the correct instance, no fan-out, no wasted charges, and no 204 that has to be interpreted.

It was deferred on implementation effort. It puts a new endpoint into every LFI — specification, build, test, certification, and an operational dependency on the /discovery path for every bank in the ecosystem — to solve a problem the Hub can address on its own with data it already holds. Deferred, not rejected: if fan-out proves expensive in practice, this is the change that replaces it.

02 · A segment hint in the discovery request

The TPP declares the expected account type and the Hub returns the matching instance. This requires the TPP to know the payee’s segment, which it has no basis for: the payee is not its customer, and a TPP paying an invoice holds a name and an IBAN, not an account type. The parameter would move the assumption from the Hub to the TPP without the retry that fan-out provides when the assumption is wrong.

07 · Questions

Two questions, asked with the vote

These appear as optional boxes when you confirm your vote. Answer the ones addressed to you — every voter sees both. The vote decides the response shape; these answers decide the version it lands at and what it costs to use.

01 · TPPs using Confirmation of Payee

When would you move to the V2.2 /discovery response shape, and would you attempt the returned endpoints one at a time or in parallel?

Data changes from an object to an array. Both versions are served side by side, so nobody is forced to move on a date — but the fix only reaches payees once TPPs do move, and we need to know whether that is a sprint or a release cycle. The second half matters separately: fanning out in parallel multiplies both the load and the charges on every check.

02 · TPPs and LFIs

Should a 204 “IBAN not recognised” remain a chargeable Confirmation of Payee transaction once fan-out means most attempts return one?

POST /confirmation is chargeable. Under fan-out a single payee check can produce three billed calls, two of which found nothing. This proposal does not change pricing — it asks the question, so the commercial decision is taken with the ecosystem’s answer in front of it.

08 · Pros

What the array buys

  • Accounts held outside a bank’s retail estate become verifiable at all. A single-valued discovery response can only ever send the check to one instance, so an account held on any other one returns “not recognised” however correct the IBAN is.
  • It removes a class of false negative. An account held outside the retail estate currently produces the same result as an IBAN that does not exist, so the payer is warned about an account that would have matched had the request reached the instance holding it.
  • No LFI implementation effort. The Hub already holds every authorisation server and resource server in the directory, so this is a response-assembly change over data it has. That is precisely why the LFI-hosted alternative was deferred rather than built.
  • It fixes every reason a bank code maps to more than one instance, not only segmentation. A digital-only brand alongside its parent, or an estate part-way through a migration, breaks discovery in exactly the same way and is fixed by the same change.
  • “Not recognised” becomes a statement about the bank rather than about one instance. The TPP reports it only after every instance the Hub named has returned 204, instead of after the single instance the Hub happened to select.
  • The IBAN stays the only input. No new field from the TPP, no new endpoint at the LFI, and no segment anyone has to ask for — which matters, because the payee is not the TPP’s customer and cannot be asked anything.
09 · Cons

What it costs

  • ×It is a breaking change to a response shape every existing Confirmation of Payee integration is built against. Serving both versions side by side means no TPP is forced to move on a date, but it does leave the Hub maintaining two response shapes for as long as V2.1 is served.
  • ×Fan-out has a price. A TPP that attempts every entry incurs up to one token exchange and one chargeable /confirmation call per instance for a single payee check, most of them returning 204 — and this proposal flags that cost rather than fixing it.
  • ×The entries are untagged, so a TPP that does know something about the payee often cannot act on it. Nothing in the response says which entry is the corporate one, so unless the TPP recognises the instance from a previous check it is left attempting them in order and paying for the attempts that miss.
  • ×The protocol fix does not deliver coverage on its own. Until the non-retail instances are CoP-enabled the array returns one entry and a corporate payee still fails — work this proposal identifies but does not schedule.
  • ×It publishes how many estates each bank runs to every TPP that resolves an IBAN. The same topology is already visible in the directory, so this is a small exposure rather than a new one, but it stops being something a TPP has to go looking for.
  • ×Because calling a single entry is permitted, the non-conformance is not in how many entries a TPP calls but in what it reports afterwards — and a TPP that reports “not recognised” off one 204 looks identical to a conformant one on every retail payee. Functional certification is the only place that would be caught.