components:
  parameters:
    adminUserID:
      in: path
      name: id
      required: true
      schema:
        format: uuid
        type: string
    apiClientID:
      in: path
      name: id
      required: true
      schema:
        format: uuid
        type: string
    avatarSize:
      in: query
      name: s
      schema:
        maximum: 256
        minimum: 32
        type: integer
    domain:
      in: path
      name: domain
      required: true
      schema:
        type: string
    eventDimensionKey:
      deprecated: true
      description: Deprecated single event dimension filter. Prefer repeatable filter=type:value.
      in: query
      name: dimension_key
      schema:
        enum:
          - path
          - hostname
          - referrer
          - referrer_host
          - device
          - country
          - city
          - provider
          - asn
          - browser
          - language
          - utm_campaign
          - utm_content
          - utm_medium
          - utm_source
          - utm_term
        type: string
    eventDimensionValue:
      deprecated: true
      description: Deprecated single event dimension value. Requires dimension_key.
      in: query
      name: dimension_value
      schema:
        type: string
    eventName:
      in: query
      name: event_name
      required: true
      schema:
        type: string
    eventPropertyKey:
      in: query
      name: property_key
      schema:
        type: string
    eventPropertyKeyRequired:
      in: query
      name: property_key
      required: true
      schema:
        type: string
    eventPropertyValue:
      in: query
      name: property_value
      schema:
        type: string
    filter:
      description: 'Filter in form type:value (repeatable). Supported types: path, hostname, referrer, referrer_host, device, country, city, provider, asn, browser, language, ai_bot, ai_bot_category, ai_source, utm_campaign, utm_content, utm_medium, utm_source, utm_term, qr_code_id.'
      in: query
      name: filter
      schema:
        type: string
    filterType:
      in: query
      name: filter_type
      schema:
        type: string
    filterValue:
      in: query
      name: filter_value
      schema:
        type: string
    format:
      description: 'Export format. Supported values: xlsx, csv, parquet, json, ndjson. Defaults: xlsx for takeout endpoints, csv for hits export endpoints.'
      in: query
      name: format
      schema:
        enum:
          - xlsx
          - csv
          - parquet
          - json
          - ndjson
        type: string
    from:
      in: query
      name: from
      schema:
        format: date-time
        type: string
    funnelID:
      in: path
      name: funnelID
      required: true
      schema:
        format: uuid
        type: string
    funnelIDQuery:
      description: Repeatable funnel definition UUID used to select first-step session cohorts.
      in: query
      name: funnel_id
      schema:
        items:
          format: uuid
          type: string
        type: array
    goalID:
      in: path
      name: goalID
      required: true
      schema:
        format: uuid
        type: string
    goalIDQuery:
      description: Repeatable goal definition UUID used to select matching-session cohorts.
      in: query
      name: goal_id
      schema:
        items:
          format: uuid
          type: string
        type: array
    itemID:
      in: query
      name: item_id
      schema:
        type: string
    itemName:
      in: query
      name: item_name
      schema:
        type: string
    limit:
      in: query
      name: limit
      schema:
        maximum: 100
        minimum: 1
        type: integer
    offset:
      in: query
      name: offset
      schema:
        minimum: 0
        type: integer
    order:
      in: query
      name: order
      schema:
        enum:
          - asc
          - desc
        type: string
    passkeyID:
      in: path
      name: id
      required: true
      schema:
        format: uuid
        type: string
    qrID:
      in: path
      name: qrID
      required: true
      schema:
        format: uuid
        type: string
    query:
      in: query
      name: q
      schema:
        type: string
    reportConfirmationToken:
      description: Single-use opaque report-recipient confirmation token.
      in: path
      name: opaque_token
      required: true
      schema:
        type: string
    reportID:
      description: Report definition UUID.
      in: path
      name: report_id
      required: true
      schema:
        format: uuid
        type: string
    reportRecipientID:
      description: Stable report recipient UUID.
      in: path
      name: recipient_id
      required: true
      schema:
        format: uuid
        type: string
    reportRunID:
      description: Report run UUID.
      in: path
      name: run_id
      required: true
      schema:
        format: uuid
        type: string
    ruleID:
      in: path
      name: ruleID
      required: true
      schema:
        format: uuid
        type: string
    shareID:
      in: path
      name: shareID
      required: true
      schema:
        format: uuid
        type: string
    siteID:
      in: path
      name: id
      required: true
      schema:
        format: uuid
        type: string
    sort:
      in: query
      name: sort
      schema:
        type: string
    teamAPIClientID:
      in: path
      name: clientId
      required: true
      schema:
        format: uuid
        type: string
    teamID:
      in: path
      name: id
      required: true
      schema:
        format: uuid
        type: string
    to:
      in: query
      name: to
      schema:
        format: date-time
        type: string
    token:
      in: path
      name: token
      required: true
      schema:
        type: string
    trackingDomainID:
      in: path
      name: domainId
      required: true
      schema:
        format: uuid
        type: string
    unsubscribeToken:
      description: Opaque signed unsubscribe token.
      in: path
      name: opaque_token
      required: true
      schema:
        type: string
    userID:
      in: path
      name: userId
      required: true
      schema:
        format: uuid
        type: string
    webVitalMetric:
      description: Web Vital metric.
      in: query
      name: metric
      schema:
        enum:
          - LCP
          - INP
          - CLS
          - FCP
          - TTFB
        type: string
    webVitalPath:
      description: Exact normalized page path.
      in: query
      name: path
      schema:
        type: string
    webVitalRating:
      description: Web Vital threshold rating.
      in: query
      name: rating
      schema:
        enum:
          - good
          - needs_improvement
          - poor
        type: string
  responses:
    DatabaseUnavailable:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DatabaseUnavailable'
      description: The shared database or an open tenant database is recovering, requires operator attention, or is unavailable.
      headers:
        Retry-After:
          description: Seconds before retrying the request.
          schema:
            minimum: 1
            type: integer
  schemas:
    AIActivityReport:
      description: 'Unified AI activity report merging tracked AI hits with server-log AI fetch records. Every count follows one rule: tracked hits plus fetch records. Depth metrics describe the fetch side only, because tracked hits carry no status code, response time, or transferred bytes.'
      properties:
        ai_requests:
          type: integer
        comparison:
          description: Merged counters for the compare_from/compare_to window. Absent unless a comparison range is requested.
          properties:
            ai_requests:
              type: integer
            fetch_count:
              type: integer
            pageviews:
              type: integer
            paths_crawled:
              type: integer
            referral_visits:
              type: integer
            tracked_hits:
              type: integer
            unique_agents:
              type: integer
          type: object
        error_rate_4xx:
          type: number
        error_rate_5xx:
          type: number
        fetch_count:
          type: integer
        median_response_ms:
          type: integer
        pageviews:
          description: Total hits for the range under the non-AI filters only, so the AI share of a segment stays comparable while an AI dimension is drilled into.
          type: integer
        paths_crawled:
          description: Distinct paths seen on either side, counted before the top-list cap.
          type: integer
        referral_visits:
          description: Distinct sessions referred by a known AI surface across the whole range.
          type: integer
        series:
          items:
            $ref: '#/components/schemas/AIActivitySeriesPoint'
          type: array
        top_agents:
          items:
            $ref: '#/components/schemas/AIActivityStat'
          type: array
        top_agents_by_category:
          additionalProperties:
            items:
              $ref: '#/components/schemas/AIActivityStat'
            type: array
          description: Merged agent top list per AI agent category, keyed by category.
          type: object
        top_categories:
          items:
            $ref: '#/components/schemas/AIActivityStat'
          type: array
        top_error_paths:
          items:
            $ref: '#/components/schemas/AIActivityStat'
          type: array
        top_families:
          items:
            $ref: '#/components/schemas/AIActivityStat'
          type: array
        top_paths:
          items:
            $ref: '#/components/schemas/AIActivityStat'
          type: array
        top_resource_types:
          items:
            $ref: '#/components/schemas/AIActivityStat'
          type: array
        top_sources:
          items:
            $ref: '#/components/schemas/AIActivityStat'
          type: array
        total_bytes:
          type: integer
        tracked_hits:
          type: integer
        unique_agents:
          type: integer
      type: object
    AIActivitySeriesPoint:
      properties:
        ai_requests:
          description: Tracked AI bot hits plus AI fetch records in the bucket.
          type: integer
        fetch_count:
          type: integer
        referral_visits:
          description: 'Distinct sessions referred by a known AI surface inside the bucket. Buckets are not additive: a session spanning two buckets counts in both.'
          type: integer
        time:
          format: date-time
          type: string
        tracked_hits:
          type: integer
      type: object
    AIActivityStat:
      description: 'One row of a merged AI activity top list. value is always tracked_hits + fetch_count. Dimensions only one source carries report zero on the other side: families and resource types exist only in server-log fetch records, referral sources only in tracked hits.'
      properties:
        fetch_count:
          type: integer
        name:
          type: string
        tracked_hits:
          type: integer
        value:
          type: integer
      type: object
    AIAgentCatalog:
      properties:
        agents:
          items:
            properties:
              category:
                enum:
                  - ai_training_crawler
                  - ai_search_indexer
                  - ai_assistant
                  - ai_agent
                  - ai_coding_agent
                  - other_ai
                type: string
              family:
                type: string
              icon_host:
                type: string
              name:
                type: string
            type: object
          type: array
        ai_referrers:
          items:
            properties:
              icon_host:
                type: string
              name:
                type: string
            type: object
          type: array
        generated_at:
          format: date-time
          type: string
      type: object
    AIFetch:
      properties:
        assistant_category:
          enum:
            - ai_training_crawler
            - ai_search_indexer
            - ai_assistant
            - ai_agent
            - ai_coding_agent
            - other_ai
          type: string
        assistant_family:
          type: string
        assistant_name:
          type: string
        bytes_served:
          type: integer
        content_type:
          type: string
        hostname:
          type: string
        id:
          format: uuid
          type: string
        path:
          type: string
        resource_type:
          enum:
            - html
            - document
            - image
            - other
          type: string
        response_ms:
          type: integer
        site_id:
          format: uuid
          type: string
        status_code:
          type: integer
        timestamp:
          format: date-time
          type: string
        user_agent:
          type: string
      type: object
    AIFetchCitationYieldRow:
      properties:
        ai_referred_visits:
          type: integer
        assistant_name:
          type: string
        citation_yield_pct:
          type: number
        fetch_count:
          type: integer
        path:
          type: string
      type: object
    AIFetchCorrelationReport:
      properties:
        citation_yield:
          items:
            $ref: '#/components/schemas/AIFetchCitationYieldRow'
          type: array
        failure_hotspots:
          items:
            $ref: '#/components/schemas/AIFetchFailureHotspot'
          type: array
        opportunity_pages:
          items:
            $ref: '#/components/schemas/AIFetchOpportunityRow'
          type: array
        summary:
          $ref: '#/components/schemas/AIFetchCorrelationSummary'
      type: object
    AIFetchCorrelationSummary:
      properties:
        ai_referred_visits:
          type: integer
        correlated_paths:
          type: integer
        fetched_paths:
          type: integer
        total_fetches:
          type: integer
        uncorrelated_fetches:
          type: integer
      type: object
    AIFetchFailureHotspot:
      properties:
        assistant_name:
          type: string
        error_rate_pct:
          type: number
        error_requests:
          type: integer
        path_prefix:
          type: string
        total_requests:
          type: integer
      type: object
    AIFetchIngestPayload:
      properties:
        bytes_served:
          minimum: 0
          type: integer
        content_type:
          type: string
        hostname:
          type: string
        path:
          type: string
        response_ms:
          minimum: 0
          type: integer
        status_code:
          maximum: 599
          minimum: 100
          type: integer
        user_agent:
          type: string
      required:
        - path
        - status_code
        - user_agent
      type: object
    AIFetchOpportunityRow:
      properties:
        ai_referred_visits:
          type: integer
        error_rate_pct:
          type: number
        error_requests:
          type: integer
        fetch_count:
          type: integer
        path:
          type: string
      type: object
    AIFetchOverview:
      properties:
        error_rate_4xx:
          type: number
        error_rate_5xx:
          type: number
        median_response_ms:
          type: integer
        resource_type_split:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_assistants:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_error_paths:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_families:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_paths:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        total_bytes:
          type: integer
        total_requests:
          type: integer
        unique_assistants:
          type: integer
        unique_paths:
          type: integer
      type: object
    AIFetchSeriesPoint:
      properties:
        count:
          type: integer
        time:
          format: date-time
          type: string
      type: object
    APIClient:
      properties:
        created_at:
          format: date-time
          type: string
        description:
          type: string
        expires_at:
          format: date-time
          type: string
        id:
          format: uuid
          type: string
        instance_role:
          type: string
        last_used_at:
          format: date-time
          type: string
        name:
          type: string
        owner_type:
          enum:
            - personal
            - team
          type: string
        revoked_at:
          format: date-time
          type: string
        site_roles:
          description: Explicit site grants. Empty means the API client has no site-scoped analytics, MCP, or ingest access.
          items:
            $ref: '#/components/schemas/APIClientSiteRole'
          type: array
        tenant_id:
          format: uuid
          type: string
        updated_at:
          format: date-time
          type: string
        user_id:
          format: uuid
          type: string
      type: object
    APIClientCreateResponse:
      properties:
        client:
          $ref: '#/components/schemas/APIClient'
        token:
          type: string
      type: object
    APIClientSiteRole:
      properties:
        role:
          type: string
        site_id:
          format: uuid
          type: string
      type: object
    AdminDeleteTeamResponse:
      properties:
        name:
          type: string
        status:
          type: string
        team_id:
          format: uuid
          type: string
      type: object
    AdminDeleteUserBlockedResponse:
      properties:
        code:
          type: string
        message:
          type: string
        status:
          type: string
        teams:
          items:
            $ref: '#/components/schemas/Team'
          type: array
      required:
        - status
        - code
        - message
        - teams
      type: object
    AdminDisableUserMFAResponse:
      properties:
        passkeys_deleted:
          type: integer
        sessions_invalidated:
          type: integer
        status:
          type: string
        totp_disabled:
          type: boolean
      required:
        - status
        - totp_disabled
        - passkeys_deleted
        - sessions_invalidated
      type: object
    AdminTeam:
      properties:
        created_at:
          format: date-time
          type: string
        id:
          format: uuid
          type: string
        is_archived:
          type: boolean
        is_default:
          type: boolean
        member_count:
          type: integer
        name:
          type: string
        site_count:
          type: integer
      type: object
    AskAIAction:
      properties:
        format:
          enum:
            - xlsx
            - json
            - csv
            - ndjson
          type: string
        label:
          type: string
        target:
          type: string
        type:
          enum:
            - navigate
            - download_export
          type: string
      required:
        - type
        - label
        - target
      type: object
    AskAIChart:
      properties:
        rows:
          items:
            additionalProperties:
              type:
                - string
                - number
                - boolean
                - "null"
            type: object
          type: array
        series:
          items:
            $ref: '#/components/schemas/AskAIChartSeries'
          type: array
        title:
          type: string
        type:
          enum:
            - line
            - bar
            - table
          type: string
        x_key:
          type: string
      required:
        - type
        - title
        - rows
      type: object
    AskAIChartSeries:
      properties:
        key:
          type: string
        label:
          type: string
      required:
        - key
        - label
      type: object
    AskAICitation:
      properties:
        label:
          type: string
        tool_call_id:
          type: string
      required:
        - label
        - tool_call_id
      type: object
    AskAIFilter:
      properties:
        type:
          type: string
        value:
          type: string
      required:
        - type
        - value
      type: object
    AskAIHistoryEntry:
      properties:
        action_count:
          type: integer
        action_types:
          items:
            enum:
              - navigate
              - download_export
            type: string
          type: array
        answer_chars:
          type: integer
        chart_count:
          type: integer
        chart_types:
          items:
            enum:
              - line
              - bar
              - table
            type: string
          type: array
        citation_count:
          type: integer
        created_at:
          format: date-time
          type: string
        error_category:
          type: string
        input_hash:
          description: SHA-256 hash of the audit-safe request snapshot.
          type: string
        input_tokens:
          type: integer
        lifecycle_event_count:
          type: integer
        model:
          type: string
        output_hash:
          description: SHA-256 hash of the validated Ask AI output.
          type: string
        output_tokens:
          type: integer
        provider:
          type: string
        run_id:
          format: uuid
          type: string
        status:
          enum:
            - reserved
            - success
            - failure
          type: string
        template_version:
          type: string
        tool_call_count:
          type: integer
        tool_names:
          items:
            type: string
          type: array
        total_tokens:
          type: integer
      required:
        - run_id
        - created_at
        - status
        - answer_chars
        - citation_count
        - chart_count
        - action_count
        - chart_types
        - action_types
        - input_tokens
        - output_tokens
        - total_tokens
        - tool_call_count
        - lifecycle_event_count
        - tool_names
      type: object
    AskAIHistoryResponse:
      properties:
        entries:
          items:
            $ref: '#/components/schemas/AskAIHistoryEntry'
          type: array
        has_more:
          type: boolean
        limit:
          type: integer
        offset:
          type: integer
        total:
          type: integer
      required:
        - entries
        - total
        - limit
        - offset
        - has_more
      type: object
    AskAIMessage:
      properties:
        content:
          type: string
        role:
          enum:
            - user
            - assistant
          type: string
      required:
        - role
        - content
      type: object
    AskAIRequest:
      properties:
        filters:
          items:
            $ref: '#/components/schemas/AskAIFilter'
          type: array
        from:
          format: date
          type: string
        history:
          items:
            $ref: '#/components/schemas/AskAIMessage'
          type: array
        query:
          type: string
        route:
          type: string
        to:
          format: date
          type: string
      required:
        - query
      type: object
    AskAIResponse:
      properties:
        actions:
          items:
            $ref: '#/components/schemas/AskAIAction'
          type: array
        answer_markdown:
          type: string
        charts:
          items:
            $ref: '#/components/schemas/AskAIChart'
          type: array
        citations:
          items:
            $ref: '#/components/schemas/AskAICitation'
          type: array
        run_id:
          format: uuid
          type: string
      required:
        - run_id
        - answer_markdown
        - citations
        - charts
        - actions
      type: object
    AskAIStatus:
      properties:
        available:
          type: boolean
        budget_exhausted:
          type: boolean
        enabled:
          type: boolean
        model:
          type: string
        provider:
          type: string
        status:
          enum:
            - disabled
            - not_configured
            - available
            - budget_exhausted
          type: string
      required:
        - enabled
        - available
        - status
        - budget_exhausted
      type: object
    AskAIStreamEvent:
      properties:
        delta_markdown:
          description: Session-only markdown answer delta. Final charts, actions, and citations are returned only on final events.
          type: string
        error:
          description: Stable non-secret error label when an in-stream failure occurs.
          type: string
        message_key:
          description: Dashboard i18n key for safe progress or error copy.
          type: string
        response:
          $ref: '#/components/schemas/AskAIResponse'
        status:
          type: string
        tool_call_id:
          description: Provider-assigned tool call identifier for progress events.
          type: string
        tool_name:
          description: Safe HitKeep analytics tool identifier for progress events.
          type: string
        type:
          enum:
            - progress
            - delta
            - final
            - error
          type: string
      required:
        - type
      type: object
    AuthSession:
      properties:
        duration_seconds:
          type: integer
        expires_at:
          format: date-time
          type: string
        extendable:
          type: boolean
        issued_at:
          format: date-time
          type: string
        remember_expires_at:
          format: date-time
          type: string
        remember_me_duration_days:
          type: integer
        remembered:
          type: boolean
        timing_adjustable:
          type: boolean
        warning_seconds:
          type: integer
      type: object
    ChartDataPoint:
      properties:
        pageviews:
          type: integer
        time:
          format: date-time
          type: string
        visitors:
          type: integer
      type: object
    CloudStatus:
      properties:
        hosted:
          type: boolean
        jurisdiction:
          type: string
        region:
          type: string
        signup_enabled:
          type: boolean
        support_url:
          type: string
        upgrade_url:
          type: string
      type: object
    CreateCustomTrackingDomainRequest:
      properties:
        hostname:
          type: string
      required:
        - hostname
      type: object
    CustomTrackingDomain:
      properties:
        active:
          description: True when the domain is enabled and all verification states are verified.
          type: boolean
        created_at:
          format: date-time
          type: string
        dns_target:
          type: string
        dns_txt_name:
          type: string
        dns_txt_value:
          type: string
        enabled:
          type: boolean
        hostname:
          type: string
        id:
          format: uuid
          type: string
        last_checked_at:
          format: date-time
          type: string
        last_error:
          type: string
        last_tls_ask_at:
          format: date-time
          type: string
        target_status:
          enum:
            - pending
            - verified
            - failed
          type: string
        team_id:
          format: uuid
          type: string
        tls_mode:
          enum:
            - external
            - caddy-on-demand
          type: string
        tls_status:
          enum:
            - pending
            - verified
            - failed
          type: string
        updated_at:
          format: date-time
          type: string
        verification_status:
          enum:
            - pending
            - verified
            - failed
          type: string
        verified_at:
          format: date-time
          type: string
      required:
        - id
        - team_id
        - hostname
        - verification_status
        - target_status
        - tls_mode
        - tls_status
        - enabled
        - active
        - dns_txt_name
        - dns_txt_value
        - dns_target
        - created_at
        - updated_at
      type: object
    DatabaseUnavailable:
      properties:
        code:
          enum:
            - database_unavailable
            - database_recovering
            - database_needs_attention
          type: string
        message:
          type: string
        retry_after_seconds:
          minimum: 1
          type: integer
        status:
          enum:
            - error
          type: string
      required:
        - status
        - code
        - message
        - retry_after_seconds
      type: object
    DuckDBMemoryStat:
      properties:
        memory_bytes:
          format: int64
          type: integer
        tag:
          type: string
        temporary_storage_bytes:
          format: int64
          type: integer
      type: object
    EcommerceProductStat:
      properties:
        item_id:
          type: string
        item_name:
          type: string
        orders:
          type: integer
        quantity:
          type: integer
        revenue:
          type: number
      type: object
    EcommerceSeriesPoint:
      properties:
        orders:
          type: integer
        revenue:
          type: number
        time:
          format: date-time
          type: string
      type: object
    EcommerceSourceStat:
      properties:
        orders:
          type: integer
        referrer:
          type: string
        revenue:
          type: number
        utm_campaign:
          type: string
        utm_medium:
          type: string
        utm_source:
          type: string
      type: object
    EcommerceSummary:
      properties:
        average_order_value:
          type: number
        checkout_conversion_rate:
          type: number
        checkout_starts:
          type: integer
        currency:
          type: string
        orders:
          type: integer
        revenue:
          type: number
        top_asns:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_cities:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_providers:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
      type: object
    Error:
      properties:
        message:
          type: string
      type: object
    EventAudience:
      properties:
        imported_excluded:
          items:
            $ref: '#/components/schemas/ImportExclusionReason'
          type: array
        top_asns:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_cities:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_countries:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_devices:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_pages:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_providers:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_referrers:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
      type: object
    EventSeriesPoint:
      properties:
        count:
          type: integer
        time:
          format: date-time
          type: string
      type: object
    Funnel:
      properties:
        created_at:
          format: date-time
          type: string
        id:
          format: uuid
          type: string
        name:
          type: string
        site_id:
          format: uuid
          type: string
        steps:
          items:
            $ref: '#/components/schemas/FunnelStep'
          type: array
      type: object
    FunnelSeriesPoint:
      properties:
        completions:
          type: integer
        entries:
          type: integer
        time:
          format: date-time
          type: string
      type: object
    FunnelStats:
      properties:
        funnel_id:
          format: uuid
          type: string
        name:
          type: string
        overall_conversion_rate:
          type: number
        steps:
          items:
            $ref: '#/components/schemas/FunnelStepStats'
          type: array
        total_completions:
          type: integer
        total_entries:
          type: integer
      type: object
    FunnelStep:
      properties:
        type:
          enum:
            - event
            - path
          type: string
        value:
          type: string
      type: object
    FunnelStepStats:
      properties:
        conversion_rate:
          type: number
        dropoff:
          type: integer
        name:
          type: string
        step_index:
          type: integer
        visitors:
          type: integer
      type: object
    Goal:
      properties:
        created_at:
          format: date-time
          type: string
        id:
          format: uuid
          type: string
        name:
          type: string
        site_id:
          format: uuid
          type: string
        type:
          enum:
            - event
            - path
          type: string
        value:
          type: string
      type: object
    GoalSeriesPoint:
      properties:
        conversions:
          type: integer
        time:
          format: date-time
          type: string
      type: object
    GoalStats:
      properties:
        conversion_rate:
          type: number
        conversions:
          type: integer
        goal_id:
          format: uuid
          type: string
        name:
          type: string
      type: object
    Hit:
      properties:
        asn:
          type: integer
        asn_org:
          type: string
        city:
          type: string
        country_code:
          type: string
        hostname:
          type: string
        id:
          format: uuid
          type: string
        is_unique:
          type: boolean
        language:
          type: string
        page_id:
          format: uuid
          type: string
        path:
          type: string
        provider:
          type: string
        qr_code_id:
          format: uuid
          type: string
        referrer:
          type: string
        region:
          type: string
        screen_height:
          type: integer
        screen_width:
          type: integer
        session_id:
          format: uuid
          type: string
        site_id:
          format: uuid
          type: string
        timestamp:
          format: date-time
          type: string
        user_agent:
          type: string
        utm_campaign:
          type: string
        utm_content:
          type: string
        utm_medium:
          type: string
        utm_source:
          type: string
        utm_term:
          type: string
        viewport_height:
          type: integer
        viewport_width:
          type: integer
      type: object
    IPExclusion:
      properties:
        cidr:
          description: Normalized IP or CIDR value for cidr rules.
          type: string
        country_code:
          description: Uppercase ISO 3166-1 alpha-2 country code for country rules.
          type: string
        created_at:
          format: date-time
          type: string
        created_by:
          description: Creator ID. Omitted from inherited rows returned across scopes.
          format: uuid
          type: string
        description:
          type: string
        id:
          format: uuid
          type: string
        inherited:
          description: True when returned through a child scope's effective read.
          type: boolean
        path:
          description: Normalized, query-free, case-sensitive path matched on segment boundaries.
          type: string
        scope:
          description: Scope that owns this rule.
          enum:
            - instance
            - team
            - site
          type: string
        site_id:
          description: Owning site for site rules.
          format: uuid
          type: string
        team_id:
          description: Owning team for team rules.
          format: uuid
          type: string
        type:
          enum:
            - cidr
            - country
            - user_agent
            - path
          type: string
        user_agent:
          description: Case-insensitive user-agent substring for user_agent rules.
          type: string
      required:
        - id
        - type
        - created_at
        - inherited
      type: object
    IPExclusionCreateRequest:
      properties:
        cidr:
          description: IP or CIDR value for cidr rules. Plain IP values are normalized to /32 (IPv4) or /128 (IPv6).
          type: string
        country_code:
          description: ISO 3166-1 alpha-2 country code for country rules. Values are normalized to uppercase.
          type: string
        description:
          maxLength: 255
          type: string
        path:
          description: Path rule. Query and fragment are removed; duplicate or trailing slashes and dot segments are normalized before case-sensitive segment-boundary matching.
          minLength: 1
          type: string
        type:
          description: Rule type. Omitted cidr requests remain accepted for backward compatibility.
          enum:
            - cidr
            - country
            - user_agent
            - path
          type: string
        user_agent:
          description: Case-insensitive substring for user_agent rules. Leading and trailing whitespace is removed.
          minLength: 1
          type: string
      type: object
    ImportDatasetSummary:
      properties:
        events:
          type: integer
        files:
          items:
            type: string
          type: array
        key:
          type: string
        name:
          type: string
        pageviews:
          type: integer
        rows_accepted:
          type: integer
        rows_scanned:
          type: integer
        rows_skipped:
          type: integer
        visitors:
          type: integer
        visits:
          type: integer
      type: object
    ImportEventCoverage:
      properties:
        event_names:
          items:
            type: string
          type: array
        events:
          description: Queryable imported custom-event count.
          type: integer
        property_keys:
          items:
            type: string
          type: array
        rows_accepted:
          type: integer
        rows_scanned:
          type: integer
        visitors:
          type: integer
      type: object
    ImportEventDimensionCoverage:
      properties:
        available:
          items:
            type: string
          type: array
        reason:
          type: string
        unavailable:
          items:
            type: string
          type: array
      type: object
    ImportEventPropertyCoverage:
      properties:
        attributed_events:
          type: integer
        attributed_property_keys:
          items:
            type: string
          type: array
        attributed_rows:
          type: integer
        attributed_visitors:
          type: integer
        unattributed_events:
          type: integer
        unattributed_property_keys:
          items:
            type: string
          type: array
        unattributed_relationship:
          type: string
        unattributed_rows:
          type: integer
        unattributed_visitors:
          type: integer
        unavailable_relationship_message:
          type: string
      type: object
    ImportExclusionReason:
      properties:
        detail:
          type: string
        reason:
          type: string
      type: object
    ImportJob:
      properties:
        bytes_received:
          type: integer
        bytes_total:
          type: integer
        error:
          type: string
        id:
          format: uuid
          type: string
        manifest:
          $ref: '#/components/schemas/ImportManifest'
        provider:
          type: string
        rows_imported:
          type: integer
        rows_scanned:
          type: integer
        site_id:
          format: uuid
          type: string
        source_hash:
          type: string
        status:
          type: string
      type: object
    ImportManifest:
      properties:
        datasets:
          items:
            $ref: '#/components/schemas/ImportDatasetSummary'
          type: array
        date_end:
          format: date-time
          type: string
        date_start:
          format: date-time
          type: string
        event_coverage:
          $ref: '#/components/schemas/ImportEventCoverage'
        event_dimension_coverage:
          $ref: '#/components/schemas/ImportEventDimensionCoverage'
        event_property_coverage:
          $ref: '#/components/schemas/ImportEventPropertyCoverage'
        files:
          items:
            type: string
          type: array
        ignored_files:
          items:
            type: string
          type: array
        missing_files:
          items:
            type: string
          type: array
        overlap:
          $ref: '#/components/schemas/ImportOverlapSummary'
        provider:
          type: string
        rows_accepted:
          type: integer
        rows_scanned:
          type: integer
        rows_skipped:
          type: integer
        source_hash:
          type: string
        warnings:
          items:
            $ref: '#/components/schemas/ImportWarning'
          type: array
      type: object
    ImportOverlapSummary:
      properties:
        estimated_skipped_events:
          type: integer
        estimated_skipped_pageviews:
          type: integer
        estimated_skipped_rows:
          type: integer
        native_event_days:
          type: integer
        native_event_keys:
          type: integer
        native_traffic_days:
          type: integer
        policy:
          enum:
            - skip_native_day
          type: string
      type: object
    ImportStageCleanupRunResult:
      properties:
        bytes_cleaned:
          format: int64
          type: integer
        errors:
          items:
            type: string
          type: array
        files_cleaned:
          type: integer
        imports_cleaned:
          type: integer
        imports_marked_failed:
          type: integer
      type: object
    ImportWarning:
      properties:
        code:
          type: string
        file:
          type: string
        message:
          type: string
      type: object
    InstanceAuditEntry:
      properties:
        action:
          type: string
        actor_email_snapshot:
          type: string
        actor_id:
          format: uuid
          type: string
        actor_role_snapshot:
          type: string
        created_at:
          format: date-time
          type: string
        details:
          type: string
        id:
          format: uuid
          type: string
        ip_address:
          type: string
        ip_country_code:
          type: string
        outcome:
          type: string
        request_id:
          type: string
        target_id:
          type: string
        target_label:
          type: string
        target_type:
          type: string
        target_user_id:
          format: uuid
          type: string
        team_id:
          format: uuid
          type: string
        user_agent:
          type: string
      type: object
    InstanceAuditListResponse:
      properties:
        entries:
          items:
            $ref: '#/components/schemas/InstanceAuditEntry'
          type: array
        has_more:
          type: boolean
        limit:
          type: integer
        offset:
          type: integer
        total:
          type: integer
      type: object
    LoginResponse:
      properties:
        challenge_token:
          type: string
        factors:
          items:
            enum:
              - totp
              - passkey
              - recovery_code
              - email_link
            type: string
          type: array
        passkey:
          additionalProperties: true
          type: object
        status:
          type: string
      type: object
    MailDeliveryStatus:
      properties:
        available:
          type: boolean
        status:
          enum:
            - available
            - unavailable
          type: string
      required:
        - available
        - status
      type: object
    MetricStat:
      properties:
        name:
          type: string
        value:
          type: integer
      type: object
    OnboardingStep:
      properties:
        complete:
          type: boolean
        current:
          type: integer
        key:
          enum:
            - create_site
            - verify_tracking
            - automatic_events
            - invite_teammate
            - schedule_report
          type: string
        site_domain:
          type: string
        site_id:
          format: uuid
          type: string
        target:
          type: integer
      type: object
    OpenAPIVersionList:
      properties:
        latest:
          type: string
        versions:
          items:
            properties:
              latest:
                type: boolean
              openapi_url:
                type: string
              version:
                type: string
            type: object
          type: array
      type: object
    Opportunity:
      properties:
        action_key:
          type: string
        cited_evidence_ids:
          items:
            type: string
          type: array
        confidence:
          enum:
            - high
            - medium
          type: string
        copy_params:
          additionalProperties: true
          type: object
        created_at:
          format: date-time
          type: string
        detector_version:
          type: string
        digest_key:
          type: string
        evidence:
          items:
            $ref: '#/components/schemas/OpportunityEvidence'
          type: array
        generated_at:
          format: date-time
          type: string
        id:
          format: uuid
          type: string
        impact_label_key:
          type: string
        impact_value:
          type: string
        kind:
          enum:
            - conversion
            - traffic
            - ai
            - search
            - setup
          type: string
        route_icon:
          type: string
        route_label_key:
          type: string
        route_params:
          additionalProperties: true
          type: object
        score:
          type: integer
        score_breakdown:
          $ref: '#/components/schemas/OpportunityScoreBreakdown'
        site_id:
          format: uuid
          type: string
        status:
          enum:
            - new
            - saved
            - done
            - dismissed
          type: string
        summary_key:
          type: string
        title_key:
          type: string
        type_key:
          type: string
        updated_at:
          format: date-time
          type: string
      required:
        - id
        - site_id
        - kind
        - type_key
        - title_key
        - summary_key
        - action_key
        - digest_key
        - copy_params
        - impact_value
        - impact_label_key
        - confidence
        - score
        - score_breakdown
        - status
        - route_label_key
        - route_icon
        - detector_version
        - evidence
        - cited_evidence_ids
        - generated_at
        - created_at
        - updated_at
      type: object
    OpportunityDigestPreviewItem:
      properties:
        action_key:
          type: string
        category:
          type: string
        cited_evidence_ids:
          items:
            type: string
          type: array
        confidence:
          enum:
            - high
            - medium
          type: string
        copy_params:
          additionalProperties: true
          type: object
        digest_key:
          type: string
        evidence:
          items:
            $ref: '#/components/schemas/OpportunityEvidence'
          type: array
        id:
          format: uuid
          type: string
        impact_label_key:
          type: string
        impact_value:
          type: string
        kind:
          enum:
            - conversion
            - traffic
            - ai
            - search
            - setup
          type: string
        route_icon:
          type: string
        route_label_key:
          type: string
        route_params:
          additionalProperties: true
          type: object
        score:
          type: integer
        score_breakdown:
          $ref: '#/components/schemas/OpportunityScoreBreakdown'
        site_id:
          format: uuid
          type: string
        status:
          enum:
            - new
            - saved
          type: string
        title_key:
          type: string
        type_key:
          type: string
      required:
        - id
        - site_id
        - kind
        - type_key
        - category
        - title_key
        - action_key
        - digest_key
        - copy_params
        - impact_value
        - impact_label_key
        - confidence
        - score
        - score_breakdown
        - status
        - route_label_key
        - route_icon
        - evidence
        - cited_evidence_ids
      type: object
    OpportunityDigestPreviewResponse:
      properties:
        frequency:
          enum:
            - daily
            - weekly
          type: string
        items:
          items:
            $ref: '#/components/schemas/OpportunityDigestPreviewItem'
          type: array
        reason:
          enum:
            - ready
            - no_opportunities
            - unsupported_frequency
          type: string
        should_send:
          type: boolean
      required:
        - frequency
        - should_send
        - reason
        - items
      type: object
    OpportunityEvidence:
      properties:
        detail_key:
          type: string
        detail_params:
          additionalProperties: true
          type: object
        id:
          type: string
        label_key:
          type: string
        value:
          type: string
      required:
        - id
        - label_key
        - value
      type: object
    OpportunityGenerateResponse:
      properties:
        ai_status:
          type: string
        opportunities:
          items:
            $ref: '#/components/schemas/Opportunity'
          type: array
      required:
        - opportunities
        - ai_status
      type: object
    OpportunityListResponse:
      properties:
        opportunities:
          items:
            $ref: '#/components/schemas/Opportunity'
          type: array
      required:
        - opportunities
      type: object
    OpportunityScoreBreakdown:
      properties:
        actionability:
          type: integer
        effort:
          type: integer
        evidence_fit:
          type: integer
        freshness:
          type: integer
        impact:
          type: integer
        sample:
          type: integer
        total:
          type: integer
        urgency:
          type: integer
      required:
        - sample
        - impact
        - urgency
        - effort
        - actionability
        - evidence_fit
        - freshness
        - total
      type: object
    OpportunityStatusUpdateRequest:
      properties:
        status:
          enum:
            - new
            - saved
            - done
            - dismissed
          type: string
      required:
        - status
      type: object
    PaginatedHits:
      properties:
        data:
          items:
            $ref: '#/components/schemas/Hit'
          type: array
        total:
          type: integer
      type: object
    PermissionContext:
      properties:
        active_team_capabilities:
          items:
            type: string
          type: array
        active_team_id:
          format: uuid
          type: string
        active_team_role:
          type: string
        can_create_teams:
          type: boolean
        instance_capabilities:
          items:
            type: string
          type: array
        instance_permissions:
          items:
            type: string
          type: array
        instance_role:
          type: string
        permissions:
          additionalProperties:
            type: string
          type: object
        site_capabilities:
          additionalProperties:
            items:
              type: string
            type: array
          type: object
      type: object
    QRCode:
      properties:
        archived_at:
          format: date-time
          type: string
        created_at:
          format: date-time
          type: string
        created_by:
          format: uuid
          type: string
        custom_params:
          additionalProperties:
            type: string
          type: object
        destination_url:
          format: uri
          type: string
        has_asset:
          type: boolean
        id:
          format: uuid
          type: string
        name:
          type: string
        redirect_url:
          description: Dynamic HitKeep redirect URL to encode in printed QR assets.
          format: uri
          type: string
        site_id:
          format: uuid
          type: string
        style:
          additionalProperties: true
          type: object
        token_hint:
          type: string
        updated_at:
          format: date-time
          type: string
        utm_campaign:
          type: string
        utm_content:
          type: string
        utm_medium:
          type: string
        utm_source:
          type: string
        utm_term:
          type: string
      type: object
    QRCodeAsset:
      description: Public QR graphic metadata. The raster image is stored under the configured HitKeep data directory; filesystem storage keys and binary payloads are not exposed in JSON or table takeout.
      properties:
        byte_size:
          type: integer
        checksum:
          type: string
        content_type:
          enum:
            - image/png
            - image/jpeg
            - image/webp
          type: string
        created_at:
          format: date-time
          type: string
        filename:
          type: string
        height:
          type: integer
        qr_code_id:
          format: uuid
          type: string
        site_id:
          format: uuid
          type: string
        updated_at:
          format: date-time
          type: string
        width:
          type: integer
      type: object
    QRCodeOpenSeriesPoint:
      properties:
        opens:
          type: integer
        time:
          format: date-time
          type: string
      type: object
    QRCodeRequest:
      properties:
        custom_params:
          additionalProperties:
            type: string
          type: object
        destination_url:
          format: uri
          type: string
        name:
          type: string
        style:
          additionalProperties: true
          type: object
        utm_campaign:
          type: string
        utm_content:
          type: string
        utm_medium:
          type: string
        utm_source:
          type: string
        utm_term:
          type: string
      required:
        - name
        - destination_url
      type: object
    QRCodeShareLink:
      properties:
        created_at:
          format: date-time
          type: string
        id:
          format: uuid
          type: string
        qr_code_id:
          format: uuid
          type: string
        site_id:
          format: uuid
          type: string
        token:
          type: string
        token_hint:
          type: string
        url:
          format: uri
          type: string
      type: object
    QRCodeSummary:
      properties:
        open_count:
          type: integer
        pageviews:
          type: integer
        qr_code:
          $ref: '#/components/schemas/QRCode'
        top_countries:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_devices:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_pages:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_referrers:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        visitors:
          type: integer
      type: object
    ReadinessUnavailable:
      properties:
        reason:
          enum:
            - not_leader
            - database_unavailable
            - database_recovering
            - database_needs_attention
          type: string
        retry_after_seconds:
          minimum: 1
          type: integer
        status:
          enum:
            - not_ready
          type: string
      required:
        - status
        - reason
        - retry_after_seconds
      type: object
    ReportDefinition:
      properties:
        consent_version:
          minimum: 1
          type: integer
        created_at:
          format: date-time
          type: string
        created_by:
          format: uuid
          type: string
        id:
          format: uuid
          type: string
        last_outcome:
          additionalProperties: true
          type: object
        name:
          type: string
        next_run_at:
          format: date-time
          type: string
        owner_user_id:
          format: uuid
          type: string
        preset:
          enum:
            - site_summary
            - portfolio_digest
            - opportunity_brief
          type: string
        recipients:
          items:
            $ref: '#/components/schemas/ReportRecipient'
          type: array
        schedule:
          $ref: '#/components/schemas/ReportSchedule'
        scope:
          enum:
            - personal
            - team
          type: string
        site_mode:
          enum:
            - selected
            - all_accessible
          type: string
        sites:
          items:
            properties:
              domain:
                type: string
              id:
                format: uuid
                type: string
            type: object
          type: array
        status:
          enum:
            - draft
            - active
            - paused
          type: string
        tenant_id:
          format: uuid
          type: string
        updated_at:
          format: date-time
          type: string
      required:
        - id
        - name
        - scope
        - preset
        - site_mode
        - sites
        - recipients
        - schedule
        - status
        - created_at
        - updated_at
      type: object
    ReportDefinitionInput:
      properties:
        external_recipient_emails:
          items:
            format: email
            type: string
          maxItems: 25
          type: array
          uniqueItems: true
        name:
          maxLength: 120
          type: string
        preset:
          enum:
            - site_summary
            - portfolio_digest
            - opportunity_brief
          type: string
        recipient_user_ids:
          items:
            format: uuid
            type: string
          type: array
        schedule:
          $ref: '#/components/schemas/ReportSchedule'
        scope:
          enum:
            - personal
            - team
          type: string
        site_ids:
          items:
            format: uuid
            type: string
          type: array
        site_mode:
          enum:
            - selected
            - all_accessible
          type: string
        status:
          enum:
            - draft
            - active
            - paused
          type: string
        tenant_id:
          format: uuid
          type: string
      required:
        - name
        - scope
        - preset
        - site_mode
        - site_ids
        - recipient_user_ids
        - external_recipient_emails
        - schedule
        - status
      type: object
    ReportDefinitionUpdate:
      properties:
        external_recipient_emails:
          items:
            format: email
            type: string
          maxItems: 25
          type: array
          uniqueItems: true
        name:
          maxLength: 120
          type: string
        preset:
          enum:
            - site_summary
            - portfolio_digest
            - opportunity_brief
          type: string
        recipient_user_ids:
          items:
            format: uuid
            type: string
          type: array
        schedule:
          $ref: '#/components/schemas/ReportSchedule'
        site_ids:
          items:
            format: uuid
            type: string
          type: array
        site_mode:
          enum:
            - selected
            - all_accessible
          type: string
        status:
          enum:
            - draft
            - active
            - paused
          type: string
      type: object
    ReportDelivery:
      properties:
        attempt_count:
          type: integer
        id:
          format: uuid
          type: string
        next_attempt_at:
          format: date-time
          type: string
        recipient_email:
          format: email
          type: string
        recipient_id:
          format: uuid
          type: string
        recipient_kind:
          enum:
            - member
            - external
          type: string
        recipient_user_id:
          format: uuid
          type: string
        safe_error_code:
          type: string
        smtp_accepted_at:
          format: date-time
          type: string
        status:
          type: string
      type: object
    ReportPreview:
      properties:
        pending_recipient_count:
          type: integer
        period_end:
          format: date-time
          type: string
        period_start:
          format: date-time
          type: string
        preset:
          type: string
        recipient_count:
          type: integer
        schedule:
          $ref: '#/components/schemas/ReportSchedule'
        site_count:
          type: integer
        subject:
          type: string
        suppressed:
          type: boolean
      type: object
    ReportRecipient:
      properties:
        confirmation_expires_at:
          format: date-time
          type: string
        confirmed_at:
          format: date-time
          type: string
        email:
          format: email
          type: string
        id:
          format: uuid
          type: string
        invitation_state:
          enum:
            - pending
            - sent
            - failed
          type: string
        kind:
          enum:
            - member
            - external
          type: string
        opted_out_at:
          format: date-time
          type: string
        status:
          enum:
            - pending_confirmation
            - confirmed
            - opted_out
          type: string
        user_id:
          format: uuid
          type: string
      required:
        - id
        - kind
        - email
        - status
      type: object
    ReportRecipientConfirmation:
      properties:
        expires_at:
          format: date-time
          type: string
        preset:
          type: string
        report_name:
          type: string
        schedule:
          $ref: '#/components/schemas/ReportSchedule'
        sites:
          items:
            properties:
              domain:
                type: string
              id:
                format: uuid
                type: string
            type: object
          type: array
        team_name:
          type: string
      required:
        - report_name
        - team_name
        - preset
        - schedule
        - sites
        - expires_at
      type: object
    ReportRun:
      properties:
        deliveries:
          items:
            $ref: '#/components/schemas/ReportDelivery'
          type: array
        id:
          format: uuid
          type: string
        period_end:
          format: date-time
          type: string
        period_start:
          format: date-time
          type: string
        report_id:
          format: uuid
          type: string
        safe_error_code:
          type: string
        scheduled_for:
          format: date-time
          type: string
        status:
          type: string
      type: object
    ReportSchedule:
      properties:
        frequency:
          enum:
            - daily
            - weekly
            - monthly
          type: string
        local_time:
          pattern: ^([01][0-9]|2[0-3]):(00|15|30|45)$
          type: string
        monthly_day:
          maximum: 28
          minimum: 1
          type: integer
        timezone:
          description: IANA timezone name.
          type: string
        weekly_day:
          maximum: 6
          minimum: 0
          type: integer
      required:
        - frequency
        - timezone
        - local_time
      type: object
    ReportTestSendResponse:
      properties:
        message_id:
          type: string
        sent_at:
          format: date-time
          type: string
        status:
          enum:
            - accepted
          type: string
      required:
        - status
        - message_id
        - sent_at
      type: object
    SSOAvailability:
      properties:
        enabled:
          type: boolean
      required:
        - enabled
      type: object
    SSOStartRequest:
      oneOf:
        - required:
            - email
        - required:
            - invite_token
      properties:
        email:
          format: email
          type: string
        invite_token:
          description: Invitation token used instead of email when starting SSO from invitation acceptance.
          type: string
        remember_me:
          type: boolean
        return_url:
          description: Optional same-origin application path. Unsafe or authentication paths fall back to /.
          type: string
      type: object
    SSOStartResponse:
      properties:
        auth_url:
          format: uri
          type: string
      required:
        - auth_url
      type: object
    SearchConsoleDimensionResponse:
      properties:
        data_source:
          const: google_search_console
          type: string
        dimension:
          enum:
            - query
            - page
            - country
            - device
          type: string
        rows:
          items:
            $ref: '#/components/schemas/SearchConsoleDimensionRow'
          type: array
      required:
        - data_source
        - dimension
        - rows
      type: object
    SearchConsoleDimensionRow:
      properties:
        average_position:
          type: number
        clicks:
          type: integer
        ctr:
          type: number
        impressions:
          type: integer
        value:
          type: string
      required:
        - value
        - clicks
        - impressions
        - ctr
        - average_position
      type: object
    SearchConsoleMetricPoint:
      properties:
        average_position:
          type: number
        clicks:
          type: integer
        ctr:
          type: number
        date:
          format: date
          type: string
        impressions:
          type: integer
      required:
        - date
        - clicks
        - impressions
        - ctr
        - average_position
      type: object
    SearchConsoleOverview:
      properties:
        average_position:
          type: number
        clicks:
          type: integer
        ctr:
          type: number
        data_source:
          const: google_search_console
          type: string
        impressions:
          type: integer
      required:
        - data_source
        - clicks
        - impressions
        - ctr
        - average_position
      type: object
    SearchConsoleSeriesResponse:
      properties:
        data_source:
          const: google_search_console
          type: string
        series:
          items:
            $ref: '#/components/schemas/SearchConsoleMetricPoint'
          type: array
      required:
        - data_source
        - series
      type: object
    ShareLink:
      properties:
        created_at:
          format: date-time
          type: string
        id:
          format: uuid
          type: string
        site_id:
          format: uuid
          type: string
        token_hint:
          type: string
      type: object
    SharedOpportunity:
      properties:
        action_key:
          type: string
        cited_evidence_ids:
          items:
            type: string
          type: array
        confidence:
          enum:
            - high
            - medium
          type: string
        copy_params:
          additionalProperties: true
          type: object
        created_at:
          format: date-time
          type: string
        detector_version:
          type: string
        digest_key:
          type: string
        evidence:
          items:
            $ref: '#/components/schemas/OpportunityEvidence'
          type: array
        generated_at:
          format: date-time
          type: string
        id:
          format: uuid
          type: string
        impact_label_key:
          type: string
        impact_value:
          type: string
        kind:
          enum:
            - conversion
            - traffic
            - ai
            - search
            - setup
          type: string
        route_icon:
          type: string
        route_label_key:
          type: string
        route_params:
          additionalProperties: true
          type: object
        score:
          type: integer
        score_breakdown:
          $ref: '#/components/schemas/OpportunityScoreBreakdown'
        site_id:
          format: uuid
          type: string
        status:
          enum:
            - new
            - saved
            - done
            - dismissed
          type: string
        summary_key:
          type: string
        title_key:
          type: string
        type_key:
          type: string
        updated_at:
          format: date-time
          type: string
      required:
        - id
        - site_id
        - kind
        - type_key
        - title_key
        - summary_key
        - action_key
        - digest_key
        - copy_params
        - impact_value
        - impact_label_key
        - confidence
        - score
        - score_breakdown
        - status
        - route_label_key
        - route_icon
        - detector_version
        - evidence
        - cited_evidence_ids
        - generated_at
        - created_at
        - updated_at
      type: object
    SharedOpportunityListResponse:
      properties:
        opportunities:
          items:
            $ref: '#/components/schemas/SharedOpportunity'
          type: array
      required:
        - opportunities
      type: object
    Site:
      properties:
        created_at:
          format: date-time
          type: string
        data_retention_days:
          type: integer
        domain:
          type: string
        id:
          format: uuid
          type: string
        user_id:
          format: uuid
          type: string
      type: object
    SiteMember:
      properties:
        added_at:
          format: date-time
          type: string
        email:
          format: email
          type: string
        id:
          format: uuid
          type: string
        role:
          type: string
        user_id:
          format: uuid
          type: string
      type: object
    SiteOverviewStats:
      properties:
        bounce_rate:
          type: number
        chart_data:
          items:
            $ref: '#/components/schemas/ChartDataPoint'
          type: array
        error:
          type: string
        site_id:
          format: uuid
          type: string
        status:
          enum:
            - ready
            - error
          type: string
        total_pageviews:
          type: integer
        unique_sessions:
          type: integer
      required:
        - site_id
        - status
        - total_pageviews
        - unique_sessions
        - bounce_rate
        - chart_data
      type: object
    SiteSetupState:
      properties:
        has_ai_fetches:
          type: boolean
        has_chatbot_events:
          type: boolean
        has_custom_events:
          type: boolean
        has_ecommerce_events:
          type: boolean
        has_web_vitals:
          type: boolean
      required:
        - has_ai_fetches
        - has_chatbot_events
        - has_custom_events
        - has_ecommerce_events
        - has_web_vitals
      type: object
    SiteStats:
      properties:
        ai_bot_hits:
          type: integer
        ai_source_visits:
          type: integer
        avg_session_duration:
          type: number
        bounce_rate:
          type: number
        chart_data:
          items:
            $ref: '#/components/schemas/ChartDataPoint'
          type: array
        funnels:
          items:
            $ref: '#/components/schemas/Funnel'
          type: array
        goals:
          items:
            $ref: '#/components/schemas/GoalStats'
          type: array
        imported_excluded:
          items:
            $ref: '#/components/schemas/ImportExclusionReason'
          type: array
        live_visitors:
          type: integer
        pages_per_session:
          type: number
        top_ai_bot_categories:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_ai_bots_by_category:
          additionalProperties:
            items:
              $ref: '#/components/schemas/MetricStat'
            type: array
          type: object
        top_ai_bots:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_ai_sources:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_asns:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_cities:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_countries:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_devices:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_exit_pages:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_landing_pages:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_languages:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_pages:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_providers:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_referrers:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_utm_campaigns:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_utm_contents:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_utm_mediums:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_utm_sources:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        top_utm_terms:
          items:
            $ref: '#/components/schemas/MetricStat'
          type: array
        total_pageviews:
          type: integer
        unique_sessions:
          type: integer
        utm_campaign_hits:
          type: integer
        utm_content_hits:
          type: integer
        utm_medium_hits:
          type: integer
        utm_source_hits:
          type: integer
        utm_term_hits:
          type: integer
      type: object
    SiteStatsResetRequest:
      properties:
        confirm_domain:
          type: string
      required:
        - confirm_domain
      type: object
    SiteStatsResetResponse:
      properties:
        families_cleared:
          items:
            type: string
          type: array
        imports_marked_deleted:
          format: int64
          type: integer
        rows_cleared:
          format: int64
          type: integer
        status:
          enum:
            - reset
          type: string
      required:
        - status
        - rows_cleared
        - imports_marked_deleted
        - families_cleared
      type: object
    SiteTrackingDomainOptions:
      properties:
        default_url:
          type: string
        domains:
          items:
            $ref: '#/components/schemas/CustomTrackingDomain'
          type: array
        site_id:
          format: uuid
          type: string
        team_id:
          format: uuid
          type: string
      required:
        - site_id
        - team_id
        - default_url
        - domains
      type: object
    SiteTrackingStatus:
      properties:
        configured_domain:
          type: string
        first_hit_at:
          format: date-time
          type: string
        last_automatic_event_at:
          format: date-time
          type: string
        last_automatic_event_name:
          type: string
        last_event_at:
          format: date-time
          type: string
        last_event_name:
          type: string
        last_hit_at:
          format: date-time
          type: string
        last_hostname:
          type: string
        site_id:
          format: uuid
          type: string
        status:
          enum:
            - waiting
            - live
            - dormant
            - domain_mismatch
          type: string
        tenant_id:
          format: uuid
          type: string
        tracker_source:
          type: string
        tracker_version:
          type: string
        updated_at:
          format: date-time
          type: string
      type: object
    SiteTransferResponse:
      properties:
        destination_team_id:
          format: uuid
          type: string
        site_id:
          format: uuid
          type: string
        source_team_id:
          format: uuid
          type: string
        status:
          type: string
      type: object
    SitesOverviewStatsResponse:
      properties:
        sites:
          items:
            $ref: '#/components/schemas/SiteOverviewStats'
          type: array
      required:
        - sites
      type: object
    SocialCompleteRequest:
      properties:
        completion_token:
          type: string
          writeOnly: true
        email:
          description: HitKeep target email required for a first unauthenticated Microsoft link.
          format: email
          type: string
      required:
        - completion_token
      type: object
    SocialCompleteResponse:
      allOf:
        - $ref: '#/components/schemas/LoginResponse'
        - properties:
            completion_token:
              description: Replacement one-time token returned only when signup details are required.
              readOnly: true
              type: string
            redirect_url:
              type: string
            status:
              enum:
                - ok
                - mfa_required
                - signup_required
                - verification_sent
              type: string
          required:
            - status
          type: object
    SocialPreviewResponse:
      properties:
        display_name:
          type: string
        email_confirmation_required:
          description: Whether this completion requires the user to confirm a HitKeep email before continuing.
          type: boolean
        email_verified:
          type: boolean
        flow:
          enum:
            - login
            - signup
            - invite
          type: string
        observed_email:
          format: email
          type: string
        provider:
          enum:
            - google
            - github
            - microsoft
          type: string
      required:
        - provider
        - display_name
        - email_verified
        - email_confirmation_required
        - flow
      type: object
    SocialProvider:
      properties:
        display_name:
          type: string
        id:
          enum:
            - google
            - github
            - microsoft
          type: string
      required:
        - id
        - display_name
      type: object
    SocialProvidersResponse:
      properties:
        providers:
          items:
            $ref: '#/components/schemas/SocialProvider'
          type: array
        signup_enabled:
          type: boolean
      required:
        - providers
        - signup_enabled
      type: object
    SocialSignupCompleteRequest:
      properties:
        accepted_tos:
          type: boolean
        billing:
          enum:
            - monthly
            - annual
          type: string
        completion_token:
          type: string
          writeOnly: true
        email:
          format: email
          type: string
        jurisdiction:
          enum:
            - EU
            - US
          type: string
        locale:
          type: string
        plan_code:
          enum:
            - free
            - pro
            - business
          type: string
        team_name:
          maxLength: 120
          type: string
      required:
        - completion_token
        - team_name
        - plan_code
        - billing
        - jurisdiction
        - locale
        - accepted_tos
      type: object
    SocialSignupCompleteResponse:
      properties:
        billing:
          type: string
        plan_code:
          type: string
        redirect_url:
          type: string
        status:
          enum:
            - ok
            - verification_sent
          type: string
      required:
        - status
        - plan_code
        - billing
      type: object
    SocialStartRequest:
      properties:
        flow:
          enum:
            - login
            - signup
            - invite
          type: string
        invite_token:
          type: string
        remember_me:
          type: boolean
        return_url:
          description: Optional safe local application path.
          type: string
      required:
        - flow
      type: object
    Status:
      properties:
        message:
          type: string
        status:
          type: string
      type: object
    SupportChat:
      properties:
        base_url:
          format: uri
          type: string
        identifier:
          type: string
        identifier_hash:
          pattern: ^[a-f0-9]{64}$
          type: string
        provider:
          enum:
            - chatwoot
          type: string
        website_token:
          type: string
      required:
        - provider
        - base_url
        - website_token
        - identifier
        - identifier_hash
      type: object
    SystemAIStatus:
      properties:
        ask_ai_available:
          type: boolean
        ask_ai_enabled:
          type: boolean
        base_url_configured:
          type: boolean
        budget_exhausted:
          type: boolean
        budget_window_minutes:
          type: integer
        config_mode:
          enum:
            - cloud_managed
            - self_hosted
          type: string
        configured:
          type: boolean
        enabled:
          type: boolean
        last_attempt_at:
          format: date-time
          type: string
        last_error_category:
          type: string
        last_success_at:
          format: date-time
          type: string
        model:
          type: string
        provider:
          type: string
        region:
          type: string
        request_limit:
          type: integer
        requests_used:
          type: integer
        status:
          enum:
            - disabled
            - not_configured
            - configured
            - budget_exhausted
            - needs_attention
          type: string
        token_limit:
          type: integer
        tokens_used:
          type: integer
      required:
        - status
        - enabled
        - configured
        - ask_ai_enabled
        - ask_ai_available
        - config_mode
        - base_url_configured
        - requests_used
        - request_limit
        - tokens_used
        - token_limit
        - budget_window_minutes
        - budget_exhausted
      type: object
    SystemActionResponse:
      properties:
        message:
          type: string
        status:
          type: string
      type: object
    SystemActivationResponse:
      properties:
        has_more:
          type: boolean
        limit:
          type: integer
        offset:
          type: integer
        rows:
          items:
            $ref: '#/components/schemas/SystemActivationRow'
          type: array
        total:
          type: integer
      type: object
    SystemActivationRow:
      properties:
        active_sites:
          type: integer
        cloud_region:
          type: string
        events_last_7d:
          type: integer
        first_hit_at:
          format: date-time
          type: string
        hits_last_24h:
          type: integer
        hits_last_7d:
          type: integer
        last_event_at:
          format: date-time
          type: string
        last_event_name:
          type: string
        last_hit_at:
          format: date-time
          type: string
        owner_email:
          format: email
          type: string
        plan_code:
          type: string
        plan_name:
          type: string
        site_domain:
          type: string
        site_id:
          format: uuid
          type: string
        sites_count:
          type: integer
        status:
          enum:
            - waiting
            - live
            - dormant
            - domain_mismatch
          type: string
        team_id:
          format: uuid
          type: string
        team_name:
          type: string
        tracker_source:
          type: string
        tracker_version:
          type: string
      type: object
    SystemBackupStatus:
      properties:
        config_path:
          type: string
        enabled:
          type: boolean
        interval_min:
          type: integer
        last_backup:
          format: date-time
          type: string
        last_error:
          type: string
        last_failed_at:
          format: date-time
          type: string
        next_backup:
          format: date-time
          type: string
        recent_failures:
          type: integer
        retention:
          type: integer
      type: object
    SystemCacheEntry:
      properties:
        max_size:
          type: integer
        size:
          type: integer
        ttl:
          type: string
      type: object
    SystemCacheStatus:
      properties:
        api_client_cache:
          $ref: '#/components/schemas/SystemCacheEntry'
        permissions_cache:
          $ref: '#/components/schemas/SystemCacheEntry'
        rate_limiter_cache:
          $ref: '#/components/schemas/SystemCacheEntry'
        status:
          type: string
      type: object
    SystemDatabaseStatus:
      properties:
        automatic_wal_recovery_enabled:
          type: boolean
        checkpoint_interval_min:
          type: integer
        last_checkpoint_at:
          format: date-time
          type: string
        last_checkpoint_error:
          type: string
        last_recovery_at:
          format: date-time
          type: string
        recovery_bundle_available:
          type: boolean
        recovery_enabled:
          type: boolean
        removed_unsafe_indexes:
          type: integer
      type: object
    SystemFeatureStatus:
      properties:
        detail:
          description: Optional non-sensitive runtime detail, such as target type or path.
          type: string
        enabled:
          type: boolean
        key:
          description: Stable feature key.
          type: string
      required:
        - key
        - enabled
      type: object
    SystemHealth:
      properties:
        database:
          type: string
        is_leader:
          type: boolean
        status:
          type: string
        workers:
          type: string
      type: object
    SystemImportStageCleanupRunResponse:
      properties:
        message:
          type: string
        result:
          $ref: '#/components/schemas/ImportStageCleanupRunResult'
        status:
          type: string
      type: object
    SystemImportStageCleanupStatus:
      properties:
        enabled:
          type: boolean
        last_cleaned_bytes:
          format: int64
          type: integer
        last_cleaned_files:
          type: integer
        last_cleaned_imports:
          type: integer
        last_error:
          type: string
        last_failed_at:
          format: date-time
          type: string
        last_marked_failed:
          type: integer
        last_run:
          format: date-time
          type: string
        recent_failures:
          type: integer
        retention_days:
          type: integer
        stale_bytes:
          format: int64
          type: integer
        stale_files:
          type: integer
        stale_imports:
          type: integer
      type: object
    SystemInfo:
      properties:
        config_flags:
          additionalProperties: true
          type: object
        enabled_features:
          items:
            $ref: '#/components/schemas/SystemFeatureStatus'
          type: array
        public_url:
          type: string
        runtime_mode:
          type: string
        uptime:
          type: string
        version:
          type: string
      type: object
    SystemIngestStats:
      properties:
        hits_per_second:
          format: double
          type: number
        recent_events:
          type: integer
        recent_hits:
          type: integer
        recent_rejections:
          type: integer
        recent_spam:
          type: integer
      type: object
    SystemMailStatus:
      properties:
        configured:
          type: boolean
        driver:
          type: string
        encryption:
          type: string
        from_address:
          type: string
        from_name:
          type: string
        host:
          type: string
        last_test_at:
          format: date-time
          type: string
        last_test_ok:
          type: boolean
        password_set:
          type: boolean
        port:
          type: integer
        username:
          type: string
      type: object
    SystemSearchConsoleStatus:
      properties:
        connected_teams:
          type: integer
        credentials_status:
          enum:
            - configured
            - missing
          type: string
        failed_syncs:
          type: integer
        last_attempt_at:
          format: date-time
          type: string
        last_success_at:
          format: date-time
          type: string
        mapped_sites:
          type: integer
        needs_attention_syncs:
          type: integer
        next_retry_at:
          format: date-time
          type: string
        pending_syncs:
          type: integer
        running_syncs:
          type: integer
        status:
          enum:
            - healthy
            - idle
            - syncing
            - degraded
            - needs_attention
            - not_configured
          type: string
        sync_status:
          enum:
            - healthy
            - idle
            - pending
            - running
            - failed
            - needs_attention
          type: string
        worker_status:
          enum:
            - enabled
            - disabled
          type: string
      required:
        - status
        - credentials_status
        - worker_status
        - sync_status
        - connected_teams
        - mapped_sites
        - pending_syncs
        - running_syncs
        - failed_syncs
        - needs_attention_syncs
      type: object
    SystemSpamStatus:
      properties:
        auto_update:
          type: boolean
        db_path:
          type: string
        last_error:
          type: string
        last_refresh:
          format: date-time
          type: string
        rule_count:
          type: integer
      type: object
    SystemStatus:
      properties:
        ask_ai:
          $ref: '#/components/schemas/AskAIStatus'
        cloud:
          $ref: '#/components/schemas/CloudStatus'
        mail_delivery:
          $ref: '#/components/schemas/MailDeliveryStatus'
        needs_setup:
          type: boolean
        version:
          type: string
      type: object
    SystemStorage:
      properties:
        backup_path:
          type: string
        control_db_bytes:
          format: int64
          type: integer
        control_db_path:
          type: string
        data_path:
          type: string
        disk_available_bytes:
          format: int64
          type: integer
        disk_total_bytes:
          format: int64
          type: integer
        duckdb_memory:
          deprecated: true
          items:
            $ref: '#/components/schemas/DuckDBMemoryStat'
          type: array
        shared_db_bytes:
          deprecated: true
          format: int64
          type: integer
        shared_db_path:
          deprecated: true
          type: string
        spam_cache_path:
          type: string
        tenant_db_count:
          type: integer
        tenant_duckdb_memory:
          items:
            $ref: '#/components/schemas/DuckDBMemoryStat'
          type: array
        tenant_dbs:
          items:
            $ref: '#/components/schemas/TenantDBInfo'
          type: array
      type: object
    Team:
      properties:
        created_at:
          format: date-time
          type: string
        entitlements:
          $ref: '#/components/schemas/TeamEntitlements'
        id:
          format: uuid
          type: string
        logo_url:
          type: string
        name:
          type: string
        plan:
          $ref: '#/components/schemas/TeamPlan'
        role:
          type: string
        usage:
          $ref: '#/components/schemas/TeamUsageSummary'
      type: object
    TeamActiveResponse:
      properties:
        active_team_id:
          format: uuid
          type: string
        recent_team_ids:
          items:
            format: uuid
            type: string
          type: array
        status:
          type: string
      type: object
    TeamArchiveResponse:
      properties:
        active_team_id:
          format: uuid
          type: string
        recent_team_ids:
          items:
            format: uuid
            type: string
          type: array
        status:
          type: string
      type: object
    TeamAuditEntry:
      properties:
        action:
          type: string
        actor_email:
          format: email
          type: string
        actor_email_snapshot:
          type: string
        actor_role_snapshot:
          type: string
        actor_user_id:
          format: uuid
          type: string
        created_at:
          format: date-time
          type: string
        details:
          type: string
        id:
          format: uuid
          type: string
        ip_address:
          type: string
        ip_country_code:
          type: string
        outcome:
          type: string
        request_id:
          type: string
        target_email:
          format: email
          type: string
        target_id:
          type: string
        target_label:
          type: string
        target_type:
          type: string
        target_user_id:
          format: uuid
          type: string
        team_id:
          format: uuid
          type: string
        user_agent:
          type: string
      type: object
    TeamAuditListResponse:
      properties:
        action:
          type: string
        entries:
          items:
            $ref: '#/components/schemas/TeamAuditEntry'
          type: array
        has_more:
          type: boolean
        limit:
          type: integer
        offset:
          type: integer
        total:
          type: integer
      type: object
    TeamCreateResponse:
      properties:
        team:
          $ref: '#/components/schemas/Team'
      type: object
    TeamEntitlements:
      properties:
        allow_custom_branding:
          type: boolean
        allow_external_report_recipients:
          description: Whether scheduled reports may be sent to confirmed addresses outside the team.
          type: boolean
        allow_sso:
          type: boolean
        max_retention_days:
          type: integer
        max_sites_per_team:
          type: integer
        max_team_members:
          type: integer
      type: object
    TeamInvite:
      properties:
        accepted_at:
          format: date-time
          type: string
        created_at:
          format: date-time
          type: string
        created_by:
          format: uuid
          type: string
        email:
          format: email
          type: string
        expires_at:
          format: date-time
          type: string
        id:
          format: uuid
          type: string
        invited_user_id:
          format: uuid
          type: string
        revoked_at:
          format: date-time
          type: string
        role:
          type: string
        status:
          type: string
        team_id:
          format: uuid
          type: string
      type: object
    TeamLeaveResponse:
      properties:
        active_team_id:
          format: uuid
          type: string
        recent_team_ids:
          items:
            format: uuid
            type: string
          type: array
        status:
          type: string
      type: object
    TeamListResponse:
      properties:
        active_team_id:
          format: uuid
          type: string
        recent_team_ids:
          items:
            format: uuid
            type: string
          type: array
        teams:
          items:
            $ref: '#/components/schemas/Team'
          type: array
      type: object
    TeamMember:
      properties:
        added_at:
          format: date-time
          type: string
        email:
          format: email
          type: string
        id:
          format: uuid
          type: string
        role:
          type: string
        user_id:
          format: uuid
          type: string
      type: object
    TeamPlan:
      properties:
        code:
          type: string
        name:
          type: string
        support_url:
          type: string
        upgrade_url:
          type: string
      type: object
    TeamSSOConfig:
      description: Redacted team OIDC configuration. The client secret is never returned.
      properties:
        allowed_domains:
          items:
            type: string
          type: array
        auto_provision:
          type: boolean
        callback_url:
          format: uri
          type: string
        client_id:
          type: string
        client_secret_configured:
          type: boolean
        display_name_claim:
          type: string
        email_claim:
          type: string
        enabled:
          type: boolean
        issuer_url:
          format: uri
          type: string
        provider_type:
          enum:
            - oidc
          type: string
        updated_at:
          format: date-time
          type: string
      required:
        - provider_type
        - issuer_url
        - client_id
        - client_secret_configured
        - allowed_domains
        - email_claim
        - display_name_claim
        - auto_provision
        - enabled
        - callback_url
      type: object
    TeamSSOInput:
      properties:
        allowed_domains:
          items:
            type: string
          maxItems: 50
          minItems: 1
          type: array
        auto_provision:
          description: When true, verified users from trusted domains may join this team as Members without an invitation. Managed-cloud seat and team limits still apply.
          type: boolean
        client_id:
          maxLength: 512
          type: string
        client_secret:
          description: Required on create. Blank preserves the stored secret on update.
          type: string
          writeOnly: true
        display_name_claim:
          default: name
          type: string
        email_claim:
          default: email
          type: string
        enabled:
          type: boolean
        issuer_url:
          format: uri
          maxLength: 2048
          type: string
        provider_type:
          enum:
            - oidc
          type: string
      required:
        - provider_type
        - issuer_url
        - client_id
        - allowed_domains
        - email_claim
        - display_name_claim
        - auto_provision
        - enabled
      type: object
    TeamUsageSummary:
      properties:
        current_members:
          type: integer
        current_pending_invites:
          type: integer
        current_sites:
          type: integer
      type: object
    TenantDBInfo:
      properties:
        bytes:
          format: int64
          type: integer
        is_default:
          type: boolean
        name:
          type: string
        path:
          type: string
        tenant_id:
          format: uuid
          type: string
      type: object
    UpdateCustomTrackingDomainRequest:
      properties:
        enabled:
          type: boolean
      required:
        - enabled
      type: object
    UserBootstrap:
      properties:
        permissions:
          $ref: '#/components/schemas/PermissionContext'
        preferences:
          $ref: '#/components/schemas/UserPreferences'
        profile:
          $ref: '#/components/schemas/UserProfile'
        session:
          $ref: '#/components/schemas/AuthSession'
        sites:
          items:
            $ref: '#/components/schemas/Site'
          type: array
        status:
          $ref: '#/components/schemas/SystemStatus'
        support_chat:
          $ref: '#/components/schemas/SupportChat'
        teams:
          $ref: '#/components/schemas/TeamListResponse'
      type: object
    UserOnboarding:
      properties:
        complete:
          type: boolean
        dismissed:
          type: boolean
        steps:
          items:
            $ref: '#/components/schemas/OnboardingStep'
          type: array
      type: object
    UserPasskey:
      properties:
        created_at:
          format: date-time
          type: string
        id:
          format: uuid
          type: string
        name:
          type: string
        updated_at:
          format: date-time
          type: string
      type: object
    UserPreferences:
      properties:
        default_locale:
          type: string
        dismissed_onboarding_at:
          format: date-time
          type: string
      type: object
    UserProfile:
      properties:
        avatar_url:
          type: string
        display_name:
          type: string
        email:
          format: email
          type: string
        given_name:
          type: string
        id:
          format: uuid
          type: string
        last_name:
          type: string
      type: object
    UserRecoveryCodesResponse:
      properties:
        codes:
          items:
            type: string
          type: array
        remaining:
          type: integer
      type: object
    UserSecurityStatus:
      properties:
        passkeys:
          items:
            $ref: '#/components/schemas/UserPasskey'
          type: array
        password_login_enabled:
          type: boolean
        recovery_codes_generated:
          type: boolean
        recovery_codes_remaining:
          type: integer
        social_identities:
          items:
            $ref: '#/components/schemas/UserSocialIdentity'
          type: array
        totp_enabled:
          type: boolean
        totp_pending:
          type: boolean
      type: object
    UserSocialIdentity:
      description: Safe linked-provider metadata. Immutable provider subjects are intentionally omitted.
      properties:
        last_used_at:
          format: date-time
          type: string
        linked_at:
          format: date-time
          type: string
        observed_email:
          format: email
          type: string
        provider:
          enum:
            - google
            - github
            - microsoft
          type: string
      required:
        - provider
        - linked_at
      type: object
    UserTOTPSetup:
      properties:
        expires_at:
          format: date-time
          type: string
        otpauth_url:
          type: string
        secret:
          type: string
      type: object
    WebVitalDimensionRow:
      properties:
        good:
          type: integer
        name:
          type: string
        needs_improvement:
          type: integer
        p75:
          type: number
        poor:
          type: integer
        rating:
          enum:
            - good
            - needs_improvement
            - poor
          type: string
        samples:
          type: integer
      type: object
    WebVitalIngestPayload:
      properties:
        mid:
          description: Optional web-vitals metric instance ID used for future dedupe and bfcache analysis.
          type: string
        n:
          description: Metric name.
          enum:
            - LCP
            - INP
            - CLS
            - FCP
            - TTFB
          type: string
        nt:
          description: Navigation type.
          type: string
        p:
          description: Browser path. Query strings and hashes are stripped server-side.
          type: string
        pid:
          format: uuid
          type: string
        sid:
          format: uuid
          type: string
        tsrc:
          type: string
        tv:
          type: string
        ua:
          description: Optional browser user agent used transiently for traffic exclusions. The request header is the compatibility fallback.
          type: string
        v:
          description: Metric value. LCP, INP, FCP, and TTFB use milliseconds. CLS is unitless.
          minimum: 0
          type: number
      required:
        - n
        - v
        - p
        - sid
        - pid
      type: object
    WebVitalMetricBreakdown:
      properties:
        good:
          type: integer
        needs_improvement:
          type: integer
        p75:
          type: number
        poor:
          type: integer
        rating:
          enum:
            - good
            - needs_improvement
            - poor
          type: string
        samples:
          type: integer
      type: object
    WebVitalPageRow:
      properties:
        good:
          type: integer
        metrics:
          additionalProperties:
            $ref: '#/components/schemas/WebVitalMetricBreakdown'
          type: object
        needs_improvement:
          type: integer
        p75:
          type: number
        path:
          type: string
        poor:
          type: integer
        rating:
          enum:
            - good
            - needs_improvement
            - poor
          type: string
        samples:
          type: integer
      type: object
    WebVitalSeriesPoint:
      properties:
        good:
          type: integer
        needs_improvement:
          type: integer
        p75:
          type: number
        poor:
          type: integer
        samples:
          type: integer
        time:
          format: date-time
          type: string
      type: object
    WebVitalSummaryMetric:
      properties:
        good:
          type: integer
        metric:
          enum:
            - LCP
            - INP
            - CLS
            - FCP
            - TTFB
          type: string
        needs_improvement:
          type: integer
        p75:
          type: number
        poor:
          type: integer
        rating:
          enum:
            - good
            - needs_improvement
            - poor
          type: string
        samples:
          type: integer
      type: object
    Webhook:
      properties:
        created_at:
          format: date-time
          type: string
        description:
          type: string
        enabled:
          type: boolean
        events:
          items:
            type: string
          type: array
        id:
          format: uuid
          type: string
        name:
          type: string
        scope:
          enum:
            - instance
            - site
          type: string
        site_id:
          format: uuid
          type:
            - string
            - "null"
        updated_at:
          format: date-time
          type: string
        url:
          format: uri
          type: string
      required:
        - id
        - scope
        - name
        - description
        - url
        - enabled
        - events
        - created_at
        - updated_at
      type: object
    WebhookDelivery:
      properties:
        attempt_count:
          type: integer
        attempts:
          items:
            $ref: '#/components/schemas/WebhookDeliveryAttempt'
          type: array
        completed_at:
          format: date-time
          type:
            - string
            - "null"
        created_at:
          format: date-time
          type: string
        event_id:
          format: uuid
          type: string
        event_type:
          type: string
        id:
          format: uuid
          type: string
        last_attempt_at:
          format: date-time
          type:
            - string
            - "null"
        last_error_code:
          type: string
        last_error_message:
          type: string
        next_attempt_at:
          format: date-time
          type:
            - string
            - "null"
        response_status:
          type: integer
        site_id:
          format: uuid
          type:
            - string
            - "null"
        status:
          enum:
            - pending
            - processing
            - retrying
            - succeeded
            - failed
          type: string
        webhook_id:
          format: uuid
          type: string
      required:
        - id
        - event_id
        - webhook_id
        - event_type
        - status
        - attempt_count
        - created_at
        - attempts
      type: object
    WebhookDeliveryAttempt:
      properties:
        attempt_number:
          type: integer
        completed_at:
          format: date-time
          type: string
        error_code:
          type: string
        error_message:
          type: string
        id:
          format: uuid
          type: string
        next_attempt_at:
          format: date-time
          type:
            - string
            - "null"
        response_status:
          type: integer
        started_at:
          format: date-time
          type: string
        status:
          type: string
      type: object
    WebhookEventDescriptor:
      properties:
        scopes:
          items:
            enum:
              - instance
              - site
            type: string
          type: array
        site_scoped:
          type: boolean
        type:
          type: string
      required:
        - type
        - site_scoped
        - scopes
      type: object
    WebhookEventPayload:
      description: Exact JSON body signed and sent for one delivery. api_version is the running HitKeep major.minor and changes with the webhook contract for that release line.
      properties:
        api_version:
          examples:
            - "2.10"
          pattern: ^\d+\.\d+$
          type: string
        created_at:
          format: date-time
          type: string
        data:
          additionalProperties: true
          type: object
        delivery_id:
          description: Stable ID for this endpoint delivery.
          format: uuid
          type: string
        id:
          description: Stable event ID used for receiver deduplication.
          format: uuid
          type: string
        type:
          type: string
      required:
        - api_version
        - id
        - delivery_id
        - type
        - created_at
        - data
      type: object
    WebhookInput:
      properties:
        description:
          maxLength: 500
          type: string
        enabled:
          type: boolean
        events:
          items:
            type: string
          minItems: 1
          type: array
          uniqueItems: true
        name:
          maxLength: 120
          type: string
        url:
          description: HTTPS destination. Loopback, private, link-local, multicast, and otherwise unsafe addresses are rejected unless development targets are explicitly enabled.
          format: uri
          type: string
      required:
        - name
        - url
        - enabled
        - events
      type: object
    WebhookSecretResponse:
      properties:
        secret:
          description: One-time signing secret. It is returned only on creation or rotation.
          type: string
        webhook:
          $ref: '#/components/schemas/Webhook'
      required:
        - webhook
        - secret
      type: object
    WebhookTestResponse:
      properties:
        delivery_ids:
          items:
            format: uuid
            type: string
          type: array
        event_id:
          format: uuid
          type: string
      required:
        - event_id
        - delivery_ids
      type: object
  securitySchemes:
    apiKeyAuth:
      description: API client token in X-API-Key header.
      in: header
      name: X-API-Key
      type: apiKey
    bearerAuth:
      bearerFormat: APIClientToken
      description: API client token in Authorization header.
      scheme: bearer
      type: http
    cookieAuth:
      description: Session cookie authentication.
      in: cookie
      name: hk_token
      type: apiKey
