---
openapi: '3.0.3'
info:
  title: 'IDkollen REST API'
  version: 'v3'
  license:
    name: 'Proprietary'
  description: |
    eID services for Swedish BankID, Freja eID, Norwegian BankID, Vipps MobilePay,
    Danish MitID, and Finnish Trust Network (FTN).

    All endpoints require HTTP Basic authentication with API customer credentials.

    ## Polling
    Start operations return `201` with an `id`. Poll the corresponding `GET /{id}`
    endpoint until `status` is `COMPLETED` or `FAILED`. The collect endpoint is
    rate-limited to 1 request/second; exceeding it returns `429`.

servers:
  - url: 'https://api.idkollen.se'
    description: 'Production'
  - url: 'https://stgapi.idkollen.se'
    description: 'Staging'

security:
  - basicAuth: []

tags:
  - name: 'BankID SE'
    description: 'Swedish BankID authentication and signing'
  - name: 'Freja'
    description: 'Freja eID authentication and signing'
  - name: 'BankID NO'
    description: 'Norwegian BankID authentication and signing'
  - name: 'Vipps MobilePay'
    description: 'Vipps MobilePay authentication'
  - name: 'MitID'
    description: 'Danish MitID authentication and signing'
  - name: 'FTN'
    description: 'Finnish Trust Network authentication'
  - name: 'Documents'
    description: '(BETA) Document upload and retrieval for PDF signing'

