openapi: 3.1.0
info:
  title: Wych Data Holder API
  version: '4.0.0'
  description: |
    The Wych Data Holder API enables Data Holder platforms to expose consent and authorisation capabilities through Wych.

    This API provides endpoints for OpenID Connect discovery, OAuth 2.0 client authentication, user-context token exchange, and consent lifecycle management. It is intended for server-side integrations where a trusted application needs to authenticate with the Wych platform, retrieve tokens, list consent records, inspect consent details, and revoke active consents.

    Use this API to:
      - discover realm-specific OpenID Connect configuration
      - authenticate using the OAuth 2.0 client credentials flow
      - retrieve a user-context access token
      - list active and historical consents
      - retrieve consent details
      - revoke consent when access should no longer be granted

    Protected API requests require a Wych gateway API key. Some operations also require a client access token or a user-context bearer token, depending on whether the request is made at system level or on behalf of a user.
  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'
jsonSchemaDialect: https://json-schema.org/draft/2020-12/schema
servers:
- url: https://api.wych.io/v4
  variables: {}
- url: https://login.wych.app
  variables: {}
tags:
  - name: auth-token
    description: Authenticate for access to the Wych platform.
    x-displayName: Authentication
  - name: consents
    description: Find and manage user consent records.
    x-displayName: Consents
x-tagGroups:
  - name: Core
    tags:
      - auth-token
  - name: Customer Data
    tags:
      - consents
