openapi: 3.0.3
info:
  title: Spec
  version: 'v2'
  description: >-
    # How early is too early to discover compliance issues?

    Wych Compliance Validation helps Data Holders find open banking compliance issues before they become release, audit, or production problems.

    The API enables developers, testers, and compliance teams to validate Data Holder implementations against expected open banking behaviours. It can be used during development, system integration testing, release validation, and automated build pipelines.

    Use this API to:
      - run compliance checks against Data Holder APIs
      - validate public, protected, and administrative endpoint behaviour
      - retrieve validation executions and results
      - inspect validator outcomes and response details
      - capture evidence for remediation and audit readiness

    Wych Compliance Validation supports a shift-left approach to open banking compliance, helping teams detect issues earlier and operate with greater confidence.
  contact:
    name: Customer Support
    email: support@wych.it

  license:
    name: 'Wych Holdings Limited, All rights reserved'
    url: 'https://www.wych.it/'

  termsOfService: 'https://www.wych.io/terms-conditions'

externalDocs:
  url: 'https://docs.wych.io'

tags:
  - name: tester-list-validators
    x-displayName: Validators
  - name: tester-execute
    x-displayName: Validation execution
  - name: tester-results
    x-displayName: Validation results

x-tagGroups:
  - name: Data Holder Tester
    tags:
      - tester-list-validators
      - tester-execute
      - tester-results

servers: 
  - url: register
    description: the register server
  - url: register-auth
    description: the register auth server
  - url: au-validator
    description: the data-holder-tester

security:
  - {}
  - apiKey: []
  - oauth2: []