paths:
  /v3/bankid-se/auth:
    post:
      tags: ['BankID SE']
      summary: 'Start a BankID SE authentication'
      operationId: 'startBankIdSeAuth'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BankIdSeAuthRequest'
      responses:
        '201':
          description: 'Authentication started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankIdSeStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/bankid-se/phone/auth:
    post:
      tags: ['BankID SE']
      summary: 'Start a BankID SE phone authentication'
      operationId: 'startBankIdSePhoneAuth'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BankIdSePhoneAuthRequest'
      responses:
        '201':
          description: 'Phone authentication started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankIdSeStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/bankid-se/auth/{id}:
    get:
      tags: ['BankID SE']
      summary: 'Get BankID SE authentication status'
      operationId: 'getBankIdSeAuthStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current authentication status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankIdSeStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['BankID SE']
      summary: 'Cancel a BankID SE authentication'
      operationId: 'cancelBankIdSeAuth'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Authentication cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/bankid-se/verify:
    post:
      tags: ['BankID SE']
      summary: 'Verify a scanned BankID SE QR code'
      operationId: 'bankIdSeVerify'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BankIdSeVerifyRequest'
      responses:
        '200':
          description: 'QR code verified'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankIdSeVerifyResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/bankid-se/sign:
    post:
      tags: ['BankID SE']
      summary: 'Start a BankID SE signing'
      operationId: 'startBankIdSeSign'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BankIdSeSignRequest'
      responses:
        '201':
          description: 'Signing started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankIdSeStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/bankid-se/phone/sign:
    post:
      tags: ['BankID SE']
      summary: 'Start a BankID SE phone signing'
      operationId: 'startBankIdSePhoneSign'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BankIdSePhoneSignRequest'
      responses:
        '201':
          description: 'Phone signing started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankIdSeStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/bankid-se/sign/{id}:
    get:
      tags: ['BankID SE']
      summary: 'Get BankID SE signing status'
      operationId: 'getBankIdSeSignStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current signing status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankIdSeStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['BankID SE']
      summary: 'Cancel a BankID SE signing'
      operationId: 'cancelBankIdSeSign'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Signing cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/bankid-se/age-verification:
    post:
      tags: ['BankID SE']
      summary: 'Start a BankID SE age verification'
      operationId: 'startBankIdSeAgeVerification'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgeVerificationRequest'
      responses:
        '201':
          description: 'Age verification started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgeVerificationStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/bankid-se/age-verification/{id}:
    get:
      tags: ['BankID SE']
      summary: 'Get BankID SE age verification status'
      operationId: 'getBankIdSeAgeVerificationStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current age verification status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgeVerificationStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['BankID SE']
      summary: 'Cancel a BankID SE age verification'
      operationId: 'cancelBankIdSeAgeVerification'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Age verification cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/freja/backchannel/auth:
    post:
      tags: ['Freja']
      summary: 'Start a Freja eID backchannel authentication'
      operationId: 'startFrejaBackchannelAuth'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FrejaBackchannelAuthRequest'
      responses:
        '201':
          description: 'Backchannel authentication started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FrejaStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/freja/auth/{id}:
    get:
      tags: ['Freja']
      summary: 'Get Freja eID authentication status'
      operationId: 'getFrejaAuthStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current authentication status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FrejaStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['Freja']
      summary: 'Cancel a Freja eID authentication'
      operationId: 'cancelFrejaAuth'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Authentication cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/freja/backchannel/sign:
    post:
      tags: ['Freja']
      summary: 'Start a Freja eID backchannel signing'
      operationId: 'startFrejaBackchannelSign'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FrejaBackchannelSignRequest'
      responses:
        '201':
          description: 'Backchannel signing started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FrejaStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/freja/sign/{id}:
    get:
      tags: ['Freja']
      summary: 'Get Freja eID signing status'
      operationId: 'getFrejaSignStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current signing status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FrejaStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['Freja']
      summary: 'Cancel a Freja eID signing'
      operationId: 'cancelFrejaSign'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Signing cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/freja/age-verification:
    post:
      tags: ['Freja']
      summary: 'Start a Freja eID age verification'
      operationId: 'startFrejaAgeVerification'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgeVerificationRequest'
      responses:
        '201':
          description: 'Age verification started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgeVerificationStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/freja/age-verification/{id}:
    get:
      tags: ['Freja']
      summary: 'Get Freja eID age verification status'
      operationId: 'getFrejaAgeVerificationStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current age verification status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgeVerificationStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['Freja']
      summary: 'Cancel a Freja eID age verification'
      operationId: 'cancelFrejaAgeVerification'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Age verification cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/bankid-no/auth:
    post:
      tags: ['BankID NO']
      summary: 'Start a Norwegian BankID authentication'
      operationId: 'startBankIdNoAuth'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BankIdNoAuthRequest'
      responses:
        '201':
          description: 'Authentication started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankIdNoStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/bankid-no/backchannel/auth:
    post:
      tags: ['BankID NO']
      summary: 'Start a Norwegian BankID backchannel authentication'
      operationId: 'startBankIdNoBackchannelAuth'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BankIdNoBackchannelAuthRequest'
      responses:
        '201':
          description: 'Backchannel authentication started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankIdNoStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/bankid-no/auth/{id}:
    get:
      tags: ['BankID NO']
      summary: 'Get Norwegian BankID authentication status'
      operationId: 'getBankIdNoAuthStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current authentication status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankIdNoStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['BankID NO']
      summary: 'Cancel a Norwegian BankID authentication'
      operationId: 'cancelBankIdNoAuth'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Authentication cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/bankid-no/sign:
    post:
      tags: ['BankID NO']
      summary: 'Start a Norwegian BankID signing'
      description: |
        Provide either `text` (plain text signing) or `documents` (PDF signing), not both.
        `documents` requires the document upload feature to be enabled.
      operationId: 'startBankIdNoSign'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BankIdNoSignRequest'
      responses:
        '201':
          description: 'Signing started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankIdNoStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/bankid-no/sign/{id}:
    get:
      tags: ['BankID NO']
      summary: 'Get Norwegian BankID signing status'
      operationId: 'getBankIdNoSignStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current signing status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankIdNoStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['BankID NO']
      summary: 'Cancel a Norwegian BankID signing'
      operationId: 'cancelBankIdNoSign'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Signing cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/bankid-no/age-verification:
    post:
      tags: ['BankID NO']
      summary: 'Start a Norwegian BankID age verification'
      operationId: 'startBankIdNoAgeVerification'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgeVerificationRequest'
      responses:
        '201':
          description: 'Age verification started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgeVerificationStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/bankid-no/age-verification/{id}:
    get:
      tags: ['BankID NO']
      summary: 'Get Norwegian BankID age verification status'
      operationId: 'getBankIdNoAgeVerificationStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current age verification status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgeVerificationStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['BankID NO']
      summary: 'Cancel a Norwegian BankID age verification'
      operationId: 'cancelBankIdNoAgeVerification'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Age verification cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/vipps/auth:
    post:
      tags: ['Vipps MobilePay']
      summary: 'Start a Vipps authentication'
      operationId: 'startVippsAuth'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VippsAuthRequest'
      responses:
        '201':
          description: 'Authentication started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VippsStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/vipps/backchannel/auth:
    post:
      tags: ['Vipps MobilePay']
      summary: 'Start a Vipps backchannel authentication'
      operationId: 'startVippsBackchannelAuth'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VippsBackchannelAuthRequest'
      responses:
        '201':
          description: 'Backchannel authentication started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VippsStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/vipps/auth/{id}:
    get:
      tags: ['Vipps MobilePay']
      summary: 'Get Vipps authentication status'
      operationId: 'getVippsAuthStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current authentication status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VippsStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['Vipps MobilePay']
      summary: 'Cancel a Vipps authentication'
      operationId: 'cancelVippsAuth'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Authentication cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/mitid/auth:
    post:
      tags: ['MitID']
      summary: 'Start a MitID authentication'
      operationId: 'startMitIdAuth'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MitIdAuthRequest'
      responses:
        '201':
          description: 'Authentication started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MitIdStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/mitid/backchannel/auth:
    post:
      tags: ['MitID']
      summary: 'Start a MitID backchannel authentication'
      operationId: 'startMitIdBackchannelAuth'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MitIdBackchannelAuthRequest'
      responses:
        '201':
          description: 'Backchannel authentication started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MitIdStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/mitid/auth/{id}:
    get:
      tags: ['MitID']
      summary: 'Get MitID authentication status'
      operationId: 'getMitIdAuthStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current authentication status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MitIdStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['MitID']
      summary: 'Cancel a MitID authentication'
      operationId: 'cancelMitIdAuth'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Authentication cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/mitid/sign:
    post:
      tags: ['MitID']
      summary: 'Start a MitID signing'
      operationId: 'startMitIdSign'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MitIdSignRequest'
      responses:
        '201':
          description: 'Signing started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MitIdStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/mitid/sign/{id}:
    get:
      tags: ['MitID']
      summary: 'Get MitID signing status'
      operationId: 'getMitIdSignStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current signing status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MitIdStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['MitID']
      summary: 'Cancel a MitID signing'
      operationId: 'cancelMitIdSign'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Signing cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/mitid/age-verification:
    post:
      tags: ['MitID']
      summary: 'Start a MitID age verification'
      operationId: 'startMitIdAgeVerification'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgeVerificationRequest'
      responses:
        '201':
          description: 'Age verification started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgeVerificationStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/mitid/age-verification/{id}:
    get:
      tags: ['MitID']
      summary: 'Get MitID age verification status'
      operationId: 'getMitIdAgeVerificationStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current age verification status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgeVerificationStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['MitID']
      summary: 'Cancel a MitID age verification'
      operationId: 'cancelMitIdAgeVerification'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Age verification cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/ftn/auth:
    post:
      tags: ['FTN']
      summary: 'Start an FTN authentication'
      operationId: 'startFtnAuth'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FtnAuthRequest'
      responses:
        '201':
          description: 'Authentication started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FtnStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/ftn/auth/{id}:
    get:
      tags: ['FTN']
      summary: 'Get FTN authentication status'
      operationId: 'getFtnAuthStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current authentication status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FtnStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['FTN']
      summary: 'Cancel an FTN authentication'
      operationId: 'cancelFtnAuth'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Authentication cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/ftn/age-verification:
    post:
      tags: ['FTN']
      summary: 'Start an FTN age verification'
      operationId: 'startFtnAgeVerification'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgeVerificationRequest'
      responses:
        '201':
          description: 'Age verification started'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgeVerificationStatusResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/ftn/age-verification/{id}:
    get:
      tags: ['FTN']
      summary: 'Get FTN age verification status'
      operationId: 'getFtnAgeVerificationStatus'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: 'Current age verification status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgeVerificationStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags: ['FTN']
      summary: 'Cancel an FTN age verification'
      operationId: 'cancelFtnAgeVerification'
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: 'Age verification cancelled'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v3/document:
    post:
      tags: ['Documents']
      summary: '(BETA) Upload a document for signing'
      description: >-
        Accepts a single PDF file. Returns a document `id` to be referenced
        in signing requests.
      operationId: 'uploadDocument'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: 'object'
              properties:
                file:
                  type: 'string'
                  format: 'binary'
      responses:
        '201':
          description: 'Document uploaded'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentUploadResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v3/document/{documentId}:
    get:
      tags: ['Documents']
      summary: '(BETA) Download a signed PDF document'
      operationId: 'getSignedDocument'
      parameters:
        - name: 'documentId'
          in: 'path'
          required: true
          schema:
            type: 'string'
      responses:
        '200':
          description: 'Signed PDF document'
          content:
            application/pdf:
              schema:
                type: 'string'
                format: 'binary'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

