openapi: 3.0.3
info:
  title: リソースオーナー用API
  description: リソースオーナー（エンドユーザー）本人が、自身のアカウント・連絡先・認証手段・身元確認を操作するためのエンドポイント仕様
  version: 1.0.0
  contact:
    name: idp-server OSS

servers:
  - url: https://api.local.test
    description: IdP Server API エンドポイント(ローカル)

tags:
  - name: ユーザー
    description: |
      リソースオーナー（ユーザー）本人による操作に関するAPI群。

      主に以下のような操作を提供します：
       - 連絡先（メールアドレス / 電話番号）の確認・変更
       - パスワードの変更・リセット
       - 多要素認証（MFA）の登録要求（FIDO-UAF など）
       - 認証デバイスの部分更新・削除
       - 自身のアカウント削除
      
      アクセストークンを用いて、ユーザー本人による安全な自己操作を実現します。
  - name: 身元確認申込み
    description: |
      `idp-server` は身元確認済みID（verified ID）を提供するために、外部のサービスと連携した申込み・審査・完了登録の一連のフローを管理できます。
      
      このAPIを利用することで、ユーザーから収集した情報を外部の身元確認サービスに送信し、確認済みクレーム（`verified_claims`）として `idp-server` に反映できます。
      
      - Control Plane APIでテンプレートを登録
      - ユーザーが申込みを行うと、テンプレートに従い外部APIと連携
      - コールバック等で審査結果・verified_claims登録用データを受信
      - 認証時、verified_claimsを含んだIDトークンやUserInfoを返却
      
      テンプレートでは各種マッピングルール、JSON Schemaによるバリデーション、外部APIとの連携方式（OAuth2, HMAC, なし）を柔軟に定義可能です。
      
      アクセストークンを用いて、ユーザー本人による安全な自己操作を実現します。
  - name: 身元確認結果
    description: |
      身元確認結果を確認するためのAPI
      アクセストークンを用いて、ユーザー本人による安全な自己操作を実現します。
      
  - name: 外部アカウント連携
    description: |
      認証済みユーザーが、自分のアカウントに外部IdPのアカウントを追加で紐付けるためのAPI群。
      紐付けの過程で外部IdPが発行したトークンを暗号化して保管します。

      ここにあるのはアクセストークンで呼ぶ部分（開始・確定・一覧・解除）です。
      ブラウザ遷移（`/v1/linking/start`・`/v1/linking/callback/{provider}`）と保管トークンの取得は
      [RP向けAPI](/docs/content_07_reference/api-rp-ja/) にあります。

      📘 [外部IdPアカウント連携](/docs/content_04_protocols/protocol-09-account-linking)

