Scan types
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).
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”.Run lifecycle
readyForExport is separate from status: a run can be COMPLETED but still need enrichment before export. Starter runs need Quick Take; deep runs need Quick Take, section briefings, Skeptic’s View, and both deferred Action Plan variants (building, hasUsers). The enrich route skips whatever already exists, so calling it twice never regenerates or double-spends.
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,evidencedetails.verdict-{ score, label, narrative, keyInsight, recommendation, confidence }. On deep reportskeyInsightis the report’s Bottom Line headline andnarrativeits verdict paragraph (labelcarries the same text asnarrative, kept for compatibility)details.market(TAM/SAM/SOM),details.competitors,details.swot,details.risks,details.validationdetails.quickTake,details.actionPlanVariants,details.citations(deduped{ title, url })details.cloneability- how replicable the idea is, and what defends it
details.cloneability is the one field whose shape changes with tier instead of going null.
Every tier gets { mode, verdict, teaserLine }: mode is "software" or "non_software",
verdict answers “can this be cloned” and is "YES", "KINDA" or "NOT_REALLY" - note the
polarity, "YES" is the bad outcome for the founder (trivially copyable) and "NOT_REALLY" is
the good one. teaserLine is a one-sentence summary. Deep runs get the same three fields plus the analysis behind them:
reasoning, cloneableParts, moatParts, mitigations ({ title, description }), and on
software runs comparableApps ({ name, verdict, note }, sometimes domain). Two optional
blocks appear when the model produced them: timeToClone ({ horizon, note }, where horizon is
"days", "weeks", "months" or "year_plus") and bigPlayerDefense
({ player, threat, defense }). 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.
details.validation.featureTriage is the scope call, and it is the field to build from: each entry
is { feature, decision, status, impact, effort, reason }, where decision normalizes the model’s
free-text status to "build", "defer", "cut" or null. Filter on decision === "build" for
the v1 surface and treat the rest as an explicit non-goals list - status is kept alongside so you
can always see the original label.
Every tier also carries details.situationBriefing (the overview narrative paragraph) and
details.scores (composite score plus the six framework subscores: lean canvas, TAM/SAM/SOM,
Porter, VC scorecard, risk factors, PMF signals).
Deep runs additionally get:
details.sections- business model (+ scenario B), execution fit, go-to-market, lean canvas, Porter forces, VC scorecard, PMF signals, financial projections, raise plan (stage-evidence gap + pricing anchors), pre-mortem, workflow evidence, display metrics, synthesis (with pivot recommendation), consistency review, section briefings, Skeptic’s Viewdetails.marketextras - segments, industry trends, competitive positioning, geographic focus, regulatory landscape (empty/nullon 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.communityDemanddetails.founderFit,details.playbook,details.proofOfDemand,details.googleTrends- once the modules have been generated
null. Exports are deterministic: the same run state always serializes identically.
With verbosity=summary, details.sections keeps exactly one subsection: synthesis, the report’s closing verdict (final verdict, key strengths and weaknesses, deal breakers, verdict flippers, pivot recommendation). Everything else in details.sections is dropped and listed under details.omitted.
Batches
POST /api/agent/analysis-batches accepts up to 10 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.