{"openapi":"3.1.0","info":{"title":"AskEngine API","version":"1.0.0","description":"Public API endpoints for AskEngine user intelligence."},"servers":[{"url":"https://silent-viper-157.convex.site","description":"AskEngine API"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Ask","description":"One question in, one cited answer out."},{"name":"Signals","description":"Cross-study beliefs backed by attributed evidence."},{"name":"Studies","description":"Research efforts and the interviews behind them."},{"name":"Evidence","description":"Attributed quotes — the proof under every claim."},{"name":"Research gaps","description":"Gaps in what AskEngine knows, flagged as open questions about its own beliefs."},{"name":"Action cards","description":"The team's inbox: pending items ranked by priority, resolvable and snoozable."},{"name":"Recommendations","description":"Recommendations derived from signals, and their outcomes."}],"paths":{"/v1/signals":{"get":{"operationId":"listSignals","summary":"List signals","description":"A paginated list of the team's signals. Each item carries the three signal axes — stage (evidence maturity), the archive axis, and derived actionable — plus trajectory. Filter by state-of-play view or trajectory.","tags":["Signals"],"parameters":[{"name":"stage","in":"query","description":"Filter to one state-of-play view. \"actionable\" and \"archived\" map server-side to the derived facts; filtered pages fill from bounded batches, so a page may run slightly over or under `limit` — keep paging until `cursor` is null.","schema":{"$ref":"#/components/schemas/SignalStateOfPlay"}},{"name":"trajectory","in":"query","description":"Filter by recent signal movement.","schema":{"$ref":"#/components/schemas/Trajectory"}},{"name":"limit","in":"query","description":"Maximum number of results to return.","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}},{"name":"cursor","in":"query","description":"Pagination cursor from the previous response.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of signals.","content":{"application/json":{"schema":{"type":"object","required":["data","cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SignalListItem"}},"cursor":{"type":["string","null"],"description":"Null when there are no more results."},"total":{"type":"integer","description":"Exact count of every match, without paging. Omitted (never approximated) where the filter derives values in memory: stage-filtered /v1/signals pages and lifecycle-filtered /v1/recommendations pages (status values other than archived, which is stored and counts exactly)."}}},"example":{"data":[{"signalId":"sig_123","ref":"SIG-14","label":"Teams want faster onboarding feedback","description":"Recent studies show repeated friction around setup and first-run clarity.","stage":"emerging","trajectory":"rising","participantCount":8,"studyCount":3,"lastUpdated":1764687600000,"confidenceBand":"moderate","archived":false,"actionable":false}],"cursor":null,"total":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signals/{signalId}":{"get":{"operationId":"getSignal","summary":"Get a signal","description":"The full signal packet: the belief and its confidence, supporting and counter-evidence, per-study breakdowns, the recommended next step, and open research gaps.","tags":["Signals"],"parameters":[{"name":"signalId","in":"path","required":true,"description":"The signal id.","schema":{"type":"string","example":"sig_123"}}],"responses":{"200":{"description":"The signal packet.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SignalPacket"}}},"example":{"data":{"signalId":"sig_123","belief":{"ref":"SIG-14","label":"Teams want faster onboarding feedback","description":"Recent studies show repeated friction around setup and first-run clarity.","trajectory":"rising","stage":"established","confidence":0.78,"firstSeen":1756911600000,"lastUpdated":1764687600000,"confidenceBand":"moderate","archived":false,"actionable":true},"implication":"Onboarding is the highest-leverage place to reduce early churn.","recommendedNextStep":{"type":"act","recommendationId":"rec_456","title":"Add an inline setup checklist","topAction":{"action":"Ship a first-run checklist","priority":"high"}},"evidence":{"items":[{"evidenceId":"ev_001","kind":"evidence","quote":"I gave up twice before I figured out where to add my API key.","themeId":"thm_setup","themeLabel":"Setup friction","studyId":"study_789","studyName":"Onboarding interviews Q4","sourceItemId":"src_a1","participantName":"Ada L.","timeInCallSecs":412,"capturedAt":1764453600000}],"distinctSourceItems":6,"studyCount":3,"quoteCount":8,"counterEvidenceCount":1,"freshness":{"latestCapturedAt":1764687600000,"isStale":false},"distinctParticipants":6},"counterEvidence":[{"evidenceId":"ev_014","kind":"counterEvidence","quote":"Setup was fine for me — the docs were clear.","note":"Experienced user; had integrated a similar tool before.","themeId":"thm_setup","themeLabel":"Setup friction","studyId":"study_789","studyName":"Onboarding interviews Q4","sourceItemId":"src_b2","participantName":"Jon R.","capturedAt":1764540000000}],"perStudy":[{"studyId":"study_789","studyName":"Onboarding interviews Q4","relationship":"supports","narrative":"Most participants stumbled at the API-key step.","addedAt":1764540000000}],"recommendation":{"recommendationId":"rec_456","title":"Add an inline setup checklist","lifecycleState":"open"},"researchGaps":[{"researchGapId":"gap_77","type":"thin_counter_evidence","status":"open"}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/studies":{"get":{"operationId":"listStudies","summary":"List studies","description":"A paginated list of the team's studies with participant and theme counts.","tags":["Studies"],"parameters":[{"name":"limit","in":"query","description":"Maximum number of results to return.","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}},{"name":"cursor","in":"query","description":"Pagination cursor from the previous response.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of studies.","content":{"application/json":{"schema":{"type":"object","required":["data","cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StudyListItem"}},"cursor":{"type":["string","null"],"description":"Null when there are no more results."},"total":{"type":"integer","description":"Exact count of every match, without paging. Omitted (never approximated) where the filter derives values in memory: stage-filtered /v1/signals pages and lifecycle-filtered /v1/recommendations pages (status values other than archived, which is stored and counts exactly)."}}},"example":{"data":[{"studyId":"study_789","name":"Onboarding interviews Q4","status":"active","participantCount":24,"themeCount":6,"createdAt":1764100000000}],"cursor":null,"total":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/studies/{studyId}/readout":{"get":{"operationId":"getStudyReadout","summary":"Get a study readout","description":"The study's synthesized readout: objective, open questions, a narrative summary with stats, and the themes that emerged with representative quotes.","tags":["Studies"],"parameters":[{"name":"studyId","in":"path","required":true,"description":"The study id.","schema":{"type":"string","example":"study_789"}}],"responses":{"200":{"description":"The study readout.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/StudyReadoutPacket"}}},"example":{"data":{"studyId":"study_789","name":"Onboarding interviews Q4","status":"active","objective":"Understand where new teams stall in their first session.","openQuestions":["Does the checklist help teams who self-serve without sales?"],"summary":{"narrative":"New teams consistently stall at configuration, especially the API-key step; those who get past it activate quickly.","confidence":"high","suggestedNextQuestions":["What would have unblocked you fastest?"],"stats":{"totalSourceItems":24,"completionRate":0.83,"avgQualityScore":0.86},"coverage":[{"targetId":"target_checklist","question":"Does the checklist help teams who self-serve without sales?","priority":"primary","substantiveSourceItems":3,"totalSourceItems":24}],"lastUpdated":1764687600000},"themes":[{"themeId":"thm_setup","label":"Setup friction","description":"Confusion around where and how to configure credentials.","sentiment":"negative","strength":"broad","trajectory":"rising","preview":[{"sourceItemId":"src_a1","quote":"I gave up twice before I figured out where to add my API key.","timeInCallSecs":412,"participantName":"Ada L."}],"distinctParticipants":14}],"sourceItemCount":24}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/studies/{studyId}/source-items":{"get":{"operationId":"listStudySourceItems","summary":"List study source items","description":"A paginated list of the source items that fed a study, with participant, status, and quality.","tags":["Studies"],"parameters":[{"name":"studyId","in":"path","required":true,"description":"The study id.","schema":{"type":"string","example":"study_789"}},{"name":"limit","in":"query","description":"Maximum number of results to return.","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}},{"name":"cursor","in":"query","description":"Pagination cursor from the previous response.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of source items.","content":{"application/json":{"schema":{"type":"object","required":["data","cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SourceItemListItem"}},"cursor":{"type":["string","null"],"description":"Null when there are no more results."},"total":{"type":"integer","description":"Exact count of every match, without paging. Omitted (never approximated) where the filter derives values in memory: stage-filtered /v1/signals pages and lifecycle-filtered /v1/recommendations pages (status values other than archived, which is stored and counts exactly)."}}},"example":{"data":[{"sourceItemId":"src_a1","status":"analyzed","participantName":"Ada L.","capturedAt":1764453600000,"qualityScore":0.88,"evidenceCount":5}],"cursor":null,"total":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/evidence":{"get":{"operationId":"getEvidence","summary":"Get evidence","description":"Attributed quotes for a scope - a signal, one or more themes, a study, or specific source items. At least one scope is required; multiple scopes intersect, so each extra id narrows the read. Optionally include counter-evidence.","tags":["Evidence"],"parameters":[{"name":"signalId","in":"query","description":"Scope to a signal's evidence.","schema":{"type":"string"}},{"name":"themeIds","in":"query","description":"Comma-separated theme ids (max 50).","schema":{"type":"string"}},{"name":"studyId","in":"query","description":"Scope to a study.","schema":{"type":"string"}},{"name":"sourceItemIds","in":"query","description":"Comma-separated source-item ids (max 50).","schema":{"type":"string"}},{"name":"evidenceIds","in":"query","description":"Comma-separated evidence ids (max 50) for a direct lookup of quotes you already hold — ask citations, search hits, packet tasters. Kind filtering does not apply to a by-id read: counter-evidence resolves without includeCounterEvidence. Ids that do not resolve — malformed, unknown, or excluded by another scope — come back in unresolvedIds; the rest return normally.","schema":{"type":"string"}},{"name":"includeCounterEvidence","in":"query","description":"Include counter-evidence in the packet.","schema":{"type":"boolean"}},{"name":"maxItems","in":"query","description":"Maximum evidence items to return. Defaults to 20; maximum 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"cursor","in":"query","description":"The cursor the previous page's packet handed back.","schema":{"type":"string"}}],"responses":{"200":{"description":"The evidence packet.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/EvidencePacket"}}},"example":{"data":{"items":[{"evidenceId":"ev_001","kind":"evidence","quote":"I gave up twice before I figured out where to add my API key.","themeId":"thm_setup","themeLabel":"Setup friction","studyId":"study_789","studyName":"Onboarding interviews Q4","sourceItemId":"src_a1","participantName":"Ada L.","timeInCallSecs":412,"capturedAt":1764453600000}],"scope":{"themeIds":["thm_setup"],"studyIds":["study_789"]},"distinctParticipants":6,"total":8,"cursor":null}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/evidence/search":{"post":{"operationId":"searchEvidence","summary":"Search evidence","description":"Semantic search across the team's evidence. Returns scored, attributed quotes matching a natural-language query.","tags":["Evidence"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["query"],"properties":{"query":{"type":"string","description":"Natural-language search query - a focused question, or a pasted quote or complaint to match against.","maxLength":2000},"signalId":{"type":"string","description":"Scope hits to one signal's evidence basis."},"studyId":{"type":"string","description":"Restrict the search to one study."},"kind":{"$ref":"#/components/schemas/EvidenceKind","default":"evidence"},"limit":{"type":"integer","minimum":1,"maximum":50,"default":10,"description":"Maximum number of hits to return."},"minScore":{"type":"number","minimum":0,"maximum":1,"description":"Drop hits scoring below this threshold."}}},"example":{"query":"confusion setting up API keys","signalId":"sig_123","limit":10}}}},"responses":{"200":{"description":"Scored evidence hits, strongest first.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EvidenceSearchHit"}}}},"example":{"data":[{"item":{"evidenceId":"ev_001","kind":"evidence","quote":"I gave up twice before I figured out where to add my API key.","themeId":"thm_setup","themeLabel":"Setup friction","studyId":"study_789","studyName":"Onboarding interviews Q4","sourceItemId":"src_a1","participantName":"Ada L.","timeInCallSecs":412,"capturedAt":1764453600000},"score":0.83}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/research-gaps":{"get":{"operationId":"listResearchGaps","summary":"List research gaps","description":"A paginated list of the gaps AskEngine has flagged in its own beliefs, surfaced as open questions. Filter by status.","tags":["Research gaps"],"parameters":[{"name":"status","in":"query","description":"Filter by status. Omitted: everything except dismissed.","schema":{"$ref":"#/components/schemas/ResearchGapStatus"}},{"name":"limit","in":"query","description":"Maximum number of results to return.","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}},{"name":"cursor","in":"query","description":"Pagination cursor from the previous response.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of research gaps.","content":{"application/json":{"schema":{"type":"object","required":["data","cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ResearchGap"}},"cursor":{"type":["string","null"],"description":"Null when there are no more results."},"total":{"type":"integer","description":"Exact count of every match, without paging. Omitted (never approximated) where the filter derives values in memory: stage-filtered /v1/signals pages and lifecycle-filtered /v1/recommendations pages (status values other than archived, which is stored and counts exactly)."}}},"example":{"data":[{"researchGapId":"gap_77","signalId":"sig_123","signalRef":"SIG-14","type":"thin_counter_evidence","status":"open","description":"Few dissenting voices — the belief may be over-weighted to power users.","suggestedAction":"Interview 5 first-week users who churned."}],"cursor":null,"total":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/recommendations":{"get":{"operationId":"listRecommendations","summary":"List recommendations","description":"A paginated list of recommendations. Filter by lifecycle state. State-filtered pages fill from bounded batches, so keep paging until the cursor is null.","tags":["Recommendations"],"parameters":[{"name":"status","in":"query","description":"Filter by lifecycle status. Omitted: everything except archived.","schema":{"$ref":"#/components/schemas/RecommendationLifecycleState"}},{"name":"state","in":"query","description":"Deprecated alias of status; status wins when both are sent.","schema":{"$ref":"#/components/schemas/RecommendationLifecycleState"},"deprecated":true},{"name":"limit","in":"query","description":"Maximum number of results to return.","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}},{"name":"cursor","in":"query","description":"Pagination cursor from the previous response.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of recommendations.","content":{"application/json":{"schema":{"type":"object","required":["data","cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RecommendationListItem"}},"cursor":{"type":["string","null"],"description":"Null when there are no more results."},"total":{"type":"integer","description":"Exact count of every match, without paging. Omitted (never approximated) where the filter derives values in memory: stage-filtered /v1/signals pages and lifecycle-filtered /v1/recommendations pages (status values other than archived, which is stored and counts exactly)."}}},"example":{"data":[{"recommendationId":"rec_456","title":"Add an inline setup checklist","lifecycleState":"accepted","confidence":"high","signals":[{"ref":"SIG-14","label":"Teams want faster onboarding feedback"}],"updatedAt":1764700000000}],"cursor":null,"total":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/recommendations/{recommendationId}":{"get":{"operationId":"getRecommendation","summary":"Get a recommendation","description":"The full recommendation: body, action items, confidence and rationale, linked work items, and its decision and outcome history.","tags":["Recommendations"],"parameters":[{"name":"recommendationId","in":"path","required":true,"description":"The recommendation id.","schema":{"type":"string","example":"rec_456"}}],"responses":{"200":{"description":"The recommendation.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Recommendation"}}},"example":{"data":{"recommendationId":"rec_456","title":"Add an inline setup checklist","body":"A first-run checklist that walks users through key setup steps should cut early drop-off around configuration.","lifecycleState":"open","workItemLinks":[{"url":"https://linear.app/acme/issue/ACM-412","provider":"linear","externalId":"ACM-412","title":"First-run setup checklist","linkedAt":1764710000000}],"generatedAt":1764690000000,"confidence":"high","confidenceRationale":"Consistent across three studies with little counter-evidence.","actionItems":[{"action":"Ship a first-run setup checklist","priority":"high","supportingEvidence":"6 of 9 participants stalled at the API-key step."}],"evidenceSnapshot":{"totalSourceItems":24,"studyCount":3,"generatedFromEvidenceCount":8},"signals":[{"signalId":"sig_123","ref":"SIG-14","label":"Teams want faster onboarding feedback"}],"decision":{"value":"accepted","at":1764700000000,"note":"Scheduled for next sprint."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/recommendations/{recommendationId}/decision":{"post":{"operationId":"decideRecommendation","summary":"Decide on a recommendation","description":"Accept or dismiss a recommendation, optionally with a note.","tags":["Recommendations"],"parameters":[{"name":"recommendationId","in":"path","required":true,"description":"The recommendation id.","schema":{"type":"string","example":"rec_456"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["decision"],"properties":{"decision":{"type":"string","enum":["accepted","dismissed"]},"note":{"type":"string","maxLength":2000}}},"example":{"decision":"accepted","note":"Scheduled for next sprint."}}}},"responses":{"200":{"description":"The recommendation was updated.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/RecommendationWriteResult"}}},"example":{"data":{"recommendationId":"rec_456"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/recommendations/{recommendationId}/outcome":{"post":{"operationId":"recordRecommendationOutcome","summary":"Record an outcome","description":"Mark an accepted recommendation shipped, validated, or stale. Recording `validated` closes the loop: the linked signals are archived in the same call, and the recommendation is archived with them (its validated outcome is preserved).","tags":["Recommendations"],"parameters":[{"name":"recommendationId","in":"path","required":true,"description":"The recommendation id.","schema":{"type":"string","example":"rec_456"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["outcome"],"properties":{"outcome":{"type":"string","enum":["shipped","validated","stale"]},"note":{"type":"string","maxLength":2000}}},"example":{"outcome":"shipped"}}}},"responses":{"200":{"description":"The outcome was recorded.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/RecommendationWriteResult"}}},"example":{"data":{"recommendationId":"rec_456"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/recommendations/{recommendationId}/reopen":{"post":{"operationId":"reopenRecommendation","summary":"Reopen a recommendation","description":"Return a decided recommendation to the open state.","tags":["Recommendations"],"parameters":[{"name":"recommendationId","in":"path","required":true,"description":"The recommendation id.","schema":{"type":"string","example":"rec_456"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":"string","maxLength":2000}}},"example":{"note":"Revisiting after new evidence."}}}},"responses":{"200":{"description":"The recommendation was reopened.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/RecommendationWriteResult"}}},"example":{"data":{"recommendationId":"rec_456"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/recommendations/{recommendationId}/work-items":{"post":{"operationId":"linkRecommendationWorkItem","summary":"Link a work item","description":"Attach an external work item (Linear, GitHub, or a URL) to a recommendation. Linking is idempotent by normalized URL: re-linking an already-linked URL updates its title and externalId in place instead of adding a duplicate. A recommendation holds at most 20 work items; linking a new URL past that cap fails with a 409.","tags":["Recommendations"],"parameters":[{"name":"recommendationId","in":"path","required":true,"description":"The recommendation id.","schema":{"type":"string","example":"rec_456"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","maxLength":2048,"description":"The work item URL. Must be http(s); other schemes are rejected."},"title":{"type":"string","maxLength":200,"description":"Display title for the work item."},"externalId":{"type":"string","maxLength":128,"description":"The tracker's own id for the work item (e.g. ENG-123)."}}},"example":{"url":"https://linear.app/acme/issue/ACM-412","title":"First-run setup checklist","externalId":"ACM-412"}}}},"responses":{"200":{"description":"The work item was linked.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/RecommendationWriteResult"}}},"example":{"data":{"recommendationId":"rec_456"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/action-cards":{"get":{"operationId":"listActionCards","summary":"List action cards","description":"The team's inbox: pending action cards ranked by priority tier (1 = most urgent), then per-item urgency, then newest first. Page through the queue with the cursor.","tags":["Action cards"],"parameters":[{"name":"limit","in":"query","description":"Maximum cards to return. Defaults to 20; maximum 50.","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}},{"name":"cursor","in":"query","description":"The cursor the previous page handed back.","schema":{"type":"string"}},{"name":"type","in":"query","description":"Filter by action card type.","schema":{"$ref":"#/components/schemas/ActionCardType"}}],"responses":{"200":{"description":"The ranked pending queue.","content":{"application/json":{"schema":{"type":"object","required":["data","cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ActionCard"}},"cursor":{"type":["string","null"],"description":"Null when there are no more results."},"total":{"type":"integer","description":"Exact count of every match, without paging. Omitted (never approximated) where the filter derives values in memory: stage-filtered /v1/signals pages and lifecycle-filtered /v1/recommendations pages (status values other than archived, which is stored and counts exactly)."}}},"example":{"data":[{"actionCardId":"card_123","type":"new_recommendation","summary":"AskEngine recommends adding verification cues to flight results.","priority":1,"createdAt":1767312000000,"refs":{"recommendationId":"rec_456","studyIds":["study_789"]},"studies":[{"studyId":"study_789","name":"Post-use impressions"}]}],"cursor":null,"total":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/action-cards/{actionCardId}/resolve":{"post":{"operationId":"resolveActionCard","summary":"Resolve an action card","description":"Mark a pending action card as followed (handled) or dismissed. For live_call cards, \"contacted\" records that the team reached out and stamps the participant's lastContactedAt before resolving. Resolving an already-resolved card is a no-op.","tags":["Action cards"],"parameters":[{"name":"actionCardId","in":"path","required":true,"description":"The action card id.","schema":{"type":"string","example":"card_123"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["dismissed","followed","contacted"],"description":"\"followed\" = the team followed the card somewhere and handled it. \"contacted\" (live_call cards only) = the team reached out to the participant and stamps lastContactedAt."}}},"example":{"action":"followed"}}}},"responses":{"200":{"description":"The card was resolved.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ActionCardWriteResult"}}},"example":{"data":{"actionCardId":"card_123"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/action-cards/{actionCardId}/snooze":{"post":{"operationId":"snoozeActionCard","summary":"Snooze an action card","description":"Hide a pending action card for a duration; it returns to the queue when the time elapses.","tags":["Action cards"],"parameters":[{"name":"actionCardId","in":"path","required":true,"description":"The action card id.","schema":{"type":"string","example":"card_123"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["durationMs"],"properties":{"durationMs":{"type":"number","description":"Snooze duration in milliseconds, at most 90 days. There is no un-snooze verb, so out-of-range values are rejected rather than burying the card.","exclusiveMinimum":0,"maximum":7776000000}}},"example":{"durationMs":86400000}}}},"responses":{"200":{"description":"The card was snoozed.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ActionCardWriteResult"}}},"example":{"data":{"actionCardId":"card_123"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/research-gaps/{researchGapId}/draft-study":{"post":{"operationId":"draftStudyFromGap","summary":"Draft a study from a gap","description":"Create a draft follow-up study from a research gap. The draft's objective and learning targets generate in the background, and the study stays a draft until published in the app. Idempotent: a gap only ever drafts one study. A dismissed or applied gap refuses with a 409 invalid_transition.","tags":["Research gaps"],"parameters":[{"name":"researchGapId","in":"path","required":true,"description":"The research gap id.","schema":{"type":"string","example":"gap_123"}}],"responses":{"200":{"description":"The draft study was created (or already existed).","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/DraftStudyFromGapResult"}}},"example":{"data":{"researchGapId":"gap_123","studyId":"study_456"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/recommendations/{recommendationId}/work-packet":{"get":{"operationId":"getWorkPacket","summary":"Get a work packet","description":"The recommendation bundled for handoff: problem, beliefs behind it, verbatim evidence and counter-evidence, and a mechanically-assembled definition of done. The markdown field is ready to paste into an issue tracker.","tags":["Recommendations"],"parameters":[{"name":"recommendationId","in":"path","required":true,"description":"The recommendation id.","schema":{"type":"string","example":"rec_456"}}],"responses":{"200":{"description":"The work packet.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WorkPacket"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/ask":{"post":{"operationId":"ask","summary":"Ask the belief layer","description":"Ask the belief layer a whole question and get one synthesized, cited answer. Synchronous and slower than the read endpoints — one LLM completion per call, so expect seconds.\n\nEvery citation is verified against the signals and evidence the model actually saw — a ref or evidence id it invented never reaches the wire. Check a cited belief with [get signal](/docs/api/get-signal) and fetch the cited quotes with [get evidence](/docs/api/get-evidence) via `evidenceIds`. When the material is thin, the answer says the belief layer does not cover the question instead of guessing.","tags":["Ask"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["question"],"properties":{"question":{"type":"string","description":"One focused question in plain language. Doubles as the semantic-search input, so it shares search's cap.","maxLength":2000}}},"example":{"question":"What do users actually think about onboarding since the redesign?"}}}},"responses":{"200":{"description":"The synthesized answer with verified citations.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AskResult"}}},"example":{"data":{"answer":"The evidence points to onboarding confusion concentrating on API-key setup (SIG-4): \"I gave up twice before I figured out where to add my API key.\" — Ada L., study \"Onboarding interviews Q4\". Counter-evidence is thin — only one participant called the flow easy — so the belief stands but is not unanimous.","citations":[{"ref":"SIG-4","signalId":"sig_123","evidenceIds":["ev_001","ev_007"]}],"searchedAt":1764453600000}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"AskEngine API key."}},"schemas":{"SignalStage":{"type":"string","enum":["emerging","established"],"description":"Pure evidence maturity, computed from evidence counts. Whether a signal is actionable or archived is carried by the separate `actionable` and `archived`/`archiveReason` fields, not by stage."},"Trajectory":{"type":"string","enum":["rising","stable","declining","new"]},"Sentiment":{"type":"string","enum":["positive","negative","neutral","mixed"]},"ThemeStrength":{"type":"string","enum":["thin","corroborated","broad"]},"StudyStatus":{"type":"string","enum":["draft","publishing","active","paused","completed"]},"SourceItemStatus":{"type":"string","enum":["in_progress","processing","failed","analyzed","insufficient","analysis_failed"]},"EvidenceKind":{"type":"string","enum":["evidence","counterEvidence"]},"ActionPriority":{"type":"string","enum":["high","medium","low"]},"RecommendationConfidence":{"type":"string","enum":["high","moderate","low"]},"RecommendationLifecycleState":{"type":"string","enum":["open","accepted","dismissed","shipped","validated","stale","archived"]},"ResearchGapType":{"type":"string","enum":["missing_breadth","thin_counter_evidence","topic_asymmetry","stale_evidence","post_ship_verification"]},"ResearchGapStatus":{"type":"string","enum":["open","accepted","applied","dismissed"]},"EvidenceItem":{"type":"object","required":["evidenceId","kind","quote","themeId","themeLabel","studyId","studyName","sourceItemId","participantName","capturedAt"],"properties":{"evidenceId":{"type":"string"},"kind":{"$ref":"#/components/schemas/EvidenceKind"},"quote":{"type":"string"},"note":{"type":"string","description":"Analyst qualifier, present on counter-evidence."},"themeId":{"type":"string"},"themeLabel":{"type":"string"},"studyId":{"type":"string"},"studyName":{"type":"string"},"sourceItemId":{"type":"string"},"participantName":{"type":"string"},"timeInCallSecs":{"type":"number"},"capturedAt":{"type":["integer","null"],"description":"Epoch milliseconds."}}},"EvidencePacket":{"type":"object","required":["items","scope","distinctParticipants","total","cursor"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/EvidenceItem"}},"scope":{"type":"object","required":["themeIds","studyIds"],"properties":{"themeIds":{"type":"array","items":{"type":"string"}},"studyIds":{"type":"array","items":{"type":"string"}}}},"distinctParticipants":{"type":"integer","description":"Distinct participants across every qualifying row, not just the page."},"total":{"type":"integer","description":"Qualifying rows before the page."},"cursor":{"type":["string","null"],"description":"Offset of the next page; null when exhausted."},"unresolvedIds":{"type":"array","items":{"type":"string"},"description":"Present only on evidenceIds reads: requested ids that did not qualify — malformed, gone, below the quality bar, or excluded by another scope. Unresolved is relative to this request: an id excluded by an ANDed scope (e.g. a signalId it does not belong to) is not dead — look it up alone before treating it as stale."}}},"EvidenceSearchHit":{"type":"object","required":["item","score"],"properties":{"item":{"$ref":"#/components/schemas/EvidenceItem"},"score":{"type":"number","description":"Relevance score for the query."}}},"RecommendedNextStep":{"description":"The single best next move for a signal, discriminated by `type`.","oneOf":[{"type":"object","required":["type","recommendationId","title","topAction"],"properties":{"type":{"type":"string","enum":["act"]},"recommendationId":{"type":"string"},"title":{"type":"string"},"topAction":{"anyOf":[{"type":"object","required":["action","priority"],"properties":{"action":{"type":"string"},"priority":{"$ref":"#/components/schemas/ActionPriority"}}},{"type":"null"}]}}},{"type":"object","required":["type","researchGapId","researchGapType","suggestedAction"],"properties":{"type":{"type":"string","enum":["learn"]},"researchGapId":{"type":"string"},"researchGapType":{"$ref":"#/components/schemas/ResearchGapType"},"suggestedAction":{"type":"string"},"draftedStudyId":{"type":"string"}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["collect_evidence"]}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["preserve"]}}}]},"WorkItemLink":{"type":"object","required":["url","provider","linkedAt"],"properties":{"url":{"type":"string"},"provider":{"type":"string","enum":["linear","github","url"]},"externalId":{"type":"string"},"title":{"type":"string"},"linkedAt":{"type":"integer","description":"Epoch milliseconds."}}},"RecommendationDecision":{"type":"object","required":["value","at"],"properties":{"value":{"type":"string","enum":["accepted","dismissed"]},"at":{"type":"integer","description":"Epoch milliseconds."},"note":{"type":"string"}}},"RecommendationOutcome":{"type":"object","required":["value","at"],"properties":{"value":{"type":"string","enum":["shipped","validated","stale"]},"at":{"type":"integer","description":"Epoch milliseconds."},"note":{"type":"string"}}},"ActionItem":{"type":"object","required":["action","priority","supportingEvidence"],"properties":{"action":{"type":"string"},"priority":{"$ref":"#/components/schemas/ActionPriority"},"supportingEvidence":{"type":"string"}}},"EvidenceSnapshot":{"type":"object","required":["totalSourceItems","studyCount","generatedFromEvidenceCount"],"properties":{"totalSourceItems":{"type":"integer"},"studyCount":{"type":"integer"},"generatedFromEvidenceCount":{"type":"integer"}}},"Recommendation":{"type":"object","required":["recommendationId","title","body","lifecycleState","signals","workItemLinks","generatedAt","confidence","actionItems"],"properties":{"recommendationId":{"type":"string"},"title":{"type":"string"},"body":{"type":"string"},"lifecycleState":{"$ref":"#/components/schemas/RecommendationLifecycleState"},"signals":{"type":"array","description":"Readable identity for each linked signal (ref like \"SIG-4\"), so the belief behind a recommendation is citable without a second call.","items":{"type":"object","properties":{"signalId":{"type":"string"},"ref":{"type":"string"},"label":{"type":"string"}},"required":["signalId","ref","label"]}},"decision":{"$ref":"#/components/schemas/RecommendationDecision"},"outcome":{"$ref":"#/components/schemas/RecommendationOutcome"},"workItemLinks":{"type":"array","items":{"$ref":"#/components/schemas/WorkItemLink"}},"generatedAt":{"type":"integer","description":"Epoch milliseconds."},"confidence":{"$ref":"#/components/schemas/RecommendationConfidence"},"confidenceRationale":{"type":"string"},"counterEvidenceNote":{"type":"string"},"actionItems":{"type":"array","items":{"$ref":"#/components/schemas/ActionItem"}},"evidenceSnapshot":{"$ref":"#/components/schemas/EvidenceSnapshot"}}},"RecommendationListItem":{"type":"object","required":["recommendationId","title","lifecycleState","confidence","signals","updatedAt"],"properties":{"recommendationId":{"type":"string"},"title":{"type":"string"},"lifecycleState":{"$ref":"#/components/schemas/RecommendationLifecycleState"},"confidence":{"$ref":"#/components/schemas/RecommendationConfidence"},"signals":{"type":"array","items":{"type":"object","required":["ref","label"],"properties":{"ref":{"type":"string"},"label":{"type":"string"}}}},"updatedAt":{"type":"integer","description":"Epoch milliseconds: the latest thing that happened to the recommendation."}}},"ResearchGap":{"type":"object","description":"The row is the whole object; there is no deeper read.","required":["researchGapId","signalId","signalRef","type","status","description","suggestedAction"],"properties":{"researchGapId":{"type":"string"},"signalId":{"type":"string"},"signalRef":{"type":"string","example":"SIG-14"},"type":{"$ref":"#/components/schemas/ResearchGapType"},"status":{"$ref":"#/components/schemas/ResearchGapStatus"},"description":{"type":"string"},"suggestedAction":{"type":"string"},"draftedStudyId":{"type":"string"}}},"StudyListItem":{"type":"object","required":["studyId","name","status","participantCount","createdAt"],"properties":{"studyId":{"type":"string"},"name":{"type":"string"},"status":{"$ref":"#/components/schemas/StudyStatus"},"participantCount":{"type":"integer"},"themeCount":{"type":"integer"},"createdAt":{"type":"integer","description":"Epoch milliseconds."}}},"StudyReadoutPacket":{"type":"object","required":["studyId","name","status","objective","openQuestions","summary","themes","sourceItemCount"],"properties":{"studyId":{"type":"string"},"name":{"type":"string"},"status":{"$ref":"#/components/schemas/StudyStatus"},"objective":{"type":["string","null"]},"openQuestions":{"type":"array","items":{"type":"string"}},"summary":{"type":["object","null"],"required":["narrative","confidence","suggestedNextQuestions","stats","coverage","lastUpdated"],"properties":{"narrative":{"type":"string"},"confidence":{"type":"string","enum":["low","moderate","high"]},"suggestedNextQuestions":{"type":"array","items":{"type":"string"}},"stats":{"type":"object","required":["totalSourceItems"],"properties":{"totalSourceItems":{"type":"integer"},"completionRate":{"type":"number"},"avgQualityScore":{"type":"number"}}},"coverage":{"type":"array","description":"Advisory \"what we didn't learn\": learning targets that stayed thin across enough interviews. Empty when nothing is worth flagging - the normal case. Never a verdict, never attributed to a participant.","items":{"type":"object","required":["targetId","question","priority","substantiveSourceItems","totalSourceItems"],"properties":{"targetId":{"type":"string"},"question":{"type":"string"},"priority":{"type":"string","enum":["primary","secondary"]},"substantiveSourceItems":{"type":"integer"},"totalSourceItems":{"type":"integer"}}}},"lastUpdated":{"type":"integer","description":"Epoch milliseconds."}}},"themes":{"type":"array","items":{"type":"object","required":["themeId","label","description","sentiment","strength","trajectory","distinctParticipants","preview"],"properties":{"themeId":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"sentiment":{"$ref":"#/components/schemas/Sentiment"},"strength":{"$ref":"#/components/schemas/ThemeStrength"},"trajectory":{"$ref":"#/components/schemas/Trajectory"},"parentThemeId":{"type":"string"},"preview":{"type":"array","maxItems":1,"description":"Single-quote taster; GET /v1/evidence?themeIds= serves every quote.","items":{"type":"object","required":["sourceItemId","quote","participantName"],"properties":{"sourceItemId":{"type":"string"},"quote":{"type":"string"},"timeInCallSecs":{"type":"number"},"participantName":{"type":"string"}}}},"distinctParticipants":{"type":"integer"}}}},"sourceItemCount":{"type":"integer"}}},"SourceItemListItem":{"type":"object","required":["sourceItemId","status","participantName","capturedAt","evidenceCount"],"properties":{"sourceItemId":{"type":"string"},"status":{"$ref":"#/components/schemas/SourceItemStatus"},"participantName":{"type":"string"},"capturedAt":{"type":"integer","description":"Epoch milliseconds."},"qualityScore":{"type":"number","minimum":0,"maximum":1},"evidenceCount":{"type":"integer"}}},"SignalPacket":{"type":"object","required":["signalId","belief","implication","recommendedNextStep","evidence","counterEvidence","perStudy","recommendation","researchGaps"],"properties":{"signalId":{"type":"string"},"belief":{"type":"object","required":["label","description","trajectory","stage","confidence","firstSeen","lastUpdated","confidenceBand","ref","archived","actionable"],"properties":{"label":{"type":"string"},"description":{"type":"string"},"trajectory":{"$ref":"#/components/schemas/Trajectory"},"stage":{"$ref":"#/components/schemas/SignalStage"},"confidence":{"type":"number","minimum":0,"maximum":1},"firstSeen":{"type":"integer","description":"Epoch milliseconds."},"lastUpdated":{"type":"integer","description":"Epoch milliseconds."},"ref":{"type":"string","description":"Citable handle, e.g. \"SIG-14\". Use it in human-facing text; API calls always take signalId."},"confidenceBand":{"$ref":"#/components/schemas/ConfidenceBand"},"archived":{"type":"boolean","description":"The archive axis: true for a closed, preserved record."},"archiveReason":{"$ref":"#/components/schemas/SignalArchiveReason","description":"Present iff archived."},"archivedAt":{"type":"integer","description":"Epoch milliseconds. Present iff archived."},"postShipReceipt":{"type":"object","description":"Dated post-ship quiet record: no new unprompted corroboration in the span since quietSince, with interviewCount organic team-wide interviews in the trailing windowDays (listening coverage, not topical coverage). Stamps only once at least two distinct humans were interviewed inside that window; cleared if a contradiction alarm later mints for the same ship event. An observation, never a verdict - do not render it as verified/fixed. Derive the quiet span from quietSince; windowDays is the gate threshold and the denominator's scope.","required":["recommendationId","shippedAt","observedAt","quietSince","windowDays","interviewCount","trajectory"],"properties":{"recommendationId":{"type":"string"},"shippedAt":{"type":"integer","description":"Epoch milliseconds."},"observedAt":{"type":"integer","description":"Epoch milliseconds."},"quietSince":{"type":"integer","description":"Epoch milliseconds. Start of the span the claim covers: the ship, or the last unprompted supporting quote if one reset the clock."},"windowDays":{"type":"integer","description":"The quiet-gate threshold in days, and the trailing window interviewCount is counted over."},"interviewCount":{"type":"integer","description":"Organic, analyzed, high-quality interviews conducted team-wide in the trailing windowDays - the listening behind the claim."},"trajectory":{"$ref":"#/components/schemas/Trajectory"}}},"actionable":{"type":"boolean","description":"Derived, never stored: true while the signal has a published recommendation that is neither dismissed nor validated. Never true while archived."}}},"implication":{"type":["string","null"]},"recommendedNextStep":{"$ref":"#/components/schemas/RecommendedNextStep"},"evidence":{"type":"object","required":["items","distinctSourceItems","studyCount","quoteCount","freshness","counterEvidenceCount","distinctParticipants"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/EvidenceItem"},"description":"A short taster only, at most 3, strongest first; quoteCount carries the full total and GET /v1/evidence?signalId= serves every quote."},"distinctSourceItems":{"type":"integer"},"studyCount":{"type":"integer"},"quoteCount":{"type":"integer","description":"Total supporting quotes behind this signal, including those beyond the embedded items cap."},"counterEvidenceCount":{"type":"integer","description":"Total counter-evidence quotes."},"freshness":{"type":"object","required":["latestCapturedAt","isStale"],"properties":{"latestCapturedAt":{"type":["integer","null"]},"isStale":{"type":"boolean"}}},"distinctParticipants":{"type":"integer"}}},"counterEvidence":{"type":"array","items":{"$ref":"#/components/schemas/EvidenceItem"},"description":"A short taster only, at most 2; counterEvidenceCount carries the full total."},"perStudy":{"type":"array","items":{"type":"object","required":["studyId","studyName","relationship","narrative","addedAt"],"properties":{"studyId":{"type":"string"},"studyName":{"type":"string"},"relationship":{"type":"string","enum":["supports","contradicts","nuances"]},"narrative":{"type":"string"},"addedAt":{"type":"integer","description":"Epoch milliseconds."}}}},"recommendation":{"anyOf":[{"type":"object","description":"Pointer, not the object — GET /v1/recommendations/{id} serves it in full.","required":["recommendationId","title","lifecycleState"],"properties":{"recommendationId":{"type":"string"},"title":{"type":"string"},"lifecycleState":{"$ref":"#/components/schemas/RecommendationLifecycleState"}}},{"type":"null"}]},"researchGaps":{"type":"array","items":{"type":"object","required":["researchGapId","type","status"],"properties":{"researchGapId":{"type":"string"},"type":{"$ref":"#/components/schemas/ResearchGapType"},"status":{"$ref":"#/components/schemas/ResearchGapStatus"}}},"description":"Non-dismissed, newest first."}}},"RecommendationWriteResult":{"type":"object","required":["recommendationId"],"properties":{"recommendationId":{"type":"string"}}},"SignalListItem":{"type":"object","required":["label","stage","signalId","ref","description","trajectory","confidenceBand","archived","actionable","participantCount","studyCount","lastUpdated"],"properties":{"label":{"type":"string"},"stage":{"$ref":"#/components/schemas/SignalStage"},"signalId":{"type":"string","description":"Opaque signal identifier."},"trajectory":{"$ref":"#/components/schemas/Trajectory"},"studyCount":{"type":"integer","minimum":0},"participantCount":{"type":"integer","minimum":0,"description":"Distinct participants backing the signal."},"lastUpdated":{"type":"integer","description":"Epoch milliseconds."},"description":{"type":"string"},"ref":{"type":"string","description":"Citable handle, e.g. \"SIG-14\". Use it in human-facing text; API calls always take signalId."},"confidenceBand":{"$ref":"#/components/schemas/ConfidenceBand"},"archived":{"type":"boolean","description":"The archive axis: true for a closed, preserved record."},"archiveReason":{"$ref":"#/components/schemas/SignalArchiveReason","description":"Present iff archived."},"actionable":{"type":"boolean","description":"Derived, never stored: true while the signal has a published recommendation that is neither dismissed nor validated. Never true while archived."}}},"ApiError":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["invalid_request","unauthenticated","forbidden","not_found","invalid_transition","rate_limited","internal"]},"message":{"type":"string"},"retryAfterMs":{"type":"integer"}}}}},"ActionCardType":{"type":"string","enum":["fix_contradicted","new_recommendation","live_call","cross_study_signal","research_gap","follow_up_suggestion","low_balance","theme_contradiction","study_saturation","interview_failure"]},"ActionCard":{"type":"object","description":"One pending inbox item. The list arrives ranked: priority tier ascending (1 = most urgent), then per-item urgency, then newest first.","required":["actionCardId","type","summary","priority","createdAt","refs","studies"],"properties":{"actionCardId":{"type":"string"},"type":{"$ref":"#/components/schemas/ActionCardType"},"summary":{"type":"string"},"quote":{"type":"string","description":"live_call only: the verbatim participant quote that justifies the card."},"reason":{"type":"string","enum":["contradicts_signal","emotional_intensity","churn_risk"],"description":"live_call only: why the card exists."},"priority":{"type":"integer","minimum":1,"maximum":10,"description":"Priority tier; lower is more urgent."},"createdAt":{"type":"integer","description":"Epoch milliseconds."},"refs":{"type":"object","description":"Ids of the entities the card points at.","properties":{"studyIds":{"type":"array","items":{"type":"string"}},"sourceItemId":{"type":"string"},"themeIds":{"type":"array","items":{"type":"string"}},"signalId":{"type":"string"},"recommendationId":{"type":"string"},"participantId":{"type":"string"}}},"participant":{"type":"object","description":"live_call only: display-only projection of the referenced participant - contact info the org already had, never anything new.","required":["participantId"],"properties":{"participantId":{"type":"string"},"displayName":{"type":"string"},"email":{"type":"string"},"lastContactedAt":{"type":"integer","description":"Epoch milliseconds."}}},"studies":{"type":"array","description":"Resolved refs.studyIds.","items":{"type":"object","required":["studyId","name"],"properties":{"studyId":{"type":"string"},"name":{"type":"string"}}}}}},"ActionCardWriteResult":{"type":"object","required":["actionCardId"],"properties":{"actionCardId":{"type":"string"}}},"DraftStudyFromGapResult":{"type":"object","description":"Drafting is idempotent: a gap only ever drafts one study, so a repeat call returns the same studyId.","required":["researchGapId","studyId"],"properties":{"researchGapId":{"type":"string"},"studyId":{"type":"string"}}},"AcceptanceCriteria":{"type":"object","description":"A definition of done assembled mechanically from stored fields - no generated prose.","required":["actionChecks","beliefChecks","verification"],"properties":{"actionChecks":{"type":"array","items":{"type":"string"}},"beliefChecks":{"type":"array","items":{"type":"string"},"description":"One machine-checkable check per linked signal."},"verification":{"type":"string"}}},"WorkPacketSignal":{"type":"object","required":["signalId","label","description","stage","trajectory","participantCount","quoteCount","freshness","ref","confidenceBand","archived"],"properties":{"signalId":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"stage":{"$ref":"#/components/schemas/SignalStage"},"trajectory":{"$ref":"#/components/schemas/Trajectory"},"participantCount":{"type":"integer"},"quoteCount":{"type":"integer"},"freshness":{"type":"object","required":["latestCapturedAt","isStale"],"properties":{"latestCapturedAt":{"type":["integer","null"]},"isStale":{"type":"boolean"}}},"ref":{"type":"string","description":"Citable handle, e.g. \"SIG-14\"."},"confidenceBand":{"$ref":"#/components/schemas/ConfidenceBand"},"archived":{"type":"boolean","description":"The archive axis: true for a closed, preserved record."}}},"WorkPacket":{"type":"object","description":"A recommendation bundled for handoff to whoever ships the fix - human or coding agent: the problem, the beliefs behind it, verbatim evidence and counter-evidence, and a definition of done in belief terms. The markdown field is the same packet rendered for pasting into an issue tracker.","required":["recommendation","signals","evidence","counterEvidence","acceptanceCriteria","markdown"],"properties":{"recommendation":{"type":"object","required":["recommendationId","title","body","lifecycleState","confidence","actionItems","workItemLinks","generatedAt"],"properties":{"recommendationId":{"type":"string"},"title":{"type":"string"},"body":{"type":"string"},"lifecycleState":{"$ref":"#/components/schemas/RecommendationLifecycleState"},"confidence":{"$ref":"#/components/schemas/RecommendationConfidence"},"confidenceRationale":{"type":"string"},"counterEvidenceNote":{"type":"string"},"actionItems":{"type":"array","items":{"$ref":"#/components/schemas/ActionItem"}},"workItemLinks":{"type":"array","items":{"$ref":"#/components/schemas/WorkItemLink"}},"generatedAt":{"type":"integer"}}},"signals":{"type":"array","items":{"$ref":"#/components/schemas/WorkPacketSignal"}},"evidence":{"type":"array","items":{"$ref":"#/components/schemas/EvidenceItem"}},"counterEvidence":{"type":"array","items":{"$ref":"#/components/schemas/EvidenceItem"}},"acceptanceCriteria":{"$ref":"#/components/schemas/AcceptanceCriteria"},"markdown":{"type":"string","description":"Convenience rendering of the structured fields as issue-tracker-ready markdown. Contains no data absent from the structured fields; consumers building their own presentation should ignore it."}}},"ConfidenceBand":{"type":"string","enum":["high","moderate","low"],"description":"Coarse confidence banding shared with the app UI: high >= 0.75, moderate >= 0.5, low below."},"SignalArchiveReason":{"type":"string","enum":["validated","abandoned"],"description":"How an archived signal's loop closed: validated = the team acted and confirmed the outcome; abandoned = set aside."},"SignalStateOfPlay":{"type":"string","enum":["emerging","established","actionable","archived"],"description":"Signal list filter vocabulary. \"actionable\" and \"archived\" filter on the derived facts (`actionable`, `archived`); \"emerging\"/\"established\" filter by stage among signals that are neither actionable nor archived. Each value selects a mutually exclusive view, matching the app's board columns."},"AskCitation":{"type":"object","required":["ref","signalId","evidenceIds"],"properties":{"ref":{"type":"string","description":"Citable signal handle, e.g. \"SIG-14\" — open with GET /v1/signals/{signalId}."},"signalId":{"type":"string"},"evidenceIds":{"type":"array","items":{"type":"string"},"description":"The quotes backing the claims made about this signal, fetchable via GET /v1/evidence?evidenceIds=. Empty when the claim rests on the signal's aggregate rather than a specific quote. The grouping is the model's reading — which quotes served which line of reasoning — not a claim that the signal's evidence basis owns them; a cited quote may live under another signal or none, and is still real, attributed, and by-id fetchable."}}},"AskResult":{"type":"object","required":["answer","citations","searchedAt"],"properties":{"answer":{"type":"string","description":"Plain-prose answer with SIG refs cited inline; an honest refusal when the belief layer does not cover the question."},"citations":{"type":"array","items":{"$ref":"#/components/schemas/AskCitation"},"description":"One entry per signal the answer relied on, verified server-side against the material the model saw."},"searchedAt":{"type":"integer","description":"Epoch ms timestamp of the evidence gather behind the answer."}}}},"responses":{"BadRequest":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"example":{"error":{"code":"invalid_request","message":"Invalid request"}}}}},"Unauthenticated":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"example":{"error":{"code":"unauthenticated","message":"Missing or invalid API key"}}}}},"Forbidden":{"description":"The API key is valid but not allowed here (e.g. not organization-scoped, or no team).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"example":{"error":{"code":"forbidden","message":"Forbidden"}}}}},"NotFound":{"description":"Resource not found for this team.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"example":{"error":{"code":"not_found","message":"Not found"}}}}},"Conflict":{"description":"The action isn't allowed from the resource's current state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"example":{"error":{"code":"invalid_transition","message":"Invalid transition"}}}}},"RateLimited":{"description":"Too many requests.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"example":{"error":{"code":"rate_limited","message":"Too many requests","retryAfterMs":60000}}}}},"InternalError":{"description":"Something went wrong on our end.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"example":{"error":{"code":"internal","message":"Internal error"}}}}}}}}