DECA Labs — Public API

A read + trigger HTTP API for consuming the ideas this engine generates, the research spine behind them (validations, themes, demand signals, gaps, change events, hypotheses, Issues), and kicking off new autonomous runs. Designed for server-to-server use by trusted platforms.

Base URL
https://YOUR_HOST/api/public/v1

All endpoints below are relative to this base. Authenticate with a bearer token (see Authentication).

Authentication

Every request must include a bearer token in the Authorization header. The same key authorizes reading ideas and triggering runs, so treat it as a secret with write power.

Authorization: Bearer YOUR_API_KEY

A missing or wrong key returns 401 unauthorized. If the server has no key configured at all, every endpoint returns 503 api_not_configured (fails closed).

GET/api/public/v1/ideas

Returns a filtered, paginated list of generated ideas, newest first (last_updated descending) unless you sort or search. Filter by market size, scores, and ICP; search full text with q. Use updated_since to poll incrementally — store the largest last_updated you've seen and pass it back next time.

Query parameters — sync, status & pagination
ParameterTypeDefaultDescription
updated_sinceISO 8601Only ideas changed strictly after this timestamp. Invalid value → 400.
statusenumactiveactive | archived | all.
surfacestringMatch a surface by id (e.g. SURF-019; legacy OPP-019 also accepted) or by name substring.
themestringMatch a linked research theme by exact slug (e.g. T012-agentic-commerce) or by name substring. Ideas inherit their parent surface's themes.
hypothesisstringOnly ideas riding this strategic hypothesis (HYP-004; unpadded HYP-4 accepted). Matches the direct link, the idea's thesis tags, and the parent surface's links — see /hypotheses for the ledger.
has_deep_divebooltrue = only ideas whose deep_dive is non-null (own or surface-inherited pack); false = only ideas without one.
first_seen_sinceISO 8601Only ideas first discovered strictly after this timestamp (vs. updated_since, which tracks any change).
limitint50Page size, clamped to 1–200.
offsetint0Pagination offset (≥ 0).
Market size (USD)

Values are absolute dollars, with optional k / m / b / t suffix (case-insensitive): 500k, 20m, 1.5b, 2t.

ParameterTypeDefaultDescription
min_tam / max_tamUSDFloor / ceiling on total addressable market.
min_sam / max_samUSDFloor / ceiling on serviceable addressable market.
min_som / max_somUSDFloor / ceiling on serviceable obtainable market.
min_cagrnumberFloor on 5-year CAGR (%). Negative allowed.
min_ltv_cacratio ≥ 0Floor on LTV:CAC (e.g. 3). LTV is the stored value, else unit margin × customer lifetime; ideas missing LTV or CAC are excluded.
Score floors
ParameterTypeDefaultDescription
min_conviction0–100Floor on conviction (backend-computed blend).
min_attention0–100Floor on the attention score.
min_timing1–5Floor on timing. Maps to the window words: 5 active window · 4 accelerating · 3 emerging · 2 weakening · 1 mature.
min_asymmetry1–5Floor on asymmetry (upside vs. effort).
min_founder_fit1–5Floor on founder-market fit.
min_severity1–5Floor on problem severity.
min_market_growth0–100Floor on normalized market growth.
max_difficulty1–5Ceiling on the founder/difficulty scale (1 solo-friendly … 5 VC-scale team). max_difficulty=2 ≈ solo-buildable.
min_votesintFloor on all-time net votes across members (votes_net). Negative allowed; unvoted ideas count as 0.

Every floor/ceiling excludes ideas whose value is null (not yet scored or estimated).

ICP
ParameterTypeDefaultDescription
business_typeenumb2b | b2c (case-insensitive).
build_typeenumsoftware | hardware (case-insensitive, prefix-matched against stored variants).
industrystringCase-insensitive substring on the idea's industry vertical, e.g. industry=fintech or industry=health. Vocabulary: Healthcare & Bio · Fintech · Security · DevTools & Infra · Enterprise Software · Consumer · Commerce & Retail · Media & Creative · Education · Legal & Compliance · Industrial & Robotics · Climate & Energy · Cross-industry (see GET /industries for live counts).
icpstringSubstring match on the idea's ICP text, e.g. icp=developer.
Search & sorting
ParameterTypeDefaultDescription
qstringFull-text search over name, tagline, ICP, problem, solution, thesis, and wedge. Web-search syntax: bare words are AND-ed, "quoted phrases" match in order, -word excludes, or alternates. Max 500 chars.
sortenumupdated · relevance with qrelevance | updated | conviction | attention | tam | sam | som | cagr | votes | ltv_cac. relevance requires q. Nulls sort last.
orderenumdescasc | desc.

