openapi: 3.0.3
info:
  title: Semantil API
  version: "2026-08-26"
  description: >
    One API for market discovery, company search, lookalikes and domain
    enrichment. Domain data is selected through explicit products and billed
    only when each product passes its quality gate.
  contact:
    name: Semantil
    url: https://semantil.com/contact
servers:
  - url: https://semantil.com
    description: Production
security:
  - BearerAuth: []
  - ApiKeyHeader: []
tags:
  - name: Domain enrichment
  - name: Search
  - name: Lookalike
  - name: Account
  - name: Reference
paths:
  /api/products:
    get:
      tags: [Domain enrichment]
      operationId: listDataProducts
      summary: List domain data products and credit units
      security: []
      responses:
        "200":
          description: Product catalog
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
            X-Semantil-Contract-Version:
              $ref: "#/components/headers/ContractVersion"
            X-Credits-Cost:
              $ref: "#/components/headers/CreditsCost"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DataProductCatalog"
  /api/domains/{domain}:
    get:
      tags: [Domain enrichment]
      operationId: getDomain
      summary: Enrich one domain with explicit data products
      parameters:
        - $ref: "#/components/parameters/Domain"
        - name: products
          in: query
          description: Comma-separated product IDs. Defaults to profile. Use all for the complete bundle.
          schema:
            type: string
          example: profile,tech,company
        - name: include
          in: query
          description: Optional field projection within the selected products.
          schema:
            type: string
          example: country,site_category,tech,company_name
        - name: max_credits
          in: query
          description: Reject before lookup when the maximum possible charge exceeds this value.
          schema:
            type: integer
            minimum: 0
      responses:
        "200":
          description: Flat domain response with a result-based credit receipt
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
            X-Semantil-Contract-Version:
              $ref: "#/components/headers/ContractVersion"
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            X-Credits-Cost:
              $ref: "#/components/headers/CreditsCost"
            X-Credits-Remaining:
              $ref: "#/components/headers/CreditsRemaining"
            X-Domain-Found:
              description: Whether Semantil found the requested domain
              schema:
                type: string
                enum: ["true", "false"]
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/DomainResult"
                  - $ref: "#/components/schemas/DomainNotFound"
        "400":
          $ref: "#/components/responses/Problem"
        "401":
          $ref: "#/components/responses/Problem"
        "403":
          $ref: "#/components/responses/Problem"
        "429":
          $ref: "#/components/responses/Problem"
        "500":
          $ref: "#/components/responses/Problem"
  /api/search:
    get:
      tags: [Search]
      operationId: searchCompanies
      summary: Search the live company catalog
      parameters:
        - $ref: "#/components/parameters/Query"
        - $ref: "#/components/parameters/SearchMode"
        - $ref: "#/components/parameters/QueryOperator"
        - $ref: "#/components/parameters/Filters"
        - $ref: "#/components/parameters/NotFilters"
        - $ref: "#/components/parameters/Include"
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Sort"
        - $ref: "#/components/parameters/IncludeMeta"
        - $ref: "#/components/parameters/Breakdown"
        - $ref: "#/components/parameters/BreakdownLimit"
        - $ref: "#/components/parameters/Population"
      responses:
        "200":
          $ref: "#/components/responses/SearchOrBreakdownResult"
        "400":
          $ref: "#/components/responses/Problem"
        "401":
          $ref: "#/components/responses/Problem"
        "403":
          $ref: "#/components/responses/Problem"
        "429":
          $ref: "#/components/responses/Problem"
    post:
      tags: [Search]
      operationId: searchCompaniesWithExclusions
      summary: Search while excluding a bounded CRM domain set
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SearchRequest"
      responses:
        "200":
          $ref: "#/components/responses/SearchResult"
        "400":
          $ref: "#/components/responses/Problem"
        "401":
          $ref: "#/components/responses/Problem"
        "403":
          $ref: "#/components/responses/Problem"
        "413":
          $ref: "#/components/responses/Problem"
        "415":
          $ref: "#/components/responses/Problem"
        "429":
          $ref: "#/components/responses/Problem"
  /api/lookalike/{domain}:
    get:
      tags: [Lookalike]
      operationId: findLookalikes
      summary: Find similar companies from one seed domain
      parameters:
        - $ref: "#/components/parameters/Domain"
        - name: k
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: strategy
          in: query
          schema:
            type: string
            enum: [hybrid, vector, llm]
            default: hybrid
        - $ref: "#/components/parameters/Filters"
        - name: ranges
          in: query
          description: JSON object of allowlisted numeric range filters.
          schema:
            type: string
        - $ref: "#/components/parameters/Include"
        - name: include_evidence
          in: query
          schema:
            type: boolean
            default: false
        - $ref: "#/components/parameters/IncludeMeta"
      responses:
        "200":
          description: Lookalike candidates
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
            X-Semantil-Contract-Version:
              $ref: "#/components/headers/ContractVersion"
            X-Credits-Cost:
              $ref: "#/components/headers/CreditsCost"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LookalikeResult"
        "400":
          $ref: "#/components/responses/Problem"
        "401":
          $ref: "#/components/responses/Problem"
        "403":
          $ref: "#/components/responses/Problem"
        "429":
          $ref: "#/components/responses/Problem"
  /api/me:
    get:
      tags: [Account]
      operationId: getAccount
      summary: Check authentication, credits and API capabilities
      responses:
        "200":
          description: Account and capability summary
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Account"
        "401":
          $ref: "#/components/responses/Problem"
  /api/fields:
    get:
      tags: [Reference]
      operationId: listFields
      summary: List canonical fields, products and search facets
      security: []
      responses:
        "200":
          description: Public contract catalog
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FieldsResponse"
  /api/errors:
    get:
      tags: [Reference]
      operationId: listErrors
      summary: List stable problem codes
      security: []
      responses:
        "200":
          description: Error catalog
          content:
            application/json:
              schema:
                type: object
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
    ApiKeyHeader:
      type: apiKey
      in: header
      name: x-api-key
  headers:
    ServerTiming:
      description: Semantil application processing time in milliseconds
      schema:
        type: string
        example: semantil;dur=327.125
    ContractVersion:
      description: Semantil public contract revision
      schema:
        type: string
        example: "2026-08-26"
    RequestId:
      description: Request identifier for support and debugging
      schema:
        type: string
    CreditsCost:
      description: Credits charged for this request
      schema:
        type: string
        example: "1"
    CreditsRemaining:
      description: Credits remaining after this request
      schema:
        type: string
        example: "99"
  parameters:
    Domain:
      name: domain
      in: path
      required: true
      description: Domain or URL to normalize and look up
      schema:
        type: string
    Query:
      name: query
      in: query
      schema:
        type: string
    QueryOperator:
      name: query_operator
      in: query
      schema:
        type: string
        enum: [and, or]
        default: or
    SearchMode:
      name: mode
      in: query
      description: Use semantic for multilingual business-meaning retrieval. Lexical keyword search remains the default and automatic fallback.
      schema:
        type: string
        enum: [lexical, semantic]
        default: lexical
    Filters:
      name: filters
      in: query
      description: JSON object of allowlisted facet values
      schema:
        type: string
    NotFilters:
      name: not_filters
      in: query
      description: JSON object of allowlisted excluded facet values
      schema:
        type: string
    Include:
      name: include
      in: query
      description: Comma-separated safe response projection for this capability
      schema:
        type: string
    Page:
      name: page
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 1000
        default: 1
    Limit:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 10
    Sort:
      name: sort
      in: query
      description: Sort ID published by /api/fields in sort_catalog.
      schema:
        type: string
    IncludeMeta:
      name: include_meta
      in: query
      description: Include request, contract and credit metadata in _meta.
      schema:
        type: boolean
        default: false
    Breakdown:
      name: breakdown
      in: query
      description: Return an exact categorical distribution instead of company rows. Use an allowlisted terms facet from /api/fields.
      schema:
        type: string
    BreakdownLimit:
      name: breakdown_limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 30
        default: 20
    Population:
      name: population
      in: query
      description: Population used by a breakdown request.
      schema:
        type: string
        enum: [all_domains, ecommerce]
        default: all_domains
  responses:
    Problem:
      description: API error
      headers:
        Server-Timing:
          $ref: "#/components/headers/ServerTiming"
        X-Semantil-Contract-Version:
          $ref: "#/components/headers/ContractVersion"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    SearchResult:
      description: Search result
      headers:
        Server-Timing:
          $ref: "#/components/headers/ServerTiming"
        X-Semantil-Contract-Version:
          $ref: "#/components/headers/ContractVersion"
        X-Credits-Cost:
          $ref: "#/components/headers/CreditsCost"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/SearchResult"
    SearchOrBreakdownResult:
      description: Company rows or an exact categorical breakdown
      headers:
        Server-Timing:
          $ref: "#/components/headers/ServerTiming"
        X-Semantil-Contract-Version:
          $ref: "#/components/headers/ContractVersion"
        X-Credits-Cost:
          $ref: "#/components/headers/CreditsCost"
      content:
        application/json:
          schema:
            oneOf:
              - $ref: "#/components/schemas/SearchResult"
              - $ref: "#/components/schemas/BreakdownResult"
  schemas:
    NullableString:
      type: string
      nullable: true
    NullableNumber:
      type: number
      nullable: true
    NullableInteger:
      type: integer
      nullable: true
    NullableBoolean:
      type: boolean
      nullable: true
    StringArray:
      type: array
      items:
        type: string
    Problem:
      type: object
      additionalProperties: true
      required: [type, title, status, detail, code]
      properties:
        type:
          type: string
          format: uri
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
        code:
          type: string
        request_id:
          type: string
    DataProduct:
      type: object
      additionalProperties: false
      required: [id, label, description, credits_when_found, fields]
      properties:
        id:
          type: string
          enum: [profile, tech, company, contacts, commerce, merchant_size]
        label:
          type: string
        description:
          type: string
        credits_when_found:
          type: integer
          minimum: 0
        fields:
          $ref: "#/components/schemas/StringArray"
    DataProductBundle:
      type: object
      additionalProperties: false
      required: [id, label, description, products, individual_maximum_credits, maximum_credits]
      properties:
        id:
          type: string
          enum: [all]
        label:
          type: string
        description:
          type: string
        products:
          $ref: "#/components/schemas/StringArray"
        individual_maximum_credits:
          type: integer
        maximum_credits:
          type: integer
    DataProductCatalog:
      type: object
      additionalProperties: false
      required: [object, contract_version, status, billing, default_products, products, bundles]
      properties:
        object:
          type: string
          enum: [data_product_list]
        contract_version:
          type: string
        status:
          type: string
          enum: [preview]
        billing:
          type: object
        default_products:
          $ref: "#/components/schemas/StringArray"
        products:
          type: array
          items:
            $ref: "#/components/schemas/DataProduct"
        bundles:
          type: array
          items:
            $ref: "#/components/schemas/DataProductBundle"
    ProductUsageEntry:
      type: object
      additionalProperties: false
      required: [product, status, credits]
      properties:
        product:
          type: string
          enum: [profile, tech, company, contacts, commerce, merchant_size]
        status:
          type: string
          enum: [found, not_found]
        credits:
          type: integer
    UsageReceipt:
      type: object
      additionalProperties: false
      required: [unit, price_book, maximum, subtotal, bundle, discount, charged, remaining, breakdown]
      properties:
        unit:
          type: string
          enum: [credit]
        price_book:
          type: string
        maximum:
          type: integer
        subtotal:
          type: integer
        bundle:
          type: string
          nullable: true
        discount:
          type: integer
        charged:
          type: integer
        remaining:
          type: integer
          nullable: true
        breakdown:
          type: array
          items:
            $ref: "#/components/schemas/ProductUsageEntry"
    DomainMeta:
      type: object
      additionalProperties: true
      required: [request_id, contract_version, status, plan, products_requested, products_returned, fields, usage]
      properties:
        request_id:
          type: string
        contract_version:
          type: string
        status:
          type: string
          enum: [preview]
        plan:
          type: string
        products_requested:
          $ref: "#/components/schemas/StringArray"
        products_returned:
          $ref: "#/components/schemas/StringArray"
        fields:
          $ref: "#/components/schemas/StringArray"
        usage:
          $ref: "#/components/schemas/UsageReceipt"
    DomainResult:
      type: object
      additionalProperties: false
      required: [object, found, domain, _meta]
      properties:
        object: { type: string, enum: [domain] }
        found: { type: boolean, enum: [true] }
        domain: { type: string }
        url: { $ref: "#/components/schemas/NullableString" }
        final_url: { $ref: "#/components/schemas/NullableString" }
        country: { $ref: "#/components/schemas/NullableString" }
        country_iso: { $ref: "#/components/schemas/NullableString" }
        region: { $ref: "#/components/schemas/StringArray" }
        city: { $ref: "#/components/schemas/NullableString" }
        state: { $ref: "#/components/schemas/NullableString" }
        state_iso: { $ref: "#/components/schemas/NullableString" }
        text_lang: { $ref: "#/components/schemas/NullableString" }
        site_category: { $ref: "#/components/schemas/NullableString" }
        categories: { $ref: "#/components/schemas/StringArray" }
        subcategory: { $ref: "#/components/schemas/NullableString" }
        subcategories: { $ref: "#/components/schemas/StringArray" }
        subcategory_id: { $ref: "#/components/schemas/NullableString" }
        subcategory_ids: { $ref: "#/components/schemas/StringArray" }
        business_market_id: { $ref: "#/components/schemas/NullableInteger", description: "Release-scoped ID of the canonical global business market." }
        business_market: { $ref: "#/components/schemas/StringArray" }
        business_segment: { $ref: "#/components/schemas/StringArray" }
        industry_cluster: { $ref: "#/components/schemas/StringArray", deprecated: true, description: "Transition alias of business_segment." }
        is_place: { $ref: "#/components/schemas/NullableBoolean" }
        place_categories: { $ref: "#/components/schemas/StringArray" }
        place_category_ids: { $ref: "#/components/schemas/StringArray" }
        place_category_groups: { $ref: "#/components/schemas/StringArray" }
        place_category_group_ids: { $ref: "#/components/schemas/StringArray" }
        meta_title: { $ref: "#/components/schemas/NullableString" }
        meta_description: { $ref: "#/components/schemas/NullableString" }
        web_presence_bucket: { $ref: "#/components/schemas/NullableString" }
        web_presence_trend_12m: { $ref: "#/components/schemas/NullableString" }
        web_popularity_score: { $ref: "#/components/schemas/NullableNumber" }
        pages_count: { $ref: "#/components/schemas/NullableNumber" }
        social:
          type: object
          nullable: true
          additionalProperties:
            type: string
            nullable: true
        website_first_seen_year: { $ref: "#/components/schemas/NullableInteger" }
        website_first_seen_month: { $ref: "#/components/schemas/NullableString" }
        store_age_tier: { $ref: "#/components/schemas/NullableString" }
        last_scanned_at: { $ref: "#/components/schemas/NullableString" }
        record_updated_at: { $ref: "#/components/schemas/NullableString" }
        primary_platform: { $ref: "#/components/schemas/NullableString" }
        platforms: { $ref: "#/components/schemas/StringArray" }
        tech: { $ref: "#/components/schemas/StringArray" }
        tech_version: { $ref: "#/components/schemas/StringArray" }
        premium_tech: { $ref: "#/components/schemas/StringArray" }
        tech_maturity_level: { $ref: "#/components/schemas/NullableString" }
        estimated_monthly_tech_spend_usd: { $ref: "#/components/schemas/NullableNumber" }
        saas_count: { $ref: "#/components/schemas/NullableNumber" }
        tech_diversity: { $ref: "#/components/schemas/NullableNumber" }
        tech_observed_at: { $ref: "#/components/schemas/NullableString" }
        tech_scan_type: { $ref: "#/components/schemas/NullableString" }
        tech_scan_count: { $ref: "#/components/schemas/NullableNumber" }
        tech_status_code: { $ref: "#/components/schemas/NullableNumber" }
        ecommerce_plugins: { $ref: "#/components/schemas/StringArray" }
        company_name: { $ref: "#/components/schemas/NullableString" }
        employees_range: { $ref: "#/components/schemas/NullableString" }
        vat_number: { $ref: "#/components/schemas/NullableString" }
        vat_country: { $ref: "#/components/schemas/NullableString" }
        vat_checksum_valid: { $ref: "#/components/schemas/NullableBoolean" }
        vat_occurrences: { $ref: "#/components/schemas/NullableNumber" }
        vat_observed_at: { $ref: "#/components/schemas/NullableString" }
        vat_source_url: { $ref: "#/components/schemas/NullableString" }
        vat_source_context: { $ref: "#/components/schemas/NullableString" }
        contact_email: { $ref: "#/components/schemas/NullableString" }
        contact_email_type: { $ref: "#/components/schemas/NullableString" }
        contact_email_observed_at: { $ref: "#/components/schemas/NullableString" }
        contact_email_source_url: { $ref: "#/components/schemas/NullableString" }
        contact_email_source_context: { $ref: "#/components/schemas/NullableString" }
        contact_phone: { $ref: "#/components/schemas/NullableString" }
        contact_phone_observed_at: { $ref: "#/components/schemas/NullableString" }
        contact_phone_source_url: { $ref: "#/components/schemas/NullableString" }
        contact_phone_source_context: { $ref: "#/components/schemas/NullableString" }
        is_ecommerce: { $ref: "#/components/schemas/NullableBoolean" }
        ecommerce_probability: { $ref: "#/components/schemas/NullableNumber" }
        cross_border_countries: { $ref: "#/components/schemas/StringArray" }
        cross_border_domains: { $ref: "#/components/schemas/StringArray" }
        cross_border_cluster_size_range: { $ref: "#/components/schemas/NullableString" }
        products_count: { $ref: "#/components/schemas/NullableInteger" }
        median_product_price_usd: { $ref: "#/components/schemas/NullableNumber" }
        payment_providers: { $ref: "#/components/schemas/StringArray" }
        shipping_carriers: { $ref: "#/components/schemas/StringArray" }
        merchant_size_score: { $ref: "#/components/schemas/NullableInteger" }
        merchant_size_band: { $ref: "#/components/schemas/NullableString" }
        merchant_size_country_score: { $ref: "#/components/schemas/NullableInteger" }
        merchant_size_country_band: { $ref: "#/components/schemas/NullableString" }
        merchant_size_confidence: { $ref: "#/components/schemas/NullableString" }
        merchant_size_source: { $ref: "#/components/schemas/NullableString" }
        _meta: { $ref: "#/components/schemas/DomainMeta" }
    DomainNotFound:
      type: object
      additionalProperties: false
      required: [object, found, domain, _meta]
      properties:
        object: { type: string, enum: [domain] }
        found: { type: boolean, enum: [false] }
        domain: { type: string }
        _meta: { $ref: "#/components/schemas/DomainMeta" }
    SearchRequest:
      type: object
      additionalProperties: false
      properties:
        query: { type: string }
        mode: { type: string, enum: [lexical, semantic], default: lexical }
        query_operator: { type: string, enum: [and, or], default: or }
        filters: { type: object, additionalProperties: true }
        not_filters: { type: object, additionalProperties: true }
        exclude_domains:
          type: array
          maxItems: 20000
          items: { type: string }
        include: { type: string }
        page: { type: integer, minimum: 1, maximum: 1000 }
        limit: { type: integer, minimum: 1, maximum: 100 }
        sort: { type: string, description: Sort ID published by /api/fields in sort_catalog. }
        include_meta: { type: boolean, default: false }
    SearchResult:
      type: object
      additionalProperties: true
      required: [object, query, mode, retrieval_engine, retrieval_requested, retrieval_fallback, total_semantics, query_operator, total, page, per_page, results]
      properties:
        object: { type: string, enum: [search_result] }
        query: { type: string }
        mode: { type: string, enum: [lexical, semantic] }
        retrieval_engine:
          type: string
          enum: [keyword, semantic]
          description: Generic retrieval method used for this response. Internal engine details are not part of the public contract.
        retrieval_requested:
          type: string
          enum: [keyword, semantic]
          description: Generic retrieval method requested by the caller.
        retrieval_fallback: { type: boolean }
        total_semantics: { type: string }
        candidate_population: { type: integer, nullable: true }
        max_retrievable: { type: integer }
        query_operator: { type: string, enum: [and, or] }
        total: { type: integer }
        page: { type: integer }
        per_page: { type: integer }
        generation: { type: string, nullable: true }
        results:
          type: array
          items:
            type: object
            description: Fail-closed projection of fields listed by /api/fields for the search context.
    BreakdownResult:
      type: object
      additionalProperties: false
      required:
        - object
        - field
        - population
        - query
        - query_operator
        - segment_total
        - field_present_count
        - missing_count
        - coverage_percent
        - unique_value_count
        - returned_buckets_exhaustive
        - scope
        - bucket_semantics
        - buckets
      properties:
        object: { type: string, enum: [breakdown_result] }
        field: { type: string }
        population: { type: string, enum: [all_domains, ecommerce] }
        query: { type: string }
        query_operator: { type: string, enum: [and, or] }
        filters: { type: object, additionalProperties: true }
        not_filters: { type: object, additionalProperties: true }
        segment_total: { type: integer }
        field_present_count: { type: integer }
        missing_count: { type: integer }
        coverage_percent: { type: number }
        unique_value_count: { type: integer }
        returned_buckets_exhaustive: { type: boolean }
        generation: { type: string, nullable: true }
        scope: { type: object, additionalProperties: true }
        bucket_semantics:
          type: object
          additionalProperties: false
          required: [multi_valued, bucket_counts_may_overlap]
          properties:
            multi_valued: { type: boolean }
            bucket_counts_may_overlap: { type: boolean }
        diagnostics:
          type: array
          items: { type: object, additionalProperties: true }
        buckets:
          type: array
          maxItems: 30
          items:
            type: object
            additionalProperties: false
            required: [value, label, count, share_of_segment_percent, share_of_field_present_percent]
            properties:
              value: { type: string }
              label: { type: string }
              count: { type: integer }
              share_of_segment_percent: { type: number }
              share_of_field_present_percent: { type: number }
    LookalikeResult:
      type: object
      additionalProperties: true
      required: [object, found, domain, results]
      properties:
        object: { type: string, enum: [lookalike_list] }
        found: { type: boolean }
        domain: { type: string }
        strategy: { type: string, nullable: true }
        results:
          type: array
          items:
            type: object
            description: Ranked candidate with a fail-closed lookalike field projection.
    Account:
      type: object
      additionalProperties: false
      required: [object, authenticated, contract_version, plan, credits, account, capabilities]
      properties:
        object: { type: string, enum: [account] }
        authenticated: { type: boolean, enum: [true] }
        contract_version: { type: string }
        plan: { type: string }
        credits: { type: object }
        account: { type: object }
        capabilities:
          type: object
          properties:
            search_fields: { $ref: "#/components/schemas/StringArray" }
            lookalike_fields: { $ref: "#/components/schemas/StringArray" }
            products: { $ref: "#/components/schemas/StringArray" }
    Field:
      type: object
      additionalProperties: false
      required: [field, label, type, product, contexts]
      properties:
        field: { type: string }
        label: { type: string }
        type: { type: string, enum: [string, number, boolean, array, object] }
        product: { type: string, nullable: true }
        contexts: { $ref: "#/components/schemas/StringArray" }
    FieldsResponse:
      type: object
      additionalProperties: true
      required: [contract_version, authenticated, current_plan, capabilities, field_catalog, products, bundles, facet_catalog, sort_catalog]
      properties:
        contract_version: { type: string }
        authenticated: { type: boolean }
        current_plan: { type: string, nullable: true }
        capabilities:
          type: object
          additionalProperties: false
          required: [semantic_search]
          properties:
            semantic_search: { type: boolean }
        field_catalog:
          type: array
          items: { $ref: "#/components/schemas/Field" }
        products:
          type: array
          items: { $ref: "#/components/schemas/DataProduct" }
        bundles:
          type: array
          items: { $ref: "#/components/schemas/DataProductBundle" }
        facet_catalog: { type: array, items: { type: object } }
        sort_catalog: { type: array, items: { type: object } }
