openapi: 3.0.4
info:
  title: APICUNIA GATEWAY - Verification of Payee (VOP) Scheme
  version: 1.0.2
  description: |
    Technical specification for Application Programming Interfaces (API's) from regulatory service provider [Apicunia](https://www.apicunia.com/). 


    **Apicunia** provides regulatory compliance for Payment Service Providers (PSP) operating within the **European Payments Council’s** (EPC) [Verification of Payee](https://www.europeanpaymentscouncil.eu/what-we-do/other-schemes/verification-payee) (VOP) Scheme.


    **Apicunia** are a fully **EPC Certified** Routing and Verification Mechanism (RVM) able to operate as both a **VOP Requestor** and a **VOP Responder**.
  contact:
    name: Apicunia Support
    url: https://www.apicunia.com/
    email: support@apicunia.com
externalDocs:
  description: Find out more about the EPC Verification Of Payee Scheme
  url: https://www.europeanpaymentscouncil.eu/what-we-do/other-schemes/verification-payee
servers:
  - url: https://test-apigw.apicunia.com
    description: VOP Gateway (Test)
tags:
  - name: Verify Payee
    description: Apicunia VOP API (Reachability and Requesting Service)
    externalDocs:
      description: Find out more
      url: https://www.apicunia.com/
paths:
  /vopgateway/v1/payee-verifications:
    post:
      summary: Verification of Payee Details
      description: |
        Returns a **Standard VOP Response** indicating if the supplied Payee details are a match or otherwise.

        **As Requestor:** The endpoint will validate and route an incoming VOP request to the applicable responding PSP/RVM and relay the VOP response from the PSP/RVM back to the calling application.

        Standard VOP Response (Status Code 200):
        ```
        MTCH (Match)
        CMTC (Close Match - matched Name is also included in the response)
        NMTC (No Match)
        NOAP (Not Applicable)
        ```
      tags:
        - Verify Payee
      operationId: postVerificationOfPayeeRequests
      parameters:
        - name: X-Request-ID
          description: The Requesting PSP's reference ID of the VOP Request (uuid - Universally Unique Identifier)
          in: header
          schema:
            type: string
            format: uuid
          example: "c9921832-e2a4-4d6d-b7d6-4d822a99b397"
          required: true
        - name: X-Request-Timestamp
          description: |
            Timestamp of the VOP Request in ISO 8601 UTC format (YYYY-MM-DDThh:mm:ss.sssZ).
          in: header
          schema:
            type: string
            format: date-time
          example: "2025-11-02T10:12:24.164Z"
          required: true
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/VerificationOfPayeeRequest"
            examples:
              IBAN and Party Name (MTCH Response):
                value:
                  party:
                    name: Lukas Klein
                  partyAccount:
                    iban: DE33500105175746121394
              IBAN and Party Name (NMTC Response):
                value:
                  party:
                    name: Anna Bauer
                  partyAccount:
                    iban: AT102040454763965773
              IBAN and Party Name (CMTC Response):
                value:
                  party:
                    name: Marc Baert
                  partyAccount:
                    iban: BE19131422633712
              IBAN and Party Name (NOAP Response):
                value:
                  party:
                    name: Ada Georgiou
                  partyAccount:
                    iban: BE23817417767591
              IBAN and LEI (MTCH Response):
                value:
                  party:
                    identification:
                      organisationId:
                        lei: 529900F6BNUR3RJ2WH29
                  partyAccount:
                    iban: NL93INGB1200042867
              IBAN and LEI (NMTC Response):
                value:
                  party:
                    identification:
                      organisationId:
                        lei: DANN00XJP2FDRM6WXR26
                  partyAccount:
                    iban: LU720107265785842882
              IBAN and TXID (MTCH Response):
                value:
                  party:
                    identification:
                      organisationId:
                        others:
                          - identification: NL638757010B01
                            schemeNameCode: TXID
                  partyAccount:
                    iban: NL73ABNA6631699223
              IBAN and TXID (NMTC Response):
                value:
                  party:
                    identification:
                      organisationId:
                        others:
                          - identification: BE0367715221
                            schemeNameCode: TXID
                  partyAccount:
                    iban: BE48812651617627
      responses:
        "200":
          description: A **Standard VOP response** code (includes the matched name if the code is 'CMTC'- Close Match)
          headers:
            X-Request-ID:
              schema:
                type: string
                format: uuid
              required: true
              description: The Requesting PSP's reference ID of the VOP Request (uuid)
            X-Response-Timestamp:
              schema:
                type: string
                format: date-time
              example: "2025-11-02T10:12:26.372Z"
              required: true
              description: Timestamp of the VOP Response in ISO 8601 UTC format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VerificationOfPayeeResponse"
              examples:
                Match - Party Name (Person or Legal):
                  value:
                    partyNameMatch: MTCH
                Match - Party ID (LEI/BIC/TXID/EUID):
                  value:
                    partyIdMatch: MTCH
                Close Match - Party Name (Person or Legal):
                  value:
                    partyNameMatch: CMTC
                    matchedName: Lukas Klein
                No Match - Party Name (Person or Legal):
                  value:
                    partyNameMatch: NMTC
                No Match - Party ID (LEI/BIC/TXID/EUID):
                  value:
                    partyIdMatch: NMTC
                Not Applicable - Party Name (Person or Legal):
                  value:
                    partyNameMatch: NOAP
                No Applicable - Party ID (LEI/BIC/TXID/EUID):
                  value:
                    partyIdMatch: NOAP

        "400":
          description: Bad/Malformed VOP Request. The request body is malformed.
          headers:
            X-Request-ID:
              schema:
                type: string
                format: uuid
              required: true
              description: The Requesting PSP's reference ID of the VOP Request (uuid)
            X-Response-Timestamp:
              schema:
                type: string
                format: date-time
              example: "2025-11-02T10:12:26.372Z"
              required: true
              description: Timestamp of the VOP Response in ISO 8601 UTC format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VerificationOfPayeeError"
              examples:
                Format Error (Missing Name/Identifier):
                  value:
                    type: https://example.com/probs/FORMAT_ERROR
                    code: FORMAT_ERROR
                    title: Validation of VOP Request failed. See Detail
                    status: 400
                    detail: At least one of 'name' or 'identification' must be provided.
                    instance: /vopgateway/v1/payee-verifications
                Format Error (IBAN Format):
                  value:
                    type: https://example.com/probs/FORMAT_ERROR
                    code: FORMAT_ERROR
                    title: Validation of VOP Request failed. See Detail
                    status: 400
                    detail: Invalid IBAN format
                    instance: /vopgateway/v1/payee-verifications
                Format Error (Name and LEI present in request):
                  value:
                    type: https://example.com/probs/FORMAT_ERROR
                    code: FORMAT_ERROR
                    title: Validation of VOP Request failed. See Detail
                    status: 400
                    detail: Invalid IBAN format  Both 'name' and 'identification' present – mutually exclusive fields
                    instance: /vopgateway/v1/payee-verifications
                Timestamp Error (Not ISO 8601 format):
                  value:
                    type: https://example.com/probs/TIMESTAMP_INVALID
                    code: TIMESTAMP_INVALID
                    title: "Invalid timestamp format: X-Request-Timestamp"
                    status: 400
                    detail: "Invalid timestamp format: X-Request-Timestamp"
                    instance: /vopgateway/v1/payee-verifications
        "401":
          description: Invalid Requesting PSP. The Requesting PSP is **not authorised** to perform the request.
          headers:
            X-Request-ID:
              schema:
                type: string
                format: uuid
              required: true
              description: The Requesting PSP's reference ID of the VOP Request (uuid)
            X-Response-Timestamp:
              schema:
                type: string
                format: date-time
              example: "2025-11-02T10:12:26.372Z"
              required: true
              description: Timestamp of the VOP Response in ISO 8601 UTC format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VerificationOfPayeeError"
              examples:
                Invalid Client 1:
                  value:
                    type: https://example.com/probs/CLIENT_INVALID
                    code: CLIENT_INVALID
                    title: Validating the client failed. See Detail
                    status: 401
                    detail: Invalid Client, no permission to access resource.
                    instance: /vopgateway/v1/payee-verifications
                Invalid Client 2:
                  value:
                    type: https://example.com/probs/CLIENT_INVALID
                    code: CLIENT_INVALID
                    title: Unknown Requestor/Responder. See Detail
                    status: 401
                    detail: One or both of the Responding/Requesting Party Agent is not recognised or not currently adherent to the VOP Scheme.
                    instance: /vopgateway/v1/payee-verifications
        "500":
          description: Internal Server Error. This code is used whenever a technical problem occurred in the application code or application server that prevented the execution of the request.
          headers:
            X-Request-ID:
              schema:
                type: string
                format: uuid
              required: true
              description: The Requesting PSP's reference ID of the VOP Request (uuid)
            X-Response-Timestamp:
              schema:
                type: string
                format: date-time
              example: "2025-11-02T10:12:26.372Z"
              required: true
              description: Timestamp of the VOP Response in ISO 8601 UTC format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VerificationOfPayeeError"
              examples:
                Internal Server Error:
                  value:
                    type: https://example.com/probs/SERVER_ERROR
                    code: SERVER_ERROR
                    title: Encountered an unexpected error/condition
                    status: 500
                    detail: Encountered an unexpected error/condition.
                    instance: /vopgateway/v1/payee-verifications
        "502":
          description: Bad Gateway Error. This code is used whenever a technical problem occurred and the server acted as a gateway or proxy and received an invalid response from an upstream server.
          headers:
            X-Request-ID:
              schema:
                type: string
                format: uuid
              required: true
              description: The Requesting PSP's reference ID of the VOP Request (uuid)
            X-Response-Timestamp:
              schema:
                type: string
                format: date-time
              example: "2025-11-02T10:12:26.372Z"
              required: true
              description: Timestamp of the VOP Response in ISO 8601 UTC format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VerificationOfPayeeError"
              examples:
                Bad Gateway Error:
                  value:
                    type: https://example.com/probs/SSL_ERROR
                    code: SSL_ERROR
                    title: SSL/TLS handshake failed
                    status: 502
                    detail: SSL/TLS handshake failed.
                    instance: /vopgateway/v1/payee-verifications
      security:
        - gateway_auth:
            - default
components:
  schemas:
    VerificationOfPayeeRequest:
      type: object
      properties:
        party:
          $ref: "#/components/schemas/PartyType"
        partyAccount:
          $ref: "#/components/schemas/AccountType"
      required:
        - party
        - partyAccount
    PartyType:
      description: |
        Separation of the two kinds of identification into distinct structures ensures that the two sets of attributes are mutually exclusive.
        Mutual exclusion is verified by business rules executed after the json parsing.
      type: object
      properties:
        name:
          $ref: "#/components/schemas/Max140Text"
        identification:
          type: object
          properties:
            organisationId:
              type: object
              properties:
                lei:
                  description: Legal Entity Identifier.
                  allOf:
                    - $ref: "#/components/schemas/LEIType"
                anyBIC:
                  $ref: "#/components/schemas/BICType"
                others:
                  description: |
                    Unique identification of an organisation, as assigned by an institution, using an identification scheme.
                    Usage Rule: only one entry to be used.
                  type: array
                  items:
                    $ref: "#/components/schemas/GenericOrganisationIdentification"
          required:
            - organisationId
    AccountType:
      type: object
      properties:
        iban:
          $ref: "#/components/schemas/IBANType"
      required:
        - iban
    GenericOrganisationIdentification:
      description: |
        Usage Rule: the use of either ‘schemeNameCode’ or ‘schemeNameProprietary’ is mandatory when the ‘identification code’ is used.
        Mutual exclusion is verified by business rules executed after the json parsing.
      type: object
      properties:
        identification:
          description: Unique and unambiguous identification of an organisation.
          allOf:
            - $ref: "#/components/schemas/Max256Text"
        schemeNameCode:
          description: An entry provided by an external ISO code list.
          allOf:
            - $ref: "#/components/schemas/OrganisationIdentificationCode"
        schemeNameProprietary:
          description: A scheme name defined in a proprietary way.
          allOf:
            - $ref: "#/components/schemas/Max35Text"
        issuer:
          description: Issuer of the identification.
          allOf:
            - $ref: "#/components/schemas/Max35Text"
      required:
        - identification
    VerificationOfPayeeResponse:
      description: |
        Separation of the two kinds of responses in different structures allows constraints on code identification results and name inclusion.
        Mutual exclusion is verified by business rules executed after the json parsing.
        If the result of partyNameMatch is CMTC, then matchedName is mandatory and can only be used in this case.
      type: object
      properties:
        partyNameMatch:
          $ref: "#/components/schemas/NameMatchType"
        partyIdMatch:
          $ref: "#/components/schemas/PartyIdMatchType"
        matchedName:
          $ref: "#/components/schemas/Max140Text"
    VerificationOfPayeeError:
      type: object
      properties:
        type:
          $ref: "#/components/schemas/Max70Text"
        code:
          description: An entry provided by an external ISO code list.
          allOf:
            - $ref: "#/components/schemas/MessageCode"
        title:
          $ref: "#/components/schemas/Max70Text"
        status:
          type: integer
          enum: [400, 401, 500, 502]
        detail:
          $ref: "#/components/schemas/Max500Text"
        instance:
          $ref: "#/components/schemas/Max256Text"
      required:
        - type
        - code
    Max35Text:
      type: string
      minLength: 0
      maxLength: 35
    Max70Text:
      type: string
      minLength: 0
      maxLength: 70
    Max140Text:
      type: string
      minLength: 0
      maxLength: 140
    Max256Text:
      type: string
      minLength: 0
      maxLength: 256
    Max500Text:
      type: string
      minLength: 0
      maxLength: 500
    IBANType:
      type: string
      pattern: "^[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}$"
    BICType:
      type: string
      pattern: "^[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?$"
      example: "ABCDBEBBFIM"
    LEIType:
      type: string
      pattern: "^[A-Z0-9]{18}[0-9]{2}$"
      description: "Code LEI (Legal Entity Identifier) ISO 17442"
      example: "549300DTUYXVMJXZNY75"
    NameMatchType:
      type: string
      enum: [MTCH, CMTC, NMTC, NOAP]
    PartyIdMatchType:
      type: string
      enum: [MTCH, NMTC, NOAP]
    OrganisationIdentificationCode:
      description: Specifies the external organisation identification scheme name code (maximum 4 characters) as defined in an external list (iso20022.org).
      type: string
      enum:
        [
          BANK,
          CBID,
          CHID,
          CINC,
          COID,
          CUST,
          DUNS,
          EMPL,
          GS1G,
          SREN,
          SRET,
          TXID,
          BDID,
          BOID,
        ]
      example: "BOID"
    MessageCode:
      type: string
      enum:
        [FORMAT_ERROR, CLIENT_INVALID, CLIENT_INCONSISTENT, TIMESTAMP_INVALID]
  securitySchemes:
    gateway_auth:
      type: oauth2
      description: |
        **OAuth 2.0 Client Credential Flow**

        **cURL Code Snippet**
        ```
        curl --location --request GET 'https://login.microsoftonline.com/{tenant_Id}/oauth2/v2.0/token' \
        --header 'Content-Type: application/x-www-form-urlencoded' \
        --data-urlencode 'grant_type=client_credentials' \
        --data-urlencode 'client_id={client_Id}' \
        --data-urlencode 'client_secret={client_Secret}' \
        --data-urlencode 'scope={scope_Id}/.default'
        ```
        <pre>
        - Values enclosed within the curly brackets "{}" shown above indicates a parameter and it should be replaced with your own values when executing.
        - Parameter values are "supplied on request".
        - Access Token Lifetime: 1 hour (3600 seconds)
        - Client Credential flow does not issue a refresh token, you must request a new access token when the current token expires.
        </pre>
      flows:
        clientCredentials:
          scopes:
            default: "{scope_Id}/.default"