With q, each row carries a relevance score (name and tagline weigh most, then ICP/problem, then the rest) — values are relative to that query, not comparable across queries. English stemming applies (planners matches planner). A query with no matches returns an empty data array, never an error.

Example requests
# Incremental sync of high-conviction active ideas
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/ideas?status=active&min_conviction=60&limit=50&updated_since=2026-06-01T00:00:00Z"

# Big-market B2B ideas, largest TAM first
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/ideas?business_type=b2b&min_tam=1b&max_tam=50b&sort=tam"

# Full-text search, best match first
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/ideas?q=ai%20agents%20-crypto&min_attention=40"

# Solo-buildable software ideas with working unit economics, best-voted first
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/ideas?build_type=software&max_difficulty=2&min_ltv_cac=3&sort=votes"

# Fresh ideas on a theme, not yet deep-dived
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/ideas?theme=agentic&has_deep_dive=false&first_seen_since=2026-07-01T00:00:00Z"
Response 200
{
  "data": [
    {
      "id": "002145db-0edb-4834-a191-27a3dda9a276",
      "idea_code": "IDEA-042",
      "name": "SettlementGlue — ERP Settlement Reconciliation Middleware",
      "tagline": "Reconciles acquirer settlement files against ERP ledgers.",
      "icp": "Merchant finance teams",
      "problem": "Settlement receipts are disconnected from attribution pipelines.",
      "solution": "Middleware that reconciles acquirer settlement files...",
      "wedge": "Start with the top-3 acquirer formats.",
      "project_thesis": "Payments complexity is compounding; finance teams...",
      "business_type": "B2B",
      "build_type": "Software",
      "industry": "Fintech",
      "status": "active",
      "scores": {
        "conviction": 81,          // 0–100, or null
        "timing": 5,               // 1–5, or null
        "asymmetry": 4,            // 1–5, or null
        "founder_market_fit": 3,   // 1–5, or null
        "severity": 5,             // 1–5, or null
        "market_growth": null,     // 0–100, or null
        "difficulty": 3            // 1–5 founder scale (1 solo … 5 VC-scale), or null
      },
      "attention": { "score": 62, "band": 3 },  // 0–100 / 1–5, or null
      "financials": {              // all number-or-null, USD unless noted
        "tam_usd": 7000000000,
        "sam_usd": 1500000000,
        "som_usd": 50000000,
        "cagr_5yr_pct": 22,
        "growth_24mo_pct": 18,
        "unit_margin_usd": 140000, // gross margin $/yr per customer
        "cac_usd": 220000,
        "customer_lifetime_years": 5,
        "ltv_usd": 700000,         // stored, else unit_margin × lifetime
        "ltv_cac": 3.18,
        "sources": {               // per-field provenance; absent = agent-stated
          "tam_usd": "estimate"    // pack | estimate | manual
        }
      },
      "surface": { "id": "SURF-019", "name": "SkillSign / TrustKit" },
      "hypotheses": ["HYP-004", "HYP-020"],  // thesis tags; filter with hypothesis=
      "deep_dive": {
        "id": "c4b3294c-...",
        "verdict": "research",     // pursue | research | deprioritize | null
        "confidence": "65%"
      },
      "votes_7d": 0,
      "votes_net": 2,              // all-time net votes across members
      "first_seen": "2026-06-16T21:00:00.000Z",
      "last_updated": "2026-06-16T21:00:00.000Z",
      "relevance": 0.83            // only present when q was given
    }
  ],
  "pagination": {
    "limit": 50,
    "offset": 0,
    "count": 1,
    "total": 464,
    "has_more": true
  }
}

surface and deep_dive are null when an idea has none. financials.sources tells you where each number came from: pack (GTM research, most reliable), estimate (model backstop), or manual (operator-pinned). To page through everything, walk offset until has_more is false.

GET/api/public/v1/ideas/{id}

Returns a single idea (the same shape as one element of data above), or 404 not_found.

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/ideas/002145db-0edb-4834-a191-27a3dda9a276"
GET/api/public/v1/industries

The vertical vocabulary behind /ideas?industry=… and each idea's industry field, with live active-idea counts — build filter chips from this instead of hard-coding labels. No parameters.

The 13 canonical verticals
Healthcare & BioFintechSecurityDevTools & InfraEnterprise SoftwareConsumerCommerce & RetailMedia & CreativeEducationLegal & ComplianceIndustrial & RoboticsClimate & EnergyCross-industry

The industry filter is a substring match, so fintech, health, or devtools all work without the full label. Ideas not yet classified have industry: null and are excluded by any industry filter.

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/industries"
Response 200
{
  "data": [
    { "industry": "Healthcare & Bio",   "canonical": true, "active_ideas": 31 },
    { "industry": "Fintech",            "canonical": true, "active_ideas": 54 },
    { "industry": "Security",           "canonical": true, "active_ideas": 22 },
    // ... all 13 canonical labels, even at zero; rare stray stored values
    // append at the end with "canonical": false
  ],
  "count": 13
}
GET/api/public/v1/ideas/{id}/deep-dive