info:
  description: Complete HTTP API for HitKeep (session, API key, and public share-token endpoints). JSON request bodies must contain exactly one value, use valid UTF-8, and use unique, case-sensitive object member names; schema-closed operations also reject unknown members. While the shared database or an open tenant database is unavailable, database-dependent API and ingest routes return HTTP 503 using components.responses.DatabaseUnavailable.
  title: HitKeep REST API
  version: v1
jsonSchemaDialect: https://spec.openapis.org/oas/3.1/dialect/base
openapi: 3.1.0
paths:
  /api/admin/exclusions:
    get:
      description: Lists instance-level CIDR, country, user-agent, and path exclusions used by forward-only ingest-time filtering. Requires instance.manage_site_exclusions.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/IPExclusion'
                type: array
          description: Global exclusions
      security:
        - cookieAuth: []
      summary: List global exclusions
      tags:
        - Admin
    post:
      description: Creates an instance-level CIDR, country, user-agent, or path exclusion for all sites. Rules affect new traffic only. Requires instance.manage_site_exclusions.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IPExclusionCreateRequest'
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IPExclusion'
          description: Created exclusion
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid exclusion rule
      security:
        - cookieAuth: []
      summary: Create global exclusion
      tags:
        - Admin
  /api/admin/exclusions/{ruleID}:
    delete:
      description: Deletes an instance-level traffic exclusion rule. Requires instance.manage_site_exclusions.
      parameters:
        - $ref: '#/components/parameters/ruleID'
      responses:
        "204":
          description: Deleted
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
      security:
        - cookieAuth: []
      summary: Delete global exclusion
      tags:
        - Admin
  /api/admin/sites:
    get:
      description: Lists all sites for admin management.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/Site'
                type: array
          description: Site list
      security:
        - cookieAuth: []
      summary: List all sites
      tags:
        - Admin
  /api/admin/sites/{id}:
    delete:
      description: Deletes site by admin endpoint.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
      security:
        - cookieAuth: []
      summary: Delete site (admin)
      tags:
        - Admin
  /api/admin/system:
    get:
      description: Returns version, runtime mode, uptime, public URL, and operator feature switch status.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemInfo'
          description: System overview
      security:
        - cookieAuth: []
      summary: Get system overview
      tags:
        - Admin
  /api/admin/system/activation:
    get:
      description: Owner-only operational activation view for teams and sites. Returns setup status, aggregate hit/event counts, owner email, plan, and timestamps without visitor-level details.
      parameters:
        - in: query
          name: status
          schema:
            enum:
              - waiting
              - live
              - dormant
              - domain_mismatch
            type: string
        - in: query
          name: team
          schema:
            type: string
        - in: query
          name: domain
          schema:
            type: string
        - in: query
          name: last_seen_from
          schema:
            format: date-time
            type: string
        - in: query
          name: last_seen_to
          schema:
            format: date-time
            type: string
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemActivationResponse'
          description: Activation rows
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid activation filter
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Forbidden
      security:
        - cookieAuth: []
      summary: List user activation
      tags:
        - Admin
  /api/admin/system/ai:
    get:
      description: Returns non-sensitive AI gateway configuration status, provider/model labels, local budget usage, and the latest safe run state without exposing provider secrets, prompts, or raw provider payloads.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemAIStatus'
          description: AI system status
      security:
        - cookieAuth: []
      summary: Get AI system status
      tags:
        - Admin
  /api/admin/system/audit:
    get:
      description: Lists instance-level audit entries for system maintenance and admin operations. Malformed actor, date, limit, and offset filters return 400.
      parameters:
        - description: Optional exact action filter.
          in: query
          name: action
          schema:
            type: string
        - description: Optional target type filter.
          in: query
          name: target_type
          schema:
            type: string
        - description: Optional outcome filter, for example success or failure.
          in: query
          name: outcome
          schema:
            type: string
        - description: Optional actor user ID filter.
          in: query
          name: actor_id
          schema:
            format: uuid
            type: string
        - description: Optional RFC3339 lower time bound.
          in: query
          name: from
          schema:
            format: date-time
            type: string
        - description: Optional RFC3339 upper time bound.
          in: query
          name: to
          schema:
            format: date-time
            type: string
        - description: Optional free-text search over action, actor, target, outcome, IP, request ID, and details.
          in: query
          name: query
          schema:
            type: string
        - description: Maximum number of rows to return.
          in: query
          name: limit
          schema:
            maximum: 200
            minimum: 1
            type: integer
        - description: Zero-based row offset.
          in: query
          name: offset
          schema:
            minimum: 0
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InstanceAuditListResponse'
          description: Instance audit entries
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid audit filter
      security:
        - cookieAuth: []
      summary: List instance audit log
      tags:
        - Admin
  /api/admin/system/audit/export:
    get:
      description: Exports matching instance-level audit entries as JSON or CSV. The export limit defaults to 10000 rows and is capped at 50000 rows.
      parameters:
        - description: Optional exact action filter.
          in: query
          name: action
          schema:
            type: string
        - description: Optional target type filter.
          in: query
          name: target_type
          schema:
            type: string
        - description: Optional outcome filter, for example success or failure.
          in: query
          name: outcome
          schema:
            type: string
        - description: Optional actor user ID filter.
          in: query
          name: actor_id
          schema:
            format: uuid
            type: string
        - description: Optional RFC3339 lower time bound.
          in: query
          name: from
          schema:
            format: date-time
            type: string
        - description: Optional RFC3339 upper time bound.
          in: query
          name: to
          schema:
            format: date-time
            type: string
        - description: Optional free-text search over action, actor, target, outcome, IP, request ID, and details.
          in: query
          name: query
          schema:
            type: string
        - description: Export format. Defaults to json.
          in: query
          name: format
          schema:
            enum:
              - json
              - csv
            type: string
        - description: Maximum number of exported rows. Defaults to 10000.
          in: query
          name: limit
          schema:
            maximum: 50000
            minimum: 1
            type: integer
      responses:
        "200":
          description: Audit export
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid audit filter
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Forbidden
      security:
        - cookieAuth: []
      summary: Export instance audit log
      tags:
        - Admin
  /api/admin/system/backups:
    get:
      description: Returns automatic backup configuration and recent backup status.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemBackupStatus'
          description: System backup status
      security:
        - cookieAuth: []
      summary: Get backup status
      tags:
        - Admin
  /api/admin/system/caches:
    get:
      description: Returns LRU cache sizes, maximum sizes, TTLs, and pressure status for permissions, API clients, and API rate limiting.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemCacheStatus'
          description: System cache status
      security:
        - cookieAuth: []
      summary: Get cache status
      tags:
        - Admin
  /api/admin/system/database:
    get:
      description: Returns sanitized recovery configuration, retained recovery history, and checkpoint state while the database is available. Live recovery state is reported through readiness and 503 responses.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemDatabaseStatus'
          description: System database status
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Database recovery is in progress or requires operator attention
      security:
        - cookieAuth: []
      summary: Get database resilience status
      tags:
        - Admin
  /api/admin/system/database/checkpoint:
    post:
      description: Runs an immediate serialized DuckDB checkpoint and records the operator action in the instance audit log.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemActionResponse'
          description: Checkpoint result
        "500":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Database checkpoint failed
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Database recovery is in progress or requires operator attention
      security:
        - cookieAuth: []
      summary: Checkpoint the shared database
      tags:
        - Admin
  /api/admin/system/health:
    get:
      description: Returns instance health, database status, worker status, and cluster leader state.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemHealth'
          description: System health
      security:
        - cookieAuth: []
      summary: Get system health
      tags:
        - Admin
  /api/admin/system/import-stage-cleanup:
    get:
      description: Returns stale staged import file counts, byte totals, retention policy, and the latest cleanup run state.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemImportStageCleanupStatus'
          description: Import staging cleanup status
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Store not available
      security:
        - cookieAuth: []
      summary: Get import staging cleanup status
      tags:
        - Admin
  /api/admin/system/import-stage-cleanup/run:
    post:
      description: Removes stale staged import upload files immediately and writes an instance audit log entry with the actor and outcome.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemImportStageCleanupRunResponse'
          description: Import staging cleanup result
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Cleanup disabled
        "500":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemImportStageCleanupRunResponse'
          description: Cleanup failed
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Store not available
      security:
        - cookieAuth: []
      summary: Run import staging cleanup
      tags:
        - Admin
  /api/admin/system/ingest:
    get:
      description: Returns recent hit, event, rejection, spam, and hit-rate counters across the instance, including tenant analytics databases.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemIngestStats'
          description: System ingest stats
      security:
        - cookieAuth: []
      summary: Get ingest volume
      tags:
        - Admin
  /api/admin/system/mail:
    get:
      description: Returns configured mail driver, host, port, encryption, sender identity, masked username, password presence, and last test result.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemMailStatus'
          description: System mail status
      security:
        - cookieAuth: []
      summary: Get mail status
      tags:
        - Admin
  /api/admin/system/mail/test:
    post:
      description: Sends a real test email to the specified recipient using the configured mail transport. Writes an instance audit log entry with the actor and outcome.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                email:
                  format: email
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemActionResponse'
          description: Mail test result
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid recipient
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Mailer not configured
      security:
        - cookieAuth: []
      summary: Send test email
      tags:
        - Admin
  /api/admin/system/report:
    get:
      description: Returns a markdown system report with instance, configuration, storage, DuckDB memory, ingest, and feature information for support requests.
      responses:
        "200":
          description: System report (text/markdown)
      security:
        - cookieAuth: []
      summary: Get system report
      tags:
        - Admin
  /api/admin/system/search-console:
    get:
      description: Returns Google Search Console credential, worker, and sync health without exposing OAuth secrets or raw Google payloads.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemSearchConsoleStatus'
          description: Search Console system status
      security:
        - cookieAuth: []
      summary: Get Search Console system status
      tags:
        - Admin
  /api/admin/system/spam-filter:
    get:
      description: Returns spam database path, rule count, auto-update state, last refresh time, and last error.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemSpamStatus'
          description: Spam filter status
      security:
        - cookieAuth: []
      summary: Get spam filter status
      tags:
        - Admin
  /api/admin/system/spam-filter/refresh:
    post:
      description: Downloads and rebuilds the spam database from the configured feeds. Writes an instance audit log entry with the actor and outcome.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemActionResponse'
          description: Refresh result
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Spam filter not available
      security:
        - cookieAuth: []
      summary: Refresh spam database
      tags:
        - Admin
  /api/admin/system/storage:
    get:
      description: Returns configured data paths, shared and tenant database sizes, backup path, spam cache path, disk capacity fields when available, and the shared database's DuckDB memory breakdown.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemStorage'
          description: System storage
      security:
        - cookieAuth: []
      summary: Get system storage
      tags:
        - Admin
  /api/admin/teams:
    get:
      description: Lists teams for instance-level administration, including default/archive state and member/site counts.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/AdminTeam'
                type: array
          description: Admin team list
      security:
        - cookieAuth: []
      summary: List all teams
      tags:
        - Admin
  /api/admin/teams/{id}:
    delete:
      description: Permanently deletes an archived non-default team and removes its per-tenant analytics database directory.
      parameters:
        - $ref: '#/components/parameters/teamID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdminDeleteTeamResponse'
          description: Delete archived team response
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Archive the team first, and ensure it has no sites
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Team not found
      security:
        - cookieAuth: []
      summary: Delete archived team
      tags:
        - Admin
  /api/admin/teams/{id}/archive:
    post:
      description: Archives a non-default team from the instance admin surface after its sites have been moved or deleted.
      parameters:
        - $ref: '#/components/parameters/teamID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: The default team cannot be archived, or the team is not found or already archived
      security:
        - cookieAuth: []
      summary: Archive team (admin)
      tags:
        - Admin
  /api/admin/users:
    get:
      description: Lists users for admin management.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  additionalProperties: true
                  type: object
                type: array
          description: User list
      security:
        - cookieAuth: []
      summary: List users
      tags:
        - Admin
  /api/admin/users/{id}:
    delete:
      description: Deletes user account (cannot delete self). Deletion is blocked if the target user is the sole owner of any team.
      parameters:
        - $ref: '#/components/parameters/adminUserID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdminDeleteUserBlockedResponse'
          description: Delete user blocked response
      security:
        - cookieAuth: []
      summary: Delete user
      tags:
        - Admin
  /api/admin/users/{id}/disable-2fa:
    post:
      description: Owner-only recovery action that clears TOTP, passkeys, pending MFA challenges, and remember-me sessions for the target user.
      parameters:
        - $ref: '#/components/parameters/adminUserID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdminDisableUserMFAResponse'
          description: Disable user MFA response
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Forbidden
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
      security:
        - cookieAuth: []
      summary: Disable user MFA
      tags:
        - Admin
  /api/admin/users/{id}/role:
    post:
      description: Updates instance role for target user.
      parameters:
        - $ref: '#/components/parameters/adminUserID'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                role:
                  type: string
              required:
                - role
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
      security:
        - cookieAuth: []
      summary: Update user role
      tags:
        - Admin
  /api/admin/webhooks:
    get:
      description: Human dashboard session only.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/Webhook'
                type: array
          description: Webhook list
      security:
        - cookieAuth: []
      summary: List instance webhooks
      tags:
        - Webhooks
    post:
      description: Creates a webhook and returns its signing secret exactly once. Human dashboard session only.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookInput'
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookSecretResponse'
          description: Created webhook and one-time secret
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid event or destination
      security:
        - cookieAuth: []
      summary: Create instance webhook
      tags:
        - Webhooks
  /api/admin/webhooks/events:
    get:
      description: Lists event types valid for this scope.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/WebhookEventDescriptor'
                type: array
          description: Event catalog
      security:
        - cookieAuth: []
      summary: List instance webhook events
      tags:
        - Webhooks
  /api/admin/webhooks/{webhookID}:
    delete:
      description: Deletes configuration. Existing delivery outcome records remain subject to retention.
      parameters:
        - in: path
          name: webhookID
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "204":
          description: Deleted
      security:
        - cookieAuth: []
      summary: Delete instance webhook
      tags:
        - Webhooks
    put:
      description: Updates configuration without changing the signing secret.
      parameters:
        - in: path
          name: webhookID
          required: true
          schema:
            format: uuid
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookInput'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Webhook'
          description: Updated webhook
      security:
        - cookieAuth: []
      summary: Update instance webhook
      tags:
        - Webhooks
  /api/admin/webhooks/{webhookID}/deliveries:
    get:
      description: Deliveries use the WebhookEventPayload schema with at-least-once, unordered semantics. Verify X-HitKeep-Signature (v1=<hex HMAC-SHA256>) over timestamp + "." + body with the one-time secret, reject stale X-HitKeep-Timestamp values, and deduplicate by X-HitKeep-Event-ID or X-HitKeep-Delivery-ID. Response bodies and signing secrets are never returned.
      parameters:
        - in: path
          name: webhookID
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/WebhookDelivery'
                type: array
          description: Delivery outcomes
      security:
        - cookieAuth: []
      summary: List instance webhook delivery outcomes
      tags:
        - Webhooks
  /api/admin/webhooks/{webhookID}/rotate:
    post:
      description: Re-signs pending deliveries, replaces the configured secret, and returns the new value exactly once. A request already in flight may still use the previous secret.
      parameters:
        - in: path
          name: webhookID
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookSecretResponse'
          description: Webhook and new one-time secret
      security:
        - cookieAuth: []
      summary: Rotate instance webhook signing secret
      tags:
        - Webhooks
  /api/admin/webhooks/{webhookID}/test:
    post:
      description: Deliveries use the WebhookEventPayload schema with at-least-once, unordered semantics. Verify X-HitKeep-Signature (v1=<hex HMAC-SHA256>) over timestamp + "." + body with the one-time secret, reject stale X-HitKeep-Timestamp values, and deduplicate by X-HitKeep-Event-ID or X-HitKeep-Delivery-ID.
      parameters:
        - in: path
          name: webhookID
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "202":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookTestResponse'
          description: Queued test delivery
      security:
        - cookieAuth: []
      summary: Queue instance webhook test
      tags:
        - Webhooks
  /api/ai-agents:
    get:
      description: Returns the embedded AI agent master list as display metadata including agent names, operator families, categories, and favicon-lookup hosts derived from each agent's documentation URL, plus the known AI referrer surfaces. The catalog is static per release.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AIAgentCatalog'
          description: AI agent catalog
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get AI agent catalog
      tags:
        - Sites
  /api/auth/accept-invite:
    post:
      description: Accepts a team invite. New invite-created users provide a password and receive a session cookie; existing users must sign in first and can accept with the token only.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                password:
                  minLength: 8
                  type: string
                token:
                  type: string
              required:
                - token
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
          description: Login response
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid or expired link
        "401":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Sign in to accept this invitation
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invite cannot be accepted
      summary: Accept invite
      tags:
        - Auth
  /api/auth/forgot-password:
    post:
      description: Sends password reset email if account exists.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                email:
                  format: email
                  type: string
              required:
                - email
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
      summary: Request password reset
      tags:
        - Auth
  /api/auth/mfa/email-link/request:
    post:
      description: Sends a one-time email link for an active MFA challenge.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                challenge_token:
                  format: uuid
                  type: string
                return_url:
                  type: string
              required:
                - challenge_token
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
        "502":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Failed to send email sign-in link
      summary: Request MFA email sign-in link
      tags:
        - Auth
  /api/auth/mfa/email-link/verify:
    get:
      description: Consumes a one-time MFA email link and completes the pending sign-in session.
      parameters:
        - in: query
          name: token
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "303":
          description: Redirects to the requested return URL after a successful sign-in
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Service not available
      summary: Verify MFA email sign-in link
      tags:
        - Auth
  /api/auth/mfa/recovery-code/verify:
    post:
      description: Consumes a recovery code for a pending MFA challenge.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                challenge_token:
                  format: uuid
                  type: string
                code:
                  type: string
              required:
                - challenge_token
                - code
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
      summary: Verify MFA recovery code
      tags:
        - Auth
  /api/auth/mfa/totp/verify:
    post:
      description: Verifies TOTP code for pending MFA challenge.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                challenge_token:
                  format: uuid
                  type: string
                code:
                  type: string
              required:
                - challenge_token
                - code
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
      summary: Verify MFA TOTP
      tags:
        - Auth
  /api/auth/passkey/login/finish:
    post:
      description: Verifies passkey assertion and issues session.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                challenge_token:
                  type: string
                credential:
                  additionalProperties: true
                  type: object
                remember_me:
                  type: boolean
              required:
                - challenge_token
                - credential
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
      summary: Finish passkey login
      tags:
        - Auth
  /api/auth/passkey/login/start:
    post:
      description: Creates passkey login challenge.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  challenge_token:
                    type: string
                  publicKey:
                    additionalProperties: true
                    type: object
                type: object
          description: Passkey challenge
      summary: Start passkey login
      tags:
        - Auth
  /api/auth/reset-password:
    post:
      description: Resets password using reset token.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                password:
                  minLength: 8
                  type: string
                token:
                  type: string
              required:
                - token
                - password
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid or expired link
      summary: Complete password reset
      tags:
        - Auth
  /api/auth/session:
    get:
      description: Returns the authenticated session expiry and configured accessibility warning policy.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthSession'
          description: Auth session
      security:
        - cookieAuth: []
      summary: Get session policy
      tags:
        - Auth
  /api/auth/session/extend:
    post:
      description: Issues a fresh session cookie and returns the updated expiry policy.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthSession'
          description: Auth session
      security:
        - cookieAuth: []
      summary: Extend session
      tags:
        - Auth
  /api/auth/social/complete:
    post:
      description: Consumes a short-lived completion token once, resolves or safely links the immutable identity, accepts a bound invitation when applicable, and returns the standard login or MFA-required shape. Microsoft first links require HitKeep email confirmation unless an authenticated session or valid invitation proves the account.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SocialCompleteRequest'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SocialCompleteResponse'
          description: Social login result
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid flow or target email
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Account unavailable
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Completion replay or identity conflict
      summary: Complete social login
      tags:
        - Auth
  /api/auth/social/confirm:
    get:
      description: Consumes a hashed one-time email confirmation token for a Microsoft account link or signup, then redirects without exposing provider identity data.
      parameters:
        - in: query
          name: token
          required: true
          schema:
            type: string
      responses:
        "303":
          description: Redirects to sign-in, onboarding, checkout, or a stable error path
      summary: Confirm Microsoft social identity
      tags:
        - Auth
  /api/auth/social/preview:
    post:
      description: Returns safe provider and observed-email metadata for a still-valid completion token. Provider subjects are never returned.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SocialCompleteRequest'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SocialPreviewResponse'
          description: Social completion preview
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Completion token invalid, expired, or already consumed
      summary: Preview social completion
      tags:
        - Auth
  /api/auth/social/providers:
    get:
      description: Returns only completely configured social providers. Open signup is reported separately and may be disabled while login, invitations, and account linking remain available.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SocialProvidersResponse'
          description: Social provider availability
      summary: List social sign-in providers
      tags:
        - Auth
  /api/auth/social/{provider}/callback:
    get:
      description: Consumes the state once, exchanges the code, validates the immutable provider identity and required claims, discards provider tokens, and redirects with a short-lived completion token in the URL fragment. Upstream responses are never exposed.
      parameters:
        - in: path
          name: provider
          required: true
          schema:
            enum:
              - google
              - github
              - microsoft
            type: string
        - in: query
          name: state
          required: true
          schema:
            type: string
        - in: query
          name: code
          required: false
          schema:
            type: string
        - in: query
          name: error
          required: false
          schema:
            type: string
      responses:
        "303":
          description: Redirects to the bound local flow with a fragment completion token or stable error code
      summary: Validate social provider callback
      tags:
        - Auth
  /api/auth/social/{provider}/start:
    post:
      description: Starts a login, signup, or invitation flow using authorization code, browser-bound one-time state, PKCE S256, and an OIDC nonce where applicable. Return URLs are restricted to safe local application paths.
      parameters:
        - in: path
          name: provider
          required: true
          schema:
            enum:
              - google
              - github
              - microsoft
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SocialStartRequest'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SSOStartResponse'
          description: Provider authorization URL
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid flow
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Provider or social signup unavailable
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Provider unavailable
      summary: Start social authentication
      tags:
        - Auth
  /api/auth/sso:
    get:
      description: Returns whether at least one team has an enabled SSO configuration. This controls whether the public login page offers SSO; it does not reveal team or provider details.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SSOAvailability'
          description: SSO availability
      summary: Get SSO availability
      tags:
        - Auth
  /api/auth/sso/callback:
    get:
      description: Consumes one OIDC callback, validates the authorization state, PKCE exchange, signed ID token, issuer, audience, nonce, verified email, and allowed domain, then rechecks live team authorization. A scoped pending invitation retains its requested role; optional trusted-domain JIT provisioning creates a Member only after managed-cloud limits pass. Provider access, ID, and refresh tokens are never persisted.
      parameters:
        - in: query
          name: state
          required: true
          schema:
            type: string
        - in: query
          name: code
          required: false
          schema:
            type: string
        - in: query
          name: error
          required: false
          schema:
            type: string
      responses:
        "303":
          description: Redirects to the requested safe return path on success. Failed invitation flows return to invitation acceptance with the unconsumed token and a stable error code; other failures return to login.
      summary: Complete SSO login
      tags:
        - Auth
  /api/auth/sso/invite:
    post:
      description: Returns whether the team targeted by an unconsumed invitation has an enabled and entitled SSO configuration for the invited email domain. Invalid, expired, and non-SSO invitations return enabled=false without exposing team or provider details.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                invite_token:
                  type: string
              required:
                - invite_token
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SSOAvailability'
          description: Invitation SSO availability
      summary: Get invitation SSO availability
      tags:
        - Auth
  /api/auth/sso/start:
    post:
      description: Resolves an enabled team OIDC provider from the submitted email or invitation, requires an existing membership, a pending invitation, or enabled trusted-domain auto-provisioning, and creates a short-lived state, nonce, and PKCE-protected authorization request. Existing password login remains available.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SSOStartRequest'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SSOStartResponse'
          description: OIDC authorization URL
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid email or no enabled SSO provider for the email domain
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: The account has no team membership, pending invitation, or permitted JIT access, or a managed-cloud limit was reached
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Configured SSO provider is unavailable
      summary: Start SSO login
      tags:
        - Auth
  /api/cloud/billing/checkout:
    post:
      description: Creates a Stripe Checkout session to upgrade the authenticated hosted cloud team to a paid plan.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                locale:
                  type: string
                plan_code:
                  enum:
                    - pro
                    - business
                  type: string
              required:
                - plan_code
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  url:
                    type: string
                required:
                  - url
                type: object
          description: Billing checkout session
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "401":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Unauthorized
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Cloud billing account not found
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Use billing portal to manage an existing paid plan
        "502":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Unable to start checkout
      security:
        - cookieAuth: []
      summary: Create billing checkout session
      tags:
        - Cloud
      x-hitkeep-availability: cloud
      x-hitkeep-build-tags:
        - billing
      x-internal: true
  /api/cloud/billing/portal:
    post:
      description: Creates a Stripe Customer Portal session for the authenticated hosted cloud team.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                locale:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  url:
                    type: string
                required:
                  - url
                type: object
          description: Billing portal session
        "401":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Unauthorized
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Cloud billing account not found
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Stripe customer is not configured
        "502":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Unable to start billing portal
      security:
        - cookieAuth: []
      summary: Create billing portal session
      tags:
        - Cloud
      x-hitkeep-availability: cloud
      x-hitkeep-build-tags:
        - billing
      x-internal: true
  /api/cloud/signup:
    post:
      description: Creates a hosted cloud user and team, then optionally returns a Stripe Checkout URL for paid plans.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                email:
                  format: email
                  type: string
                given_name:
                  type: string
                jurisdiction:
                  type: string
                last_name:
                  type: string
                locale:
                  type: string
                password:
                  minLength: 8
                  type: string
                plan_code:
                  enum:
                    - free
                    - pro
                    - business
                  type: string
                team_name:
                  type: string
              required:
                - email
                - password
                - team_name
                - plan_code
              type: object
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                properties:
                  billing:
                    enum:
                      - monthly
                      - annual
                    type: string
                  checkout_url:
                    type: string
                  plan_code:
                    type: string
                  redirect_url:
                    type: string
                  retry_after_seconds:
                    minimum: 0
                    type: integer
                  status:
                    type: string
                required:
                  - status
                  - plan_code
                type: object
          description: Cloud signup response
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Cloud signup disabled
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Email already exists
        "502":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Unable to start checkout
      summary: Create managed cloud account
      tags:
        - Cloud
      x-hitkeep-availability: cloud
      x-hitkeep-build-tags:
        - billing
      x-internal: true
  /api/cloud/signup/resend-verification:
    post:
      description: Requests another verification email for a password-based managed cloud signup. Valid requests receive the same response regardless of account state.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                email:
                  format: email
                  type: string
              required:
                - email
              type: object
        required: true
      responses:
        "202":
          content:
            application/json:
              schema:
                properties:
                  retry_after_seconds:
                    minimum: 0
                    type: integer
                  status:
                    enum:
                      - accepted
                    type: string
                required:
                  - status
                  - retry_after_seconds
                type: object
          description: Signup verification resend accepted
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Cloud signup disabled
        "429":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Too many requests
      summary: Resend managed cloud signup verification
      tags:
        - Cloud
      x-hitkeep-availability: cloud
      x-hitkeep-build-tags:
        - billing
      x-internal: true
  /api/cloud/signup/social/complete:
    post:
      description: Creates a hosted cloud user and team after provider authentication, preserving the existing plan, billing-intent, locale, region, Terms, and onboarding behavior. Google and GitHub verified email skip HitKeep email verification; Microsoft returns verification_sent.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SocialSignupCompleteRequest'
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SocialSignupCompleteResponse'
          description: Social signup result
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid signup intent
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Social signup disabled
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Account or identity already exists
      summary: Complete managed social signup
      tags:
        - Cloud
      x-hitkeep-availability: cloud
      x-hitkeep-build-tags:
        - billing
      x-internal: true
  /api/cloud/webhooks/stripe:
    post:
      description: Processes Stripe billing lifecycle events for managed cloud subscriptions.
      responses:
        "200":
          description: Webhook processed
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid webhook
      summary: Stripe webhook
      tags:
        - Cloud
      x-hitkeep-availability: cloud
      x-hitkeep-build-tags:
        - billing
      x-internal: true
  /api/docs/v1/openapi.json:
    get:
      description: Returns the full OpenAPI 3.1 specification for v1.
      responses:
        "200":
          description: OpenAPI 3.1 JSON
      summary: OpenAPI v1 document
      tags:
        - System
  /api/docs/versions:
    get:
      description: Returns available OpenAPI document versions.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OpenAPIVersionList'
          description: Version list
      summary: List API doc versions
      tags:
        - System
  /api/favicon/{domain}:
    get:
      description: Proxies favicon by domain.
      parameters:
        - $ref: '#/components/parameters/domain'
      responses:
        "200":
          description: Favicon image
      summary: Get favicon
      tags:
        - Sites
  /api/ingest/server/event:
    post:
      description: Accepts one trusted server-side custom event from an API client. This server-to-server endpoint does not require browser Origin or Referer headers. HitKeep resolves the site from the submitted URL hostname, requires site.manage_data for that resolved site, and uses visitor_ip for ingest-time exclusions, spam filtering, and country, region, city, provider, and ASN lookup. Stored analytics records contain derived country, region, city, provider, and ASN context. HitKeep does not store the raw visitor IP. This endpoint uses the ingest rate limiter.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                dnt:
                  description: When true, HitKeep drops the record consistently with browser tracker privacy behavior.
                  type: boolean
                name:
                  description: Custom event name.
                  type: string
                properties:
                  additionalProperties: true
                  type: object
                referrer:
                  type: string
                session_id:
                  description: Optional visitor grouping key. If omitted, this event gets its own standalone session.
                  format: uuid
                  type: string
                timestamp:
                  description: Canonical analytics time in RFC3339 format.
                  format: date-time
                  type: string
                url:
                  description: Absolute page or event context URL. The hostname resolves the configured HitKeep site.
                  format: uri
                  type: string
                user_agent:
                  description: User agent from the original visitor request context.
                  type: string
                visitor_ip:
                  description: Trusted transient visitor IP context. Used to derive country, region, city, provider, and ASN metadata. HitKeep does not store the raw visitor IP.
                  format: ip
                  type: string
              required:
                - url
                - timestamp
                - visitor_ip
                - user_agent
                - name
              type: object
        required: true
      responses:
        "202":
          description: Accepted. Empty response body.
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "401":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Unauthorized
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Site not found
        "429":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Too many requests
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Service not available
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Ingest server-side event
      tags:
        - Ingest
  /api/ingest/server/pageview:
    post:
      description: Accepts one trusted server-side pageview from an API client. Use this endpoint for backend forwarding, CMS plugins, reverse proxy forwarding, edge workers, and historical pageview replay. It does not require browser Origin or Referer headers. HitKeep resolves the site from the submitted URL hostname, requires site.manage_data for that resolved site, and uses visitor_ip for ingest-time exclusions, spam filtering, and country, region, city, provider, and ASN lookup. UTM values and hk_qr QR attribution are read from the query string in url, for example utm_source, utm_campaign, and hk_qr on the visitor-facing URL, not from top-level JSON fields. Stored analytics records contain derived country, region, city, provider, and ASN context. HitKeep does not store the raw visitor IP. This endpoint uses the ingest rate limiter.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                dnt:
                  description: When true, HitKeep drops the record consistently with browser tracker privacy behavior.
                  type: boolean
                language:
                  type: string
                page_id:
                  description: Optional page identifier. Generated when omitted.
                  format: uuid
                  type: string
                referrer:
                  type: string
                screen_height:
                  type: integer
                screen_width:
                  type: integer
                session_id:
                  description: Optional visitor grouping key. If omitted, this record gets its own standalone session.
                  format: uuid
                  type: string
                timestamp:
                  description: Canonical analytics time in RFC3339 format.
                  format: date-time
                  type: string
                url:
                  description: Absolute page URL. The hostname resolves the configured HitKeep site. UTM values and hk_qr QR attribution are read from this URL query string.
                  format: uri
                  type: string
                user_agent:
                  description: User agent from the original visitor request context.
                  type: string
                viewport_height:
                  type: integer
                viewport_width:
                  type: integer
                visitor_ip:
                  description: Trusted transient visitor IP context. Used to derive country, region, city, provider, and ASN metadata. HitKeep does not store the raw visitor IP.
                  format: ip
                  type: string
              required:
                - url
                - timestamp
                - visitor_ip
                - user_agent
              type: object
        required: true
      responses:
        "202":
          description: Accepted. Empty response body.
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "401":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Unauthorized
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Site not found
        "429":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Too many requests
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Service not available
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Ingest server-side pageview
      tags:
        - Ingest
  /api/initial-user:
    post:
      description: Bootstraps first user account during setup.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                email:
                  format: email
                  type: string
                given_name:
                  type: string
                last_name:
                  type: string
                password:
                  minLength: 8
                  type: string
              required:
                - email
                - password
              type: object
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                properties:
                  token:
                    type: string
                type: object
          description: Token created
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Setup already complete
      summary: Create initial admin
      tags:
        - Auth
  /api/integrations/google-search-console/oauth/callback:
    get:
      description: Completes the authenticated Google Search Console OAuth callback.
      parameters:
        - in: query
          name: state
          required: true
          schema:
            type: string
        - in: query
          name: code
          required: true
          schema:
            type: string
      responses:
        "302":
          description: Redirects to the integration page.
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid OAuth state or callback
        "502":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: OAuth exchange failed
      security:
        - cookieAuth: []
      summary: Complete Google Search Console OAuth
      tags:
        - Integrations
  /api/login:
    post:
      description: Authenticates user credentials and issues session cookie.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                email:
                  format: email
                  type: string
                password:
                  type: string
                remember_me:
                  type: boolean
              required:
                - email
                - password
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
          description: Login response
        "401":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid credentials
      summary: Login
      tags:
        - Auth
  /api/logout:
    post:
      description: Clears session and remember-me cookies.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
      security:
        - cookieAuth: []
      summary: Logout
      tags:
        - Auth
  /api/qr-share/{token}/qr-code:
    get:
      description: Returns the single QR code exposed by a QR-only share link.
      parameters:
        - $ref: '#/components/parameters/token'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QRCode'
          description: QR code
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
      summary: Get QR-only shared code
      tags:
        - Share
  /api/qr-share/{token}/qr-code/asset:
    get:
      description: Returns the filesystem-backed QR graphic exposed by a QR-only share link.
      parameters:
        - $ref: '#/components/parameters/token'
      responses:
        "200":
          description: Image asset stream
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Asset not found
      summary: Get QR-only shared asset
      tags:
        - Share
  /api/qr-share/{token}/qr-code/opens/timeseries:
    get:
      description: Returns QR redirect opens over time for a QR-only share link.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/QRCodeOpenSeriesPoint'
                type: array
          description: QR open series
      summary: Get QR-only shared open timeseries
      tags:
        - Share
  /api/qr-share/{token}/qr-code/summary:
    get:
      description: Returns redirect opens and QR-scoped analytics for a QR-only share link.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QRCodeSummary'
          description: QR summary
      summary: Get QR-only shared summary
      tags:
        - Share
  /api/qr-share/{token}/qr-code/takeout:
    get:
      description: Exports one QR code's definition, opens, and scoped analytics through a QR-only share link.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/format'
      responses:
        "200":
          description: Export file stream
      summary: Export QR-only shared takeout
      tags:
        - Share
  /api/report-recipient-confirmations/{opaque_token}:
    get:
      description: Returns non-sensitive team, report, cadence, site-domain, and expiry metadata without changing consent.
      parameters:
        - $ref: '#/components/parameters/reportConfirmationToken'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReportRecipientConfirmation'
          description: Confirmation metadata
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid confirmation
        "410":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Expired confirmation
      summary: Inspect report recipient confirmation
      tags:
        - Reports
    post:
      description: Confirms or declines external report delivery by explicit POST. The token is single-use.
      parameters:
        - $ref: '#/components/parameters/reportConfirmationToken'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                action:
                  enum:
                    - confirm
                    - decline
                  type: string
              required:
                - action
              type: object
        required: true
      responses:
        "204":
          description: Consent decision recorded
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid confirmation
        "410":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Expired confirmation
      summary: Decide report recipient confirmation
      tags:
        - Reports
  /api/report-runs/{run_id}/retry:
    post:
      description: Queues failed deliveries from a manageable report run for retry with the stable message ID.
      parameters:
        - $ref: '#/components/parameters/reportRunID'
      responses:
        "202":
          description: Retry queued
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Mail unavailable
      security:
        - cookieAuth: []
      summary: Retry failed report run
      tags:
        - Reports
  /api/reports:
    get:
      description: Lists personal and team reports visible to the authenticated user, including schedule, recipients, next run, and last outcome.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/ReportDefinition'
                type: array
          description: Reports
      security:
        - cookieAuth: []
      summary: List reports
      tags:
        - Reports
    post:
      description: Creates a personal or authorized team report. Active reports require configured mail delivery.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReportDefinitionInput'
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReportDefinition'
          description: Created report
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid report
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Mail unavailable
      security:
        - cookieAuth: []
      summary: Create report
      tags:
        - Reports
  /api/reports/preview:
    post:
      description: Validates a draft report and returns its resolved schedule and reporting period without persisting rendered email content.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                definition:
                  $ref: '#/components/schemas/ReportDefinitionInput'
                report_id:
                  format: uuid
                  type: string
              required:
                - definition
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReportPreview'
          description: Report preview
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid report
      security:
        - cookieAuth: []
      summary: Preview report
      tags:
        - Reports
  /api/reports/unsubscribe/{opaque_token}:
    get:
      description: Visible unsubscribe target that opts the signed recipient out of one report.
      parameters:
        - $ref: '#/components/parameters/unsubscribeToken'
      responses:
        "200":
          description: Unsubscribed
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid token
      summary: Unsubscribe from report
      tags:
        - Reports
    post:
      description: RFC 8058 one-click unsubscribe endpoint. The opaque token is signed and only its hash is stored.
      parameters:
        - $ref: '#/components/parameters/unsubscribeToken'
      responses:
        "204":
          description: Unsubscribed
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid token
      summary: One-click unsubscribe
      tags:
        - Reports
  /api/reports/{report_id}:
    delete:
      description: Deletes a manageable report and its delivery ledger.
      parameters:
        - $ref: '#/components/parameters/reportID'
      responses:
        "204":
          description: Deleted
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
      security:
        - cookieAuth: []
      summary: Delete report
      tags:
        - Reports
    get:
      description: Returns one report visible to the authenticated user.
      parameters:
        - $ref: '#/components/parameters/reportID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReportDefinition'
          description: Report
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
      security:
        - cookieAuth: []
      summary: Get report
      tags:
        - Reports
    patch:
      description: Updates an owned personal report or an authorized team report and recalculates its next UTC run.
      parameters:
        - $ref: '#/components/parameters/reportID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReportDefinitionUpdate'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReportDefinition'
          description: Updated report
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid report
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Mail unavailable
      security:
        - cookieAuth: []
      summary: Update report
      tags:
        - Reports
  /api/reports/{report_id}/recipients/{recipient_id}/confirmation:
    post:
      description: Rotates and resends an external recipient's confirmation token. Requires team owner or admin access and enforces a 15-minute cooldown.
      parameters:
        - $ref: '#/components/parameters/reportID'
        - $ref: '#/components/parameters/reportRecipientID'
      responses:
        "202":
          description: Confirmation accepted by mail server
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Mail unavailable
        "429":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Confirmation sent recently
        "502":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Mail server did not accept message
      security:
        - cookieAuth: []
      summary: Resend external recipient confirmation
      tags:
        - Reports
  /api/reports/{report_id}/resubscribe:
    post:
      description: Clears the current user's opt-out for this report. Team managers cannot resubscribe another recipient.
      parameters:
        - $ref: '#/components/parameters/reportID'
      responses:
        "204":
          description: Resubscribed
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
      security:
        - cookieAuth: []
      summary: Resubscribe to report
      tags:
        - Reports
  /api/reports/{report_id}/runs:
    get:
      description: Lists recent runs and recipient delivery outcomes without rendered bodies or raw SMTP errors.
      parameters:
        - $ref: '#/components/parameters/reportID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/ReportRun'
                type: array
          description: Report runs
      security:
        - cookieAuth: []
      summary: List report runs
      tags:
        - Reports
  /api/reports/{report_id}/test-send:
    post:
      description: Builds the current report content for its latest completed period and sends it only to the current user. Team tests are audited.
      parameters:
        - $ref: '#/components/parameters/reportID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReportTestSendResponse'
          description: Accepted by mail server
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Mail unavailable
        "422":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Report content unavailable or suppressed
        "502":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Mail server did not accept message
      security:
        - cookieAuth: []
      summary: Test report delivery
      tags:
        - Reports
  /api/share/{token}/site:
    get:
      description: Gets site metadata from share token.
      parameters:
        - $ref: '#/components/parameters/token'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Site'
          description: Site
      summary: Get shared site
      tags:
        - Share
  /api/share/{token}/sites/{id}/ai-activity:
    get:
      description: 'Returns the unified AI activity report through a read-only share token, including the server-log fetch side: the query runs under the token grant for the shared site.'
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/filterType'
        - $ref: '#/components/parameters/filterValue'
        - description: Start of an optional comparison window. Malformed values are ignored rather than rejected.
          in: query
          name: compare_from
          schema:
            format: date-time
            type: string
        - description: End of an optional comparison window.
          in: query
          name: compare_to
          schema:
            format: date-time
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AIActivityReport'
          description: AI activity report
      summary: Shared AI activity report
      tags:
        - Share
  /api/share/{token}/sites/{id}/events/audience:
    get:
      description: Returns event audience through share token, including aggregate pages, referrers, devices, countries, cities, providers, and ASNs. Optional property filters and repeatable filter=type:value hit-dimension filters restrict the sessions included.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/eventName'
        - $ref: '#/components/parameters/eventPropertyKey'
        - $ref: '#/components/parameters/eventPropertyValue'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/eventDimensionKey'
        - $ref: '#/components/parameters/eventDimensionValue'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventAudience'
          description: Event audience
      summary: Shared event audience
      tags:
        - Share
  /api/share/{token}/sites/{id}/events/breakdown:
    get:
      description: Returns event property value breakdown through share token.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/eventName'
        - $ref: '#/components/parameters/eventPropertyKeyRequired'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/MetricStat'
                type: array
          description: Event property breakdown
      summary: Shared event property breakdown
      tags:
        - Share
  /api/share/{token}/sites/{id}/events/names:
    get:
      description: Lists event names through share token.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  type: string
                type: array
          description: Event names
      summary: Shared event names
      tags:
        - Share
  /api/share/{token}/sites/{id}/events/properties:
    get:
      description: Lists event property keys through share token.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/eventName'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  type: string
                type: array
          description: Event property keys
      summary: Shared event property keys
      tags:
        - Share
  /api/share/{token}/sites/{id}/events/timeseries:
    get:
      description: Returns event timeseries through share token. Optional property filters and repeatable filter=type:value hit-dimension filters restrict the sessions counted.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/eventName'
        - $ref: '#/components/parameters/eventPropertyKey'
        - $ref: '#/components/parameters/eventPropertyValue'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/eventDimensionKey'
        - $ref: '#/components/parameters/eventDimensionValue'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/EventSeriesPoint'
                type: array
          description: Event timeseries
      summary: Shared event timeseries
      tags:
        - Share
  /api/share/{token}/sites/{id}/funnels:
    get:
      description: Lists funnels through share token.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/Funnel'
                type: array
          description: Funnels
      summary: Shared funnels
      tags:
        - Share
  /api/share/{token}/sites/{id}/funnels/timeseries:
    get:
      description: Returns funnel timeseries through share token.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/funnelIDQuery'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/FunnelSeriesPoint'
                type: array
          description: Funnel timeseries
      summary: Shared funnel timeseries
      tags:
        - Share
  /api/share/{token}/sites/{id}/funnels/{funnelID}/stats:
    get:
      description: Returns funnel stats through share token.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/funnelID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FunnelStats'
          description: Funnel stats
      summary: Shared funnel stats
      tags:
        - Share
  /api/share/{token}/sites/{id}/goals:
    get:
      description: Lists goals through share token.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/Goal'
                type: array
          description: Goals
      summary: Shared goals
      tags:
        - Share
  /api/share/{token}/sites/{id}/goals/timeseries:
    get:
      description: Returns goal timeseries through share token.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/goalIDQuery'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/GoalSeriesPoint'
                type: array
          description: Goal timeseries
      summary: Shared goal timeseries
      tags:
        - Share
  /api/share/{token}/sites/{id}/hits:
    get:
      description: Returns paginated raw hits through share token.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/query'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/order'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/filterType'
        - $ref: '#/components/parameters/filterValue'
        - $ref: '#/components/parameters/goalIDQuery'
        - $ref: '#/components/parameters/funnelIDQuery'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedHits'
          description: Paginated hits
      summary: Shared hits
      tags:
        - Share
  /api/share/{token}/sites/{id}/hits/export:
    get:
      description: Exports hits through share token in csv/xlsx/parquet/json/ndjson.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/query'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/filterType'
        - $ref: '#/components/parameters/filterValue'
        - $ref: '#/components/parameters/goalIDQuery'
        - $ref: '#/components/parameters/funnelIDQuery'
        - $ref: '#/components/parameters/format'
      responses:
        "200":
          description: Export file stream
      summary: Export shared hits
      tags:
        - Share
  /api/share/{token}/sites/{id}/opportunities:
    get:
      description: Lists saved opportunity recommendations through a read-only share token. Returns only validated customer-visible outputs with cited evidence.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SharedOpportunityListResponse'
          description: Opportunity list
      summary: Shared opportunities
      tags:
        - Share
  /api/share/{token}/sites/{id}/qr-codes:
    get:
      description: Lists QR campaign definitions through an existing site share link.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/QRCode'
                type: array
          description: QR codes
      summary: List shared QR codes
      tags:
        - Share
  /api/share/{token}/sites/{id}/qr-codes/{qrID}:
    get:
      description: Returns one QR campaign definition through an existing site share link.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QRCode'
          description: QR code
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
      summary: Get shared QR code
      tags:
        - Share
  /api/share/{token}/sites/{id}/qr-codes/{qrID}/asset:
    get:
      description: Returns the filesystem-backed QR graphic through an existing site share link.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
      responses:
        "200":
          description: Image asset stream
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Asset not found
      summary: Get shared QR asset
      tags:
        - Share
  /api/share/{token}/sites/{id}/qr-codes/{qrID}/opens/timeseries:
    get:
      description: Returns QR redirect opens over time through an existing site share link.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/QRCodeOpenSeriesPoint'
                type: array
          description: QR open series
      summary: Shared QR open timeseries
      tags:
        - Share
  /api/share/{token}/sites/{id}/qr-codes/{qrID}/summary:
    get:
      description: Returns one QR code's analytics through an existing site share link.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QRCodeSummary'
          description: QR summary
      summary: Shared QR analytics summary
      tags:
        - Share
  /api/share/{token}/sites/{id}/qr-codes/{qrID}/takeout:
    get:
      description: Exports one QR code's definition, opens, and scoped analytics through an existing site share link.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
        - $ref: '#/components/parameters/format'
      responses:
        "200":
          description: Export file stream
      summary: Export shared QR takeout
      tags:
        - Share
  /api/share/{token}/sites/{id}/realtime:
    get:
      description: Streams privacy-safe site-scoped analytics invalidation events through a read-only share token using server-sent events.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          description: Server-sent event stream with analytics.changed and analytics.resync events
      summary: Stream shared realtime changes
      tags:
        - Share
      x-internal: true
  /api/share/{token}/sites/{id}/stats:
    get:
      description: Returns stats through share token.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/filterType'
        - $ref: '#/components/parameters/filterValue'
        - $ref: '#/components/parameters/goalIDQuery'
        - $ref: '#/components/parameters/funnelIDQuery'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SiteStats'
          description: Site stats
      summary: Shared site stats
      tags:
        - Share
  /api/share/{token}/sites/{id}/web-vitals/breakdown:
    get:
      description: Returns Web Vitals visitor context breakdown through a read-only share token.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/webVitalPath'
        - $ref: '#/components/parameters/webVitalRating'
        - $ref: '#/components/parameters/webVitalMetric'
          required: true
        - description: Visitor context dimension.
          in: query
          name: dimension
          required: true
          schema:
            enum:
              - browser
              - country
              - language
              - device
              - city
              - provider
              - asn
            type: string
        - $ref: '#/components/parameters/limit'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/WebVitalDimensionRow'
                type: array
          description: Web Vitals breakdown
      summary: Shared Web Vitals visitor context breakdown
      tags:
        - Share
  /api/share/{token}/sites/{id}/web-vitals/pages:
    get:
      description: Returns Web Vitals page breakdown through a read-only share token.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/webVitalPath'
        - $ref: '#/components/parameters/webVitalRating'
        - $ref: '#/components/parameters/webVitalMetric'
          required: true
        - $ref: '#/components/parameters/limit'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/WebVitalPageRow'
                type: array
          description: Web Vitals pages
      summary: Shared Web Vitals page breakdown
      tags:
        - Share
  /api/share/{token}/sites/{id}/web-vitals/summary:
    get:
      description: Returns Web Vitals summary through a read-only share token.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/webVitalPath'
        - $ref: '#/components/parameters/webVitalRating'
        - $ref: '#/components/parameters/webVitalMetric'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/WebVitalSummaryMetric'
                type: array
          description: Web Vitals summary
      summary: Shared Web Vitals summary
      tags:
        - Share
  /api/share/{token}/sites/{id}/web-vitals/timeseries:
    get:
      description: Returns Web Vitals timeseries through a read-only share token.
      parameters:
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/webVitalPath'
        - $ref: '#/components/parameters/webVitalRating'
        - $ref: '#/components/parameters/webVitalMetric'
          required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/WebVitalSeriesPoint'
                type: array
          description: Web Vitals timeseries
      summary: Shared Web Vitals timeseries
      tags:
        - Share
  /api/sites:
    get:
      description: Lists sites visible to caller (session or API key scope).
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/Site'
                type: array
          description: Sites
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: List accessible sites
      tags:
        - Sites
    post:
      description: Creates a new tracked site using an apex domain or subdomain without protocol, port, path, query, fragment, or www prefix.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                domain:
                  description: Apex domain or subdomain without protocol, port, path, query, fragment, or www prefix (e.g. example.com or blog.example.com).
                  type: string
              required:
                - domain
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Site'
          description: Site
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid domain
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Domain exists
      security:
        - cookieAuth: []
      summary: Create site
      tags:
        - Sites
  /api/sites/overview:
    get:
      description: Returns lightweight overview metrics and chart data for all sites visible to the caller.
      parameters:
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SitesOverviewStatsResponse'
          description: Sites overview stats
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get sites overview stats
      tags:
        - Sites
  /api/sites/{id}:
    delete:
      description: Deletes a site and associated analytics data.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
      security:
        - cookieAuth: []
      summary: Delete site
      tags:
        - Sites
  /api/sites/{id}/ai-activity:
    get:
      description: 'Returns the unified AI activity report for a site: one merged view over tracked AI hits (classified at query time from the user agent and referrer) and server-log AI fetch records. Every count is tracked hits plus fetch records, and each row keeps the provenance split. Repeatable filter=type:value params narrow both sides where the dimension exists on both: ai_bot maps to the fetch assistant name, ai_bot_category to the fetch assistant category (falling back to the agent name for records ingested before that column existed), and path to both. An ai_source filter excludes the fetch side outright because fetch records carry no referrer, while hit-only dimensions such as country or device leave the fetch side unrestricted. The pageviews denominator applies the non-AI filters only.'
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/filterType'
        - $ref: '#/components/parameters/filterValue'
        - description: Start of an optional comparison window. Malformed values are ignored rather than rejected.
          in: query
          name: compare_from
          schema:
            format: date-time
            type: string
        - description: End of an optional comparison window.
          in: query
          name: compare_to
          schema:
            format: date-time
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AIActivityReport'
          description: AI activity report
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get AI activity report
      tags:
        - Sites
  /api/sites/{id}/ai-chatbots/export:
    get:
      description: Exports AI chatbot instrumentation events for the selected site and date range in csv/xlsx/parquet/json/ndjson. Optional scope filters restrict the export to a single provider, bot, surface, or model. Repeatable hit-dimension filters restrict the export to matching audience sessions.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/format'
        - $ref: '#/components/parameters/filter'
        - description: Optional chatbot scope filter key.
          in: query
          name: scope_key
          schema:
            enum:
              - provider
              - bot_id
              - surface
              - model
            type: string
        - description: Optional chatbot scope filter value. Requires scope_key.
          in: query
          name: scope_value
          schema:
            type: string
      responses:
        "200":
          description: Export file stream
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Export AI chatbot events
      tags:
        - Sites
  /api/sites/{id}/ai-fetch/correlation:
    get:
      description: Returns directional AI fetch correlation metrics for a site by matching AI crawler fetches to later AI-referred visits on the same path within a bounded window. Assistant filters apply to the fetch side only; correlated visit counts include any AI assistant referrer that later drove a human visit to the same path.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - description: Optional AI assistant bot name filter.
          in: query
          name: assistant_name
          schema:
            type: string
        - description: Optional AI assistant family filter.
          in: query
          name: assistant_family
          schema:
            type: string
        - description: Optional AI fetch resource type filter.
          in: query
          name: resource_type
          schema:
            enum:
              - html
              - document
              - image
              - other
            type: string
        - description: Optional exact fetched path filter.
          in: query
          name: path
          schema:
            type: string
        - description: Directional correlation window in days. Must be between 1 and 90. Defaults to 30.
          in: query
          name: window_days
          schema:
            default: 30
            maximum: 90
            minimum: 1
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AIFetchCorrelationReport'
          description: AI fetch correlation report
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get AI fetch correlation report
      tags:
        - Sites
  /api/sites/{id}/ai-fetch/export:
    get:
      description: Exports AI fetch records for the selected site and date range in csv/xlsx/parquet/json/ndjson. Optional assistant and resource filters restrict the export to a subset of crawler traffic.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/format'
        - description: Optional AI assistant bot name filter.
          in: query
          name: assistant_name
          schema:
            type: string
        - description: Optional AI assistant family filter.
          in: query
          name: assistant_family
          schema:
            type: string
        - description: Optional AI fetch resource type filter.
          in: query
          name: resource_type
          schema:
            enum:
              - html
              - document
              - image
              - other
            type: string
        - description: Optional exact fetched path filter.
          in: query
          name: path
          schema:
            type: string
      responses:
        "200":
          description: Export file stream
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Export AI fetch records
      tags:
        - Sites
  /api/sites/{id}/ai-fetch/overview:
    get:
      description: Returns aggregate AI fetch metrics for a site including request counts, error rates, response time, assistant breakdowns, and resource type split.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - description: Optional AI assistant bot name filter.
          in: query
          name: assistant_name
          schema:
            type: string
        - description: Optional AI assistant family filter.
          in: query
          name: assistant_family
          schema:
            type: string
        - description: Optional AI fetch resource type filter.
          in: query
          name: resource_type
          schema:
            enum:
              - html
              - document
              - image
              - other
            type: string
        - description: Optional exact fetched path filter.
          in: query
          name: path
          schema:
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AIFetchOverview'
          description: AI fetch overview
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get AI fetch overview
      tags:
        - Sites
  /api/sites/{id}/ai-fetch/timeseries:
    get:
      description: Returns AI fetch request counts over time for the selected site and filter set.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - description: Optional AI assistant bot name filter.
          in: query
          name: assistant_name
          schema:
            type: string
        - description: Optional AI assistant family filter.
          in: query
          name: assistant_family
          schema:
            type: string
        - description: Optional AI fetch resource type filter.
          in: query
          name: resource_type
          schema:
            enum:
              - html
              - document
              - image
              - other
            type: string
        - description: Optional exact fetched path filter.
          in: query
          name: path
          schema:
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/AIFetchSeriesPoint'
                type: array
          description: AI fetch timeseries
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get AI fetch timeseries
      tags:
        - Sites
  /api/sites/{id}/ask-ai:
    post:
      description: Runs the session-only, site-scoped dashboard assistant over read-only aggregate analytics tools. Requires a human dashboard session with site.view; API client bearer tokens and shared dashboard routes are rejected.
      parameters:
        - $ref: '#/components/parameters/siteID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AskAIRequest'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AskAIResponse'
          description: Ask AI response
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid Ask AI request
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Dashboard session required or forbidden
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AskAIStatus'
          description: Ask AI unavailable
        "429":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AskAIStatus'
          description: Ask AI budget exhausted
        "502":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Ask AI provider or validation failure
      security:
        - cookieAuth: []
      summary: Ask AI
      tags:
        - Sites
  /api/sites/{id}/ask-ai/events:
    post:
      description: Runs the same audited, session-only Ask AI workflow and streams safe Server-Sent Events for progress, answer deltas, and the final validated response. Requires a human dashboard session with site.view; API client bearer tokens and shared dashboard routes are rejected.
      parameters:
        - $ref: '#/components/parameters/siteID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AskAIRequest'
        required: true
      responses:
        "200":
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/AskAIStreamEvent'
          description: Ask AI Server-Sent Events stream
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid Ask AI request
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Dashboard session required or forbidden
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AskAIStatus'
          description: Ask AI unavailable
        "429":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AskAIStatus'
          description: Ask AI budget exhausted
      security:
        - cookieAuth: []
      summary: Stream Ask AI
      tags:
        - Sites
  /api/sites/{id}/ask-ai/history:
    get:
      description: Returns audit-safe Ask AI run summaries for the selected site. The response includes run metadata, hashes, usage, and validated summary counts only; raw prompts, full answers, provider payloads, headers, and credentials are not returned. Requires a human dashboard session with site.view; API client bearer tokens and shared dashboard routes are rejected.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - description: Maximum runs to return.
          in: query
          name: limit
          schema:
            default: 20
            maximum: 100
            minimum: 1
            type: integer
        - description: Number of runs to skip.
          in: query
          name: offset
          schema:
            default: 0
            minimum: 0
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AskAIHistoryResponse'
          description: Ask AI history
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid Ask AI history request
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Dashboard session required or forbidden
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AskAIStatus'
          description: Ask AI unavailable
      security:
        - cookieAuth: []
      summary: List Ask AI history
      tags:
        - Sites
  /api/sites/{id}/domain:
    put:
      description: Renames the tracked domain of a site while keeping its analytics history. Requires site.manage_data (site admin or higher). The tracker only matches hits for the new domain after the rename.
      parameters:
        - $ref: '#/components/parameters/siteID'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                domain:
                  description: Apex domain or subdomain without protocol, port, path, query, fragment, or www prefix (e.g. example.com or blog.example.com).
                  type: string
              required:
                - domain
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Site'
          description: Renamed site
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid domain
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Site not found
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Domain already in use by another site
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Rename site domain
      tags:
        - Sites
  /api/sites/{id}/ecommerce:
    get:
      description: Returns revenue, orders, average order value, checkout starts, checkout conversion, and aggregate city, provider, and ASN breakdowns for a site.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/filterType'
        - $ref: '#/components/parameters/filterValue'
        - $ref: '#/components/parameters/itemID'
        - $ref: '#/components/parameters/itemName'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EcommerceSummary'
          description: Ecommerce summary
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get ecommerce summary
      tags:
        - Sites
  /api/sites/{id}/ecommerce/products:
    get:
      description: Returns top products by revenue from purchase events.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/filterType'
        - $ref: '#/components/parameters/filterValue'
        - $ref: '#/components/parameters/itemID'
        - $ref: '#/components/parameters/itemName'
        - $ref: '#/components/parameters/limit'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/EcommerceProductStat'
                type: array
          description: Ecommerce products
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get top ecommerce products
      tags:
        - Sites
  /api/sites/{id}/ecommerce/sources:
    get:
      description: Returns revenue and order counts grouped by UTM source, medium, campaign, and referrer.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/filterType'
        - $ref: '#/components/parameters/filterValue'
        - $ref: '#/components/parameters/itemID'
        - $ref: '#/components/parameters/itemName'
        - $ref: '#/components/parameters/limit'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/EcommerceSourceStat'
                type: array
          description: Ecommerce sources
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get ecommerce sources
      tags:
        - Sites
  /api/sites/{id}/ecommerce/timeseries:
    get:
      description: Returns revenue and order counts over time for a site.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/filterType'
        - $ref: '#/components/parameters/filterValue'
        - $ref: '#/components/parameters/itemID'
        - $ref: '#/components/parameters/itemName'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/EcommerceSeriesPoint'
                type: array
          description: Ecommerce timeseries
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get ecommerce timeseries
      tags:
        - Sites
  /api/sites/{id}/events/audience:
    get:
      description: Returns top pages, referrers, devices, countries, cities, providers, and ASNs for sessions containing the selected event. Optional property filters and repeatable filter=type:value hit-dimension filters restrict the sessions included.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/eventName'
        - $ref: '#/components/parameters/eventPropertyKey'
        - $ref: '#/components/parameters/eventPropertyValue'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/eventDimensionKey'
        - $ref: '#/components/parameters/eventDimensionValue'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventAudience'
          description: Event audience
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get event audience
      tags:
        - Sites
  /api/sites/{id}/events/breakdown:
    get:
      description: Returns distinct property values for one event property key, ordered by session count.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/eventName'
        - $ref: '#/components/parameters/eventPropertyKeyRequired'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/MetricStat'
                type: array
          description: Event property breakdown
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get event property breakdown
      tags:
        - Sites
  /api/sites/{id}/events/names:
    get:
      description: Lists custom and automatic event names observed for a site in the selected date range.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  type: string
                type: array
          description: Event names
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: List event names
      tags:
        - Sites
  /api/sites/{id}/events/properties:
    get:
      description: Lists JSON property keys observed for an event name in the selected date range.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/eventName'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  type: string
                type: array
          description: Event property keys
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: List event property keys
      tags:
        - Sites
  /api/sites/{id}/events/timeseries:
    get:
      description: Returns event occurrence counts over time. Optional property filters and repeatable filter=type:value hit-dimension filters restrict the sessions counted.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/eventName'
        - $ref: '#/components/parameters/eventPropertyKey'
        - $ref: '#/components/parameters/eventPropertyValue'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/eventDimensionKey'
        - $ref: '#/components/parameters/eventDimensionValue'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/EventSeriesPoint'
                type: array
          description: Event timeseries
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get event timeseries
      tags:
        - Sites
  /api/sites/{id}/exclusions:
    get:
      description: Lists traffic exclusions owned by the site. Set effective=true to include inherited instance and current-team rules in instance, team, site order, newest-first within each scope. Inherited rows are read-only and omit cross-scope creator IDs. Requires site data-control permission or the narrow instance site-exclusion permission.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - description: Include inherited instance and current-team rules.
          in: query
          name: effective
          schema:
            default: false
            type: boolean
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/IPExclusion'
                type: array
          description: Site exclusions
      security:
        - cookieAuth: []
      summary: List site exclusions
      tags:
        - Sites
    post:
      description: Creates a forward-only CIDR, country, user-agent, or path exclusion for this site. Requires site data-control permission or the narrow instance site-exclusion permission.
      parameters:
        - $ref: '#/components/parameters/siteID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IPExclusionCreateRequest'
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IPExclusion'
          description: Created exclusion
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid exclusion rule
      security:
        - cookieAuth: []
      summary: Create site exclusion
      tags:
        - Sites
  /api/sites/{id}/exclusions/{ruleID}:
    delete:
      description: Deletes a rule owned by this site. Inherited instance or team rules returned by effective reads cannot be deleted through this route. Requires site data-control permission or the narrow instance site-exclusion permission.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/ruleID'
      responses:
        "204":
          description: Deleted
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
      security:
        - cookieAuth: []
      summary: Delete site exclusion
      tags:
        - Sites
  /api/sites/{id}/funnels:
    get:
      description: Lists funnels for a site.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/Funnel'
                type: array
          description: Funnels
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: List funnels
      tags:
        - Funnels
    post:
      description: Creates a multi-step funnel.
      parameters:
        - $ref: '#/components/parameters/siteID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Funnel'
        required: true
      responses:
        "201":
          description: Created
      security:
        - cookieAuth: []
      summary: Create funnel
      tags:
        - Funnels
  /api/sites/{id}/funnels/timeseries:
    get:
      description: Returns funnel entry/completion timeseries.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/funnelIDQuery'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/FunnelSeriesPoint'
                type: array
          description: Funnel timeseries
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Funnel timeseries
      tags:
        - Funnels
  /api/sites/{id}/funnels/{funnelID}:
    delete:
      description: Deletes funnel from site.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/funnelID'
      responses:
        "200":
          description: Deleted
      security:
        - cookieAuth: []
      summary: Delete funnel
      tags:
        - Funnels
    put:
      description: Updates a site-scoped funnel in place and invalidates its rollups.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/funnelID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Funnel'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Funnel'
          description: Updated funnel
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Funnel not found
      security:
        - cookieAuth: []
      summary: Update funnel
      tags:
        - Funnels
  /api/sites/{id}/funnels/{funnelID}/stats:
    get:
      description: Returns full funnel step stats.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/funnelID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FunnelStats'
          description: Funnel stats
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Funnel stats
      tags:
        - Funnels
  /api/sites/{id}/goals:
    get:
      description: Lists goals for a site.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/Goal'
                type: array
          description: Goals
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: List goals
      tags:
        - Goals
    post:
      description: Creates a conversion goal.
      parameters:
        - $ref: '#/components/parameters/siteID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Goal'
        required: true
      responses:
        "201":
          description: Created
      security:
        - cookieAuth: []
      summary: Create goal
      tags:
        - Goals
  /api/sites/{id}/goals/timeseries:
    get:
      description: Returns goal conversion timeseries.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/goalIDQuery'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/GoalSeriesPoint'
                type: array
          description: Goal timeseries
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Goal timeseries
      tags:
        - Goals
  /api/sites/{id}/goals/{goalID}:
    delete:
      description: Deletes goal from site.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/goalID'
      responses:
        "200":
          description: Deleted
      security:
        - cookieAuth: []
      summary: Delete goal
      tags:
        - Goals
    put:
      description: Updates a site-scoped goal in place and invalidates its rollups.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/goalID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Goal'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Goal'
          description: Updated goal
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Goal not found
      security:
        - cookieAuth: []
      summary: Update goal
      tags:
        - Goals
  /api/sites/{id}/hits:
    get:
      description: Returns paginated raw hits.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/query'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/order'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/filterType'
        - $ref: '#/components/parameters/filterValue'
        - $ref: '#/components/parameters/goalIDQuery'
        - $ref: '#/components/parameters/funnelIDQuery'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedHits'
          description: Paginated hits
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get site hits
      tags:
        - Sites
  /api/sites/{id}/hits/export:
    get:
      description: Exports filtered site hits in csv/xlsx/parquet/json/ndjson.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/query'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/filterType'
        - $ref: '#/components/parameters/filterValue'
        - $ref: '#/components/parameters/goalIDQuery'
        - $ref: '#/components/parameters/funnelIDQuery'
        - $ref: '#/components/parameters/format'
      responses:
        "200":
          description: Export file stream
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Export site hits
      tags:
        - Sites
  /api/sites/{id}/importers:
    get:
      description: Lists available historical data import providers for the selected site.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  additionalProperties: false
                  properties:
                    accepted_extensions:
                      items:
                        type: string
                      type: array
                    capabilities:
                      items:
                        type: string
                      type: array
                    key:
                      type: string
                    name:
                      type: string
                  required:
                    - key
                    - name
                    - accepted_extensions
                    - capabilities
                  type: object
                type: array
          description: Importers
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: List site importers
      tags:
        - Imports
  /api/sites/{id}/imports:
    get:
      description: Lists import jobs for the site.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  imports:
                    items:
                      $ref: '#/components/schemas/ImportJob'
                    type: array
                type: object
          description: Import list
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: List site imports
      tags:
        - Imports
  /api/sites/{id}/imports/uploads/{importID}/files/{fileID}/chunks:
    put:
      description: Uploads one raw file chunk at the requested byte offset.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - in: path
          name: importID
          required: true
          schema:
            format: uuid
            type: string
        - in: path
          name: fileID
          required: true
          schema:
            format: uuid
            type: string
        - in: query
          name: offset
          required: true
          schema:
            minimum: 0
            type: integer
      requestBody:
        content:
          application/octet-stream:
            schema:
              format: binary
              type: string
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
          description: Chunk progress
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Upload not accepting chunks
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Upload import chunk
      tags:
        - Imports
  /api/sites/{id}/imports/uploads/{importID}/validate:
    post:
      description: Scans staged files row by row and returns the validation manifest without committing analytics rows.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - in: path
          name: importID
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImportJob'
          description: Import job
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Validation failed
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Validate import upload
      tags:
        - Imports
  /api/sites/{id}/imports/{importID}:
    delete:
      description: Deletes imported aggregate rows for the selected import and marks its history row deleted.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - in: path
          name: importID
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Import still running
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Delete import
      tags:
        - Imports
    get:
      description: Returns import status, files, and validation manifest.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - in: path
          name: importID
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImportJob'
          description: Import job
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get import
      tags:
        - Imports
  /api/sites/{id}/imports/{importID}/start:
    post:
      description: Starts a previously validated import job in the background.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - in: path
          name: importID
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImportJob'
          description: Import job
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Import is not validated
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Start validated import
      tags:
        - Imports
  /api/sites/{id}/imports/{provider}/uploads:
    post:
      description: Creates a staged import upload session. Use chunk upload endpoints to send the declared ZIP or CSV files.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - in: path
          name: provider
          required: true
          schema:
            enum:
              - plausible
              - simpleanalytics
            type: string
      requestBody:
        content:
          application/json:
            schema:
              properties:
                files:
                  items:
                    properties:
                      filename:
                        type: string
                      sha256:
                        description: Optional lowercase or uppercase hex SHA-256 checksum for the staged file.
                        type: string
                      size_bytes:
                        type: integer
                    required:
                      - filename
                      - size_bytes
                    type: object
                  type: array
              required:
                - files
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
          description: Upload session
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid upload
        "413":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Import exceeds maximum staged size
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Create import upload
      tags:
        - Imports
  /api/sites/{id}/ingest/ai-fetch:
    post:
      description: Accepts a server-side AI crawler fetch record for a site. The user agent must match a known AI bot. Intended for edge or log-forwarded fetch analytics.
      parameters:
        - $ref: '#/components/parameters/siteID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AIFetchIngestPayload'
        required: true
      responses:
        "202":
          description: Accepted
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Record AI fetch
      tags:
        - Sites
  /api/sites/{id}/integrations/google-search-console:
    get:
      description: Returns the Search Console property mapping for a site.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  can_manage:
                    type: boolean
                  mapped:
                    type: boolean
                  mapped_at:
                    format: date-time
                    type: string
                  property_permission_level:
                    type: string
                  property_uri:
                    type: string
                  site_id:
                    format: uuid
                    type: string
                  sync_status:
                    properties:
                      imported_end_date:
                        format: date
                        type: string
                      imported_start_date:
                        format: date
                        type: string
                      last_attempt_at:
                        format: date-time
                        type: string
                      last_error_category:
                        type: string
                      last_success_at:
                        format: date-time
                        type: string
                      manual:
                        type: boolean
                      next_retry_at:
                        format: date-time
                        type: string
                      state:
                        enum:
                          - pending
                          - running
                          - succeeded
                          - failed
                          - needs_attention
                        type: string
                    required:
                      - state
                      - manual
                    type: object
                  team_id:
                    format: uuid
                    type: string
                required:
                  - site_id
                  - team_id
                  - mapped
                  - can_manage
                type: object
          description: Google Search Console site mapping
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
      security:
        - cookieAuth: []
      summary: Get site Search Console mapping
      tags:
        - Integrations
  /api/sites/{id}/integrations/google-search-console/property:
    delete:
      description: Removes the Search Console property mapping from a HitKeep site.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  can_manage:
                    type: boolean
                  mapped:
                    type: boolean
                  mapped_at:
                    format: date-time
                    type: string
                  property_permission_level:
                    type: string
                  property_uri:
                    type: string
                  site_id:
                    format: uuid
                    type: string
                  sync_status:
                    properties:
                      imported_end_date:
                        format: date
                        type: string
                      imported_start_date:
                        format: date
                        type: string
                      last_attempt_at:
                        format: date-time
                        type: string
                      last_error_category:
                        type: string
                      last_success_at:
                        format: date-time
                        type: string
                      manual:
                        type: boolean
                      next_retry_at:
                        format: date-time
                        type: string
                      state:
                        enum:
                          - pending
                          - running
                          - succeeded
                          - failed
                          - needs_attention
                        type: string
                    required:
                      - state
                      - manual
                    type: object
                  team_id:
                    format: uuid
                    type: string
                required:
                  - site_id
                  - team_id
                  - mapped
                  - can_manage
                type: object
          description: Google Search Console site mapping
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "412":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Google Search Console is not connected
      security:
        - cookieAuth: []
      summary: Unmap Search Console property
      tags:
        - Integrations
    put:
      description: Maps a visible Search Console property to a HitKeep site.
      parameters:
        - $ref: '#/components/parameters/siteID'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                property_uri:
                  type: string
              required:
                - property_uri
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  can_manage:
                    type: boolean
                  mapped:
                    type: boolean
                  mapped_at:
                    format: date-time
                    type: string
                  property_permission_level:
                    type: string
                  property_uri:
                    type: string
                  site_id:
                    format: uuid
                    type: string
                  sync_status:
                    properties:
                      imported_end_date:
                        format: date
                        type: string
                      imported_start_date:
                        format: date
                        type: string
                      last_attempt_at:
                        format: date-time
                        type: string
                      last_error_category:
                        type: string
                      last_success_at:
                        format: date-time
                        type: string
                      manual:
                        type: boolean
                      next_retry_at:
                        format: date-time
                        type: string
                      state:
                        enum:
                          - pending
                          - running
                          - succeeded
                          - failed
                          - needs_attention
                        type: string
                    required:
                      - state
                      - manual
                    type: object
                  team_id:
                    format: uuid
                    type: string
                required:
                  - site_id
                  - team_id
                  - mapped
                  - can_manage
                type: object
          description: Google Search Console site mapping
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid or unavailable property
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "412":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Google Search Console is not connected
      security:
        - cookieAuth: []
      summary: Map Search Console property
      tags:
        - Integrations
  /api/sites/{id}/integrations/google-search-console/sync:
    post:
      description: Queues a manual Search Console import for the mapped site and returns the current mapping sync status.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  can_manage:
                    type: boolean
                  mapped:
                    type: boolean
                  mapped_at:
                    format: date-time
                    type: string
                  property_permission_level:
                    type: string
                  property_uri:
                    type: string
                  site_id:
                    format: uuid
                    type: string
                  sync_status:
                    properties:
                      imported_end_date:
                        format: date
                        type: string
                      imported_start_date:
                        format: date
                        type: string
                      last_attempt_at:
                        format: date-time
                        type: string
                      last_error_category:
                        type: string
                      last_success_at:
                        format: date-time
                        type: string
                      manual:
                        type: boolean
                      next_retry_at:
                        format: date-time
                        type: string
                      state:
                        enum:
                          - pending
                          - running
                          - succeeded
                          - failed
                          - needs_attention
                        type: string
                    required:
                      - state
                      - manual
                    type: object
                  team_id:
                    format: uuid
                    type: string
                required:
                  - site_id
                  - team_id
                  - mapped
                  - can_manage
                type: object
          description: Google Search Console site mapping
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "412":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Google Search Console is not connected or the site is not mapped
      security:
        - cookieAuth: []
      summary: Request Search Console sync
      tags:
        - Integrations
  /api/sites/{id}/members:
    get:
      description: Lists site members and roles.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/SiteMember'
                type: array
          description: Members
      security:
        - cookieAuth: []
      summary: List site members
      tags:
        - Admin
    post:
      description: Adds member to site and optionally sends invite.
      parameters:
        - $ref: '#/components/parameters/siteID'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                email:
                  format: email
                  type: string
                role:
                  type: string
              required:
                - email
                - role
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
      security:
        - cookieAuth: []
      summary: Add site member
      tags:
        - Admin
  /api/sites/{id}/members/{userId}:
    delete:
      description: Removes a user from site membership.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/userID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
      security:
        - cookieAuth: []
      summary: Remove site member
      tags:
        - Admin
  /api/sites/{id}/opportunities:
    get:
      description: Lists saved opportunity recommendations for a site. Requires site.view and returns only validated customer-visible outputs with cited evidence.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OpportunityListResponse'
          description: Opportunity list
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: List opportunities
      tags:
        - Opportunities
  /api/sites/{id}/opportunities/digest-preview:
    get:
      description: Returns the safe daily or weekly digest payload that would be emailed for saved opportunity recommendations. Requires site.view and returns translation keys, placeholders, scores, and cited aggregate evidence only.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - in: query
          name: frequency
          required: false
          schema:
            default: weekly
            enum:
              - daily
              - weekly
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OpportunityDigestPreviewResponse'
          description: Opportunity digest preview
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Unsupported opportunity digest frequency
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Preview opportunity digest
      tags:
        - Opportunities
  /api/sites/{id}/opportunities/generate:
    post:
      description: Runs deterministic opportunity detectors for the selected range and, when AI is configured and budget is available, asks the provider only for structured copy from cited evidence. Requires site.manage_data.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OpportunityGenerateResponse'
          description: Generated opportunities
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Generate opportunities
      tags:
        - Opportunities
  /api/sites/{id}/opportunities/{opportunityID}:
    patch:
      description: Marks an opportunity saved, done, or dismissed. Requires site.manage_data.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - in: path
          name: opportunityID
          required: true
          schema:
            format: uuid
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OpportunityStatusUpdateRequest'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Opportunity'
          description: Opportunity
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Update opportunity status
      tags:
        - Opportunities
  /api/sites/{id}/qr-codes:
    get:
      description: Lists saved QR campaign definitions for a site. Requires site.view.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/QRCode'
                type: array
          description: QR codes
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: List QR codes
      tags:
        - QR Campaigns
    post:
      description: Creates a dynamic QR campaign definition. Requires site.manage_data.
      parameters:
        - $ref: '#/components/parameters/siteID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/QRCodeRequest'
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QRCode'
          description: Created QR code
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid QR request
      security:
        - cookieAuth: []
      summary: Create QR code
      tags:
        - QR Campaigns
  /api/sites/{id}/qr-codes/{qrID}:
    delete:
      description: Soft-deletes a QR code so historical analytics remain available and printed codes are not hard-deleted accidentally. Requires site.manage_data.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
      responses:
        "204":
          description: Archived
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
      security:
        - cookieAuth: []
      summary: Archive QR code
      tags:
        - QR Campaigns
    get:
      description: Returns one saved QR campaign definition. Requires site.view.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QRCode'
          description: QR code
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get QR code
      tags:
        - QR Campaigns
    patch:
      description: Updates the destination, campaign parameters, custom parameters, and style for a dynamic QR code after print. Requires site.manage_data.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/QRCodeRequest'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QRCode'
          description: QR code
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
      security:
        - cookieAuth: []
      summary: Update QR code
      tags:
        - QR Campaigns
  /api/sites/{id}/qr-codes/{qrID}/asset:
    delete:
      description: Removes the persisted graphic file and asset metadata from a QR code. Requires site.manage_data.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
      responses:
        "204":
          description: Deleted
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
      security:
        - cookieAuth: []
      summary: Delete QR graphic asset
      tags:
        - QR Campaigns
    get:
      description: Returns the filesystem-backed raster graphic attached to a QR code. Public JSON exposes asset metadata only; storage paths are never returned. Requires site.view.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
      responses:
        "200":
          description: Image asset stream
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Asset not found
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get QR graphic asset
      tags:
        - QR Campaigns
    put:
      description: Stores a PNG, JPEG, or WebP graphic for a QR code under the configured HitKeep data directory and persists only metadata in the control-plane database. Maximum size is 2 MB. Requires site.manage_data.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
      requestBody:
        content:
          multipart/form-data:
            schema:
              properties:
                asset:
                  format: binary
                  type: string
              required:
                - asset
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QRCodeAsset'
          description: QR asset
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid asset
        "413":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Asset too large
      security:
        - cookieAuth: []
      summary: Upload QR graphic asset
      tags:
        - QR Campaigns
  /api/sites/{id}/qr-codes/{qrID}/opens/timeseries:
    get:
      description: Returns QR redirect opens over time for one QR code. Requires site.view.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/QRCodeOpenSeriesPoint'
                type: array
          description: QR open series
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get QR open timeseries
      tags:
        - QR Campaigns
  /api/sites/{id}/qr-codes/{qrID}/share:
    get:
      description: Lists QR-only share links for one QR code. Requires site.view.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/QRCodeShareLink'
                type: array
          description: QR share links
      security:
        - cookieAuth: []
      summary: List QR share links
      tags:
        - QR Campaigns
    post:
      description: Creates a read-only QR-only share link exposing one QR code's stats and export. Requires site.manage_data.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QRCodeShareLink'
          description: QR share link
      security:
        - cookieAuth: []
      summary: Create QR share link
      tags:
        - QR Campaigns
  /api/sites/{id}/qr-codes/{qrID}/share/{shareID}:
    delete:
      description: Revokes a QR-only share link. Requires site.manage_data.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
        - $ref: '#/components/parameters/shareID'
      responses:
        "204":
          description: Deleted
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
      security:
        - cookieAuth: []
      summary: Delete QR share link
      tags:
        - QR Campaigns
  /api/sites/{id}/qr-codes/{qrID}/summary:
    get:
      description: Returns redirect opens plus QR-scoped pageviews, visitors, pages, referrers, devices, and countries for one QR code. Requires site.view.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QRCodeSummary'
          description: QR summary
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get QR analytics summary
      tags:
        - QR Campaigns
  /api/sites/{id}/qr-codes/{qrID}/takeout:
    get:
      description: Exports one QR code definition, asset metadata, QR opens, and QR-filtered analytics rows. Requires site.view.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/qrID'
        - $ref: '#/components/parameters/format'
      responses:
        "200":
          description: Export file stream
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Export QR takeout
      tags:
        - QR Campaigns
  /api/sites/{id}/realtime:
    get:
      description: Streams privacy-safe site-scoped analytics invalidation events using server-sent events. Requires site.view.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          description: Server-sent event stream with analytics.changed and analytics.resync events
      security:
        - cookieAuth: []
      summary: Stream site realtime changes
      tags:
        - Sites
      x-internal: true
  /api/sites/{id}/retention:
    put:
      description: Updates per-site retention days.
      parameters:
        - $ref: '#/components/parameters/siteID'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                days:
                  minimum: 0
                  type: integer
              required:
                - days
              type: object
        required: true
      responses:
        "200":
          description: Updated
      security:
        - cookieAuth: []
      summary: Update retention policy
      tags:
        - Sites
  /api/sites/{id}/search-console/breakdowns:
    get:
      description: Returns Search Console rows grouped by country or device for a mapped site.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - description: Exact Search Console page URL filter.
          in: query
          name: page
          schema:
            type: string
        - description: HitKeep path filter matched against Search Console page URLs.
          in: query
          name: path
          schema:
            type: string
        - description: Search Console country code filter.
          in: query
          name: country
          schema:
            type: string
        - description: Search Console device filter.
          in: query
          name: device
          schema:
            type: string
        - description: Maximum rows for table endpoints. Between 1 and 100.
          in: query
          name: limit
          schema:
            default: 10
            maximum: 100
            minimum: 1
            type: integer
        - description: Breakdown dimension.
          in: query
          name: dimension
          required: true
          schema:
            enum:
              - country
              - device
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchConsoleDimensionResponse'
          description: Search Console report
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "401":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Unauthorized
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "412":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Search Console property is not mapped
        "429":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Too many requests
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Service not available
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get Search Console country or device breakdown
      tags:
        - Reports
  /api/sites/{id}/search-console/overview:
    get:
      description: Returns aggregate Google Search Console clicks, impressions, CTR, and average position for a mapped site.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - description: Exact Search Console page URL filter.
          in: query
          name: page
          schema:
            type: string
        - description: HitKeep path filter matched against Search Console page URLs.
          in: query
          name: path
          schema:
            type: string
        - description: Search Console country code filter.
          in: query
          name: country
          schema:
            type: string
        - description: Search Console device filter.
          in: query
          name: device
          schema:
            type: string
        - description: Maximum rows for table endpoints. Between 1 and 100.
          in: query
          name: limit
          schema:
            default: 10
            maximum: 100
            minimum: 1
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchConsoleOverview'
          description: Search Console report
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "401":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Unauthorized
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "412":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Search Console property is not mapped
        "429":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Too many requests
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Service not available
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get Search Console overview
      tags:
        - Reports
  /api/sites/{id}/search-console/pages:
    get:
      description: Returns top Search Console page rows for a mapped site.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - description: Exact Search Console page URL filter.
          in: query
          name: page
          schema:
            type: string
        - description: HitKeep path filter matched against Search Console page URLs.
          in: query
          name: path
          schema:
            type: string
        - description: Search Console country code filter.
          in: query
          name: country
          schema:
            type: string
        - description: Search Console device filter.
          in: query
          name: device
          schema:
            type: string
        - description: Maximum rows for table endpoints. Between 1 and 100.
          in: query
          name: limit
          schema:
            default: 10
            maximum: 100
            minimum: 1
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchConsoleDimensionResponse'
          description: Search Console report
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "401":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Unauthorized
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "412":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Search Console property is not mapped
        "429":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Too many requests
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Service not available
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get top Search Console pages
      tags:
        - Reports
  /api/sites/{id}/search-console/queries:
    get:
      description: Returns top Search Console query rows for a mapped site. Query values are imported aggregate Google data and are not session filters.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - description: Exact Search Console page URL filter.
          in: query
          name: page
          schema:
            type: string
        - description: HitKeep path filter matched against Search Console page URLs.
          in: query
          name: path
          schema:
            type: string
        - description: Search Console country code filter.
          in: query
          name: country
          schema:
            type: string
        - description: Search Console device filter.
          in: query
          name: device
          schema:
            type: string
        - description: Maximum rows for table endpoints. Between 1 and 100.
          in: query
          name: limit
          schema:
            default: 10
            maximum: 100
            minimum: 1
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchConsoleDimensionResponse'
          description: Search Console report
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "401":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Unauthorized
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "412":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Search Console property is not mapped
        "429":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Too many requests
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Service not available
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get top Search Console queries
      tags:
        - Reports
  /api/sites/{id}/search-console/series:
    get:
      description: Returns daily Google Search Console metrics for a mapped site.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - description: Exact Search Console page URL filter.
          in: query
          name: page
          schema:
            type: string
        - description: HitKeep path filter matched against Search Console page URLs.
          in: query
          name: path
          schema:
            type: string
        - description: Search Console country code filter.
          in: query
          name: country
          schema:
            type: string
        - description: Search Console device filter.
          in: query
          name: device
          schema:
            type: string
        - description: Maximum rows for table endpoints. Between 1 and 100.
          in: query
          name: limit
          schema:
            default: 10
            maximum: 100
            minimum: 1
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchConsoleSeriesResponse'
          description: Search Console report
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "401":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Unauthorized
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "412":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Search Console property is not mapped
        "429":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Too many requests
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Service not available
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get Search Console series
      tags:
        - Reports
  /api/sites/{id}/setup-state:
    get:
      description: Reports whether a site has ever recorded AI fetches, chatbot events, custom events, ecommerce events, or web vitals so the dashboard can show setup guidance instead of empty reports. Requires site.view.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SiteSetupState'
          description: Site setup state
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
      security:
        - cookieAuth: []
      summary: Get setup state
      tags:
        - Sites
  /api/sites/{id}/share:
    get:
      description: Lists share links for site.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/ShareLink'
                type: array
          description: Share links
      security:
        - cookieAuth: []
      summary: List share links
      tags:
        - Share
    post:
      description: Creates new read-only share token URL.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  created_at:
                    format: date-time
                    type: string
                  id:
                    format: uuid
                    type: string
                  token:
                    type: string
                  token_hint:
                    type: string
                  url:
                    type: string
                type: object
          description: Share link created
      security:
        - cookieAuth: []
      summary: Create share link
      tags:
        - Share
  /api/sites/{id}/share/{shareID}:
    delete:
      description: Revokes a share link.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/shareID'
      responses:
        "204":
          description: Deleted
      security:
        - cookieAuth: []
      summary: Delete share link
      tags:
        - Share
  /api/sites/{id}/stats:
    get:
      description: Aggregated site metrics and charts.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/filterType'
        - $ref: '#/components/parameters/filterValue'
        - $ref: '#/components/parameters/goalIDQuery'
        - $ref: '#/components/parameters/funnelIDQuery'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SiteStats'
          description: Site stats
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get site stats
      tags:
        - Sites
  /api/sites/{id}/stats/reset:
    post:
      description: Irreversibly clears currently stored measured stats for a site while preserving the site configuration. Requires a human dashboard session with site.delete; API client bearer tokens are rejected.
      parameters:
        - $ref: '#/components/parameters/siteID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SiteStatsResetRequest'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SiteStatsResetResponse'
          description: Reset summary
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request or confirmation mismatch
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Dashboard session required or forbidden
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Site not found
        "500":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Reset failed
      security:
        - cookieAuth: []
      summary: Reset site stats
      tags:
        - Sites
  /api/sites/{id}/takeout:
    get:
      description: Exports site data as xlsx/csv/parquet/json/ndjson.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/format'
      responses:
        "200":
          description: Export file stream
      security:
        - cookieAuth: []
      summary: Site takeout
      tags:
        - Takeout
  /api/sites/{id}/tracking-domain-options:
    get:
      description: Returns the default tracker URL and team custom tracking domains available to the site. Requires site.view.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SiteTrackingDomainOptions'
          description: Tracking domain options
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Site not found
      security:
        - cookieAuth: []
      summary: List tracking domain options
      tags:
        - Sites
  /api/sites/{id}/tracking/status:
    get:
      description: Returns privacy-safe operational tracking metadata for a site, including live/waiting/dormant/domain-mismatch status and the latest accepted tracker source.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SiteTrackingStatus'
          description: Tracking status
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Site not found
      security:
        - cookieAuth: []
      summary: Get tracking status
      tags:
        - Sites
  /api/sites/{id}/transfer-team:
    post:
      description: Moves a site into another team the caller can administer and migrates analytics data to the destination tenant store.
      parameters:
        - $ref: '#/components/parameters/siteID'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                team_id:
                  format: uuid
                  type: string
              required:
                - team_id
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SiteTransferResponse'
          description: Site transferred
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
      security:
        - cookieAuth: []
      summary: Transfer site to another team
      tags:
        - Sites
  /api/sites/{id}/web-vitals/breakdown:
    get:
      description: Returns Web Vitals p75 and rating counts for one metric grouped by browser, country, language, device, city, provider, or ASN using the matching pageview context.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/webVitalPath'
        - $ref: '#/components/parameters/webVitalRating'
        - $ref: '#/components/parameters/webVitalMetric'
          required: true
        - description: Visitor context dimension.
          in: query
          name: dimension
          required: true
          schema:
            enum:
              - browser
              - country
              - language
              - device
              - city
              - provider
              - asn
            type: string
        - $ref: '#/components/parameters/limit'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/WebVitalDimensionRow'
                type: array
          description: Web Vitals breakdown
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get Web Vitals visitor context breakdown
      tags:
        - Sites
  /api/sites/{id}/web-vitals/pages:
    get:
      description: Returns page paths ranked by the selected metric p75, with per-metric p75 cells for every Web Vital available on each path.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/webVitalPath'
        - $ref: '#/components/parameters/webVitalRating'
        - $ref: '#/components/parameters/webVitalMetric'
          required: true
        - $ref: '#/components/parameters/limit'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/WebVitalPageRow'
                type: array
          description: Web Vitals pages
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get Web Vitals page breakdown
      tags:
        - Sites
  /api/sites/{id}/web-vitals/summary:
    get:
      description: Returns p75, sample count, rating counts, and derived p75 rating for each Web Vital metric.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/webVitalPath'
        - $ref: '#/components/parameters/webVitalRating'
        - $ref: '#/components/parameters/webVitalMetric'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/WebVitalSummaryMetric'
                type: array
          description: Web Vitals summary
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get Web Vitals summary
      tags:
        - Sites
  /api/sites/{id}/web-vitals/timeseries:
    get:
      description: Returns p75 and rating counts bucketed by hour or day for one Web Vital metric.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - $ref: '#/components/parameters/from'
        - $ref: '#/components/parameters/to'
        - $ref: '#/components/parameters/webVitalPath'
        - $ref: '#/components/parameters/webVitalRating'
        - $ref: '#/components/parameters/webVitalMetric'
          required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/WebVitalSeriesPoint'
                type: array
          description: Web Vitals timeseries
      security:
        - cookieAuth: []
        - bearerAuth: []
        - apiKeyAuth: []
      summary: Get Web Vitals timeseries
      tags:
        - Sites
  /api/sites/{id}/webhooks:
    get:
      description: Human dashboard session only.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/Webhook'
                type: array
          description: Webhook list
      security:
        - cookieAuth: []
      summary: List site webhooks
      tags:
        - Webhooks
    post:
      description: Creates a webhook and returns its signing secret exactly once. Human dashboard session only.
      parameters:
        - $ref: '#/components/parameters/siteID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookInput'
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookSecretResponse'
          description: Created webhook and one-time secret
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid event or destination
      security:
        - cookieAuth: []
      summary: Create site webhook
      tags:
        - Webhooks
  /api/sites/{id}/webhooks/events:
    get:
      description: Lists event types valid for this scope.
      parameters:
        - $ref: '#/components/parameters/siteID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/WebhookEventDescriptor'
                type: array
          description: Event catalog
      security:
        - cookieAuth: []
      summary: List site webhook events
      tags:
        - Webhooks
  /api/sites/{id}/webhooks/{webhookID}:
    delete:
      description: Deletes configuration. Existing delivery outcome records remain subject to retention.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - in: path
          name: webhookID
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "204":
          description: Deleted
      security:
        - cookieAuth: []
      summary: Delete site webhook
      tags:
        - Webhooks
    put:
      description: Updates configuration without changing the signing secret.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - in: path
          name: webhookID
          required: true
          schema:
            format: uuid
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookInput'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Webhook'
          description: Updated webhook
      security:
        - cookieAuth: []
      summary: Update site webhook
      tags:
        - Webhooks
  /api/sites/{id}/webhooks/{webhookID}/deliveries:
    get:
      description: Deliveries use the WebhookEventPayload schema with at-least-once, unordered semantics. Verify X-HitKeep-Signature (v1=<hex HMAC-SHA256>) over timestamp + "." + body with the one-time secret, reject stale X-HitKeep-Timestamp values, and deduplicate by X-HitKeep-Event-ID or X-HitKeep-Delivery-ID. Response bodies and signing secrets are never returned.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - in: path
          name: webhookID
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/WebhookDelivery'
                type: array
          description: Delivery outcomes
      security:
        - cookieAuth: []
      summary: List site webhook delivery outcomes
      tags:
        - Webhooks
  /api/sites/{id}/webhooks/{webhookID}/rotate:
    post:
      description: Re-signs pending deliveries, replaces the configured secret, and returns the new value exactly once. A request already in flight may still use the previous secret.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - in: path
          name: webhookID
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookSecretResponse'
          description: Webhook and new one-time secret
      security:
        - cookieAuth: []
      summary: Rotate site webhook signing secret
      tags:
        - Webhooks
  /api/sites/{id}/webhooks/{webhookID}/test:
    post:
      description: Deliveries use the WebhookEventPayload schema with at-least-once, unordered semantics. Verify X-HitKeep-Signature (v1=<hex HMAC-SHA256>) over timestamp + "." + body with the one-time secret, reject stale X-HitKeep-Timestamp values, and deduplicate by X-HitKeep-Event-ID or X-HitKeep-Delivery-ID.
      parameters:
        - $ref: '#/components/parameters/siteID'
        - in: path
          name: webhookID
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "202":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookTestResponse'
          description: Queued test delivery
      security:
        - cookieAuth: []
      summary: Queue site webhook test
      tags:
        - Webhooks
  /api/status:
    get:
      description: Setup and version status, plus optional managed-cloud metadata.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  ask_ai:
                    $ref: '#/components/schemas/AskAIStatus'
                  cloud:
                    $ref: '#/components/schemas/CloudStatus'
                  needs_setup:
                    type: boolean
                  version:
                    type: string
                type: object
          description: Status payload
      summary: Instance status
      tags:
        - System
  /api/user/api-clients:
    get:
      description: Lists API clients for authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/APIClient'
                type: array
          description: API clients
      security:
        - cookieAuth: []
      summary: List API clients
      tags:
        - User
    post:
      description: Creates delegated API client and returns one-time token. Site analytics, MCP, and ingest access require explicit site_roles grants.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                description:
                  type: string
                expires_at:
                  format: date-time
                  type: string
                instance_role:
                  type: string
                name:
                  type: string
                site_roles:
                  items:
                    properties:
                      role:
                        type: string
                      site_id:
                        format: uuid
                        type: string
                    type: object
                  type: array
              required:
                - name
              type: object
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIClientCreateResponse'
          description: Created API client
      security:
        - cookieAuth: []
      summary: Create API client
      tags:
        - User
  /api/user/api-clients/{id}:
    delete:
      description: Deletes delegated API client.
      parameters:
        - $ref: '#/components/parameters/apiClientID'
      responses:
        "204":
          description: Deleted
      security:
        - cookieAuth: []
      summary: Delete API client
      tags:
        - User
    put:
      description: Updates delegated API client.
      parameters:
        - $ref: '#/components/parameters/apiClientID'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                description:
                  type: string
                expires_at:
                  format: date-time
                  type: string
                instance_role:
                  type: string
                name:
                  type: string
                revoked:
                  type: boolean
                site_roles:
                  items:
                    $ref: '#/components/schemas/APIClientSiteRole'
                  type: array
              required:
                - name
                - instance_role
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIClient'
          description: Updated API client
      security:
        - cookieAuth: []
      summary: Update API client
      tags:
        - User
  /api/user/api-clients/{id}/rotate:
    post:
      description: Generates a new one-time token for an active delegated API client and immediately invalidates the previous token.
      parameters:
        - $ref: '#/components/parameters/apiClientID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIClientCreateResponse'
          description: Rolled API client token
        "409":
          description: API client is revoked or expired
      security:
        - cookieAuth: []
      summary: Roll API client token
      tags:
        - User
  /api/user/avatar:
    get:
      description: Proxies authenticated user's avatar image.
      parameters:
        - $ref: '#/components/parameters/avatarSize'
      responses:
        "200":
          description: Avatar image
      security:
        - cookieAuth: []
      summary: Get avatar
      tags:
        - User
  /api/user/bootstrap:
    get:
      description: Returns authenticated dashboard startup context in one response.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserBootstrap'
          description: Dashboard bootstrap
        "401":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Unauthorized
      security:
        - cookieAuth: []
      summary: Get dashboard bootstrap
      tags:
        - User
      x-internal: true
  /api/user/current-ip:
    get:
      description: Returns the resolved client IP and single-host CIDR for quick exclusion setup.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  cidr:
                    type: string
                  ip:
                    type: string
                required:
                  - ip
                  - cidr
                type: object
          description: Current IP
      security:
        - cookieAuth: []
      summary: Get current IP
      tags:
        - User
  /api/user/onboarding:
    get:
      description: Returns an onboarding checklist computed from sites, tracking status, team membership, and scheduled report state.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserOnboarding'
          description: Onboarding state
      security:
        - cookieAuth: []
      summary: Get onboarding checklist
      tags:
        - User
  /api/user/onboarding/dismiss:
    post:
      description: Persists the authenticated user's onboarding dismissal timestamp.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
      security:
        - cookieAuth: []
      summary: Dismiss onboarding checklist
      tags:
        - User
  /api/user/password:
    post:
      description: Changes password for authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                current_password:
                  type: string
                new_password:
                  minLength: 8
                  type: string
              required:
                - current_password
                - new_password
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Current password is incorrect
      security:
        - cookieAuth: []
      summary: Change password
      tags:
        - Auth
  /api/user/permissions:
    get:
      description: Returns authenticated user's roles, raw permissions, and derived access capabilities.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PermissionContext'
          description: Permission context
      security:
        - cookieAuth: []
      summary: Get permission context
      tags:
        - Permissions
  /api/user/preferences:
    get:
      description: Returns authenticated user preferences.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserPreferences'
          description: Preferences
      security:
        - cookieAuth: []
      summary: Get user preferences
      tags:
        - User
    put:
      description: Updates authenticated user preferences.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserPreferences'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserPreferences'
          description: Preferences
      security:
        - cookieAuth: []
      summary: Update user preferences
      tags:
        - User
  /api/user/profile:
    get:
      description: Returns authenticated user profile.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserProfile'
          description: User profile
      security:
        - cookieAuth: []
      summary: Get profile
      tags:
        - User
    put:
      description: Updates authenticated user profile details.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                email:
                  format: email
                  type: string
                given_name:
                  type: string
                last_name:
                  type: string
              required:
                - email
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserProfile'
          description: Updated profile
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: User not found
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Email already exists
      security:
        - cookieAuth: []
      summary: Update profile
      tags:
        - User
  /api/user/security:
    get:
      description: Returns TOTP/passkey status.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserSecurityStatus'
          description: Security status
      security:
        - cookieAuth: []
      summary: Get user security status
      tags:
        - User
  /api/user/security/passkeys/register/finish:
    post:
      description: Verifies passkey attestation and stores credential.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                id:
                  type: string
                rawId:
                  type: string
                response:
                  additionalProperties: true
                  type: object
                type:
                  type: string
              required:
                - id
                - type
                - rawId
                - response
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserSecurityStatus'
          description: Security status
      security:
        - cookieAuth: []
      summary: Finish passkey registration
      tags:
        - User
  /api/user/security/passkeys/register/start:
    post:
      description: Creates passkey registration challenge and options.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                name:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  publicKey:
                    additionalProperties: true
                    type: object
                type: object
          description: Passkey creation options
      security:
        - cookieAuth: []
      summary: Start passkey registration
      tags:
        - User
  /api/user/security/passkeys/{id}:
    delete:
      description: Deletes a registered passkey only when another passkey, social provider, or enabled password remains usable.
      parameters:
        - $ref: '#/components/parameters/passkeyID'
      responses:
        "204":
          description: Passkey deleted
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Passkey not found
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Passkey is the last usable primary login method
      security:
        - cookieAuth: []
      summary: Delete passkey
      tags:
        - User
  /api/user/security/recovery-codes/regenerate:
    post:
      description: Generates a new one-time set of backup recovery codes for the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserRecoveryCodesResponse'
          description: Recovery codes
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Enable TOTP or register a passkey before generating recovery codes
      security:
        - cookieAuth: []
      summary: Regenerate recovery codes
      tags:
        - User
  /api/user/security/social/{provider}:
    delete:
      description: Unlinks a provider only when another social provider, passkey, or recently confirmed enabled password remains usable. TOTP, recovery codes, email MFA, and team SSO do not satisfy this guard.
      parameters:
        - in: path
          name: provider
          required: true
          schema:
            enum:
              - google
              - github
              - microsoft
            type: string
      requestBody:
        content:
          application/json:
            schema:
              properties:
                current_password:
                  type: string
                  writeOnly: true
              type: object
        required: true
      responses:
        "204":
          description: Provider unlinked
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Identity not linked
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: No alternative primary login method
      security:
        - cookieAuth: []
      summary: Unlink social provider
      tags:
        - User
  /api/user/security/social/{provider}/start:
    post:
      description: Starts a provider authorization bound to the authenticated HitKeep account.
      parameters:
        - in: path
          name: provider
          required: true
          schema:
            enum:
              - google
              - github
              - microsoft
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SSOStartResponse'
          description: Provider authorization URL
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Provider unavailable
      security:
        - cookieAuth: []
      summary: Start authenticated social link
      tags:
        - User
  /api/user/security/totp/disable:
    post:
      description: Disables TOTP after current code verification.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                code:
                  type: string
              required:
                - code
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserSecurityStatus'
          description: Security status
      security:
        - cookieAuth: []
      summary: Disable TOTP
      tags:
        - User
  /api/user/security/totp/setup/start:
    post:
      description: Starts TOTP enrollment and returns secret + OTPAuth URI.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserTOTPSetup'
          description: TOTP setup
      security:
        - cookieAuth: []
      summary: Start TOTP setup
      tags:
        - User
  /api/user/security/totp/setup/verify:
    post:
      description: Verifies TOTP code and enables TOTP.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                code:
                  type: string
              required:
                - code
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserSecurityStatus'
          description: Security status
      security:
        - cookieAuth: []
      summary: Verify TOTP setup
      tags:
        - User
  /api/user/takeout:
    get:
      description: Exports all user data across sites as xlsx/csv/parquet/json/ndjson.
      parameters:
        - $ref: '#/components/parameters/format'
      responses:
        "200":
          description: Export file stream
      security:
        - cookieAuth: []
      summary: User takeout
      tags:
        - Takeout
  /api/user/teams:
    get:
      description: Returns all teams for the authenticated user and the current active team.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamListResponse'
          description: Team list
      security:
        - cookieAuth: []
      summary: List teams
      tags:
        - Teams
    post:
      description: Creates a new team and returns the created team payload.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                logo_url:
                  type: string
                name:
                  type: string
              required:
                - name
              type: object
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamCreateResponse'
          description: Created team
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Team limit reached
      security:
        - cookieAuth: []
      summary: Create team
      tags:
        - Teams
  /api/user/teams/active:
    put:
      description: Sets the current active team context for the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                team_id:
                  format: uuid
                  type: string
              required:
                - team_id
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamActiveResponse'
          description: Active team response
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
      security:
        - cookieAuth: []
      summary: Set active team
      tags:
        - Teams
  /api/user/teams/{id}:
    patch:
      description: Updates team settings. This is the canonical update route.
      parameters:
        - $ref: '#/components/parameters/teamID'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                logo_url:
                  type: string
                name:
                  type: string
              required:
                - name
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
      security:
        - cookieAuth: []
      summary: Update team
      tags:
        - Teams
    put:
      description: Deprecated compatibility alias for team updates. Use PATCH /api/user/teams/{id}.
      parameters:
        - $ref: '#/components/parameters/teamID'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                logo_url:
                  type: string
                name:
                  type: string
              required:
                - name
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
      security:
        - cookieAuth: []
      summary: Update team (deprecated)
      tags:
        - Teams
  /api/user/teams/{id}/api-clients:
    get:
      description: Lists team-owned API clients for a team owner or admin.
      parameters:
        - $ref: '#/components/parameters/teamID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/APIClient'
                type: array
          description: Team API clients
      security:
        - cookieAuth: []
      summary: List team API clients
      tags:
        - Teams
    post:
      description: Creates a team-owned API client and returns a one-time token. Team-owned clients can access only delegated site_roles grants within the team.
      parameters:
        - $ref: '#/components/parameters/teamID'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                description:
                  type: string
                expires_at:
                  format: date-time
                  type: string
                name:
                  type: string
                site_roles:
                  items:
                    $ref: '#/components/schemas/APIClientSiteRole'
                  type: array
              required:
                - name
              type: object
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIClientCreateResponse'
          description: Created team API client
      security:
        - cookieAuth: []
      summary: Create team API client
      tags:
        - Teams
  /api/user/teams/{id}/api-clients/{clientId}:
    delete:
      description: Deletes a team-owned API client.
      parameters:
        - $ref: '#/components/parameters/teamID'
        - $ref: '#/components/parameters/teamAPIClientID'
      responses:
        "204":
          description: Deleted
      security:
        - cookieAuth: []
      summary: Delete team API client
      tags:
        - Teams
    put:
      description: Updates a team-owned API client.
      parameters:
        - $ref: '#/components/parameters/teamID'
        - $ref: '#/components/parameters/teamAPIClientID'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                description:
                  type: string
                expires_at:
                  format: date-time
                  type: string
                name:
                  type: string
                revoked:
                  type: boolean
                site_roles:
                  items:
                    $ref: '#/components/schemas/APIClientSiteRole'
                  type: array
              required:
                - name
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIClient'
          description: Updated team API client
      security:
        - cookieAuth: []
      summary: Update team API client
      tags:
        - Teams
  /api/user/teams/{id}/api-clients/{clientId}/rotate:
    post:
      description: Generates a new one-time token for an active team-owned API client and immediately invalidates the previous token.
      parameters:
        - $ref: '#/components/parameters/teamID'
        - $ref: '#/components/parameters/teamAPIClientID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIClientCreateResponse'
          description: Rolled team API client token
        "409":
          description: API client is revoked or expired
      security:
        - cookieAuth: []
      summary: Roll team API client token
      tags:
        - Teams
  /api/user/teams/{id}/archive:
    post:
      description: Archives a non-default team after all sites have been transferred or removed.
      parameters:
        - $ref: '#/components/parameters/teamID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamArchiveResponse'
          description: Archive team response
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Cannot archive the default team or a team that still owns sites
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Only owners can archive teams
      security:
        - cookieAuth: []
      summary: Archive team
      tags:
        - Teams
  /api/user/teams/{id}/audit:
    get:
      description: Lists recent audit events for team management actions.
      parameters:
        - $ref: '#/components/parameters/teamID'
        - description: Optional exact audit action to filter by.
          in: query
          name: action
          schema:
            type: string
        - description: Optional exact audit outcome to filter by.
          in: query
          name: outcome
          schema:
            type: string
        - description: Optional exact audit target type to filter by.
          in: query
          name: target_type
          schema:
            type: string
        - description: Searches actor, target, details, IP, country, and request ID fields.
          in: query
          name: query
          schema:
            type: string
        - description: Inclusive RFC3339 start timestamp.
          in: query
          name: from
          schema:
            format: date-time
            type: string
        - description: Inclusive RFC3339 end timestamp.
          in: query
          name: to
          schema:
            format: date-time
            type: string
        - description: Maximum number of audit rows to return (default 25, max 200).
          in: query
          name: limit
          schema:
            maximum: 200
            minimum: 1
            type: integer
        - description: Zero-based audit row offset for pagination.
          in: query
          name: offset
          schema:
            minimum: 0
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamAuditListResponse'
          description: Team audit entries
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid query parameters
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
      security:
        - cookieAuth: []
      summary: List team audit log
      tags:
        - Teams
  /api/user/teams/{id}/exclusions:
    get:
      description: Lists traffic exclusions owned by the team. Set effective=true to include inherited instance rules before team rules, newest-first within each scope. Inherited rows are read-only and omit cross-scope creator IDs. Requires team.manage_settings.
      parameters:
        - $ref: '#/components/parameters/teamID'
        - description: Include inherited instance rules.
          in: query
          name: effective
          schema:
            default: false
            type: boolean
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/IPExclusion'
                type: array
          description: Team traffic exclusions
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
      security:
        - cookieAuth: []
      summary: List team traffic exclusions
      tags:
        - Teams
    post:
      description: Creates a forward-only CIDR, country, user-agent, or path exclusion for every site currently owned by the team. Requires team.manage_settings.
      parameters:
        - $ref: '#/components/parameters/teamID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IPExclusionCreateRequest'
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IPExclusion'
          description: Created team exclusion
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid exclusion rule
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
      security:
        - cookieAuth: []
      summary: Create team traffic exclusion
      tags:
        - Teams
  /api/user/teams/{id}/exclusions/{ruleID}:
    delete:
      description: Deletes a rule owned by this team. Instance rules returned by effective reads cannot be deleted through this route. Requires team.manage_settings.
      parameters:
        - $ref: '#/components/parameters/teamID'
        - $ref: '#/components/parameters/ruleID'
      responses:
        "204":
          description: Deleted
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Not found
      security:
        - cookieAuth: []
      summary: Delete team traffic exclusion
      tags:
        - Teams
  /api/user/teams/{id}/integrations/google-search-console:
    delete:
      description: Disconnects the team-level Google Search Console connection and clears stored token material.
      parameters:
        - $ref: '#/components/parameters/teamID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
      security:
        - cookieAuth: []
      summary: Disconnect Google Search Console
      tags:
        - Integrations
  /api/user/teams/{id}/integrations/google-search-console/connect:
    post:
      description: Creates a state-bound read-only Google Search Console OAuth URL for the team.
      parameters:
        - $ref: '#/components/parameters/teamID'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                return_path:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  auth_url:
                    format: uri
                    type: string
                required:
                  - auth_url
                type: object
          description: Google Search Console OAuth URL
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "412":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Credentials missing
      security:
        - cookieAuth: []
      summary: Start Google Search Console OAuth
      tags:
        - Integrations
  /api/user/teams/{id}/integrations/google-search-console/properties:
    get:
      description: Lists Search Console properties visible to the connected team account and caches non-secret property metadata.
      parameters:
        - $ref: '#/components/parameters/teamID'
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  properties:
                    items:
                      properties:
                        permission_level:
                          type: string
                        uri:
                          type: string
                      required:
                        - uri
                        - permission_level
                      type: object
                    type: array
                required:
                  - properties
                type: object
          description: Google Search Console properties
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "412":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Google Search Console is not connected
      security:
        - cookieAuth: []
      summary: List Google Search Console properties
      tags:
        - Integrations
  /api/user/teams/{id}/integrations/google-search-console/status:
    get:
      description: Returns team-level Google Search Console connection and credential status.
      parameters:
        - $ref: '#/components/parameters/teamID'
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  can_manage:
                    type: boolean
                  configured:
                    type: boolean
                  connected:
                    type: boolean
                  connected_account_label:
                    type: string
                  credential_status:
                    enum:
                      - configured
                      - missing
                    type: string
                  last_connected_at:
                    format: date-time
                    type: string
                  last_disconnected_at:
                    format: date-time
                    type: string
                  managed_credentials_mode:
                    enum:
                      - managed
                      - self_hosted
                    type: string
                  needs_admin_action:
                    type: boolean
                  status:
                    enum:
                      - connected
                      - disconnected
                      - credentials_missing
                    type: string
                required:
                  - status
                  - configured
                  - connected
                  - credential_status
                  - needs_admin_action
                  - can_manage
                  - managed_credentials_mode
                type: object
          description: Google Search Console status
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
      security:
        - cookieAuth: []
      summary: Get Google Search Console status
      tags:
        - Integrations
  /api/user/teams/{id}/invites:
    get:
      description: Lists pending invites for the specified team.
      parameters:
        - $ref: '#/components/parameters/teamID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/TeamInvite'
                type: array
          description: Team invites
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
      security:
        - cookieAuth: []
      summary: List team invites
      tags:
        - Teams
  /api/user/teams/{id}/invites/{inviteId}:
    delete:
      description: Revokes a pending invite.
      parameters:
        - $ref: '#/components/parameters/teamID'
        - in: path
          name: inviteId
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invite not found
      security:
        - cookieAuth: []
      summary: Revoke team invite
      tags:
        - Teams
  /api/user/teams/{id}/invites/{inviteId}/resend:
    post:
      description: Refreshes and resends a pending invite.
      parameters:
        - $ref: '#/components/parameters/teamID'
        - in: path
          name: inviteId
          required: true
          schema:
            format: uuid
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  invite:
                    $ref: '#/components/schemas/TeamInvite'
                  status:
                    type: string
                type: object
          description: Invite resend response
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invite not found
      security:
        - cookieAuth: []
      summary: Resend team invite
      tags:
        - Teams
  /api/user/teams/{id}/leave:
    delete:
      description: Removes the authenticated user from the specified team and returns the new active team.
      parameters:
        - $ref: '#/components/parameters/teamID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamLeaveResponse'
          description: Leave team response
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Cannot leave your only team or last owner
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
      security:
        - cookieAuth: []
      summary: Leave team
      tags:
        - Teams
  /api/user/teams/{id}/members:
    get:
      description: Lists members for the specified team.
      parameters:
        - $ref: '#/components/parameters/teamID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/TeamMember'
                type: array
          description: Team members
      security:
        - cookieAuth: []
      summary: List team members
      tags:
        - Teams
    post:
      description: Creates a pending invite for a user or updates the role of an existing member.
      parameters:
        - $ref: '#/components/parameters/teamID'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                email:
                  format: email
                  type: string
                role:
                  enum:
                    - owner
                    - admin
                    - member
                  type: string
              required:
                - email
                - role
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  invite:
                    $ref: '#/components/schemas/TeamInvite'
                  is_invite:
                    type: boolean
                  status:
                    type: string
                type: object
          description: Invite or update response
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invite already pending
      security:
        - cookieAuth: []
      summary: Invite team member
      tags:
        - Teams
  /api/user/teams/{id}/members/{userId}:
    delete:
      description: Removes a member from the specified team.
      parameters:
        - $ref: '#/components/parameters/teamID'
        - $ref: '#/components/parameters/userID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Cannot remove last owner
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Team member not found
      security:
        - cookieAuth: []
      summary: Remove team member
      tags:
        - Teams
  /api/user/teams/{id}/sso:
    delete:
      description: Deletes the team SSO configuration and its allowed domains. Existing HitKeep sessions and team roles are unchanged.
      parameters:
        - $ref: '#/components/parameters/teamID'
      responses:
        "204":
          description: SSO configuration deleted
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Team settings permission or SSO entitlement required
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: SSO configuration not found
      security:
        - cookieAuth: []
      summary: Delete team SSO configuration
      tags:
        - Teams
    get:
      description: Returns the team's redacted OIDC configuration and callback URL. The client secret is never returned.
      parameters:
        - $ref: '#/components/parameters/teamID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamSSOConfig'
          description: Redacted team SSO configuration
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Team settings permission or SSO entitlement required
      security:
        - cookieAuth: []
      summary: Get team SSO configuration
      tags:
        - Teams
    put:
      description: Validates OIDC discovery and stores the client secret encrypted with the instance key. Leave client_secret blank to keep an existing secret, or provide a new value to rotate it. Enabling this configuration adds SSO as an optional login method; password login is unchanged.
      parameters:
        - $ref: '#/components/parameters/teamID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TeamSSOInput'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamSSOConfig'
          description: Redacted team SSO configuration
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid configuration or missing client secret
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Team settings permission or SSO entitlement required
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: An allowed email domain belongs to another team SSO configuration
        "502":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: OIDC discovery failed
      security:
        - cookieAuth: []
      summary: Create or update team SSO configuration
      tags:
        - Teams
  /api/user/teams/{id}/sso/test:
    post:
      description: Decrypts the saved client secret in memory and validates that the configured OIDC discovery document remains reachable. Secrets and provider response bodies are not returned.
      parameters:
        - $ref: '#/components/parameters/teamID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Connection test status
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Team settings permission or SSO entitlement required
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: SSO configuration not found
        "502":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: OIDC discovery failed
      security:
        - cookieAuth: []
      summary: Test team SSO connection
      tags:
        - Teams
  /api/user/teams/{id}/tracking-domains:
    get:
      description: Lists custom tracking domains configured for the team, including DNS instructions and verification state. Requires team.manage_settings.
      parameters:
        - $ref: '#/components/parameters/teamID'
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/CustomTrackingDomain'
                type: array
          description: Tracking domains
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
      security:
        - cookieAuth: []
      summary: List tracking domains
      tags:
        - Teams
    post:
      description: Creates a custom tracking domain for the team and returns the DNS TXT ownership record plus DNS target instructions. Requires team.manage_settings.
      parameters:
        - $ref: '#/components/parameters/teamID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCustomTrackingDomainRequest'
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomTrackingDomain'
          description: Created tracking domain
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid hostname
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Hostname conflict
      security:
        - cookieAuth: []
      summary: Create tracking domain
      tags:
        - Teams
  /api/user/teams/{id}/tracking-domains/{domainId}:
    delete:
      description: Deletes a team custom tracking domain. Deleted domains are not offered as snippet hosts and cannot serve tracker traffic. Requires team.manage_settings.
      parameters:
        - $ref: '#/components/parameters/teamID'
        - $ref: '#/components/parameters/trackingDomainID'
      responses:
        "204":
          description: Deleted
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Tracking domain not found
      security:
        - cookieAuth: []
      summary: Delete tracking domain
      tags:
        - Teams
    patch:
      description: Enables or disables a team custom tracking domain. Disabled domains are not offered as snippet hosts and cannot serve tracker traffic. Requires team.manage_settings.
      parameters:
        - $ref: '#/components/parameters/teamID'
        - $ref: '#/components/parameters/trackingDomainID'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateCustomTrackingDomainRequest'
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomTrackingDomain'
          description: Tracking domain
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Tracking domain not found
      security:
        - cookieAuth: []
      summary: Update tracking domain
      tags:
        - Teams
  /api/user/teams/{id}/tracking-domains/{domainId}/verify:
    post:
      description: Checks TXT ownership, DNS target, and HTTPS tracker availability for a team custom tracking domain. Requires team.manage_settings.
      parameters:
        - $ref: '#/components/parameters/teamID'
        - $ref: '#/components/parameters/trackingDomainID'
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomTrackingDomain'
          description: Tracking domain
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Access denied
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Tracking domain not found
      security:
        - cookieAuth: []
      summary: Verify tracking domain
      tags:
        - Teams
  /api/user/teams/{id}/transfer-ownership:
    post:
      description: Transfers ownership from the current owner to another existing team member.
      parameters:
        - $ref: '#/components/parameters/teamID'
      requestBody:
        content:
          application/json:
            schema:
              properties:
                target_user_id:
                  format: uuid
                  type: string
              required:
                - target_user_id
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
          description: Status
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid target user
        "403":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Only owners can transfer ownership
        "409":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Transfer conflict
      security:
        - cookieAuth: []
      summary: Transfer team ownership
      tags:
        - Teams
  /healthz:
    get:
      description: Liveness endpoint.
      responses:
        "200":
          description: OK
      summary: Health check
      tags:
        - System
  /ingest:
    options:
      description: CORS preflight for pageview ingest.
      responses:
        "200":
          description: Preflight response
      summary: Preflight ingest
      tags:
        - Ingest
    post:
      description: Ingests a pageview hit from the browser tracker. This public endpoint expects the compact hk.js payload and browser Origin request context. The tracker sends initial hk_qr attribution as qr when a visitor arrives through a dynamic QR redirect. HitKeep uses the resolved request IP transiently for exclusions, spam filtering, and country, region, city, provider, and ASN lookup, stores derived country, region, city, provider, and ASN context, and does not store the raw visitor IP. Trusted server-side pageviews should use POST /api/ingest/server/pageview instead.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                lang:
                  type: string
                page_id:
                  format: uuid
                  type: string
                path:
                  type: string
                qr:
                  description: Initial hk_qr attribution captured by the browser tracker.
                  format: uuid
                  type: string
                referrer:
                  type: string
                sc_h:
                  type: integer
                sc_w:
                  type: integer
                session_id:
                  format: uuid
                  type: string
                u_cmp:
                  type: string
                u_cnt:
                  type: string
                u_med:
                  type: string
                u_src:
                  type: string
                u_trm:
                  type: string
                ua:
                  type: string
                unique:
                  type: boolean
                vp_h:
                  type: integer
                vp_w:
                  type: integer
              required:
                - path
                - session_id
                - page_id
              type: object
        required: true
      responses:
        "202":
          description: Accepted
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
      summary: Ingest pageview
      tags:
        - Ingest
  /ingest/event:
    options:
      description: CORS preflight for custom event ingest.
      responses:
        "200":
          description: Preflight response
      summary: Preflight event ingest
      tags:
        - Ingest
    post:
      description: Ingests a custom event from the browser tracker. Current trackers include transient path and user-agent context for exclusion matching; older trackers remain supported.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                n:
                  type: string
                p:
                  additionalProperties: true
                  type: object
                path:
                  description: Optional browser path used transiently for traffic exclusions.
                  type: string
                sid:
                  format: uuid
                  type: string
                ua:
                  description: Optional browser user agent used transiently for traffic exclusions.
                  type: string
              required:
                - n
                - sid
              type: object
        required: true
      responses:
        "202":
          description: Accepted
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
      summary: Ingest custom event
      tags:
        - Ingest
  /ingest/web-vitals:
    options:
      description: CORS preflight for opt-in Web Vitals ingest.
      responses:
        "200":
          description: Preflight response
      summary: Preflight Web Vitals ingest
      tags:
        - Ingest
    post:
      description: Ingests one compact opt-in Web Vital sample from hk-vitals.js. The server resolves the site from Origin, derives the rating from standard thresholds, and strips browser-provided query strings or hashes from the path.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebVitalIngestPayload'
        required: true
      responses:
        "202":
          description: Accepted
        "400":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
      summary: Ingest Web Vital
      tags:
        - Ingest
  /q/{token}:
    get:
      description: Public dynamic QR redirect. Resolves a saved QR campaign token, records a privacy-safe QR open unless DNT/exclusions/spam suppression applies, and redirects to the saved destination with UTM/custom parameters plus hk_qr=<qr_code_uuid>.
      parameters:
        - $ref: '#/components/parameters/token'
      responses:
        "302":
          description: Redirect to tracked destination
        "404":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: QR code not found
      summary: Open QR redirect
      tags:
        - QR Campaigns
  /readyz:
    get:
      description: Readiness endpoint for leader state and shared or open tenant database availability. Recovery and operator-attention states return a stable reason with a short retry interval.
      responses:
        "200":
          description: Ready
        "503":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReadinessUnavailable'
          description: Not ready
          headers:
            Retry-After:
              description: Seconds before retrying the readiness check.
              schema:
                minimum: 1
                type: integer
      summary: Readiness check
      tags:
        - System