paths:
  /realms/{externalId}/.well-known/openid-configuration:
    servers:
      - url: https://login.wych.app
    get:
      tags:
      - auth-token
      summary: getOpenIdConfiguration
      description: >
        Returns the OpenID Connect discovery configuration for the specified realm `{externalId}`.

        Includes key endpoints for authorization, token issuance, introspection, user info, and logout.
      operationId: getOpenIdConfiguration
      parameters:
      - name: externalId
        in: path
        description: The unique external ID of the realm (e.g., "partner", "customer-xyz").
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Successful retrieval of OpenID Connect configuration.
          headers: {}
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/OpenIDConfiguration'
                - examples:
                  - issuer: https://login.wych.app/realms/partner
                    authorization_endpoint: https://login.wych.app/realms/partner/protocol/openid-connect/auth
                    token_endpoint: https://login.wych.app/realms/partner/protocol/openid-connect/token
                    introspection_endpoint: https://login.wych.app/realms/partner/protocol/openid-connect/token/introspect
                    userinfo_endpoint: https://login.wych.app/realms/partner/protocol/openid-connect/userinfo
                    end_session_endpoint: https://login.wych.app/realms/partner/protocol/openid-connect/logout
                    jwks_uri: https://login.wych.app/realms/partner/protocol/openid-connect/certs
                    grant_types_supported:
                    - authorization_code
                    - refresh_token
                    - client_credentials
                contentMediaType: application/json
              example:
                issuer: https://login.wych.app/realms/partner
                authorization_endpoint: https://login.wych.app/realms/partner/protocol/openid-connect/auth
                token_endpoint: https://login.wych.app/realms/partner/protocol/openid-connect/token
                introspection_endpoint: https://login.wych.app/realms/partner/protocol/openid-connect/token/introspect
                userinfo_endpoint: https://login.wych.app/realms/partner/protocol/openid-connect/userinfo
                end_session_endpoint: https://login.wych.app/realms/partner/protocol/openid-connect/logout
                jwks_uri: https://login.wych.app/realms/partner/protocol/openid-connect/certs
                grant_types_supported:
                - authorization_code
                - refresh_token
                - client_credentials
      deprecated: false
      security:
      - apiKey: []
    parameters: []
  /realms/{externalId}/protocol/openid-connect/token:
    servers:
      - url: https://login.wych.app
    post:
      tags:
      - auth-token
      summary: authenticateDataHolder
      description: >
        Authenticate using the OAuth2 `client_credentials` grant type.

        Returns an access token for system-level (non-user) API access.
      operationId: authenticateDataHolder
      parameters:
      - name: externalId
        in: path
        description: The unique external ID of the realm (e.g., "partner", "customer-xyz").
        required: true
        schema:
          type: string
      - name: Content-Type
        in: header
        description: ''
        required: true
        schema:
          const: application/x-www-form-urlencoded
          type: string
      requestBody:
        content:
          application/x-www-form-urlencoded:
            encoding: {}
            schema:
              required:
              - grant_type
              - client_id
              - client_secret
              type: object
              properties:
                grant_type:
                  allOf:
                  - $ref: '#/components/schemas/grant_type'
                  - description: Must be `client_credentials`.
                    examples:
                    - client_credentials
                client_id:
                  type: string
                  description: Your application's client ID.
                  examples:
                  - your-client-id
                client_secret:
                  type: string
                  description: Your application's client secret.
                  examples:
                  - your-client-secret
              contentMediaType: application/x-www-form-urlencoded
        required: false
      responses:
        '200':
          description: Access token successfully issued.
          headers: {}
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/ClientToken'
                - examples:
                  - access_token: eyJhbGciOi...
                    expires_in: 900
                    token_type: Bearer
                    scope: email EXECUTE:TOKEN_EXCHANGE READ:APP_USERS profile
                contentMediaType: application/json
              example:
                access_token: eyJhbGciOi...
                expires_in: 900
                token_type: Bearer
                scope: email EXECUTE:TOKEN_EXCHANGE READ:APP_USERS profile
      deprecated: false
      security:
      - apiKey: []
    parameters: []
  /v4/user/{user_id}/token:
    get:
      tags:
      - auth-token
      summary: retrieveImpersonatedUserToken
      description: >
        Requests a new access token that acts on behalf of the specified user.

        The returned token (`user_access_token`) must be used for all subsequent API calls requiring user context.
      operationId: retrieveImpersonatedUserToken
      parameters:
      - name: user_id
        in: path
        description: The unique identifier of the user you wish to impersonate.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Successfully issued a user-context access token.
          headers: {}
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/UserToken'
                - examples:
                  - access_token: eyJhbGciOi...
                    token_type: Bearer
                    scope: '...READ:USER EXECUTE:REVOKE...'
                contentMediaType: application/json
              example:
                access_token: eyJhbGciOi...
                token_type: Bearer
                scope: '...READ:USER EXECUTE:REVOKE...'
      deprecated: false
      security:
      - apiKey: []
        clientToken: []
    parameters: []
  /v4/consents:
    get:
      tags:
      - consents
      summary: listConsents
      description: >
        Retrieves a list of all active and historical consents associated with the authenticated user or system.

        Supports optional filtering by status and pagination.
      operationId: listConsents
      parameters:
      - name: status
        in: query
        description: Filter consents by status (e.g., ACTIVE, REVOKED, EXPIRED).
        style: form
        explode: true
        schema:
          allOf:
          - $ref: '#/components/schemas/status1'
          - description: Filter consents by status (e.g., ACTIVE, REVOKED, EXPIRED).
      - name: limit
        in: query
        description: Maximum number of results to return (default is 50).
        style: form
        explode: true
        schema:
          maximum: 100
          minimum: 1
          type: integer
          contentEncoding: int32
          default: 50
      - name: offset
        in: query
        description: Number of records to skip for pagination.
        style: form
        explode: true
        schema:
          type: integer
          contentEncoding: int32
          default: 0
      responses:
        '200':
          description: A paginated list of consents.
          headers: {}
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Consent'
                description: ''
                contentMediaType: application/json
        '401':
          description: Unauthorized - missing or invalid token.
          headers: {}
          content: {}
        '403':
          description: Forbidden - insufficient permissions.
          headers: {}
          content: {}
        '500':
          description: Internal Server Error.
          headers: {}
          content: {}
      deprecated: false
      security:
      - apiKey: []
      - userToken: []
    parameters: []
  /v4/consent/{consentId}:
    get:
      tags:
      - consents
      summary: getConsent
      description: >
        Retrieves detailed information about a specific consent using its `consentId`.
      operationId: getConsent
      parameters:
      - name: consentId
        in: path
        description: The unique identifier for the consent.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Consent details retrieved successfully.
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Consent'
        '401':
          description: Unauthorized - missing or invalid token.
          headers: {}
          content: {}
        '403':
          description: Forbidden - insufficient permissions.
          headers: {}
          content: {}
        '500':
          description: Internal Server Error.
          headers: {}
          content: {}
      deprecated: false
      security:
      - apiKey: []
      - userToken: []
    delete:
      tags:
      - consents
      summary: revokeConsent
      description: "Revokes the specified consent. \n\nAfter revocation, the associated user data access permissions are removed, and any tokens granted under this consent will become invalid.\n"
      operationId: revokeConsent
      parameters:
      - name: consentId
        in: path
        description: The unique identifier for the consent to revoke.
        required: true
        schema:
          type: string
      responses:
        '204':
          description: Consent successfully revoked. No content is returned.
          headers: {}
          content: {}
        '401':
          description: Unauthorized - missing or invalid token.
          headers: {}
          content: {}
        '403':
          description: Forbidden - insufficient permissions.
          headers: {}
          content: {}
        '404':
          description: Consent not found or already revoked.
          headers: {}
          content: {}
        '500':
          description: Internal Server Error.
          headers: {}
          content: {}
      deprecated: false
      security:
      - apiKey: []
      - userToken: []
    parameters: []
