> ## Documentation Index
> Fetch the complete documentation index at: https://technologychecker.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Validate churn signals API

> Actively re-detect churn candidates in real time and get a reasoned verdict per domain. Billed per domain after completion; verdicts cached 48 hours.

## Frequently asked questions

<AccordionGroup>
  <Accordion title="When should I use this instead of the confidence score?">
    The confidence score is statistical — it grades a churn from detection history. This endpoint is definitive: it loads the site again right now with a browser and consent-banner clicks, so it sees more than the bulk crawl did. Use it on the handful of churns you are about to act on, not on the whole feed.
  </Accordion>

  <Accordion title="What do the four verdicts mean?">
    `confirmed` — the technology is absent on a healthy re-detection, so the removal is genuine. `false_positive` — it is still there, so the churn was a prior crawl miss. `masked` — a header-detected origin web server is now behind a CDN or proxy, which is a detection change rather than a removal. `inconclusive` — the site was unreachable, blocked, or gave too little signal.
  </Accordion>

  <Accordion title="What does it cost?">
    Roughly 2 credits per domain in fetch mode and 5 in browser mode, charged after completion. An `inconclusive` result is never charged, because that is our side failing rather than yours. Verdicts are cached for 48 hours per domain and technology pair, so a repeat call returns the stored verdict with `cached: true` at no charge and with no re-crawl. Inconclusive results are never cached, so a retry genuinely re-runs.
  </Accordion>

  <Accordion title="How many items can I send at once?">
    Up to 10 in `auto` or `browser` mode, and up to 100 in `fetch` mode. Fetch parallelises at roughly 1–3 seconds per item; browser takes 30–60 seconds each, which is why its cap is small enough to fit a synchronous response. Split larger browser batches on your side.
  </Accordion>

  <Accordion title="What does auto mode actually do?">
    It routes technologies with a reliable detection method to the fast fetch path and sends everything else straight to a browser. If a fetch result comes back weak it escalates to a browser once. That keeps the average cost near the fetch price while still settling the hard cases.
  </Accordion>
</AccordionGroup>


## OpenAPI

````yaml POST /v1/signals/churn/validate
openapi: 3.1.0
info:
  title: TechnologyChecker API
  description: >-
    Discover technology stacks, track market trends, and enrich data with
    company intelligence.
  version: 1.0.0
  contact:
    name: TechnologyChecker Support
    email: support@technologychecker.io
    url: https://technologychecker.io
servers:
  - url: https://api.technologychecker.io
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Domain technologies
    description: Look up technologies detected on any domain
  - name: Technology data
    description: Search, look up, and get details on tracked technologies
  - name: Market intelligence
    description: Technology stats, trends, and market share
  - name: Company data
    description: Firmographic and company intelligence
  - name: Live detection
    description: Real-time technology detection with browser rendering
  - name: Signals
    description: >-
      Intent signals derived from technology state transitions: who adopted, who
      churned, who switched, and who has a gap in their stack.
  - name: Alerts
    description: >-
      Saved signal queries delivered to your webhook. Every signal you can
      query, you can subscribe to.
  - name: Audiences
    description: >-
      Saved groups of domains — uploaded lists or computed recipes — that you
      can profile, slice and monitor.
  - name: Lead lists
    description: >-
      Build and export targeted domain lists combining technology detection with
      company data.
paths:
  /v1/signals/churn/validate:
    post:
      tags:
        - Signals
      summary: Validate churn signals API
      description: >-
        Actively re-detect churn candidates right now and return a reasoned
        verdict per item. The statistical accuracy layers grade a churn from
        history; this endpoint settles it by loading the site again with a
        browser and consent-banner clicks, so it sees more than the bulk crawl
        did.


        **Billing is per domain, charged after completion:** roughly 2 credits
        for a fetch-mode check and 5 for a browser-mode check. An `inconclusive`
        result is never charged. Verdicts are cached for 48 hours per `(domain,
        technology_id)` pair — a repeat call returns the stored verdict with
        `cached: true`, with no re-crawl and no charge.
      operationId: validateChurn
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - items
              properties:
                items:
                  type: array
                  minItems: 1
                  description: >-
                    The churn candidates to re-check. Maximum 10 items in `auto`
                    or `browser` mode, 100 in `fetch` mode.
                  items:
                    type: object
                    required:
                      - domain
                      - technology_id
                    properties:
                      domain:
                        type: string
                        example: sandovalcountynm.gov
                      technology_id:
                        type: integer
                        example: 300
                mode:
                  type: string
                  enum:
                    - auto
                    - fetch
                    - browser
                  default: auto
                  description: >-
                    `auto` routes high-reliability technologies to a fast fetch
                    and everything else straight to a browser, escalating weak
                    fetch results once. `fetch` parallelises at roughly 1–3
                    seconds per item. `browser` takes 30–60 seconds per item.
                interact:
                  type: boolean
                  default: true
                  description: Click consent banners so consent-gated tags fire.
            example:
              items:
                - domain: sandovalcountynm.gov
                  technology_id: 300
              mode: auto
              interact: true
      responses:
        '200':
          description: A verdict per submitted item
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: object
                    properties:
                      results:
                        type: array
                        items:
                          type: object
                          properties:
                            domain:
                              type: string
                              example: sandovalcountynm.gov
                            technology_id:
                              type: integer
                              example: 300
                            verdict:
                              type: string
                              enum:
                                - confirmed
                                - false_positive
                                - masked
                                - inconclusive
                              description: >-
                                `confirmed` — absent on a healthy re-detection,
                                a genuine removal. `false_positive` — still
                                detected live, so the churn was a prior crawl
                                miss. `masked` — a header-detected origin web
                                server is now behind a CDN or proxy, which is a
                                detection change rather than a removal.
                                `inconclusive` — unreachable, blocked or too
                                little signal; never cached and never charged.
                              example: confirmed
                            still_detected:
                              type: boolean
                              example: false
                            newly_detected:
                              type: array
                              items:
                                type: string
                              example:
                                - Cloudflare
                            mode_used:
                              type: string
                              enum:
                                - fetch
                                - browser
                              example: browser
                            cached:
                              type: boolean
                              description: >-
                                The verdict came from the 48-hour cache; nothing
                                was re-crawled or charged.
                              example: false
                            credits_charged:
                              type: integer
                              example: 5
                            reason:
                              type: string
                      credits_charged:
                        type: integer
                        description: Total charged for this request.
                        example: 5
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  responses:
    BadRequest:
      description: Invalid or missing parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            success: false
            error:
              code: VALIDATION_ERROR
              message: >-
                Provide exactly one scope: technology_id, technology or
                category_id.
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    PaymentRequired:
      description: Not enough credits, or the plan does not include this feature
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            success: false
            error:
              code: INSUFFICIENT_CREDITS
              message: Insufficient credits for this request.
              details:
                required: 1
                available: 0
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    RateLimited:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
  schemas:
    ApiError:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          properties:
            code:
              type: string
              example: UNAUTHORIZED
            message:
              type: string
              example: Missing or invalid API key
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        API key in format `tapi_live_[32-char]` (live) or `tapi_test_[32-char]`
        (test)

````