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

# Deep Research

> Decide whether a single, known company fits your criteria in terms of recent activity and organizational structure as a qualifier.

<Info>**No API key yet?** [Sign up via Agent Auth](/agent-auth/agent-auth/agent-sign-up) to get your `X-API-Key` - the only required header for this endpoint.</Info>

Combine an activity question (answered from job postings) and a team-composition question (answered from LinkedIn people) in a single call, with cited evidence per stage. Provide a `domain` plus at least one of `activity_question` or `qualifier_question`.

If you are not sure which domain OpenFunnel has indexed for a company, call [Lookup Companies](/helpers/lookup-companies) first. It accepts company names, domains, and LinkedIn URLs and returns the normalized company/domain records you can pass into Deep Research.

Each parameter is hard-wired to a specific data source - pick the one that matches where the answer lives:
- **`activity_question`** → answered from the company's **recent job postings**. Use for hiring intent, team expansion, tech-stack adoption visible in JDs, geographic hiring footprint, count/spike questions, first-time hiring or first-time JD mentions, and recent-hire intent.
- **`qualifier_question`** → answered from **LinkedIn people data** (current-employee profiles). Use for team composition, roles already filled, org structure (e.g. "has a Head of Security", "has ≥5 SDRs and a Sales Director", "has a dedicated AppSec team").

Activity question examples by category:
- **Single-role hiring:** "Is this company hiring SDRs based on recent job postings?"; "Do their recent job postings show developer relations engineer hiring?"
- **Initiative/team buildout:** "Do their job descriptions suggest they are building an AI customer support automation team?"; "Do recent postings indicate they are staffing a data governance program?"
- **Tech/vendor/concept mentions:** "Are they posting roles that mention Snowflake in at least 5 job descriptions in the last 90 days?"; "Do their job postings mention Salesforce more than 3 times this month?"
- **Role-count hiring:** "Have they posted more than 5 SDR roles in the last 90 days?"; "Are they expanding platform engineering with at least 4 platform engineer postings in the last 60 days?"
- **Concept-count hiring:** "Are they building around AI automation, with at least 4 related job postings this quarter?"; "Are they staffing generative AI work, with 6 or more related job postings in the last six months?"
- **Relative spikes:** "Does their recent job activity show a spike in SDR hiring?"; "Are data engineer postings up 40% week over week?"; "Are Kubernetes mentions in their job descriptions up 80%?"
- **Recent-hire intent:** "Does recent hiring activity suggest they brought someone in to build outbound sales?"; "Does recent hiring suggest they brought in a leader to stand up product security?"
- **First-time signals:** "Is this the first time their job postings mention Kubernetes?"; "Are they hiring Solutions Engineers for the first time?"; "Does this look like their first Head of Data hire?"

Qualifier question examples by category:
- **Named team / product ownership:** "Which engineers are in the Dropbox Dash team?"; "Which VP + sales people are in the AWS Textract team?"
- **Recent people changes:** "New joiners in sales in the last 60 days"; "Any recent GTM engineering hires in sales in the last 90 days?"
- **Team-size / count checks:** "Do they have more than 2 customer support people?"; "Do they have at least 5 SDRs/BDRs?"
- **Seniority / role coverage:** "Do they have a VP or Head of Data Platform?"; "Do they have a Head of Security?"
- **Specialized teams / skills:** "Do they have a dedicated AppSec / product-security team?"; "Do they employ Kubernetes-experienced platform engineers?"

Pass both `activity_question` and `qualifier_question` when a question spans both signals - each stage runs against its own data source and returns independent evidence.

Each stage returns a natural-language `answer` suitable for inline display, a boolean `qualified` verdict, and structured source evidence. Activity evidence is returned in `activity.sources`; qualifier evidence is returned in `qualifier.sources`. Job sources include title, URL, location, created timestamp, and why-relevant reason. People sources include name, title, LinkedIn URL, seniority, department, and start date when available.

Credits: charged **per call**, not per source returned. 1 successful call (at least one stage produced a finding) = 1 charge at the per-call price configured for your workspace. A 404 (domain could not be resolved) is free. The exact amount billed is returned in `credits_consumed` on every response.



## OpenAPI

````yaml agent-primitives/openapi.json POST /api/v2/research/deep
openapi: 3.1.0
info:
  title: OpenFunnel Agent Primitives
  version: 1.0.0
servers:
  - url: https://api.openfunnel.dev