components:
  schemas:
    OpenIDConfiguration:
      title: OpenIDConfiguration
      type: object
      properties:
        issuer:
          type: string
        authorization_endpoint:
          type: string
        token_endpoint:
          type: string
        introspection_endpoint:
          type: string
        userinfo_endpoint:
          type: string
        end_session_endpoint:
          type: string
        jwks_uri:
          type: string
        check_session_iframe:
          type: string
        grant_types_supported:
          uniqueItems: true
          type: array
          items:
            $ref: '#/components/schemas/GrantTypesSupported'
          description: ''
    ClientToken:
      title: ClientToken
      type: object
      properties:
        access_token:
          type: string
          description: Bearer token for API authorization.
        expires_in:
          type: integer
          description: Number of seconds before the token expires.
          contentEncoding: int32
        token_type:
          allOf:
          - $ref: '#/components/schemas/TokenType'
          - description: Type of token issued (always `Bearer`).
        scope:
          type: string
          description: Space-separated scopes associated with the token.
    UserToken:
      title: UserToken
      type: object
      properties:
        access_token:
          type: string
          description: The access token for the impersonated user.
        token_type:
          allOf:
          - $ref: '#/components/schemas/TokenType1'
          - description: Type of token issued.
        scope:
          type: string
          description: Space-separated list of scopes associated with the token.
    Permission:
      title: Permission
      enum:
      - ReadAccountsBasic
      - ReadAccountsDetail
      - ReadBalances
      - ReadBeneficiariesBasic
      - ReadBeneficiariesDetail
      - ReadDirectDebits
      - ReadOffers
      - ReadParty
      - ReadPartyAuthUser
      - ReadScheduledPaymentsBasic
      - ReadScheduledPaymentsDetail
      - ReadStandingOrdersBasic
      - ReadStandingOrdersDetail
      - ReadStatementsBasic
      - ReadStatementsDetail
      - ReadTransactionsBasic
      - ReadTransactionsCredits
      - ReadTransactionsDebits
      - ReadTransactionsDetail
      type: string
    Recipient:
      title: Recipient
      type: object
      properties:
        recipientId:
          type: string
        name:
          type: string
    Consent:
      title: Consent
      type: object
      properties:
        consentId:
          type: string
        recipient:
          allOf:
          - $ref: '#/components/schemas/Recipient'
          - {}
        userId:
          type: string
        accounts:
          type: array
          items:
            type: string
          description: ''
        status:
          allOf:
          - $ref: '#/components/schemas/Status'
          - {}
        scopes:
          type: string
        permissions:
          type: array
          items:
            $ref: '#/components/schemas/Permission'
          description: ''
        statusUpdated:
          type: string
        start:
          type: string
        end:
          type: string
        transactionFrom:
          type: string
        transactionTo:
          type: string
      examples:
      - consentId: d3a4f6c8-74a1-4567-9f2b-61f4bcf2543a
        recipient:
          recipientId: app-xyz-001
          name: SmartBudget App
        userId: user-456
        status: Authorised
        scopes: openid accounts transactions
        permissions:
        - ReadAccountsBasic
        - ReadBalances
        - ReadTransactionsDetail
        accounts:
        - account-123
        - account-789
        statusUpdated: 2025-05-08T10:15:30Z
        start: 2025-05-01
        end: 2025-11-01
        transactionFrom: 2025-01-01
        transactionTo: 2025-04-30
    grant_type:
      title: grant_type
      const: client_credentials
      type: string
      description: Must be `client_credentials`.
    GrantTypesSupported:
      title: GrantTypesSupported
      enum:
      - authorization_code
      - refresh_token
      - client_credentials
      type: string
    Status:
      title: Status
      enum:
      - Authorised
      - AwaitingAuthorisation
      - Rejected
      - Revoked
      type: string
    status1:
      title: status1
      enum:
      - ACTIVE
      - REVOKED
      - EXPIRED
      type: string
    TokenType:
      title: TokenType
      const: Bearer
      type: string
      description: Type of token issued (always `Bearer`).
    TokenType1:
      title: TokenType1
      const: Bearer
      type: string
      description: Type of token issued.
  securitySchemes:
    apiKey:
      type: apiKey
      description: Wych gateway API key, provided via the Wych customer portal.
      name: x-api-key
      in: header
    clientToken:
      type: oauth2
      description: Retrieve this token using the OAuth2 client credentials flow.
      flows:
        clientCredentials:
          tokenUrl: https://api.wych.io/v4/realms/partner/protocol/openid-connect/token
          scopes:
            READ:APP_USERS: Read all users associated with an app.
            EXECUTE:TOKEN_EXCHANGE: Retrieve a user token via token exchange.
    userToken:
      type: http
      description: User token retrieved through token exchange. See [Retrieve Impersonated User Token](#tag/auth-token/operation/retrieveImpersonatedUserToken) for details.
      scheme: bearer
      bearerFormat: jwt
security:
- apiKey: []
- clientToken: []
- userToken: []
externalDocs:
  url: https://docs.wych.io/dataholder/