servers:
  - url: http://127.0.0.1:25737
tags:
  - description: Service health and API documentation endpoints.
    name: System
  - description: Public tracking and event ingestion endpoints.
    name: Ingest
  - description: Authentication, password, and sign-in flows.
    name: Auth
  - description: Internal managed HitKeep Cloud application endpoints; they are not part of the supported public API. Available only in hosted cloud builds with billing enabled.
    name: Cloud
  - description: Authenticated user profile, preferences, and security endpoints.
    name: User
  - description: Authenticated permission context endpoints.
    name: Permissions
  - description: Instance-level admin and membership management endpoints.
    name: Admin
  - description: Site lifecycle, stats, hits, and retention endpoints.
    name: Sites
  - description: Historical analytics import validation, upload, and lifecycle endpoints.
    name: Imports
  - description: Goal and goal-timeseries endpoints.
    name: Goals
  - description: Funnel CRUD and analytics endpoints.
    name: Funnels
  - description: Dynamic QR campaign assets, print exports, redirects, attribution, and QR-scoped analytics.
    name: QR Campaigns
  - description: Share-link management and public shared analytics endpoints.
    name: Share
  - description: Data export endpoints for user and site data.
    name: Takeout
  - description: Scheduled email reports, delivery history, recipient confirmation, and unsubscribe controls.
    name: Reports
  - description: Tenant team membership and active-team context endpoints.
    name: Teams
  - description: Human-administered instance and site operational webhook endpoints.
    name: Webhooks
