Release candidate

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.

LFI · Banking · Confirmation of Payee

Confirmation of Payee — API Guide 2 min read

Confirmation of Payee (CoP) lets a TPP verify that an IBAN belongs to the named individual or business before initiating a payment.

01 API Sequence Flow

End-to-end Confirmation of Payee

Sequence diagramConfirmation of Payee API FlowClick to expand
02 POST /customers/action/cop-query

Return the name(s) held against an IBAN

POST/customers/action/cop-query
Migrating from v2.1

Both bodies changed in v2.2. The request no longer carries data.account.name — drop it from your parser, and stop reading it if you used it to select which name to return. The response is flattened: replace data[].verifiedClaims[].verification and data[].verifiedClaims[].claims with a single data[].name, mapping claims.fullName to name.fullName, claims.givenName to name.givenName, claims.familyName to name.familyName, and organisationClaims.name to name.businessName. Add the account’s status to every entry as data[].accountStatus. Everything else the envelope carried — trust framework, assurance evidence, and the wider customer fields such as emiratesId, birthDate and salary — is removed and MUST NOT be sent.

Headers, query parameters and error codes are unchanged, and the only new status code is 504 (see Error responses below), so this is a rewrite of your request parser and response builder rather than a new integration.

Request headers

HeaderRequiredDescription
o3-provider-idYesIdentifier for your LFI registered in the Hub
o3-aspsp-idYes (deprecated)Deprecated alias for o3-provider-id. Will be removed in a future version — use o3-provider-id
o3-caller-org-idYesOrganisation ID of the TPP making the underlying request
o3-caller-client-idYesOIDC client ID of the TPP application
o3-caller-software-statement-idYesSoftware statement ID of the TPP application
o3-api-uriYesThe parameterised URL of the API being called by the TPP
o3-api-operationYesThe HTTP method of the operation carried out by the TPP (e.g. POST)
o3-ozone-interaction-idYesHub-generated interaction ID. Equals o3-caller-interaction-id if the TPP provided one
o3-caller-interaction-idNoInteraction ID passed in by the TPP, if present

Query parameters

ParameterRequiredDefaultDescription
pageYes1Page number for paginated results
page-sizeYes100Number of records per page

Request body

Content-Type: application/json

The Hub sends a plain JSON body — not a JWS. The body identifies a single account by IBAN, and carries nothing else. The name the TPP submitted is not sent to you: look the account up by IBAN and return the holders you have, and the Hub does the comparing.

data.account

FieldTypeRequiredDescription
schemeNamestringYesAlways IBAN
identificationstringYesThe IBAN to look up

Example

cop-query requestjson
{
  "data": {
    "account": {
      "schemeName": "IBAN",
      "identification": "AE070331234567890123456"
    }
  }
}

Response

Content-Type: application/json

Return 200 in all lookup scenarios — whether the account is found or not. The Hub interprets the data array contents to determine the match result returned to the TPP.

200 — Account found

Return a data array containing one entry per account holder. id, name and accountStatus are the only required members of an entry.

accountStatus uses the same code set as status on the account resource returned by GET /accounts/{accountId}: Active, Inactive, Dormant, Unclaimed, Deceased, Suspended or Closed. A joint account MUST return the same value on every entry. The Hub returns it to the TPP as AccountStatus.

Personal account

name.fullName is mandatory and is the value the Hub matches on today. Every other name field is optional — supply givenName, middleName, familyName and alsoKnownAs where your systems hold them separately, so that improvements to the matching algorithm can use them without a further change to this contract. These are the names CustomerPersonClaims uses on GET /customer. A response carrying only fullName and accountStatus is fully conformant.

personal account responsejson
{
  "data": [
    {
      "id": "cust-001",
      "name": {
        "fullName": "Ahmed Al Mansouri",
        "givenName": "Ahmed",
        "familyName": "Al Mansouri"
      },
      "accountStatus": "Active"
    }
  ],
  "meta": {
    "totalPages": 1,
    "totalRecords": 1
  }
}
Personal account — minimum conformant response
minimum conformant responsejson
{
  "data": [
    {
      "id": "cust-001",
      "name": {
        "fullName": "Ahmed Al Mansouri"
      },
      "accountStatus": "Active"
    }
  ],
  "meta": {
    "totalPages": 1,
    "totalRecords": 1
  }
}
Joint account

Return one entry per holder. The Hub evaluates every entry in data, so the order is not significant — do not attempt to place the most likely match first.

joint account responsejson
{
  "data": [
    {
      "id": "cust-001",
      "name": {
        "fullName": "Ahmed Al Mansouri",
        "givenName": "Ahmed",
        "familyName": "Al Mansouri"
      },
      "accountStatus": "Active"
    },
    {
      "id": "cust-002",
      "name": {
        "fullName": "Fatima Al Mansouri",
        "givenName": "Fatima",
        "familyName": "Al Mansouri"
      },
      "accountStatus": "Active"
    }
  ],
  "meta": {
    "totalPages": 1,
    "totalRecords": 2
  }
}
Business account

Populate name.businessName with the registered business name on the account. alsoKnownAs is optional.

business account responsejson
{
  "data": [
    {
      "id": "cust-003",
      "name": {
        "businessName": "Al Mansouri Trading LLC",
        "alsoKnownAs": ["Al Mansouri Trading"]
      },
      "accountStatus": "Active"
    }
  ],
  "meta": {
    "totalPages": 1,
    "totalRecords": 1
  }
}

200 — Account not found, opted out

Return 200 with an empty data array where no account was found matching the IBAN, the account is under a bar, or the customer has opted out of CoP. Do not use 204, 404, 201 or 202 — the Hub expects 200 and treats an empty array as a no-result response.

The three cases are deliberately indistinguishable to the TPP, so that a CoP query cannot be used to probe for the existence of an account.

not found responsejson
{
  "data": [],
  "meta": {
    "totalPages": 0,
    "totalRecords": 0
  }
}

Error responses

All error bodies must include errorCode and errorMessage.

403 — Forbidden
errorCodeerrorMessageWhen to use
Consent.AccountTemporarilyBlockedThe account is blocked from receiving payments.The account is blocked from receiving payments for a temporary reason — e.g. account status is Suspended
Consent.PermanentAccountAccessFailureThe account is blocked from receiving payments.The account is blocked from receiving payments permanently — e.g. account status is Closed, Deceased, or Unclaimed
504 — Gateway Timeout

You MAY return 504 when you cannot complete processing within your internal time limit. The API Hub treats it as a timeout. The body for this status is not standardised and no schema is defined for it. A 504 is a 5xx response, so it counts as a failed request under the Ozone Connect Availability Policy.