openapi: 3.0.3
info:
  title: REST API
  description: |
    SalesBrain REST API

    ## レート制限
    検索 API（`/corporates/search`, `/jobs/search`）はユーザーあたり 5 req/sec に制限されます。
    マスターデータ API（`/master/*`）はレート制限の対象外です。

    制限超過時は HTTP 429 と `Retry-After` ヘッダ（再試行までの秒数）を返します。
    レスポンスヘッダ `X-RateLimit-Limit` / `X-RateLimit-Remaining` / `X-RateLimit-Reset` で現在の状態を確認できます。

    ## 未定義フィールド
    リクエストボディに schema で定義されていないフィールドが含まれている場合、HTTP 400 を返します。
    タイポや旧フィールド名の利用を早期に検知できるよう、未定義フィールドは黙って無視せず明示的にエラーとします。
  version: 2.0.0
servers:
  - url: https://api.salesbrain.jp/rest/v1
    description: Production server
security:
  - apiKeyAuth: []
paths:
  /corporates/search:
    post:
      operationId: searchCorporates
      summary: 企業検索
      description: 企業情報を検索します。
      tags:
        - Corporates
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CorporateSearchRequest"
      responses:
        "200":
          description: 検索結果
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CorporateSearchResponse"
        "400":
          description: リクエストエラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: 認証エラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: サーバーエラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "402":
          description: Payment Required - APIクレジットが不足
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Too Many Requests - レートリミット超過
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /jobs/search:
    post:
      operationId: searchJobs
      summary: 求人検索
      description: 求人情報を検索します。
      tags:
        - Jobs
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/JobSearchRequest"
      responses:
        "200":
          description: 検索結果
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobSearchResponse"
        "400":
          description: リクエストエラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: 認証エラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: サーバーエラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "402":
          description: Payment Required - APIクレジットが不足
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Too Many Requests - レートリミット超過
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /master/prefectures:
    get:
      operationId: getMasterPrefectures
      summary: 都道府県マスターデータ取得
      description: |
        都道府県の一覧を取得します。
        クレジット消費なし。Cache-Control: max-age=86400 でキャッシュ可能。
      tags:
        - Master
      responses:
        "200":
          description: 都道府県一覧
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/MasterPrefecture"
        "401":
          description: 認証エラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: サーバーエラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Too Many Requests - レートリミット超過
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /master/industries:
    get:
      operationId: getMasterIndustries
      summary: 業界マスターデータ取得
      description: |
        業界（大分類・小分類）の一覧を取得します。
        クレジット消費なし。Cache-Control: max-age=86400 でキャッシュ可能。
      tags:
        - Master
      responses:
        "200":
          description: 業界一覧
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/MasterIndustryCategory"
        "401":
          description: 認証エラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: サーバーエラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Too Many Requests - レートリミット超過
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /master/media:
    get:
      operationId: getMasterMedia
      summary: 媒体マスターデータ取得
      description: |
        媒体の一覧を取得します。
        クレジット消費なし。Cache-Control: max-age=86400 でキャッシュ可能。
      tags:
        - Master
      responses:
        "200":
          description: 媒体一覧
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/MasterMedia"
        "401":
          description: 認証エラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: サーバーエラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Too Many Requests - レートリミット超過
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /master/job-categories:
    get:
      operationId: getMasterJobCategories
      summary: 職種マスターデータ取得
      description: |
        職種（大分類・小分類）の一覧を取得します。
        クレジット消費なし。Cache-Control: max-age=86400 でキャッシュ可能。
      tags:
        - Master
      responses:
        "200":
          description: 職種一覧
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/MasterJobCategory"
        "401":
          description: 認証エラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: サーバーエラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Too Many Requests - レートリミット超過
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /master/markets:
    get:
      operationId: getMasterMarkets
      summary: 市場区分マスターデータ取得
      description: |
        市場区分の一覧を取得します。
        クレジット消費なし。Cache-Control: max-age=86400 でキャッシュ可能。
      tags:
        - Master
      responses:
        "200":
          description: 市場区分一覧
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/MasterMarket"
        "401":
          description: 認証エラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: サーバーエラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Too Many Requests - レートリミット超過
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /master/employment-types:
    get:
      operationId: getMasterEmploymentTypes
      summary: 雇用形態マスターデータ取得
      description: |
        雇用形態の一覧を取得します。
        クレジット消費なし。Cache-Control: max-age=86400 でキャッシュ可能。
      tags:
        - Master
      responses:
        "200":
          description: 雇用形態一覧
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/MasterEmploymentType"
        "401":
          description: 認証エラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: サーバーエラー
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Too Many Requests - レートリミット超過
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

