openapi: 3.1.0
info:
  title: YCMT EU Partner Authentication API
  version: 1.0.0
  summary: Partner-facing authentication API for YCMT EU.
  contact:
    name: YesCash Group Engineering
  license:
    name: Confidential — YesCash Group internal and per-partner-onboarding-NDA distribution
    url: https://docs.yescash.example/license
  description: Partner-facing OpenAPI contract for YCMT EU. This file describes the
    request and response interface that partner applications use. It intentionally
    omits YCMT internal implementation details. The human-readable Partner Developer
    API Guide remains the primary implementation companion for examples and flow guidance.
servers:
- url: https://api.eu.yescash.example
  description: YCMT EU production host placeholder. The production host is confirmed
    during partner activation.
- url: https://sandbox.api.eu.yescash.example
  description: YCMT EU sandbox host placeholder. The sandbox host is provided during
    partner onboarding.
tags:
- name: Authentication
- name: Registration
- name: Identifiers
- name: Password Reset
- name: MFA
- name: Device-Bound Auth
security:
- apimSubscriptionKey: []
  bearerAuth: []
components:
  securitySchemes:
    apimSubscriptionKey:
      type: apiKey
      in: header
      name: Ocp-Apim-Subscription-Key
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
    registrationSessionToken:
      type: apiKey
      in: header
      name: X-Registration-Session-Token
    mfaChallengeToken:
      type: apiKey
      in: header
      name: X-Mfa-Challenge-Token
  parameters:
    XCorrelationIdHeader:
      name: X-Correlation-Id
      in: header
      required: false
      schema:
        type: string
        format: uuid
        example: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
    XIdempotencyKeyHeader:
      name: X-Idempotency-Key
      in: header
      required: true
      schema:
        type: string
        format: uuid
        example: 8b2c4d6e-1f3a-4b5c-9d8e-7f6a5b4c3d2e
    XIdempotencyKeyHeaderOptional:
      name: X-Idempotency-Key
      in: header
      required: false
      schema:
        type: string
        format: uuid
        example: 8b2c4d6e-1f3a-4b5c-9d8e-7f6a5b4c3d2e
    AcceptLanguageHeader:
      name: Accept-Language
      in: header
      required: false
      schema:
        type: string
        example: en
        default: es-ES
  schemas:
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          - correlationId
          properties:
            code:
              type: string
              example: auth.tokenExpired
            message:
              type: string
              example: The bearer token has expired.
            correlationId:
              type: string
              format: uuid
              example: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
            details: {}
    ValidationIssue:
      type: object
      required:
      - field
      - code
      properties:
        field:
          type: string
          example: identifier
        code:
          type: string
          example: validation.fieldFormatInvalid
        message:
          type: string
          example: identifier must be a valid email or E.164 phone number
    PasswordPolicyDetails:
      type: object
      required:
      - rules
      - violations
      properties:
        rules:
          type: array
          items:
            type: string
            enum:
            - MIN_LENGTH_10
            - REQUIRE_UPPERCASE
            - REQUIRE_LOWERCASE
            - REQUIRE_DIGIT
            - REQUIRE_SYMBOL
          example:
          - MIN_LENGTH_10
          - REQUIRE_UPPERCASE
          - REQUIRE_LOWERCASE
          - REQUIRE_DIGIT
          - REQUIRE_SYMBOL
        violations:
          type: array
          items:
            type: string
            enum:
            - MIN_LENGTH_10
            - REQUIRE_UPPERCASE
            - REQUIRE_LOWERCASE
            - REQUIRE_DIGIT
            - REQUIRE_SYMBOL
          example:
          - MIN_LENGTH_10
          - REQUIRE_DIGIT
    OtpAttemptDetails:
      type: object
      properties:
        attemptsRemaining:
          type: integer
          minimum: 0
          maximum: 2
          example: 2
    CustomerStatusDetails:
      type: object
      properties:
        currentStatus:
          type: string
          enum:
          - PENDING
          - VALID
          - MONITORED
          - RESTRICTED
          - BLOCKED
          - CLOSED
        requiredStatus:
          type: string
          enum:
          - PENDING
          - VALID
          - MONITORED
          - RESTRICTED
          - BLOCKED
          - CLOSED
        remediationHint:
          type: string
          example: CONTACT_SUPPORT
    RateLimitDetails:
      type: object
      properties:
        scope:
          type: string
          enum:
          - partnerCustomer
          - partnerIp
          - partnerIdentifier
          - partnerGlobal
        retryAfterSeconds:
          type: integer
          example: 60
    IdentifierType:
      type: string
      enum:
      - EMAIL
      - PHONE
    Identifier:
      type: string
      examples:
      - alice.example@example.com
      - 34612345678
    IdentifierMasked:
      type: string
      examples:
      - a****@example.com
      - +34******5678
    TokenBundle:
      type: object
      required:
      - accessToken
      - refreshToken
      - tokenType
      - expiresIn
      properties:
        accessToken:
          type: string
          example: eyJraWQiOiJrZXktZXUtMjAyNi0wMSIsImFsZyI6IlJTMjU2In0.<jwt-claims>.<sig>
        refreshToken:
          type: string
          example: eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiZGlyIn0.<refresh-claims>
        idToken:
          type: string
          example: eyJraWQiOiJrZXktZXUtMjAyNi0wMSIsImFsZyI6IlJTMjU2In0.<id-claims>.<sig>
        tokenType:
          type: string
          enum:
          - Bearer
        expiresIn:
          type: integer
          example: 3600
        accessTokenExpiresAt:
          type: string
          format: date-time
          example: 2026-05-12 15:03:15+00:00
        refreshTokenExpiresAt:
          type: string
          format: date-time
          example: 2026-05-22 14:03:15+00:00
        idTokenExpiresAt:
          type: string
          format: date-time
          example: 2026-05-12 15:03:15+00:00
    AuthStartRequest:
      type: object
      required:
      - identifier
      - identifierType
      properties:
        identifier:
          $ref: '#/components/schemas/Identifier'
        identifierType:
          $ref: '#/components/schemas/IdentifierType'
    AuthStartResponse:
      type: object
      required:
      - loginAttemptId
      - loginAttemptExpiresAt
      properties:
        loginAttemptId:
          type: string
          example: LA-01HX9F2J7K3M5N7P9Q1R3T5V7W
        loginAttemptExpiresAt:
          type: string
          format: date-time
          example: 2026-05-11 14:10:12+00:00
    AuthLoginRequest:
      type: object
      required:
      - loginAttemptId
      properties:
        loginAttemptId:
          type: string
          example: LA-01HX9F2J7K3M5N7P9Q1R3T5V7W
        password:
          type: string
          format: password
          example: AliceStr0ngP@ssw0rd!
        mfaChallengeId:
          type: string
          example: MFC-01HX9J5Q1R7S9T1U3V5W7X9Y1Z
        mfaCode:
          type: string
          example: '847291'
        mfaMethod:
          type: string
          enum:
          - TOTP
          - SMS_MFA
        mfaEnrolmentSessionId:
          type: string
          example: MFE-01HX9K7S2T9U1V3W5X7Y9Z1A3B
        mfaEnrolmentMethod:
          type: string
          enum:
          - TOTP
          - SMS_MFA
        mfaEnrolmentCode:
          type: string
          example: '319847'
    AuthLoginResponse:
      oneOf:
      - $ref: '#/components/schemas/AuthLoginResponseAuthenticated'
      - $ref: '#/components/schemas/AuthLoginResponseMfaChallengeRequired'
      - $ref: '#/components/schemas/AuthLoginResponseMfaEnrolmentRequired'
      discriminator:
        propertyName: authStatus
        mapping:
          AUTHENTICATED: '#/components/schemas/AuthLoginResponseAuthenticated'
          MFA_CHALLENGE_REQUIRED: '#/components/schemas/AuthLoginResponseMfaChallengeRequired'
          MFA_ENROLMENT_REQUIRED: '#/components/schemas/AuthLoginResponseMfaEnrolmentRequired'
    AuthLoginResponseAuthenticated:
      type: object
      required:
      - authStatus
      - accessToken
      - refreshToken
      - tokenType
      - expiresIn
      - subscriptionId
      properties:
        authStatus:
          type: string
          enum:
          - AUTHENTICATED
        accessToken:
          type: string
        refreshToken:
          type: string
        idToken:
          type: string
        tokenType:
          type: string
          enum:
          - Bearer
        expiresIn:
          type: integer
          example: 3600
        accessTokenExpiresAt:
          type: string
          format: date-time
        refreshTokenExpiresAt:
          type: string
          format: date-time
        idTokenExpiresAt:
          type: string
          format: date-time
        subscriptionId:
          type: string
          example: SUB-01HX9F2J7K3M5N7P9Q1R3T5V7W
        customerStatus:
          type: string
          enum:
          - PENDING
          - VALID
          - MONITORED
          - RESTRICTED
          - BLOCKED
          - CLOSED
        mfaEnrolment:
          type: object
          properties:
            method:
              type: string
              enum:
              - TOTP
              - SMS_MFA
            enrolledAt:
              type: string
              format: date-time
    AuthLoginResponseMfaChallengeRequired:
      type: object
      required:
      - authStatus
      - mfaChallengeId
      - mfaMethod
      - mfaChallengeExpiresAt
      properties:
        authStatus:
          type: string
          enum:
          - MFA_CHALLENGE_REQUIRED
        mfaChallengeId:
          type: string
          example: MFC-01HX9J5Q1R7S9T1U3V5W7X9Y1Z
        mfaMethod:
          type: string
          enum:
          - TOTP
          - SMS_MFA
        mfaChallengeExpiresAt:
          type: string
          format: date-time
        supportedMethods:
          type: array
          items:
            type: string
            enum:
            - TOTP
            - SMS_MFA
    AuthLoginResponseMfaEnrolmentRequired:
      type: object
      required:
      - authStatus
      - mfaEnrolmentSessionId
      - supportedMethods
      - mfaEnrolmentSessionExpiresAt
      properties:
        authStatus:
          type: string
          enum:
          - MFA_ENROLMENT_REQUIRED
        mfaEnrolmentSessionId:
          type: string
          example: MFE-01HX9K7S2T9U1V3W5X7Y9Z1A3B
        supportedMethods:
          type: array
          items:
            type: object
            required:
            - method
            - recommended
            - rationale
            properties:
              method:
                type: string
                enum:
                - TOTP
                - SMS_MFA
              recommended:
                type: boolean
              rationale:
                type: string
        mfaEnrolmentSessionExpiresAt:
          type: string
          format: date-time
    AuthRefreshRequest:
      type: object
      required:
      - refreshToken
      properties:
        refreshToken:
          type: string
          example: eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiZGlyIn0...
    AuthRefreshResponse:
      type: object
      required:
      - accessToken
      - refreshToken
      - tokenType
      - expiresIn
      properties:
        accessToken:
          type: string
        refreshToken:
          type: string
        idToken:
          type: string
        tokenType:
          type: string
          enum:
          - Bearer
        expiresIn:
          type: integer
          example: 3600
        accessTokenExpiresAt:
          type: string
          format: date-time
        idTokenExpiresAt:
          type: string
          format: date-time
    AuthLogoutRequest:
      type: object
      required:
      - refreshToken
      properties:
        refreshToken:
          type: string
    RegisterStartRequest:
      type: object
      required:
      - identifier
      - identifierType
      properties:
        identifier:
          $ref: '#/components/schemas/Identifier'
        identifierType:
          $ref: '#/components/schemas/IdentifierType'
        partnerCustomerRef:
          type: string
          maxLength: 128
          example: PARTNER-ALICE-001
    RegisterStartResponse:
      type: object
      required:
      - registrationId
      - next
      - registrationIdExpiresAt
      properties:
        registrationId:
          type: string
          example: RG-01HX9F2J7K3M5N7P9Q1R3T5V7W
        next:
          type: string
          enum:
          - OTP
        registrationIdExpiresAt:
          type: string
          format: date-time
          example: 2026-05-12 14:30:01+00:00
    RegisterVerifyOtpRequest:
      type: object
      required:
      - registrationId
      - otp
      properties:
        registrationId:
          type: string
          example: RG-01HX9F2J7K3M5N7P9Q1R3T5V7W
        otp:
          type: string
          pattern: ^[0-9]{6}$
          example: '849213'
    RegisterVerifyOtpResponse:
      type: object
      required:
      - registrationId
      - branch
      - next
      properties:
        registrationId:
          type: string
          example: RG-01HX9F2J7K3M5N7P9Q1R3T5V7W
        branch:
          type: string
          enum:
          - NEW_CUSTOMER
          - EXISTING_CUSTOMER
        next:
          type: string
          enum:
          - SET_PASSWORD
          - VERIFY_PASSWORD
          - PASSWORD
    RegisterSetPasswordRequest:
      type: object
      required:
      - registrationId
      - password
      properties:
        registrationId:
          type: string
          example: RG-01HX9F2J7K3M5N7P9Q1R3T5V7W
        password:
          type: string
          format: password
          example: AliceStr0ngP@ssw0rd!
    RegisterSetPasswordResponse:
      type: object
      required:
      - accessToken
      - refreshToken
      - tokenType
      - expiresIn
      - customerStatus
      properties:
        accessToken:
          type: string
        refreshToken:
          type: string
        idToken:
          type: string
        tokenType:
          type: string
          enum:
          - Bearer
        expiresIn:
          type: integer
          example: 3600
        accessTokenExpiresAt:
          type: string
          format: date-time
        refreshTokenExpiresAt:
          type: string
          format: date-time
        idTokenExpiresAt:
          type: string
          format: date-time
        subscriptionId:
          type: string
          example: SUB-01HX9F2J7K3M5N7P9Q1R3T5V7W
        customerStatus:
          type: string
          enum:
          - PENDING
          - VALID
          - MONITORED
          - RESTRICTED
          - BLOCKED
          - CLOSED
    IdentifierListResponse:
      type: object
      required:
      - identifiers
      properties:
        identifiers:
          type: array
          items:
            $ref: '#/components/schemas/IdentifierListItem'
    IdentifierListItem:
      type: object
      required:
      - identifierId
      - identifierType
      - identifierMasked
      - verifiedAt
      - canRemove
      properties:
        identifierId:
          type: string
          example: ID-01HX9F2J7K3M5N7P9Q1R3T5V7W
        identifierType:
          $ref: '#/components/schemas/IdentifierType'
        identifierMasked:
          $ref: '#/components/schemas/IdentifierMasked'
        verifiedAt:
          type: string
          format: date-time
        canRemove:
          type: boolean
    IdentifierAddStartRequest:
      type: object
      required:
      - identifier
      - identifierType
      properties:
        identifier:
          $ref: '#/components/schemas/Identifier'
        identifierType:
          $ref: '#/components/schemas/IdentifierType'
    IdentifierAddStartResponse:
      type: object
      required:
      - identifierAddId
      - next
      - identifierAddIdExpiresAt
      properties:
        identifierAddId:
          type: string
          example: IA-01HX9F2J7K3M5N7P9Q1R3T5V7W
        next:
          type: string
          enum:
          - OTP
        identifierAddIdExpiresAt:
          type: string
          format: date-time
          example: 2026-05-11 14:35:12+00:00
    IdentifierAddVerifyOtpRequest:
      type: object
      required:
      - identifierAddId
      - otp
      properties:
        identifierAddId:
          type: string
          example: IA-01HX9F2J7K3M5N7P9Q1R3T5V7W
        otp:
          type: string
          pattern: ^[0-9]{6}$
          example: '123456'
    IdentifierAddVerifyOtpResponse:
      $ref: '#/components/schemas/IdentifierListItem'
    IdentifierRemoveStartRequest:
      type: object
      required:
      - otpDeliveryIdentifierId
      properties:
        otpDeliveryIdentifierId:
          type: string
          example: ID-01HX9F2J7K3M5N7P9Q1R3T5V7X
    IdentifierRemoveStartResponse:
      type: object
      required:
      - identifierRemoveId
      - next
      - otpDeliveryIdentifierMasked
      - identifierRemoveIdExpiresAt
      properties:
        identifierRemoveId:
          type: string
          example: IR-01HX9F2J7K3M5N7P9Q1R3T5V7W
        next:
          type: string
          enum:
          - OTP
        otpDeliveryIdentifierMasked:
          $ref: '#/components/schemas/IdentifierMasked'
        identifierRemoveIdExpiresAt:
          type: string
          format: date-time
    IdentifierRemoveVerifyOtpRequest:
      type: object
      required:
      - identifierRemoveId
      - otp
      properties:
        identifierRemoveId:
          type: string
          example: IR-01HX9F2J7K3M5N7P9Q1R3T5V7W
        otp:
          type: string
          pattern: ^[0-9]{6}$
          example: '123456'
    IdentifierRemoveVerifyOtpResponse:
      type: object
      required:
      - identifierId
      - deactivatedAt
      properties:
        identifierId:
          type: string
          example: ID-01HX9F2J7K3M5N7P9Q1R3T5V7W
        deactivatedAt:
          type: string
          format: date-time
    PasswordForgotRequest:
      type: object
      required:
      - identifier
      - identifierType
      properties:
        identifier:
          $ref: '#/components/schemas/Identifier'
        identifierType:
          $ref: '#/components/schemas/IdentifierType'
        registrationId:
          type: string
          example: RG-01HX9F2J7K3M5N7P9Q1R3T5V7W
    PasswordForgotResponse:
      type: object
      required:
      - passwordResetId
      - next
      - passwordResetIdExpiresAt
      properties:
        passwordResetId:
          type: string
          example: PR-01HX9F2J7K3M5N7P9Q1R3T5V7W
        next:
          type: string
          enum:
          - OTP
        passwordResetIdExpiresAt:
          type: string
          format: date-time
          example: 2026-05-11 14:35:12+00:00
    PasswordResetRequest:
      type: object
      required:
      - passwordResetId
      - otp
      - newPassword
      properties:
        passwordResetId:
          type: string
          example: PR-01HX9F2J7K3M5N7P9Q1R3T5V7W
        otp:
          type: string
          pattern: ^[0-9]{6}$
          example: '123456'
        newPassword:
          type: string
          format: password
    PasswordResetResponse:
      oneOf:
      - $ref: '#/components/schemas/PasswordResetResponseStandalone'
      - $ref: '#/components/schemas/PasswordResetResponseCarryForward'
      discriminator:
        propertyName: next
        mapping:
          LOGIN: '#/components/schemas/PasswordResetResponseStandalone'
          REGISTER_COMPLETE: '#/components/schemas/PasswordResetResponseCarryForward'
    PasswordResetResponseStandalone:
      type: object
      required:
      - next
      properties:
        next:
          type: string
          enum:
          - LOGIN
    PasswordResetResponseCarryForward:
      type: object
      required:
      - next
      - registrationId
      - registrationIdExpiresAt
      properties:
        next:
          type: string
          enum:
          - REGISTER_COMPLETE
        registrationId:
          type: string
          example: RG-01HX9F2J7K3M5N7P9Q1R3T5V7W
        registrationIdExpiresAt:
          type: string
          format: date-time
    DeviceMetadata:
      type: object
      properties:
        deviceModel:
          type: string
          example: iPhone15,3
        osVersion:
          type: string
          example: iOS 18.2
        appVersion:
          type: string
          example: 1.4.2
        platform:
          type: string
          enum:
          - iOS
          - Android
          - Web
    DeviceRegistrationStartRequest:
      type: object
      properties:
        deviceMetadata:
          $ref: '#/components/schemas/DeviceMetadata'
        stepUpToken:
          type: string
          example: <base64url single-use token>
    DeviceRegistrationStartResponse:
      oneOf:
      - $ref: '#/components/schemas/DeviceRegistrationStartHappyPath'
      - $ref: '#/components/schemas/DeviceRegistrationStartStepUpRequired'
    DeviceRegistrationStartHappyPath:
      type: object
      required:
      - deviceRegistrationId
      - registrationChallenge
      - expiresAt
      - publicKeyRequirements
      - stepUpApplied
      properties:
        deviceRegistrationId:
          type: string
          example: DRG-01HX9F2J7K3M5N7P9Q1R3T5V7W
        registrationChallenge:
          type: string
          example: Y2hhbGxlbmdlLWJhc2U2NHVybC1lbmNvZGVkLTMyLWJ5dGVz
        expiresAt:
          type: string
          format: date-time
          example: 2026-05-11 15:13:22+00:00
        publicKeyRequirements:
          type: object
          required:
          - algorithm
          - minimumModulusBits
          - recommendedModulusBits
          properties:
            algorithm:
              type: string
              enum:
              - RS256
            minimumModulusBits:
              type: integer
              enum:
              - 2048
            recommendedModulusBits:
              type: integer
              example: 3072
        stepUpApplied:
          type: boolean
          example: false
    DeviceRegistrationStartStepUpRequired:
      type: object
      required:
      - stepUpRequired
      - kycSessionId
      - livenessWebviewUrl
      - expiresAt
      - nextStep
      properties:
        stepUpRequired:
          type: string
          enum:
          - LIVENESS_CHECK
        kycSessionId:
          type: string
          example: KYC-01HX9F2J7K3M5N7P9Q1R3T5V7W
        livenessWebviewUrl:
          type: string
          format: uri
          example: https://kyc.eu.yescash.example/sessions/KYC-01HX...
        expiresAt:
          type: string
          format: date-time
        nextStep:
          type: string
          enum:
          - OPEN_LIVENESS_WEBVIEW
    PublicKeyJwk:
      type: object
      required:
      - kty
      - n
      - e
      - alg
      - use
      properties:
        kty:
          type: string
          enum:
          - RSA
        n:
          type: string
          example: 0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cb...
        e:
          type: string
          example: AQAB
        kid:
          type: string
          example: device-key-2026-05-11T14-58Z
        alg:
          type: string
          enum:
          - RS256
        use:
          type: string
          enum:
          - sig
    DeviceRegistrationProof:
      type: string
      example: eyJhbGciOiJSUzI1NiIsImtpZCI6ImRldmljZS1rZXktMjAyNi0wNS0xMVQxNC01OFoiLCJ0eXAiOiJKV1QifQ.eyJkZXZpY2VfcmVnaXN0cmF0aW9uX2lkIjoiRFJHLTAxSFg5RjJKN0szTTVON1A5UTFSM1Q1VjdXIiwiaWF0IjoxNzE1NjA4ODIyLCJwdWJsaWNfa2V5X3RodW1icHJpbnQiOiI4bUFoUWpKeVptSlZaa1JEZVhCS1ZucFFVRmhNUTNKSlZuRktVelJTZFRrIiwicmVnaXN0cmF0aW9uX2NoYWxsZW5nZSI6IlkyaGhiR3hsYm1kbExXSmhjMlUyTkhWeWJDMWxibU52WkdWa0xUTXlMV0o1ZEdWeiIsInJlZ2lzdHJhdGlvbl9wcm9vZl92MSI6InYxIn0.<signature_bytes>
    DeviceRegistrationCompleteRequest:
      type: object
      required:
      - deviceRegistrationId
      - publicKey
      - registrationProof
      properties:
        deviceRegistrationId:
          type: string
          example: DRG-01HX9F2J7K3M5N7P9Q1R3T5V7W
        publicKey:
          $ref: '#/components/schemas/PublicKeyJwk'
        registrationProof:
          $ref: '#/components/schemas/DeviceRegistrationProof'
    DeviceRegistrationCompleteResponse:
      type: object
      required:
      - deviceId
      - registeredAt
      - publicKeyThumbprint
      - stepUpApplied
      properties:
        deviceId:
          type: string
          example: DEV-01HX9F2J7K3M5N7P9Q1R3T5V7W
        registeredAt:
          type: string
          format: date-time
        publicKeyThumbprint:
          type: string
          example: 8mAhQjJyZmJVZkRDeXBKVnpQUFhMQ3JJVnFKUzRSdTk
        stepUpApplied:
          type: boolean
        priorDeviceIdDeactivated:
          type: string
          nullable: true
          example: DEV-01HX9F2J7K3M5N7P9Q1R3T5V6X
  responses:
    BadRequest:
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/XCorrelationIdResponseHeader'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            fieldRequired:
              summary: validation.fieldRequired — required field absent
              value:
                error:
                  code: validation.fieldRequired
                  message: One or more required fields are missing.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
                  details:
                    issues:
                    - field: identifier
                      code: validation.fieldRequired
            fieldFormatInvalid:
              summary: validation.fieldFormatInvalid — bad format
              value:
                error:
                  code: validation.fieldFormatInvalid
                  message: One or more fields have an invalid format.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
                  details:
                    issues:
                    - field: identifier
                      code: validation.fieldFormatInvalid
    Unauthorized:
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/XCorrelationIdResponseHeader'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            tokenExpired:
              summary: auth.tokenExpired — access token TTL elapsed
              value:
                error:
                  code: auth.tokenExpired
                  message: The bearer token has expired.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
            tokenInvalid:
              summary: auth.tokenInvalid — bad token
              value:
                error:
                  code: auth.tokenInvalid
                  message: The bearer token is invalid.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
            credentialMismatch:
              summary: auth.credentialMismatch — generic credential rejection (AML-tipping-off
                framework)
              value:
                error:
                  code: auth.credentialMismatch
                  message: The credentials you provided are not correct.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
            otpInvalid:
              summary: auth.otpInvalid — OTP did not match
              value:
                error:
                  code: auth.otpInvalid
                  message: The verification code is incorrect.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
                  details:
                    attemptsRemaining: 2
    Forbidden:
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/XCorrelationIdResponseHeader'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            statusBlocking:
              summary: customer.statusBlocking — customer is in a blocking status
              value:
                error:
                  code: customer.statusBlocking
                  message: Your account is not available. Please contact support.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
                  details:
                    currentStatus: BLOCKED
                    remediationHint: CONTACT_SUPPORT
            partnerNotActive:
              summary: scope.partnerNotActive — partner status is not active
              value:
                error:
                  code: scope.partnerNotActive
                  message: The service is not available at this time.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
    NotFound:
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/XCorrelationIdResponseHeader'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            scopeNotFound:
              summary: scope.notFound — resource not found within partner scope
              value:
                error:
                  code: scope.notFound
                  message: The requested resource was not found.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
    Conflict:
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/XCorrelationIdResponseHeader'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            idempotencyConflict:
              summary: idempotency.keyConflict — same key with divergent body
              value:
                error:
                  code: idempotency.keyConflict
                  message: The idempotency key was already used with a different request
                    body.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
    Gone:
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/XCorrelationIdResponseHeader'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            stepUpTokenExpired:
              summary: device.stepUpTokenExpired
              value:
                error:
                  code: device.stepUpTokenExpired
                  message: The step-up token has expired. Please restart the liveness
                    verification.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
    UnprocessableEntity:
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/XCorrelationIdResponseHeader'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            publicKeyAlgorithmUnsupported:
              summary: device.publicKeyAlgorithmUnsupported — wrong algorithm or key
                type
              value:
                error:
                  code: device.publicKeyAlgorithmUnsupported
                  message: The submitted public key algorithm is not supported.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
    TooManyRequests:
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/XCorrelationIdResponseHeader'
        Retry-After:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            rateLimitExceeded:
              summary: validation.rateLimitExceeded
              value:
                error:
                  code: validation.rateLimitExceeded
                  message: Rate limit exceeded.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
                  details:
                    scope: partnerIdentifier
                    retryAfterSeconds: 60
            otpAttemptsExhausted:
              summary: auth.otpAttemptsExhausted — 3 failed OTP attempts
              value:
                error:
                  code: auth.otpAttemptsExhausted
                  message: Too many incorrect codes. Please start again.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
            accountLockout:
              summary: auth.accountTemporarilyLocked
              value:
                error:
                  code: auth.accountTemporarilyLocked
                  message: Too many failed attempts. Please try again later.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
    InternalServerError:
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/XCorrelationIdResponseHeader'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            serverUnexpected:
              summary: server.unexpected
              value:
                error:
                  code: server.unexpected
                  message: An unexpected error occurred.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
    BadGateway:
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/XCorrelationIdResponseHeader'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            otpDispatchFailed:
              summary: auth.otpDispatchFailed
              value:
                error:
                  code: auth.otpDispatchFailed
                  message: OTP dispatch failed. Please retry.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
    ServiceUnavailable:
      headers:
        X-Correlation-Id:
          $ref: '#/components/headers/XCorrelationIdResponseHeader'
        Retry-After:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            upstreamUnavailable:
              summary: auth.upstreamUnavailable
              value:
                error:
                  code: auth.upstreamUnavailable
                  message: Authentication service is temporarily unavailable. Please
                    retry.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
            softFailover:
              summary: availability.softFailover
              value:
                error:
                  code: availability.softFailover
                  message: The service is in a degraded state. Read operations may
                    succeed.
                  correlationId: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
  headers:
    XCorrelationIdResponseHeader:
      schema:
        type: string
        format: uuid
      example: c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d
