components:
  headers:
    Cache-Control:
      description: Association file は public で5分間 cache される。
      schema:
        const: public, max-age=300
        type: string
    ETag:
      description: 'Environment の config_version 由来の weak ETag。例: W/"12"。'
      schema:
        pattern: ^W/"[0-9]+"$
        type: string
    Location:
      description: リダイレクト先 URL
      schema:
        type: string
    X-Request-Id:
      description: リクエストID。全レスポンスに付与される (design.md §26.3)。
      schema:
        pattern: ^req_[a-z2-7]{32}$
        type: string
  parameters:
    IfNoneMatch:
      description: カンマ区切りの entity tag。現行 weak ETag (`W/"<config_version>"`) を含む場合は 304 を返す。handler は `*` wildcard をサポートしない。
      in: header
      name: If-None-Match
      required: false
      schema:
        type: string
  responses:
    BadRequest:
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
      description: リクエスト不正。入力検証エラー等 (Problem Details)。
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
    DefaultError:
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
      description: 上記以外の予期しないエラー (Problem Details)。
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
    Forbidden:
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
      description: "拒否 (Problem Details)。この API では §28.5 の新規ユーザー作成ゲートが返す: 72 時間の無料期間が失効し未請求の workspace への新規ユーザー作成がブロックされると `code: new_user_creation_blocked`。既存ユーザーのログインは影響を受けない。"
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
    InternalServerError:
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
      description: サーバ内部エラー (Problem Details)。
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
    NotFound:
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
      description: 対象リソース (トランザクション等) が存在しない (Problem Details)。
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
    ServiceUnavailable:
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
      description: 登録済み mobile app 設定を読み込めない (Problem Details)。
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
    TooManyRequests:
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
      description: レート制限超過 (Problem Details)。
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
    Unauthorized:
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
      description: 認証エラー。アクセストークンまたはクライアント認証が無効 (Problem Details)。
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
  schemas:
    AppleAppSiteAssociation:
      additionalProperties: false
      properties:
        webcredentials:
          additionalProperties: false
          properties:
            apps:
              items:
                minLength: 1
                type: string
              type: array
          required:
            - apps
          type: object
      required:
        - webcredentials
      type: object
    AssetLinks:
      items:
        additionalProperties: false
        properties:
          relation:
            prefixItems:
              - const: delegate_permission/common.handle_all_urls
                type: string
              - const: delegate_permission/common.get_login_creds
                type: string
            type: array
          target:
            additionalProperties: false
            properties:
              namespace:
                const: android_app
                type: string
              package_name:
                minLength: 1
                type: string
              sha256_cert_fingerprints:
                items:
                  minLength: 1
                  type: string
                minItems: 1
                type: array
            required:
              - namespace
              - package_name
              - sha256_cert_fingerprints
            type: object
        required:
          - relation
          - target
        type: object
      type: array
    EmailChallengeResponse:
      additionalProperties: false
      properties:
        challenge_id:
          minLength: 1
          type: string
        expires_at:
          format: date-time
          pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
          type: string
      required:
        - challenge_id
        - expires_at
      type: object
    EmailResendRequest:
      additionalProperties: false
      properties:
        challenge_id:
          minLength: 1
          type: string
      required:
        - challenge_id
      type: object
    EmailStartRequest:
      additionalProperties: false
      properties:
        email:
          format: email
          pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
          type: string
      required:
        - email
      type: object
    EmailVerifyRequest:
      additionalProperties: false
      properties:
        challenge_id:
          minLength: 1
          type: string
        code:
          pattern: ^[0-9]{6}$
          type: string
      required:
        - challenge_id
        - code
      type: object
    IntrospectionRequest:
      additionalProperties: false
      properties:
        client_id:
          pattern: ^cli_[a-z2-7]{32}$
          type: string
        client_secret:
          maxLength: 512
          minLength: 1
          type: string
        token:
          minLength: 1
          type: string
        token_type_hint:
          enum:
            - access_token
            - refresh_token
          type: string
      required:
        - token
      type: object
    IntrospectionResponse:
      anyOf:
        - additionalProperties: false
          properties:
            active:
              const: true
              type: boolean
            aud:
              anyOf:
                - type: string
                - items:
                    type: string
                  type: array
            client_id:
              pattern: ^cli_[a-z2-7]{32}$
              type: string
            exp:
              maximum: 9007199254740991
              minimum: -9007199254740991
              type: integer
            iat:
              maximum: 9007199254740991
              minimum: -9007199254740991
              type: integer
            iss:
              format: uri
              type: string
            jti:
              pattern: ^jti_[a-z2-7]{32}$
              type: string
            nbf:
              maximum: 9007199254740991
              minimum: -9007199254740991
              type: integer
            scope:
              minLength: 1
              type: string
            sub:
              pattern: ^usr_[a-z2-7]{32}$
              type: string
            token_type:
              const: Bearer
              type: string
          required:
            - active
          type: object
        - additionalProperties: false
          properties:
            active:
              const: false
              type: boolean
          required:
            - active
          type: object
    JwksDocument:
      additionalProperties: false
      properties:
        keys:
          items:
            additionalProperties: false
            properties:
              alg:
                const: ES256
                type: string
              crv:
                const: P-256
                type: string
              kid:
                minLength: 1
                type: string
              kty:
                const: EC
                type: string
              use:
                const: sig
                type: string
              x:
                minLength: 1
                type: string
              y:
                minLength: 1
                type: string
            required:
              - kty
              - crv
              - x
              - y
              - kid
              - alg
              - use
            type: object
          type: array
      required:
        - keys
      type: object
    LogoutRequest:
      properties:
        client_id:
          pattern: ^cli_[a-z2-7]{32}$
          type: string
        id_token_hint:
          type: string
        post_logout_redirect_uri:
          maxLength: 2048
          minLength: 1
          type: string
        state:
          maxLength: 512
          minLength: 1
          type: string
      type: object
    MeDeleteResponse:
      additionalProperties: false
      properties:
        status:
          const: pending_deletion
          type: string
        user_id:
          pattern: ^usr_[a-z2-7]{32}$
          type: string
      required:
        - user_id
        - status
      type: object
    MeEmailChangeStartRequest:
      additionalProperties: false
      properties:
        new_email:
          format: email
          pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
          type: string
      required:
        - new_email
      type: object
    MeEmailChangeVerifyNewRequest:
      additionalProperties: false
      properties:
        challenge_id:
          minLength: 1
          type: string
        code:
          pattern: ^[0-9]{6}$
          type: string
      required:
        - challenge_id
        - code
      type: object
    MeEmailChangeVerifyNewResponse:
      additionalProperties: false
      properties:
        email:
          format: email
          pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
          type: string
        email_verified:
          const: true
          type: boolean
      required:
        - email
        - email_verified
      type: object
    MeEmailChangeVerifyOldRequest:
      additionalProperties: false
      properties:
        challenge_id:
          minLength: 1
          type: string
        code:
          pattern: ^[0-9]{6}$
          type: string
      required:
        - challenge_id
        - code
      type: object
    MeIdentitiesListResponse:
      additionalProperties: false
      properties:
        identities:
          items:
            additionalProperties: false
            properties:
              identity_id:
                pattern: ^idn_[a-z2-7]{32}$
                type: string
              last_used_at:
                anyOf:
                  - format: date-time
                    pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                    type: string
                  - type: "null"
              linked_at:
                format: date-time
                pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                type: string
              provider:
                enum:
                  - google
                  - apple
                  - github
                  - microsoft
                  - generic_oidc
                type: string
            required:
              - identity_id
              - provider
              - linked_at
              - last_used_at
            type: object
          type: array
      required:
        - identities
      type: object
    MeIdentityLinkRequest:
      additionalProperties: false
      properties:
        redirect_uri:
          maxLength: 2048
          minLength: 1
          type: string
      required:
        - redirect_uri
      type: object
    MeIdentityUnlinkResponse:
      additionalProperties: false
      properties:
        identity_id:
          pattern: ^idn_[a-z2-7]{32}$
          type: string
        unlinked:
          const: true
          type: boolean
      required:
        - identity_id
        - unlinked
      type: object
    MePasskeyDeleteResponse:
      additionalProperties: false
      properties:
        deleted:
          const: true
          type: boolean
        passkey_id:
          pattern: ^psk_[a-z2-7]{32}$
          type: string
      required:
        - passkey_id
        - deleted
      type: object
    MePasskeyRegistrationVerifyResponse:
      additionalProperties: false
      properties:
        passkey:
          additionalProperties: false
          properties:
            backed_up:
              type: boolean
            created_at:
              format: date-time
              pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
              type: string
            device_type:
              anyOf:
                - enum:
                    - single_device
                    - multi_device
                  type: string
                - type: "null"
            last_used_at:
              anyOf:
                - format: date-time
                  pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                  type: string
                - type: "null"
            name:
              anyOf:
                - type: string
                - type: "null"
            passkey_id:
              pattern: ^psk_[a-z2-7]{32}$
              type: string
            transports:
              items:
                enum:
                  - ble
                  - hybrid
                  - internal
                  - nfc
                  - usb
                type: string
              type: array
          required:
            - passkey_id
            - name
            - created_at
            - last_used_at
            - device_type
            - backed_up
          type: object
      required:
        - passkey
      type: object
    MePasskeysListResponse:
      additionalProperties: false
      properties:
        passkeys:
          items:
            additionalProperties: false
            properties:
              backed_up:
                type: boolean
              created_at:
                format: date-time
                pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                type: string
              device_type:
                anyOf:
                  - enum:
                      - single_device
                      - multi_device
                    type: string
                  - type: "null"
              last_used_at:
                anyOf:
                  - format: date-time
                    pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                    type: string
                  - type: "null"
              name:
                anyOf:
                  - type: string
                  - type: "null"
              passkey_id:
                pattern: ^psk_[a-z2-7]{32}$
                type: string
              transports:
                items:
                  enum:
                    - ble
                    - hybrid
                    - internal
                    - nfc
                    - usb
                  type: string
                type: array
            required:
              - passkey_id
              - name
              - created_at
              - last_used_at
              - device_type
              - backed_up
            type: object
          type: array
      required:
        - passkeys
      type: object
    MeSessionRevokeResponse:
      additionalProperties: false
      properties:
        revoked:
          const: true
          type: boolean
        session_id:
          pattern: ^ses_[a-z2-7]{32}$
          type: string
      required:
        - session_id
        - revoked
      type: object
    MeSessionsListResponse:
      additionalProperties: false
      properties:
        sessions:
          items:
            additionalProperties: false
            properties:
              client_id:
                pattern: ^cli_[a-z2-7]{32}$
                type: string
              created_at:
                format: date-time
                pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                type: string
              current:
                type: boolean
              device_name:
                anyOf:
                  - type: string
                  - type: "null"
              last_active_at:
                format: date-time
                pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                type: string
              session_id:
                pattern: ^ses_[a-z2-7]{32}$
                type: string
            required:
              - session_id
              - client_id
              - device_name
              - created_at
              - last_active_at
              - current
            type: object
          type: array
      required:
        - sessions
      type: object
    MeSessionsRevokeAllResponse:
      additionalProperties: false
      properties:
        revoked:
          const: true
          type: boolean
      required:
        - revoked
      type: object
    MeUpdateRequest:
      additionalProperties: false
      properties:
        avatar_url:
          format: uri
          type: string
        locale:
          maxLength: 35
          minLength: 1
          type: string
        name:
          maxLength: 200
          minLength: 1
          type: string
        timezone:
          maxLength: 64
          minLength: 1
          type: string
      type: object
    MeUser:
      additionalProperties: false
      properties:
        avatar_url:
          anyOf:
            - format: uri
              type: string
            - type: "null"
        created_at:
          format: date-time
          pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
          type: string
        email:
          anyOf:
            - format: email
              pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
              type: string
            - type: "null"
        email_verified:
          type: boolean
        last_login_at:
          anyOf:
            - format: date-time
              pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
              type: string
            - type: "null"
        locale:
          anyOf:
            - type: string
            - type: "null"
        name:
          anyOf:
            - type: string
            - type: "null"
        status:
          enum:
            - active
            - blocked
            - pending_deletion
          type: string
        timezone:
          anyOf:
            - type: string
            - type: "null"
        user_id:
          pattern: ^usr_[a-z2-7]{32}$
          type: string
      required:
        - user_id
        - email
        - email_verified
        - name
        - avatar_url
        - locale
        - timezone
        - status
        - created_at
        - last_login_at
      type: object
    OidcDiscoveryDocument:
      additionalProperties: false
      properties:
        authorization_endpoint:
          format: uri
          type: string
        claims_supported:
          items:
            type: string
          type: array
        code_challenge_methods_supported:
          items:
            enum:
              - S256
            type: string
          type: array
        end_session_endpoint:
          format: uri
          type: string
        grant_types_supported:
          items:
            enum:
              - authorization_code
              - refresh_token
            type: string
          type: array
        id_token_signing_alg_values_supported:
          items:
            const: ES256
            type: string
          type: array
        introspection_endpoint:
          format: uri
          type: string
        issuer:
          format: uri
          type: string
        jwks_uri:
          format: uri
          type: string
        response_types_supported:
          items:
            enum:
              - code
            type: string
          type: array
        revocation_endpoint:
          format: uri
          type: string
        scopes_supported:
          items:
            type: string
          type: array
        subject_types_supported:
          items:
            const: public
            type: string
          type: array
        token_endpoint:
          format: uri
          type: string
        token_endpoint_auth_methods_supported:
          items:
            enum:
              - client_secret_basic
              - client_secret_post
              - none
            type: string
          type: array
        userinfo_endpoint:
          format: uri
          type: string
      required:
        - issuer
        - authorization_endpoint
        - token_endpoint
        - userinfo_endpoint
        - jwks_uri
        - revocation_endpoint
        - introspection_endpoint
        - end_session_endpoint
        - response_types_supported
        - grant_types_supported
        - code_challenge_methods_supported
        - token_endpoint_auth_methods_supported
        - scopes_supported
        - subject_types_supported
        - id_token_signing_alg_values_supported
        - claims_supported
      type: object
    PasskeyAuthenticationOptionsRequest:
      additionalProperties: false
      properties:
        email:
          format: email
          pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
          type: string
        rp:
          enum:
            - active
            - legacy
          type: string
        rp_id:
          minLength: 1
          type: string
      type: object
    PasskeyAuthenticationOptionsResponse:
      additionalProperties: false
      properties:
        allowCredentials:
          items:
            additionalProperties: false
            properties:
              id:
                minLength: 1
                pattern: ^[A-Za-z0-9_-]+$
                type: string
              transports:
                items:
                  enum:
                    - ble
                    - hybrid
                    - internal
                    - nfc
                    - usb
                  type: string
                type: array
              type:
                const: public-key
                type: string
            required:
              - id
              - type
            type: object
          type: array
        challenge:
          minLength: 1
          pattern: ^[A-Za-z0-9_-]+$
          type: string
        rpId:
          minLength: 1
          type: string
        timeout:
          exclusiveMinimum: 0
          maximum: 9007199254740991
          type: integer
        userVerification:
          const: required
          type: string
      required:
        - challenge
        - rpId
        - userVerification
      type: object
    PasskeyAuthenticationVerifyRequest:
      additionalProperties: false
      properties:
        credential:
          additionalProperties: false
          properties:
            authenticatorAttachment:
              enum:
                - platform
                - cross-platform
              type: string
            clientExtensionResults:
              additionalProperties: {}
              propertyNames:
                type: string
              type: object
            id:
              minLength: 1
              pattern: ^[A-Za-z0-9_-]+$
              type: string
            rawId:
              minLength: 1
              pattern: ^[A-Za-z0-9_-]+$
              type: string
            response:
              additionalProperties: false
              properties:
                authenticatorData:
                  minLength: 1
                  pattern: ^[A-Za-z0-9_-]+$
                  type: string
                clientDataJSON:
                  minLength: 1
                  pattern: ^[A-Za-z0-9_-]+$
                  type: string
                signature:
                  minLength: 1
                  pattern: ^[A-Za-z0-9_-]+$
                  type: string
                userHandle:
                  minLength: 1
                  pattern: ^[A-Za-z0-9_-]+$
                  type: string
              required:
                - clientDataJSON
                - authenticatorData
                - signature
              type: object
            type:
              const: public-key
              type: string
          required:
            - id
            - rawId
            - type
            - response
          type: object
      required:
        - credential
      type: object
    PasskeyRegistrationOptionsResponse:
      additionalProperties: false
      properties:
        attestation:
          const: none
          type: string
        authenticatorSelection:
          additionalProperties: false
          properties:
            authenticatorAttachment:
              enum:
                - platform
                - cross-platform
              type: string
            residentKey:
              const: required
              type: string
            userVerification:
              const: required
              type: string
          required:
            - residentKey
            - userVerification
          type: object
        challenge:
          minLength: 1
          pattern: ^[A-Za-z0-9_-]+$
          type: string
        excludeCredentials:
          items:
            additionalProperties: false
            properties:
              id:
                minLength: 1
                pattern: ^[A-Za-z0-9_-]+$
                type: string
              transports:
                items:
                  enum:
                    - ble
                    - hybrid
                    - internal
                    - nfc
                    - usb
                  type: string
                type: array
              type:
                const: public-key
                type: string
            required:
              - id
              - type
            type: object
          type: array
        pubKeyCredParams:
          items:
            additionalProperties: false
            properties:
              alg:
                maximum: 9007199254740991
                minimum: -9007199254740991
                type: integer
              type:
                const: public-key
                type: string
            required:
              - type
              - alg
            type: object
          minItems: 1
          type: array
        rp:
          additionalProperties: false
          properties:
            id:
              minLength: 1
              type: string
            name:
              minLength: 1
              type: string
          required:
            - id
            - name
          type: object
        timeout:
          exclusiveMinimum: 0
          maximum: 9007199254740991
          type: integer
        user:
          additionalProperties: false
          properties:
            displayName:
              minLength: 1
              type: string
            id:
              minLength: 1
              pattern: ^[A-Za-z0-9_-]+$
              type: string
            name:
              minLength: 1
              type: string
          required:
            - id
            - name
            - displayName
          type: object
      required:
        - rp
        - user
        - challenge
        - pubKeyCredParams
        - authenticatorSelection
        - attestation
      type: object
    PasskeyRegistrationVerifyRequest:
      additionalProperties: false
      properties:
        credential:
          additionalProperties: false
          properties:
            authenticatorAttachment:
              enum:
                - platform
                - cross-platform
              type: string
            clientExtensionResults:
              additionalProperties: {}
              propertyNames:
                type: string
              type: object
            id:
              minLength: 1
              pattern: ^[A-Za-z0-9_-]+$
              type: string
            rawId:
              minLength: 1
              pattern: ^[A-Za-z0-9_-]+$
              type: string
            response:
              additionalProperties: false
              properties:
                attestationObject:
                  minLength: 1
                  pattern: ^[A-Za-z0-9_-]+$
                  type: string
                authenticatorData:
                  minLength: 1
                  pattern: ^[A-Za-z0-9_-]+$
                  type: string
                clientDataJSON:
                  minLength: 1
                  pattern: ^[A-Za-z0-9_-]+$
                  type: string
                publicKey:
                  minLength: 1
                  pattern: ^[A-Za-z0-9_-]+$
                  type: string
                publicKeyAlgorithm:
                  maximum: 9007199254740991
                  minimum: -9007199254740991
                  type: integer
                transports:
                  items:
                    enum:
                      - ble
                      - hybrid
                      - internal
                      - nfc
                      - usb
                    type: string
                  type: array
              required:
                - clientDataJSON
                - attestationObject
              type: object
            type:
              const: public-key
              type: string
          required:
            - id
            - rawId
            - type
            - response
          type: object
        name:
          maxLength: 100
          minLength: 1
          type: string
      required:
        - credential
      type: object
    ProblemDetails:
      description: RFC 9457 Problem Details。`code` は機械可読な snake_case のエラーコード、`request_id` は全レスポンスに付与されるリクエストID (design.md §26.3 の例に準拠)。
      properties:
        code:
          description: 機械可読なエラーコード (snake_case)
          type: string
        detail:
          description: 今回の発生分に固有の詳細メッセージ
          type: string
        request_id:
          description: リクエストID (req_...)
          pattern: ^req_[a-z2-7]{32}$
          type: string
        status:
          maximum: 599
          minimum: 100
          type: integer
        title:
          description: 人間可読な短いタイトル
          type: string
        type:
          description: "問題タイプを識別する URI (例: https://docs.example-auth.com/errors/invalid-redirect-uri)"
          type: string
      required:
        - type
        - title
        - status
      type: object
    PublicConfigResponse:
      additionalProperties: false
      properties:
        available_methods:
          items:
            enum:
              - passkey
              - email_otp
              - google
              - apple
              - github
              - microsoft
              - generic_oidc
            type: string
          type: array
        issuer:
          format: uri
          type: string
        legal:
          additionalProperties: false
          properties:
            privacy_url:
              format: uri
              type: string
            terms_url:
              format: uri
              type: string
          type: object
        rp_id:
          minLength: 1
          type: string
        sdk_compatibility_version:
          exclusiveMinimum: 0
          maximum: 9007199254740991
          type: integer
        theme: {}
      required:
        - issuer
        - rp_id
        - available_methods
        - theme
        - sdk_compatibility_version
        - legal
      type: object
    RevocationRequest:
      additionalProperties: false
      properties:
        client_id:
          pattern: ^cli_[a-z2-7]{32}$
          type: string
        client_secret:
          maxLength: 512
          minLength: 1
          type: string
        token:
          minLength: 1
          type: string
        token_type_hint:
          enum:
            - access_token
            - refresh_token
          type: string
      required:
        - token
      type: object
    RevocationResponse:
      additionalProperties: false
      properties: {}
      type: object
    SocialCallbackRequest:
      properties:
        code:
          minLength: 1
          type: string
        error:
          minLength: 1
          type: string
        error_description:
          type: string
        state:
          maxLength: 512
          minLength: 1
          type: string
      required:
        - state
      type: object
    SocialStartResponse:
      additionalProperties: false
      properties:
        authorization_url:
          format: uri
          type: string
      required:
        - authorization_url
      type: object
    TokenRequest:
      oneOf:
        - additionalProperties: false
          properties:
            client_id:
              pattern: ^cli_[a-z2-7]{32}$
              type: string
            client_secret:
              maxLength: 512
              minLength: 1
              type: string
            code:
              pattern: ^ac_[a-z2-7]{32}$
              type: string
            code_verifier:
              pattern: ^[A-Za-z0-9._~-]{43,128}$
              type: string
            grant_type:
              const: authorization_code
              type: string
            redirect_uri:
              maxLength: 2048
              minLength: 1
              type: string
            resource:
              items:
                format: uri
                type: string
              maxItems: 20
              type: array
          required:
            - grant_type
            - code
            - redirect_uri
            - code_verifier
          type: object
        - additionalProperties: false
          properties:
            client_id:
              pattern: ^cli_[a-z2-7]{32}$
              type: string
            client_secret:
              maxLength: 512
              minLength: 1
              type: string
            grant_type:
              const: refresh_token
              type: string
            refresh_token:
              minLength: 1
              type: string
            resource:
              items:
                format: uri
                type: string
              maxItems: 20
              type: array
            scope:
              minLength: 1
              type: string
          required:
            - grant_type
            - refresh_token
          type: object
    TokenResponse:
      additionalProperties: false
      properties:
        access_token:
          minLength: 1
          type: string
        expires_in:
          exclusiveMinimum: 0
          maximum: 9007199254740991
          type: integer
        id_token:
          minLength: 1
          type: string
        refresh_token:
          minLength: 1
          type: string
        scope:
          minLength: 1
          type: string
        token_type:
          const: Bearer
          type: string
      required:
        - access_token
        - token_type
        - expires_in
        - scope
      type: object
    TransactionAuthResultResponse:
      additionalProperties: false
      properties:
        status:
          enum:
            - created
            - interaction_started
            - challenge_issued
            - link_required
            - authenticated
            - code_issued
            - consumed
            - failed
            - expired
          type: string
        transaction_id:
          pattern: ^txn_[a-z2-7]{32}$
          type: string
      required:
        - transaction_id
        - status
      type: object
    TransactionCancelResponse:
      additionalProperties: false
      properties:
        status:
          const: failed
          type: string
        transaction_id:
          pattern: ^txn_[a-z2-7]{32}$
          type: string
      required:
        - transaction_id
        - status
      type: object
    TransactionCompleteResponse:
      additionalProperties: false
      properties:
        authorization_code:
          pattern: ^ac_[a-z2-7]{32}$
          type: string
        redirect_uri:
          maxLength: 2048
          minLength: 1
          type: string
        state:
          maxLength: 512
          minLength: 1
          type: string
      required:
        - authorization_code
        - state
        - redirect_uri
      type: object
    TransactionCreateRequest:
      additionalProperties: false
      properties:
        client_id:
          pattern: ^cli_[a-z2-7]{32}$
          type: string
        code_challenge:
          pattern: ^[A-Za-z0-9_-]{43}$
          type: string
        code_challenge_method:
          const: S256
          type: string
        nonce:
          maxLength: 512
          minLength: 1
          type: string
        redirect_uri:
          maxLength: 2048
          minLength: 1
          type: string
        resource:
          items:
            format: uri
            type: string
          maxItems: 20
          type: array
        response_mode:
          enum:
            - redirect
            - native_return
          type: string
        scope:
          minLength: 1
          type: string
        state:
          maxLength: 512
          minLength: 1
          type: string
      required:
        - client_id
        - redirect_uri
        - code_challenge
        - code_challenge_method
        - state
        - scope
      type: object
    TransactionCreateResponse:
      additionalProperties: false
      properties:
        available_methods:
          items:
            enum:
              - passkey
              - email_otp
              - google
              - apple
              - github
              - microsoft
              - generic_oidc
            type: string
          type: array
        expires_at:
          format: date-time
          pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
          type: string
        transaction_id:
          pattern: ^txn_[a-z2-7]{32}$
          type: string
      required:
        - transaction_id
        - expires_at
        - available_methods
      type: object
    TransactionGetResponse:
      additionalProperties: false
      properties:
        available_methods:
          items:
            enum:
              - passkey
              - email_otp
              - google
              - apple
              - github
              - microsoft
              - generic_oidc
            type: string
          type: array
        expires_at:
          format: date-time
          pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
          type: string
        method:
          enum:
            - passkey
            - email_otp
            - social
          type: string
        pending_link:
          anyOf:
            - additionalProperties: false
              properties:
                provider:
                  enum:
                    - google
                    - apple
                    - github
                    - microsoft
                    - generic_oidc
                  type: string
              required:
                - provider
              type: object
            - type: "null"
        status:
          enum:
            - created
            - interaction_started
            - challenge_issued
            - link_required
            - authenticated
            - code_issued
            - consumed
            - failed
            - expired
          type: string
        transaction_id:
          pattern: ^txn_[a-z2-7]{32}$
          type: string
      required:
        - transaction_id
        - status
        - expires_at
        - available_methods
        - pending_link
      type: object
    TransactionPasskeyRegistrationVerifyResponse:
      additionalProperties: false
      properties:
        passkey:
          additionalProperties: false
          properties:
            backed_up:
              type: boolean
            created_at:
              format: date-time
              pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
              type: string
            device_type:
              anyOf:
                - enum:
                    - single_device
                    - multi_device
                  type: string
                - type: "null"
            last_used_at:
              anyOf:
                - format: date-time
                  pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                  type: string
                - type: "null"
            name:
              anyOf:
                - type: string
                - type: "null"
            passkey_id:
              pattern: ^psk_[a-z2-7]{32}$
              type: string
            transports:
              items:
                enum:
                  - ble
                  - hybrid
                  - internal
                  - nfc
                  - usb
                type: string
              type: array
          required:
            - passkey_id
            - name
            - created_at
            - last_used_at
            - device_type
            - backed_up
          type: object
        status:
          enum:
            - created
            - interaction_started
            - challenge_issued
            - link_required
            - authenticated
            - code_issued
            - consumed
            - failed
            - expired
          type: string
        transaction_id:
          pattern: ^txn_[a-z2-7]{32}$
          type: string
      required:
        - transaction_id
        - status
        - passkey
      type: object
    UserinfoResponse:
      additionalProperties: false
      properties:
        email:
          format: email
          pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
          type: string
        email_verified:
          type: boolean
        locale:
          type: string
        name:
          type: string
        sub:
          pattern: ^usr_[a-z2-7]{32}$
          type: string
        updated_at:
          maximum: 9007199254740991
          minimum: 0
          type: integer
      required:
        - sub
      type: object
  securitySchemes:
    BearerAuth:
      bearerFormat: JWT
      description: ES256 アクセストークン (design.md §15.6)。/v1/me と userinfo で必須。
      scheme: bearer
      type: http
    ClientBasic:
      description: confidential client (web_bff) の client_secret_basic (design.md §15.4)。public client (web_spa / react_native) には secret を発行しない。
      scheme: basic
      type: http