components:
  schemas:
    # ============================================================
    # Enum 定義
    # ============================================================
    ExchangeCode:
      type: string
      enum:
        - tse
        - nse
        - fse
        - sse

    CorporateType:
      type: string
      enum:
        - kabushiki_gaisha
        - yugen_gaisha
        - godo_gaisha
        - gomei_gaisha
        - goshi_gaisha
        - foreign_company
        - national_org
        - local_gov
        - other

    RevenueRange:
      type: string
      enum:
        - under_100m
        - from_100m_to_300m
        - from_300m_to_1b
        - from_1b_to_5b
        - from_5b_to_30b
        - from_30b_to_100b
        - over_100b

    FundingSeries:
      type: string
      description: 投資ラウンド（シード〜シリーズ G）
      enum:
        - seed
        - series_a
        - series_b
        - series_c
        - series_d
        - series_e
        - series_f
        - series_g

    PaidMediaSpendRange:
      type: string
      description: 年間広告出稿額のレンジ
      enum:
        - none
        - from_0_to_1m
        - from_1m_to_3m
        - from_3m_to_5m
        - from_5m_to_10m
        - from_10m_to_30m
        - from_30m_to_50m
        - from_50m_to_100m
        - over_100m

    # ============================================================
    # 企業検索
    # ============================================================
    CorporateSearchRequest:
      type: object
      additionalProperties: false
      properties:
        # ページネーション
        limit:
          type: integer
          minimum: 1
          maximum: 1000
          default: 25
        offset:
          type: integer
          minimum: 0
          default: 0
        includeTotalResults:
          type: boolean
          default: true
        # ソート
        sort:
          type: object
          description: |
            並び替え指定。省略時は name の昇順。
            集約フィールド（`jobCreatedAt` 等）は紐づく求人の値の最大値で並び替えされる。

            **ソート可能なフィールド**:
            - 企業基本情報: `name`（企業名）, `foundedAt`（設立年月日）, `capitalMillionYen`（資本金）
            - 規模・成長: `employeeCount`（従業員数）, `employeeDiff6mMin`（6ヶ月従業員増加数）, `employeeDiff12m`（12ヶ月従業員増加数）
            - 広告: `totalAdCost`（広告出稿総額）
            - 連絡先: `phoneNumber`, `fax`, `email`, `websiteUrl`
            - 紐づく求人の集約値（最大値）: `jobCreatedAt`, `jobOpenedOn`, `jobDeadlineOn`, `jobSalaryMin`, `jobHiringPlanMin`
          properties:
            field:
              type: string
              description: 並び替え対象のフィールド名
              enum:
                - name
                - foundedAt
                - capitalMillionYen
                - employeeCount
                - totalAdCost
                - employeeDiff6mMin
                - employeeDiff12m
                - phoneNumber
                - fax
                - email
                - websiteUrl
                - jobCreatedAt
                - jobOpenedOn
                - jobDeadlineOn
                - jobSalaryMin
                - jobHiringPlanMin
            order:
              type: string
              description: 並び順（昇順 / 降順）
              enum: [asc, desc]
          required: [field, order]
        # 企業名（テキスト）
        corporateNameOr:
          type: array
          description: 企業名の完全一致フィルター。指定した企業名のいずれかに一致する企業を返します。
          items:
            type: string
        corporateNameNot:
          type: array
          description: 企業名の除外フィルター。指定した企業名のいずれにも一致しない企業を返します。
          items:
            type: string
        corporateNamePartialMatchOr:
          type: array
          description: 企業名の部分一致フィルター。指定した文字列のいずれかを含む企業を返します。
          items:
            type: string
        corporateNamePartialMatchNot:
          type: array
          description: 企業名の部分一致除外フィルター。指定した文字列のいずれも含まない企業を返します。
          items:
            type: string

        # ID 参照フィルター
        prefectureIdOr:
          type: array
          description: 都道府県IDフィルター（OR条件）。`/master/prefectures` で取得できるIDを指定します。
          items:
            type: integer
        prefectureIdNot:
          type: array
          description: 都道府県IDフィルター（除外条件）。`/master/prefectures` で取得できるIDを指定します。
          items:
            type: integer
        # 住所（キーワード部分一致）
        cityNamePartialMatchOr:
          type: array
          description: 市区町村の部分一致フィルター（OR条件）。指定文字列のいずれかを含む市区町村の企業を返します。
          items:
            type: string
        cityNamePartialMatchNot:
          type: array
          description: 市区町村の部分一致除外フィルター。指定文字列のいずれも含まない市区町村の企業を返します。
          items:
            type: string
        streetAddressPartialMatchOr:
          type: array
          description: 住所（番地以降）の部分一致フィルター（OR条件）。
          items:
            type: string
        streetAddressPartialMatchNot:
          type: array
          description: 住所（番地以降）の部分一致除外フィルター。
          items:
            type: string
        industryCategoryIdOr:
          type: array
          description: 業界カテゴリIDフィルター（OR条件）。`/master/industries` で取得できる大分類IDを指定します。
          items:
            type: integer
        industryCategoryIdNot:
          type: array
          description: 業界カテゴリIDフィルター（除外条件）。`/master/industries` で取得できる大分類IDを指定します。
          items:
            type: integer
        industrySubCategoryIdOr:
          type: array
          description: 業界サブカテゴリIDフィルター（OR条件）。`/master/industries` で取得できる小分類IDを指定します。
          items:
            type: integer
        industrySubCategoryIdNot:
          type: array
          description: 業界サブカテゴリIDフィルター（除外条件）。`/master/industries` で取得できる小分類IDを指定します。
          items:
            type: integer
        # Enum フィルター
        corporateTypeOr:
          type: array
          items:
            $ref: "#/components/schemas/CorporateType"
        corporateTypeNot:
          type: array
          items:
            $ref: "#/components/schemas/CorporateType"
        marketCodeOr:
          type: array
          description: 市場区分コードフィルター（OR条件）。`/master/markets` で取得できるコードを指定します。
          items:
            type: string
        marketCodeNot:
          type: array
          description: 市場区分コードフィルター（除外条件）。`/master/markets` で取得できるコードを指定します。
          items:
            type: string
        isListed:
          type: boolean
          description: 上場/未上場フィルター。true=上場企業のみ、false=未上場企業のみ。
          nullable: true
        revenueRangeOr:
          type: array
          items:
            $ref: "#/components/schemas/RevenueRange"
        revenueRangeNot:
          type: array
          items:
            $ref: "#/components/schemas/RevenueRange"

        # 数値範囲
        employeeCountMin:
          type: integer
          description: 従業員数の下限（以上）
        employeeCountMax:
          type: integer
          description: 従業員数の上限（以下）
        capitalMillionYenMin:
          type: number
          description: 資本金の下限（百万円単位、以上）
        capitalMillionYenMax:
          type: number
          description: 資本金の上限（百万円単位、以下）

        # 連絡先有無
        hasCorporatePhone:
          type: boolean
          description: 電話番号の有無でフィルター
        hasCorporateWebsite:
          type: boolean
          description: Webサイトの有無でフィルター
        hasCorporateEmail:
          type: boolean
          description: メールアドレスの有無でフィルター

        # 日付範囲
        foundedAtFrom:
          type: string
          format: date
          description: 設立日の開始日（YYYY-MM-DD形式、以降）
        foundedAtTo:
          type: string
          format: date
          description: 設立日の終了日（YYYY-MM-DD形式、以前）

        # 法人番号
        corporateNumberOr:
          type: array
          description: 法人番号フィルター（OR条件）。13桁の法人番号を指定します。
          items:
            type: string

        # クロスエンティティ（求人テーブル参照）
        jobTitlePartialMatchOr:
          type: array
          description: 求人タイトルの部分一致フィルター（OR条件）。紐づく求人のタイトルに指定文字列のいずれかを含む企業を返します。
          items:
            type: string
        jobTitlePartialMatchNot:
          type: array
          description: 求人タイトルの部分一致除外フィルター。紐づく求人のタイトルに指定文字列のいずれも含まない企業を返します。
          items:
            type: string
        jobMediaIdOr:
          type: array
          description: 求人媒体IDフィルター（OR条件）。`/master/media` で取得できるIDを指定します。
          items:
            type: integer
        jobMediaIdNot:
          type: array
          description: 求人媒体IDフィルター（除外条件）。`/master/media` で取得できるIDを指定します。
          items:
            type: integer
        jobEmploymentTypeIdOr:
          type: array
          description: 雇用形態IDフィルター（OR条件）。`/master/employment-types` で取得できるIDを指定します。
          items:
            type: integer
        jobEmploymentTypeIdNot:
          type: array
          description: 雇用形態IDフィルター（除外条件）。`/master/employment-types` で取得できるIDを指定します。
          items:
            type: integer
        jobSalaryMin:
          type: integer
          description: 求人の給与下限（円単位、以上）
        jobSalaryMax:
          type: integer
          description: 求人の給与上限（円単位、以下）
        jobCategoryIdOr:
          type: array
          description: 職種カテゴリIDフィルター（OR条件）。`/master/job-categories` で取得できる大分類IDを指定します。
          items:
            type: integer
        jobCategoryIdNot:
          type: array
          description: 職種カテゴリIDフィルター（除外条件）。`/master/job-categories` で取得できる大分類IDを指定します。
          items:
            type: integer
        jobSubCategoryIdOr:
          type: array
          description: 職種サブカテゴリIDフィルター（OR条件）。`/master/job-categories` で取得できる小分類IDを指定します。
          items:
            type: integer
        jobSubCategoryIdNot:
          type: array
          description: 職種サブカテゴリIDフィルター（除外条件）。`/master/job-categories` で取得できる小分類IDを指定します。
          items:
            type: integer
        jobOpenedOnFrom:
          type: string
          format: date
          description: 掲載開始日の開始日（YYYY-MM-DD形式、以降）
        jobOpenedOnTo:
          type: string
          format: date
          description: 掲載開始日の終了日（YYYY-MM-DD形式、以前）
        jobDeadlineOnFrom:
          type: string
          format: date
          description: 掲載終了日の開始日（YYYY-MM-DD形式、以降）
        jobDeadlineOnTo:
          type: string
          format: date
          description: 掲載終了日の終了日（YYYY-MM-DD形式、以前）
        jobAdPlanOr:
          type: array
          description: 広告プランフィルター（OR条件）。紐づく求人の広告プラン名を指定します。
          items:
            type: string
        hasDispatchLicense:
          type: boolean
          description: 派遣免許の有無でフィルター
        hasRecruitmentLicense:
          type: boolean
          description: 職業紹介免許の有無でフィルター
        jobHasContactEmail:
          type: boolean
          description: 紐づく求人のメールアドレスの有無でフィルター
        jobHasContactPhone:
          type: boolean
          description: 紐づく求人の電話番号の有無でフィルター
        # === 求人のさらなる項目（両画面対称化） ===
        jobCreatedAtFrom:
          type: string
          format: date
          description: 紐づく求人の取得日の開始日（YYYY-MM-DD形式、以降）
        jobCreatedAtTo:
          type: string
          format: date
          description: 紐づく求人の取得日の終了日（YYYY-MM-DD形式、以前）
        jobHiringPlanMinAtLeast:
          type: integer
          minimum: 0
          description: 紐づく求人の採用予定最小人数が N 人以上（N を指定）
        # 投資ラウンド
        fundingSeriesOr:
          type: array
          description: 投資ラウンドフィルター（OR条件）。指定した投資ラウンドのいずれかに該当する企業を返します。
          items:
            $ref: "#/components/schemas/FundingSeries"
        fundingSeriesNot:
          type: array
          description: 投資ラウンドフィルター（除外条件）。指定した投資ラウンドのいずれにも該当しない企業を返します。
          items:
            $ref: "#/components/schemas/FundingSeries"
        # 広告出稿総額
        totalAdCostMin:
          type: integer
          format: int64
          minimum: 0
          description: 広告出稿総額の下限（円、以上）
        totalAdCostMax:
          type: integer
          format: int64
          minimum: 0
          description: 広告出稿総額の上限（円、以下）
        # 広告出稿レンジ
        paidMediaSpendOr:
          type: array
          description: 出稿額レンジフィルター（OR条件）。
          items:
            $ref: "#/components/schemas/PaidMediaSpendRange"
        paidMediaSpendNot:
          type: array
          description: 出稿額レンジフィルター（除外条件）。
          items:
            $ref: "#/components/schemas/PaidMediaSpendRange"
        # 掲載課金型媒体
        paidMediaUsedMediaIdOr:
          type: array
          description: 掲載課金型媒体フィルター（OR条件）。`/master/media` で取得できる媒体IDを指定します。指定いずれかの媒体に掲載している企業を返します。
          items:
            type: integer
        paidMediaUsedMediaIdNot:
          type: array
          description: 掲載課金型媒体フィルター（除外条件）。
          items:
            type: integer
        # FAX の有無
        hasFax:
          type: boolean
          description: FAX 番号の有無でフィルター
        # 従業員増加数（6 ヶ月・12 ヶ月）
        employeeDiff6mMinMin:
          type: integer
          description: 6 ヶ月従業員増加数の下限（以上）
        employeeDiff6mMinMax:
          type: integer
          description: 6 ヶ月従業員増加数の上限（以下）
        employeeDiff12mMin:
          type: integer
          description: 12 ヶ月従業員増加数の下限（以上）
        employeeDiff12mMax:
          type: integer
          description: 12 ヶ月従業員増加数の上限（以下）
        # 従業員推移フラグ
        employeeTransitionPartialMatchOr:
          type: array
          description: 従業員推移フラグの部分一致フィルター（OR条件）。例：「6 ヶ月連続増加」「12 ヶ月連続増加」
          items:
            type: string
        employeeTransitionPartialMatchNot:
          type: array
          description: 従業員推移フラグの部分一致除外フィルター。
          items:
            type: string

    CorporateSearchResponse:
      type: object
      required:
        - results
      properties:
        totalResults:
          type: integer
          nullable: true
        results:
          type: array
          items:
            $ref: "#/components/schemas/Corporate"

    Corporate:
      type: object
      required:
        - corporateNumber
        - name
      properties:
        corporateNumber:
          type: string
        name:
          type: string
        furigana:
          type: string
          nullable: true
        corporateType:
          nullable: true
          allOf:
            - $ref: "#/components/schemas/CorporateType"
        corporateTypeLabel:
          type: string
          nullable: true
        postalCode:
          type: string
          nullable: true
        prefectureId:
          type: integer
          nullable: true
        prefectureName:
          type: string
          nullable: true
        cityName:
          type: string
          nullable: true
        streetAddress:
          type: string
          nullable: true
        industryCategoryId:
          type: integer
          nullable: true
        industryCategoryName:
          type: string
          nullable: true
        industrySubCategoryId:
          type: integer
          nullable: true
        industrySubCategoryName:
          type: string
          nullable: true
        phoneNumber:
          type: string
          nullable: true
        fax:
          type: string
          nullable: true
        websiteUrl:
          type: string
          nullable: true
        email:
          type: string
          nullable: true
        employeeCount:
          type: integer
          nullable: true
        capitalMillionYen:
          type: number
          nullable: true
        revenueRange:
          nullable: true
          allOf:
            - $ref: "#/components/schemas/RevenueRange"
        revenueRangeLabel:
          type: string
          nullable: true
        markets:
          type: array
          items:
            $ref: "#/components/schemas/CorporateMarket"
        fiscalMonth:
          type: string
          nullable: true
        ceoName:
          type: string
          nullable: true
        foundedAt:
          type: string
          format: date-time
          nullable: true
        hasDispatchLicense:
          type: boolean
          nullable: true
          description: 派遣免許の有無
        hasRecruitmentLicense:
          type: boolean
          nullable: true
          description: 職業紹介免許の有無
        # 従業員推移（metrics）
        emp1MAgo:
          type: integer
          nullable: true
          description: 1 ヶ月前従業員数
        emp7MAgo:
          type: integer
          nullable: true
          description: 7 ヶ月前従業員数
        emp13MAgo:
          type: integer
          nullable: true
          description: 13 ヶ月前従業員数
        employeeDiff6mMin:
          type: integer
          nullable: true
          description: 6 ヶ月従業員増加数
        employeeDiff12m:
          type: integer
          nullable: true
          description: 12 ヶ月従業員増加数
        employeeTransition:
          type: string
          nullable: true
          description: 従業員推移フラグ（「6 ヶ月連続増加」等の結合文字列）
        # 投資・広告出稿
        fundingSeries:
          nullable: true
          allOf:
            - $ref: "#/components/schemas/FundingSeries"
        fundingSeriesLabel:
          type: string
          nullable: true
        totalAdCost:
          type: integer
          format: int64
          nullable: true
          description: 広告出稿総額（円）
        paidMediaSpend:
          nullable: true
          allOf:
            - $ref: "#/components/schemas/PaidMediaSpendRange"
        paidMediaSpendLabel:
          type: string
          nullable: true
        paidMedias:
          type: array
          description: 掲載課金型媒体（企業単位）
          items:
            $ref: "#/components/schemas/CorporatePaidMedia"

    CorporatePaidMedia:
      type: object
      required:
        - mediaId
        - mediaName
      properties:
        mediaId:
          type: integer
        mediaName:
          type: string

    CorporateMarket:
      type: object
      properties:
        code:
          type: string
        exchangeCode:
          $ref: "#/components/schemas/ExchangeCode"
        name:
          type: string

    # ============================================================
    # 求人検索
    # ============================================================
    JobSearchRequest:
      type: object
      additionalProperties: false
      properties:
        # ページネーション
        limit:
          type: integer
          minimum: 1
          maximum: 1000
          default: 25
        offset:
          type: integer
          minimum: 0
          default: 0
        includeTotalResults:
          type: boolean
          default: true
        # ソート
        sort:
          type: object
          description: |
            並び替え指定。省略時は jobCreatedAt の降順。

            **ソート可能なフィールド**:
            - 求人: `jobCreatedAt`（求人取得日）, `jobOpenedOn`（募集開始日）, `jobDeadlineOn`（募集終了日）
            - 給与・採用: `salaryMin`（給与下限）, `salaryMax`（給与上限）, `hiringPlanMin`（採用予定人数下限）
            - 求人連絡先: `contactEmail`, `contactPhoneNumber`
            - 企業（クロスエンティティ）: `corporateFoundedAt`, `corporateCapitalMillionYen`, `corporateEmployeeCount`, `corporateTotalAdCost`, `corporateEmployeeDiff6mMin`, `corporateEmployeeDiff12m`, `corporatePhoneNumber`, `corporateFax`, `corporateEmail`, `corporateWebsiteUrl`
          properties:
            field:
              type: string
              description: 並び替え対象のフィールド名
              enum:
                - jobCreatedAt
                - jobOpenedOn
                - jobDeadlineOn
                - salaryMin
                - salaryMax
                - hiringPlanMin
                - contactEmail
                - contactPhoneNumber
                - corporateFoundedAt
                - corporateCapitalMillionYen
                - corporateEmployeeCount
                - corporateTotalAdCost
                - corporateEmployeeDiff6mMin
                - corporateEmployeeDiff12m
                - corporatePhoneNumber
                - corporateFax
                - corporateEmail
                - corporateWebsiteUrl
            order:
              type: string
              description: 並び順（昇順 / 降順）
              enum: [asc, desc]
          required: [field, order]
        # 求人タイトル（テキスト）
        jobTitleOr:
          type: array
          description: 求人タイトルの完全一致フィルター（OR条件）。
          items:
            type: string
        jobTitleNot:
          type: array
          description: 求人タイトルの完全一致除外フィルター。
          items:
            type: string
        jobTitlePartialMatchOr:
          type: array
          description: 求人タイトルの部分一致フィルター（OR条件）。指定文字列のいずれかを含む求人を返します。
          items:
            type: string
        jobTitlePartialMatchNot:
          type: array
          description: 求人タイトルの部分一致除外フィルター。指定文字列のいずれも含まない求人を返します。
          items:
            type: string

        # 企業名（テキスト、corporate.name 経由）
        corporateNameOr:
          type: array
          description: 企業名の完全一致フィルター（OR条件）。紐づく企業名で絞り込みます。
          items:
            type: string
        corporateNameNot:
          type: array
          description: 企業名の完全一致除外フィルター。
          items:
            type: string
        corporateNamePartialMatchOr:
          type: array
          description: 企業名の部分一致フィルター（OR条件）。紐づく企業名に指定文字列のいずれかを含む求人を返します。
          items:
            type: string
        corporateNamePartialMatchNot:
          type: array
          description: 企業名の部分一致除外フィルター。
          items:
            type: string

        # ID 参照フィルター（求人固有）
        mediaIdOr:
          type: array
          description: 媒体IDフィルター（OR条件）。`/master/media` で取得できるIDを指定します。
          items:
            type: integer
        mediaIdNot:
          type: array
          description: 媒体IDフィルター（除外条件）。`/master/media` で取得できるIDを指定します。
          items:
            type: integer
        jobCategoryIdOr:
          type: array
          description: 職種カテゴリIDフィルター（OR条件）。`/master/job-categories` で取得できる大分類IDを指定します。
          items:
            type: integer
        jobCategoryIdNot:
          type: array
          description: 職種カテゴリIDフィルター（除外条件）。`/master/job-categories` で取得できる大分類IDを指定します。
          items:
            type: integer
        jobSubCategoryIdOr:
          type: array
          description: 職種サブカテゴリIDフィルター（OR条件）。`/master/job-categories` で取得できる小分類IDを指定します。
          items:
            type: integer
        jobSubCategoryIdNot:
          type: array
          description: 職種サブカテゴリIDフィルター（除外条件）。`/master/job-categories` で取得できる小分類IDを指定します。
          items:
            type: integer
        employmentTypeIdOr:
          type: array
          description: 雇用形態IDフィルター（OR条件）。`/master/employment-types` で取得できるIDを指定します。
          items:
            type: integer
        employmentTypeIdNot:
          type: array
          description: 雇用形態IDフィルター（除外条件）。`/master/employment-types` で取得できるIDを指定します。
          items:
            type: integer

        # 広告プラン
        adPlanOr:
          type: array
          description: 広告プランのフィルター（OR条件）。求人の広告プラン名を指定します。
          items:
            type: string

        # 給与
        salaryMin:
          type: integer
          description: 給与の下限（円単位、以上）
        salaryMax:
          type: integer
          description: 給与の上限（円単位、以下）

        # 投資ラウンド（求人検索からも company.fundingSeries を参照）
        fundingSeriesOr:
          type: array
          description: 企業の投資ラウンドフィルター（OR条件）。`corporateFundingSeriesOr` のエイリアス。
          items:
            $ref: "#/components/schemas/FundingSeries"
        fundingSeriesNot:
          type: array
          description: 企業の投資ラウンドフィルター（除外条件）。`corporateFundingSeriesNot` のエイリアス。
          items:
            $ref: "#/components/schemas/FundingSeries"

        # 連絡先有無
        hasContactEmail:
          type: boolean
          description: メールアドレスの有無でフィルター
        hasContactPhone:
          type: boolean
          description: 電話番号の有無でフィルター

        # 日付
        jobCreatedAtFrom:
          type: string
          format: date
          description: 求人取得日の開始日（YYYY-MM-DD形式、以降）
        jobCreatedAtTo:
          type: string
          format: date
          description: 求人取得日の終了日（YYYY-MM-DD形式、以前）
        jobOpenedOnFrom:
          type: string
          format: date
          description: 掲載開始日の開始日（YYYY-MM-DD形式、以降）
        jobOpenedOnTo:
          type: string
          format: date
          description: 掲載開始日の終了日（YYYY-MM-DD形式、以前）
        jobDeadlineOnFrom:
          type: string
          format: date
          description: 掲載終了日の開始日（YYYY-MM-DD形式、以降）
        jobDeadlineOnTo:
          type: string
          format: date
          description: 掲載終了日の終了日（YYYY-MM-DD形式、以前）

        # 法人番号
        corporateNumberOr:
          type: array
          description: 法人番号フィルター（OR条件）。13桁の法人番号を指定します。
          items:
            type: string

        # クロスエンティティ（企業テーブル参照）
        corporatePrefectureIdOr:
          type: array
          description: 企業の都道府県IDフィルター（OR条件）。`/master/prefectures` で取得できるIDを指定します。
          items:
            type: integer
        corporatePrefectureIdNot:
          type: array
          description: 企業の都道府県IDフィルター（除外条件）。`/master/prefectures` で取得できるIDを指定します。
          items:
            type: integer
        corporateCityNamePartialMatchOr:
          type: array
          description: 企業の市区町村の部分一致フィルター（OR条件）。
          items:
            type: string
        corporateCityNamePartialMatchNot:
          type: array
          description: 企業の市区町村の部分一致除外フィルター。
          items:
            type: string
        corporateStreetAddressPartialMatchOr:
          type: array
          description: 企業の住所（番地以降）の部分一致フィルター（OR条件）。
          items:
            type: string
        corporateStreetAddressPartialMatchNot:
          type: array
          description: 企業の住所（番地以降）の部分一致除外フィルター。
          items:
            type: string
        corporateIndustryCategoryIdOr:
          type: array
          description: 企業の業界カテゴリIDフィルター（OR条件）。`/master/industries` で取得できる大分類IDを指定します。
          items:
            type: integer
        corporateIndustryCategoryIdNot:
          type: array
          description: 企業の業界カテゴリIDフィルター（除外条件）。`/master/industries` で取得できる大分類IDを指定します。
          items:
            type: integer
        corporateIndustrySubCategoryIdOr:
          type: array
          description: 企業の業界サブカテゴリIDフィルター（OR条件）。`/master/industries` で取得できる小分類IDを指定します。
          items:
            type: integer
        corporateIndustrySubCategoryIdNot:
          type: array
          description: 企業の業界サブカテゴリIDフィルター（除外条件）。`/master/industries` で取得できる小分類IDを指定します。
          items:
            type: integer
        corporateCorporateTypeOr:
          type: array
          items:
            $ref: "#/components/schemas/CorporateType"
        corporateCorporateTypeNot:
          type: array
          items:
            $ref: "#/components/schemas/CorporateType"
        corporateMarketCodeOr:
          type: array
          description: 企業の市場区分コードフィルター（OR条件）。`/master/markets` で取得できるコードを指定します。
          items:
            type: string
        corporateMarketCodeNot:
          type: array
          description: 企業の市場区分コードフィルター（除外条件）。`/master/markets` で取得できるコードを指定します。
          items:
            type: string
        corporateIsListed:
          type: boolean
          description: 企業の上場/未上場フィルター。true=上場企業のみ、false=未上場企業のみ。
          nullable: true
        corporateRevenueRangeOr:
          type: array
          items:
            $ref: "#/components/schemas/RevenueRange"
        corporateRevenueRangeNot:
          type: array
          items:
            $ref: "#/components/schemas/RevenueRange"
        corporateCapitalMillionYenMin:
          type: number
        corporateCapitalMillionYenMax:
          type: number
        corporateEmployeeCountMin:
          type: integer
        corporateEmployeeCountMax:
          type: integer
        corporateHasCorporateWebsite:
          type: boolean
        corporateHasCorporatePhone:
          type: boolean
        corporateHasCorporateEmail:
          type: boolean
        # 採用計画
        hiringPlanMinAtLeast:
          type: integer
          minimum: 0
          description: 採用予定最小人数が N 人以上（N を指定）
        # === DEV-223: 企業の投資・広告・従業員推移（求人検索からも参照可能に） ===
        corporateFundingSeriesOr:
          type: array
          description: 企業の投資ラウンドフィルター（OR条件）。
          items:
            $ref: "#/components/schemas/FundingSeries"
        corporateFundingSeriesNot:
          type: array
          description: 企業の投資ラウンドフィルター（除外条件）。
          items:
            $ref: "#/components/schemas/FundingSeries"
        corporateTotalAdCostMin:
          type: integer
          minimum: 0
          description: 企業の広告出稿総額の下限（円）。
        corporateTotalAdCostMax:
          type: integer
          minimum: 0
          description: 企業の広告出稿総額の上限（円）。
        corporatePaidMediaSpendOr:
          type: array
          description: 企業の出稿額レンジフィルター（OR条件）。
          items:
            $ref: "#/components/schemas/PaidMediaSpendRange"
        corporatePaidMediaSpendNot:
          type: array
          description: 企業の出稿額レンジフィルター（除外条件）。
          items:
            $ref: "#/components/schemas/PaidMediaSpendRange"
        corporatePaidMediaUsedMediaIdOr:
          type: array
          description: 企業の掲載課金型媒体フィルター（OR条件）。`/master/media` で取得できる ID を指定します。
          items:
            type: integer
        corporatePaidMediaUsedMediaIdNot:
          type: array
          description: 企業の掲載課金型媒体フィルター（除外条件）。`/master/media` で取得できる ID を指定します。
          items:
            type: integer
        corporateHasFax:
          type: boolean
          description: 企業の FAX 番号の有無。true=あり、false=なし。
        corporateEmployeeDiff6mMinMin:
          type: integer
          description: 企業の 6 ヶ月従業員増加数の下限（min 値）。
        corporateEmployeeDiff6mMinMax:
          type: integer
          description: 企業の 6 ヶ月従業員増加数の上限（max 値）。
        corporateEmployeeDiff12mMin:
          type: integer
          description: 企業の 12 ヶ月従業員増加数の下限。
        corporateEmployeeDiff12mMax:
          type: integer
          description: 企業の 12 ヶ月従業員増加数の上限。
        corporateEmployeeTransitionPartialMatchOr:
          type: array
          description: 企業の従業員推移フラグの部分一致（OR条件）。
          items:
            type: string
        corporateEmployeeTransitionPartialMatchNot:
          type: array
          description: 企業の従業員推移フラグの部分一致（除外条件）。
          items:
            type: string

    JobSearchResponse:
      type: object
      required:
        - results
      properties:
        totalResults:
          type: integer
          nullable: true
        results:
          type: array
          items:
            $ref: "#/components/schemas/Job"

    Job:
      type: object
      required:
        - id
        - shortId
        - corporateNumber
        - corporateName
        - mediaId
        - mediaName
        - title
        - url
        - jobCreatedAt
      properties:
        id:
          type: string
        shortId:
          type: string
          minLength: 18
          maxLength: 18
          description: 顧客提供用の短縮 ID（18 文字）
        corporateNumber:
          type: string
        corporateName:
          type: string
        corporatePrefectureName:
          type: string
          nullable: true
          description: 企業の本社所在地（都道府県）
        corporateCityName:
          type: string
          nullable: true
          description: 企業の本社所在地（市区町村）
        corporateStreetAddress:
          type: string
          nullable: true
          description: 企業の本社所在地（番地以降）
        corporateEmployeeCount:
          type: integer
          nullable: true
          description: 企業の従業員数
        mediaId:
          type: integer
        mediaName:
          type: string
        title:
          type: string
        url:
          type: string
        jobCategoryId:
          type: integer
          nullable: true
        jobCategoryName:
          type: string
          nullable: true
        jobSubCategoryId:
          type: integer
          nullable: true
        jobSubCategoryName:
          type: string
          nullable: true
        employmentTypes:
          type: array
          items:
            $ref: "#/components/schemas/JobEmploymentType"
        adPlan:
          type: string
          nullable: true
        salaryType:
          type: string
          nullable: true
        salaryMin:
          type: integer
          nullable: true
        salaryMax:
          type: integer
          nullable: true
        contactEmail:
          type: string
          nullable: true
        contactPhoneNumber:
          type: string
          nullable: true
        workLocationList:
          type: array
          nullable: true
          description: 勤務地リスト（構造化されたもの）
          items:
            type: string
        openedOn:
          type: string
          format: date-time
          nullable: true
        deadlineOn:
          type: string
          format: date-time
          nullable: true
        jobCreatedAt:
          type: string
          format: date-time
          description: 求人取得日

    JobEmploymentType:
      type: object
      properties:
        id:
          type: integer
        code:
          type: string
        name:
          type: string

    # ============================================================
    # マスターデータ
    # ============================================================
    MasterPrefecture:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
        name:
          type: string

    MasterIndustryCategory:
      type: object
      required:
        - id
        - name
        - subCategories
      properties:
        id:
          type: integer
        name:
          type: string
        subCategories:
          type: array
          items:
            $ref: "#/components/schemas/MasterSubCategory"

    MasterMedia:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
        name:
          type: string

    MasterJobCategory:
      type: object
      required:
        - id
        - name
        - subCategories
      properties:
        id:
          type: integer
        name:
          type: string
        subCategories:
          type: array
          items:
            $ref: "#/components/schemas/MasterSubCategory"

    MasterSubCategory:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
        name:
          type: string

    MasterMarket:
      type: object
      required:
        - id
        - code
        - exchangeCode
        - name
      properties:
        id:
          type: integer
        code:
          type: string
        exchangeCode:
          $ref: "#/components/schemas/ExchangeCode"
        name:
          type: string

    MasterEmploymentType:
      type: object
      required:
        - id
        - code
        - name
      properties:
        id:
          type: integer
        code:
          type: string
        name:
          type: string

    # ============================================================
    # 共通
    # ============================================================
    ErrorResponse:
      type: object
      required:
        - message
      properties:
        message:
          type: string
        errors:
          type: array
          description: バリデーションエラーの詳細
          items:
            $ref: "#/components/schemas/ValidationError"

    ValidationError:
      type: object
      required:
        - field
        - message
      properties:
        field:
          type: string
          description: エラーが発生したフィールドのパス
        message:
          type: string
          description: エラーメッセージ

  securitySchemes:
    apiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