security: []
paths:
  /api/v2/research/deep:
    post:
      tags:
        - Company Research
      summary: Deep Research
      description: >-
        Decide whether a single, known company fits your criteria in terms of
        recent activity and organizational structure as a qualifier.


        <Info>**No API key yet?** [Sign up via Agent
        Auth](/agent-auth/agent-auth/agent-sign-up) to get your `X-API-Key` -
        the only required header for this endpoint.</Info>


        Combine an activity question (answered from job postings) and a
        team-composition question (answered from LinkedIn people) in a single
        call, with cited evidence per stage. Provide a `domain` plus at least
        one of `activity_question` or `qualifier_question`.


        If you are not sure which domain OpenFunnel has indexed for a company,
        call [Lookup Companies](/helpers/lookup-companies) first. It accepts
        company names, domains, and LinkedIn URLs and returns the normalized
        company/domain records you can pass into Deep Research.


        Each parameter is hard-wired to a specific data source - pick the one
        that matches where the answer lives:

        - **`activity_question`** → answered from the company's **recent job
        postings**. Use for hiring intent, team expansion, tech-stack adoption
        visible in JDs, geographic hiring footprint, count/spike questions,
        first-time hiring or first-time JD mentions, and recent-hire intent.

        - **`qualifier_question`** → answered from **LinkedIn people data**
        (current-employee profiles). Use for team composition, roles already
        filled, org structure (e.g. "has a Head of Security", "has ≥5 SDRs and a
        Sales Director", "has a dedicated AppSec team").


        Activity question examples by category:

        - **Single-role hiring:** "Is this company hiring SDRs based on recent
        job postings?"; "Do their recent job postings show developer relations
        engineer hiring?"

        - **Initiative/team buildout:** "Do their job descriptions suggest they
        are building an AI customer support automation team?"; "Do recent
        postings indicate they are staffing a data governance program?"

        - **Tech/vendor/concept mentions:** "Are they posting roles that mention
        Snowflake in at least 5 job descriptions in the last 90 days?"; "Do
        their job postings mention Salesforce more than 3 times this month?"

        - **Role-count hiring:** "Have they posted more than 5 SDR roles in the
        last 90 days?"; "Are they expanding platform engineering with at least 4
        platform engineer postings in the last 60 days?"

        - **Concept-count hiring:** "Are they building around AI automation,
        with at least 4 related job postings this quarter?"; "Are they staffing
        generative AI work, with 6 or more related job postings in the last six
        months?"

        - **Relative spikes:** "Does their recent job activity show a spike in
        SDR hiring?"; "Are data engineer postings up 40% week over week?"; "Are
        Kubernetes mentions in their job descriptions up 80%?"

        - **Recent-hire intent:** "Does recent hiring activity suggest they
        brought someone in to build outbound sales?"; "Does recent hiring
        suggest they brought in a leader to stand up product security?"

        - **First-time signals:** "Is this the first time their job postings
        mention Kubernetes?"; "Are they hiring Solutions Engineers for the first
        time?"; "Does this look like their first Head of Data hire?"


        Qualifier question examples by category:

        - **Named team / product ownership:** "Which engineers are in the
        Dropbox Dash team?"; "Which VP + sales people are in the AWS Textract
        team?"

        - **Recent people changes:** "New joiners in sales in the last 60 days";
        "Any recent GTM engineering hires in sales in the last 90 days?"

        - **Team-size / count checks:** "Do they have more than 2 customer
        support people?"; "Do they have at least 5 SDRs/BDRs?"

        - **Seniority / role coverage:** "Do they have a VP or Head of Data
        Platform?"; "Do they have a Head of Security?"

        - **Specialized teams / skills:** "Do they have a dedicated AppSec /
        product-security team?"; "Do they employ Kubernetes-experienced platform
        engineers?"


        Pass both `activity_question` and `qualifier_question` when a question
        spans both signals - each stage runs against its own data source and
        returns independent evidence.


        Each stage returns a natural-language `answer` suitable for inline
        display, a boolean `qualified` verdict, and structured source evidence.
        Activity evidence is returned in `activity.sources`; qualifier evidence
        is returned in `qualifier.sources`. Job sources include title, URL,
        location, created timestamp, and why-relevant reason. People sources
        include name, title, LinkedIn URL, seniority, department, and start date
        when available.


        Credits: charged **per call**, not per source returned. 1 successful
        call (at least one stage produced a finding) = 1 charge at the per-call
        price configured for your workspace. A 404 (domain could not be
        resolved) is free. The exact amount billed is returned in
        `credits_consumed` on every response.
      operationId: deep_research_api_v2_research_deep_post
      parameters:
        - name: X-API-Key
          in: header
          required: true
          schema:
            type: string
            title: X-Api-Key
          description: >-
            Your OpenFunnel API key. Get one via [Agent
            Auth](/agent-auth/agent-auth/agent-sign-up).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeepResearchRequest'
            example:
              domain: usepylon.com
              activity_question: Have they posted more than 5 SDR roles in the last 90 days?
              qualifier_question: Has at least 5 SDRs/BDRs and a Sales Director
              timeframe_days: 90
              max_jobs_to_check: 25
      responses:
        '200':
          description: Research findings with natural-language answers and source evidence.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeepResearchResponse'
        '400':
          description: Insufficient credits to run the call.
        '404':
          description: >-
            Domain could not be resolved to a company in the OpenFunnel
            companies index. Check the domain or try the primary corporate
            domain.
        '422':
          description: >-
            Request validation error (e.g. neither `activity_question` nor
            `qualifier_question` provided, invalid domain format, out-of-range
            `timeframe_days`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    DeepResearchRequest:
      properties:
        domain:
          type: string
          title: Domain
          description: >-
            Company website domain to research (e.g. 'stripe.com'). Protocol and
            `www.` prefixes are stripped automatically. The domain is resolved
            to a LinkedIn org via the internal companies index before either
            stage runs. If this does not resolve, use [Lookup
            Companies](/helpers/lookup-companies) to find the normalized indexed
            domain.
          examples:
            - stripe.com
            - notion.so
            - databricks.com
        activity_question:
          anyOf:
            - type: string
            - type: 'null'
          title: Activity Question
          description: >-
            **Answered from the company's recent job postings.**
            Natural-language description of the activity signal to look for in
            open roles. Use this for hiring intent, team expansion, tech-stack
            or vendor adoption visible in JDs, geographic hiring footprint,
            count/spike questions, first-time hiring, first-time job-post
            mentions, or recent-hire intent. Examples: `Is this company hiring
            SDRs based on recent job postings?`, `Do their job descriptions
            suggest they are building an AI customer support automation team?`,
            `Are they posting roles that mention Snowflake in at least 5 job
            descriptions in the last 90 days?`, `Have they posted more than 5
            SDR roles in the last 90 days?`, `Are data engineer postings up 40%
            week over week?`, `Is this the first time their job postings mention
            Kubernetes?`. Pass `null` or omit to skip this stage.
          examples:
            - Is this company hiring SDRs based on recent job postings?
            - >-
              Do their job descriptions suggest they are building an AI customer
              support automation team?
            - Have they posted more than 5 SDR roles in the last 90 days?
            - Are data engineer postings up 40% week over week?
            - Is this the first time their job postings mention Kubernetes?
        qualifier_question:
          anyOf:
            - type: string
            - type: 'null'
          title: Qualifier Question
          description: >-
            **Answered from LinkedIn people data** (current-employee profiles).
            Natural-language criteria about who already works at the company or
            how the org is shaped. Use this for team composition, named
            teams/products, roles already filled, seniority coverage,
            team-size/count checks, recent joiners or start-date questions, and
            skill/team specialization. Examples: `Which engineers are in the
            Dropbox Dash team?`, `New joiners in sales in the last 60 days`, `Do
            they have more than 2 customer support people?`, `Has at least 5
            SDRs/BDRs and a Sales Director`, `Has a dedicated AppSec /
            product-security team`. Pass `null` or omit to skip this stage.
          examples:
            - Which engineers are in the Dropbox Dash team?
            - New joiners in sales in the last 60 days
            - Do they have more than 2 customer support people?
            - Has at least 5 SDRs/BDRs and a Sales Director
            - Has a dedicated AppSec / product-security team
            - Which VP + sales people are in the AWS Textract team?
        timeframe_days:
          type: integer
          maximum: 365
          minimum: 1
          title: Timeframe Days
          description: >-
            Lookback window (days) used by the activity stage. Has no effect
            when `activity_question` is omitted.
          default: 90
        max_jobs_to_check:
          type: integer
          maximum: 200
          minimum: 1
          title: Max Jobs To Check
          description: >-
            Upper bound on the number of recent job postings the activity stage
            scores with the LLM relevance pass. Higher values raise recall but
            add latency / token cost (each job is judged by an LLM call).
            Default 25 is tuned for interactive use; bump it for batch /
            data-extraction workflows where you want maximum recall. Has no
            effect when `activity_question` is omitted.
          default: 25
      type: object
      required:
        - domain
      title: DeepResearchRequest
      description: >-
        Inputs for a single Deep Research call.


        Provide a `domain` plus at least one of:

        - `activity_question` - answered from the company's recent **job
        postings** (hiring intent, team expansion, JD-visible tech/vendor
        signals, count/spike signals, first-time hiring or first-time JD
        mentions).

        - `qualifier_question` - answered from **LinkedIn people data** (team
        composition, named teams/products, roles already filled, recent joiners,
        team-size checks, org shape).


        Pick the param whose backing data source contains the answer; pass both
        when the question spans both signals. If the company domain is ambiguous
        or does not resolve, use `POST /api/v1/account/lookup-companies` first
        to find the normalized domain OpenFunnel has indexed.
      examples:
        - domain: usepylon.com
          activity_question: Have they posted more than 5 SDR roles in the last 90 days?
          timeframe_days: 90
          max_jobs_to_check: 25
        - domain: dropbox.com
          qualifier_question: Which engineers are in the Dropbox Dash team?
        - domain: reducto.ai
          qualifier_question: New joiners in sales in the last 60 days
        - domain: mintlify.com
          qualifier_question: Do they have more than 2 customer support people?
        - domain: databricks.com
          activity_question: Are they posting data roles that require Snowflake and dbt?
          qualifier_question: Has a VP or Head of Data Platform
          timeframe_days: 60
    DeepResearchResponse:
      properties:
        domain:
          type: string
          title: Domain
          description: Echo of the requested domain (normalized).
        company:
          anyOf:
            - $ref: '#/components/schemas/CompanyInfo'
            - type: 'null'
          description: >-
            Resolved firmographic context for the company. Null only if the
            domain could not be resolved (in which case the endpoint returns 404
            instead).
        activity:
          anyOf:
            - $ref: '#/components/schemas/ActivityFinding'
            - type: 'null'
          description: >-
            Findings for the activity question. Null when `activity_question`
            was not provided. Matching job source links are returned in
            `activity.sources`.
        qualifier:
          anyOf:
            - $ref: '#/components/schemas/QualifierFinding'
            - type: 'null'
          description: >-
            Findings for the qualifier question. Null when `qualifier_question`
            was not provided. Matching people source links are returned in
            `qualifier.sources`.
        credits_consumed:
          type: integer
          title: Credits Consumed
          description: >-
            Credits charged for this call. Charge is recorded in the background
            after the response is sent. 0 when the call failed before producing
            any findings.
          default: 0
        latency_ms:
          type: integer
          title: Latency Ms
          description: End-to-end request latency in milliseconds.
      type: object
      required:
        - domain
        - latency_ms
      title: DeepResearchResponse
      description: >-
        Top-level Deep Research response. Stage-level `activity.sources` and
        `qualifier.sources` contain cited evidence.
    HTTPValidationError:
      type: object
      properties:
        detail:
          type: array
          items:
            $ref: '#/components/schemas/ValidationError'
    CompanyInfo:
      properties:
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Canonical company name.
        domain:
          type: string
          title: Domain
          description: Echo of the requested domain (normalized).
        linkedin_slug:
          anyOf:
            - type: string
            - type: 'null'
          title: Linkedin Slug
          description: LinkedIn company slug (lowercased).
        employee_count:
          anyOf:
            - type: integer
            - type: 'null'
          title: Employee Count
          description: Estimated current employee count, if known.
        stage:
          anyOf:
            - type: string
            - type: 'null'
          title: Stage
          description: Funding stage label (e.g. 'Series B', 'Public', 'Acquired').
        industry:
          anyOf:
            - type: string
            - type: 'null'
          title: Industry
          description: Primary industry tag.
        location:
          anyOf:
            - type: string
            - type: 'null'
          title: Location
          description: HQ city/region/country string.
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: Short company description from the companies index, truncated.
      type: object
      required:
        - domain
      title: CompanyInfo
      description: Resolved firmographic context for the researched company.
    ActivityFinding:
      properties:
        question:
          type: string
          title: Question
          description: Echo of the activity question.
        qualified:
          type: boolean
          title: Qualified
          description: >-
            True iff the company's recent job-posting activity matches the
            question with high enough signal to qualify.
        answer:
          type: string
          title: Answer
          description: >-
            Natural-language answer (1-3 sentences) summarizing the verdict and
            the key evidence the LLM relied on.
        confidence:
          anyOf:
            - type: string
            - type: 'null'
          title: Confidence
          description: >-
            LLM-reported confidence label, typically 'low' / 'medium' / 'high'.
            May be empty when the qualifier returned a structured-recipe verdict
            without an explicit confidence.
        jobs_searched:
          type: integer
          title: Jobs Searched
          description: >-
            Number of recent job postings considered before LLM relevance
            scoring.
        jobs_matched:
          type: integer
          title: Jobs Matched
          description: Number of job postings the LLM judged relevant.
        sources:
          items:
            $ref: '#/components/schemas/JobSource'
          type: array
          title: Sources
          description: >-
            Up to ~10 matching job postings used as evidence, ordered by
            recency.
        elapsed_ms:
          type: integer
          title: Elapsed Ms
          description: Stage latency in milliseconds.
        error:
          anyOf:
            - type: string
            - type: 'null'
          title: Error
          description: >-
            Set when the stage failed mid-flight (e.g. company missing from the
            jobs index). `qualified` is False and `sources` is empty in this
            case.
      type: object
      required:
        - question
        - qualified
        - answer
        - jobs_searched
        - jobs_matched
        - elapsed_ms
      title: ActivityFinding
      description: Result of the activity-question stage.
    QualifierFinding:
      properties:
        question:
          type: string
          title: Question
          description: Echo of the qualifier question.
        qualified:
          type: boolean
          title: Qualified
          description: True iff the company's team matches the criteria.
        answer:
          type: string
          title: Answer
          description: >-
            One-sentence natural-language summary of the verdict - suitable for
            inline use in a CRM note or alert.
        detailed_reasoning:
          type: string
          title: Detailed Reasoning
          description: >-
            Longer LLM-written reasoning: which signals matched, which didn't,
            and any caveats. May reference specific people.
        people_evaluated:
          type: integer
          title: People Evaluated
          description: >-
            Number of LinkedIn profiles the LLM evaluated (after department /
            seniority pre-filtering).
        sources:
          items:
            $ref: '#/components/schemas/PersonSource'
          type: array
          title: Sources
          description: >-
            Profiles the LLM cited as matching the criteria. Empty when
            `qualified` is False or the criteria was about absence rather than
            presence.
        elapsed_ms:
          type: integer
          title: Elapsed Ms
          description: Stage latency in milliseconds.
        error:
          anyOf:
            - type: string
            - type: 'null'
          title: Error
          description: >-
            Set when the stage failed mid-flight (e.g. company has no people
            indexed). `qualified` is False and `sources` is empty in this case.
      type: object
      required:
        - question
        - qualified
        - answer
        - detailed_reasoning
        - people_evaluated
        - elapsed_ms
      title: QualifierFinding
      description: Result of the people-qualifier stage.
    ValidationError:
      type: object
      required:
        - loc
        - msg
        - type
      properties:
        loc:
          type: array
          items:
            anyOf:
              - type: string
              - type: integer
        msg:
          type: string
        type:
          type: string
    JobSource:
      properties:
        title:
          type: string
          title: Title
          description: Job title as posted.
        url:
          anyOf:
            - type: string
            - type: 'null'
          title: Url
          description: Canonical URL to the job posting.
        location:
          anyOf:
            - type: string
            - type: 'null'
          title: Location
          description: Posted location string.
        created:
          anyOf:
            - type: string
            - type: 'null'
          title: Created
          description: ISO timestamp of when the posting first appeared in the index.
        why_relevant:
          anyOf:
            - type: string
            - type: 'null'
          title: Why Relevant
          description: >-
            Short LLM-generated explanation of why this posting is evidence for
            the activity question.
      type: object
      required:
        - title
      title: JobSource
      description: A job posting cited by the activity stage.
    PersonSource:
      properties:
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Full name from the profile.
        title:
          anyOf:
            - type: string
            - type: 'null'
          title: Title
          description: Current job title.
        linkedin_url:
          anyOf:
            - type: string
            - type: 'null'
          title: Linkedin Url
          description: Canonical LinkedIn profile URL.
        seniority:
          anyOf:
            - type: string
            - type: 'null'
          title: Seniority
          description: >-
            Precomputed seniority bucket: FOUNDER, CXO, PARTNER, VP, HEAD,
            DIRECTOR, MANAGER, SENIOR, ENTRY, INTERN.
        department:
          anyOf:
            - type: string
            - type: 'null'
          title: Department
          description: >-
            Precomputed department bucket: ENGINEERING, PRODUCT, DATA, SALES,
            MARKETING, OPERATIONS, FINANCE, HR, LEGAL, EXECUTIVE,
            CUSTOMER_SUCCESS, SECURITY, IT, OTHER.
        started_on:
          anyOf:
            - type: string
            - type: 'null'
          title: Started On
          description: >-
            ISO start date of the person's current role (populated only when the
            qualifier question implied tenure / recency, e.g. 'joined in the
            last 90 days').
      type: object
      title: PersonSource
      description: A person cited by the qualifier stage.

````