---
title: "Merged discovery view (status + result when terminal)."
method: GET
path: "/api/v1/discoveries/{jobId}"
tags: ["discoveries"]
---

# Merged discovery view (status + result when terminal).

`GET /api/v1/discoveries/{jobId}`

## Request

- Base URL: `https://api.prosp.cc`
- URL: `https://api.prosp.cc/api/v1/discoveries/{jobId}`
- Auth: OAuth 2 (client credentials, token URL `https://prosp-mcp-641053169627.auth.eu-central-1.amazoncognito.com/oauth2/token`, scopes: `mcp/execute`)

## Response `200`

Always returns the current status; includes `result` once terminal
(`completed`, `failed`, `exhausted`, or `cancelled`). A `cancelled` run's
`result` carries the prospects it delivered before cancellation.

- Discovery: A discovery's status merged with its result. The `result` block is present only once the discovery reaches a terminal state.
  - `jobId` string, required: Id of this discovery.
  - `icp_id` string: The saved ICP this discovery was started from (`POST /api/v1/discoveries {"icpId": ...}`), for billing attribution — the same value the matching credit-ledger rows carry, so a charge reconciles back to the ICP that caused it. Served on EVERY job surface (the progressive status, the merged discovery view, the finished result, and each entry of the discoveries list) from one reader, so a customer can attribute a run to its ICP without waiting for the run to finish or cross-referencing the ledger. ABSENT (field omitted, never an empty string) when the run was ad-hoc from inline criteria. Ids are scoped to the calling customer — the same namespace `GET /api/v1/icps` returns.
  - `status` 'queued' | 'running' | 'processing' | 'completed' | 'failed' | 'exhausted' | 'cancelled', required: Discovery lifecycle state. `queued` = accepted, not yet started; `running` and `processing` = discovery in progress; `completed` = finished and reached its target; `failed` = ended with an error; `exhausted` = finished but ran out of candidates before hitting the target; `cancelled` = stopped by the caller via `POST /mcp/jobs/{jobId}/cancel`. The last four are terminal.
  - `result` JobResult: The result of a finished discovery, present once the run reaches a terminal state. Field names are snake_case. `prospects[]` carries only customer-safe fields — internal scoring, provenance, and raw content are never included.
    - `jobId` string, required: Id of this discovery.
    - `icp_id` string: The saved ICP this discovery was started from (`POST /api/v1/discoveries {"icpId": ...}`), for billing attribution — the same value the matching credit-ledger rows carry, so a charge reconciles back to the ICP that caused it. Served on EVERY job surface (the progressive status, the merged discovery view, the finished result, and each entry of the discoveries list) from one reader, so a customer can attribute a run to its ICP without waiting for the run to finish or cross-referencing the ledger. ABSENT (field omitted, never an empty string) when the run was ad-hoc from inline criteria. Ids are scoped to the calling customer — the same namespace `GET /api/v1/icps` returns.
    - `status` 'queued' | 'running' | 'processing' | 'completed' | 'failed' | 'exhausted' | 'cancelled', required: Discovery lifecycle state. `queued` = accepted, not yet started; `running` and `processing` = discovery in progress; `completed` = finished and reached its target; `failed` = ended with an error; `exhausted` = finished but ran out of candidates before hitting the target; `cancelled` = stopped by the caller via `POST /mcp/jobs/{jobId}/cancel`. The last four are terminal.
    - `total_prospects` integer: Number of prospects this discovery produced — the canonical per-discovery delivery count, the SAME field name and metric the live `JobStatus.total_prospects` reports (this is its terminal, frozen value). Key on this ONE name to group/count a discovery's delivery across the status and result surfaces. On the merged `Discovery` view this is read at `result.total_prospects`. Distinct from the pagination `total` on `ProspectList`/`ProspectSummaryList` (a LIST/SEARCH result-set count across your inbox).
    - `withheld_count` integer: How many companies this discovery DELIVERED and then RETRACTED, so `total_prospects` above is the SERVABLE set rather than the originally-announced one. A prospect is retracted when a check that can only complete after delivery decides it does not belong to your search — most often a headquarters re-check placing it outside your target region. Retracted companies are removed from `prospects[]`/`found[]` and never reach your CRM. ABSENT (never `0`) when nothing was retracted: absence is the signal.
    - `withheld` object[]: The retracted companies, one entry per row removed from `prospects[]`/`found[]`. Present only alongside a non-zero `withheld_count`.
      - `domain` string: The retracted company's domain, as it was originally delivered.
      - `reason` 'sub_region' | 'kb_seed_dedup' | 'exclusion' | 'business_model' | 'delivery_cap' | 'unspecified': Bounded reason the company was retracted. `sub_region` = its headquarters resolved outside your target region; `exclusion` / `business_model` = it matched a filter you set; `kb_seed_dedup` = you already own it from an earlier search; `delivery_cap` = it arrived after the run had already met your target.
    - `terminal_reason` 'candidates_exhausted' | 'trailing_candidates_forgiven' | 'candidate_cap_reached' | 'candidate_search_failed' | 'time_limit_reached' | 'stalled_no_progress' | 'fan_in_incomplete' | 'target_reached' | 'saturated_known_set' | 'cancelled_by_customer' | 'null', nullable: Why the discovery stopped, once terminal; null while still running, and null on any job terminal before this field's writers shipped. Carried on BOTH the progressive status and the finalized result — they read the same durable value, so the two surfaces cannot disagree about why a run ended. `candidates_exhausted` = the market had no more matching candidates to evaluate, and every candidate it did dispatch was evaluated; `trailing_candidates_forgiven` = the run stopped while a small remainder of its own candidates (up to 5%) was still being evaluated, so the shortfall was forgiven rather than exhausted — reported separately from `candidates_exhausted` because the two call for OPPOSITE responses: a genuinely dry market wants a wider profile, whereas a forgiven remainder means the run was cut short and re-running the SAME profile may deliver; `candidate_cap_reached` = when a run's candidates ran out before your target, it fetched further batches of new candidates, and it stopped because it had screened the maximum number of candidates one run may evaluate — the sources were still returning new candidates, so the shortfall reflects the per-run screening limit, not an exhausted market; `candidate_search_failed` = the run's candidates ran out before your target and the search for further candidates failed repeatedly, so the market was never shown to be exhausted — re-running the SAME profile later may deliver; `time_limit_reached` = the run hit its time budget while still producing; `stalled_no_progress` = the run stopped producing results and was terminated early rather than hitting its budget (a different situation from either of the two above, and reported separately so it is not mistaken for a thin market or a short budget); `fan_in_incomplete` = a search step never finished, so the run was never able to ask for more candidates; `target_reached` = the run delivered everything you asked for; `saturated_known_set` = every delivered domain was already yours from an earlier search (refunded); `cancelled_by_customer` = you cancelled it via `POST /mcp/jobs/{jobId}/cancel`. When credits are refunded, the refund reason matches this.
    - `already_owned_count` integer: How many of this discovery's delivered domains you ALREADY owned from a PRIOR discovery (any earlier search, not just this one's filters) — i.e. cross-search overlap with your existing prospects. 0 means every delivered domain is new to you. Domains already owned under this exact search's filters are de-duplicated out before delivery and are not counted here. Served on both the progressive status and the finalized result from one durable value. See `already_owned_withheld_count` for how many WERE de-duplicated out.
    - `already_owned_withheld_count` integer: How many companies this discovery found, judged in-scope for THIS search, and then did NOT deliver because you already own them from an earlier run of the same search. This is the count `already_owned_count` explicitly excludes: those domains were suppressed one step BEFORE delivery, so they appear in neither `prospects` nor `total_prospects`. It is the number that explains an otherwise puzzling result — a repeat search of a market you have already worked returns few new companies precisely BECAUSE most of what it found is already yours, and without this field that outcome is indistinguishable from a failed run. Counted as DISTINCT companies across both suppression paths (freshly searched and re-served from your existing knowledge base), so it never double-counts one company found twice. ABSENT — never 0 — when nothing was suppressed, so any value you receive is a real one.
    - `candidates_screened` integer: Number of candidate companies this discovery began SCREENING (search/candidate-dispatch phase). Increases monotonically during screening so a client sees live progress even before the first prospect domain lands; 0 before screening has started. It legitimately PLATEAUS once search has exhausted the niche and the run moves into its crawl/validation tail — that is not a stall. For liveness during that tail, read `evaluated_count`. On the finished result it is the frozen final value, so the same number explains a run after it ends.
    - `evaluated_count` integer: Number of screened candidates that have finished crawl + validation (a running verdict count = accepted + rejected). Complements `candidates_screened`: it keeps climbing through the crawl/validation tail — after screening plateaus — so a polling client sees liveness for the WHOLE run. Retains its final total once the discovery is terminal, where together with `candidates_screened` it is the funnel that explains the delivered count.
    - `auto_complete_reason` 'stuck_processing_no_marker' | 'stuck_processing_no_prospects' | 'null', nullable: Present ONLY when automatic recovery force-completed a run that stalled in `processing` with no completion marker (`stuck_processing_no_marker` when prospects had accumulated, `stuck_processing_no_prospects` when none had). Absent on every normally-terminating run.
    - `credits_refunded` integer, nullable: Credits returned to the caller's balance when this run was auto-refunded (e.g. an exhausted run that delivered fewer prospects than requested). Absent when no refund applied. Same field, semantics, and shape as `JobCancelled.credits_refunded` — one canonical refund projection across the result and cancel surfaces.
    - `credits_refund_reason` string, nullable: Bounded reason the refund was applied (e.g. `candidates_exhausted`, `time_limit_reached`), resolved from durable terminal state. Present iff `credits_refunded` is. Mirrors `JobCancelled.credits_refund_reason`.
    - `completed_at` string, date-time, nullable: When the discovery finished (ISO-8601). Null while not yet terminal.
    - `prospects` Prospect[]: The customer-safe prospects this run found (same field set as `ProspectList`).
      - `domain` string, required: The company's primary website domain. The stable identifier for the prospect.
      - `name` string, nullable: The company's name.
      - `description` string, nullable: Short description of what the company does.
      - `industry` string, nullable: The company's primary industry (coarse-grained). Free-text, but normalized from a canonical taxonomy: `fashion_retail`, `electronics_retail`, `grocery_retail`, `home_furniture`, `furniture_manufacturing`, `automotive`, `manufacturing`, `chemicals_materials`, `agriculture_food_production`, `logistics`, `b2b_services`, `technology_software`, `healthcare`, `financial_services`, `cosmetics_beauty`, `jewelry_accessories`, `hospitality_restaurant`, `energy`, `sporting_goods`, `recreation`, `education`, and the specialty-retail categories `florist_flowers`, `bakery_pastry`, `butcher_delicatessen`, `optician_eyewear`, `pharmacy_drugstore`, `pet_shop`, `toy_hobby`, `stationery_office_supplies`, `bookstore`, `garden_center`, `musical_instruments`, and `hardware_diy`. The list is not strict — a value outside it may occasionally be returned — so treat it as the expected vocabulary rather than a closed enum.
      - `employee_count` integer, nullable: Estimated number of employees.
      - `employee_count_source` 'official_registry' | 'public_knowledge_base' | 'professional_network' | 'web_research' | 'company_website' | 'unverified_estimate' | 'verified' | 'null', nullable: Authority band of `employee_count` — a source-agnostic label for the kind of source that won the precedence contest when the value was last written (`official_registry` > `public_knowledge_base` > `professional_network` > `web_research` > `company_website` > `unverified_estimate`, with `verified` an operator override). `official_registry` is an official government/company registry; `public_knowledge_base` is a public, structured, source-referenced knowledge base; `professional_network` is a public professional-network profile; `web_research` is a third-party OSINT figure researched from cited public sources; `company_website` is the company's own website; `unverified_estimate` is an UNVERIFIED third-party or model-derived estimate — nothing was measured, filed or self-declared, so treat it as the pipeline's best guess and NOT as a sourced figure. Surfaced customer-side so the freshness/authority of the headcount can be audited (provenance of a delivered fact) without naming any internal pipeline/vendor. Null when no source has been recorded.
      - `employee_count_as_of` string, date-time, nullable: ISO-8601 UTC timestamp of when `employee_count` was last written by a winning source (server-stamped). The freshness marker paired with `employee_count_source`. Null when no source has been recorded.
      - `size_certainty` 'verified' | 'estimated' | 'unknown': How much to trust company_size/employee_count: verified = official registry or declared; estimated = third-party or model estimate; unknown = no size.
      - `company_size` 'micro' | 'small' | 'medium' | 'large' | 'enterprise' | 'null', nullable: Company size bucket by employee headcount (lowercase; null when undetermined). `micro` (≤10 employees), `small` (≤50), `medium` (≤200), `large` (≤500), `enterprise` (500+). Uses the SAME lowercase tokens as the `company_sizes` INPUT filter (DiscoveryCriteria.company_sizes), so a value read off a result can be re-submitted verbatim as a filter without a 400. `micro` companies are returned by a `small` `company_sizes` filter.
      - `headquarters_location` string, nullable: Headquarters location as a "City, Country" string.
      - `country` string, nullable: The company's country.
      - `country_code` string, nullable: The company's country as an ISO-3166-1 alpha-2 code.
      - `city` string, nullable: The company's city.
      - `state` string, nullable: The company's state, province, or region.
      - `postal_code` string, nullable: The company's postal or ZIP code.
      - `street` string, nullable: The company's street address.
      - `street_romanized` string, nullable: Latin-script transliteration of `street`, for companies in non-Latin-script markets.
      - `contacts` Contact[]: People discovered at the company.
        - `name` string, nullable: The person's full name.
        - `email` string, nullable: The person's email address, where discovered.
        - `position` string, nullable: The person's job title or role.
        - `title` string, nullable: The person's job title or role, as surfaced during discovery (alternative source to `position`).
        - `phone` string, nullable: A direct phone number for the person.
        - `department` string, nullable: The department or function the person works in.
        - `organization` string, nullable: The organization or company the person is associated with.
        - `linkedin_url` string, nullable: The person's LinkedIn profile URL.
        - `instagram_url` string, nullable: The person's Instagram profile URL.
        - `facebook_url` string, nullable: The person's Facebook profile URL.
        - `twitter_url` string, nullable: The person's X (Twitter) profile URL.
        - `youtube_url` string, nullable: The person's YouTube channel URL.
        - `telegram_url` string, nullable: The person's Telegram contact URL.
        - `viber_url` string, nullable: The person's Viber contact URL.
        - `whatsapp_url` string, nullable: The person's WhatsApp click-to-chat URL.
        - `contact_address` string, nullable: The person's street address, where discovered.
        - `contact_city` string, nullable: The person's city.
        - `contact_country` string, nullable: The person's country.
        - `contact_country_code` string, nullable: The person's country as an ISO-3166-1 alpha-2 code.
        - `contact_region` string, nullable: The person's sub-country region (e.g. state or province).
        - `registry_source` string, nullable: The government registry this person was verified against, when applicable.
        - `email_source` string, nullable: Provenance of `email` when it was INFERRED rather than directly discovered. `derived_domain_pattern` = a name-coupled mailbox reconstructed from the company's own observed local-part convention (confidence ~0.5, never verified); `micro_company_generic_pairing` = a sole owner paired to the company's generic mailbox. Absent (null) for a crawled/verified email. Use it to weight an inferred address (which carries bounce risk) before outreach.
        - `is_officer` boolean: True when this person matches a statutory officer listed in a government registry.
      - `phones` string[]: Company phone numbers.
      - `company_emails` string[]: General company email addresses (e.g. info@, sales@), as opposed to a specific person's.
      - `city_romanized` string, nullable: Latin-script transliteration of city (non-Latin source markets).
      - `state_romanized` string, nullable: Latin-script transliteration of state (non-Latin source markets).
      - `linkedin_url` string, nullable: The company's LinkedIn page URL.
      - `instagram_url` string, nullable: The company's Instagram profile URL.
      - `facebook_url` string, nullable: The company's Facebook page URL.
      - `twitter_url` string, nullable: The company's X (Twitter) profile URL.
      - `youtube_url` string, nullable: The company's YouTube channel URL.
      - `telegram_url` string, nullable: The company's Telegram channel or click-to-chat URL.
      - `viber_url` string, nullable: The company's Viber click-to-chat URL.
      - `whatsapp_url` string, nullable: The company's WhatsApp click-to-chat URL.
      - `weibo_url` string, nullable: The company's Weibo profile URL.
      - `douyin_url` string, nullable: The company's Douyin (Chinese TikTok) profile URL.
      - `wechat_id` string, nullable: WeChat account ID (WeChat has no public profile URL).
      - `qq_id` string, nullable: QQ account ID (QQ has no public profile URL).
      - `business_model` 'manufacturer' | 'retailer' | 'distributor' | 'service_provider' | 'unknown' | 'null', nullable: The company's classified business model — `manufacturer`, `retailer`, `distributor`, or `unknown`. Null when not classified.
      - `estimated_annual_revenue_usd` integer, nullable: Estimated annual revenue in whole USD dollars, derived from a model (based on employee count). Null when undetermined — never a fabricated zero.
      - `estimated_revenue_currency` string, nullable: ISO-4217 alpha-3 currency for the revenue estimate (currently always USD).
      - `founded_year` integer, nullable: Year the company was founded (company-maturity signal). Null when unknown.
      - `reported_revenue_usd` integer, nullable: Reported annual revenue in whole USD dollars, sourced from public company financials (distinct from the model-based `estimated_annual_revenue_usd`). Null when not reported.
      - `funding_total_usd` integer, nullable: Total capital raised to date in whole USD dollars, from public company financials. Null when not reported.
      - `funding_latest_round` object, nullable: Latest funding round (round name/type, date, amount, currency). Returned as a decoded object — the API decodes the stored JSON for you at the boundary, so do NOT decode a second time. The key set comes from the financial source and is not closed: read the keys you need and ignore the rest. Often null.
      - `web_traffic` object, nullable: Website-traffic snapshot. Returned as a decoded object — the API decodes the stored JSON for you at the boundary, so do NOT decode a second time. The properties below are the ones commonly present; the key set comes from the traffic source and is not closed, so read the keys you need and ignore the rest. Often null.
        - `visitsMonthly` number, nullable: Visits in the most recent complete month.
        - `countryRank` number, nullable: Traffic rank of the site within its own country (lower is busier).
        - `avgDurationSeconds` number, nullable: Average visit duration in seconds.
        - `history` object[], nullable: Month-by-month visit history, oldest period first.
          - `value` number, nullable: Visits in that period.
          - `dateFrom` string, nullable: Start of the period, `YYYY-MM`.
          - `dateTo` string, nullable: End of the period, `YYYY-MM`.
      - `hq_address` string, nullable: Full one-line formatted headquarters address (companion to city/country/postal).
      - `logo_url` string, uri, nullable: Company logo/image URL.
      - `source_published_date` string, nullable: Publication date of the web source this prospect was surfaced from (per-source freshness).
      - `source_last_updated` string, nullable: Last-updated date of the web source this prospect was surfaced from (per-source freshness).
      - `registry_id` string, nullable: The company's government registry identifier (e.g. IČO, HRB, KRS), where matched.
      - `registry_source` string, nullable: Which government registry `registry_id` came from.
      - `lei` string, nullable: Legal Entity Identifier (ISO 17442), where the company is present in the GLEIF register. Null otherwise.
      - `uscc` string, nullable: Chinese Unified Social Credit Code. Populated only for Chinese companies. Null otherwise.
      - `social_profiles` object[], nullable: Per-platform public social profiles discovered for the company, beyond the singular per-platform `*_url` fields (which carry only the headline platform). One entry per platform, returned as decoded objects — the API decodes the stored JSON for you at the boundary, so do NOT decode a second time.
        - `platform` string, required: Which public platform this entry is from, and the provenance for the rest of the entry's fields (e.g. `linkedin`, `youtube`, `twitter`, `instagram`).
        - `url` string, required: The public profile URL on that platform.
        - `follower_count` integer, nullable: Audience size on that platform (followers, or subscribers on video platforms). Null when the profile published no count.
        - `description` string, nullable: The company's own business summary as published on that platform, verbatim. Null when the profile published none.
        - `legal_name` string, nullable: Registered legal-entity name CANDIDATE taken from that profile's own title or summary. A candidate only — it never overrides the company name. Null when the profile published no distinct legal name.
        - `extra_links` string[], nullable: External links the profile itself lists (subsidiary, other-market or store domains). Null for platforms that publish no such block.
      - `employer_reputation_tier` 'strong' | 'mixed' | 'limited' | 'null', nullable: Coarse, un-attributed employer-reputation band — `strong`, `mixed`, or `limited`. A derived legitimacy/size-corroboration signal; NOT a third-party review platform's verbatim rating (which is never returned) and carrying NO source name. Null when no reputation signal is available.
      - `beian_number` string, nullable: China MIIT web-filing number (beian = the ICP备案 recordal, e.g. 湘ICP备17001547号) mined from the site footer. A MIIT-registered-commercial-site provenance signal — NOT a corporate registry id (registry_source is set to 'MIIT-BEIAN' when present).
      - `footer_legal_name` string, nullable: Market-agnostic registered legal-name candidate anchored in the page footer / legal-notice region. Two producers today: the CN beian (ICP备案)/copyright footer (Chinese …有限公司 or English … LIMITED/LTD) and the DE/AT §5 TMG Impressum legal name (…GmbH/AG). Verbatim — never translated. A candidate that never overwrites company_name. Which producer supplied it is on footer_legal_name_source.
      - `footer_legal_name_source` 'beian' | 'impressum' | 'copyright' | 'null', nullable: Where `footer_legal_name` was taken from — `beian` (Chinese MIIT web-filing footer), `impressum` (German/Austrian legal-notice footer), or `copyright` (a plain copyright line). Null when `footer_legal_name` is null.
      - `social_description` string, nullable: Business summary harvested from the prospect's public social profile page (no login). Winning-platform priority LinkedIn > YouTube > Twitter; the source platform is on social_source.
      - `social_follower_count` integer, nullable: Audience-size traction proxy from the winning social profile — LinkedIn follower count OR YouTube subscriber count (which one is on social_source).
      - `social_legal_name` string, nullable: Registered legal-entity name candidate from a social profile's og:title / og:description (never overwrites company_name; names never translated).
      - `social_source` 'linkedin' | 'youtube' | 'twitter' | 'null', nullable: Which public social profile the social intel above came from — `linkedin`, `youtube`, or `twitter`. Null when no social profile was found.
      - `social_extra_links` string[], nullable: YouTube channel About-tab external links (subsidiary / other-market / store domains). A YouTube-only signal.
      - `nace_code` string, nullable: The company's NACE industry-classification code, from its registry entry where available.
      - `legal_form` string, nullable: The company's legal form (e.g. GmbH, s.r.o., Ltd), from its registry entry where available.
      - `officers` string[], nullable: Names of the company's statutory officers, from its government registry entry where available.
      - `registration_date` string, nullable: Date the company was registered, from its registry entry where available.
      - `discovered_at` string, date-time, nullable: When this company was first discovered for you (ISO-8601).
      - `detected_language` string, nullable: ISO-639-1 language detected for the company's content.
      - `vat_id` string, nullable: VAT/tax identifier where available.
      - `detected_industry` string, nullable: Finer-grained detected industry (companion to the coarse `industry`). Drawn from the same canonical taxonomy listed under `industry` — including the specialty-retail categories (`florist_flowers`, `bakery_pastry`, `butcher_delicatessen`, `optician_eyewear`, `pharmacy_drugstore`, `pet_shop`, `toy_hobby`, `stationery_office_supplies`, `bookstore`, `garden_center`, `musical_instruments`, `hardware_diy`) — and likewise not a strict closed set.
      - `company_status` string, nullable: The company's operational status, where a government registry reports it (e.g. `active`, `liquidated`, `dissolved`). Free-text — not a fixed set.
      - `has_wikipedia_article` boolean, nullable: Notability / enterprise signal — a Wikipedia article was found.
      - `domain_age_days` integer, nullable: Age of the registered domain in days — company maturity/stability signal.
      - `branch_regions` string[], nullable: Registry-authoritative ISO-3166-2 branch-office regions (multi-location footprint).
      - `sub_country_region` string, nullable: The ISO-3166-2 sub-country region (e.g. `ES-VC`, `DE-BY`) the company's own headquarters geography places it in — resolved from its stored `state`, `city` and `postal_code`, so it can be checked against the address on the same record. It is the region a `sub_country_regions` discovery filter is matched against. Null when that geography does not resolve to a region; it is cleared again if a later correction to the address stops supporting it. A single HQ region, distinct from `branch_regions`.
      - `certifications` string[], nullable: Quality/compliance certifications the company publicly claims (e.g. ISO 9001:2015, IATF 16949, IPC-A-610, RoHS, REACH), extracted from the company's public web presence.
      - `customer_count` integer, nullable: Self-reported count of customers/clients served — a traction proxy (distinct from employee_count).
      - `export_markets` object, nullable: Export / market-distribution snapshot. Returned as a decoded object — the API decodes the stored JSON for you at the boundary, so do NOT decode a second time. Two shapes are emitted depending on what the source disclosed: `{"regions":{"Europe":48,...}}` (share per region) or `{"export_share_pct":80,"regions":[...]}` (headline share plus a region list). The key set is not closed. Often null.
      - `production_capacity` string, nullable: Manufacturing-capacity snapshot (e.g. "30,000 sq.m; 3,000 part numbers; 10+ SMT lines") — a capability signal for manufacturing/EMS prospects.
      - `served_industries` string[], nullable: Verticals/end-markets the company states it serves — a multi-value companion to detected_industry.
      - `branch_footprint` string[], nullable: Multi-site branch/office city names the company publicly lists (GeoNames-gated) — a human-readable companion to the ISO-3166-2 branch_regions.
      - `events` object[], nullable: Trade fairs, conferences and similar events the company itself publicly lists taking part in, within the last 24 months or the next 12 (at most 2, the ones closest to today). Each entry was verified against a quote on the company's own site; `source_url` is that page. Null when none is on record.
        - `name` string: The event's name as the company writes it.
        - `start_date` string: ISO yyyy-MM-dd start date.
        - `city` string, nullable: City the event takes place in.
        - `role` 'EXHIBITOR' | 'SPEAKER' | 'SPONSOR' | 'AWARD_WINNER' | 'ORGANISER' | 'OTHER' | 'PARTNER' | 'null', nullable: The company's role at the event.
        - `kind` 'TRADE_FAIR' | 'CONFERENCE' | 'SUMMIT' | 'WEBINAR' | 'AWARD_CEREMONY' | 'SHAREHOLDER_MEETING' | 'OWN_EVENT' | 'OTHER' | 'null', nullable: What kind of event it is (a trade fair, a conference or a summit survive the extractor's filter).
        - `source_url` string, nullable: The company page the participation was read from.
      - `origin_country` string, nullable: The company's inferred country of origin, as an ISO-3166-1 alpha-2 code. Helps distinguish a headquarters from a local subsidiary.
      - `facts` object[]: Extracted business facts about the company — leadership, products/services, market positioning, history, and certifications — each as a short natural-language statement WITH provenance (origin + when it was extracted) for SDR briefing. Present on every prospect; an empty array when no facts are available. Capped per prospect to keep the response bounded.
        - `text` string, required: The business fact, as a short natural-language statement.
        - `source` 'web' | 'wikidata' | 'wikipedia', required: Origin the fact was derived from — `web` (extracted from the company's public web content), `wikidata` (a structured Wikidata claim), or `wikipedia` (the resolved Wikipedia article).
        - `source_url` string, uri, nullable: Stable click-through link to the company HOMEPAGE — the entry point to the company's public web content. This is NOT necessarily the exact sub-page the fact was extracted from; the exact per-page source URL is not retained, so this field is the company homepage rather than an overclaimed extraction URL.
        - `extracted_at` string, date-time, nullable: ISO-8601 timestamp of when the fact was extracted.
        - `first_party` boolean: `true` when the fact is VERIFIED to come from the company's OWN web presence — a citation whose registrable (eTLD+1) domain equals the prospect's. `false` means "not verified as first-party", which covers TWO different situations and is never a positive claim of third-party origin: the fact was mined from a third-party page (a directory, aggregator, news site or filing, and note Wikipedia/Wikidata count as third-party here however well their entity was resolved), OR its provenance cannot be established because no citation host was ever stored for it. The second case is not marginal — MEASURED on the production corpus, 46% of facts carry no stored citation host at all; they are legacy points and the inspectable sample was model-derived rather than site-derived. A consumer must therefore read `false` as "unconfirmed", not as "independently sourced". Why it is customer-visible: a first-party claim is the company describing itself, while a third-party claim is someone else describing it, and an SDR should weigh a self-description differently from an independent one before quoting it. Third-party facts also carry the residual entity-confusion risk that first-party facts cannot: DNS ownership is collision-free, whereas a shared brand token, ticker or surname is not. Off-domain facts pass an entity-identity check, but that check can let a fact through when it is unavailable — which is exactly why the customer still gets to see the distinction. ABSENCE IS NOT `false`. The key is omitted on facts stored before it was introduced, and such a fact's provenance is UNKNOWN, not third-party — a consumer that defaults a missing value to `false` will silently mislabel every It is therefore deliberately NOT in `required`.
      - `knowledge_graph` unknown[], nullable: Extracted knowledge-graph facts about the company (relationship triples). Returned only by the detail endpoint (`GET /api/v1/prospects/{domain}`).
        - unknown
      - `knowledge_graph_grouped` object, nullable: The same knowledge-graph facts, grouped by relationship type for convenience. Returned only by the detail endpoint.
      - `pages` unknown[], nullable: References to the company web pages that were read during discovery. Returned only by the detail endpoint.
        - unknown
      - `semantic_score` number, nullable: Relevance score for this company against the search query. Returned only by the search endpoint (`GET /api/v1/prospects/search`).
    - `suggestions` Suggestion[]: Data-driven entries derived from THIS run. At most ONE leak-safe ADVICE recommendation (the single most impactful change) is emitted when the run underperformed (exhausted with fewer prospects than requested), each carrying a human-readable message and an optional `suggested_parameters` delta to merge over your original criteria and start a new discovery. In addition, ZERO OR MORE mandatory `FILTER_RELAXED` disclosures may appear on any terminal run when a requested filter (`company_sizes` or `sub_country_regions`) was relaxed so some returned companies fall outside it, each carrying a `suggested_parameters` clearing that filter. For a region-scoped run where no out-of-region company is yet provable (the region signal often finalizes after async enrichment), a single informational `REGION_PREFERENCE_APPLIED` note appears instead, carrying the same `{sub_country_regions: []}` correction. Empty when the run met target, no filter was relaxed, and no regional preference was requested.
      - `code` 'WIDEN_LOCATION' | 'ALLOW_LARGER_SIZE' | 'ALLOW_SMALLER_SIZE' | 'BROADEN_QUERY' | 'LIMITED_MARKET_DATA' | 'LOWER_TARGET' | 'icp_realignment' | 'TRANSIENT_INTERRUPTION' | 'PIPELINE_LIMITED' | 'ALREADY_DELIVERED' | 'FILTER_RELAXED' | 'REGION_PREFERENCE_APPLIED', required: Bounded code (NOT a rejection reason). The first seven values are advice codes; `FILTER_RELAXED` is a mandatory relaxation disclosure, `TRANSIENT_INTERRUPTION` advises a plain re-run after an infra stall, `PIPELINE_LIMITED` and `ALREADY_DELIVERED` are honest non-actionable outcomes that deliberately carry NO broadening advice, and `REGION_PREFERENCE_APPLIED` is an informational note that a `sub_country_regions` preference shaped this run.
      - `message` string, required: Customer-safe, human-readable recommendation or disclosure. Contains no internal rejection reasons or funnel counts.
      - `suggested_parameters` SuggestedCriteria: A partial `DiscoveryCriteria` delta. Merge it over your original request (the delta wins) and start a new discovery. Every field is optional; only the changed values are present. An empty array (e.g. `sub_country_regions: []`) explicitly clears that field. When the run under-delivered, an LLM improvement pass may also rewrite the free-text `query` itself — when present, `query` REPLACES your original `query` verbatim (it is not merged token-wise); apply it alongside the other fields to re-run the improved ICP.
        - `query` string: LLM-rewritten replacement for the free-text `query`, refined from this run's outcome to surface more matching companies. Present only when the improvement pass produced a better query; use it as-is in place of your original `query` on the re-run.
        - `locations` string[]: Suggested replacement for `locations` (e.g. a wider set of countries).
        - `company_sizes` string[]: Suggested replacement for `company_sizes`.
        - `sub_country_regions` string[]: Suggested replacement for `sub_country_regions`. An empty array clears the field.
        - `industries` string[]: Suggested replacement for `industries`.
        - `keywords` string[]: Suggested replacement for `keywords`.
        - `maxResults` integer: Suggested replacement for `maxResults`.
    - `circular_opportunity` CircularOpportunity: One graded circular-economy opportunity for your business. Individually runnable via `POST /api/v1/discoveries` with `opportunityHandle`.
      - `handle` string, required: Opaque id of this entry, scoped to your portfolio. Pass it to run the counterpart search.
      - `category_group` string, required: The kind of opportunity - materials recovery is one of several and is never a default. A bounded vocabulary; treat an unfamiliar value as opaque rather than as an error.
      - `mode` string, required: The shape of the search this entry becomes. Bounded vocabulary.
      - `direction` 'CUSTOMER_HOLDS' | 'CUSTOMER_SEEKS', required: `CUSTOMER_HOLDS` - you hold underutilized value and the search finds who wants it. `CUSTOMER_SEEKS` - you would source value another company holds.
      - `resource` CircularClaim, required: One claim, together with what may be DONE with it. `label` is presented verbatim in its original language and is never translated - it may carry a product, brand or material designation. `speech_act` is the machine-readable form of a rule that is a sentence on a chat surface: an `IMPLIED` claim is something we inferred about your business, not something you told us, so it may only ever be put to you as a QUESTION.
        - `label` string, required: The flow, asset, capacity, capability or counterpart type, in plain commercial language.
        - `grounding` 'STATED' | 'CONFIRMED' | 'IMPLIED', required: `STATED` - appears in your business profile. `CONFIRMED` - you corrected or confirmed it and we retained that. `IMPLIED` - inferred, and not yet confirmed by you.
        - `speech_act` 'ASSERT' | 'ASK', required: `ASSERT` for a stated or confirmed claim. `ASK` for an unconfirmed hypothesis, which must be phrased as a question about your business and must not enter outreach.
      - `counterpart` CircularClaim, required: One claim, together with what may be DONE with it. `label` is presented verbatim in its original language and is never translated - it may carry a product, brand or material designation. `speech_act` is the machine-readable form of a rule that is a sentence on a chat surface: an `IMPLIED` claim is something we inferred about your business, not something you told us, so it may only ever be put to you as a QUESTION.
        - `label` string, required: The flow, asset, capacity, capability or counterpart type, in plain commercial language.
        - `grounding` 'STATED' | 'CONFIRMED' | 'IMPLIED', required: `STATED` - appears in your business profile. `CONFIRMED` - you corrected or confirmed it and we retained that. `IMPLIED` - inferred, and not yet confirmed by you.
        - `speech_act` 'ASSERT' | 'ASK', required: `ASSERT` for a stated or confirmed claim. `ASK` for an unconfirmed hypothesis, which must be phrased as a question about your business and must not enter outreach.
      - `markets` string[]: ISO-3166 alpha-2 markets the counterpart search would cover.
      - `proximity_bound` boolean: True when the exchange only transacts over short distances (heat, CO2, water, bulky low-value material, shared capacity), so reachability constrains the search.
      - `grounding_sources` string[]: Customer-safe CATEGORIES the claims rest on - where and when, never who. Paired with the list's `computed_at`, these are the provenance of everything shown here.
      - `outreach_draft_eligible` boolean: Whether this entry may GROUND an outreach draft. False when the `resource` - the thing a message would be ABOUT - is an unconfirmed hypothesis; such an entry must not be used to compose outreach in any form, not even as a question, until the customer confirms it. True does NOT license asserting the `counterpart`: that is a hypothesis about a MARKET rather than about the customer, it carries its own `speech_act`, and on a true entry it is very often still `ASK`. Read both.
      - `requires_resource_confirmation` boolean: True when running this entry requires `confirmedResource` on the trigger, because its resource is an unconfirmed hypothesis.
      - `credits_cost` integer: What running the counterpart search costs, shown before anything is spent.

## Other responses

- `401`: Missing/invalid/expired Bearer token.
- `403`: The token's tenant does not own this job.
- `404`: Resource not found.
- `405`: HTTP method not allowed on this endpoint (rejected before any credit debit).
- `429`: Rate limit exceeded (100 requests / 60s per customer).

---

[API](https://skmtc.dev/prosp/apis/prosp-discovery-api.md) · [All operations](https://skmtc.dev/prosp/apis/prosp-discovery-api/llms.txt) · [OpenAPI document](https://skmtc.dev/prosp/apis/prosp-discovery-api/revisions/1f2e0c55da22?raw)