paths:
  /{tenant-id}/v1/me:
    delete:
      tags:
        - ユーザー
      summary: ユーザー削除
      description: |
        ユーザー（リソースオーナー）自身の削除を行う。
      parameters:
        - $ref: '#/components/parameters/TenantId'
      security:
        - OAuth2:
            - openid
      responses:
        '204':
          description: User deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: スコープ不足（`openid` が必要）
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InsufficientScopeError'
              example:
                error: insufficient_scope
                error_description: "The request requires 'openid' scope"
                scope: openid
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/mfa/{mfa-operation-type}:
    post:
      tags:
        - ユーザー
      summary: MFAの登録要求
      description: |
        FIDO-UAFなどの登録要求の依頼を行う。
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/MfaOperationType'
      security:
        - OAuth2: []
      requestBody:
        required: false
        description: MFAの仕様に応じたパラメータをBodyに設定する。
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/FidoUafRegistrationRequest'
                - $ref: '#/components/schemas/FidoUafDeRegistrationRequest'
      responses:
        '200':
          description: Operation requested
          content:
            application/json:
              schema:
                type: object
                required:
                  - id
                properties:
                  id:
                    type: string
                    description: 認証トランザクションID
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/authentication-devices/{device-id}:
    patch:
      tags:
        - ユーザー
      summary: 認証デバイス部分更新API
      description: |
        認証デバイスの属性情報の部分更新の依頼を行う。
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/DeviceId'
      security:
        - OAuth2:
            - claims:authentication_devices
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthenticationDeviceRequest'
      responses:
        '200':
          description: 200 OK
        '400':
          description: |
            リクエストボディの検証エラー、または指定したデバイスを当該ユーザーが保持していない。
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: "invalid_request"
                  error_description:
                    type: string
                    example: "User does not have authentication device (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)."
                  error_messages:
                    type: array
                    items:
                      type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: スコープ不足（`claims:authentication_devices` が必要）
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InsufficientScopeError'
              example:
                error: insufficient_scope
                error_description: "The request requires 'claims:authentication_devices' scope"
                scope: claims:authentication_devices
        '500':
          $ref: '#/components/responses/InternalServerError'

    delete:
      tags:
        - ユーザー
      summary: 認証デバイス削除API
      description: |
        認証デバイスの登録解除を行う。削除後、当該デバイスでの認証はできなくなる。

        ## セキュリティイベント
        - `authentication_device_deregistration_success` / `authentication_device_deregistration_failure`
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/DeviceId'
      security:
        - OAuth2:
            - claims:authentication_devices
      responses:
        '204':
          description: 削除成功
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: スコープ不足（`claims:authentication_devices` が必要）
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InsufficientScopeError'
              example:
                error: insufficient_scope
                error_description: "The request requires 'claims:authentication_devices' scope"
                scope: claims:authentication_devices
        '404':
          description: |
            指定したデバイスを当該ユーザーが保持していない。
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: "invalid_request"
                  error_description:
                    type: string
                    example: "User does not have authentication device (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)."
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/email/verification:
    post:
      tags:
        - ユーザー
      summary: メールアドレスの確認（開始）
      description: |
        認証済みユーザーが、**現在アカウントに登録されているメールアドレス**の到達性を確認するフローを開始します。

        **送信先はサーバが決めます。** リクエストボディは受け取らず、`new_value` を送っても無視されるため、
        このエンドポイントは送信先を差し替えられません。メールアドレスを変える場合は `/v1/me/email/change` を
        使います（必要スコープが異なります）。

        アカウントにメールアドレスが登録されていない場合は `400`。

        直前の送信から `resend_cooldown_seconds`（未設定は 60 秒）以内の再送も `400`。
        クールダウンは「ユーザー + 操作」単位なので、変更フロー側とは独立です。

        ## セキュリティイベント
        - `email_verify_request_success` / `email_verify_request_failure`
      parameters:
        - $ref: '#/components/parameters/TenantId'
      security:
        - OAuth2:
            - openid
      responses:
        '200':
          $ref: '#/components/responses/ContactChallengeStarted'
        '400':
          $ref: '#/components/responses/ContactVerificationBadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: スコープ不足（`openid` が必要）
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InsufficientScopeError'
              example:
                error: insufficient_scope
                error_description: "The request requires 'openid' scope"
                scope: openid
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/email/verification/{id}/verify:
    post:
      tags:
        - ユーザー
      summary: メールアドレスの確認（コード検証・確定）
      description: |
        コードを検証し、`email_verified: true` を確定します。メールアドレス自体は変わりません。

        ## セキュリティイベント
        - `email_verify_success` / `email_verify_failure`
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/ContactChallengeId'
      security:
        - OAuth2:
            - openid
      requestBody:
        $ref: '#/components/requestBodies/ContactVerificationCode'
      responses:
        '200':
          description: 検証成功
        '400':
          description: コード不正 / 期限切れ / 試行上限超過
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: スコープ不足（`openid` が必要）
        '404':
          $ref: '#/components/responses/ContactChallengeNotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/email/change:
    post:
      tags:
        - ユーザー
      summary: メールアドレスの変更（開始）
      description: |
        認証済みユーザーが自分のメールアドレスを**別の値に変更**するフローを開始します。
        確認コードは**変更先**にメールで送信されます。

        **`openid` ではなく専用スコープ `email:change` が必要です。** identity policy が `EMAIL` の場合、
        確定後は新しいメールアドレスがログイン識別子（`preferred_username`）になるため、通常の OIDC トークンでは
        実行できないようにしています。

        `new_value` に現在の値を指定した場合は `400`（`/v1/me/email/verification` を使います）。

        直前の送信から `resend_cooldown_seconds`（未設定は 60 秒）以内の再送も `400`。
        宛先を呼び出し側が指定できるため、無制限な送信を防ぐための制限です。
        クールダウンは「ユーザー + 操作」単位なので、もう一方のチャネルや確認フローは通ります。

        ## セキュリティイベント
        - `email_change_request_success` / `email_change_request_failure`
      parameters:
        - $ref: '#/components/parameters/TenantId'
      security:
        - OAuth2:
            - email:change
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - new_value
              properties:
                new_value:
                  type: string
                  maxLength: 255
                  pattern: '^[^\s@]+@[^\s@]+\.[^\s@]+$'
                  description: |
                    新しいメールアドレス。
                    送信前に形式・型検証され、不正なら 400（コードは送信されない）。
                    非文字列（数値・オブジェクト・配列・null・真偽値）も拒否されます。
            example:
              new_value: "new@example.com"
      responses:
        '200':
          $ref: '#/components/responses/ContactChallengeStarted'
        '400':
          $ref: '#/components/responses/ContactVerificationBadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: スコープ不足（`email:change` が必要）
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InsufficientScopeError'
              example:
                error: insufficient_scope
                error_description: "The request requires 'email:change' scope"
                scope: email:change
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/email/change/{id}/verify:
    post:
      tags:
        - ユーザー
      summary: メールアドレスの変更（コード検証・確定）
      description: |
        コードを検証し、メールアドレスを確定します。`email` が更新され `email_verified: true` になります。
        identity policy が `EMAIL` の場合、確定後は新しいメールアドレスがログイン識別子になり、
        旧メールアドレスでのログインはできなくなります。

        ## セキュリティイベント
        - `email_change_success` / `email_change_failure`
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/ContactChallengeId'
      security:
        - OAuth2:
            - email:change
      requestBody:
        $ref: '#/components/requestBodies/ContactVerificationCode'
      responses:
        '200':
          description: 検証成功
        '400':
          description: コード不正 / 期限切れ / 試行上限超過 / 変更先が他ユーザーで使用済み
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: スコープ不足（`email:change` が必要）
        '404':
          $ref: '#/components/responses/ContactChallengeNotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/phone/verification:
    post:
      tags:
        - ユーザー
      summary: 電話番号の確認（開始）
      description: |
        認証済みユーザーが、**現在アカウントに登録されている電話番号**の到達性を確認するフローを開始します。

        **送信先はサーバが決めます。** リクエストボディは受け取らず、`new_value` を送っても無視されるため、
        このエンドポイントは送信先を差し替えられません。電話番号を変える場合は `/v1/me/phone/change` を
        使います（必要スコープが異なります）。

        アカウントに電話番号が登録されていない場合は `400`。

        直前の送信から `resend_cooldown_seconds`（未設定は 60 秒）以内の再送も `400`。
        クールダウンは「ユーザー + 操作」単位なので、変更フロー側とは独立です。

        ## セキュリティイベント
        - `phone_verify_request_success` / `phone_verify_request_failure`
      parameters:
        - $ref: '#/components/parameters/TenantId'
      security:
        - OAuth2:
            - openid
      responses:
        '200':
          $ref: '#/components/responses/ContactChallengeStarted'
        '400':
          $ref: '#/components/responses/ContactVerificationBadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: スコープ不足（`openid` が必要）
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InsufficientScopeError'
              example:
                error: insufficient_scope
                error_description: "The request requires 'openid' scope"
                scope: openid
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/phone/verification/{id}/verify:
    post:
      tags:
        - ユーザー
      summary: 電話番号の確認（コード検証・確定）
      description: |
        コードを検証し、`phone_number_verified: true` を確定します。電話番号自体は変わりません。

        ## セキュリティイベント
        - `phone_verify_success` / `phone_verify_failure`
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/ContactChallengeId'
      security:
        - OAuth2:
            - openid
      requestBody:
        $ref: '#/components/requestBodies/ContactVerificationCode'
      responses:
        '200':
          description: 検証成功
        '400':
          description: コード不正 / 期限切れ / 試行上限超過
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: スコープ不足（`openid` が必要）
        '404':
          $ref: '#/components/responses/ContactChallengeNotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/phone/change:
    post:
      tags:
        - ユーザー
      summary: 電話番号の変更（開始）
      description: |
        認証済みユーザーが自分の電話番号を**別の値に変更**するフローを開始します。
        確認コードは**変更先**にSMSで送信されます。

        **`openid` ではなく専用スコープ `phone:change` が必要です。** identity policy が `PHONE` の場合、
        確定後は新しい電話番号がログイン識別子（`preferred_username`）になるため、通常の OIDC トークンでは
        実行できないようにしています。

        `new_value` に現在の値を指定した場合は `400`（`/v1/me/phone/verification` を使います）。

        直前の送信から `resend_cooldown_seconds`（未設定は 60 秒）以内の再送も `400`。
        宛先を呼び出し側が指定できるため、無制限な送信を防ぐための制限です。
        クールダウンは「ユーザー + 操作」単位なので、もう一方のチャネルや確認フローは通ります。

        ## セキュリティイベント
        - `phone_change_request_success` / `phone_change_request_failure`
      parameters:
        - $ref: '#/components/parameters/TenantId'
      security:
        - OAuth2:
            - phone:change
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - new_value
              properties:
                new_value:
                  type: string
                  maxLength: 32
                  pattern: '^[+0-9][0-9 ()\-]{2,31}$'
                  description: |
                    新しい電話番号。E.164 を推奨しますが、国内表記（ハイフン区切り等）も受け付けます（テナントごとの表記揺れを弾かないため、意図的に緩い検証です）。
                    送信前に形式・型検証され、不正なら 400（コードは送信されない）。
                    非文字列（数値・オブジェクト・配列・null・真偽値）も拒否されます。
            example:
              new_value: "+819012345678"
      responses:
        '200':
          $ref: '#/components/responses/ContactChallengeStarted'
        '400':
          $ref: '#/components/responses/ContactVerificationBadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: スコープ不足（`phone:change` が必要）
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InsufficientScopeError'
              example:
                error: insufficient_scope
                error_description: "The request requires 'phone:change' scope"
                scope: phone:change
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/phone/change/{id}/verify:
    post:
      tags:
        - ユーザー
      summary: 電話番号の変更（コード検証・確定）
      description: |
        コードを検証し、電話番号を確定します。`phone_number` が更新され `phone_number_verified: true` になります。
        identity policy が `PHONE` の場合、確定後は新しい電話番号がログイン識別子になり、
        旧電話番号でのログインはできなくなります。

        ## セキュリティイベント
        - `phone_change_success` / `phone_change_failure`
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/ContactChallengeId'
      security:
        - OAuth2:
            - phone:change
      requestBody:
        $ref: '#/components/requestBodies/ContactVerificationCode'
      responses:
        '200':
          description: 検証成功
        '400':
          description: コード不正 / 期限切れ / 試行上限超過 / 変更先が他ユーザーで使用済み
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: スコープ不足（`phone:change` が必要）
        '404':
          $ref: '#/components/responses/ContactChallengeNotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'


  /{tenant-id}/v1/me/password/change:
    post:
      tags:
        - ユーザー
      summary: パスワード変更
      description: |
        認証済みユーザーのパスワードを変更します。

        このAPIは以下の手順でパスワード変更を行います：
        1. 現在のパスワードの検証
        2. 新しいパスワードのポリシー検証（テナント設定に基づく）
        3. パスワードの更新
        4. セキュリティイベントの発行（password_change_success/password_change_failure）

        ## パスワードポリシー

        テナントごとに以下のポリシーを設定可能（`identity_policy_config.password_policy`）：
        - `min_length`: 最小文字数（デフォルト: 8）
        - `require_uppercase`: 大文字必須（デフォルト: false）
        - `require_lowercase`: 小文字必須（デフォルト: false）
        - `require_digit`: 数字必須（デフォルト: false）
        - `require_special_char`: 記号必須（デフォルト: false）

        デフォルトポリシーはNIST SP 800-63Bの推奨に従い、最小8文字のみを要求します。

        ## セキュリティイベント

        パスワード変更の成功・失敗時に以下のセキュリティイベントが発行されます：
        - `password_change_success`: パスワード変更成功時
        - `password_change_failure`: パスワード変更失敗時（検証エラー等）

        ## 参考実装

        - [Microsoft Graph API: changePassword](https://learn.microsoft.com/en-us/graph/api/user-changepassword)
        - [Okta Users API: Change Password](https://developer.okta.com/docs/reference/api/users/#change-password)
      parameters:
        - $ref: '#/components/parameters/TenantId'
      security:
        - OAuth2:
            - openid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PasswordChangeRequest'
            example:
              current_password: "CurrentPassword123"
              new_password: "NewSecurePassword456"
      responses:
        '200':
          description: パスワード変更成功
          content:
            application/json:
              schema:
                type: object
                required:
                  - message
                properties:
                  message:
                    type: string
                    description: 成功メッセージ
                    example: "Password changed successfully."
        '400':
          description: |
            バリデーションエラー
            - 現在のパスワードが未入力
            - 新しいパスワードが未入力
            - 現在のパスワードが不正
            - 新しいパスワードがポリシー違反
          content:
            application/json:
              schema:
                type: object
                required:
                  - error
                  - error_description
                properties:
                  error:
                    type: string
                    enum:
                      - invalid_request
                      - invalid_current_password
                      - invalid_new_password
                    description: エラーコード
                  error_description:
                    type: string
                    description: エラー詳細メッセージ
              examples:
                missing_current_password:
                  value:
                    error: "invalid_request"
                    error_description: "Current password is required."
                missing_new_password:
                  value:
                    error: "invalid_request"
                    error_description: "New password is required."
                incorrect_current_password:
                  value:
                    error: "invalid_current_password"
                    error_description: "Current password is incorrect."
                password_too_short:
                  value:
                    error: "invalid_new_password"
                    error_description: "Password must be at least 8 characters."
                password_policy_violation:
                  value:
                    error: "invalid_new_password"
                    error_description: "Password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character."
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: |
            スコープ不足
            - 必要スコープ: `openid`
          content:
            application/json:
              schema:
                type: object
                required:
                  - error
                  - error_description
                  - scope
                properties:
                  error:
                    type: string
                    example: "insufficient_scope"
                  error_description:
                    type: string
                    example: "The request requires 'openid' scope"
                  scope:
                    type: string
                    example: "openid"
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/password/reset:
    post:
      tags:
        - ユーザー
      summary: パスワードリセット
      description: |
        セルフサービスでパスワードをリセットします。

        ## 認可フロー

        このAPIは特別なスコープ `password:reset` が必要です。
        通常のログインフローではなく、以下の手順で取得します：

        1. Email認証フローで認可リクエスト開始（scope=password:reset）
        2. Email認証チャレンジで検証コード送信
        3. Email認証検証でユーザー認証
        4. 認可コード取得
        5. トークンエンドポイントで `password:reset` スコープ付きアクセストークン取得
        6. このAPIでパスワードリセット実行

        ## password/change との違い

        | API | 必要なもの | スコープ | ユースケース |
        |-----|----------|---------|-------------|
        | `/v1/me/password/change` | 現在のパスワード | `openid` | ログイン中のパスワード変更 |
        | `/v1/me/password/reset` | Email認証等 | `password:reset` | パスワードを忘れた場合 |

        **重要**: このAPIは現在のパスワードを**必要としません**。代わりに、Email認証などの別の認証方式で取得した `password:reset` スコープ付きトークンを使用します。

        ## パスワードポリシー

        テナントごとに以下のポリシーを設定可能（`identity_policy_config.password_policy`）：
        - `min_length`: 最小文字数（デフォルト: 8）
        - `require_uppercase`: 大文字必須（デフォルト: false）
        - `require_lowercase`: 小文字必須（デフォルト: false）
        - `require_number`: 数字必須（デフォルト: false）
        - `require_special_char`: 記号必須（デフォルト: false）

        デフォルトポリシーはNIST SP 800-63Bの推奨に従い、最小8文字のみを要求します。

        ## セキュリティイベント

        パスワードリセットの成功・失敗時に以下のセキュリティイベントが発行されます：
        - `password_reset_success`: パスワードリセット成功時
        - `password_reset_failure`: パスワードリセット失敗時（ポリシー違反等）

        ## 参考

        - Issue #1002: Self-service password reset using email authentication
        - [NIST SP 800-63B](https://pages.nist.gov/800-63-3/sp800-63b.html)
      parameters:
        - $ref: '#/components/parameters/TenantId'
      security:
        - OAuth2:
            - password:reset
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - new_password
              properties:
                new_password:
                  type: string
                  description: 新しいパスワード
                  minLength: 8
                  maxLength: 72
                  example: "NewSecurePassword456!"
            example:
              new_password: "NewSecurePassword456!"
      responses:
        '200':
          description: パスワードリセット成功
          content:
            application/json:
              schema:
                type: object
                required:
                  - message
                properties:
                  message:
                    type: string
                    description: 成功メッセージ
                    example: "Password changed successfully."
        '400':
          description: |
            バリデーションエラー
            - 新しいパスワードが未入力
            - 新しいパスワードがポリシー違反
          content:
            application/json:
              schema:
                type: object
                required:
                  - error
                  - error_description
                properties:
                  error:
                    type: string
                    enum:
                      - invalid_request
                      - invalid_new_password
                    description: エラーコード
                  error_description:
                    type: string
                    description: エラー詳細メッセージ
              examples:
                missing_new_password:
                  value:
                    error: "invalid_request"
                    error_description: "New password is required."
                password_too_short:
                  value:
                    error: "invalid_new_password"
                    error_description: "Password must be at least 8 characters long."
                password_policy_violation:
                  value:
                    error: "invalid_new_password"
                    error_description: "Password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character."
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: |
            スコープ不足
            - 必要スコープ: `password:reset`
          content:
            application/json:
              schema:
                type: object
                required:
                  - error
                  - error_description
                  - scope
                properties:
                  error:
                    type: string
                    example: "insufficient_scope"
                  error_description:
                    type: string
                    example: "The request requires 'password:reset' scope"
                  scope:
                    type: string
                    example: "password:reset"
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/identity-verification/applications/{verification-type}/{verification-process}:
    post:
      tags:
        - 身元確認申込み
      security:
        - OAuth2:
            - identity_verification_application
      summary: 身元確認申込み
      description: |
        指定された verification-type（テンプレート種別）および verification-process（申込み処理タイプ）に従い、
        身元確認申込みの初期リクエストを実行します。
        
        レスポンスに後続で利用するidが返却されます。
        
        このAPIは、あらかじめ Control Plane API により定義されたテンプレートに基づいて外部サービスに対して申請データを送信し、
        審査プロセスの開始またはeKYCプロセスの起動を行います。
        
        - `verification-type` はテンプレートの `type` に対応
        - `verification-process` はテンプレート内の `processes` のキーに対応（例: apply, request-ekyc など）
        
        リクエストボディはテンプレートで定義された `request.schema` に基づいてバリデーションされ、
        必須項目の漏れや形式の不正がある場合はエラーになります。
        
        正常に処理された場合は、`response.body_mapping_rules` に従いレスポンスを返却します。
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/VerificationType'
        - $ref: '#/components/parameters/VerificationProcess'
      requestBody:
        required: false
        description: |
          テンプレートで定義されたスキーマに従うリクエストボディ。
          内容は `verification-type` および `verification-process` ごとに異なる。
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true

            example:
              last_name: "伊藤"
              first_name: "一朗"
              birthdate: "1990-01-01"
              email_address: "ichiro@example.com"

      responses:
        '200':
          description: 申込み成功時のレスポンス
          content:
            application/json:
              schema:
                type: object
                required:
                  - id
                properties:
                  id:
                    type: string
                    description: |
                      後続で利用する身元確認申込の識別子。
                additionalProperties:
                  description:
                    設定ファイルの `response` に設定した項目が返却される
              example:
               id: "565b030e-04b5-45eb-989e-136e3623dc7d"
               application_number: "abcdefg012345"
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '408':
          $ref: '#/components/responses/RequestTimeout'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'

  /{tenant-id}/v1/me/identity-verification/applications:
    get:
      tags:
        - 身元確認申込み
      security:
        - OAuth2:
          - identity_verification_application
      summary: 身元確認申込み一覧取得
      description: |
        身元確認の申込みの一覧を取得する。

      parameters:
        - $ref: '#/components/parameters/TenantId'
        - name: id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: 身元確認申請のID（UUID形式）での絞り込み
        - name: type
          in: query
          required: false
          schema:
            type: string
          description: 申込み種別（verification-type）での絞り込み
        - name: client_id
          in: query
          required: false
          schema:
            type: string
          description: 申請時に使用されたクライアントID
        - name: status
          in: query
          required: false
          schema:
            type: string
          description: |
            申請のステータス
        - in: query
          name: application_details.<key>
          schema:
            type: string
          description: 申込み詳細情報のオブジェクトのkeyに対する検索（keyは具体的な値の設定が必要）
        - in: query
          name: external_application_details.<key>
          schema:
            type: string
          description: 外部申込み結果の詳細情報のオブジェクトのkeyに対する検索（keyは具体的な値の設定が必要）
        - name: from
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: 絞り込み開始日時（ISO 8601形式）
        - name: to
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: 絞り込み終了日時（ISO 8601形式）
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 20
          description: 取得件数の上限（デフォルトは20件）
        - name: offset
          in: query
          required: false
          schema:
            type: integer
            default: 0
          description: ページネーション用のオフセット（デフォルトは0）

      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  list:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                          description: 申込みID
                        tenant_id:
                          type: string
                          format: uuid
                          description: テナントID
                        user_id:
                          type: string
                          format: uuid
                          description: ユーザーID
                        type:
                          type: string
                          description: 申込み種別（verification-type）
                        client_id:
                          type: string
                          description: クライアントID
                        application_details:
                          type: object
                          description: |
                            申込みの詳細情報。storeの設定に応じて保存したデータ。
                        processes:
                          type: object
                          additionalProperties:
                            type: object
                            properties:
                              call_count:
                                type: integer
                              success_count:
                                type: integer
                              failure_count:
                                type: integer
                        attributes:
                          type: object
                          description: |
                            申込みの属性情報。attributesに設定したデータ。
                        status:
                          type: string
                          description: |
                            現在のステータス

                            | ステータス名                   | 説明                                                             |
                            |--------------------------|----------------------------------------------------------------|
                            | `requested`              | 申請リクエストが正常に受理された直後の状態です。                                       |
                            | `applying`               | ユーザーまたは外部システムが必要な情報を入力・収集中の状態です。フォーム入力中や追加書類のアップロード待ちなどが該当します。 |
                            | `applied`                | 申請情報の送信が完了し、審査待ちの状態です。全ての申込み処理が終了した後に遷移します。                  |
                            | `examination_processing` | 申請内容に対する審査が実施されている状態です。外部eKYCサービスとの連携や人手による審査が行われている場合も含まれます。  |
                            | `approved`               | 審査の結果、申請が承認された状態です。ユーザーの身元確認が完了し、検証済みクレームの登録などが行われます。          |
                            | `rejected`               | 審査の結果、申請が却下された状態です。必要に応じて理由の提示や再申請の導線提示が推奨されます。                |
                            | `expired`                | 有効期限切れなどにより、申請が無効となった状態です。一定期間操作が行われなかった場合などに自動的に遷移することがあります。  |
                            | `cancelled`              | ユーザーまたは管理者によって申請が任意に中断された状態です。取り下げやキャンセル操作などが該当します。            |
                            | `unknown`                | 状態が特定できない不明な状態です。移行中やデータ不整合、バージョン差異等により例外的に発生する可能性があります。       |

                          enum:
                            - requested
                            - applying
                            - applied
                            - examination_processing
                            - approved
                            - rejected
                            - expired
                            - cancelled
                            - unknown
                        requested_at:
                          type: string
                          format: date-time
                          description: 申請が行われた日時
                  total_count:
                    type: integer
                    description: 申込の総数
                  limit:
                    type: integer
                    description: 取得件数の上限
                  offset:
                    type: integer
                    description:  ページネーション用のオフセット
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalServerError'




  /{tenant-id}/v1/me/identity-verification/applications/{verification-type}/{id}/{verification-process}:
    post:
      tags:
        - 身元確認申込み
      security:
        - OAuth2:
          - identity_verification_application
      summary: 身元確認申込み後続処理
      description: |
        既存の身元確認申込み（application）に対して、定義済みテンプレートおよびプロセスに従った後続の処理を実行します。
        
        このAPIは、初回申込み（apply等）によって発行された申込みID（`id`）を指定し、
        対象となる申込みに対して、eKYC完了通知・審査要求・外部サービスとの再連携などのアクションを柔軟に実行可能です。
        
        - `verification-type` はテンプレートの `type` に対応
        - `id` は既存の申込みIDを指定
        - `verification-process` はテンプレートの `processes` 内で定義された処理（例: `complete-ekyc`, `callback-result`）
        
        リクエストボディは、該当プロセスで必要とされる `request.schema` に基づいてバリデーションされ、
        外部サービスとの連携や verified_claims 登録等が自動的に実行されます。
        
        正常に処理された場合は、`response.body_mapping_rules` に従いレスポンスを返却します。
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/VerificationType'
        - $ref: '#/components/parameters/ApplicationId'
        - $ref: '#/components/parameters/VerificationProcess'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Processed
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  description: |
                    設定ファイルの `response` に設定した項目が返却される
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '408':
          $ref: '#/components/responses/RequestTimeout'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'


  /{tenant-id}/v1/me/identity-verification/applications/{verification-type}/{id}:
    delete:
      tags:
        - 身元確認申込み
      security:
        - OAuth2:
          - identity_verification_application_delete
      summary: 身元確認申込み削除
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/VerificationType'
        - $ref: '#/components/parameters/ApplicationId'
      responses:
        '200':
          description: 削除成功
          content:
            application/json:
              schema:
                type: object
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/identity-verification/results:
    get:
      tags:
        - 身元確認結果
      security:
        - OAuth2:
          - identity_verification_result
      summary: 身元確認結果の一覧取得
      description: |
        身元確認結果の一覧を取得します。
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - name: id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: 身元確認結果のID（UUID形式）での絞り込み
        - name: application_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: 身元確認申込みのID（UUID形式）での絞り込み
        - name: type
          in: query
          required: false
          schema:
            type: string
          description: 申込み種別（verification-type）での絞り込み
        - name: external_application_id
          in: query
          required: false
          schema:
            type: string
          description: 外部連携先の申請ID
        - in: query
          name: verified_claims.<key>
          schema:
            type: string
          description: verified_claimsのオブジェクトのkeyに対する検索（keyは具体的な値の設定が必要）
        - name: verified_at_from
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: verified_atに対する絞り込み 開始日時（ISO 8601形式）
        - name: verified_at_to
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: verified_atに対する絞り込み 終了日時（ISO 8601形式）
        - name: verified_until_from
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: verified_untilに対する絞り込み 開始日時（ISO 8601形式）
        - name: verified_until_to
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: verified_untilに対する絞り込み 終了日時（ISO 8601形式）
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 20
          description: 取得件数の上限（デフォルトは20件）
        - name: offset
          in: query
          required: false
          schema:
            type: integer
            default: 0
          description: ページネーション用のオフセット（デフォルトは0）
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  list:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                          description: 身元確認結果のID
                        tenant_id:
                          type: string
                          format: uuid
                          description: テナントID
                        user_id:
                          type: string
                          format: uuid
                          description: ユーザーID
                        application_id:
                          type: string
                          format: uuid
                          description: 元となる申請のID
                        type:
                          type: string
                          description: 申込み種別（verification-type）
                        verified_at:
                          type: string
                          format: date-time
                          description: 身元確認が完了した日時
                        verified_until:
                          type: string
                          format: date-time
                          nullable: true
                          description: 身元確認が有効な期限（省略可能）
                        source:
                          type: string
                          description: |
                            結果の生成元（例: application）
                        source_details:
                          type: object
                          description: |
                            設定の `result.source_details` に定義したデータ。
                        attributes:
                          type: object
                          description: |
                            身元確認の属性情報。設定のattributesが登録される。
                        verified_claims:
                          type: object
                          description: |
                            設定の `result.verified_claims` に定義したデータ。IDA準拠を想定。
                          properties:
                            claims:
                              type: object
                            verification:
                              type: object
                        applied_user_claims:
                          type: object
                          nullable: true
                          description: |
                            承認時にこの結果が実際にユーザーへ適用した値の記録。
                            設定の `result` で `user_claims_mapping_rules` / `custom_properties_mapping_rules` / `user_status` を使った場合に、
                            実際に適用された値が記録される。適用が無かったパートは省略される。
                          properties:
                            user_claims:
                              type: object
                              description: |
                                適用した標準クレーム（family_name 等のパッチ可能なOIDCプロファイルクレームのみ）。
                            custom_properties:
                              type: object
                              description: |
                                適用した業務属性（custom_properties）。
                            user_status:
                              type: string
                              description: |
                                実際に遷移したユーザーステータス（遷移が起きた場合のみ記録。現状維持の場合は省略）。
                  total_count:
                    type: integer
                    description: 身元確認結果の総数
                  limit:
                    type: integer
                    description: 取得件数の上限
                  offset:
                    type: integer
                    description: ページネーション用のオフセット
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/linked-external-accounts/link/{provider}:
    post:
      tags:
        - 外部アカウント連携
      summary: 外部アカウント連携の開始
      description: |
        認証済みユーザーの外部IdPアカウント連携を開始します。アクセストークンのユーザーとクライアントを連携セッションに結び付け、
        ブラウザで開く `start_url` を返します。

        RP は `start_url` をブラウザの**トップレベル遷移**（`location.href` など）で開きます。iframe の中では連携は成立しません。
        以降のブラウザ遷移（`/v1/linking/start`・`/v1/linking/callback/{provider}`）は
        [RP向けAPI](/docs/content_07_reference/api-rp-ja/) を参照してください。

        - `redirect_uri` は連携の終わりにブラウザを戻す先です。クライアント設定の `extension.linking_return_uris` と完全一致で照合します。
        - 連携セッションの有効期間は 900 秒です（`expires_in`）。

        ## 必要なスコープ
        `linked_account:manage`
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/LinkingProvider'
      security:
        - OAuth2:
            - linked_account:manage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AccountLinkingStartRequest'
      responses:
        '201':
          description: 連携セッションを作成した
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountLinkingStartResponse'
        '400':
          description: |
            `redirect_uri` が未指定、またはクライアントの `linking_return_uris` に登録されていない。
            または `{provider}` の連携先が設定されていない・無効。
            または `scope` が連携先の設定の `scopes_supported` の範囲を超えている。
            または連携先の `scopes_supported` に `openid` があるのに、`scope` に `openid` が無い。
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountLinkingErrorResponse'
              examples:
                missing:
                  value:
                    error: invalid_request
                    error_description: "redirect_uri is required."
                not_registered:
                  value:
                    error: invalid_request
                    error_description: "redirect_uri is not registered for this client."
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: |
            スコープ不足（`linked_account:manage` が必要）。
            または `{provider}` がクライアントの `linking_providers` に無い。
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InsufficientScopeError'
              example:
                error: insufficient_scope
                error_description: "The request requires the 'linked_account:manage' scope."
                scope: linked_account:manage
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/linked-external-accounts/complete:
    post:
      tags:
        - 外部アカウント連携
      summary: 外部アカウント連携の確定
      description: |
        コールバックで預けた外部IdPのトークンを、アクセストークンのユーザーの連携として確定します。
        ブラウザが `redirect_uri?linking=done&state=...` に戻ってきたら、その `state` を送ります。

        次の全てを満たすときだけ確定します。

        - `state` に対応する連携セッションがあり、期限内である
        - アクセストークンのクライアントが、連携を**開始したクライアント**と同じである（同じユーザーでも別のクライアントのトークンは `403`）
        - アクセストークンのユーザーが、連携を開始したユーザーと同じである
        - 連携セッションがコールバックまで進んでおり、まだ確定されていない

        同じユーザーが同じ外部アカウントをもう一度連携した場合はエラーにならず、既存の連携を更新します（`alias` は保たれます）。
        連携先が 1 件だけを許す（`multiple_accounts: false`、既定）ときは、外部アカウントを名乗らない連携先でも、もう一度の連携は既存の 1 件の更新になります。別の外部アカウントは `409` です。

        ## 必要なスコープ
        `linked_account:manage`

        ## セキュリティイベント
        - `external_account_linked` / `external_account_link_failed`
      parameters:
        - $ref: '#/components/parameters/TenantId'
      security:
        - OAuth2:
            - linked_account:manage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - state
              properties:
                state:
                  type: string
                  description: 開始レスポンスの `state`（戻り先の `state` クエリと同じ値）
            example:
              state: "Xb3k...Qz"
      responses:
        '201':
          description: 連携を確定した
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LinkedExternalAccount'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: |
            次のいずれか。

            - スコープ不足（`linked_account:manage` が必要）。`insufficient_scope` 形式
            - 連携を開始したクライアントと違うクライアントのトークン
            - 連携を開始したユーザーと違うユーザーのトークン
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/InsufficientScopeError'
                  - $ref: '#/components/schemas/AccountLinkingErrorResponse'
              examples:
                insufficient_scope:
                  value:
                    error: insufficient_scope
                    error_description: "The request requires the 'linked_account:manage' scope."
                    scope: linked_account:manage
                client_mismatch:
                  value:
                    error: invalid_request
                    error_description: "Account linking must be completed by the client that started it. state=..."
                user_mismatch:
                  value:
                    error: invalid_request
                    error_description: "Account linking session is bound to a different user. state=..., provider=..."
        '404':
          description: |
            `state` に対応する連携セッションが無い。
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountLinkingErrorResponse'
              example:
                error: invalid_request
                error_description: "Account linking session not found."
        '409':
          description: |
            次のいずれか。

            - 連携セッションが期限切れ
            - 連携セッションが確定できる状態にない（コールバック前、または確定済み）
            - 同じ外部アカウントが既に他のユーザーに紐付いている（連携先の `duplicate_link_policy` が `reject` のとき）。誰に紐付いているかは返しません
            - この連携先の別のアカウントを既に連携している（連携先の `multiple_accounts` が `false` のとき）。先に解除してから連携します
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountLinkingErrorResponse'
              examples:
                expired:
                  value:
                    error: invalid_request
                    error_description: "Account linking session is expired. state=..."
                duplicate:
                  value:
                    error: invalid_request
                    error_description: "This external account cannot be linked."
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/linked-external-accounts:
    get:
      tags:
        - 外部アカウント連携
      summary: 外部アカウント連携の一覧
      description: |
        アクセストークンのユーザーの外部アカウント連携を一覧で返します。保管しているトークンは含みません。

        ## 必要なスコープ
        `linked_account:read` または `linked_account:manage`
      parameters:
        - $ref: '#/components/parameters/TenantId'
      security:
        - OAuth2:
            - linked_account:read
        - OAuth2:
            - linked_account:manage
      responses:
        '200':
          description: 連携の一覧
          content:
            application/json:
              schema:
                type: object
                required:
                  - list
                properties:
                  list:
                    type: array
                    items:
                      $ref: '#/components/schemas/LinkedExternalAccount'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: スコープ不足（`linked_account:read` または `linked_account:manage` が必要）
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InsufficientScopeError'
              example:
                error: insufficient_scope
                error_description: "The request requires the 'linked_account:read' scope."
                scope: linked_account:read
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{tenant-id}/v1/me/linked-external-accounts/{alias}:
    delete:
      tags:
        - 外部アカウント連携
      summary: 外部アカウント連携の解除
      description: |
        アクセストークンのユーザーの連携を 1 件解除します。対象は呼び出したユーザー自身の連携だけで、他人の `alias` は `404` になります。

        行を消す前に、保管しているトークンを外部IdPの失効エンドポイント（RFC 7009）で失効させます。
        失効はベストエフォートで、`revocation_endpoint` が未設定・外部IdPに届かない・失効を拒否された場合も連携は解除されます。

        ## 必要なスコープ
        `linked_account:manage`

        ## セキュリティイベント
        - `external_account_unlinked`
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/LinkedAccountAlias'
      security:
        - OAuth2:
            - linked_account:manage
      responses:
        '204':
          description: 解除成功
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: スコープ不足（`linked_account:manage` が必要）
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InsufficientScopeError'
              example:
                error: insufficient_scope
                error_description: "The request requires the 'linked_account:manage' scope."
                scope: linked_account:manage
        '404':
          description: 指定した `alias` が呼び出したユーザーの連携に無い
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountLinkingErrorResponse'
              example:
                error: not_found
                error_description: "Linked external account not found."
        '500':
          $ref: '#/components/responses/InternalServerError'

components:
  responses:
    ContactChallengeStarted:
      description: 確認コード送信成功。返却された id を verify に渡す
      content:
        application/json:
          schema:
            type: object
            required:
              - id
            properties:
              id:
                type: string
                description: チャレンジID
          example:
            id: "3fa85f64-5717-4562-b3fc-2c963f66afa6"

    ContactVerificationBadRequest:
      description: リクエストエラー（詳細は description 参照）
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
                example: "invalid_request"
              error_description:
                type: string
              error_messages:
                type: array
                items:
                  type: string

    ContactChallengeNotFound:
      description: |
        チャレンジが存在しない。呼び出しユーザーのものでない場合、操作種別が一致しない場合、
        既に消費済みの場合も同じ 404（存在有無を推測させないため）。

    BadRequest:
      description: |
        リクエストパラメータが不正、検証エラー、プロセスシーケンスエラー
      content:
        application/json:
          schema:
            type: object
            required:
              - error
              - error_description
            properties:
              error:
                type: string
                example: "invalid_request"
                description: |
                  エラーコード。以下の値を取りうる:

                  | error 値 | 説明 |
                  |---------|------|
                  | `invalid_request` | リクエストパラメータの不正、JSON Schema 検証エラー、プロセスシーケンスエラー |
                  | `pre_hook_validation_failed` | Pre-hook バリデーション失敗（ユーザークレーム検証、重複申込み検知等） |
                  | `execution_failed` | 外部サービス実行エラー（`error_details` に詳細が含まれる） |
              error_description:
                type: string
                example: "Request validation failed"
              error_messages:
                type: array
                items:
                  type: string
                description: |
                  検証エラーの詳細メッセージ配列（JSON Schema検証エラー、プロセスシーケンスエラー等）。オプショナル。
              error_details:
                type: object
                description: |
                  外部サービス実行時のエラー詳細情報（`execution_failed` の場合のみ）。オプショナル。
                required:
                  - execution_type
                  - status_category
                  - status_code
                properties:
                  execution_type:
                    type: string
                    description: 実行タイプ
                    example: "http_request"
                  status_category:
                    type: string
                    description: HTTPステータスカテゴリ
                    enum: ["client_error", "server_error"]
                  status_code:
                    type: integer
                    description: 外部サービスから返されたHTTPステータスコード
                    example: 400
                  response_body:
                    type: object
                    description: 外部サービスのレスポンスボディ（オプショナル）
          examples:
            schema_validation_error:
              value:
                error: "invalid_request"
                error_description: "The identity verification request is invalid. Please review your input for missing or incorrect fields."
                error_messages:
                  - "last_name is missing"
                  - "first_name is missing"
                  - "birthdate is missing"
            type_mismatch_error:
              value:
                error: "invalid_request"
                error_description: "The identity verification request is invalid. Please review your input for missing or incorrect fields."
                error_messages:
                  - "age is not a integer"
                  - "address is not a object"
            process_sequence_error:
              value:
                error: "pre_hook_validation_failed"
                error_description: "Pre-hook validation failed for identity verification request"
                error_messages:
                  - "Process 'crm-registration' requires completion of: apply. Missing: apply"
                error_details: {}
            pre_hook_validation_failed:
              value:
                error: "pre_hook_validation_failed"
                error_description: "Pre-hook validation failed for identity verification request"
                error_messages:
                  - "User claim verification failed. unmatched: $.request_body.email_address, user:email"
                error_details:
                  verification_failure: true
            external_service_400:
              value:
                error: "execution_failed"
                error_description: "execution failed"
                error_messages:
                  - "External service returned 400: invalid_request"
                error_details:
                  execution_type: "http_request"
                  status_category: "client_error"
                  status_code: 400
                  response_body:
                    error: "invalid_request"
                    error_description: "The request is missing required parameters or contains invalid values"

    Unauthorized:
      description: |
        認証エラー。アクセストークンが無効または期限切れ、または外部サービスから401エラーが返された場合
      content:
        application/json:
          schema:
            type: object
            required:
              - error
              - error_description
            properties:
              error:
                type: string
                example: "invalid_token"
                description: |
                  エラーコード。`invalid_token`（アクセストークンが無効または期限切れ）または `execution_failed`（外部サービスエラー）
              error_description:
                type: string
                example: "The access token is invalid or expired"
              error_messages:
                type: array
                items:
                  type: string
                description: エラーメッセージ配列。オプショナル。
              error_details:
                type: object
                description: 外部サービスエラー時の詳細情報（execution_failed の場合のみ）。オプショナル。
                required:
                  - execution_type
                  - status_category
                  - status_code
                properties:
                  execution_type:
                    type: string
                    example: "http_request"
                  status_category:
                    type: string
                    example: "client_error"
                  status_code:
                    type: integer
                    example: 401
                  response_body:
                    type: object
                    description: 外部サービスのレスポンスボディ（オプショナル）
          examples:
            invalid_token:
              value:
                error: "invalid_token"
                error_description: "The access token is invalid or expired"
            external_service_401:
              value:
                error: "execution_failed"
                error_description: "execution failed"
                error_messages:
                  - "External service returned 401: invalid_token"
                error_details:
                  execution_type: "http_request"
                  status_category: "client_error"
                  status_code: 401
                  response_body:
                    error: "invalid_token"
                    error_description: "The access token provided is invalid or expired"

    Forbidden:
      description: |
        権限エラー。**ボディの形が 2 通りあります。**

        ## 1. スコープ不足

        身元確認系のスコープ検査は Spring Security のフィルタ層で行うため
        （`SecurityConfig` の `hasAuthority`）、OAuth のエラー形式ではなく
        フレームワーク既定のエラーボディが返ります。`insufficient_scope` は**返りません**。

        ```json
        { "timestamp": "2026-09-19T07:56:30.704Z", "status": 403, "error": "Forbidden", "path": "/{tenant-id}/v1/me/identity-verification/results" }
        ```

        連絡先・パスワード・認証デバイス・ユーザー削除は各操作の中で検査するため
        `insufficient_scope` 形式です（各操作の `403` を参照）。

        ## 2. 外部サービスが 403 を返した

        `execution_failed` として `error_details` に外部の応答を入れて返します。
      content:
        application/json:
          schema:
            oneOf:
              - title: スコープ不足（フィルタ層）
                type: object
                required:
                  - status
                  - error
                properties:
                  timestamp:
                    type: string
                    format: date-time
                    example: "2026-09-19T07:56:30.704Z"
                  status:
                    type: integer
                    example: 403
                  error:
                    type: string
                    example: "Forbidden"
                  path:
                    type: string
                    example: "/{tenant-id}/v1/me/identity-verification/results"
              - title: 外部サービスエラー
                type: object
                required:
                  - error
                  - error_description
                properties:
                  error:
                    type: string
                    example: "execution_failed"
                  error_description:
                    type: string
                    example: "execution failed"
                  error_messages:
                    type: array
                    items:
                      type: string
                    description: エラーメッセージ配列。オプショナル。
                  error_details:
                    type: object
                    description: 外部サービスエラー時の詳細情報。オプショナル。
                    required:
                      - execution_type
                      - status_category
                      - status_code
                    properties:
                      execution_type:
                        type: string
                        example: "http_request"
                      status_category:
                        type: string
                        example: "client_error"
                      status_code:
                        type: integer
                        example: 403
                      response_body:
                        type: object
                        description: 外部サービスのレスポンスボディ（オプショナル）
          examples:
            insufficient_scope:
              summary: スコープ不足（フィルタ層で弾かれた場合）
              value:
                timestamp: "2026-09-19T07:56:30.704Z"
                status: 403
                error: "Forbidden"
                path: "/{tenant-id}/v1/me/identity-verification/results"
            external_service_403:
              value:
                error: "execution_failed"
                error_description: "execution failed"
                error_messages:
                  - "External service returned 403: insufficient_scope"
                error_details:
                  execution_type: "http_request"
                  status_category: "client_error"
                  status_code: 403
                  response_body:
                    error: "insufficient_scope"
                    error_description: "The request requires specific permissions"
                    scope: "identity_verification_application_delete"

    NotFound:
      description: |
        リソースが存在しない
      content:
        application/json:
          schema:
            type: object
            required:
              - error
              - error_description
            properties:
              error:
                type: string
                example: "invalid_request"
                description: |
                  エラーコード（実装では `invalid_request` を使用）
              error_description:
                type: string
                example: "The requested resource was not found"
              error_messages:
                type: array
                items:
                  type: string
                description: エラーメッセージ配列。オプショナル。
          examples:
            application_not_found:
              value:
                error: "invalid_request"
                error_description: "identity verification application not found"
                error_messages:
                  - "identity verification application not found"

    RequestTimeout:
      description: |
        リクエストタイムアウト、または外部サービスから408エラーが返された場合
      content:
        application/json:
          schema:
            type: object
            required:
              - error
              - error_description
            properties:
              error:
                type: string
                example: "request_timeout"
                description: |
                  エラーコード。`request_timeout`（内部エラー）または `execution_failed`（外部サービスエラー）
              error_description:
                type: string
                example: "The request timed out"
              error_messages:
                type: array
                items:
                  type: string
                description: エラーメッセージ配列。オプショナル。
              error_details:
                type: object
                description: 外部サービスエラー時の詳細情報（execution_failed の場合のみ）。オプショナル。
                required:
                  - execution_type
                  - status_category
                  - status_code
                properties:
                  execution_type:
                    type: string
                    example: "http_request"
                  status_category:
                    type: string
                    example: "client_error"
                  status_code:
                    type: integer
                    example: 408
                  response_body:
                    type: object
                    description: 外部サービスのレスポンスボディ（オプショナル）
          examples:
            request_timeout:
              value:
                error: "request_timeout"
                error_description: "The request timed out"
            external_service_408:
              value:
                error: "execution_failed"
                error_description: "execution failed"
                error_messages:
                  - "External service returned 408: request_timeout"
                error_details:
                  execution_type: "http_request"
                  status_category: "client_error"
                  status_code: 408
                  response_body:
                    error: "request_timeout"
                    error_description: "The server timed out waiting for the request"
                    timeout_seconds: 30

    Conflict:
      description: |
        リソースの状態が競合している、または外部サービスから409エラーが返された場合
      content:
        application/json:
          schema:
            type: object
            required:
              - error
              - error_description
            properties:
              error:
                type: string
                example: "conflict"
                description: |
                  エラーコード。`conflict`（内部エラー）または `execution_failed`（外部サービスエラー）
              error_description:
                type: string
                example: "A verification application is already in progress"
              error_messages:
                type: array
                items:
                  type: string
                description: エラーメッセージ配列。オプショナル。
              error_details:
                type: object
                description: 外部サービスエラー時の詳細情報（execution_failed の場合のみ）。オプショナル。
                required:
                  - execution_type
                  - status_category
                  - status_code
                properties:
                  execution_type:
                    type: string
                    example: "http_request"
                  status_category:
                    type: string
                    example: "client_error"
                  status_code:
                    type: integer
                    example: 409
                  response_body:
                    type: object
                    description: 外部サービスのレスポンスボディ（オプショナル）
          examples:
            conflict:
              summary: "内部エラー - 競合"
              description: "idp-server内部で申込みの競合が検出された場合"
              value:
                error: "conflict"
                error_description: "A verification application is already in progress"
                error_messages:
                  - "Cannot create new application while previous application is still active"
            external_service_409:
              summary: "外部サービスエラー - 状態遷移エラー"
              description: "外部サービスから409が返された場合。response_bodyには状態遷移情報（current_state, requested_state等）を含む可能性がある。"
              value:
                error: "execution_failed"
                error_description: "execution failed"
                error_messages:
                  - "External service returned 409: invalid_request"
                error_details:
                  execution_type: "http_request"
                  status_category: "client_error"
                  status_code: 409
                  response_body:
                    error: "invalid_request"
                    error_description: "Cannot transition from 'approved' to 'applying'. Invalid state transition."
                    current_state: "approved"
                    requested_state: "applying"

    PayloadTooLarge:
      description: |
        ペイロードが大きすぎる、または外部サービスから413エラーが返された場合
      content:
        application/json:
          schema:
            type: object
            required:
              - error
              - error_description
            properties:
              error:
                type: string
                example: "payload_too_large"
                description: |
                  エラーコード。`payload_too_large`（内部エラー）または `execution_failed`（外部サービスエラー）
              error_description:
                type: string
                example: "The request payload is too large"
              error_messages:
                type: array
                items:
                  type: string
                description: エラーメッセージ配列。オプショナル。
              error_details:
                type: object
                description: 外部サービスエラー時の詳細情報（execution_failed の場合のみ）。オプショナル。
                required:
                  - execution_type
                  - status_category
                  - status_code
                properties:
                  execution_type:
                    type: string
                    example: "http_request"
                  status_category:
                    type: string
                    example: "client_error"
                  status_code:
                    type: integer
                    example: 413
                  response_body:
                    type: object
                    description: 外部サービスのレスポンスボディ（オプショナル）
          examples:
            payload_too_large:
              summary: "内部エラー - ペイロード超過"
              description: "idp-server内部でペイロードサイズ制限に達した場合"
              value:
                error: "payload_too_large"
                error_description: "The request payload is too large"
            external_service_413:
              summary: "外部サービスエラー - ペイロード超過"
              description: "外部サービスから413が返された場合。response_bodyにはペイロードサイズ情報（max_size_bytes, received_size_bytes等）を含む可能性がある。"
              value:
                error: "execution_failed"
                error_description: "execution failed"
                error_messages:
                  - "External service returned 413: payload_too_large"
                error_details:
                  execution_type: "http_request"
                  status_category: "client_error"
                  status_code: 413
                  response_body:
                    error: "payload_too_large"
                    error_description: "The request payload exceeds the maximum allowed size"
                    max_size_bytes: 5242880
                    received_size_bytes: 10485760

    UnsupportedMediaType:
      description: |
        サポートされていないメディアタイプ、または外部サービスから415エラーが返された場合
      content:
        application/json:
          schema:
            type: object
            required:
              - error
              - error_description
            properties:
              error:
                type: string
                example: "unsupported_media_type"
                description: |
                  エラーコード。`unsupported_media_type`（内部エラー）または `execution_failed`（外部サービスエラー）
              error_description:
                type: string
                example: "The media type is not supported"
              error_messages:
                type: array
                items:
                  type: string
                description: エラーメッセージ配列。オプショナル。
              error_details:
                type: object
                description: 外部サービスエラー時の詳細情報（execution_failed の場合のみ）。オプショナル。
                required:
                  - execution_type
                  - status_category
                  - status_code
                properties:
                  execution_type:
                    type: string
                    example: "http_request"
                  status_category:
                    type: string
                    example: "client_error"
                  status_code:
                    type: integer
                    example: 415
                  response_body:
                    type: object
                    description: 外部サービスのレスポンスボディ（オプショナル）
          examples:
            unsupported_media_type:
              summary: "内部エラー - 非サポートメディアタイプ"
              description: "idp-server内部でメディアタイプが拒否された場合"
              value:
                error: "unsupported_media_type"
                error_description: "The media type is not supported"
            external_service_415:
              summary: "外部サービスエラー - 非サポートメディアタイプ"
              description: "外部サービスから415が返された場合。response_bodyにはサポートされるメディアタイプ情報（supported_types, received_type等）を含む可能性がある。"
              value:
                error: "execution_failed"
                error_description: "execution failed"
                error_messages:
                  - "External service returned 415: unsupported_media_type"
                error_details:
                  execution_type: "http_request"
                  status_category: "client_error"
                  status_code: 415
                  response_body:
                    error: "unsupported_media_type"
                    error_description: "The media type is not supported"
                    supported_types: ["image/png", "image/jpeg"]
                    received_type: "image/gif"

    UnprocessableEntity:
      description: |
        バリデーションエラー、または外部サービスから422エラーが返された場合
      content:
        application/json:
          schema:
            type: object
            required:
              - error
              - error_description
            properties:
              error:
                type: string
                example: "validation_error"
                description: |
                  エラーコード。`validation_error`（内部エラー）または `execution_failed`（外部サービスエラー）
              error_description:
                type: string
                example: "The request data failed validation"
              error_messages:
                type: array
                items:
                  type: string
                description: エラーメッセージ配列。オプショナル。
              error_details:
                type: object
                description: 外部サービスエラー時の詳細情報（execution_failed の場合のみ）。オプショナル。
                required:
                  - execution_type
                  - status_category
                  - status_code
                properties:
                  execution_type:
                    type: string
                    example: "http_request"
                  status_category:
                    type: string
                    example: "client_error"
                  status_code:
                    type: integer
                    example: 422
                  response_body:
                    type: object
                    description: 外部サービスのレスポンスボディ（オプショナル）
          examples:
            validation_error:
              summary: "内部エラー - バリデーションエラー"
              description: "idp-server内部でバリデーションエラーが発生した場合"
              value:
                error: "validation_error"
                error_description: "The request data failed validation"
            external_service_422:
              summary: "外部サービスエラー - バリデーションエラー"
              description: "外部サービスから422が返された場合。response_bodyにはバリデーションエラー詳細（validation_errors等）を含む可能性がある。"
              value:
                error: "execution_failed"
                error_description: "execution failed"
                error_messages:
                  - "External service returned 422: validation_error"
                error_details:
                  execution_type: "http_request"
                  status_category: "client_error"
                  status_code: 422
                  response_body:
                    error: "validation_error"
                    error_description: "The request data failed validation"
                    validation_errors:
                      - field: "birthdate"
                        message: "Invalid date format. Expected YYYY-MM-DD"
                      - field: "document_number"
                        message: "Document number contains invalid characters"

    TooManyRequests:
      description: |
        レート制限超過、または外部サービスから429エラーが返された場合
      content:
        application/json:
          schema:
            type: object
            required:
              - error
              - error_description
            properties:
              error:
                type: string
                example: "too_many_requests"
                description: |
                  エラーコード。`too_many_requests`（内部エラー）または `execution_failed`（外部サービスエラー）
              error_description:
                type: string
                example: "Too many requests. Please try again later."
              error_messages:
                type: array
                items:
                  type: string
                description: エラーメッセージ配列。オプショナル。
              error_details:
                type: object
                description: 外部サービスエラー時の詳細情報（execution_failed の場合のみ）。オプショナル。
                required:
                  - execution_type
                  - status_category
                  - status_code
                properties:
                  execution_type:
                    type: string
                    example: "http_request"
                  status_category:
                    type: string
                    example: "client_error"
                  status_code:
                    type: integer
                    example: 429
                  response_body:
                    type: object
                    description: 外部サービスのレスポンスボディ（オプショナル）
          examples:
            too_many_requests:
              summary: "内部エラー - レート制限超過"
              description: "idp-server内部でレート制限に達した場合"
              value:
                error: "too_many_requests"
                error_description: "Too many requests. Please try again later."
            external_service_429:
              summary: "外部サービスエラー - レート制限超過"
              description: "外部サービスから429が返された場合。response_bodyにはレート制限情報（retry_after等）を含む可能性がある。"
              value:
                error: "execution_failed"
                error_description: "execution failed"
                error_messages:
                  - "External service returned 429: too_many_requests"
                error_details:
                  execution_type: "http_request"
                  status_category: "client_error"
                  status_code: 429
                  response_body:
                    error: "too_many_requests"
                    error_description: "Rate limit exceeded. Please try again later."
                    retry_after: 60

    InternalServerError:
      description: |
        サーバー内部エラー、または外部サービスから500エラーが返された場合
      content:
        application/json:
          schema:
            type: object
            required:
              - error
              - error_description
            properties:
              error:
                type: string
                example: "server_error"
                description: |
                  エラーコード。`server_error`（内部エラー）または `execution_failed`（外部サービスエラー）
              error_description:
                type: string
                example: "An internal server error occurred"
              error_messages:
                type: array
                items:
                  type: string
                description: エラーメッセージ配列。オプショナル。
              error_details:
                type: object
                description: 外部サービスエラー時の詳細情報（execution_failed の場合のみ）。オプショナル。
                required:
                  - execution_type
                  - status_category
                  - status_code
                properties:
                  execution_type:
                    type: string
                    example: "http_request"
                  status_category:
                    type: string
                    example: "server_error"
                  status_code:
                    type: integer
                    example: 500
                  response_body:
                    type: object
                    description: 外部サービスのレスポンスボディ（オプショナル）
          examples:
            internal_error:
              value:
                error: "server_error"
                error_description: "An internal server error occurred"
            external_service_500:
              value:
                error: "execution_failed"
                error_description: "execution failed"
                error_messages:
                  - "External service returned 500: server_error"
                error_details:
                  execution_type: "http_request"
                  status_category: "server_error"
                  status_code: 500
                  response_body:
                    error: "server_error"
                    error_description: "An unexpected error occurred on the external service"
                    error_id: "err_abc123xyz"

    BadGateway:
      description: |
        ゲートウェイエラー、または外部サービスから502エラーが返された場合
      content:
        application/json:
          schema:
            type: object
            required:
              - error
              - error_description
            properties:
              error:
                type: string
                example: "bad_gateway"
                description: |
                  エラーコード。`bad_gateway`（内部エラー）または `execution_failed`（外部サービスエラー）
              error_description:
                type: string
                example: "The upstream server returned an invalid response"
              error_messages:
                type: array
                items:
                  type: string
                description: エラーメッセージ配列。オプショナル。
              error_details:
                type: object
                description: 外部サービスエラー時の詳細情報（execution_failed の場合のみ）。オプショナル。
                required:
                  - execution_type
                  - status_category
                  - status_code
                properties:
                  execution_type:
                    type: string
                    example: "http_request"
                  status_category:
                    type: string
                    example: "server_error"
                  status_code:
                    type: integer
                    example: 502
                  response_body:
                    type: object
                    description: 外部サービスのレスポンスボディ（オプショナル）
          examples:
            bad_gateway:
              value:
                error: "bad_gateway"
                error_description: "The upstream server returned an invalid response"
            external_service_502:
              value:
                error: "execution_failed"
                error_description: "execution failed"
                error_messages:
                  - "External service returned 502: bad_gateway"
                error_details:
                  execution_type: "http_request"
                  status_category: "server_error"
                  status_code: 502
                  response_body:
                    error: "bad_gateway"
                    error_description: "The upstream server returned an invalid response"
                    upstream_error: "Connection refused"

    ServiceUnavailable:
      description: |
        サービス一時利用不可、または外部サービスから503エラーが返された場合
      content:
        application/json:
          schema:
            type: object
            required:
              - error
              - error_description
            properties:
              error:
                type: string
                example: "service_unavailable"
                description: |
                  エラーコード。`service_unavailable`（内部エラー）または `execution_failed`（外部サービスエラー）
              error_description:
                type: string
                example: "The service is temporarily unavailable. Please try again later."
              error_messages:
                type: array
                items:
                  type: string
                description: エラーメッセージ配列。オプショナル。
              error_details:
                type: object
                description: 外部サービスエラー時の詳細情報（execution_failed の場合のみ）。オプショナル。
                required:
                  - execution_type
                  - status_category
                  - status_code
                properties:
                  execution_type:
                    type: string
                    example: "http_request"
                  status_category:
                    type: string
                    example: "server_error"
                  status_code:
                    type: integer
                    example: 503
                  response_body:
                    type: object
                    description: 外部サービスのレスポンスボディ（オプショナル）
          examples:
            service_unavailable:
              value:
                error: "service_unavailable"
                error_description: "The service is temporarily unavailable. Please try again later."
            external_service_503:
              value:
                error: "execution_failed"
                error_description: "execution failed"
                error_messages:
                  - "External service returned 503: service_unavailable"
                error_details:
                  execution_type: "http_request"
                  status_category: "server_error"
                  status_code: 503
                  response_body:
                    error: "service_unavailable"
                    error_description: "The external service is temporarily unavailable. Please retry after some time."
                    retryable: true
                    retry_after: 60

    GatewayTimeout:
      description: |
        外部サービスタイムアウト、または外部サービスから504エラーが返された場合
      content:
        application/json:
          schema:
            type: object
            required:
              - error
              - error_description
            properties:
              error:
                type: string
                example: "gateway_timeout"
                description: |
                  エラーコード。`gateway_timeout`（内部エラー）または `execution_failed`（外部サービスエラー）
              error_description:
                type: string
                example: "The external service request timed out"
              error_messages:
                type: array
                items:
                  type: string
                description: エラーメッセージ配列。オプショナル。
              error_details:
                type: object
                description: 外部サービスエラー時の詳細情報（execution_failed の場合のみ）。オプショナル。
                required:
                  - execution_type
                  - status_category
                  - status_code
                properties:
                  execution_type:
                    type: string
                    example: "http_request"
                  status_category:
                    type: string
                    example: "server_error"
                  status_code:
                    type: integer
                    example: 504
                  response_body:
                    type: object
                    description: 外部サービスのレスポンスボディ（オプショナル）
          examples:
            gateway_timeout:
              value:
                error: "gateway_timeout"
                error_description: "The external service request timed out"
            external_service_504:
              value:
                error: "execution_failed"
                error_description: "execution failed"
                error_messages:
                  - "External service returned 504: gateway_timeout"
                error_details:
                  execution_type: "http_request"
                  status_category: "server_error"
                  status_code: 504
                  response_body:
                    error: "gateway_timeout"
                    error_description: "The gateway did not receive a timely response from the upstream server"
                    timeout_seconds: 30

  securitySchemes:
    OAuth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://api.local.test/{tenant-id}/v1/authorizations
          tokenUrl: https://api.local.test/{tenant-id}/v1/tokens
          scopes:
            openid: OpenID Connect の必須スコープ。自身の連絡先の確認とプロフィール操作に必要
            email:change: メールアドレスの変更に必要なスコープ。確認（verification）には不要
            phone:change: 電話番号の変更に必要なスコープ。確認（verification）には不要
            password:reset: パスワードリセットに必要なスコープ
            claims:authentication_devices: 認証デバイス情報の参照・更新に必要なスコープ
            identity_verification_application: 身元確認の申込みのAPIに必要なスコープ
            identity_verification_application_delete: 身元確認の申込みの削除スコープ
            identity_verification_result: 身元確認結果のAPIに必要なスコープ
            linked_account:read: 外部アカウント連携の一覧に必要なスコープ
            linked_account:manage: 外部アカウント連携の開始・確定・解除に必要なスコープ。一覧も含む

  requestBodies:
    ContactVerificationCode:
      required: true
      content:
        application/json:
          schema:
            type: object
            required:
              - verification_code
            properties:
              verification_code:
                type: string
          example:
            verification_code: "123456"

  parameters:
    ContactChallengeId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: チャレンジID（開始レスポンスの id）

    TenantId:
      name: tenant-id
      in: path
      required: true
      schema:
        type: string
    MfaOperationType:
      name: mfa-operation-type
      in: path
      required: true
      schema:
        type: string
        example: fido-uaf-registration
    DeviceId:
      name: device-id
      in: path
      required: true
      schema:
        type: string
        example: 0048607e-9b89-4ad9-a203-976b7f72f5f2
    VerificationType:
      name: verification-type
      in: path
      required: true
      schema:
        type: string
        example: my_number
    VerificationProcess:
      name: verification-process
      in: path
      required: true
      schema:
        type: string
        example: capture
    ApplicationId:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid

    LinkingProvider:
      name: provider
      in: path
      required: true
      description: "連携先の設定名（`federation-configurations` の `sso_provider`、`type: oidc`）"
      schema:
        type: string
        example: example-idp
    LinkedAccountAlias:
      name: alias
      in: path
      required: true
      description: 連携の識別子（一覧の `alias`）
      schema:
        type: string
        example: example-idp-k3f9x2qa
  schemas:
    InsufficientScopeError:
      type: object
      properties:
        error:
          type: string
          example: insufficient_scope
        error_description:
          type: string
        scope:
          type: string
          description: 要求されるスコープ

    AccountLinkingStartRequest:
      type: object
      required:
        - redirect_uri
      properties:
        redirect_uri:
          type: string
          format: uri
          description: |
            連携の終わりにブラウザを戻す先。クライアント設定の `extension.linking_return_uris` と完全一致で照合します。
            認可の `redirect_uris` とは別の設定です。
          example: "https://rp.example.com/linking/callback"
        scope:
          type: string
          description: |
            外部IdPに要求するスコープ（スペース区切り）。省略すると連携先設定の `scopes_supported` を使います。
          example: "openid email offline_access"

    AccountLinkingStartResponse:
      type: object
      required:
        - start_url
        - state
        - expires_in
      properties:
        start_url:
          type: string
          format: uri
          description: |
            ブラウザをトップレベル遷移で開かせる URL。外部IdPではなく idp-server の `/v1/linking/start` です
            （認可サーバーの issuer から組み立てます）。
          example: "https://api.local.test/67e7eae6-62b0-4500-9eff-87459f63fc66/v1/linking/start?state=Xb3k...Qz"
        state:
          type: string
          description: 連携セッションの識別子。確定（`complete`）で送ります
        expires_in:
          type: integer
          description: 連携セッションの有効期間（秒）。固定で 900
          example: 900

    LinkedExternalAccount:
      type: object
      description: 外部アカウントの連携。保管しているトークンは含みません。
      properties:
        alias:
          type: string
          description: |
            連携の識別子。`{provider}-{8 文字の乱数}` の形で、ユーザーの中で一意です。同じ外部アカウントを連携し直しても変わりません。解除した連携の識別子が別の連携に使い回されることはありません。
            解除して別のアカウントをつなぎ直すと新しい識別子になるので、前の識別子で取得すると `404` になります。そのときは一覧を引き直します。
          example: example-idp-k3f9x2qa
        provider:
          type: string
          description: 連携先の設定名（`sso_provider`）
          example: example-idp
        federated_username:
          type: string
          nullable: true
          description: 外部IdPの userinfo の `email`。連携先に userinfo が無い場合は `null`
          example: user@example.com
        scope:
          type: string
          nullable: true
          description: |
            外部IdPが許可したスコープ。トークン応答に `scope` が無い場合は連携先設定の `scopes_supported`
          example: "openid email offline_access"
        access_token_expires_at:
          type: string
          nullable: true
          description: 保管しているアクセストークンの有効期限（タイムゾーンなしの ISO 8601 ローカル日時）
          example: "2026-10-21T11:15:30.123456"
        refresh_token_expires_at:
          type: string
          nullable: true
          description: |
            保管しているリフレッシュトークンの有効期限（同上）。リフレッシュトークンが無い場合は `null`
          example: "2026-10-22T10:15:30.123456"
        created_at:
          type: string
          description: 作成日時（同上）
          example: "2026-10-21T10:15:30.123456"
        updated_at:
          type: string
          description: 更新日時（同上）
          example: "2026-10-21T10:15:30.123456"

    AccountLinkingErrorResponse:
      type: object
      required:
        - error
        - error_description
      properties:
        error:
          type: string
          description: |
            エラーコード。解除の `404` は `not_found`、想定外のエラーは `server_error`、それ以外は `invalid_request`
          example: invalid_request
        error_description:
          type: string

    FidoUafRegistrationRequest:
      description: FIDO-UAF登録用のリクエスト
      allOf:
        - $ref: '#/components/schemas/AuthenticationDeviceRequest'

    FidoUafDeRegistrationRequest:
      description: FIDO-UAF解除用のリクエスト
      type: object
      properties:
        device_id:
          type: string
          example: cab44004-c288-4ae4-be88-9b1cbbe79b71

    AuthenticationDeviceRequest:
      type: object
      properties:
        app_name:
          type: string
          description: アプリ名（例：◯◯アプリ）。
        platform:
          type: string
          description: デバイスのプラットフォーム名（例："Android", "iOS" など）。
        os:
          type: string
          description: オペレーティングシステムのバージョン情報（例："Android15"）。
        model:
          type: string
          description: デバイスモデル名（例："galaxy z fold 6"）。
        locale:
          type: string
          description: 言語設定。（例：ja, en）
        notification_channel:
          type: string
          enum: [ fcm, apns ]
          description: 通知チャネル（"fcm", "apns"）。Firebase Cloud Messaging（Android）またはApple Push Notification Service（iOS）を指定。
        notification_token:
          type: string
          description: 通知を送信するためのトークン（例：FCMトークン）。
        priority:
          type: integer
          minimum: 1
          description: >
            このデバイスの通知の優先順位（例: 1, 50, 100）。
            数値が大きいほど優先度が高く、100が最も高い。
            省略された場合は、認証デバイスの登録数の連番となる。

    PasswordChangeRequest:
      type: object
      required:
        - current_password
        - new_password
      properties:
        current_password:
          type: string
          description: |
            現在のパスワード。

            このパスワードが正しいかどうかを検証してから新しいパスワードへの変更を行います。
          example: "CurrentPassword123"
        new_password:
          type: string
          description: |
            新しいパスワード。

            テナントに設定されたパスワードポリシー（`identity_policy_config.password_policy`）に従って検証されます。

            デフォルトポリシー：
            - 最小8文字（NIST SP 800-63B推奨）

            設定可能なポリシー：
            - `min_length`: 最小文字数
            - `require_uppercase`: 大文字を含む
            - `require_lowercase`: 小文字を含む
            - `require_digit`: 数字を含む
            - `require_special_char`: 記号を含む
          example: "NewSecurePassword456"
