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

# Scans and Enrichment

> Starter vs deep scans, the async run lifecycle, and what the ideas-json export contains.

## Scan types

| | `scanType: "starter"` | `scanType: "deep"` |
| - | - | - |
| Cost | No charge (Starter scan allowance) | Consumes paid account quota |
| Depth | Core viability analysis + score | 15+ sections, pivots, citations, full research |
| Typical duration | 1-3 min | 5-10 min |
| Export | Summary contract | Summary + `details.sections`, `details.pivots`, module payloads |
| Modules | Not available (`403 MODULES_REQUIRE_DEEP`) | Opt-in via [enrich](/modules) |

`scanType` is always explicit - there is no default. A refused deep scan is never silently downgraded to starter; it fails loudly (`402 INSUFFICIENT_TOKENS` or a service-disabled error).

<Note>
  While a deep run is still `PROCESSING`, `analysisTier` reads `basic` until the deep sections land.
  Use `scanType` (your request) as the in-flight signal and `analysisTier` as "is the deep result
  ready yet".
</Note>

## Run lifecycle

```text theme={null}
POST /analyses            -> { id, status: PROCESSING }
GET  /analyses/:id        -> PROCESSING ... COMPLETED (or FAILED)
POST /analyses/:id/enrich -> generates missing export sections (idempotent)
GET  /analyses/:id/export -> ideas-json
```

`readyForExport` is separate from `status`: a run can be `COMPLETED` but still need enrichment before export. The enrich route skips whatever already exists, so calling it twice never regenerates or double-spends.

One exception: on a report created on the preuve.ai dashboard, `readyForExport` is always `false` and enrichment will not fix it. Those responses also carry `exportable: false` and `exportBlockedCode: "REPORT_NOT_FROM_AGENT_RUN"`. Poll them here. For the data, relay `exportBlockedHint`: it names the way forward for that report, which may be Build with AI on its report page (its build kit holds the whole report as `research/full-report.md`) or, when Build with AI is not available for it, unlocking or re-running it there. Scan depth alone does not decide that, so do not infer it from `scanType`. A fresh deep analysis started here always works.

Partial enrichment failures return HTTP `207` with a success-shaped body and a `failures` list - retry by calling enrich again.

## The ideas-json export (schemaVersion 2)

Every tier gets the summary contract:

* `verdict`, `score`, `risk`, `quote`, `evidence`
* `details.verdict` - `{ score, label, narrative, keyInsight, recommendation, confidence }`. `label` carries the same text as `narrative`, kept for compatibility
* `details.market` (`trend` on every tier; `tam`/`sam`/`som`/`growthRate` on deep reports only, `null` and named under `locked.fields` on a starter scan, which has no research step behind them), `details.competitors`, `details.swot`, `details.risks`, `details.validation`
* `details.quickTake`, `details.situationBriefing` (string), `details.actionPlanVariants`
* `details.citations` - deduped `{ title, url }`
* `details.scores` - `compositeScore` plus per-framework subscores
* `details.cloneability` - see below

### `details.cloneability`

The one field whose shape changes with tier instead of going `null`. Branch on the fields you need rather than on tier: `null` means the run has no cloneability section at all, which is normal for analyses created before this field existed.

| Field | Type | Tier |
| - | - | - |
| `mode` | `"software"` \| `"non_software"` | every |
| `verdict` | `"YES"` \| `"KINDA"` \| `"NOT_REALLY"` - `"YES"` means trivially copyable | every |
| `teaserLine` | string | every |
| `reasoning` | string | deep |
| `cloneableParts`, `moatParts` | string\[] | deep |
| `mitigations` | `{ title, description }[]` | deep |
| `comparableApps` | `{ name, verdict, note, domain? }[]` | deep, `software` mode |
| `timeToClone` | `{ horizon: "days" \| "weeks" \| "months" \| "year_plus", note }`, optional | deep |
| `bigPlayerDefense` | `{ player, threat, defense }`, optional | deep |

### `details.validation.featureTriage`

Each entry is `{ feature, decision, status, impact, effort, reason }`. `decision` normalizes the model's free-text `status` to `"build"`, `"defer"`, `"cut"` or `null`; `status` is kept alongside so the original label stays readable.

## Deep-only fields

* `details.sections` - business model, execution fit, go-to-market, lean canvas, Porter forces, VC scorecard, PMF signals, financial projections, raise plan, pre-mortem, workflow evidence, display metrics, synthesis, consistency review, section briefings, Skeptic's View
* `details.market` extras - `segments`, `industryTrends`, `competitivePositioning`, `geographicFocus`, `regulatoryLandscape` (empty or `null` on starter exports)
* Full competitor cards - funding, estimated users, market share, activity level, threat level, scale tier and evidence, strengths, exploit tactics (starter exports keep `name` + `url` only)
* `details.pivots` - `{ suggestions, generatedAt }`
* `details.communityDemand`
* `details.founderFit`, `details.playbook`, `details.proofOfDemand`, `details.googleTrends` - once the [modules](/modules) have been generated

On starter runs all deep-only fields are `null`. Exports are deterministic: the same run state always serializes identically.

With `verbosity=summary`, `details.sections` keeps exactly one subsection, `synthesis`. Everything else in `details.sections` is dropped and listed under `details.omitted`.

## Batches

`POST /api/agent/analysis-batches` accepts up to 5 items, each with its own `clientRunId` and `scanType` (mix starter and deep intentionally). The batch export includes only completed, export-ready items, plus `omittedItems` explaining every skipped item and aggregate `counts.exported` / `counts.omitted`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.