paths:
  /v1/auth/start:
    post:
      operationId: authStart
      tags:
      - Authentication
      summary: Initiate customer login (step 1 of 2).
      security:
      - apimSubscriptionKey: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      - $ref: '#/components/parameters/AcceptLanguageHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthStartRequest'
            example:
              identifier: alice.example@example.com
              identifierType: EMAIL
      responses:
        '200':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthStartResponse'
              example:
                loginAttemptId: LA-01HX9F2J7K3M5N7P9Q1R3T5V7W
                loginAttemptExpiresAt: 2026-05-11 14:10:12+00:00
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/login:
    post:
      operationId: authLogin
      tags:
      - Authentication
      summary: Complete customer login (step 2 of 2; also MFA challenge / enrolment
        continuation).
      security:
      - apimSubscriptionKey: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      - $ref: '#/components/parameters/AcceptLanguageHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthLoginRequest'
            examples:
              initialCredential:
                summary: Initial password submission after /auth/start
                value:
                  loginAttemptId: LA-01HX9F2J7K3M5N7P9Q1R3T5V7W
                  password: AliceStr0ngP@ssw0rd!
              mfaChallengeCompletion:
                summary: MFA challenge completion (after MFA_CHALLENGE_REQUIRED)
                value:
                  loginAttemptId: LA-01HX9F2J7K3M5N7P9Q1R3T5V7W
                  mfaChallengeId: MFC-01HX9J5Q1R7S9T1U3V5W7X9Y1Z
                  mfaCode: '847291'
                  mfaMethod: TOTP
              mfaEnrolmentCommit:
                summary: MFA enrolment commit (after MFA_ENROLMENT_REQUIRED)
                value:
                  loginAttemptId: LA-01HX9F2J7K3M5N7P9Q1R3T5V7W
                  mfaEnrolmentSessionId: MFE-01HX9K7S2T9U1V3W5X7Y9Z1A3B
                  mfaEnrolmentMethod: TOTP
                  mfaEnrolmentCode: '319847'
      responses:
        '200':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthLoginResponse'
              examples:
                authenticated:
                  summary: AUTHENTICATED — token bundle issued
                  value:
                    authStatus: AUTHENTICATED
                    accessToken: eyJraWQiOiJrZXktZXUtMjAyNi0wMSIsImFsZyI6IlJTMjU2In0.<claims>.<sig>
                    accessTokenExpiresAt: 2026-05-12 15:03:15+00:00
                    refreshToken: eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiZGlyIn0.<refresh>
                    refreshTokenExpiresAt: 2026-05-22 14:03:15+00:00
                    idToken: eyJraWQiOiJrZXktZXUtMjAyNi0wMSIsImFsZyI6IlJTMjU2In0.<id>.<sig>
                    idTokenExpiresAt: 2026-05-12 15:03:15+00:00
                    tokenType: Bearer
                    expiresIn: 3600
                    subscriptionId: SUB-01HX9F2J7K3M5N7P9Q1R3T5V7W
                    customerStatus: ACTIVE
                mfaChallengeRequired:
                  summary: MFA_CHALLENGE_REQUIRED — risk signal fired; MFA challenge
                    issued
                  value:
                    authStatus: MFA_CHALLENGE_REQUIRED
                    mfaChallengeId: MFC-01HX9J5Q1R7S9T1U3V5W7X9Y1Z
                    mfaMethod: TOTP
                    mfaChallengeExpiresAt: 2026-05-12 07:55:00+00:00
                    supportedMethods:
                    - TOTP
                mfaEnrolmentRequired:
                  summary: MFA_ENROLMENT_REQUIRED — customer must enrol an MFA factor
                  value:
                    authStatus: MFA_ENROLMENT_REQUIRED
                    mfaEnrolmentSessionId: MFE-01HX9K7S2T9U1V3W5X7Y9Z1A3B
                    supportedMethods:
                    - method: TOTP
                      recommended: true
                      rationale: Authenticator app — recommended for higher security
                        against SIM-swap attacks
                    - method: SMS_MFA
                      recommended: false
                      rationale: SMS to phone identifier — accessible fallback; lower
                        assurance against SIM-swap
                    mfaEnrolmentSessionExpiresAt: 2026-05-12 07:55:00+00:00
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/refresh:
    post:
      operationId: authRefresh
      tags:
      - Authentication
      summary: Refresh the access token.
      security:
      - apimSubscriptionKey: []
        bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthRefreshRequest'
            example:
              refreshToken: eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiZGlyIn0...
      responses:
        '200':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthRefreshResponse'
              example:
                accessToken: eyJraWQiOiJrZXktZXUtMjAyNi0wMSIsImFsZyI6IlJTMjU2In0.<claims>.<sig>
                accessTokenExpiresAt: 2026-05-13 10:58:31+00:00
                refreshToken: eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiZGlyIn0.<rotated>
                idToken: eyJraWQiOiJrZXktZXUtMjAyNi0wMSIsImFsZyI6IlJTMjU2In0.<id>.<sig>
                idTokenExpiresAt: 2026-05-13 10:58:31+00:00
                tokenType: Bearer
                expiresIn: 3600
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/logout:
    post:
      operationId: authLogout
      tags:
      - Authentication
      summary: Revoke the current customer session.
      security:
      - apimSubscriptionKey: []
        bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthLogoutRequest'
            example:
              refreshToken: eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiZGlyIn0...
      responses:
        '204':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/register/start:
    post:
      operationId: registerStart
      tags:
      - Registration
      summary: Initiate customer-direct registration (step 1 of 3).
      security:
      - apimSubscriptionKey: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      - $ref: '#/components/parameters/AcceptLanguageHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterStartRequest'
            example:
              identifier: 34612345678
              identifierType: PHONE
              partnerCustomerRef: PARTNER-ALICE-001
      responses:
        '200':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RegisterStartResponse'
              example:
                registrationId: RG-01HX9F2J7K3M5N7P9Q1R3T5V7W
                next: OTP
                registrationIdExpiresAt: 2026-05-12 14:30:01+00:00
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/register/verify-otp:
    post:
      operationId: registerVerifyOtp
      tags:
      - Registration
      summary: Verify registration OTP (step 2 of 3).
      security:
      - apimSubscriptionKey: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      - $ref: '#/components/parameters/AcceptLanguageHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterVerifyOtpRequest'
            example:
              registrationId: RG-01HX9F2J7K3M5N7P9Q1R3T5V7W
              otp: '849213'
      responses:
        '200':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RegisterVerifyOtpResponse'
              examples:
                newCustomer:
                  summary: NEW_CUSTOMER branch — customer will create credentials
                  value:
                    registrationId: RG-01HX9F2J7K3M5N7P9Q1R3T5V7W
                    branch: NEW_CUSTOMER
                    next: SET_PASSWORD
                existingCustomer:
                  summary: EXISTING_CUSTOMER branch — customer already has credentials
                  value:
                    registrationId: RG-01HX9F2J7K3M5N7P9Q1R3T5V7W
                    branch: EXISTING_CUSTOMER
                    next: VERIFY_PASSWORD
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/register/set-password:
    post:
      operationId: registerSetPassword
      tags:
      - Registration
      summary: Complete registration by setting / verifying password (step 3 of 3).
      security:
      - apimSubscriptionKey: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      - $ref: '#/components/parameters/XIdempotencyKeyHeaderOptional'
      - $ref: '#/components/parameters/AcceptLanguageHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterSetPasswordRequest'
            example:
              registrationId: RG-01HX9F2J7K3M5N7P9Q1R3T5V7W
              password: AliceStr0ngP@ssw0rd!
      responses:
        '200':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RegisterSetPasswordResponse'
              example:
                accessToken: eyJraWQiOiJrZXktZXUtMjAyNi0wMSIsImFsZyI6IlJTMjU2In0.<claims>.<sig>
                accessTokenExpiresAt: 2026-05-12 15:03:15+00:00
                refreshToken: eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiZGlyIn0.<refresh>
                refreshTokenExpiresAt: 2026-05-22 14:03:15+00:00
                idToken: eyJraWQiOiJrZXktZXUtMjAyNi0wMSIsImFsZyI6IlJTMjU2In0.<id>.<sig>
                idTokenExpiresAt: 2026-05-12 15:03:15+00:00
                tokenType: Bearer
                expiresIn: 3600
                subscriptionId: SUB-01HX9F2J7K3M5N7P9Q1R3T5V7W
                customerStatus: PENDING
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/identifiers:
    get:
      operationId: identifierList
      tags:
      - Identifiers
      summary: List the authenticated customer's active identifiers.
      security:
      - apimSubscriptionKey: []
        bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      responses:
        '200':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IdentifierListResponse'
              example:
                identifiers:
                - identifierId: ID-01HX9F2J7K3M5N7P9Q1R3T5V7W
                  identifierType: EMAIL
                  identifierMasked: a****@example.com
                  verifiedAt: 2025-11-08 14:22:43+00:00
                  canRemove: true
                - identifierId: ID-01HX9F2J7K3M5N7P9Q1R3T5V7X
                  identifierType: PHONE
                  identifierMasked: +34******5678
                  verifiedAt: 2026-02-15 09:11:02+00:00
                  canRemove: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/identifiers/add/start:
    post:
      operationId: identifierAddStart
      tags:
      - Identifiers
      summary: Initiate identifier-add (step 1 of 2).
      security:
      - apimSubscriptionKey: []
        bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      - $ref: '#/components/parameters/AcceptLanguageHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IdentifierAddStartRequest'
            example:
              identifier: newaddress@example.com
              identifierType: EMAIL
      responses:
        '200':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IdentifierAddStartResponse'
              example:
                identifierAddId: IA-01HX9F2J7K3M5N7P9Q1R3T5V7W
                next: OTP
                identifierAddIdExpiresAt: 2026-05-11 14:35:12+00:00
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/identifiers/add/verify-otp:
    post:
      operationId: identifierAddVerifyOtp
      tags:
      - Identifiers
      summary: Complete identifier-add (step 2 of 2).
      security:
      - apimSubscriptionKey: []
        bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IdentifierAddVerifyOtpRequest'
            example:
              identifierAddId: IA-01HX9F2J7K3M5N7P9Q1R3T5V7W
              otp: '123456'
      responses:
        '200':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IdentifierAddVerifyOtpResponse'
              example:
                identifierId: ID-01HX9F2J7K3M5N7P9Q1R3T5V7Y
                identifierType: EMAIL
                identifierMasked: n****@example.com
                verifiedAt: 2026-05-11 14:32:18+00:00
                canRemove: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/identifiers/{identifierId}/remove/start:
    post:
      operationId: identifierRemoveStart
      tags:
      - Identifiers
      summary: Initiate identifier-remove (step 1 of 2).
      security:
      - apimSubscriptionKey: []
        bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      - $ref: '#/components/parameters/AcceptLanguageHeader'
      - name: identifierId
        in: path
        required: true
        schema:
          type: string
        example: ID-01HX9F2J7K3M5N7P9Q1R3T5V7W
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IdentifierRemoveStartRequest'
            example:
              otpDeliveryIdentifierId: ID-01HX9F2J7K3M5N7P9Q1R3T5V7X
      responses:
        '200':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IdentifierRemoveStartResponse'
              example:
                identifierRemoveId: IR-01HX9F2J7K3M5N7P9Q1R3T5V7W
                next: OTP
                otpDeliveryIdentifierMasked: +34******5678
                identifierRemoveIdExpiresAt: 2026-05-11 14:35:12+00:00
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/identifiers/{identifierId}/remove/verify-otp:
    post:
      operationId: identifierRemoveVerifyOtp
      tags:
      - Identifiers
      summary: Complete identifier-remove (step 2 of 2).
      security:
      - apimSubscriptionKey: []
        bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      - name: identifierId
        in: path
        required: true
        schema:
          type: string
        example: ID-01HX9F2J7K3M5N7P9Q1R3T5V7W
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IdentifierRemoveVerifyOtpRequest'
            example:
              identifierRemoveId: IR-01HX9F2J7K3M5N7P9Q1R3T5V7W
              otp: '123456'
      responses:
        '200':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IdentifierRemoveVerifyOtpResponse'
              example:
                identifierId: ID-01HX9F2J7K3M5N7P9Q1R3T5V7W
                deactivatedAt: 2026-05-11 14:32:18+00:00
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/password/forgot:
    post:
      operationId: passwordForgot
      tags:
      - Password Reset
      summary: Initiate password reset (step 1 of 2).
      security:
      - apimSubscriptionKey: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      - $ref: '#/components/parameters/AcceptLanguageHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PasswordForgotRequest'
            examples:
              standalone:
                summary: Standalone — customer used the "Forgot password?" affordance
                value:
                  identifier: j.smith@example.com
                  identifierType: EMAIL
              carryForward:
                summary: Carry-forward — from credential-mismatch at /register/set-password
                value:
                  identifier: j.smith@example.com
                  identifierType: EMAIL
                  registrationId: RG-01HX9F2J7K3M5N7P9Q1R3T5V7W
      responses:
        '200':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PasswordForgotResponse'
              example:
                passwordResetId: PR-01HX9F2J7K3M5N7P9Q1R3T5V7W
                next: OTP
                passwordResetIdExpiresAt: 2026-05-11 14:35:12+00:00
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/password/reset:
    post:
      operationId: passwordReset
      tags:
      - Password Reset
      summary: Complete password reset (step 2 of 2).
      security:
      - apimSubscriptionKey: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PasswordResetRequest'
            example:
              passwordResetId: PR-01HX9F2J7K3M5N7P9Q1R3T5V7W
              otp: '123456'
              newPassword: NewStr0ngP@ssw0rd!
      responses:
        '200':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PasswordResetResponse'
              examples:
                standalone:
                  summary: Standalone path — customer-app routes to login
                  value:
                    next: LOGIN
                carryForward:
                  summary: Carry-forward path — customer-app re-presents at /register/set-password
                  value:
                    next: REGISTER_COMPLETE
                    registrationId: RG-01HX9F2J7K3M5N7P9Q1R3T5V7W
                    registrationIdExpiresAt: 2026-05-11 15:02:18+00:00
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/device-registration/start:
    post:
      operationId: deviceRegistrationStart
      tags:
      - Device-Bound Auth
      summary: Initiate device-registration ceremony (step 1 of 2).
      security:
      - apimSubscriptionKey: []
        bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      - $ref: '#/components/parameters/XIdempotencyKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeviceRegistrationStartRequest'
            examples:
              firstRegistration:
                summary: Onboarding-time first registration (no step-up token)
                value:
                  deviceMetadata:
                    deviceModel: iPhone15,3
                    osVersion: iOS 18.2
                    appVersion: 1.4.2
                    platform: iOS
              reRegistrationRetry:
                summary: Re-registration retry (with step-up token after liveness)
                value:
                  stepUpToken: <base64url single-use token from liveness webview closure>
                  deviceMetadata:
                    deviceModel: iPhone16,1
                    osVersion: iOS 18.4
                    appVersion: 1.4.2
                    platform: iOS
      responses:
        '201':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeviceRegistrationStartHappyPath'
              example:
                deviceRegistrationId: DRG-01HX9F2J7K3M5N7P9Q1R3T5V7W
                registrationChallenge: Y2hhbGxlbmdlLWJhc2U2NHVybC1lbmNvZGVkLTMyLWJ5dGVz
                expiresAt: 2026-05-11 15:13:22+00:00
                publicKeyRequirements:
                  algorithm: RS256
                  minimumModulusBits: 2048
                  recommendedModulusBits: 3072
                stepUpApplied: false
        '202':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeviceRegistrationStartStepUpRequired'
              example:
                stepUpRequired: LIVENESS_CHECK
                kycSessionId: KYC-01HX9F2J7K3M5N7P9Q1R3T5V7W
                livenessWebviewUrl: https://kyc.eu.yescash.example/sessions/KYC-01HX9F2J7K3M5N7P9Q1R3T5V7W
                expiresAt: 2026-05-11 15:23:22+00:00
                nextStep: OPEN_LIVENESS_WEBVIEW
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '410':
          $ref: '#/components/responses/Gone'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/auth/device-registration/complete:
    post:
      operationId: deviceRegistrationComplete
      tags:
      - Device-Bound Auth
      summary: Complete device-registration with proof-of-possession (step 2 of 2).
      security:
      - apimSubscriptionKey: []
        bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/XCorrelationIdHeader'
      - $ref: '#/components/parameters/XIdempotencyKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeviceRegistrationCompleteRequest'
            example:
              deviceRegistrationId: DRG-01HX9F2J7K3M5N7P9Q1R3T5V7W
              publicKey:
                kty: RSA
                n: 0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw
                e: AQAB
                kid: device-key-2026-05-11T14-58Z
                alg: RS256
                use: sig
              registrationProof: eyJhbGciOiJSUzI1NiIsImtpZCI6ImRldmljZS1rZXktMjAyNi0wNS0xMVQxNC01OFoiLCJ0eXAiOiJKV1QifQ.eyJkZXZpY2VfcmVnaXN0cmF0aW9uX2lkIjoiRFJHLTAxSFg5RjJKN0szTTVON1A5UTFSM1Q1VjdXIiwiaWF0IjoxNzE1NjA4ODIyLCJwdWJsaWNfa2V5X3RodW1icHJpbnQiOiI4bUFoUWpKeVptSlZaa1JEZVhCS1ZucFFVRmhNUTNKSlZuRktVelJTZFRrIiwicmVnaXN0cmF0aW9uX2NoYWxsZW5nZSI6IlkyaGhiR3hsYm1kbExXSmhjMlUyTkhWeWJDMWxibU52WkdWa0xUTXlMV0o1ZEdWeiIsInJlZ2lzdHJhdGlvbl9wcm9vZl92MSI6InYxIn0.<signature_bytes>
      responses:
        '201':
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/XCorrelationIdResponseHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeviceRegistrationCompleteResponse'
              example:
                deviceId: DEV-01HX9F2J7K3M5N7P9Q1R3T5V7W
                registeredAt: 2026-05-11 14:58:22+00:00
                publicKeyThumbprint: 8mAhQjJyZmJVZkRDeXBKVnpQUFhMQ3JJVnFKUzRSdTk
                stepUpApplied: false
                priorDeviceIdDeactivated: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