Returns the idea's deep dive with its full founder-diligence pack — the 11 research markdown files (executive thesis, ICP, competitive analysis, pricing, GTM, demand tests, risks, …) plus the pitch-deck HTML, verdict, confidence, and venture economics. {id} accepts the idea UUID or its IDEA-### code.

source tells you whose pack it is: idea = generated for this exact idea; surface = inherited from the parent surface's newest pack (possibly researched for a sibling idea). If neither exists you get 404 no_deep_dive — the body carries the generation queue state, so after POSTing you can poll this same URL until the pack lands.

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/ideas/IDEA-042/deep-dive"
Response 200
{
  "idea": { "id": "002145db-...", "idea_code": "IDEA-042", "name": "SettlementGlue" },
  "deep_dive": {
    "id": "c4b3294c-...",
    "run_id": "8a1f5331-...",
    "source": "idea",                  // idea | surface (see above)
    "idea_id": "002145db-...",         // null on surface-inherited packs
    "surface": { "id": "SURF-019", "name": "SkillSign / TrustKit" },
    "verdict": "research",             // pursue | research | deprioritize | null
    "confidence": "65%",
    "headline_risk": "Buyers may not pay before a standard emerges.",
    "economics": { "tam": "$7B", "som": "$50M", "revenue_m6": "$40k", ... },
    "created_at": "2026-07-20T09:12:44.000Z",
    "founder_diligence": [             // the pack's markdown files, verbatim
      { "filename": "00-executive-thesis.md",       "content": "# ..." },
      { "filename": "01-ideal-customer-profile.md", "content": "# ..." },
      // ... through 10-product-handoff.md
    ],
    "pitch_deck_html": "<!doctype html>..."   // or null
  }
}

pitch_deck_html is raw agent-authored HTML — if you embed it, do so only in a sandboxed iframe. Responses run large (hundreds of KB): fetch per idea on demand, don't bulk-poll this endpoint.

Response 404 (idea exists, no pack yet)
{
  "error": "no_deep_dive",
  "queue_status": "running",   // queued | running | failed | null (never queued)
  "runId": "d90c11f2-...",     // the generating run, when one is attached
  "generateUrl": "/api/public/v1/ideas/002145db-.../deep-dive"
}
POST/api/public/v1/ideas/{id}/deep-dive

Generates the deep dive on demand — the same flow as the idea engine's Start Deep Dive: the idea is promoted into the Deep Dive Queue and a dedicated run starts immediately, running the GTM research agent on the idea's parent surface (idea injected as context) and then the validation agent (synthetic customers + customer intelligence). Returns 202 Accepted right away; the research takes tens of minutes. Poll statusUrl until the run completes, then fetch resultUrl. The request body is ignored.

This spends real research budget (premium model + web search), so it is deliberately conservative: it refuses if the idea already has its own pack (409 deep_dive_exists — GET it instead), if its generation is already underway (409 deep_dive_in_progress), or while any other run is in flight (409 run_in_progress — retry after it finishes). Ideas without a parent surface can't be researched: 422 no_parent_surface.

Example request
curl -X POST -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/ideas/IDEA-042/deep-dive"
Response 202 Accepted
{
  "runId": "d90c11f2-...",
  "status": "pending",
  "idea": { "id": "002145db-...", "idea_code": "IDEA-042", "name": "SettlementGlue" },
  "statusUrl": "/api/public/v1/runs/d90c11f2-...",
  "resultUrl": "/api/public/v1/ideas/002145db-.../deep-dive"
}
POST/api/public/v1/runs

Starts a full autonomous run end-to-end — sensing, synthesis, deep dives, and digest — with no human approval step. The run auto-selects its top surfaces and never pauses. Returns immediately; the work continues in the background. The request body is ignored.

Only one run executes at a time. If a run is already in flight, this returns 409 run_in_progress with the active run's id and status, and does not start a new one.

Example request
curl -X POST -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/runs"
Response 202 Accepted
{
  "runId": "8a1f5331-156c-41a0-bc28-509d96d0ee9e",
  "status": "pending",
  "statusUrl": "/api/public/v1/runs/8a1f5331-156c-41a0-bc28-509d96d0ee9e"
}
Response 409 Conflict
{
  "error": "run_in_progress",
  "runId": "8a1f5331-...",
  "status": "running"
}
GET/api/public/v1/runs/{id}

Poll a run's status using the statusUrl returned by the trigger. Poll every ~30–60s until status is terminal. Once available, the response also carries the run's digest markdown and a one-liner for each deep dive (deep_dives).