info:
  description: |-
    Hosted UI / Web Headless / React Native SDK が共通で利用する Public Authentication API (design.md §16) と、
    Authorization Server / OIDC エンドポイント (design.md §15) の OpenAPI 3.1 定義。

    - `/v1/...` は各 Environment の interaction domain で提供され、`/e/{environmentId}/v1/...` は同じ Public API の issuer-host alias。
    - `/.well-known/apple-app-site-association` と `/.well-known/assetlinks.json` は weak ETag (`W/"<config_version>"`) と `cache-control: public, max-age=300` を返し、`If-None-Match` に現行 ETag が含まれる場合は body のない 304 を返す。
    - すべてのエラーレスポンスは RFC 9457 Problem Details (`application/problem+json`) で返される。
    - すべてのレスポンスに `X-Request-Id` ヘッダが付与される。
  title: パスワードレス認証基盤 Public API
  version: 0.1.0
jsonSchemaDialect: https://json-schema.org/draft/2020-12/schema
openapi: 3.1.0
paths:
  /.well-known/apple-app-site-association:
    get:
      description: '登録済み iOS アプリから Apple association JSON を生成する (design.md §17.3)。`cache-control: public, max-age=300` と `ETag: W/"<config_version>"` を返し、`If-None-Match` に現在の ETag が含まれる場合は同じ cache / ETag headers と空 body の 304 を返す。'
      operationId: getAppleAppSiteAssociation
      parameters:
        - $ref: "#/components/parameters/IfNoneMatch"
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppleAppSiteAssociation"
          description: 登録済み iOS アプリの関連付け JSON
          headers:
            Cache-Control:
              $ref: "#/components/headers/Cache-Control"
            ETag:
              $ref: "#/components/headers/ETag"
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "304":
          description: If-None-Match に現在の弱い ETag が含まれる場合。レスポンス body はない。
          headers:
            Cache-Control:
              $ref: "#/components/headers/Cache-Control"
            ETag:
              $ref: "#/components/headers/ETag"
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: Apple App Site Association の取得
      tags:
        - Passkeys
  /.well-known/assetlinks.json:
    get:
      description: '登録済み Android アプリから Google 形式の bare JSON array を生成する (design.md §17.3)。`cache-control: public, max-age=300` と `ETag: W/"<config_version>"` を返し、`If-None-Match` に現在の ETag が含まれる場合は同じ cache / ETag headers と空 body の 304 を返す。'
      operationId: getAssetLinks
      parameters:
        - $ref: "#/components/parameters/IfNoneMatch"
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AssetLinks"
          description: 登録済み Android アプリの関連付け JSON array
          headers:
            Cache-Control:
              $ref: "#/components/headers/Cache-Control"
            ETag:
              $ref: "#/components/headers/ETag"
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "304":
          description: If-None-Match に現在の弱い ETag が含まれる場合。レスポンス body はない。
          headers:
            Cache-Control:
              $ref: "#/components/headers/Cache-Control"
            ETag:
              $ref: "#/components/headers/ETag"
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: Android assetlinks.json の取得
      tags:
        - Passkeys
  /e/{environmentId}/.well-known/openid-configuration:
    get:
      description: Environment 単位の OpenID Provider Configuration を返す (design.md §15.1)。署名アルゴリズムは ES256 のみ (§15.6)。
      operationId: getOidcConfiguration
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OidcDiscoveryDocument"
          description: OpenID Provider Configuration
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: OpenID Connect Discovery
      tags:
        - OIDC
  /e/{environmentId}/oauth2/authorize:
    get:
      description: Authorization Code Grant + PKCE S256 の認可リクエストを処理する (design.md §15.1, §15.2)。redirect_uri は登録値との byte-for-byte 完全一致 (§15.9)。成功時は Hosted Login またはクライアントの redirect_uri へ 302。`resource` (RFC 8707) はここで確定する (§15.7)。エラーは可能な限り redirect_uri へ `error` 付きでリダイレクトし、redirect できない場合は Problem Details を返す。
      operationId: authorize
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: "`code` 固定 (design.md §15.2)"
          in: query
          name: response_type
          required: true
          schema:
            enum:
              - code
            type: string
        - description: Client ID (cli_...)
          in: query
          name: client_id
          required: true
          schema:
            pattern: ^cli_[a-z2-7]{32}$
            type: string
        - description: 登録済み redirect_uri と完全一致が必要 (design.md §15.9)
          in: query
          name: redirect_uri
          required: true
          schema:
            maxLength: 2048
            minLength: 1
            type: string
        - description: スペース区切りの scope (openid profile email offline_access)
          in: query
          name: scope
          required: true
          schema:
            minLength: 1
            type: string
        - description: クライアント指定の opaque 値 (サーバは MAC のみ保存)
          in: query
          name: state
          required: true
          schema:
            maxLength: 512
            minLength: 1
            type: string
        - description: ID Token の replay 対策用 opaque 値
          in: query
          name: nonce
          required: false
          schema:
            maxLength: 512
            minLength: 1
            type: string
        - description: PKCE code challenge (S256)
          in: query
          name: code_challenge
          required: true
          schema:
            pattern: ^[A-Za-z0-9_-]{43}$
            type: string
        - description: "`S256` 固定 (`plain` は非対応、design.md §15.3)"
          in: query
          name: code_challenge_method
          required: true
          schema:
            enum:
              - S256
            type: string
        - description: RFC 8707 Resource Indicator。繰り返し指定可
          explode: true
          in: query
          name: resource
          required: false
          schema:
            items:
              format: uri
              type: string
            maxItems: 20
            type: array
          style: form
      responses:
        "302":
          description: Hosted Login または redirect_uri へのリダイレクト
          headers:
            Location:
              $ref: "#/components/headers/Location"
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: 認可エンドポイント
      tags:
        - OIDC
  /e/{environmentId}/oauth2/introspect:
    post:
      description: "token の有効性を検査する (RFC 7662, design.md §15.8)。署名・`exp`・`iss`・`aud`・`client_id` に加え、`UserSecurityDO` の block 状態と security version を照合し、失効済み session/family なら `active: false` を返す。confidential client または scope 付き Management API key のみ利用可能。"
      operationId: introspectToken
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/IntrospectionRequest"
          application/x-www-form-urlencoded:
            schema:
              $ref: "#/components/schemas/IntrospectionRequest"
        description: 検査対象の token
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IntrospectionResponse"
          description: イントロスペクション結果
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - ClientBasic: []
        - {}
      summary: トークンイントロスペクション
      tags:
        - OIDC
  /e/{environmentId}/oauth2/jwks:
    get:
      description: Environment 単位の署名公開鍵セット (ES256 / P-256) を返す (design.md §15.1, §15.6)。
      operationId: getJwks
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JwksDocument"
          description: JSON Web Key Set
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: JWKS エンドポイント
      tags:
        - OIDC
  /e/{environmentId}/oauth2/logout:
    get:
      description: RP-Initiated Logout を処理する (design.md §15.1, §15.2)。`post_logout_redirect_uri` が登録値と一致すれば `state` 付きで 302 リダイレクトする。
      operationId: logoutViaGet
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: ログアウト対象を特定する ID Token
          in: query
          name: id_token_hint
          required: false
          schema:
            type: string
        - description: 登録済みのログアウト後リダイレクト先
          in: query
          name: post_logout_redirect_uri
          required: false
          schema:
            maxLength: 2048
            minLength: 1
            type: string
        - description: クライアント指定の opaque 値 (リダイレクト時にそのまま返す)
          in: query
          name: state
          required: false
          schema:
            maxLength: 512
            minLength: 1
            type: string
        - description: Client ID (cli_...)
          in: query
          name: client_id
          required: false
          schema:
            pattern: ^cli_[a-z2-7]{32}$
            type: string
      responses:
        "302":
          description: post_logout_redirect_uri へのリダイレクト
          headers:
            Location:
              $ref: "#/components/headers/Location"
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: RP-Initiated Logout (GET)
      tags:
        - OIDC
    post:
      description: GET と同様に RP-Initiated Logout を処理する (design.md §15.1)。body でパラメータを受け取る。
      operationId: logoutViaPost
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LogoutRequest"
          application/x-www-form-urlencoded:
            schema:
              $ref: "#/components/schemas/LogoutRequest"
        description: ログアウトパラメータ
        required: true
      responses:
        "302":
          description: post_logout_redirect_uri へのリダイレクト
          headers:
            Location:
              $ref: "#/components/headers/Location"
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: RP-Initiated Logout (POST)
      tags:
        - OIDC
  /e/{environmentId}/oauth2/revoke:
    post:
      description: refresh token / access token を失効する (RFC 7009, design.md §15.1)。refresh token の revoke は family 全体を失効する (§15.8)。存在しない token に対しても成功レスポンスを返し、token の存在を漏らさない (§15.8)。
      operationId: revokeToken
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RevocationRequest"
          application/x-www-form-urlencoded:
            schema:
              $ref: "#/components/schemas/RevocationRequest"
        description: 失効対象の token
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RevocationResponse"
          description: 常に成功 (token の存在を漏らさないため空オブジェクト)
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - ClientBasic: []
        - {}
      summary: トークン失効エンドポイント
      tags:
        - OIDC
  /e/{environmentId}/oauth2/token:
    post:
      description: authorization code (TTL 60 秒) または refresh token を access token (TTL 5 分) 等へ交換する (design.md §15.5)。`web_bff` は `client_secret_basic` / `client_secret_post` + PKCE、`web_spa` / `react_native` は PKCE 必須 (§15.4)。`resource` / `scope` は元の grant の縮小のみ許可 (§15.7)。
      operationId: exchangeToken
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TokenRequest"
          application/x-www-form-urlencoded:
            schema:
              $ref: "#/components/schemas/TokenRequest"
        description: grant_type 別のトークンリクエスト (authorization_code / refresh_token)
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TokenResponse"
          description: 発行されたトークン
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - ClientBasic: []
        - {}
      summary: トークンエンドポイント
      tags:
        - OIDC
  /e/{environmentId}/oauth2/userinfo:
    get:
      description: access token (Bearer) に対応するユーザー情報を返す (design.md §15.1)。`email` は原則 access token に含まれず、ここか ID Token から取得する (§15.6)。
      operationId: getUserinfo
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserinfoResponse"
          description: ユーザー情報
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: UserInfo エンドポイント
      tags:
        - OIDC
  /e/{environmentId}/v1/auth/social/{provider}/callback:
    get:
      description: プロバイダからの OAuth2 リダイレクトを処理する (design.md §20.2)。署名済み `state` から Environment とトランザクションを復元するため、この route は `:transactionId` を持たない。成功後はクライアントの redirect 先へ 302 で戻る。
      operationId: socialLoginCallbackViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: ソーシャルログインプロバイダ (design.md §20.1)
          in: path
          name: provider
          required: true
          schema:
            enum:
              - google
              - apple
              - github
              - microsoft
              - generic_oidc
            type: string
        - description: 署名済み state (トランザクション復元用)
          in: query
          name: state
          required: true
          schema:
            maxLength: 512
            minLength: 1
            type: string
        - description: プロバイダの authorization code
          in: query
          name: code
          required: false
          schema:
            minLength: 1
            type: string
        - description: プロバイダ側エラーコード
          in: query
          name: error
          required: false
          schema:
            minLength: 1
            type: string
        - description: プロバイダ側エラー詳細
          in: query
          name: error_description
          required: false
          schema:
            type: string
      responses:
        "302":
          description: クライアントの redirect 先へのリダイレクト
          headers:
            Location:
              $ref: "#/components/headers/Location"
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: ソーシャルログインのコールバック
      tags:
        - Social
    post:
      description: GET と同じ OAuth2 コールバック処理を POST でも受け付ける (design.md §20.2)。`application/x-www-form-urlencoded` または JSON body から `state`・`code`・エラー項目を受け取り、成功後はクライアントの redirect 先へ 302 で戻る。
      operationId: socialLoginCallbackPostViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: ソーシャルログインプロバイダ (design.md §20.1)
          in: path
          name: provider
          required: true
          schema:
            enum:
              - google
              - apple
              - github
              - microsoft
              - generic_oidc
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SocialCallbackRequest"
          application/x-www-form-urlencoded:
            schema:
              $ref: "#/components/schemas/SocialCallbackRequest"
        description: プロバイダからの OAuth2 callback パラメータ
        required: true
      responses:
        "302":
          description: クライアントの redirect 先へのリダイレクト
          headers:
            Location:
              $ref: "#/components/headers/Location"
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: ソーシャルログインのコールバック
      tags:
        - Social
  /e/{environmentId}/v1/auth/transactions:
    post:
      description: Hosted UI / Web Headless / React Native を統一する認証トランザクションを作成する (design.md §16.1)。PKCE は S256 のみ (`plain` は非対応、§15.3)。`state` と `nonce` はサーバに MAC のみ保存される。`resource` (RFC 8707) はここで確定し、token endpoint では縮小のみ許可される (§15.7)。
      operationId: createTransactionViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TransactionCreateRequest"
        description: トランザクション作成パラメータ (design.md §16.1 の例に一致)
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionCreateResponse"
          description: 作成されたトランザクション
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: 認証トランザクションの作成
      tags:
        - Transactions
  /e/{environmentId}/v1/auth/transactions/{transactionId}:
    get:
      description: トランザクションの現在状態 (design.md §14.2 の status machine) を返す。Hosted UI / React Native のポーリング用。
      operationId: getTransactionViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionGetResponse"
          description: トランザクション状態
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: トランザクション状態の取得
      tags:
        - Transactions
  /e/{environmentId}/v1/auth/transactions/{transactionId}/cancel:
    post:
      description: トランザクションをキャンセルする。キャンセル後のトランザクションは `failed` へ遷移し再開できない (design.md §14.2)。リクエストボディはない。
      operationId: cancelTransactionViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionCancelResponse"
          description: キャンセル結果
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: トランザクションのキャンセル
      tags:
        - Transactions
  /e/{environmentId}/v1/auth/transactions/{transactionId}/complete:
    post:
      description: 認証成功後、1 回限りの authorization code を返す (design.md §16.3)。トランザクションは事前にいずれかの verify 系エンドポイントで `authenticated` へ進んでいる必要がある。React Native SDK はこの code を token endpoint へ交換する。リクエストボディはない。
      operationId: completeTransactionViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionCompleteResponse"
          description: 1 回限りの authorization code
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: トランザクションの完了
      tags:
        - Transactions
  /e/{environmentId}/v1/auth/transactions/{transactionId}/email/resend:
    post:
      description: OTP を再送する。generation がインクリメントされ、以前のコードは無効化される (design.md §18.2)。存在有無に関わらず同一レスポンス (§18.3)。
      operationId: resendEmailOtpViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EmailResendRequest"
        description: start が返した challenge_id
        required: true
      responses:
        "202":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailChallengeResponse"
          description: 新しい OTP チャレンジ
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: メールOTPの再送
      tags:
        - Email OTP
  /e/{environmentId}/v1/auth/transactions/{transactionId}/email/start:
    post:
      description: 指定メールアドレスへ 6 桁の OTP を送信する (design.md §18)。アカウント列挙対策として、存在するメールと存在しないメールで同一の HTTP status と body を返す (§18.3)。
      operationId: startEmailOtpViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EmailStartRequest"
        description: OTP 送信先メールアドレス
        required: true
      responses:
        "202":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailChallengeResponse"
          description: OTP チャレンジ (challenge_id は resend/verify と共有)
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: メールOTPチャレンジの開始
      tags:
        - Email OTP
  /e/{environmentId}/v1/auth/transactions/{transactionId}/email/verify:
    post:
      description: OTP コードを検証する。成功するとトランザクションは `authenticated` へ進む。access/refresh token はここでは返さず (design.md §16.3)、`POST .../complete` が 1 回限りの authorization code を返す。既知のメールアドレスでない新規ユーザー作成が §28.5 のゲートで拒否されると 403 `new_user_creation_blocked` (この場合 OTP チャレンジは消費済みのため、クライアントは新しいコードを要求し直す)。
      operationId: verifyEmailOtpViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EmailVerifyRequest"
        description: challenge_id と 6 桁コード
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionAuthResultResponse"
          description: トランザクション状態の ack
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: メールOTPの検証
      tags:
        - Email OTP
  /e/{environmentId}/v1/auth/transactions/{transactionId}/passkeys/authentication/options:
    post:
      description: WebAuthn 認証オプションを発行する (design.md §17.2)。ユーザー名なしの discoverable credential フローが標準。任意の `email` による allowCredentials 絞り込みはアカウント列挙を起こさないレスポンスで提供される。
      operationId: createPasskeyAuthenticationOptionsViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PasskeyAuthenticationOptionsRequest"
        description: 任意のメールアドレスによる絞り込み
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PasskeyAuthenticationOptionsResponse"
          description: WebAuthn 認証オプション
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: パスキー認証オプションの発行
      tags:
        - Passkeys
  /e/{environmentId}/v1/auth/transactions/{transactionId}/passkeys/authentication/verify:
    post:
      description: 認証 assertion を検証する。成功するとトランザクションは `authenticated` へ進む。access/refresh token はここでは返さない (design.md §16.3)。sign counter は異常検知信号として扱い、counter 挙動だけでは即 block しない (§17.1)。
      operationId: verifyPasskeyAuthenticationViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PasskeyAuthenticationVerifyRequest"
        description: authenticator の認証レスポンス
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionAuthResultResponse"
          description: トランザクション状態の ack
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: パスキー認証の検証
      tags:
        - Passkeys
  /e/{environmentId}/v1/auth/transactions/{transactionId}/passkeys/registration/options:
    post:
      description: 'WebAuthn 登録オプションを発行する (design.md §17.1)。`residentKey: "required"`、`userVerification: "required"`、`attestation: "none"` で固定。user handle にメールアドレスは含まれない。登録には既存の fresh session または直前の Email OTP / Social 再認証が必要。リクエストボディはない。'
      operationId: createPasskeyRegistrationOptionsViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PasskeyRegistrationOptionsResponse"
          description: WebAuthn 登録オプション
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: パスキー登録オプションの発行
      tags:
        - Passkeys
  /e/{environmentId}/v1/auth/transactions/{transactionId}/passkeys/registration/verify:
    post:
      description: "登録 credential を検証する (design.md §17.1: challenge・origin・RP ID・UP・UV・signature を検証し、challenge は成否に関わらず再利用不可)。"
      operationId: verifyPasskeyRegistrationViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PasskeyRegistrationVerifyRequest"
        description: authenticator の登録レスポンスと任意のラベル
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionPasskeyRegistrationVerifyResponse"
          description: 登録されたパスキーとトランザクション状態
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: パスキー登録の検証
      tags:
        - Passkeys
  /e/{environmentId}/v1/auth/transactions/{transactionId}/social/{provider}/start:
    post:
      description: プロバイダの認可 URL を返す (design.md §20)。クライアントはシステムブラウザで遷移する (embedded WebView は禁止、§20.6)。リクエストボディはない。
      operationId: startSocialLoginViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
        - description: ソーシャルログインプロバイダ (design.md §20.1)
          in: path
          name: provider
          required: true
          schema:
            enum:
              - google
              - apple
              - github
              - microsoft
              - generic_oidc
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SocialStartResponse"
          description: 遷移先の認可 URL
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: ソーシャルログインの開始
      tags:
        - Social
  /e/{environmentId}/v1/config:
    get:
      description: client_id と現在の Host から、公開可能な Environment 設定・利用可能な認証方式・theme・issuer・RP ID・SDK 互換 version を返す (design.md §16.2)。secret・provider client secret・内部 shard 名は一切返さない。
      operationId: getPublicConfigViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: Client ID (cli_...)。Environment 自体は Host ヘッダから解決される (design.md §8.2)。
          in: query
          name: client_id
          required: true
          schema:
            pattern: ^cli_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicConfigResponse"
          description: 公開設定
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: 公開設定の取得
      tags:
        - Config
  /e/{environmentId}/v1/me:
    delete:
      description: アカウントを論理削除 (`pending_deletion`) へ移行する (design.md §33)。fresh session が必要 (§24.4)。リクエストボディはない。
      operationId: deleteMeViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeDeleteResponse"
          description: 削除受付結果
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: アカウントの削除
      tags:
        - Me
    get:
      description: 認証中ユーザーのプロフィールを返す (design.md §16.2)。`security_version` や token ciphertext 等の内部カラムは投影されない。
      operationId: getMeViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeUser"
          description: ユーザープロフィール
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: 自ユーザー情報の取得
      tags:
        - Me
    patch:
      description: 表示名・アバター・locale・timezone を更新する。メールアドレスの変更はこの route では行えず、`POST /v1/me/email/change/start` による新旧両メールの検証フロー (design.md §25) を使う。
      operationId: updateMeViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MeUpdateRequest"
        description: 更新するプロフィール項目
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeUser"
          description: 更新後のユーザープロフィール
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: 自ユーザー情報の更新
      tags:
        - Me
  /e/{environmentId}/v1/me/email/change/start:
    post:
      description: メールアドレス変更フローを開始する (design.md §25)。現在 (旧) メールアドレス宛に所有確認の OTP challenge を送る。
      operationId: startMeEmailChangeViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MeEmailChangeStartRequest"
        description: 新しいメールアドレス
        required: true
      responses:
        "202":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailChallengeResponse"
          description: 旧メールアドレス宛の OTP チャレンジ
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: メールアドレス変更の開始
      tags:
        - Me
  /e/{environmentId}/v1/me/email/change/verify-new:
    post:
      description: 新メールアドレス宛の OTP を検証し、メールアドレス変更を確定する (design.md §25)。新旧両方の検証が必須。
      operationId: verifyMeEmailChangeNewViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MeEmailChangeVerifyNewRequest"
        description: 新アドレス宛 challenge_id と 6 桁コード
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeEmailChangeVerifyNewResponse"
          description: 変更後のメールアドレス
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: "メールアドレス変更: 新アドレスの検証"
      tags:
        - Me
  /e/{environmentId}/v1/me/email/change/verify-old:
    post:
      description: 旧メールアドレス宛の OTP を検証する (design.md §25)。成功すると新メールアドレス宛に次の OTP challenge が送られる。
      operationId: verifyMeEmailChangeOldViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MeEmailChangeVerifyOldRequest"
        description: 旧アドレス宛 challenge_id と 6 桁コード
        required: true
      responses:
        "202":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailChallengeResponse"
          description: 新メールアドレス宛の OTP チャレンジ
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: "メールアドレス変更: 旧アドレスの検証"
      tags:
        - Me
  /e/{environmentId}/v1/me/identities:
    get:
      description: リンク済みのソーシャルアイデンティティ一覧を返す (design.md §20.5)。
      operationId: listMeIdentitiesViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeIdentitiesListResponse"
          description: アイデンティティ一覧
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: リンク済み外部アイデンティティ一覧の取得
      tags:
        - Me
  /e/{environmentId}/v1/me/identities/{identityId}:
    delete:
      description: 指定アイデンティティのリンクを解除する (design.md §20.5)。
      operationId: unlinkMeIdentityViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: 外部アイデンティティID (idn_...)
          in: path
          name: identityId
          required: true
          schema:
            pattern: ^idn_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeIdentityUnlinkResponse"
          description: 解除結果
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: 外部アイデンティティのリンク解除
      tags:
        - Me
  /e/{environmentId}/v1/me/identities/{provider}/link:
    post:
      description: プロバイダとのリンク用 OAuth ラウンドトリップを開始する。完了後にユーザーを戻す `redirect_uri` を指定する (アプリコンテキストのリンクのため、トランザクション作成時の redirect_uri とは別に指定する)。
      operationId: linkMeIdentityViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: ソーシャルログインプロバイダ (design.md §20.1)
          in: path
          name: provider
          required: true
          schema:
            enum:
              - google
              - apple
              - github
              - microsoft
              - generic_oidc
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MeIdentityLinkRequest"
        description: リンク完了後の戻り先
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SocialStartResponse"
          description: 遷移先の認可 URL
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: 外部アイデンティティのリンク開始
      tags:
        - Me
  /e/{environmentId}/v1/me/passkeys:
    get:
      description: 認証中ユーザーのパスキー一覧を返す (design.md §12.5)。`credential_id`・`public_key`・`sign_count` は含まれない。
      operationId: listMePasskeysViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MePasskeysListResponse"
          description: パスキー一覧
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: 登録パスキー一覧の取得
      tags:
        - Me
  /e/{environmentId}/v1/me/passkeys/registration/options:
    post:
      description: 認証済みセッションからの追加パスキー登録用 WebAuthn オプションを発行する (design.md §17.1 と同一の固定設定)。リクエストボディはない。
      operationId: createMePasskeyRegistrationOptionsViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PasskeyRegistrationOptionsResponse"
          description: WebAuthn 登録オプション
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: パスキー追加登録オプションの発行
      tags:
        - Me
  /e/{environmentId}/v1/me/passkeys/registration/verify:
    post:
      description: 追加登録 credential を検証する (design.md §17.1)。
      operationId: verifyMePasskeyRegistrationViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PasskeyRegistrationVerifyRequest"
        description: authenticator の登録レスポンスと任意のラベル
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MePasskeyRegistrationVerifyResponse"
          description: 登録されたパスキー
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: パスキー追加登録の検証
      tags:
        - Me
  /e/{environmentId}/v1/me/passkeys/{passkeyId}:
    delete:
      description: 指定パスキーを削除する (design.md §12.5)。
      operationId: deleteMePasskeyViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: パスキーID (psk_...)
          in: path
          name: passkeyId
          required: true
          schema:
            pattern: ^psk_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MePasskeyDeleteResponse"
          description: 削除結果
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: パスキーの削除
      tags:
        - Me
  /e/{environmentId}/v1/me/sessions:
    delete:
      description: 全セッションを失効する。`UserSecurityDO.securityVersion` が increment され、既発行 token も無効化される (design.md §24.3)。
      operationId: revokeAllMeSessionsViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeSessionsRevokeAllResponse"
          description: 失効結果
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: 全セッションの失効
      tags:
        - Me
    get:
      description: 有効なセッション一覧を返す (design.md §12.4, §24)。`current` は現在のリクエストに使われたセッションを示す。
      operationId: listMeSessionsViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeSessionsListResponse"
          description: セッション一覧
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: セッション一覧の取得
      tags:
        - Me
  /e/{environmentId}/v1/me/sessions/{sessionId}:
    delete:
      description: 指定セッションを失効する (design.md §24)。
      operationId: revokeMeSessionViaIssuer
      parameters:
        - description: Environment ID (env_...)
          in: path
          name: environmentId
          required: true
          schema:
            pattern: ^env_[a-z2-7]{32}$
            type: string
        - description: セッションID (ses_...)
          in: path
          name: sessionId
          required: true
          schema:
            pattern: ^ses_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeSessionRevokeResponse"
          description: 失効結果
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: 個別セッションの失効
      tags:
        - Me
  /v1/auth/social/{provider}/callback:
    get:
      description: プロバイダからの OAuth2 リダイレクトを処理する (design.md §20.2)。署名済み `state` から Environment とトランザクションを復元するため、この route は `:transactionId` を持たない。成功後はクライアントの redirect 先へ 302 で戻る。
      operationId: socialLoginCallback
      parameters:
        - description: ソーシャルログインプロバイダ (design.md §20.1)
          in: path
          name: provider
          required: true
          schema:
            enum:
              - google
              - apple
              - github
              - microsoft
              - generic_oidc
            type: string
        - description: 署名済み state (トランザクション復元用)
          in: query
          name: state
          required: true
          schema:
            maxLength: 512
            minLength: 1
            type: string
        - description: プロバイダの authorization code
          in: query
          name: code
          required: false
          schema:
            minLength: 1
            type: string
        - description: プロバイダ側エラーコード
          in: query
          name: error
          required: false
          schema:
            minLength: 1
            type: string
        - description: プロバイダ側エラー詳細
          in: query
          name: error_description
          required: false
          schema:
            type: string
      responses:
        "302":
          description: クライアントの redirect 先へのリダイレクト
          headers:
            Location:
              $ref: "#/components/headers/Location"
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: ソーシャルログインのコールバック
      tags:
        - Social
    post:
      description: GET と同じ OAuth2 コールバック処理を POST でも受け付ける (design.md §20.2)。`application/x-www-form-urlencoded` または JSON body から `state`・`code`・エラー項目を受け取り、成功後はクライアントの redirect 先へ 302 で戻る。
      operationId: socialLoginCallbackPost
      parameters:
        - description: ソーシャルログインプロバイダ (design.md §20.1)
          in: path
          name: provider
          required: true
          schema:
            enum:
              - google
              - apple
              - github
              - microsoft
              - generic_oidc
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SocialCallbackRequest"
          application/x-www-form-urlencoded:
            schema:
              $ref: "#/components/schemas/SocialCallbackRequest"
        description: プロバイダからの OAuth2 callback パラメータ
        required: true
      responses:
        "302":
          description: クライアントの redirect 先へのリダイレクト
          headers:
            Location:
              $ref: "#/components/headers/Location"
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: ソーシャルログインのコールバック
      tags:
        - Social
  /v1/auth/transactions:
    post:
      description: Hosted UI / Web Headless / React Native を統一する認証トランザクションを作成する (design.md §16.1)。PKCE は S256 のみ (`plain` は非対応、§15.3)。`state` と `nonce` はサーバに MAC のみ保存される。`resource` (RFC 8707) はここで確定し、token endpoint では縮小のみ許可される (§15.7)。
      operationId: createTransaction
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TransactionCreateRequest"
        description: トランザクション作成パラメータ (design.md §16.1 の例に一致)
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionCreateResponse"
          description: 作成されたトランザクション
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: 認証トランザクションの作成
      tags:
        - Transactions
  /v1/auth/transactions/{transactionId}:
    get:
      description: トランザクションの現在状態 (design.md §14.2 の status machine) を返す。Hosted UI / React Native のポーリング用。
      operationId: getTransaction
      parameters:
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionGetResponse"
          description: トランザクション状態
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: トランザクション状態の取得
      tags:
        - Transactions
  /v1/auth/transactions/{transactionId}/cancel:
    post:
      description: トランザクションをキャンセルする。キャンセル後のトランザクションは `failed` へ遷移し再開できない (design.md §14.2)。リクエストボディはない。
      operationId: cancelTransaction
      parameters:
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionCancelResponse"
          description: キャンセル結果
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: トランザクションのキャンセル
      tags:
        - Transactions
  /v1/auth/transactions/{transactionId}/complete:
    post:
      description: 認証成功後、1 回限りの authorization code を返す (design.md §16.3)。トランザクションは事前にいずれかの verify 系エンドポイントで `authenticated` へ進んでいる必要がある。React Native SDK はこの code を token endpoint へ交換する。リクエストボディはない。
      operationId: completeTransaction
      parameters:
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionCompleteResponse"
          description: 1 回限りの authorization code
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: トランザクションの完了
      tags:
        - Transactions
  /v1/auth/transactions/{transactionId}/email/resend:
    post:
      description: OTP を再送する。generation がインクリメントされ、以前のコードは無効化される (design.md §18.2)。存在有無に関わらず同一レスポンス (§18.3)。
      operationId: resendEmailOtp
      parameters:
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EmailResendRequest"
        description: start が返した challenge_id
        required: true
      responses:
        "202":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailChallengeResponse"
          description: 新しい OTP チャレンジ
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: メールOTPの再送
      tags:
        - Email OTP
  /v1/auth/transactions/{transactionId}/email/start:
    post:
      description: 指定メールアドレスへ 6 桁の OTP を送信する (design.md §18)。アカウント列挙対策として、存在するメールと存在しないメールで同一の HTTP status と body を返す (§18.3)。
      operationId: startEmailOtp
      parameters:
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EmailStartRequest"
        description: OTP 送信先メールアドレス
        required: true
      responses:
        "202":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailChallengeResponse"
          description: OTP チャレンジ (challenge_id は resend/verify と共有)
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: メールOTPチャレンジの開始
      tags:
        - Email OTP
  /v1/auth/transactions/{transactionId}/email/verify:
    post:
      description: OTP コードを検証する。成功するとトランザクションは `authenticated` へ進む。access/refresh token はここでは返さず (design.md §16.3)、`POST .../complete` が 1 回限りの authorization code を返す。既知のメールアドレスでない新規ユーザー作成が §28.5 のゲートで拒否されると 403 `new_user_creation_blocked` (この場合 OTP チャレンジは消費済みのため、クライアントは新しいコードを要求し直す)。
      operationId: verifyEmailOtp
      parameters:
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EmailVerifyRequest"
        description: challenge_id と 6 桁コード
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionAuthResultResponse"
          description: トランザクション状態の ack
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: メールOTPの検証
      tags:
        - Email OTP
  /v1/auth/transactions/{transactionId}/passkeys/authentication/options:
    post:
      description: WebAuthn 認証オプションを発行する (design.md §17.2)。ユーザー名なしの discoverable credential フローが標準。任意の `email` による allowCredentials 絞り込みはアカウント列挙を起こさないレスポンスで提供される。
      operationId: createPasskeyAuthenticationOptions
      parameters:
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PasskeyAuthenticationOptionsRequest"
        description: 任意のメールアドレスによる絞り込み
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PasskeyAuthenticationOptionsResponse"
          description: WebAuthn 認証オプション
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: パスキー認証オプションの発行
      tags:
        - Passkeys
  /v1/auth/transactions/{transactionId}/passkeys/authentication/verify:
    post:
      description: 認証 assertion を検証する。成功するとトランザクションは `authenticated` へ進む。access/refresh token はここでは返さない (design.md §16.3)。sign counter は異常検知信号として扱い、counter 挙動だけでは即 block しない (§17.1)。
      operationId: verifyPasskeyAuthentication
      parameters:
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PasskeyAuthenticationVerifyRequest"
        description: authenticator の認証レスポンス
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionAuthResultResponse"
          description: トランザクション状態の ack
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: パスキー認証の検証
      tags:
        - Passkeys
  /v1/auth/transactions/{transactionId}/passkeys/registration/options:
    post:
      description: 'WebAuthn 登録オプションを発行する (design.md §17.1)。`residentKey: "required"`、`userVerification: "required"`、`attestation: "none"` で固定。user handle にメールアドレスは含まれない。登録には既存の fresh session または直前の Email OTP / Social 再認証が必要。リクエストボディはない。'
      operationId: createPasskeyRegistrationOptions
      parameters:
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PasskeyRegistrationOptionsResponse"
          description: WebAuthn 登録オプション
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: パスキー登録オプションの発行
      tags:
        - Passkeys
  /v1/auth/transactions/{transactionId}/passkeys/registration/verify:
    post:
      description: "登録 credential を検証する (design.md §17.1: challenge・origin・RP ID・UP・UV・signature を検証し、challenge は成否に関わらず再利用不可)。"
      operationId: verifyPasskeyRegistration
      parameters:
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PasskeyRegistrationVerifyRequest"
        description: authenticator の登録レスポンスと任意のラベル
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionPasskeyRegistrationVerifyResponse"
          description: 登録されたパスキーとトランザクション状態
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: パスキー登録の検証
      tags:
        - Passkeys
  /v1/auth/transactions/{transactionId}/social/{provider}/start:
    post:
      description: プロバイダの認可 URL を返す (design.md §20)。クライアントはシステムブラウザで遷移する (embedded WebView は禁止、§20.6)。リクエストボディはない。
      operationId: startSocialLogin
      parameters:
        - description: 認証トランザクションID (txn_...)
          in: path
          name: transactionId
          required: true
          schema:
            pattern: ^txn_[a-z2-7]{32}$
            type: string
        - description: ソーシャルログインプロバイダ (design.md §20.1)
          in: path
          name: provider
          required: true
          schema:
            enum:
              - google
              - apple
              - github
              - microsoft
              - generic_oidc
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SocialStartResponse"
          description: 遷移先の認可 URL
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: ソーシャルログインの開始
      tags:
        - Social
  /v1/config:
    get:
      description: client_id と現在の Host から、公開可能な Environment 設定・利用可能な認証方式・theme・issuer・RP ID・SDK 互換 version を返す (design.md §16.2)。secret・provider client secret・内部 shard 名は一切返さない。
      operationId: getPublicConfig
      parameters:
        - description: Client ID (cli_...)。Environment 自体は Host ヘッダから解決される (design.md §8.2)。
          in: query
          name: client_id
          required: true
          schema:
            pattern: ^cli_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicConfigResponse"
          description: 公開設定
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      summary: 公開設定の取得
      tags:
        - Config
  /v1/me:
    delete:
      description: アカウントを論理削除 (`pending_deletion`) へ移行する (design.md §33)。fresh session が必要 (§24.4)。リクエストボディはない。
      operationId: deleteMe
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeDeleteResponse"
          description: 削除受付結果
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: アカウントの削除
      tags:
        - Me
    get:
      description: 認証中ユーザーのプロフィールを返す (design.md §16.2)。`security_version` や token ciphertext 等の内部カラムは投影されない。
      operationId: getMe
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeUser"
          description: ユーザープロフィール
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: 自ユーザー情報の取得
      tags:
        - Me
    patch:
      description: 表示名・アバター・locale・timezone を更新する。メールアドレスの変更はこの route では行えず、`POST /v1/me/email/change/start` による新旧両メールの検証フロー (design.md §25) を使う。
      operationId: updateMe
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MeUpdateRequest"
        description: 更新するプロフィール項目
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeUser"
          description: 更新後のユーザープロフィール
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: 自ユーザー情報の更新
      tags:
        - Me
  /v1/me/email/change/start:
    post:
      description: メールアドレス変更フローを開始する (design.md §25)。現在 (旧) メールアドレス宛に所有確認の OTP challenge を送る。
      operationId: startMeEmailChange
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MeEmailChangeStartRequest"
        description: 新しいメールアドレス
        required: true
      responses:
        "202":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailChallengeResponse"
          description: 旧メールアドレス宛の OTP チャレンジ
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: メールアドレス変更の開始
      tags:
        - Me
  /v1/me/email/change/verify-new:
    post:
      description: 新メールアドレス宛の OTP を検証し、メールアドレス変更を確定する (design.md §25)。新旧両方の検証が必須。
      operationId: verifyMeEmailChangeNew
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MeEmailChangeVerifyNewRequest"
        description: 新アドレス宛 challenge_id と 6 桁コード
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeEmailChangeVerifyNewResponse"
          description: 変更後のメールアドレス
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: "メールアドレス変更: 新アドレスの検証"
      tags:
        - Me
  /v1/me/email/change/verify-old:
    post:
      description: 旧メールアドレス宛の OTP を検証する (design.md §25)。成功すると新メールアドレス宛に次の OTP challenge が送られる。
      operationId: verifyMeEmailChangeOld
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MeEmailChangeVerifyOldRequest"
        description: 旧アドレス宛 challenge_id と 6 桁コード
        required: true
      responses:
        "202":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailChallengeResponse"
          description: 新メールアドレス宛の OTP チャレンジ
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: "メールアドレス変更: 旧アドレスの検証"
      tags:
        - Me
  /v1/me/identities:
    get:
      description: リンク済みのソーシャルアイデンティティ一覧を返す (design.md §20.5)。
      operationId: listMeIdentities
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeIdentitiesListResponse"
          description: アイデンティティ一覧
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: リンク済み外部アイデンティティ一覧の取得
      tags:
        - Me
  /v1/me/identities/{identityId}:
    delete:
      description: 指定アイデンティティのリンクを解除する (design.md §20.5)。
      operationId: unlinkMeIdentity
      parameters:
        - description: 外部アイデンティティID (idn_...)
          in: path
          name: identityId
          required: true
          schema:
            pattern: ^idn_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeIdentityUnlinkResponse"
          description: 解除結果
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: 外部アイデンティティのリンク解除
      tags:
        - Me
  /v1/me/identities/{provider}/link:
    post:
      description: プロバイダとのリンク用 OAuth ラウンドトリップを開始する。完了後にユーザーを戻す `redirect_uri` を指定する (アプリコンテキストのリンクのため、トランザクション作成時の redirect_uri とは別に指定する)。
      operationId: linkMeIdentity
      parameters:
        - description: ソーシャルログインプロバイダ (design.md §20.1)
          in: path
          name: provider
          required: true
          schema:
            enum:
              - google
              - apple
              - github
              - microsoft
              - generic_oidc
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MeIdentityLinkRequest"
        description: リンク完了後の戻り先
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SocialStartResponse"
          description: 遷移先の認可 URL
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: 外部アイデンティティのリンク開始
      tags:
        - Me
  /v1/me/passkeys:
    get:
      description: 認証中ユーザーのパスキー一覧を返す (design.md §12.5)。`credential_id`・`public_key`・`sign_count` は含まれない。
      operationId: listMePasskeys
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MePasskeysListResponse"
          description: パスキー一覧
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: 登録パスキー一覧の取得
      tags:
        - Me
  /v1/me/passkeys/registration/options:
    post:
      description: 認証済みセッションからの追加パスキー登録用 WebAuthn オプションを発行する (design.md §17.1 と同一の固定設定)。リクエストボディはない。
      operationId: createMePasskeyRegistrationOptions
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PasskeyRegistrationOptionsResponse"
          description: WebAuthn 登録オプション
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: パスキー追加登録オプションの発行
      tags:
        - Me
  /v1/me/passkeys/registration/verify:
    post:
      description: 追加登録 credential を検証する (design.md §17.1)。
      operationId: verifyMePasskeyRegistration
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PasskeyRegistrationVerifyRequest"
        description: authenticator の登録レスポンスと任意のラベル
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MePasskeyRegistrationVerifyResponse"
          description: 登録されたパスキー
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: パスキー追加登録の検証
      tags:
        - Me
  /v1/me/passkeys/{passkeyId}:
    delete:
      description: 指定パスキーを削除する (design.md §12.5)。
      operationId: deleteMePasskey
      parameters:
        - description: パスキーID (psk_...)
          in: path
          name: passkeyId
          required: true
          schema:
            pattern: ^psk_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MePasskeyDeleteResponse"
          description: 削除結果
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: パスキーの削除
      tags:
        - Me
  /v1/me/sessions:
    delete:
      description: 全セッションを失効する。`UserSecurityDO.securityVersion` が increment され、既発行 token も無効化される (design.md §24.3)。
      operationId: revokeAllMeSessions
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeSessionsRevokeAllResponse"
          description: 失効結果
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: 全セッションの失効
      tags:
        - Me
    get:
      description: 有効なセッション一覧を返す (design.md §12.4, §24)。`current` は現在のリクエストに使われたセッションを示す。
      operationId: listMeSessions
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeSessionsListResponse"
          description: セッション一覧
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: セッション一覧の取得
      tags:
        - Me
  /v1/me/sessions/{sessionId}:
    delete:
      description: 指定セッションを失効する (design.md §24)。
      operationId: revokeMeSession
      parameters:
        - description: セッションID (ses_...)
          in: path
          name: sessionId
          required: true
          schema:
            pattern: ^ses_[a-z2-7]{32}$
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeSessionRevokeResponse"
          description: 失効結果
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        default:
          $ref: "#/components/responses/DefaultError"
      security:
        - BearerAuth: []
      summary: 個別セッションの失効
      tags:
        - Me
servers:
  - description: issuer / interaction host (Environment ごとの interaction host は設定に依存。`/v1` は interaction domain、`/e/{environmentId}/v1` はその issuer-host API alias、`/e/{environmentId}` は OIDC を提供)。
    url: https://id.example-auth.com
tags:
  - description: クライアント公開設定 (design.md §16.2)
    name: Config
  - description: 認証トランザクション (design.md §16.1, §14.2)
    name: Transactions
  - description: メール OTP 認証 (design.md §18)
    name: Email OTP
  - description: パスキー (WebAuthn) 認証 (design.md §17)
    name: Passkeys
  - description: ソーシャルログイン (design.md §20)
    name: Social
  - description: 認証済みユーザーのセルフサービス (design.md §16.2)
    name: Me
  - description: Authorization Server / OIDC (design.md §15)
    name: OIDC