components:
  securitySchemes:
    basicAuth:
      type: 'http'
      scheme: 'basic'

  parameters:
    id:
      name: 'id'
      in: 'path'
      required: true
      schema:
        type: 'string'
        minLength: 1

  responses:
    BadRequest:
      description: 'Feature not enabled, invalid parameters, or provider error'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiErrorResponse'
    Unauthorized:
      description: 'Authentication credentials missing or invalid'
    NotFound:
      description: 'Session ID not found or expired'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiErrorResponse'
    TooManyRequests:
      description: 'Polling rate limit exceeded'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiErrorResponse'

  schemas:
    ApiErrorResponse:
      type: 'object'
      properties:
        message:
          type: 'string'

    ApiErrorCode:
      type: 'string'
      description: |
        Standardised error code present in all `FAILED` status responses.
        - `AUTH_FAILED` — authentication was rejected
        - `CANCELLED` — user or RP cancelled the transaction
        - `INVALID_ID` — certificate or identity document is invalid/expired
        - `CONFLICT` — a conflicting transaction is already in progress
        - `INTERNAL_ERROR` — unexpected error
        - `SESSION_TIMEOUT` — the session expired before completion
        - `UNSUPPORTED_CLIENT` — the user's device or app version is unsupported
      enum:
        - 'AUTH_FAILED'
        - 'CANCELLED'
        - 'INVALID_ID'
        - 'CONFLICT'
        - 'INTERNAL_ERROR'
        - 'SESSION_TIMEOUT'
        - 'UNSUPPORTED_CLIENT'

    Language:
      type: 'string'
      enum: ['ENGLISH', 'SWEDISH', 'NORWEGIAN', 'DANISH', 'FINNISH']

    CallInitiator:
      type: 'string'
      description: 'Whether the user or the relying party (RP) initiated the phone call.'
      enum: ['USER', 'RP']

    BankIdSeAuthRequest:
      type: 'object'
      properties:
        ssn:
          type: 'string'
          minLength: 1
          nullable: true
          description: >-
            Swedish personal identification number. Restricts the session
            to this user.
        ipAddress:
          type: 'string'
          description: 'End-user IP address (or the closest proxy address).'
        callbackUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to receive the result callback on success or failure.'
        pinRequired:
          type: 'boolean'
          default: false
          description: 'Force PIN entry even when biometrics are enabled.'
        intent:
          type: 'string'
          minLength: 1
          maxLength: 2000
          description: >-
            Text describing the purpose of the identification shown to the user.
        orgNumber:
          type: 'string'
          description: 'Swedish organisation number — enables company signatory check.'
        requestAddress:
          type: 'boolean'
          description: 'Fetch the user''s registered address on completion.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'

    BankIdSePhoneAuthRequest:
      type: 'object'
      required: ['ssn', 'callInitiator']
      properties:
        ssn:
          type: 'string'
          minLength: 1
          description: 'Swedish personal identification number of the user to authenticate.'
        callbackUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to receive the result callback on success or failure.'
        pinRequired:
          type: 'boolean'
          default: false
          description: 'Force PIN entry even when biometrics are enabled.'
        intent:
          type: 'string'
          minLength: 1
          maxLength: 2000
          description: >-
            Text describing the purpose of the identification shown to the user.
        orgNumber:
          type: 'string'
          description: 'Swedish organisation number — enables company signatory check.'
        requestAddress:
          type: 'boolean'
          description: 'Fetch the user''s registered address on completion.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'
        callInitiator:
          $ref: '#/components/schemas/CallInitiator'

    BankIdSeSignRequest:
      type: 'object'
      required: ['text']
      properties:
        ssn:
          type: 'string'
          minLength: 1
          description: 'Restrict the signing session to this Swedish personal number.'
        callbackUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to receive the result callback on success or failure.'
        text:
          type: 'string'
          maxLength: 50000
          description: 'Visible text the user must approve in BankID.'
        digest:
          type: 'string'
          minLength: 1
          maxLength: 2000
          description: 'Hash digest of an associated file.'
        ipAddress:
          type: 'string'
          description: 'End-user IP address (or the closest proxy address).'
        pinRequired:
          type: 'boolean'
          default: false
          description: 'Force PIN entry even when biometrics are enabled.'
        orgNumber:
          type: 'string'
          description: 'Swedish organisation number — enables company signatory check.'
        requestAddress:
          type: 'boolean'
          description: 'Fetch the user''s registered address on completion.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'

    BankIdSePhoneSignRequest:
      type: 'object'
      required: ['ssn', 'text', 'callInitiator']
      properties:
        ssn:
          type: 'string'
          minLength: 1
          description: 'Swedish personal identification number of the user to sign.'
        callbackUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to receive the result callback on success or failure.'
        text:
          type: 'string'
          maxLength: 50000
          description: 'Visible text the user must approve in BankID.'
        digest:
          type: 'string'
          minLength: 1
          maxLength: 2000
          description: 'Hash digest of an associated file.'
        pinRequired:
          type: 'boolean'
          default: false
          description: 'Force PIN entry even when biometrics are enabled.'
        orgNumber:
          type: 'string'
          description: 'Swedish organisation number — enables company signatory check.'
        requestAddress:
          type: 'boolean'
          description: 'Fetch the user''s registered address on completion.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'
        callInitiator:
          $ref: '#/components/schemas/CallInitiator'

    BankIdSeVerifyRequest:
      type: 'object'
      required: ['qrCode']
      properties:
        qrCode:
          type: 'string'
          minLength: 1
          description: 'Complete content of the scanned BankID QR code.'

    AgeVerificationRequest:
      type: 'object'
      description: >-
        At least one of `minAge` or `maxAge` must be provided.
        If both are given, `maxAge` must be >= `minAge`.
      properties:
        minAge:
          type: 'integer'
          minimum: 1
          description: 'Minimum age (inclusive).'
        maxAge:
          type: 'integer'
          minimum: 1
          description: 'Maximum age (inclusive).'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'
        callbackUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to receive the result callback on success or failure.'
        redirectUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to redirect the user to after completing age verification.'

    FrejaBackchannelAuthRequest:
      type: 'object'
      required: ['ssn', 'country']
      properties:
        ssn:
          type: 'string'
          minLength: 1
          description: 'Personal number of the user to authenticate.'
        country:
          type: 'string'
          enum: ['SWEDEN', 'NORWAY', 'DENMARK', 'FINLAND']
          description: 'Country of the user''s Freja identity document.'
        callbackUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to receive the result callback on success or failure.'
        minRegistrationLevel:
          type: 'string'
          enum: ['EXTENDED', 'PLUS']
          default: 'EXTENDED'
          description: 'Minimum required Freja registration level.'
        orgNumber:
          type: 'string'
          description: 'Organisation number — enables company signatory check.'
        requestAddress:
          type: 'boolean'
          description: 'Fetch the user''s registered address on completion.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'

    FrejaBackchannelSignRequest:
      type: 'object'
      required: ['ssn', 'country', 'text']
      properties:
        ssn:
          type: 'string'
          minLength: 1
          description: 'Personal number of the user to sign.'
        country:
          type: 'string'
          enum: ['SWEDEN', 'NORWAY', 'DENMARK', 'FINLAND']
          description: 'Country of the user''s Freja identity document.'
        callbackUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to receive the result callback on success or failure.'
        text:
          type: 'string'
          minLength: 1
          description: 'Text to sign, displayed to the user in the Freja app.'
        minRegistrationLevel:
          type: 'string'
          enum: ['EXTENDED', 'PLUS']
          default: 'EXTENDED'
          description: 'Minimum required Freja registration level.'
        orgNumber:
          type: 'string'
          description: 'Organisation number — enables company signatory check.'
        requestAddress:
          type: 'boolean'
          description: 'Fetch the user''s registered address on completion.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'

    BankIdNoAuthRequest:
      type: 'object'
      properties:
        redirectUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to redirect the user to after completing the flow.'
        requestSsn:
          type: 'boolean'
          description: 'Request the user''s Norwegian personal number (fødselsnummer).'
        requestPhone:
          type: 'boolean'
          description: 'Request the user''s phone number.'
        requestEmail:
          type: 'boolean'
          description: 'Request the user''s email address.'
        requestAddress:
          type: 'boolean'
          description: 'Request the user''s registered address.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'
        appCallbackUri:
          type: 'string'
          format: 'uri'
          description: '(BETA) Deep-link URI to return the user to your app after authentication.'

    BankIdNoBackchannelAuthRequest:
      type: 'object'
      required: ['ssn']
      properties:
        ssn:
          type: 'string'
          minLength: 1
          description: 'Norwegian personal number (fødselsnummer).'
        callbackUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to receive the result callback on success or failure.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'

    BankIdNoSignRequest:
      type: 'object'
      properties:
        redirectUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to redirect the user to after completing the flow.'
        text:
          type: 'string'
          maxLength: 118
          description: 'Text to sign. Mutually exclusive with `documents`.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'
        documents:
          type: 'array'
          items:
            type: 'string'
            minLength: 1
          description: >-
            Document IDs to sign (from `/v3/document`).
            Mutually exclusive with `text`.

    VippsAuthRequest:
      type: 'object'
      properties:
        redirectUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to redirect the user to after completing the flow.'
        requestSsn:
          type: 'boolean'
          description: 'Request the user''s Norwegian personal number.'
        requestPhone:
          type: 'boolean'
          description: 'Request the user''s phone number.'
        requestEmail:
          type: 'boolean'
          description: 'Request the user''s email address.'
        requestAddress:
          type: 'boolean'
          description: 'Request the user''s registered address.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'
        appCallbackUri:
          type: 'string'
          format: 'uri'
          description: '(BETA) Deep-link URI to return the user to your app after authentication.'

    VippsBackchannelAuthRequest:
      type: 'object'
      required: ['phone']
      properties:
        phone:
          type: 'string'
          description: 'Phone number of the user to authenticate.'
        requestSsn:
          type: 'boolean'
          description: 'Request the user''s Norwegian personal number.'
        requestEmail:
          type: 'boolean'
          description: 'Request the user''s email address.'
        requestAddress:
          type: 'boolean'
          description: 'Request the user''s registered address.'
        callbackUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to receive the result callback on success or failure.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'

    MitIdAuthRequest:
      type: 'object'
      properties:
        redirectUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to redirect the user to after completing the flow.'
        referenceText:
          type: 'string'
          maxLength: 130
          pattern: '^[^%<]*$'
          description: >-
            Text shown to the user during authentication.
            Must not contain `%` or `<`.
        requestPhone:
          type: 'boolean'
          description: 'Request the user''s phone number.'
        requestEmail:
          type: 'boolean'
          description: 'Request the user''s email address.'
        requestAddress:
          type: 'boolean'
          description: 'Request the user''s registered address.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'

    MitIdBackchannelAuthRequest:
      type: 'object'
      required: ['ssn', 'bindingMessage']
      properties:
        ssn:
          type: 'string'
          minLength: 1
          description: 'Danish CPR number.'
        bindingMessage:
          type: 'string'
          minLength: 1
          description: 'Message displayed in the MitID app to bind the session.'
        callbackUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to receive the result callback on success or failure.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'

    MitIdSignRequest:
      type: 'object'
      required: ['text']
      properties:
        redirectUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to redirect the user to after completing the flow.'
        text:
          type: 'string'
          minLength: 1
          maxLength: 600
          description: 'Text to sign, displayed in MitID.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'

    FtnAuthRequest:
      type: 'object'
      properties:
        redirectUrl:
          type: 'string'
          format: 'uri'
          description: 'URL to redirect the user to after completing the flow.'
        requestPhone:
          type: 'boolean'
          description: 'Request the user''s phone number.'
        requestEmail:
          type: 'boolean'
          description: 'Request the user''s email address.'
        requestAddress:
          type: 'boolean'
          description: 'Request the user''s registered address.'
        refId:
          type: 'string'
          description: 'Reference ID returned verbatim in the result and callback.'

    BankIdSeStatusBase:
      type: 'object'
      required: ['id']
      properties:
        id:
          type: 'string'
          description: 'BankID order reference / session ID.'
        refId:
          type: 'string'

    BankIdSeStatusPending:
      allOf:
        - $ref: '#/components/schemas/BankIdSeStatusBase'
        - type: 'object'
          required: ['status', 'autoStartToken', 'qrStartToken', 'qrStartSecret']
          properties:
            status:
              type: 'string'
              enum: ['PENDING']
            autoStartToken:
              type: 'string'
              description: 'Token for launching the BankID app directly (autostart URL).'
            qrStartToken:
              type: 'string'
              description: 'Static token used to seed the animated QR code.'
            qrStartSecret:
              type: 'string'
              description: 'Secret used together with `qrStartToken` to generate QR frames.'
            hintCode:
              type: 'string'
              nullable: true
              description: 'BankID hint code describing the current waiting state.'

    BankIdSeStatusCompleted:
      allOf:
        - $ref: '#/components/schemas/BankIdSeStatusBase'
        - type: 'object'
          required: ['status', 'ssn', 'name', 'givenName', 'surname', 'certStartDate']
          properties:
            status:
              type: 'string'
              enum: ['COMPLETED']
            ssn:
              type: 'string'
              description: 'Swedish personal identification number (personnummer).'
            name:
              type: 'string'
            givenName:
              type: 'string'
            surname:
              type: 'string'
            certStartDate:
              type: 'string'
              format: 'date'
              description: 'Date the BankID certificate became valid (YYYY-MM-DD).'
            address:
              type: 'string'
              description: 'Present only when `requestAddress` was `true`.'
            companySignatoryText:
              type: 'string'
              description: 'Company signatory result text, present only when `orgNumber` was provided.'

    BankIdSeStatusFailed:
      allOf:
        - $ref: '#/components/schemas/BankIdSeStatusBase'
        - type: 'object'
          required: ['status', 'error']
          properties:
            status:
              type: 'string'
              enum: ['FAILED']
            error:
              $ref: '#/components/schemas/ApiErrorCode'

    BankIdSeStatusResponse:
      oneOf:
        - $ref: '#/components/schemas/BankIdSeStatusPending'
        - $ref: '#/components/schemas/BankIdSeStatusCompleted'
        - $ref: '#/components/schemas/BankIdSeStatusFailed'
      discriminator:
        propertyName: 'status'
        mapping:
          PENDING: '#/components/schemas/BankIdSeStatusPending'
          COMPLETED: '#/components/schemas/BankIdSeStatusCompleted'
          FAILED: '#/components/schemas/BankIdSeStatusFailed'

    BankIdSeVerifyResponse:
      type: 'object'
      required: ['ssn', 'name', 'givenName', 'surname', 'age', 'verifiedAt']
      properties:
        ssn:
          type: 'string'
          description: 'Swedish personal identification number.'
        name:
          type: 'string'
        givenName:
          type: 'string'
        surname:
          type: 'string'
        age:
          type: 'integer'
        verifiedAt:
          type: 'string'
          format: 'date'
          description: 'Date the QR code was verified (YYYY-MM-DD, UTC).'

    FrejaStatusBase:
      type: 'object'
      required: ['id']
      properties:
        id:
          type: 'string'
        refId:
          type: 'string'

    FrejaStatusPending:
      allOf:
        - $ref: '#/components/schemas/FrejaStatusBase'
        - type: 'object'
          required: ['status', 'autoStartToken', 'qrData']
          properties:
            status:
              type: 'string'
              enum: ['PENDING']
            autoStartToken:
              type: 'string'
              description: 'Freja transaction reference — also used as the autostart token.'
            qrData:
              type: 'string'
              description: 'Data string to render as the Freja QR code.'

    FrejaStatusCompleted:
      allOf:
        - $ref: '#/components/schemas/FrejaStatusBase'
        - type: 'object'
          required: ['status', 'ssn', 'country', 'name', 'givenName', 'surname']
          properties:
            status:
              type: 'string'
              enum: ['COMPLETED']
            ssn:
              type: 'string'
            country:
              type: 'string'
              description: 'Country enum name of the identity document (e.g. `SWEDEN`).'
            name:
              type: 'string'
            givenName:
              type: 'string'
            surname:
              type: 'string'
            address:
              type: 'string'
              description: 'Present only when `requestAddress` was `true`.'
            companySignatoryText:
              type: 'string'
              description: 'Present only when `orgNumber` was provided.'

    FrejaStatusFailed:
      allOf:
        - $ref: '#/components/schemas/FrejaStatusBase'
        - type: 'object'
          required: ['status', 'error']
          properties:
            status:
              type: 'string'
              enum: ['FAILED']
            error:
              $ref: '#/components/schemas/ApiErrorCode'

    FrejaStatusResponse:
      oneOf:
        - $ref: '#/components/schemas/FrejaStatusPending'
        - $ref: '#/components/schemas/FrejaStatusCompleted'
        - $ref: '#/components/schemas/FrejaStatusFailed'
      discriminator:
        propertyName: 'status'
        mapping:
          PENDING: '#/components/schemas/FrejaStatusPending'
          COMPLETED: '#/components/schemas/FrejaStatusCompleted'
          FAILED: '#/components/schemas/FrejaStatusFailed'

    BankIdNoStatusBase:
      type: 'object'
      required: ['id']
      properties:
        id:
          type: 'string'
        refId:
          type: 'string'

    BankIdNoStatusPending:
      allOf:
        - $ref: '#/components/schemas/BankIdNoStatusBase'
        - type: 'object'
          required: ['status']
          properties:
            status:
              type: 'string'
              enum: ['PENDING']
            url:
              type: 'string'
              format: 'uri'
              description: 'Redirect URL for the browser-flow login page.'
            bindingMessage:
              type: 'string'
              description: 'Present in the backchannel flow — display this to the user.'

    BankIdNoStatusCompleted:
      allOf:
        - $ref: '#/components/schemas/BankIdNoStatusBase'
        - type: 'object'
          required: ['status', 'name', 'givenName', 'surname']
          properties:
            status:
              type: 'string'
              enum: ['COMPLETED']
            ssn:
              type: 'string'
              nullable: true
              description: 'Norwegian personal number. Present when `requestSsn` was `true`.'
            name:
              type: 'string'
            givenName:
              type: 'string'
            surname:
              type: 'string'
            phone:
              type: 'string'
              description: 'Present when `requestPhone` was `true`.'
            email:
              type: 'string'
              description: 'Present when `requestEmail` was `true`.'
            address:
              type: 'string'
              description: 'Present when `requestAddress` was `true`.'
            birthDate:
              type: 'string'
              format: 'date'
              nullable: true
            pid:
              type: 'string'
              description: 'BankID PID.'
            bankId:
              type: 'string'
            signResult:
              $ref: '#/components/schemas/BankIdNoSignResult'
            signedDocuments:
              type: 'array'
              items:
                $ref: '#/components/schemas/BankIdNoSignedDocument'

    BankIdNoStatusFailed:
      allOf:
        - $ref: '#/components/schemas/BankIdNoStatusBase'
        - type: 'object'
          required: ['status', 'error']
          properties:
            status:
              type: 'string'
              enum: ['FAILED']
            error:
              $ref: '#/components/schemas/ApiErrorCode'

    BankIdNoStatusResponse:
      oneOf:
        - $ref: '#/components/schemas/BankIdNoStatusPending'
        - $ref: '#/components/schemas/BankIdNoStatusCompleted'
        - $ref: '#/components/schemas/BankIdNoStatusFailed'
      discriminator:
        propertyName: 'status'
        mapping:
          PENDING: '#/components/schemas/BankIdNoStatusPending'
          COMPLETED: '#/components/schemas/BankIdNoStatusCompleted'
          FAILED: '#/components/schemas/BankIdNoStatusFailed'

    BankIdNoSignResult:
      type: 'object'
      required: ['endUser', 'merchant', 'hash']
      properties:
        endUser:
          type: 'string'
        merchant:
          type: 'string'
        hash:
          type: 'string'

    BankIdNoSignedDocument:
      type: 'object'
      required: ['id', 'hash']
      properties:
        id:
          type: 'string'
          description: 'Document UUID matching the uploaded document.'
        hash:
          type: 'string'
          description: 'SHA hash of the signed PDF.'

    VippsStatusBase:
      type: 'object'
      required: ['id']
      properties:
        id:
          type: 'string'
        refId:
          type: 'string'

    VippsStatusPending:
      allOf:
        - $ref: '#/components/schemas/VippsStatusBase'
        - type: 'object'
          required: ['status']
          properties:
            status:
              type: 'string'
              enum: ['PENDING']
            url:
              type: 'string'
              format: 'uri'

    VippsStatusCompleted:
      allOf:
        - $ref: '#/components/schemas/VippsStatusBase'
        - type: 'object'
          required: ['status', 'name', 'givenName', 'surname']
          properties:
            status:
              type: 'string'
              enum: ['COMPLETED']
            ssn:
              type: 'string'
              nullable: true
              description: 'Present when `requestSsn` was `true`.'
            name:
              type: 'string'
            givenName:
              type: 'string'
            surname:
              type: 'string'
            phone:
              type: 'string'
            email:
              type: 'string'
            address:
              type: 'string'
            birthDate:
              type: 'string'
              format: 'date'
              nullable: true
            pid:
              type: 'string'
            bankId:
              type: 'string'

    VippsStatusFailed:
      allOf:
        - $ref: '#/components/schemas/VippsStatusBase'
        - type: 'object'
          required: ['status', 'error']
          properties:
            status:
              type: 'string'
              enum: ['FAILED']
            error:
              $ref: '#/components/schemas/ApiErrorCode'

    VippsStatusResponse:
      oneOf:
        - $ref: '#/components/schemas/VippsStatusPending'
        - $ref: '#/components/schemas/VippsStatusCompleted'
        - $ref: '#/components/schemas/VippsStatusFailed'
      discriminator:
        propertyName: 'status'
        mapping:
          PENDING: '#/components/schemas/VippsStatusPending'
          COMPLETED: '#/components/schemas/VippsStatusCompleted'
          FAILED: '#/components/schemas/VippsStatusFailed'

    MitIdStatusBase:
      type: 'object'
      required: ['id']
      properties:
        id:
          type: 'string'
        refId:
          type: 'string'

    MitIdStatusPending:
      allOf:
        - $ref: '#/components/schemas/MitIdStatusBase'
        - type: 'object'
          required: ['status']
          properties:
            status:
              type: 'string'
              enum: ['PENDING']
            url:
              type: 'string'
              format: 'uri'
            bindingMessage:
              type: 'string'
              description: 'Present in the backchannel flow — display this to the user.'

    MitIdStatusCompleted:
      allOf:
        - $ref: '#/components/schemas/MitIdStatusBase'
        - type: 'object'
          required: ['status', 'name', 'givenName', 'surname']
          properties:
            status:
              type: 'string'
              enum: ['COMPLETED']
            ssn:
              type: 'string'
              nullable: true
              description: 'Danish CPR number.'
            name:
              type: 'string'
            givenName:
              type: 'string'
            surname:
              type: 'string'
            phone:
              type: 'string'
            email:
              type: 'string'
            address:
              type: 'string'
            birthDate:
              type: 'string'
              format: 'date'
              nullable: true
            pid:
              type: 'string'
            bankId:
              type: 'string'
            signResult:
              $ref: '#/components/schemas/MitIdSignResult'

    MitIdStatusFailed:
      allOf:
        - $ref: '#/components/schemas/MitIdStatusBase'
        - type: 'object'
          required: ['status', 'error']
          properties:
            status:
              type: 'string'
              enum: ['FAILED']
            error:
              $ref: '#/components/schemas/ApiErrorCode'

    MitIdStatusResponse:
      oneOf:
        - $ref: '#/components/schemas/MitIdStatusPending'
        - $ref: '#/components/schemas/MitIdStatusCompleted'
        - $ref: '#/components/schemas/MitIdStatusFailed'
      discriminator:
        propertyName: 'status'
        mapping:
          PENDING: '#/components/schemas/MitIdStatusPending'
          COMPLETED: '#/components/schemas/MitIdStatusCompleted'
          FAILED: '#/components/schemas/MitIdStatusFailed'

    MitIdSignResult:
      type: 'object'
      required: ['checksum']
      properties:
        checksum:
          type: 'string'

    FtnStatusBase:
      type: 'object'
      required: ['id']
      properties:
        id:
          type: 'string'
        refId:
          type: 'string'

    FtnStatusPending:
      allOf:
        - $ref: '#/components/schemas/FtnStatusBase'
        - type: 'object'
          required: ['status', 'url']
          properties:
            status:
              type: 'string'
              enum: ['PENDING']
            url:
              type: 'string'
              format: 'uri'

    FtnStatusCompleted:
      allOf:
        - $ref: '#/components/schemas/FtnStatusBase'
        - type: 'object'
          required: ['status', 'name', 'givenName', 'surname']
          properties:
            status:
              type: 'string'
              enum: ['COMPLETED']
            ssn:
              type: 'string'
              nullable: true
              description: >-
                Finnish personal identity code (henkilötunnus).
                May be absent depending on the provider.
            name:
              type: 'string'
            givenName:
              type: 'string'
            surname:
              type: 'string'
            phone:
              type: 'string'
            email:
              type: 'string'
            address:
              type: 'string'
            birthDate:
              type: 'string'
              format: 'date'
              nullable: true
            pid:
              type: 'string'
            bankId:
              type: 'string'

    FtnStatusFailed:
      allOf:
        - $ref: '#/components/schemas/FtnStatusBase'
        - type: 'object'
          required: ['status', 'error']
          properties:
            status:
              type: 'string'
              enum: ['FAILED']
            error:
              $ref: '#/components/schemas/ApiErrorCode'

    FtnStatusResponse:
      oneOf:
        - $ref: '#/components/schemas/FtnStatusPending'
        - $ref: '#/components/schemas/FtnStatusCompleted'
        - $ref: '#/components/schemas/FtnStatusFailed'
      discriminator:
        propertyName: 'status'
        mapping:
          PENDING: '#/components/schemas/FtnStatusPending'
          COMPLETED: '#/components/schemas/FtnStatusCompleted'
          FAILED: '#/components/schemas/FtnStatusFailed'

    AgeVerificationStatusBase:
      type: 'object'
      required: ['id']
      properties:
        id:
          type: 'string'

    AgeVerificationStatusPending:
      allOf:
        - $ref: '#/components/schemas/AgeVerificationStatusBase'
        - type: 'object'
          required: ['status']
          properties:
            status:
              type: 'string'
              enum: ['PENDING']
            url:
              type: 'string'
              format: 'uri'
              nullable: true
            minAge:
              type: 'integer'
              nullable: true
            maxAge:
              type: 'integer'
              nullable: true

    AgeVerificationStatusCompleted:
      allOf:
        - $ref: '#/components/schemas/AgeVerificationStatusBase'
        - type: 'object'
          required: ['status', 'ageVerified']
          properties:
            status:
              type: 'string'
              enum: ['COMPLETED']
            ageVerified:
              type: 'boolean'
              description: '`true` if the user''s age is within the requested range.'

    AgeVerificationStatusFailed:
      allOf:
        - $ref: '#/components/schemas/AgeVerificationStatusBase'
        - type: 'object'
          required: ['status', 'error']
          properties:
            status:
              type: 'string'
              enum: ['FAILED']
            error:
              $ref: '#/components/schemas/ApiErrorCode'

    AgeVerificationStatusResponse:
      oneOf:
        - $ref: '#/components/schemas/AgeVerificationStatusPending'
        - $ref: '#/components/schemas/AgeVerificationStatusCompleted'
        - $ref: '#/components/schemas/AgeVerificationStatusFailed'
      discriminator:
        propertyName: 'status'
        mapping:
          PENDING: '#/components/schemas/AgeVerificationStatusPending'
          COMPLETED: '#/components/schemas/AgeVerificationStatusCompleted'
          FAILED: '#/components/schemas/AgeVerificationStatusFailed'

    DocumentUploadResponse:
      type: 'object'
      required: ['id', 'hash']
      properties:
        id:
          type: 'string'
          description: 'Document UUID — pass this as a reference in signing requests.'
        hash:
          type: 'string'
          description: 'SHA hash of the uploaded document for integrity verification.'