paths:
  /admin/execute:
    post:
      description: |-
        Run tests validating endpoints the register needs to call
      operationId: adminExecution
      summary: Validate Admin Endpoints
      tags:
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      # Docs
      - tester-execute
      requestBody:
        $ref: '#/components/requestBodies/AdminRequest'
      responses:
        200:
          description: |-
            A json response containing the state and where necessary the exception for each of the relevant areas
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Execution'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'
  /public/execute:
    post:
      description: |-
        Run tests validating endpoints the public clients can call
      operationId: publicExecution
      summary: Validate Public Endpoints
      tags:
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      # Docs
      - tester-execute
      requestBody:
        $ref: '#/components/requestBodies/PublicRequest'
      responses:
        200:
          description: |-
            A json response containing the state and where necessary the exception for each of the relevant areas
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Execution'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'
  /connection/execute:
    post:
      description: |-
        Run tests validating endpoints the consented clients can call
      operationId: protectedExecution
      summary: Validate Protected Endpoints
      tags:
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      # Docs
      - tester-execute
      requestBody:
        $ref: '#/components/requestBodies/ProtectedRequest'
      responses:
        200:
          description: |-
            A json response containing the state and where necessary the exception for each of the relevant areas
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Execution'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'
  
  /brand/{brand}/connection:
    parameters:
    - name: brand
      in: path
      required: true
      schema:
        type: string
      description: Id of the brand to search against
    get:
      description: |-
        Grab all connections associated with a data holder
      operationId: dataholderConnections
      summary: Connect to the Data Holder 
      tags:
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      # Docs
      - tester-execute
      responses:
        200:
          description: |-
            A json response containing the connections for a data holder
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Connections'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'
  /brand/{brand}/public:
    parameters:
    - name: brand
      in: path
      required: true
      schema:
        type: string
      description: Id of the brand to execute against
    post:
      description: |-
        Validate the dataholder public endpoints 
      operationId: validatePublicEndpoints
      summary: Validate Public endpoints
      tags:
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      # Docs
      - tester-execute
      responses:
        200:
          description: |-
            A json response containing the state and where necessary the exception for each of the relevant areas
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Execution'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'
  /brand/{brand}/softwareProduct/{softwareProduct}/authorize:
    parameters:
    - name: brand
      in: path
      required: true
      schema:
        type: string
      description: Id of the brand to execute against
    - name: softwareProduct
      in: path
      required: true
      schema:
        type: string
      description: Id of the software product to execute with
    post:
      description: |-
        Establish a connection with a data holder using a software product
        Will always run a PAR test
      operationId: authorize
      summary: Connect to the Data Holder 
      security:
        - apiKey: []
          oauth2: ["CREATE:AU:RECIPIENT"]
      tags:
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      # Docs
      - tester-execute
      responses:
        200:
          description: |-
            A json response containing the state and where necessary the exception for each of the relevant areas
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Execution'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'
  /brand/{brand}/softwareProduct/{softwareProduct}/public:
    parameters:
    - name: brand
      in: path
      required: true
      schema:
        type: string
      description: Id of the brand to execute against
    - name: softwareProduct
      in: path
      required: true
      schema:
        type: string
      description: Id of the software product (Used to grab the public endpoint from the register)
    post:
      description: |-
        Validate the dataholder public endpoints 
      operationId: validatePublicEndpointsSP
      summary: Validate Public endpoints
      tags:
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      # Docs
      - tester-execute
      responses:
        200:
          description: |-
            A json response containing the state and where necessary the exception for each of the relevant areas
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Execution'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'
  /softwareProduct/{softwareProduct}/connection:
    parameters:
    - name: softwareProduct
      in: path
      required: true
      schema:
        type: string
      description: Id of the software product to search against
    get:
      description: |-
        Grab all connections associated with a software product
      operationId: softwareProductConnetions
      summary: Software Product Connections
      tags:
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      # docs
      - admin-dr-product
      responses:
        200:
          description: |-
            A json response containing the connections for a software product
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Connections'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'
  /softwareProduct/{softwareProduct}/jwks:
    parameters:
    - name: softwareProduct
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: App external Id
    get:
      description: |-
        Get the public keysets for a recipient
      operationId: readRecipientJwks
      security:
       - apiKey: []
      summary: Get the public keyset associated with an recipient
      tags:
      # product
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      # docs
      - admin-dr-product
      responses:
        200:
          description: JWKS of recipient
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Keyset'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'
  /connection/{connection}/execute:
    parameters:
    - name: connection
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: Id of the connection to execute with
    post:
      description: |-
        Use a connection to validate protected endpoints 
      operationId: execute
      summary: Validate Data Holder connection 
      tags:
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      # Docs
      - tester-execute
      responses:
        200:
          description: |-
            A json response containing the state and where necessary the exception for each of the relevant areas
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Execution'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'
  /executions:
    get:
      description: |-
        Fetch all executions
      operationId: readExecutions
      summary: Fetch executions
      tags:
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      # docs
      - tester-results
      responses:
        200:
          description: |-
            A json response containing the a list of executions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Executions'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'
  /execution/{executionId}:
    parameters:
    - name: executionId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: Id of the execution to fetch results for
    get:
      description: |-
        Fetch an execution's results
      operationId: readExecution
      summary: Fetch execution results
      tags:
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      # docs
      - tester-results
      responses:
        200:
          description: |-
            A json response containing the state and where necessary the exception for each of the relevant areas
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Execution'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'
  /execution/{executionId}/validator/{validator}/test/{test}/data:
    parameters:
    - name: executionId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: Id of the execution run
    - name: validator
      in: path
      required: true
      schema:
        type: string
      description: Id of the validator
    - name: test
      in: path
      required: true
      schema:
        type: string
      description: Id of the validator
    get:
      description: |-
        Retrieve the cached data for a test
      operationId: readTestResponseData
      summary: Read Test Response Data
      tags:
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      # docs
      - tester-results
      responses:
        200:
          description: |-
            A list of the cached responses
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataResponse'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'

  /validators:
    get:
      description: |-
        Retrieve the list of Validators that can be executed
      operationId: readValidators
      summary: Read Validator List
      tags:
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      # docs
      - tester-list-validators
      responses:
        200:
          description: |-
            A list of Validator objects
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Validators'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'
  /callback:
    get:
      description: Retrieve the token associated with the authorization_code provided by the data holder
      operationId: callback
      parameters:
      - name: response
        in: query
        required: false
        schema:
          type: string
        description: The jwt returned by the data holder to be exchanged for an AccessToken
      - name: code
        in: query
        required: false
        schema:
          type: string
        description: The authorization code returned by the data holder to be exchanged for an AccessToken
      - name: state
        in: query
        required: false
        schema:
          type: string
        description: The request identifier returned by the data holder to be exchanged for an AccessToken
      security:
        - apiKey: []
      summary: Retrieve the access token related using the authorization code
      tags:
      # product
      - recipient
      # Product
      - au-dataholder-tester
      # Version
      - v4
      responses:
        201:
          $ref: '#/components/responses/201'
        400:
          $ref: '#/components/responses/400'
        401:
          $ref: '#/components/responses/401'
        404:
          $ref: '#/components/responses/404'
        422:
          $ref: '#/components/responses/422'
        500:
          $ref: '#/components/responses/500'
