# Enrich an analysis Source: https://docs.preuve.ai/api-reference/analyses/enrich-an-analysis /api-reference/openapi.yaml post /api/agent/analyses/{id}/enrich Idempotently generates the missing core export sections for a COMPLETED run. Optionally starts deep-only modules via body.modules (one successful generation per module per report). founderFit requires founderProfile and runs inline; the other modules run async - poll get. Partial failures return HTTP 207 with a success-shaped body. # Export an analysis (ideas-json) Source: https://docs.preuve.ai/api-reference/analyses/export-an-analysis-ideas-json /api-reference/openapi.yaml get /api/agent/analyses/{id}/export Structured JSON export (schemaVersion 2) for one COMPLETED, export-ready analysis. Deep runs include full sections, pivots, citations, and generated module payloads. # Poll an analysis Source: https://docs.preuve.ai/api-reference/analyses/poll-an-analysis /api-reference/openapi.yaml get /api/agent/analyses/{id} Returns run status, readyForExport, enrichment progress, per-module statuses (deep runs), and report/share URLs. Accepts the run id or the report id. Does not return the report payload - use export. # Start an analysis Source: https://docs.preuve.ai/api-reference/analyses/start-an-analysis /api-reference/openapi.yaml post /api/agent/analyses Create one analysis run. Async - poll with GET /api/agent/analyses/{id}. scanType is required and explicit: "starter" costs nothing, "deep" consumes paid account quota. clientRunId is your idempotency key: re-POSTing the same value replays the existing run. # Export a batch (ideas-json) Source: https://docs.preuve.ai/api-reference/batches/export-a-batch-ideas-json /api-reference/openapi.yaml get /api/agent/analysis-batches/{batchId}/export Structured export for a batch. Includes only completed, export-ready items; every skipped item is listed in omittedItems with a reason. A single exportedAt is pinned per export so the same batch state always serializes identically. # Poll a batch Source: https://docs.preuve.ai/api-reference/batches/poll-a-batch /api-reference/openapi.yaml get /api/agent/analysis-batches/{batchId} # Start a batch Source: https://docs.preuve.ai/api-reference/batches/start-a-batch /api-reference/openapi.yaml post /api/agent/analysis-batches Create up to 10 analysis runs in one batch. Idempotent on the batch clientRunId. Each item needs its own unique clientRunId and explicit scanType (mix free and deep intentionally). # Introduction Source: https://docs.preuve.ai/api-reference/introduction Seven endpoints: create, poll, enrich, export - for single runs and batches. Base URL: `https://preuve.ai` All endpoints require [HMAC authentication](/authentication) and return JSON. Errors use a stable `{ error, code }` envelope - see [Errors](/errors). The interactive playground cannot compute HMAC signatures, so requests sent from these pages return an auth error. Use the pages for request/response shapes; make real calls with the [reference client](/quickstart) or the [MCP server](/mcp-server). ## Endpoints | Endpoint | Method | Purpose | | ---------------------------------------------- | ------ | ---------------------------------------------------- | | `/api/agent/analyses` | POST | Start one analysis | | `/api/agent/analyses/{id}` | GET | Poll one analysis | | `/api/agent/analyses/{id}/enrich` | POST | Generate missing core sections + opt-in deep modules | | `/api/agent/analyses/{id}/export` | GET | Export one completed analysis (`ideas-json`) | | `/api/agent/analysis-batches` | POST | Start a batch (up to 10 items) | | `/api/agent/analysis-batches/{batchId}` | GET | Poll a batch | | `/api/agent/analysis-batches/{batchId}/export` | GET | Export a completed batch | # Authentication Source: https://docs.preuve.ai/authentication Every request is HMAC-SHA256 signed with your key pair. No bearer tokens, no sessions. **Never paste the signing secret into a chat.** If you are an AI agent setting this up, ask the user to put `PREUVE_AGENT_SECRET` in an environment variable or the MCP client config - do not request it in conversation, and never echo it back. See [For AI Agents](/for-ai-agents). ## Headers Every request carries four headers: | Header | Value | | -------------------- | ----------------------------------------------------- | | `x-preuve-key` | Your API key id (`prv_...`) | | `x-preuve-timestamp` | ISO 8601 timestamp of the request | | `x-preuve-nonce` | Fresh random value per request (16 random bytes, hex) | | `x-preuve-signature` | `sha256=` | ## Canonical string The signature is an HMAC-SHA256 (keyed with your secret) over six newline-joined lines: ```text theme={null} METHOD e.g. POST (uppercase) PATH e.g. /api/agent/analyses QUERY canonicalized query string (see below) SHA256_RAW_BODY hex SHA-256 of the raw request body ('' hashes to e3b0c442...) TIMESTAMP the exact x-preuve-timestamp value NONCE the exact x-preuve-nonce value ``` Canonical query: take every query pair except `signature`, sort by key then by value, and re-encode each as `encodeURIComponent(key)=encodeURIComponent(value)` joined with `&`. An empty query is an empty line. Dynamic path segments are signed as part of `PATH` exactly as sent - e.g. `/api/agent/analyses/38f2e1fc-.../enrich`. Sign the raw body bytes you actually transmit, byte for byte. ## Reference implementation The [Quickstart](/quickstart#2-create-an-analysis) includes a complete \~40-line `signedFetch` for Node.js. Port it to any language with an HMAC-SHA256 primitive; the canonical string above is the whole contract. ## Validity rules * **Clock skew**: timestamps outside the accepted window are rejected. Keep the client clock NTP-synced and generate the timestamp at send time. * **Nonce**: use a fresh nonce per request; replayed nonces are rejected. * **Scopes**: keys carry scopes (`analysis:write`, `export:read`, ...). A key without the required scope receives `403`. * **Failed auth** always returns a stable `{ "error", "code" }` JSON envelope - see [Errors](/errors). The API playground in these docs cannot compute HMAC signatures, so "Send" from the browser will return an auth error. Use it to explore request and response shapes; make real calls with the reference client or the [MCP server](/mcp-server). # Errors Source: https://docs.preuve.ai/errors Every error is a stable JSON envelope with a machine-readable code. All errors - including unexpected server errors - return: ```json theme={null} { "error": "Human-readable message", "code": "MACHINE_READABLE_CODE" } ``` Some errors add context fields (`details`, `limit`, `tokenBalance`, `resetAt`, ...). The `code` values are stable; branch on them, not on messages. ## Common codes | HTTP | Code | Meaning | What to do | | ---- | ----------------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------------- | | 400 | `INVALID_MODULES` | Unknown or malformed `modules` array | Fix the module names | | 400 | `INVALID_FOUNDER_PROFILE` | Missing/invalid `founderProfile` for `founderFit` | Fix the fields listed in `details` | | 400 | `INVALID_ANALYSIS_ID` | Malformed run/report id | Use the id returned by create | | 401 | (auth codes) | Bad key, signature, timestamp, or nonce | Check [Authentication](/authentication); sync your clock | | 402 | `INSUFFICIENT_TOKENS` | Deep scan with no quota and no tokens | Top up or use `scanType: "starter"` | | 403 | `MODULES_REQUIRE_DEEP` | Modules requested on a non-deep report | Run a deep scan first | | 403 | `STARTER_LIMIT_REACHED` | Starter scan allowance exhausted (free/regional) | Wait for reset or run deep | | 429 | `FAIR_USE_LIMIT` | Paid account hit the starter fair-use ceiling | Contact support to lift it | | 404 | `ANALYSIS_NOT_FOUND` | Unknown id, or the run belongs to another account | Check the id | | 409 | `ANALYSIS_NOT_COMPLETE` | Run still processing | Keep polling | | 409 | `ENRICHMENT_NOT_COMPLETE` | Export requested before enrichment | Call enrich, poll, retry | | 409 | `MODULE_ALREADY_GENERATED` | Per-module cap reached (one success per report) | Read the payload from the export | | 413 | `PAYLOAD_TOO_LARGE` | Body over the size limit | Trim the request | | 422 | `ANALYSIS_FAILED` | The analysis itself failed | Create a new run | | 429 | `RATE_LIMITED` | Per-route rate limit hit | Back off and retry | | 429 | `CONCURRENT_LIMIT_REACHED` | Too many analyses in flight (3 free / 2 deep) | Wait for one to finish, re-POST the same `clientRunId` | | 429 | `DAILY_LIMIT_REACHED` | Rolling 24h creation ceiling | Retry after `resetAt` | | 429 | `REGEN_LIMIT_REACHED` | Founder Fit lifetime cap on this report | No retry - cap is final | | 503 | `INSUFFICIENT_TIME_BUDGET` | Inline module refused to start late in the request | Retry the same enrich call | | 503 | `SERVICE_DISABLED` / `GENERATION_DISABLED` / `POD_DISABLED` | Feature temporarily off | Retry later | | 502 | `ENQUEUE_FAILED` | Background dispatch failed | Retry; nothing was consumed | | 500 | `INTERNAL_ERROR` | Unexpected server error | Retry with backoff | ## Partial success (207) Enrich returns HTTP `207` with a **success-shaped** body when some sections or modules failed while others succeeded. Inspect `failures` (core sections) and the per-module `modules` map, then retry the failed parts - the route is idempotent and never regenerates what already completed. # For AI Agents Source: https://docs.preuve.ai/for-ai-agents How agents should read these docs, call the API, and behave with a Preuve key. Guidance rules, retry semantics, and safety. This page is written for the agent itself: if you are an AI agent working with the Preuve Agent API or MCP server, follow these rules. ## Reading these docs programmatically * Every docs page has a Markdown twin: append `.md` to its URL (e.g. `https://docs.preuve.ai/quickstart.md`). * The full docs index lives at [`/llms.txt`](https://docs.preuve.ai/llms.txt); the complete content in one file at [`/llms-full.txt`](https://docs.preuve.ai/llms-full.txt). * This docs site is itself an MCP server: connect `https://docs.preuve.ai/mcp` from any MCP client to search these docs. * The OpenAPI spec is at [`/api-reference/openapi.yaml`](https://docs.preuve.ai/api-reference/openapi.yaml). ## Agent guidance rules 1. **Prefer the [MCP server](/mcp-server)** when it is connected - it handles HMAC signing for you. Fall back to the [reference client](/quickstart) for raw HTTP. 2. **`scanType` is always an explicit choice.** `"starter"` costs nothing; `"deep"` consumes the user's paid quota. Never run a deep scan the user did not ask for. 3. **Reuse the same `clientRunId` to retry.** Retries never double-spend; a run that already started returns its stored outcome instead of running again. 4. **Poll, don't hammer.** Starter scans complete in about a minute, deep scans in about eight. Poll every 10-30 seconds and back off on `429` - `RATE_LIMITED` and `CONCURRENT_LIMIT_REACHED` are retryable with the same `clientRunId`. 5. **Branch on `code`, not on error messages.** Every error is a stable `{ error, code }` envelope - see [Errors](/errors). 6. **Do not expose secrets in chat.** Never ask the user to paste `PREUVE_AGENT_SECRET` into a conversation, and never echo it back. Prefer environment variables or the MCP server config. 7. **Do not pass `publish: true` unless the user explicitly wants a public share link.** Runs are private by default. 8. **Deep modules are capped at one successful generation per module per report.** A `409 MODULE_ALREADY_GENERATED` means the payload already exists - read it from the export instead of retrying. ## What a key can and cannot do A Preuve API key is a scoped analysis credential, not an account login. | Can | Cannot | | ------------------------------------------------------------ | ------------------------------------------- | | Create starter and deep analysis runs for the owning account | Log into preuve.ai or read the dashboard | | Poll, enrich, and export runs **it created** | See reports the user ran in the web app | | Start deep modules on its own deep reports | Change plans, buy tokens, or manage billing | | Create public share links (only with explicit `publish`) | Create or revoke API keys | If a key leaks, the user revokes it; the account itself is untouched. ## Recommended agent workflow ```text theme={null} 1. start_analysis { clientRunId, scanType: "starter", idea } 2. get_analysis until status = COMPLETED (poll every 10-30s) 3. export_analysis -> ideas-json (scores, verdict, competitors, risks, citations) 4. Only if the user asks for the full report: start_analysis with scanType: "deep", then enrich_analysis (+ modules), then export. ``` # Overview Source: https://docs.preuve.ai/index Validate startup ideas programmatically. The Preuve Agent API lets CLIs, agents, and automations run full viability analyses and pull structured JSON. The Preuve Agent API lets a CLI, an AI agent, an MCP client, or any automation create a Preuve analysis, poll it, generate the export-ready sections, and fetch structured `ideas-json` for downstream workflows - the same analysis engine that powers [preuve.ai](https://preuve.ai), without a browser session. **Early access beta.** The endpoint contract and error codes are stable, but rough edges are possible. Two guarantees hold throughout the beta: a run that fails before analysis starts refunds whatever quota it claimed, and `clientRunId` retries never double-spend. ## What you can build Score a backlog of startup ideas in batch (up to 10 per batch) and rank them by viability score. Let Claude, Cursor, or any MCP client validate ideas mid-conversation through the Preuve MCP server. Run paid deep analyses with 15+ sections, pivots, citations, and opt-in modules (Proof of Demand, Founder Fit, Playbook, Trends). Export stable, versioned JSON (`schemaVersion: 2`) built for programmatic consumption. ## How it works `POST /api/agent/analyses` with your idea and an explicit `scanType` (`starter` or `deep`). Runs are async. `GET /api/agent/analyses/:id` until `status` is `COMPLETED` and `readyForExport` is `true`. If `readyForExport` is `false`, `POST /api/agent/analyses/:id/enrich` generates the missing sections idempotently. Deep runs can opt into extra modules here. `GET /api/agent/analyses/:id/export?format=ideas-json` returns the structured result. ## Access API keys are issued on request while the Agent API is in early access. [Request a key](https://preuve.ai/mcp#get-access) or reach out at [vincent@preuve.ai](mailto:vincent@preuve.ai). Every request is HMAC-signed - see [Authentication](/authentication). The API consumes your existing Preuve account quotas (tokens, lifetime scans, subscription scans). There is no separate API billing. See [Quotas and billing](/quotas-and-billing). # MCP Server Source: https://docs.preuve.ai/mcp-server Use Preuve from Claude, Cursor, or any MCP client. Seven tools, stdio transport, two commands to set up. The Preuve MCP server wraps the Agent API 1:1 over stdio. Once connected, your agent can start analyses, poll them, enrich, run deep modules, and export - all from conversation. ## Setup (Claude Code) During early access the server script (`preuve-mcp-server.mjs`, a single Node file) is provided with your API key. Register it: ```bash theme={null} claude mcp add preuve \ --env PREUVE_AGENT_KEY=prv_... \ --env PREUVE_AGENT_SECRET=prv_sk_... \ -- node /path/to/preuve-mcp-server.mjs ``` Any MCP client that speaks stdio works the same way: run the script with the two environment variables set. An npm package is planned for general availability. ## Or let your agent install it Paste this into Claude Code, Cursor, or Codex and let the agent do the setup: ```text theme={null} Set up the Preuve MCP server for me. 1. I have three things from the Preuve early-access email: an API key (prv_...), a signing secret (prv_sk_...), and a file called preuve-mcp-server.mjs. 2. Ask me for the path to preuve-mcp-server.mjs. Do NOT ask me to paste the secret into chat - tell me where to put it as an environment variable, or register it directly in the MCP client config. 3. Register the server (Claude Code example): claude mcp add preuve \ --env PREUVE_AGENT_KEY= \ --env PREUVE_AGENT_SECRET= \ -- node /preuve-mcp-server.mjs 4. Verify: list the MCP tools and confirm the seven preuve tools appear (start_analysis, get_analysis, enrich_analysis, export_analysis, create_batch, get_batch, export_batch). 5. Run a smoke test with a STARTER scan only (scanType: "starter" - it costs nothing): start_analysis on the idea "test idea for MCP setup", poll it with get_analysis, and show me the viability score when it completes. Reference docs: https://docs.preuve.ai/mcp-server.md and https://docs.preuve.ai/for-ai-agents.md ``` ## Tools | Tool | What it does | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------- | | `start_analysis` | Create one run. Requires explicit `scanType` (`free`/`deep`) so agents never spend paid quota by accident. | | `get_analysis` | Poll a run: status, `readyForExport`, enrichment progress, per-module statuses, report/share URLs. | | `enrich_analysis` | Idempotently generate missing core sections; optionally start deep modules (`proofOfDemand`, `founderFit`, `playbook`, `trends`). | | `export_analysis` | Structured `ideas-json` export (schemaVersion 2) for one completed run. | | `create_batch` | Up to 10 runs in one batch, idempotent on the batch `clientRunId`. | | `get_batch` | Per-item statuses for a batch. | | `export_batch` | Batch export with `counts.exported/omitted` and per-item omission reasons. | ## Example prompts ```text theme={null} Validate this idea with a starter scan: "AI bookkeeping for solo lawyers in France" Run a deep scan on my top idea, then generate the launch playbook and proof of demand once it completes. Batch-score these 12 ideas and rank them by viability score. ``` Deep scans and deep modules consume your account quota exactly like the web app - the MCP server adds no billing of its own. See [Quotas and billing](/quotas-and-billing). # Deep Modules Source: https://docs.preuve.ai/modules Opt-in analysis modules on paid deep runs: Proof of Demand, Founder Fit, Playbook, and Trends. Deep (paid) analyses can generate four optional modules through the enrich endpoint. Pass `modules` in the body of `POST /api/agent/analyses/:id/enrich`: ```json theme={null} { "modules": ["playbook", "proofOfDemand", "trends"] } ``` | Module | What it generates | Execution | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | | `proofOfDemand` | **Beta.** Verified real-demand prospects: real people publicly expressing the pain or asking for the solution, with verbatim quotes. Results can be uneven; every quote links to its source thread | Async | | `founderFit` | Founder-vs-plan underwriting from your `founderProfile`: fit score, top risks, pivot fit | Inline (up to \~90s) | | `playbook` | A concrete launch playbook for the idea | Async | | `trends` | Google Trends data for the idea's keywords, generated on demand (the analysis itself never fetches it) | Async | ## Rules * **Deep only.** Requesting modules on a free run returns `403 MODULES_REQUIRE_DEEP`. The analysis must be `COMPLETED` (`409 ANALYSIS_NOT_COMPLETE` otherwise). * **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 `202` with `state: "generating"`. Poll `GET /api/agent/analyses/:id`: its `modules` map reports `not_generated | generating | failed | completed` per module. A generation already started elsewhere (for example by the report owner in the web app) returns `202` with `alreadyStarted: true` - the API and the web app can never double-generate. * Generated payloads land in the export as `details.founderFit`, `details.playbook`, and `details.proofOfDemand`, and in the report's web view. ## Founder Fit `founderFit` requires a `founderProfile` in the same request: ```json theme={null} { "modules": ["founderFit"], "founderProfile": { "hoursPerWeek": 20, "runwayMonths": 6, "domainYears": 3, "shippedBefore": "side_project", "audienceSize": "under_1k", "teamStatus": "solo", "twelveMonthGoal": "replace_income", "sabotagePattern": "I rebuild the landing page instead of talking to users" } } ``` | Field | Type | Values | | ----------------- | ---------------- | ---------------------------------------------------------------------- | | `hoursPerWeek` | number | 1-168 | | `runwayMonths` | number | 0-120 | | `domainYears` | number | 0-60 | | `shippedBefore` | enum | `never`, `side_project`, `launched` | | `audienceSize` | enum | `none`, `under_1k`, `1k_10k`, `over_10k` | | `teamStatus` | enum | `solo`, `cofounded_friends`, `cofounded_colleagues`, `team_with_hires` | | `twelveMonthGoal` | enum, optional | `replace_income`, `side_income`, `vc_scale` | | `sabotagePattern` | string, optional | Max 280 chars | 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. # Quickstart Source: https://docs.preuve.ai/quickstart Run your first analysis in five minutes. ## 1. Get your credentials API keys are issued on request during early access ([request a key](https://preuve.ai/mcp#get-access)). You receive two values: ```bash theme={null} export PREUVE_AGENT_KEY=prv_... export PREUVE_AGENT_SECRET=prv_sk_... ``` The secret is shown once at issuance and signs every request. Store it in a secret manager; never commit it. ## 2. Create an analysis Every request is HMAC-signed (see [Authentication](/authentication)). The fastest path is the reference client below - it signs for you. ```javascript signedFetch.mjs theme={null} import { randomBytes, createHash, createHmac } from 'node:crypto'; const BASE_URL = process.env.PREUVE_AGENT_BASE_URL || 'https://preuve.ai'; function sha256Hex(value) { return createHash('sha256') .update(value || '') .digest('hex'); } function canonicalQuery(searchParams) { const pairs = [...searchParams.entries()].filter(([k]) => k !== 'signature'); pairs.sort((a, b) => a[0].localeCompare(b[0]) || a[1].localeCompare(b[1])); return pairs.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&'); } export async function signedFetch(method, path, bodyObject = null) { const url = new URL(path, BASE_URL); const body = bodyObject ? JSON.stringify(bodyObject) : ''; const timestamp = new Date().toISOString(); const nonce = randomBytes(16).toString('hex'); const canonical = [ method.toUpperCase(), url.pathname, canonicalQuery(url.searchParams), sha256Hex(body), timestamp, nonce, ].join('\n'); const signature = `sha256=${createHmac('sha256', process.env.PREUVE_AGENT_SECRET) .update(canonical) .digest('hex')}`; const response = await fetch(url, { method, headers: { 'content-type': 'application/json', 'x-preuve-key': process.env.PREUVE_AGENT_KEY, 'x-preuve-timestamp': timestamp, 'x-preuve-nonce': nonce, 'x-preuve-signature': signature, }, body: body || undefined, }); return response.json(); } ``` ```javascript createRun.mjs theme={null} import { signedFetch } from './signedFetch.mjs'; const run = await signedFetch('POST', '/api/agent/analyses', { clientRunId: 'my-run-001', scanType: 'starter', idea: 'A scheduling assistant that helps independent coffee shops coordinate weekly local supplier orders with lower waste.', targetMarket: 'Independent coffee shops', targetCountry: 'US', enrichmentMode: 'core', publish: true, }); console.log(run.id, run.status); ``` `scanType` is required and explicit so an agent can never spend a paid deep scan by accident: `"starter"` runs the basic analysis at no cost, `"deep"` runs the full paid analysis and consumes account quota. `clientRunId` is your idempotency key - re-POSTing the same value replays the existing run instead of creating a new one. ## 3. Poll until complete ```javascript theme={null} const status = await signedFetch('GET', `/api/agent/analyses/${run.id}`); // { id, reportId, status: 'PROCESSING' | 'COMPLETED' | 'FAILED', readyForExport, enrichment, reportUrl, shareUrl } ``` Starter runs typically complete in 1-3 minutes, deep runs in 5-10. Poll every 10-15 seconds. ## 4. Enrich, then export ```javascript theme={null} if (status.status === 'COMPLETED' && !status.readyForExport) { await signedFetch('POST', `/api/agent/analyses/${run.id}/enrich`, {}); // idempotent - skips already-completed sections; poll again afterwards } const result = await signedFetch('GET', `/api/agent/analyses/${run.id}/export?format=ideas-json`); console.log(result.idea.score, result.idea.verdict); ``` The export is stable JSON (`schemaVersion: 2`): scores, verdict, market, competitors, SWOT, risks, quick take, action plans, and citations on every tier; full paid sections, pivots, and module payloads on deep runs. See [Scans and enrichment](/scans-and-enrichment). ## Next steps Set up the Preuve MCP server in two commands. Score up to 10 ideas per batch with one export. Proof of Demand, Founder Fit, Playbook, and Trends on demand. What each call consumes, and every safety net. # Quotas and Billing Source: https://docs.preuve.ai/quotas-and-billing The API consumes your existing Preuve account quotas. No separate API billing. The Agent API is an access channel, not a separate product: every call draws on the same account balance and quotas as the web app. ## What each call consumes | Call | Consumes | | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `scanType: "starter"` run | Never charged. Free-tier and regional accounts: one scan from the Starter scan allowance. Paid accounts (Founder, Lifetime, premium): unlimited, under a silent fair-use ceiling | | `scanType: "deep"` run | See the waterfall below | | Enrich (core sections) | Nothing extra - included in the run | | Deep modules | Nothing extra - included in the paid deep report (capped at one successful generation per module) | | Poll / export | Nothing | ## Deep scan quota waterfall A deep run claims quota in this order: 1. **Active subscription** (Radar Pro, etc.) - decrements your monthly subscription scan counter. 2. **Lifetime plan** (Lifetime Pro / Business) - decrements your monthly lifetime scan counter (10 or 20 scans/month). When the monthly counter is exhausted, the run falls back to tokens if you have any. 3. **Tokens** - deducts 1 token from your balance, recorded in the transaction ledger. No quota and no tokens returns `402 INSUFFICIENT_TOKENS` - the run fails before any analysis starts, and nothing is charged. ## Safety nets * **Automatic refunds**: if a run fails before the analysis actually starts (service disabled, dispatch failure, ...), whatever it claimed - token, lifetime scan, subscription scan, Starter scan - is refunded. * **Idempotent retries**: re-POSTing a `clientRunId` whose run failed before dispatch retries it without double-spending. A run that started always returns its stored outcome. * **Explicit `scanType`**: agents cannot spend paid quota by accident; deep is always an explicit choice. * **Failed modules never consume the per-module cap**; only a successful generation does. ## Rate limits | Limiter | Limit | | -------------- | -------------------------------------------------------------------------------------------- | | Run creation | 10/min per key | | Concurrency | 3 starter / 2 deep analyses in flight per account (single runs; batches are bounded by size) | | Batch creation | 10/min per key | | Enrich | 12/min per key + analysis | | Daily ceiling | Rolling 24h cap on run creation per key (`429 DAILY_LIMIT_REACHED` with the `limit` field) | Rate limits key on the API key, not the caller IP, so IP rotation does not widen any window. # Scans and Enrichment Source: https://docs.preuve.ai/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). 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 ```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. 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`, `evidence` * `details.market` (TAM/SAM/SOM), `details.competitors`, `details.swot`, `details.risks`, `details.validation` * `details.quickTake`, `details.actionPlanVariants`, `details.citations` (deduped `{ title, url }`) 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, synthesis (with pivot recommendation), consistency review, section briefings, Skeptic's View * `details.pivots` - `{ suggestions, generatedAt }` * `details.communityDemand` * `details.founderFit`, `details.playbook`, `details.proofOfDemand` - 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. ## 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`.