modules in the body of POST /api/agent/analyses/:id/enrich:
Rules
- Deep only. Requesting modules on a free run returns
403 MODULES_REQUIRE_DEEP. The analysis must beCOMPLETED(409 ANALYSIS_NOT_COMPLETEotherwise). - One successful generation per module per report. A module already generated returns
409 MODULE_ALREADY_GENERATED. A failed generation never consumes the cap - retry freely. - Async modules return
202withstate: "generating". PollGET /api/agent/analyses/:id: itsmodulesmap reportsnot_generated | generating | failed | completedper module. A generation already started elsewhere (for example by the report owner in the web app) returns202withalreadyStarted: true- the API and the web app can never double-generate. - Generated payloads land in the export as
details.founderFit,details.playbook, anddetails.proofOfDemand, and in the report’s web view.
Founder Fit
founderFit requires a founderProfile in the same request:
A missing or invalid profile returns
400 INVALID_FOUNDER_PROFILE with per-field details. Founder Fit runs inline and returns state: "completed" in the same response on success. Two extra guards:
503 INSUFFICIENT_TIME_BUDGET(retryable): the request spent too much time on core enrichment first. Call enrich again - core is now done, so the retry has the full time budget.429 REGEN_LIMIT_REACHED: the report’s lifetime Founder Fit generation cap (shared with the web app) is exhausted.
Trends
trends is on-demand by design: the analysis pipeline never fetches Google Trends itself. Startable when the data is absent or a previous fetch failed; successful data is final (409 MODULE_ALREADY_GENERATED) - it is never re-fetched for freshness.
Once generated, the data lands in the export as details.googleTrends: fetchedAt, timeRange, and per-keyword { keyword, averageVolume, growthPercent, peakMonth, dataPoints }.
Community
community is on-demand by design too: the deep analysis pipeline never generates community signals itself. This module - or the “Analyze Community Signals” button in the web report - is the only way to get them. It takes about 90 seconds and shares its in-flight lock with that button: if a refresh is already running, calling the module returns 202 with alreadyStarted: true instead of starting a second one.