components:
  apiKey:
    type: apiKey
    in: header
    name: x-api-key
    description: 'Wych gateway API key, this key can be found in the Wych customer portal.'
  clientToken:
    type: oauth2
    description: 'You retrieve this by using client credentials grant.'
    flows:
      clientCredentials:
        tokenUrl: /realms/partner/protocol/openid-connect/token
        scopes: {'READ:APP_USERS': Read all users associated with an app, 'EXECUTE:TOKEN_EXCHANGE': Retrieve the user token}
  requestBodies:
    AdminRequest:
      required: true
      content:
        application/x-www-form-urlencoded:
          schema:
            $ref: '#/components/schemas/AdminRequest'
    PublicRequest:
      required: true
      content:
        application/x-www-form-urlencoded:
          schema:
            $ref: '#/components/schemas/PublicRequest'
    AuthorizationRequest:
      required: true
      content:
        application/x-www-form-urlencoded:
          schema:
            $ref: '#/components/schemas/AuthorizationRequest'
    ProtectedRequest:
      required: true
      content:
        application/x-www-form-urlencoded:
          schema:
            $ref: '#/components/schemas/ProtectedRequest'
  responses:
    '200':
      description: OK
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/OK'
    '201':
      description: |-
        Created
        The response object contains the ID of the created object.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Created'
    '202':
      description: |-
        Accepted
        The request has been accepted for processing.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Accepted'
    '204':
      description: |-
        No content
        The request has been accepted for processing.
    '400':
      description: >-
        Bad Request

        The minimum header and parameter requirement for this request has not
        been met.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/BadRequest'
    '401':
      description: |-
        Unauthorised
        The minimum authorisation for the request has been met. 
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Unauthorised'
    '404':
      description: |-
        Not Found
        The requested entity could not be found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/NotFound'
    '422':
      description: |-
        Unprocessable Entity
        The request failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/UnprocessableEntity'
    '500':
      description: |-
        Service Unavailable
        The request failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    State:
      type: string
      description: did the check pass?
      enum:
        - SUCCESS
        - FAIL
        - SKIPPED
        - PENDING
    AdminRequest:
      type: object
      properties:
        uri:
          type: string
        token:
          type: string
    PublicRequest:
      type: object
      properties:
        uri:
          type: string
    AuthorizationRequest:
      type: object
      properties:
        uri:
          type: string
        clientAssertion:
          type: string
    ProtectedRequest:
      type: object
      properties:
        uri:
          type: string
        token:
          type: string
    Validators:
      type: object
      required:
      - states
      properties:
        validators:
          type: array
          items:
            $ref: '#/components/schemas/Validator'
    Validator:
      type: object
      required:
      - id
      - tests
      properties:
        id:
          type: string
          description: the id of the validator
        tests:
          type: array
          items:
            type: string
          description: |-
           items under test
        dependencies:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
          description: |-
           Any dependencies that will be run before running this validator
    Executions:
      type: object
      properties:
        executions:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                format: uuid
                description: ID of the Execution
              start:
                type: string
                format: date-time
              end:
                type: string
                format: date-time
              type:
                type: string
    Execution:
      type: object
      properties:
        executionId:
          type: string
          format: uuid
          description: Id of the execution
        start:
          type: string
          format: date-time
        end:
          type: string
          format: date-time
        type:
          type: string
        validations:
          type: array
          items:
            $ref: '#/components/schemas/Validation'
    Validation:
      type: object
      required:
      - name
      - state
      properties:
        test:
          type: string
          description: id of the test case
        suite:
          type: string
          description: id of the test suite
        name:
          type: string
          description: what is being checked?
        state:
          $ref: '#/components/schemas/State'
        manualStep:
          type: string
          description: A manual step is required, this will be a url pointed at the manual step 
        message:
          type: string
          description: any comments or exception trace
        specReference:
          type: string
          description: A link to the specification document which descibes how the response should be formatted
        numberOfTests:
          type: integer
          description: The number of tests performed for this validation
    DataResponse:
      type: object
      required:
      - states
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Data'
    Data:
      type: object
      properties:
        data:
          type: object
          additionalProperties: true
          description: The raw data response
        message:
          type: string
          description: Any additional information about the response data
        externalId:
          type: string
          description: The id of the response object if there is one
        account:
          type: string
          description: Optional parameter which helps filter the response data
    Connections:
      type: object
      properties:
        connections:
          type: array
          items:
            $ref: '#/components/schemas/Connection'
    Connection:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        arrangement:
          type: string
    Keyset:
      type: object
      properties:
        keys:
          $ref: '#/components/schemas/Jwks'
    Jwks:
      type: array
      items:
        $ref: '#/components/schemas/Jwk'
    Jwk:
      type: object
      properties:
        kty:
          type: string
        n:
          type: string
        e:
          type: string
        alg:
          type: string
        kid:
          type: string
        use:
          type: string
        key_ops:
          type: array
          items:
            type: string
    Header:
      type: object
      properties:
        name:
          type: string
        value:
          type: string
    Request:
      type: object
      properties:
        method:
          type: string
        uri:
          type: string
        requestHeaders:
          type: array
          items:
            $ref: '#/components/schemas/Header'
        requestBody:
          type: object
          additionalProperties: true
        responseCode:
          type: string
        responseCodeMessage:
          type: string
        responseHeaders:
          type: array
          items:
            $ref: '#/components/schemas/Header'
        responseBody:
          type: object
          additionalProperties: true
    BadRequest:
      type: object
      description: >-
        Bad Request

        The minimum header and parameter requirement for this request has not
        been met.
      example:
        code: 400
        id: 7d5ec75b-0142-437c-840f-ab5946481eaa
        message: The profile ID is required.
        type: BadRequest
      properties:
        message:
          type: string
        id:
          type: string
          format: uuid
        code:
          type: integer
          default: 400
        type:
          type: string
          default: BadRequest
    Created:
      type: object
      properties:
        message:
          type: string
        id:
          type: string
          format: uuid
        code:
          type: integer
          default: 201
        type:
          type: string
          default: Created
    Accepted:
      type: object
      properties:
        message:
          type: string
        id:
          type: string
          format: uuid
        code:
          type: integer
          default: 202
        type:
          type: string
          default: Accepted
    Error:
      type: object
      description: |-
        Service Unavailable
        An unexpected error has occurred. The service is currently unavailable.
      properties:
        message:
          type: string
        id:
          type: string
          format: uuid
        code:
          type: integer
          default: 500
        type:
          type: string
          default: Error
    Forbidden:
      type: object
      properties:
        message:
          type: string
        id:
          type: string
          format: uuid
        code:
          type: integer
          default: 403
        type:
          type: string
          default: Forbidden
    MethodNotAllowed:
      type: object
      properties:
        message:
          type: string
        id:
          type: string
          format: uuid
        code:
          type: integer
          default: 405
        type:
          type: string
          default: MethodNotAllowed
    NotFound:
      type: object
      example:
        type: NotFound
        id: 7d5ec75b-0142-437c-840f-ab5946481eaa
        message: No profile found for ID
      properties:
        message:
          type: string
        id:
          type: string
          format: uuid
        code:
          type: integer
          default: 404
        type:
          type: string
          default: NotFound
    OK:
      type: object
      properties:
        message:
          type: string
        id:
          type: string
          format: uuid
        code:
          type: integer
          default: 200
        type:
          type: string
          default: OK
    Unauthorised:
      type: object
      description: |-
        Unauthorised
        The minimum authorisation for the request has been met.
      example:
        code: 401
        message: This action requires permission.
        type: Unauthorised
      properties:
        message:
          type: string
        id:
          type: string
          format: uuid
        code:
          type: integer
          default: 401
        type:
          type: string
          default: Unauthorised
    UnprocessableEntity:
      type: object
      properties:
        message:
          type: string
        id:
          type: string
          format: uuid
        code:
          type: integer
          default: 422
        type:
          type: string
          default: UnprocessableEntity