Response 200
{
  "runId": "8a1f5331-...",
  "status": "completed",
  "trigger_type": "api",
  "started_at": "2026-06-17T14:53:06.823Z",
  "completed_at": "2026-06-17T15:41:22.001Z",
  "digest": "## This run...\n- ...",   // run digest markdown, null until ready
  "deep_dives": [                      // one-liner per deep dive, [] until ready
    {
      "surface": "SkillSign / TrustKit",
      "verdict": "research",           // pursue | research | deprioritize | null
      "confidence": "65%",
      "headline_risk": "Buyers may not pay before a standard emerges."
    }
  ]
}
Status values
pendingrunningrunning_deepdivescompletedfailed

completed and failed are terminal; completed_at is set on success. Once a run is completed, fetch its output via GET /ideas?updated_since=<started_at>.

GET/api/public/v1/validations

Lists synthetic customer validations (VAL-###) — for each researched idea, a 7-persona × 8-round synthetic focus group with a backend-computed customer-demand confidence (CDC). Use it as a second opinion beside the deep dive's own verdict (both are in each row), or to rank the shelf by validated demand.

Query parameters
ParameterTypeDefaultDescription
ideastringOnly validations of this idea (UUID or IDEA-###).
recommendationenumproceed | pivot | pause | kill.
statusenumcompletecomplete | draft | quarantined | superseded | all.
min_cdc0–100Floor on the computed customer-demand confidence.
sort / orderenumgenerated · descsort: generated | cdc. order: asc | desc.
limit / offsetint50 / 0Pagination (limit clamped 1–200).
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/validations?recommendation=proceed&min_cdc=50&sort=cdc"
Response 200
{
  "data": [
    {
      "id": "5b0c72a1-...",
      "val_code": "VAL-024",
      "version": "v1",
      "status": "complete",
      "recommendation": "pivot",            // proceed | pivot | pause | kill
      "central_unresolved_question": "Will mid-market finance teams trust ...?",
      "cdc": { "score": 33.1, "band": "low", "delta": null },  // computed, never LLM-asserted
      "idea": { "id": "002145db-...", "idea_code": "IDEA-042", "name": "SettlementGlue" },
      "surface": { "id": "SURF-019", "name": "SkillSign / TrustKit" },
      "pack_code": "PACK-017",
      "deep_dive": { "verdict": "research", "confidence": "65%" },  // the GTM verdict, side by side
      "personas_present": 7,               // of the 7-persona manifest
      "grounding_count": 6,
      "run_id": "8a1f5331-...",
      "generated_at": "2026-08-06T18:22:10.000Z"
    }
  ],
  "pagination": { "limit": 50, "offset": 0, "count": 1, "total": 24, "has_more": false }
}

The CDC deliberately discounts synthetic evidence (×0.35), so low bands on unlaunched ideas are normal — it rises as real signals replace synthetic ones. Fetch the full transcript-level detail per idea via /ideas/{id}/validation.

GET/api/public/v1/ideas/{id}/validation

The idea's newest validation, fully expanded: the persona manifest (reusable as ad-audience hypotheses), the 8 focus-group rounds with the debate consensus, the 6 CDC inputs, cited demand-side grounding (real quotes and sources), the assumption backlog, and the marketing handoff — the exact probes (ICP segments, channels, messaging angles, pricing points, landing variants) the validation says to test with real traffic next. {id} accepts the idea UUID or IDEA-###.

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/ideas/IDEA-042/validation"
Response 200
{
  "idea": { "id": "002145db-...", "idea_code": "IDEA-042", "name": "SettlementGlue" },
  "validation": {
    // ...all list fields above, plus:
    "personas": [
      { "persona": "economic_buyer", "display_name": "Dana, VP Finance",
        "role": "Controls the budget", "is_synthetic": true,
        "present": true, "min_evidence_met": true },
      // ... innovator | pragmatist | power_user | skeptic | competitor_user | non_customer
    ],
    "rounds": [
      { "round_no": 1, "round": "problem_discovery", "is_debate": false,
        "present": true, "personas_in_round": ["innovator","pragmatist"],
        "consensus_score": null, "divergence_points": null,
        "conclusion": "Pain is real but episodic..." },
      // ... round 8 = structured_debate carries consensus_score + divergence_points
    ],
    "cdc_inputs": [
      { "input": "pain_severity", "value_0_100": 62, "value_basis": "sourced",
        "confidence": "medium", "evidence_ref": "G-3" }
      // wtp_strength | persona_consensus | adoption_barrier_height | feature_value_clarity | wedge_resonance
    ],
    "grounding": [
      { "claim": "Reconciliation threads recur weekly on r/accounting",
        "url": "https://reddit.com/...", "source_name": "Reddit",
        "source_type": "community", "source_tier": 2, "value_basis": "sourced",
        "confidence": "medium", "as_of_date": "2026-07-30" }
    ],
    "marketing_handoff": [
      { "probe_kind": "messaging_angle", "detail": "Lead with 'close the books 2 days faster'",
        "resolves_assumption": "A2", "upgrades_input": "wtp_strength" }
    ],
    "assumptions": [
      { "assumption_code": "A2", "category": "economic",
        "impact_1_10": 8, "uncertainty_1_10": 7, "risk_score": 56 }
    ],
    "lineage": [ { "kind": "hypothesis", "ref": "HYP-004" }, { "kind": "theme", "ref": "T012" } ]
  }
}

If the idea has no validation yet you get 404 no_validation with a generateUrl — validations mint on the deep-dive flow (POST the deep dive, then poll).

GET/api/public/v1/themes

The research themes behind the ideas, each with its latest computed state: evidence strength, supply/demand split, momentum, demand-vs-supply opportunity score, and change pressure. This is the "why now / still now?" layer — an idea whose theme is decaying carries timing risk its validation can't see. Ideas link to themes via their surface (/ideas?theme=…).

Query parameters
ParameterTypeDefaultDescription
lifecycleenumallwatchlist | tracked | dormant | merged | all.
qstringSubstring match on slug or name.
sort / orderenumstrength · descsort: strength | momentum | opportunity | demand | updated. order: asc | desc.
limit / offsetint50 / 0Pagination (limit clamped 1–200).
# Where is demand outrunning supply right now?
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/themes?lifecycle=tracked&sort=opportunity"
Response 200
{
  "data": [
    {
      "slug": "coding-agents-enterprise-scale",
      "name": "Coding Agents at Enterprise Scale",
      "description": "Autonomous coding agents moving from IDE toys to ...",
      "layer": "application",              // infrastructure | platform | application | unspecified
      "lifecycle": "tracked",              // watchlist | tracked | dormant | merged
      "demand_native": false,
      "first_seen_run": 3,
      "latest": {                          // newest snapshot; null for never-scored themes
        "run_index": 16,
        "evidence_strength": 18.1,         // 0–100, combined headline
        "supply_strength": 3.8,
        "demand_strength": 14.0,
        "momentum": 2.4,                   // Δ vs prior run; null = baseline
        "demand_momentum": 5.9,
        "opportunity_score": 15.4,         // demand-vs-supply gap, 0–100
        "change_pressure": null,           // −1..1, from the change-detection agent
        "rank": 21,
        "conviction": 3,                   // 1–5; team override wins over computed
        "computed_at": "2026-08-06T18:03:58.060Z"
      },
      "updated_at": "2026-08-06T18:03:58.060Z"
    }
  ],
  "pagination": { "limit": 50, "offset": 0, "count": 1, "total": 21, "has_more": false }
}
GET/api/public/v1/themes/{slug}

One theme in depth: everything the list returns, plus history (the full snapshot time series, oldest first — plot it for the momentum/decay curve), and the theme's 25 most recent gaps, changes, and signals (same shapes as the corresponding list endpoints). Unknown slug → 404 not_found.

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/themes/coding-agents-enterprise-scale"
GET/api/public/v1/board

The unified Themes & Demand board: one row per theme carrying both the supply (Agent 1) and demand (Agent 2) sides of its latest computed state, signal counts, demand gaps, evidence-chain link counts, and the team's net vote. The richest single call for "what does the engine believe right now?" — where /themes is the thin list, this is the screener, with score-floor, categorical, and time filters. All filters combine with AND; total reflects the filtered set.

Query parameters
ParameterTypeDefaultDescription
min_strength / min_supply_strength / min_demand_strength / min_opportunitynumberScore floors (0–100) on the latest snapshot: combined evidence, supply, demand, and the demand-vs-supply opportunity score.
min_momentumnumberMomentum floor (−100–100); positive = strengthening since the prior run.
min_convictionnumberConviction floor (1–5; team override wins over computed).
layerenuminfrastructure | platform | application | unspecified.
lifecycleenumallwatchlist | tracked | dormant | merged | all.
origin_agentenumagent_1 (supply-first) | agent_2 (demand-first) — which agent minted the theme.
demand_nativebooltrue = themes born from demand evidence rather than builder/funding supply.
run_indexintOnly themes whose latest snapshot is from exactly this run.
latest_run_onlybooltrue = only themes scored in the most recent run (drops stale/dormant snapshots).
updated_sinceISO 8601Themes updated strictly after this timestamp.
first_seen_sinceISO 8601Themes first discovered on/after this date.
latest_signal_sinceISO 8601Themes with fresh evidence on/after this date.
qstringSubstring match on slug or name.
sort / orderenumopportunity · descsort: opportunity | strength | supply | demand | momentum | demand_momentum | conviction | updated | latest_signal | found. order: asc | desc.
limit / offsetint50 / 0Pagination (limit clamped 1–200).
# Tracked themes where demand is strong and evidence is fresh
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/board?lifecycle=tracked&min_demand_strength=40&latest_signal_since=2026-08-01&sort=opportunity"
Response 200
{
  "as_of_run": 16,                       // most recent chain run index
  "data": [
    {
      "slug": "agent-kill-switch-and-runtime-control",
      "name": "Agent Kill-Switch and Runtime Control",
      "description": "Runtime controls for stopping, constraining, ...",
      "layer": "infrastructure",
      "lifecycle": "watchlist",
      "demand_native": false,
      "origin_agent": "agent_1",           // agent_1 | agent_2
      "first_seen_run": 9,
      "found_date": "2026-07-15",          // date the theme first appeared
      "updated_at": "2026-08-06T18:03:05.950Z",
      "latest": {                          // newest snapshot; same shape as /themes
        "run_index": 16,
        "evidence_strength": 75.3,
        "supply_strength": 39.1,
        "demand_strength": 65.9,
        "momentum": 0.9,
        "demand_momentum": 0.9,
        "opportunity_score": 33.2,
        "change_pressure": null,
        "rank": 1,
        "conviction": 5,
        "computed_at": "2026-08-06T18:03:58.067Z"
      },
      "supply_signal_count": 9,            // all-time, Agent 1 side
      "demand_signal_count": 26,           // all-time, Agent 2 side
      "runs_active": 8,                    // distinct runs that recorded a signal
      "latest_signal_date": "2026-08-06",  // freshest evidence
      "active_opp_count": 3,               // non-archived surfaces on this theme
      "linked": {                          // evidence-chain counts (details via /ideas?theme=…)
        "hypothesis_count": 5,
        "surface_count": 3,
        "idea_count": 49
      },
      "gaps": [                            // latest gap-bearing run, max 10; same shape as /gaps
        {
          "gap_key": "GAP-2026-08-06-01",
          "gap_type": "demand_gt_supply",
          "strategic_implication": "Operators are encountering failures ...",
          "confidence": "medium",
          "demand_strength_at": 65.9,
          "supply_strength_at": 39.1,
          "gap_strength": 24.1
        }
      ],
      "vote": 2                            // team net vote; null when none
    }
  ],
  "pagination": { "limit": 50, "offset": 0, "count": 1, "total": 12, "has_more": false }
}
GET/api/public/v1/signals

The atomic evidence behind the themes. Demand signals (class=demand) carry real user language — jobs-to-be-done, pain, frequency, willingness-to-pay — with the cited source URL: exactly the grounding landing copy and ad creative want instead of invented voice-of-customer. Supply signals are the builder/funding side.

Query parameters
ParameterTypeDefaultDescription
classenumsupply | demand. Omit for both.
themestringExact slug or theme-name substring.
disconfirmingbooltrue = only bear-case / thesis-contradicting signals.
sinceISO 8601Only signals ingested strictly after this timestamp.
limit / offsetint50 / 0Pagination (limit clamped 1–200).
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/signals?class=demand&theme=coding-agents"
Response 200
{
  "data": [
    {
      "signal_key": "2026-08-06-017",
      "theme": { "slug": "coding-agents-enterprise-scale", "name": "Coding Agents at Enterprise Scale" },
      "class": "demand",
      "claim": "Safe sandboxing and non-interactive MCP execution can conflict...",
      "direction": "strengthen",           // post-validation: strengthen | weaken | neutral | context_only
      "magnitude": 2,                      // post-validation, 0–3
      "is_disconfirming": false,
      "published_date": "2026-08-04T00:00:00.000Z",
      "source": {
        "url": "https://forum.cursor.com/t/...",
        "name": "Cursor Forum", "domain": "forum.cursor.com",
        "type": "community", "tier": 1,    // 1 best … 4; demand uses a per-signal effective tier
        "from_harvest": true               // the URL came from the harvest corpus
      },
      "demand_attributes": {               // demand signals only; null for supply
        "jtbd": "Run agents unattended without breaking security review",
        "pain": "high", "frequency": "weekly", "wtp": "unknown",
        "solution_gap": "no vendor covers both", "evidence_type": "forum_thread"
      },
      "notes": null,
      "created_at": "2026-08-06T18:02:11.000Z"
    }
  ],
  "pagination": { "limit": 50, "offset": 0, "count": 1, "total": 160, "has_more": true }
}
GET/api/public/v1/gaps

Supply-vs-demand divergences (GAP-…): where customer demand outruns what's being built (or contradicts the thesis). Each gap carries the strengths on both sides at detection time and a computed gap_strength.

Query parameters
ParameterTypeDefaultDescription
gap_typeenumdemand_gt_supply | supply_gt_demand | demand_contradicts_thesis.
themestringExact slug or theme-name substring.
min_strengthnumber ≥ 0Floor on gap_strength (quarantined gaps have null and are excluded).
sinceISO 8601Only gaps detected strictly after this timestamp.
sort / orderenumcreated · descsort: created | strength. order: asc | desc.
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/gaps?gap_type=demand_gt_supply&sort=strength"
Response 200
{
  "data": [
    {
      "gap_key": "GAP-2026-07-09-02",
      "theme": { "slug": "agentic-commerce", "name": "Agentic Commerce" },
      "gap_type": "demand_gt_supply",
      "strategic_implication": "Buyers are asking for agent-native checkout; few credible vendors.",
      "confidence": "high",                // low | medium | high
      "is_disconfirming": false,
      "demand_strength_at": 42.1,
      "supply_strength_at": 7.3,
      "gap_strength": 34.8,                // null = quarantined
      "created_at": "2026-07-09T03:14:22.000Z"
    }
  ],
  "pagination": { "limit": 50, "offset": 0, "count": 1, "total": 43, "has_more": false }
}
GET/api/public/v1/harvest

The raw harvested corpus the sensing agents read: RSS/newsletter posts, HN threads, GitHub issue exhaust, app-store reviews, forum threads, funding filings, and more — titles + excerpts of real user language, newest first. Useful directly as copy/creative grounding when you want the source text itself rather than the distilled signal.

Query parameters
ParameterTypeDefaultDescription
sourceenumrss | hn | github | edgar | appstore | pkg | crypto | forum | gdelt | epoch | macro | ph.
categorystringPerspective lane / feed category (e.g. skeptic, emerging_vc).
sitestringExact site key (case-insensitive), e.g. hn, a16z.
qstringSubstring match on title or excerpt.
sinceISO 8601Only items published strictly after this timestamp.
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/harvest?source=forum&q=agent&since=2026-08-01T00:00:00Z"
Response 200
{
  "data": [
    {
      "source": "forum",
      "category": "forum",
      "url": "https://forum.cursor.com/t/background-agents-timeout/...",
      "title": "Background agents keep timing out on long refactors",
      "excerpt": "Every time I hand the agent a multi-file refactor it dies at ...",
      "site": "cursor-forum",
      "author": "dev_ml",
      "published_at": "2026-08-05T11:02:00.000Z",
      "points": null,                      // HN only
      "num_comments": 37,
      "times_seen": 2,
      "first_seen_at": "2026-08-05T12:00:04.000Z",
      "last_seen_at": "2026-08-08T12:00:02.000Z"
    }
  ],
  "pagination": { "limit": 50, "offset": 0, "count": 1, "total": 454, "has_more": true }
}
GET/api/public/v1/changes

The canonical change timeline (CHG-…): typed, material moves in theme or gap state — accelerations, decays, reversals, new-theme proposals — with magnitudes recomputed from the snapshot series (never model-asserted). Watch this to catch timing risk: an idea whose underlying theme shows decay events may have missed its window.

Query parameters
ParameterTypeDefaultDescription
themestringExact slug or theme-name substring.
typestringPost-validation change type, e.g. acceleration, decay, reversal.
min_magnitude0–100Floor on the recomputed materiality.
bar_clearedbooltrue = only events that cleared the materiality floor.
sinceISO 8601Only events created strictly after this timestamp.
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/changes?theme=coding-agents&min_magnitude=25&bar_cleared=true"
Response 200
{
  "data": [
    {
      "change_key": "CHG-2026-08-06-02",
      "title": "Enterprise coding-agent demand accelerating",
      "target_kind": "theme",              // theme | gap
      "theme": { "slug": "coding-agents-enterprise-scale", "name": "Coding Agents at Enterprise Scale" },
      "gap_key": null,                     // set when target_kind = gap
      "type": "acceleration",              // post-validation type (use this)
      "emitted_type": "acceleration",      // as classified by the model
      "confidence": "strong",              // strong | moderate | emerging
      "strategic_implication": "Window is opening; move surfaces on this theme up.",
      "hypothesis_codes": ["HYP-004"],
      "narrative_alignment": "confirms",   // confirms | contradicts | ledger_leads | ledger_lags | mixed | not_checked
      "raw_delta": 12.4,                   // signed Δ, recomputed from snapshots
      "magnitude": 59.3,                   // 0–100 materiality, recomputed
      "persistence_runs": 3,
      "bar_cleared": true,
      "run_index": 16,
      "from_run_index": 15,
      "created_at": "2026-08-06T18:05:41.000Z"
    }
  ],
  "pagination": { "limit": 50, "offset": 0, "count": 1, "total": 64, "has_more": true }
}
GET/api/public/v1/hypotheses

The strategy ledger: the organization's core bets (7 by default;CORE-1..7) and the secondary hypotheses (HYP-###) with computed support scores and, per hypothesis, the ideas riding it — so a consumer can group the shelf by thesis instead of by score and triage a whole bet at once. Both tiers return in one unpaginated response (the ledger is small).

Query parameters
ParameterTypeDefaultDescription
lifecycleenumactiveactive | merged | retired | all.
statusstringExact status (case-insensitive): Emerging, Strengthening, Stable, Weakening, Invalidated, ...
corestringOnly hypotheses mapped to this core bet (CORE-4; core-4 accepted).
min_support0–100Floor on the computed support score.
qstringSubstring match on the hypothesis statement.
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/hypotheses?core=CORE-4&min_support=50"
Response 200
{
  "core": [
    {
      "core_id": "CORE-1",
      "position": 1,
      "title": "Agents become the primary software interface",
      "body": "Full prose of the core bet...",
      "hypothesis_ids": ["HYP-003", "HYP-005", "HYP-012"],  // active secondaries mapped here
      "updated_at": "2026-07-28T10:00:00.000Z"
    }
  ],
  "data": [
    {
      "hyp_id": "HYP-004",
      "statement": "Mid-market teams will pay for agent-native reconciliation...",
      "category": "Demand",
      "tier": "secondary",                 // core | secondary
      "layer": "application",
      "lifecycle": "active",               // active | merged | retired | proposed_change_pending
      "status": "Strengthening",
      "timing_assessment": "Active Window",
      "support": { "score": 68.5, "band": 4 },  // computed from evidence, canonical
      "confidence": 4,                     // legacy asserted score
      "evidence": { "for": 9, "against": 2 },
      "maps_to_core": ["CORE-4", "CORE-5"],
      "why_now": "...",
      "what_must_be_true": "...",
      "falsification_criteria": "...",
      "strategic_implication": "...",
      "idea_codes": ["IDEA-042", "IDEA-179", "IDEA-301"],  // ideas riding this bet
      "first_seen_run": 6,
      "last_calibrated_run": 16,
      "date_added": "2026-06-25T00:00:00.000Z",
      "last_updated": "2026-08-06T00:00:00.000Z"
    }
  ],
  "count": 1
}

idea_codes uses the same linkage as /ideas?hypothesis=… (direct pin + thesis tags + parent surface), so the two views stay consistent.

GET/api/public/v1/issues

The Issue archive — the frozen Executive Overview brief minted after each completed run (twice weekly), newest first. Poll the list for a new issue_number to detect a fresh brief, then fetch it by number.

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/issues"
Response 200
{
  "data": [
    { "issue_number": 15, "issue_date": "2026-08-06", "run_id": "8a1f5331-...",
      "created_at": "2026-08-06T18:40:12.000Z" }
  ],
  "pagination": { "limit": 50, "offset": 0, "count": 1, "total": 15, "has_more": false }
}
GET/api/public/v1/issues/{number}

One Issue. {number} is an issue number or latest. The default JSON carries dataset — the machine-readable brief (summary, themes + demand state, hypothesis scoreboard, pipeline, portfolio, VC signals) that rendered the page, ideal for an in-console market brief. Add ?format=html for the rendered self-contained document (a deterministic template render, not raw LLM HTML).

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/issues/latest"

# The rendered document
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://YOUR_HOST/api/public/v1/issues/latest?format=html" -o issue.html

Errors

Errors are JSON with an error string (and sometimes extra fields). HTTP status carries the meaning.

StatusBodyMeaning
400{"error":"invalid_param","param":"…"}A query parameter failed validation.
401{"error":"unauthorized"}Missing or incorrect bearer token.
404{"error":"not_found"}No idea or run with that id.
404{"error":"no_deep_dive",…}Idea exists but has no deep dive yet; body carries the queue state.
404{"error":"no_validation",…}Idea exists but has no synthetic validation yet; body carries the generateUrl.
409{"error":"run_in_progress",…}A run is already executing; trigger refused.
409{"error":"deep_dive_exists",…}The idea already has its own pack — GET it instead of regenerating.
409{"error":"deep_dive_in_progress",…}This idea's pack is already being generated.
422{"error":"no_parent_surface"}The idea has no parent surface, so nothing can be researched.
503{"error":"api_not_configured"}Server has no API key set — API disabled.
DECA Labs Public API · v1 · Replace YOUR_API_KEY with your issued key. The host shown above is this deployment.
Public API Documentation · DECA Labs