Files
zooqueen 252e1cb076 Generated clients call the API where it is mounted
`insights/mount.py` is explicit: `/v1/` is the mount and `/api/` is the older
spelling, kept on the wire for released CLI builds and personal-API-key
holders and rewritten once at the edge by `ApiRewriteMiddleware`. Our own
frontend was still on the older spelling: the orval clients were generated
two days before the mount moved and the move did not regenerate them, so 70
files and 2323 paths said `/api/`.

That is not only a spelling. `prepareUrl` in `lib/api.ts` rewrites `/v1/*` to
the selected region's host in OAuth mode and nothing else, because that is
what the cloud CORS allowlist covers — so every generated call quietly stayed
same-origin, and `getExportsContentRetrieveUrl` builds an `<img src>` that
never reaches `lib/api.ts` at all.

The clients are regenerated from a spec that needs postgres, the datastore and
Django, so this is the path strings only, which is the whole of what the mount
move changes in them: orval copies paths verbatim from the spec, and the spec
takes them from the URLconf. Doc comments carried the same stale prefix and
move with them; their Python docstrings already say `/v1/`. The two frozen
OAuth client-metadata paths are identifiers rather than API surface and appear
in none of these files.

The Slack task prompt told the agent to POST to
`$INSIGHTS_API_URL/api/projects/...`, which is a live request path, not prose.
2026-08-24 23:13:08 -07:00

4146 lines
169 KiB
TypeScript
Generated
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Auto-generated from the Django backend OpenAPI schema.
* To modify these types, update the Django serializers or views, then run:
* insightscli build:openapi
* Questions or issues? #team-devex on Slack
*
* Insights API - generated
* OpenAPI spec version: 1.0.0
*/
export interface PauseStateResponseApi {
/**
* The timestamp the pipeline is paused until, or null if not paused/not running.
* @nullable
*/
paused_until: string | null
}
export interface PaginatedPauseStateResponseListApi {
count: number
/** @nullable */
next?: string | null
/** @nullable */
previous?: string | null
results: PauseStateResponseApi[]
}
export interface PauseUntilRequestApi {
/** Pause the grouping pipeline until this timestamp (ISO 8601). */
timestamp: string
}
export interface PauseResponseApi {
/** Always 'paused'. */
status: string
/** The timestamp the pipeline is paused until. */
paused_until: string
}
/**
* * `potential` - Potential
* * `candidate` - Candidate
* * `in_progress` - In Progress
* * `pending_input` - Pending Input
* * `ready` - Ready
* * `resolved` - Resolved
* * `failed` - Failed
* * `deleted` - Deleted
* * `suppressed` - Suppressed
*/
export type SignalReportStatusEnumApi = (typeof SignalReportStatusEnumApi)[keyof typeof SignalReportStatusEnumApi]
export const SignalReportStatusEnumApi = {
Potential: 'potential',
Candidate: 'candidate',
InProgress: 'in_progress',
PendingInput: 'pending_input',
Ready: 'ready',
Resolved: 'resolved',
Failed: 'failed',
Deleted: 'deleted',
Suppressed: 'suppressed',
} as const
/**
* * `small` - small
* * `medium` - medium
* * `large` - large
*/
export type SizeEnumApi = (typeof SizeEnumApi)[keyof typeof SizeEnumApi]
export const SizeEnumApi = {
Small: 'small',
Medium: 'medium',
Large: 'large',
} as const
/**
* One chart attached to a report — rendered in the inbox and referenceable from the summary.
*/
export interface ReportChartApi {
/**
* Stable slug for this chart within the report (lowercase letters, numbers, underscores, hyphens; must start with a letter or number). Reference it from `summary` as a markdown link with a `chart:` target — `[Daily signups](chart:signups-drop)` — to place the chart at that point in the body. A chart you don't reference still renders, below the summary.
* @maxLength 100
*/
chart_id: string
/**
* Short heading shown above the chart.
* @maxLength 200
*/
title: string
/** The query node to render. `kind` must be `InsightVizNode` (an ad-hoc product analytics chart), `DataVisualizationNode` (a SQL series — a `InsightsQLQuery` source plus a `display`), or `SavedInsightNode` (an existing insight by `shortId`). Pin the window to absolute dates where the node supports it, so the reader sees the data you wrote about rather than whatever a relative range resolves to when they open the report. */
query: unknown
/**
* Optional one-line note on what to look at in the chart.
* @maxLength 500
* @nullable
*/
caption?: string | null
/** How much height the chart gets: `small` for a single number or a short series, `medium` for an ordinary graph, `large` when there are rows or a grid to read (retention, paths, a wide breakdown). Leave it out unless the default looks wrong — the inbox sizes a chart from its query, and two charts referenced from the same paragraph sit side by side.
*
* * `small` - small
* * `medium` - medium
* * `large` - large */
size?: SizeEnumApi | null
}
/**
* * `pr_incorrect` - PR incorrect
* * `pr_not_useful` - PR not useful
* * `duplicate` - Duplicate
* * `other` - Other
*/
export type SignalReportRefundReasonEnumApi =
(typeof SignalReportRefundReasonEnumApi)[keyof typeof SignalReportRefundReasonEnumApi]
export const SignalReportRefundReasonEnumApi = {
PrIncorrect: 'pr_incorrect',
PrNotUseful: 'pr_not_useful',
Duplicate: 'duplicate',
Other: 'other',
} as const
/**
* * `excluded` - Excluded
* * `credited` - Credited
*/
export type BillingPathEnumApi = (typeof BillingPathEnumApi)[keyof typeof BillingPathEnumApi]
export const BillingPathEnumApi = {
Excluded: 'excluded',
Credited: 'credited',
} as const
export interface SignalReportRefundApi {
readonly id: string
/** Why the user refunded this PR (feeds the refund review).
*
* * `pr_incorrect` - PR incorrect
* * `pr_not_useful` - PR not useful
* * `duplicate` - Duplicate
* * `other` - Other */
readonly reason: SignalReportRefundReasonEnumApi
/** Optional free-form note captured with the refund. */
readonly note: string
/** How the refund was executed, frozen at refund time: 'excluded' (same UTC day as the billable PR run — the report never reaches billing) or 'credited' (billing issues a Stripe customer-balance credit).
*
* * `excluded` - Excluded
* * `credited` - Credited */
readonly billing_path: BillingPathEnumApi
/** Signals credits refunded (flat per-PR charge snapshot; 1 credit = $0.01). */
readonly credits: number
/** The refunded implementation PR's GitHub URL, snapshotted at refund time. */
readonly pr_url: string
/** When the first billable PR run was created — the charge this reverses. */
readonly pr_run_created_at: string
/**
* USD amount the billing service credited (credited path only). Null until the sync completes; '0.00' is a legitimate outcome (e.g. the PR was inside the free tier).
* @nullable
* @pattern ^-?\d{0,8}(?:\.\d{0,2})?$
*/
readonly credit_amount_usd: string | null
/** Whether the billing service has acknowledged this refund. Always relevant for the credited path (the Stripe credit is issued asynchronously); excluded-path refunds need no billing sync and report false. */
readonly billing_synced: boolean
/** When the refund was created. */
readonly created_at: string
}
export type RefundIneligibilityReasonEnumApi =
(typeof RefundIneligibilityReasonEnumApi)[keyof typeof RefundIneligibilityReasonEnumApi]
export const RefundIneligibilityReasonEnumApi = {
AlreadyRefunded: 'already_refunded',
BillingExempt: 'billing_exempt',
NoBillablePr: 'no_billable_pr',
OutOfPeriod: 'out_of_period',
} as const
/**
* * `insights_health_check` - Insights health check
* * `insights_onboarding` - Insights onboarding
* * `insights_system` - Insights system
*/
export type BillingExemptReasonEnumApi = (typeof BillingExemptReasonEnumApi)[keyof typeof BillingExemptReasonEnumApi]
export const BillingExemptReasonEnumApi = {
InsightsHealthCheck: 'insights_health_check',
InsightsOnboarding: 'insights_onboarding',
InsightsSystem: 'insights_system',
} as const
export interface SignalReportApi {
readonly id: string
/** @nullable */
readonly title: string | null
/** @nullable */
readonly summary: string | null
readonly status: SignalReportStatusEnumApi
readonly total_weight: number
readonly signal_count: number
readonly signals_at_run: number
readonly created_at: string
readonly updated_at: string
readonly artefact_count: number
/** Charts the report shows, in the order they were written. The summary places one with a `[label](chart:<chart_id>)` link; the rest render below it. */
readonly charts: readonly ReportChartApi[]
/**
* P0–P4 from the latest priority judgment artefact (when present).
* @nullable
*/
readonly priority: string | null
/**
* Actionability choice from the latest actionability judgment artefact (when present).
* @nullable
*/
readonly actionability: string | null
/**
* Whether the issue is already being handled — fixed in recent changes, or with a fix in flight (an open PR, a recently active branch, an assigned / in-progress issue or agent task) — from the actionability judgment artefact.
* @nullable
*/
readonly already_addressed: boolean | null
/**
* Reason code from the latest dismissal artefact, set when the report was suppressed (when present).
* @nullable
*/
readonly dismissal_reason: string | null
/**
* Free-form note captured alongside the dismissal reason (when present).
* @nullable
*/
readonly dismissal_note: string | null
readonly is_suggested_reviewer: boolean
/** Distinct source products contributing signals to this report (from Datastore). */
readonly source_products: readonly string[]
/**
* skill_name slug of the scout that authored this report, when scout-authored (from Datastore); null otherwise.
* @nullable
*/
readonly scout_name: string | null
/**
* PR URL from the latest implementation task run, if available.
* @nullable
*/
readonly implementation_pr_url: string | null
/** Whether that implementation PR is merged, per the GitHub webhook. False when there is no PR or it hasn't merged. Report status doesn't imply this: a resolved report may have been resolved directly, without a merged PR. */
readonly implementation_pr_merged: boolean
/** The report's PR refund, when one exists. One refund per report, ever. */
readonly refund: SignalReportRefundApi | null
/** Why refunding this report's PR would be rejected right now, or null when a refund would be accepted (see the field's schema for the reason values). */
readonly refund_ineligibility_reason: RefundIneligibilityReasonEnumApi | null
/** Non-null when this report is system-marked never-billable (Insights-system origin, e.g. a health-check scout finding) — its implementation PRs are free and cannot be refunded because nothing was charged.
*
* * `insights_health_check` - Insights health check
* * `insights_onboarding` - Insights onboarding
* * `insights_system` - Insights system */
readonly billing_exempt_reason: BillingExemptReasonEnumApi | null
}
export interface PaginatedSignalReportListApi {
count: number
/** @nullable */
next?: string | null
/** @nullable */
previous?: string | null
results: SignalReportApi[]
}
/**
* Editable human-facing fields on a signal report (PATCH).
*
* Both fields are optional so a caller can change either independently, but at least one
* must be supplied. Every other report field — status, weights, judgments — is owned by the
* signals pipeline and is deliberately not writable here.
*/
export interface PatchedSignalReportContentUpdateApi {
/**
* New human-facing title for the report. Omit to leave the title unchanged.
* @minLength 1
* @maxLength 300
*/
title?: string
/**
* New summary (the report's description) explaining what the report is about. Omit to leave the summary unchanged.
* @minLength 1
* @maxLength 10000
*/
summary?: string
}
/**
* * `positive` - positive
* * `negative` - negative
*/
export type SentimentEnumApi = (typeof SentimentEnumApi)[keyof typeof SentimentEnumApi]
export const SentimentEnumApi = {
Positive: 'positive',
Negative: 'negative',
} as const
export interface SignalReportFeedbackRequestApi {
/** The rating left on the report: 'positive' (thumbs up) or 'negative' (thumbs down).
*
* * `positive` - positive
* * `negative` - negative */
sentiment: SentimentEnumApi
/**
* Free-form note explaining the rating. Capped at 4000 characters. Only submitted alongside a note — a bare thumb carries none — and, for a report authored by a scout, forwarded to that scout as a steering note.
* @maxLength 4000
*/
note: string
}
export interface SignalReportFeedbackResponseApi {
/** Whether the note was forwarded to the report's authoring scout as a steering note. False when the report has no resolvable authoring scout, or the caller lacks scout-steering access. */
forwarded: boolean
}
/**
* One CI check on a pull request's head commit — a GitHub Actions check run or a legacy commit
* status, normalized to a common shape.
*/
export interface PullRequestCheckApi {
/** Check run name or status context. */
readonly name: string
/**
* Lifecycle state: 'queued', 'in_progress', or 'completed'.
* @nullable
*/
readonly status: string | null
/**
* Outcome once completed: 'success', 'failure', 'neutral', 'cancelled', 'skipped', 'timed_out', or 'action_required'. Null while still running.
* @nullable
*/
readonly conclusion: string | null
/**
* Link to the check run / status detail on GitHub.
* @nullable
*/
readonly url: string | null
}
/**
* Response for the PR checks endpoint — the CI status of a report's implementation PR.
*/
export interface PullRequestChecksResponseApi {
readonly checks: readonly PullRequestCheckApi[]
}
/**
* * `conversation` - conversation
* * `review` - review
*/
export type CommentTypeEnumApi = (typeof CommentTypeEnumApi)[keyof typeof CommentTypeEnumApi]
export const CommentTypeEnumApi = {
Conversation: 'conversation',
Review: 'review',
} as const
/**
* * `LEFT` - LEFT
* * `RIGHT` - RIGHT
*/
export type SideEnumApi = (typeof SideEnumApi)[keyof typeof SideEnumApi]
export const SideEnumApi = {
Left: 'LEFT',
Right: 'RIGHT',
} as const
/**
* One emoji reaction on a review comment, with the reactor so the viewer's own can be toggled.
*/
export interface PullRequestCommentReactionApi {
/** GitHub reaction id (needed to remove it). */
readonly id: string
/** Reaction key: '+1', '-1', 'laugh', 'hooray', 'confused', 'heart', 'rocket', or 'eyes'. */
readonly content: string
/**
* GitHub login of the user who added the reaction.
* @nullable
*/
readonly user_login: string | null
}
/**
* One comment on a pull request — a conversation comment or an inline review comment.
*/
export interface PullRequestCommentApi {
/** GitHub comment id. */
readonly id: string
/**
* Comment author's GitHub login.
* @nullable
*/
readonly author: string | null
/**
* Author's GitHub avatar URL.
* @nullable
*/
readonly author_avatar_url: string | null
/** Comment body (GitHub-flavored markdown). */
readonly body: string
/**
* ISO 8601 creation timestamp.
* @nullable
*/
readonly created_at: string | null
/**
* Link to the comment on GitHub.
* @nullable
*/
readonly url: string | null
/** 'conversation' for a PR discussion comment, 'review' for an inline code-review comment.
*
* * `conversation` - conversation
* * `review` - review */
readonly comment_type: CommentTypeEnumApi
/**
* File path the review comment is anchored to (review comments only).
* @nullable
*/
readonly path: string | null
/**
* Line in the diff the review comment is anchored to — the end line for multi-line comments (review comments only; null when the comment is outdated relative to the PR head).
* @nullable
*/
readonly line: number | null
/**
* First line of a multi-line review comment's range (review comments only).
* @nullable
*/
readonly start_line: number | null
/** Diff side the review comment is anchored to: 'LEFT' = deletions, 'RIGHT' = additions (review comments only).
*
* * `LEFT` - LEFT
* * `RIGHT` - RIGHT */
readonly side: SideEnumApi | null
/**
* Diff hunk excerpt the review comment applies to (review comments only).
* @nullable
*/
readonly diff_hunk: string | null
/**
* Id of the thread root comment this one replies to; null for thread roots and conversation comments.
* @nullable
*/
readonly in_reply_to_id: string | null
/**
* SHA of the commit the review comment was made against (review comments only).
* @nullable
*/
readonly commit_id: string | null
/** Emoji reactions on this review comment, one entry per reactor. */
readonly reactions: readonly PullRequestCommentReactionApi[]
}
/**
* Response for the PR comments endpoint — conversation and review comments merged chronologically.
*/
export interface PullRequestCommentsResponseApi {
readonly comments: readonly PullRequestCommentApi[]
}
/**
* Request body for posting an inline PR review comment as the requesting user.
*
* Two shapes: a reply to an existing thread (only `body` + `in_reply_to`), or a new
* thread on a diff line (`body` + `path` + `line`, optionally `side`).
*/
export interface PullRequestReviewCommentCreateApi {
/**
* Comment body (GitHub-flavored markdown).
* @maxLength 65536
*/
body: string
/**
* Numeric id of the thread root comment to reply to. When set, path/line/side are ignored.
* @nullable
* @pattern ^[0-9]+$
*/
in_reply_to?: string | null
/**
* File path to anchor a new comment thread to (required when starting a new thread).
* @nullable
*/
path?: string | null
/**
* Diff line to anchor a new comment thread to (required when starting a new thread).
* @minimum 1
* @nullable
*/
line?: number | null
/** Diff side of the anchor line: 'LEFT' = deletions, 'RIGHT' = additions. Defaults to 'RIGHT'.
*
* * `LEFT` - LEFT
* * `RIGHT` - RIGHT */
side?: SideEnumApi | null
}
/**
* Response after posting a review comment — the created comment in the normalized PR-comment shape.
*/
export interface PullRequestReviewCommentCreateResponseApi {
readonly comment: PullRequestCommentApi
}
/**
* Request body for editing a review comment's markdown body.
*/
export interface PatchedPullRequestReviewCommentUpdateApi {
/**
* New comment body (GitHub-flavored markdown).
* @maxLength 65536
*/
body?: string
}
/**
* * `+1` - +1
* * `-1` - -1
* * `laugh` - laugh
* * `hooray` - hooray
* * `confused` - confused
* * `heart` - heart
* * `rocket` - rocket
* * `eyes` - eyes
*/
export type ContentEnumApi = (typeof ContentEnumApi)[keyof typeof ContentEnumApi]
export const ContentEnumApi = {
'1': '+1',
NumberMinus1: '-1',
Laugh: 'laugh',
Hooray: 'hooray',
Confused: 'confused',
Heart: 'heart',
Rocket: 'rocket',
Eyes: 'eyes',
} as const
/**
* Request body for adding an emoji reaction to a review comment.
*/
export interface PullRequestReviewCommentReactionCreateApi {
/** Reaction to add: one of '+1', '-1', 'laugh', 'hooray', 'confused', 'heart', 'rocket', 'eyes'.
*
* * `+1` - +1
* * `-1` - -1
* * `laugh` - laugh
* * `hooray` - hooray
* * `confused` - confused
* * `heart` - heart
* * `rocket` - rocket
* * `eyes` - eyes */
content: ContentEnumApi
}
/**
* Response after adding a reaction — the created reaction, so the frontend can track its id.
*/
export interface PullRequestReviewCommentReactionCreateResponseApi {
readonly reaction: PullRequestCommentReactionApi
}
export interface SignalReportRefundRequestApi {
/** Why this PR is being refunded. One of: pr_incorrect (the PR doesn't address what the report promised), pr_not_useful (technically fine but not worth paying for), duplicate (covers work already charged elsewhere), other. Required — refund reviews key on it.
*
* * `pr_incorrect` - PR incorrect
* * `pr_not_useful` - PR not useful
* * `duplicate` - Duplicate
* * `other` - Other */
reason: SignalReportRefundReasonEnumApi
/**
* Optional free-form context for the refund; stored on the refund and echoed in the report's dismissal artefact. Capped at 4000 characters.
* @maxLength 4000
*/
note?: string
}
export interface SignalReportRefundResponseApi {
readonly id: string
/** Why the user refunded this PR (feeds the refund review).
*
* * `pr_incorrect` - PR incorrect
* * `pr_not_useful` - PR not useful
* * `duplicate` - Duplicate
* * `other` - Other */
readonly reason: SignalReportRefundReasonEnumApi
/** Optional free-form note captured with the refund. */
readonly note: string
/** How the refund was executed, frozen at refund time: 'excluded' (same UTC day as the billable PR run — the report never reaches billing) or 'credited' (billing issues a Stripe customer-balance credit).
*
* * `excluded` - Excluded
* * `credited` - Credited */
readonly billing_path: BillingPathEnumApi
/** Signals credits refunded (flat per-PR charge snapshot; 1 credit = $0.01). */
readonly credits: number
/** The refunded implementation PR's GitHub URL, snapshotted at refund time. */
readonly pr_url: string
/** When the first billable PR run was created — the charge this reverses. */
readonly pr_run_created_at: string
/**
* USD amount the billing service credited (credited path only). Null until the sync completes; '0.00' is a legitimate outcome (e.g. the PR was inside the free tier).
* @nullable
* @pattern ^-?\d{0,8}(?:\.\d{0,2})?$
*/
readonly credit_amount_usd: string | null
/** Whether the billing service has acknowledged this refund. Always relevant for the credited path (the Stripe credit is issued asynchronously); excluded-path refunds need no billing sync and report false. */
readonly billing_synced: boolean
/** When the refund was created. */
readonly created_at: string
/** True when the report already had a refund and that existing refund is returned unchanged — refunds are one-per-report and repeat calls are idempotent. */
readonly already_refunded: boolean
}
/**
* * `session_replay` - session_replay
* * `llm_analytics` - llm_analytics
* * `github` - github
* * `linear` - linear
* * `jira` - jira
* * `zendesk` - zendesk
* * `conversations` - conversations
* * `error_tracking` - error_tracking
* * `endpoints` - endpoints
* * `pganalyze` - pganalyze
* * `signals_scout` - signals_scout
* * `logs` - logs
* * `health_checks` - health_checks
* * `replay_vision` - replay_vision
* * `analytics` - analytics
* * `freshdesk` - freshdesk
* * `freshservice` - freshservice
* * `front` - front
* * `gorgias` - gorgias
* * `kustomer` - kustomer
* * `dixa` - dixa
* * `plain` - plain
* * `gitlab` - gitlab
* * `gitea` - gitea
* * `shortcut` - shortcut
* * `sentry` - sentry
* * `rollbar` - rollbar
* * `bugsnag` - bugsnag
* * `honeybadger` - honeybadger
* * `raygun` - raygun
* * `snyk` - snyk
* * `sonarqube` - sonarqube
* * `semgrep` - semgrep
* * `rapid7_insightvm` - rapid7_insightvm
* * `featurebase` - featurebase
* * `frill` - frill
* * `aha` - aha
* * `uservoice` - uservoice
* * `productboard` - productboard
* * `canny` - canny
* * `asknicely` - asknicely
* * `retently` - retently
* * `appfigures` - appfigures
* * `appfollow` - appfollow
* * `judgeme_reviews` - judgeme_reviews
* * `intercom` - intercom
* * `hubspot` - hubspot
* * `engineering_analytics` - engineering_analytics
* * `google_search_console` - google_search_console
*/
export type SignalSourceProductApi = (typeof SignalSourceProductApi)[keyof typeof SignalSourceProductApi]
export const SignalSourceProductApi = {
SessionReplay: 'session_replay',
LlmAnalytics: 'llm_analytics',
Github: 'github',
Linear: 'linear',
Jira: 'jira',
Zendesk: 'zendesk',
Conversations: 'conversations',
ErrorTracking: 'error_tracking',
Endpoints: 'endpoints',
Pganalyze: 'pganalyze',
SignalsScout: 'signals_scout',
Logs: 'logs',
HealthChecks: 'health_checks',
ReplayVision: 'replay_vision',
Analytics: 'analytics',
Freshdesk: 'freshdesk',
Freshservice: 'freshservice',
Front: 'front',
Gorgias: 'gorgias',
Kustomer: 'kustomer',
Dixa: 'dixa',
Plain: 'plain',
Gitlab: 'gitlab',
Gitea: 'gitea',
Shortcut: 'shortcut',
Sentry: 'sentry',
Rollbar: 'rollbar',
Bugsnag: 'bugsnag',
Honeybadger: 'honeybadger',
Raygun: 'raygun',
Snyk: 'snyk',
Sonarqube: 'sonarqube',
Semgrep: 'semgrep',
Rapid7Insightvm: 'rapid7_insightvm',
Featurebase: 'featurebase',
Frill: 'frill',
Aha: 'aha',
Uservoice: 'uservoice',
Productboard: 'productboard',
Canny: 'canny',
Asknicely: 'asknicely',
Retently: 'retently',
Appfigures: 'appfigures',
Appfollow: 'appfollow',
JudgemeReviews: 'judgeme_reviews',
Intercom: 'intercom',
Hubspot: 'hubspot',
EngineeringAnalytics: 'engineering_analytics',
GoogleSearchConsole: 'google_search_console',
} as const
/**
* * `session_analysis_cluster` - session_analysis_cluster
* * `session_problem` - session_problem
* * `evaluation` - evaluation
* * `evaluation_report` - evaluation_report
* * `issue` - issue
* * `ticket` - ticket
* * `issue_created` - issue_created
* * `issue_reopened` - issue_reopened
* * `issue_spiking` - issue_spiking
* * `endpoint_execution_failed` - endpoint_execution_failed
* * `endpoint_breakdown_limit_exceeded` - endpoint_breakdown_limit_exceeded
* * `cross_source_issue` - cross_source_issue
* * `alert_state_change` - alert_state_change
* * `health_issue` - health_issue
* * `scanner_finding` - scanner_finding
* * `anomaly_investigation` - anomaly_investigation
* * `feedback` - feedback
* * `review` - review
* * `ci_flaky_check` - ci_flaky_check
* * `ci_broken_default_branch` - ci_broken_default_branch
* * `ci_duration_regression` - ci_duration_regression
* * `search_opportunity` - search_opportunity
*/
export type SignalSourceTypeApi = (typeof SignalSourceTypeApi)[keyof typeof SignalSourceTypeApi]
export const SignalSourceTypeApi = {
SessionAnalysisCluster: 'session_analysis_cluster',
SessionProblem: 'session_problem',
Evaluation: 'evaluation',
EvaluationReport: 'evaluation_report',
Issue: 'issue',
Ticket: 'ticket',
IssueCreated: 'issue_created',
IssueReopened: 'issue_reopened',
IssueSpiking: 'issue_spiking',
EndpointExecutionFailed: 'endpoint_execution_failed',
EndpointBreakdownLimitExceeded: 'endpoint_breakdown_limit_exceeded',
CrossSourceIssue: 'cross_source_issue',
AlertStateChange: 'alert_state_change',
HealthIssue: 'health_issue',
ScannerFinding: 'scanner_finding',
AnomalyInvestigation: 'anomaly_investigation',
Feedback: 'feedback',
Review: 'review',
CiFlakyCheck: 'ci_flaky_check',
CiBrokenDefaultBranch: 'ci_broken_default_branch',
CiDurationRegression: 'ci_duration_regression',
SearchOpportunity: 'search_opportunity',
} as const
export type ProblemTypeEnumApi = (typeof ProblemTypeEnumApi)[keyof typeof ProblemTypeEnumApi]
export const ProblemTypeEnumApi = {
Confusion: 'confusion',
Abandonment: 'abandonment',
BlockingException: 'blocking_exception',
NonBlockingException: 'non_blocking_exception',
Failure: 'failure',
} as const
export interface SessionProblemEventEntryApi {
event: string
timestamp: string
current_url?: string | null
event_type?: string | null
interaction_text?: string | null
}
export interface SessionProblemSignalExtraApi {
session_id: string
segment_title: string
start_time: string
end_time: string
problem_type: ProblemTypeEnumApi
distinct_id: string
session_start_time?: string | null
session_end_time?: string | null
session_duration?: number | null
session_active_seconds?: number | null
exported_asset_id?: number | null
event_history?: SessionProblemEventEntryApi[] | null
}
export interface LlmEvalSignalExtraApi {
evaluation_id: string
target_event_id?: string | null
target_event_type?: string | null
trace_id: string
model?: string | null
provider?: string | null
}
export interface LlmEvalReportSignalExtraApi {
evaluation_id: string
evaluation_name: string
evaluation_description: string
report_id: string
report_run_id: string
period_start: string
period_end: string
}
export interface ZendeskTicketSignalExtraApi {
url: string
type: string | null
tags: string[]
created_at: string
priority: string | null
status: string
}
export interface GithubIssueSignalExtraApi {
html_url: string
number: number
labels: string[]
created_at: string
updated_at: string
locked: boolean
state: string
}
export interface LinearIssueSignalExtraApi {
url: string
identifier: string
number: number
priority: number
priority_label: string
labels: string[]
state_name: string | null
state_type: string | null
team_name: string | null
created_at: string
updated_at: string
}
export interface JiraIssueSignalExtraApi {
key: string
url: string | null
status: string | null
priority: string | null
assignee: string | null
labels: string[]
created: string | null
updated: string | null
}
export interface ConversationsTicketImageApi {
url: string
author: string
}
export interface ConversationsTicketSignalExtraApi {
ticket_number: number
channel_source: string
channel_detail: string | null
status: string
priority: string | null
created_at: string
email_subject: string | null
images?: ConversationsTicketImageApi[] | null
}
export interface ErrorTrackingSignalExtraApi {
fingerprint: string
}
export interface PgAnalyzeIssueReferenceApi {
kind?: string | null
name?: string | null
url?: string | null
queryText?: string | null
}
export interface PgAnalyzeIssueSignalExtraApi {
severity: string | null
references: PgAnalyzeIssueReferenceApi[]
database_id: string | null
server_human_id: string | null
server_name: string | null
synced_at: string
}
export interface EndpointExecutionFailedSignalExtraApi {
endpoint_name: string
endpoint_version: number | null
materialized: boolean
saved_query_id: string | null
error_class: string
error_message: string
}
export interface EndpointBreakdownLimitExceededSignalExtraApi {
endpoint_name: string
breakdown_limit: number
}
export type ReportPriorityApi = (typeof ReportPriorityApi)[keyof typeof ReportPriorityApi]
export const ReportPriorityApi = {
P0: 'P0',
P1: 'P1',
P2: 'P2',
P3: 'P3',
P4: 'P4',
} as const
export interface SignalsScoutEvidenceEntryApi {
source_product: string
entity_id?: string | null
summary: string
}
export interface SignalsScoutTimeRangeApi {
date_from: string
date_to: string
}
export interface SignalsScoutSignalExtraApi {
scout_run_id: string
task_run_id: string
task_id?: string | null
finding_id: string
skill_name: string
skill_version: number
confidence: number
severity?: ReportPriorityApi | null
hypothesis?: string | null
evidence: SignalsScoutEvidenceEntryApi[]
dedupe_keys?: string[] | null
tags?: string[] | null
time_range?: SignalsScoutTimeRangeApi | null
mcp_trace_id?: string | null
}
export type LogsAlertStateChangeSignalExtraActionEnumApi =
(typeof LogsAlertStateChangeSignalExtraActionEnumApi)[keyof typeof LogsAlertStateChangeSignalExtraActionEnumApi]
export const LogsAlertStateChangeSignalExtraActionEnumApi = {
Firing: 'firing',
Broken: 'broken',
} as const
export type LogsAlertStateChangeSignalExtraThresholdOperatorEnumApi =
(typeof LogsAlertStateChangeSignalExtraThresholdOperatorEnumApi)[keyof typeof LogsAlertStateChangeSignalExtraThresholdOperatorEnumApi]
export const LogsAlertStateChangeSignalExtraThresholdOperatorEnumApi = {
Above: 'above',
Below: 'below',
} as const
export type LogsAlertStateChangeSignalExtraApiFilters = { [key: string]: unknown }
export interface LogsAlertStateChangeSignalExtraApi {
alert_id: string
alert_name: string
action: LogsAlertStateChangeSignalExtraActionEnumApi
threshold_count: number
threshold_operator: LogsAlertStateChangeSignalExtraThresholdOperatorEnumApi
window_minutes: number
result_count: number | null
consecutive_failures: number
filters: LogsAlertStateChangeSignalExtraApiFilters
url: string
}
export interface ReplayVisionScannerFindingSignalExtraApi {
scanner_id: string
scanner_name: string
scanner_type: string
observation_id: string
session_id: string
confidence: number
problem_type: string
start_time: number
end_time: number
url: string
exported_asset_id: number
distinct_id?: string | null
recording_start_time?: string | null
recording_end_time?: string | null
recording_duration?: number | null
recording_active_seconds?: number | null
}
/**
* * `true_positive` - true_positive
* * `false_positive` - false_positive
* * `inconclusive` - inconclusive
*/
export type InvestigationVerdictEnumApi = (typeof InvestigationVerdictEnumApi)[keyof typeof InvestigationVerdictEnumApi]
export const InvestigationVerdictEnumApi = {
TruePositive: 'true_positive',
FalsePositive: 'false_positive',
Inconclusive: 'inconclusive',
} as const
export interface AnalyticsAnomalyInvestigationSignalExtraApi {
alert_id: string
alert_name: string
alert_check_id: string
insight_id: string
detector_type: string
verdict: InvestigationVerdictEnumApi
url: string
insight_name?: string | null
insight_short_id?: string | null
triggered_dates?: string[] | null
notebook_short_id?: string | null
}
export type HealthCheckSignalExtraSeverityEnumApi =
(typeof HealthCheckSignalExtraSeverityEnumApi)[keyof typeof HealthCheckSignalExtraSeverityEnumApi]
export const HealthCheckSignalExtraSeverityEnumApi = {
Critical: 'critical',
Warning: 'warning',
Info: 'info',
} as const
export type HealthCheckSignalExtraApiPayload = { [key: string]: unknown }
export interface HealthCheckSignalExtraApi {
kind: string
severity: HealthCheckSignalExtraSeverityEnumApi
issue_id: string
title: string
summary: string
link: string
url: string
payload: HealthCheckSignalExtraApiPayload
}
/**
* One immutable flaky observation: failed then passed on a later attempt of the same run,
* so only non-determinism can explain the flip.
*/
export interface EngineeringAnalyticsCIFlakyCheckSignalExtraApi {
repo_owner: string
repo_name: string
workflow_name: string
job_name: string
run_id: number
head_sha: string
failed_attempt: number
passed_attempt: number
flaky_count: number
window_days: number
}
export interface EngineeringAnalyticsCIBrokenDefaultBranchSignalExtraApi {
repo_owner: string
repo_name: string
workflow_name: string
branch: string
conclusive_success_rate: number
conclusive_run_count: number
latest_conclusion: string
window_hours: number
}
export interface EngineeringAnalyticsCIDurationRegressionSignalExtraApi {
repo_owner: string
repo_name: string
workflow_name: string
current_p95_seconds: number
baseline_p95_seconds: number
pct_increase: number
current_p50_seconds: number
baseline_p50_seconds: number
window_days: number
}
export interface FreshdeskTicketSignalExtraApi {
status: string | null
priority: string | null
type: string | null
tags: unknown[]
created_at: string | null
}
export interface FreshserviceTicketSignalExtraApi {
status: string | null
priority: string | null
type: string | null
category: string | null
tags: unknown[]
created_at: string | null
}
export interface FrontConversationSignalExtraApi {
status: string | null
tags: unknown[]
created_at: string | null
}
export interface GorgiasTicketSignalExtraApi {
status: string | null
priority: string | null
channel: string | null
tags: unknown[]
created_datetime: string | null
}
export interface KustomerConversationSignalExtraApi {
status: string | null
priority: string | null
tags: unknown[]
createdAt: string | null
}
export interface DixaConversationSignalExtraApi {
status: string | null
channel: string | null
tags: unknown[]
created_at: string | null
}
export interface PlainThreadSignalExtraApi {
status: string | null
priority: string | null
labels: unknown[]
createdAt: string | null
}
export interface GitlabIssueSignalExtraApi {
state: string | null
labels: unknown[]
iid: string | null
project_id: string | null
created_at: string | null
}
export interface GiteaIssueSignalExtraApi {
state: string | null
labels: unknown[]
html_url: string | null
number: string | null
created_at: string | null
}
export interface ShortcutStorySignalExtraApi {
story_type: string | null
labels: unknown[]
workflow_state_id: string | null
created_at: string | null
}
export interface SentryIssueSignalExtraApi {
level: string | null
status: string | null
permalink: string | null
shortId: string | null
firstSeen: string | null
}
export interface RollbarItemSignalExtraApi {
level: string | null
status: string | null
environment: string | null
framework: string | null
last_occurrence_timestamp: string | null
}
export interface BugsnagErrorSignalExtraApi {
severity: string | null
status: string | null
context: string | null
first_seen: string | null
last_seen: string | null
}
export interface HoneybadgerFaultSignalExtraApi {
environment: string | null
component: string | null
action: string | null
tags: unknown[]
url: string | null
created_at: string | null
}
export interface RaygunErrorGroupSignalExtraApi {
status: string | null
applicationUrl: string | null
lastOccurredAt: string | null
createdAt: string | null
}
export interface SnykScannerFindingSignalExtraApi {
effective_severity_level: string | null
status: string | null
type: string | null
created_at: string | null
}
export interface SonarqubeScannerFindingSignalExtraApi {
severity: string | null
type: string | null
status: string | null
component: string | null
rule: string | null
creationDate: string | null
}
export interface SemgrepScannerFindingSignalExtraApi {
severity: string | null
confidence: string | null
status: string | null
state: string | null
created_at: string | null
}
export interface Rapid7InsightvmScannerFindingSignalExtraApi {
severity: string | null
cvss_v3_score: string | null
published: string | null
added: string | null
}
export interface FeaturebaseFeedbackSignalExtraApi {
status: string | null
tags: unknown[]
upvotes: string | null
createdAt: string | null
}
export interface FrillFeedbackSignalExtraApi {
status: string | null
vote_count: string | null
topics: unknown[]
created_at: string | null
}
export interface AhaFeedbackSignalExtraApi {
workflow_status: string | null
score: string | null
votes: string | null
url: string | null
created_at: string | null
}
export interface UservoiceFeedbackSignalExtraApi {
state: string | null
vote_count: string | null
category_name: string | null
created_at: string | null
}
export interface ProductboardFeedbackSignalExtraApi {
state: string | null
tags: unknown[]
displayUrl: string | null
createdAt: string | null
}
export interface CannyFeedbackSignalExtraApi {
status: string | null
tags: unknown[]
score: string | null
voteCount: string | null
url: string | null
created: string | null
}
export interface AsknicelyFeedbackSignalExtraApi {
score: string | null
status: string | null
question_type: string | null
segment: string | null
created: string | null
}
export interface RetentlyFeedbackSignalExtraApi {
score: string | null
ratingCategory: string | null
feedbackTopics: unknown[]
resolved: string | null
createdDate: string | null
}
export interface AppfiguresReviewSignalExtraApi {
stars: string | null
version: string | null
product: string | null
date: string | null
}
export interface AppfollowReviewSignalExtraApi {
rating: string | null
store: string | null
app_version: string | null
date: string | null
}
export interface JudgemeReviewsReviewSignalExtraApi {
rating: string | null
product_title: string | null
verified: string | null
created_at: string | null
}
export interface IntercomTicketSignalExtraApi {
state: string | null
priority: string | null
admin_assignee_id: string | null
created_at: string | null
}
export interface HubspotTicketSignalExtraApi {
hs_ticket_priority: string | null
hs_pipeline_stage: string | null
hs_ticket_category: string | null
createdate: string | null
}
export interface GoogleSearchConsoleSearchOpportunitySignalExtraApi {
page: string
query: string
date: string
clicks: number
impressions: number
ctr: number
position: number
}
export type SignalExtraApi =
| SessionProblemSignalExtraApi
| LlmEvalSignalExtraApi
| LlmEvalReportSignalExtraApi
| ZendeskTicketSignalExtraApi
| GithubIssueSignalExtraApi
| LinearIssueSignalExtraApi
| JiraIssueSignalExtraApi
| ConversationsTicketSignalExtraApi
| ErrorTrackingSignalExtraApi
| PgAnalyzeIssueSignalExtraApi
| EndpointExecutionFailedSignalExtraApi
| EndpointBreakdownLimitExceededSignalExtraApi
| SignalsScoutSignalExtraApi
| LogsAlertStateChangeSignalExtraApi
| ReplayVisionScannerFindingSignalExtraApi
| AnalyticsAnomalyInvestigationSignalExtraApi
| HealthCheckSignalExtraApi
| EngineeringAnalyticsCIFlakyCheckSignalExtraApi
| EngineeringAnalyticsCIBrokenDefaultBranchSignalExtraApi
| EngineeringAnalyticsCIDurationRegressionSignalExtraApi
| FreshdeskTicketSignalExtraApi
| FreshserviceTicketSignalExtraApi
| FrontConversationSignalExtraApi
| GorgiasTicketSignalExtraApi
| KustomerConversationSignalExtraApi
| DixaConversationSignalExtraApi
| PlainThreadSignalExtraApi
| GitlabIssueSignalExtraApi
| GiteaIssueSignalExtraApi
| ShortcutStorySignalExtraApi
| SentryIssueSignalExtraApi
| RollbarItemSignalExtraApi
| BugsnagErrorSignalExtraApi
| HoneybadgerFaultSignalExtraApi
| RaygunErrorGroupSignalExtraApi
| SnykScannerFindingSignalExtraApi
| SonarqubeScannerFindingSignalExtraApi
| SemgrepScannerFindingSignalExtraApi
| Rapid7InsightvmScannerFindingSignalExtraApi
| FeaturebaseFeedbackSignalExtraApi
| FrillFeedbackSignalExtraApi
| AhaFeedbackSignalExtraApi
| UservoiceFeedbackSignalExtraApi
| ProductboardFeedbackSignalExtraApi
| CannyFeedbackSignalExtraApi
| AsknicelyFeedbackSignalExtraApi
| RetentlyFeedbackSignalExtraApi
| AppfiguresReviewSignalExtraApi
| AppfollowReviewSignalExtraApi
| JudgemeReviewsReviewSignalExtraApi
| IntercomTicketSignalExtraApi
| HubspotTicketSignalExtraApi
| GoogleSearchConsoleSearchOpportunitySignalExtraApi
export interface SpecificityMetadataApi {
/** Title of the PR the specificity gate evaluated. */
pr_title: string
/** Whether the report passed the PR-specificity gate. */
specific_enough: boolean
/** The gate's reasoning. */
reason: string
}
export interface MatchedMetadataApi {
/** Signal already in the report that this one matched. */
parent_signal_id: string
/** Query used to find the parent signal. */
match_query: string
/** Why the signals were judged to describe the same issue. */
reason: string
/** PR-specificity gate result, when the gate ran. */
specificity?: SpecificityMetadataApi | null
}
export interface NoMatchMetadataApi {
/** Why no existing report matched. */
reason: string
/** Candidate signals that were considered and rejected. */
rejected_signal_ids: string[]
/** PR-specificity gate result that caused a rejection, when present. */
specificity_rejection?: SpecificityMetadataApi | null
}
export type SignalMatchMetadataApi = MatchedMetadataApi | NoMatchMetadataApi
export interface SignalNodeApi {
/** Datastore document id of the signal. */
signal_id: string
/** The signal's human-readable description. */
content: string
/** Product that emitted the signal.
*
* * `session_replay` - session_replay
* * `llm_analytics` - llm_analytics
* * `github` - github
* * `linear` - linear
* * `jira` - jira
* * `zendesk` - zendesk
* * `conversations` - conversations
* * `error_tracking` - error_tracking
* * `endpoints` - endpoints
* * `pganalyze` - pganalyze
* * `signals_scout` - signals_scout
* * `logs` - logs
* * `health_checks` - health_checks
* * `replay_vision` - replay_vision
* * `analytics` - analytics
* * `freshdesk` - freshdesk
* * `freshservice` - freshservice
* * `front` - front
* * `gorgias` - gorgias
* * `kustomer` - kustomer
* * `dixa` - dixa
* * `plain` - plain
* * `gitlab` - gitlab
* * `gitea` - gitea
* * `shortcut` - shortcut
* * `sentry` - sentry
* * `rollbar` - rollbar
* * `bugsnag` - bugsnag
* * `honeybadger` - honeybadger
* * `raygun` - raygun
* * `snyk` - snyk
* * `sonarqube` - sonarqube
* * `semgrep` - semgrep
* * `rapid7_insightvm` - rapid7_insightvm
* * `featurebase` - featurebase
* * `frill` - frill
* * `aha` - aha
* * `uservoice` - uservoice
* * `productboard` - productboard
* * `canny` - canny
* * `asknicely` - asknicely
* * `retently` - retently
* * `appfigures` - appfigures
* * `appfollow` - appfollow
* * `judgeme_reviews` - judgeme_reviews
* * `intercom` - intercom
* * `hubspot` - hubspot
* * `engineering_analytics` - engineering_analytics
* * `google_search_console` - google_search_console */
source_product: SignalSourceProductApi
/** Signal type within the source product.
*
* * `session_analysis_cluster` - session_analysis_cluster
* * `session_problem` - session_problem
* * `evaluation` - evaluation
* * `evaluation_report` - evaluation_report
* * `issue` - issue
* * `ticket` - ticket
* * `issue_created` - issue_created
* * `issue_reopened` - issue_reopened
* * `issue_spiking` - issue_spiking
* * `endpoint_execution_failed` - endpoint_execution_failed
* * `endpoint_breakdown_limit_exceeded` - endpoint_breakdown_limit_exceeded
* * `cross_source_issue` - cross_source_issue
* * `alert_state_change` - alert_state_change
* * `health_issue` - health_issue
* * `scanner_finding` - scanner_finding
* * `anomaly_investigation` - anomaly_investigation
* * `feedback` - feedback
* * `review` - review
* * `ci_flaky_check` - ci_flaky_check
* * `ci_broken_default_branch` - ci_broken_default_branch
* * `ci_duration_regression` - ci_duration_regression
* * `search_opportunity` - search_opportunity */
source_type: SignalSourceTypeApi
/** Emitter-scoped id of the underlying object (issue, ticket, ...). */
source_id: string
/** Signal weight in [0, 1]; drives report ranking. */
weight: number
/** Emission timestamp. */
timestamp: string
/** Product-specific payload; shape depends on (source_product, source_type). */
extra: SignalExtraApi
/** Clustering match/no-match metadata, when present. */
match_metadata?: SignalMatchMetadataApi | null
}
/**
* Response body for GET /v1/projects/:id/signals/reports/:id/signals/.
*/
export interface ReportSignalsResponseApi {
/** The report these signals were clustered into. */
report: SignalReportApi
/** All signals contributing to the report. */
signals: SignalNodeApi[]
}
/**
* * `suppressed` - suppressed
* * `potential` - potential
* * `resolved` - resolved
*/
export type SignalReportStateEnumApi = (typeof SignalReportStateEnumApi)[keyof typeof SignalReportStateEnumApi]
export const SignalReportStateEnumApi = {
Suppressed: 'suppressed',
Potential: 'potential',
Resolved: 'resolved',
} as const
/**
* * `already_fixed` - Already fixed
* * `report_unclear` - Report is unclear to me
* * `analysis_wrong` - Agent's analysis is wrong
* * `wontfix_intentional` - Won't fix - intentional behavior
* * `wontfix_irrelevant` - Won't fix - issue is real but insignificant
* * `other` - Something else…
*/
export type DismissalReasonEnumApi = (typeof DismissalReasonEnumApi)[keyof typeof DismissalReasonEnumApi]
export const DismissalReasonEnumApi = {
AlreadyFixed: 'already_fixed',
ReportUnclear: 'report_unclear',
AnalysisWrong: 'analysis_wrong',
WontfixIntentional: 'wontfix_intentional',
WontfixIrrelevant: 'wontfix_irrelevant',
Other: 'other',
} as const
export interface SignalReportStateRequestApi {
/** Target state for the report. Use 'suppressed' to dismiss the report from the inbox, 'potential' to snooze/reopen it for later review, or 'resolved' when the work this report asked for has been done. Resolving is only allowed from a researched status (ready or pending_input) or a suppressed report; other statuses return 409 (skipped in bulk).
*
* * `suppressed` - suppressed
* * `potential` - potential
* * `resolved` - resolved */
state: SignalReportStateEnumApi
/** Optional canonical reason code for the dismissal. Must be one of: already_fixed, report_unclear, analysis_wrong, wontfix_intentional, wontfix_irrelevant, other — these match the inbox UI so the rationale renders as a labelled chip rather than a raw code. When the work this report asked for is done, the honest transition is state='resolved' (the reason/note records why). Reserve 'already_fixed' with state='potential' (snooze/restore) for "fixed by something else / might recur" cases, so the report reappears if the issue comes back. Use 'other' together with a dismissal_note for anything that doesn't fit a code.
*
* * `already_fixed` - Already fixed
* * `report_unclear` - Report is unclear to me
* * `analysis_wrong` - Agent's analysis is wrong
* * `wontfix_intentional` - Won't fix - intentional behavior
* * `wontfix_irrelevant` - Won't fix - issue is real but insignificant
* * `other` - Something else… */
dismissal_reason?: DismissalReasonEnumApi
/**
* Optional free-form note explaining the dismissal. Capped at 4000 characters.
* @maxLength 4000
*/
dismissal_note?: string
/**
* Optional, only honored when state is 'potential'. Number of additional signals the report must accumulate before it is re-promoted into the pipeline — effectively snoozing it until then. Omit to let the report re-enter the pipeline on the next matching signal.
* @minimum 1
* @maximum 100000
*/
snooze_for?: number
}
/**
* * `video_segment` - Video Segment
* * `safety_judgment` - Safety Judgment
* * `actionability_judgment` - Actionability Judgment
* * `priority_judgment` - Priority Judgment
* * `signal_finding` - Signal Finding
* * `repo_selection` - Repo Selection
* * `suggested_reviewers` - Suggested Reviewers
* * `dismissal` - Dismissal
* * `code_reference` - Code Reference
* * `commit` - Commit
* * `task_run` - Task Run
* * `note` - Note
* * `title_change` - Title Change
* * `summary_change` - Summary Change
* * `code_review` - Code Review
* * `related_to` - Related To
*/
export type SignalReportArtefactTypeEnumApi =
(typeof SignalReportArtefactTypeEnumApi)[keyof typeof SignalReportArtefactTypeEnumApi]
export const SignalReportArtefactTypeEnumApi = {
VideoSegment: 'video_segment',
SafetyJudgment: 'safety_judgment',
ActionabilityJudgment: 'actionability_judgment',
PriorityJudgment: 'priority_judgment',
SignalFinding: 'signal_finding',
RepoSelection: 'repo_selection',
SuggestedReviewers: 'suggested_reviewers',
Dismissal: 'dismissal',
CodeReference: 'code_reference',
Commit: 'commit',
TaskRun: 'task_run',
Note: 'note',
TitleChange: 'title_change',
SummaryChange: 'summary_change',
CodeReview: 'code_review',
RelatedTo: 'related_to',
} as const
export interface _UserApi {
readonly id: number
readonly uuid: string
readonly first_name: string
readonly last_name: string
readonly email: string
}
export type SignalReportArtefactApiContent = { [key: string]: unknown } | unknown[]
export interface SignalReportArtefactApi {
readonly id: string
readonly type: SignalReportArtefactTypeEnumApi
readonly content: SignalReportArtefactApiContent
readonly created_at: string
/** @nullable */
readonly updated_at: string | null
/** User the artefact is attributed to, when a user produced it. Null for task/system writes. */
readonly created_by: _UserApi | null
/**
* Task the artefact is attributed to, when an agent produced it. Null for user/system writes.
* @nullable
*/
readonly task_id: string | null
}
export interface PaginatedSignalReportArtefactListApi {
count: number
/** @nullable */
next?: string | null
/** @nullable */
previous?: string | null
results: SignalReportArtefactApi[]
}
/**
* Body for appending an artefact to a report.
*
* Everything is append-only: log artefacts accumulate, status artefacts supersede the previous
* version (latest-wins). The `content` shape depends on `artefact_type` and is validated
* against the type's schema (see `products/signals/backend/artefact_schemas.py`).
*/
export interface SignalReportArtefactLogCreateApi {
/** The artefact type. One of: actionability_judgment, code_reference, commit, dismissal, note, priority_judgment, related_to, repo_selection, safety_judgment, signal_finding, suggested_reviewers, task_run. Log types accumulate; status types (safety_judgment, actionability_judgment, priority_judgment, repo_selection, suggested_reviewers) are latest-wins — appending a new version supersedes the previous one as the report's canonical status. */
artefact_type: string
/** The artefact payload as a JSON object or array; shape depends on artefact_type and is validated against its schema. */
content: unknown
}
/**
* Response shape for the log-artefact create/update endpoints — echoes the stored row.
*/
export interface SignalReportArtefactWriteResponseApi {
/** The artefact's unique id. */
readonly id: string
/** The id of the report this artefact belongs to. */
readonly report_id: string
/** The artefact type. */
readonly type: string
/** The artefact payload, parsed from storage. */
readonly content: unknown
/** When the artefact was created. */
readonly created_at: string
/**
* When the artefact was last written — set on creation and refreshed on each edit. Null only for rows created before this field existed.
* @nullable
*/
readonly updated_at: string | null
/**
* Task the artefact is attributed to, when an agent produced it. Null for user writes.
* @nullable
*/
readonly task_id: string | null
}
/**
* Body for replacing the content of an existing artefact (addressed by id).
*
* Per-type schema validation happens in the view, which knows the artefact's type.
*/
export interface PatchedSignalReportArtefactLogUpdateApi {
/** The new artefact payload as a JSON object or array, matching the artefact type's schema. */
content?: unknown
}
/**
* Response for the `commit` artefact diff endpoint — the commit's branch rendered against the
* repository default branch.
*/
export interface CommitDiffResponseApi {
/** Unified diff (patch) text of the branch against the repository default branch, from the GitHub compare API. */
readonly diff: string
/** True when the diff was too large to return in full and has been truncated. */
readonly truncated: boolean
}
export interface SignalReportBulkStateRequestApi {
/** Target state for the report. Use 'suppressed' to dismiss the report from the inbox, 'potential' to snooze/reopen it for later review, or 'resolved' when the work this report asked for has been done. Resolving is only allowed from a researched status (ready or pending_input) or a suppressed report; other statuses return 409 (skipped in bulk).
*
* * `suppressed` - suppressed
* * `potential` - potential
* * `resolved` - resolved */
state: SignalReportStateEnumApi
/** Optional canonical reason code for the dismissal. Must be one of: already_fixed, report_unclear, analysis_wrong, wontfix_intentional, wontfix_irrelevant, other — these match the inbox UI so the rationale renders as a labelled chip rather than a raw code. When the work this report asked for is done, the honest transition is state='resolved' (the reason/note records why). Reserve 'already_fixed' with state='potential' (snooze/restore) for "fixed by something else / might recur" cases, so the report reappears if the issue comes back. Use 'other' together with a dismissal_note for anything that doesn't fit a code.
*
* * `already_fixed` - Already fixed
* * `report_unclear` - Report is unclear to me
* * `analysis_wrong` - Agent's analysis is wrong
* * `wontfix_intentional` - Won't fix - intentional behavior
* * `wontfix_irrelevant` - Won't fix - issue is real but insignificant
* * `other` - Something else… */
dismissal_reason?: DismissalReasonEnumApi
/**
* Optional free-form note explaining the dismissal. Capped at 4000 characters.
* @maxLength 4000
*/
dismissal_note?: string
/**
* Optional, only honored when state is 'potential'. Number of additional signals the report must accumulate before it is re-promoted into the pipeline — effectively snoozing it until then. Omit to let the report re-enter the pipeline on the next matching signal.
* @minimum 1
* @maximum 100000
*/
snooze_for?: number
/**
* Report ids to transition to `state` in one call (1–100). Duplicates are de-duplicated; each id is processed independently so one disallowed transition does not block the rest. `dismissal_reason`, `dismissal_note` and `snooze_for` apply to every id.
* @maxItems 100
*/
ids: string[]
}
export interface SignalReportBulkStateResultApi {
/** The report id this result refers to. */
id: string
/** One of: transitioned, skipped, failed, not_found. transitioned: the state change was applied. skipped: the transition was not allowed from the report's current status (a 409 on the single-report endpoint). failed: the request data was invalid for this report. not_found: no report with this id is visible to you. */
outcome: string
/**
* The report's status after the transition. Present only when outcome is 'transitioned'.
* @nullable
*/
status?: string | null
/**
* Human-readable explanation for non-transitioned outcomes (skipped / failed / not_found).
* @nullable
*/
detail?: string | null
}
export interface SignalReportBulkStateResponseApi {
/** One result per requested id, in request order (after de-duplication). */
results: SignalReportBulkStateResultApi[]
/** Number of reports whose state was changed. */
transitioned_count: number
/** Number of reports whose transition was not allowed. */
skipped_count: number
/** Number of reports that failed on invalid request data. */
failed_count: number
/** Number of requested ids not visible to the caller. */
not_found_count: number
}
export interface SignalReportRefundSummaryResponseApi {
/** Number of credited-path refunds across the whole organization whose refunded PR run falls in the current billing period. Excluded-path refunds never reach billing usage, so they are deliberately absent. */
credited_refund_count: number
/** Total signals credits those refunds returned (1 credit = $0.01). Divide by the flat per-PR charge to get the number of PRs to subtract from billing usage. */
credited_credits: number
/** The organization's live billable signals credits for the current billing period, computed by the same rules as the nightly usage report — including PRs created today that billing hasn't recorded yet, and already excluding refund-excluded and billing-exempt reports. Take the max of this and billing's recorded usage for a live PR count that reacts to new PRs and same-day refunds immediately. */
period_billable_credits: number
/** Whether autonomous PR generation is currently paused for this project because the organization is over its self-driving credits quota. Read from the quota limiter, so it reflects the same state the pipeline gates enforce. */
quota_limited: boolean
}
export interface LLMSkillFileInputApi {
/**
* File path relative to skill root, e.g. 'scripts/setup.sh' or 'references/guide.md'.
* @maxLength 500
*/
path: string
/** Text content of the file. */
content: string
/**
* MIME type of the file content.
* @maxLength 100
*/
content_type?: string
}
export interface SignalScoutSlackDestinationApi {
/**
* ID of the Slack integration whose bot posts this scout's findings and reports.
* @minimum 1
*/
integration_id: number
/**
* Slack channel target in the channel picker's `channel_id|#channel-name` format. Null while choosing a channel; no messages are sent until it is set.
* @maxLength 255
* @nullable
*/
channel?: string | null
}
export interface SignalScoutOutputDestinationsApi {
/** Slack destination for each emitted scout finding or report. Null or omitted disables Slack delivery. */
slack?: SignalScoutSlackDestinationApi | null
}
/**
* * `trusted` - Trusted domains only
* * `full` - Full
*/
export type ScoutConfigNetworkAccessEnumApi =
(typeof ScoutConfigNetworkAccessEnumApi)[keyof typeof ScoutConfigNetworkAccessEnumApi]
export const ScoutConfigNetworkAccessEnumApi = {
Trusted: 'trusted',
Full: 'full',
} as const
/**
* Optional JSON Schema (draft 2020-12) describing ONE structured record this scout produces via `scout-record-output` — e.g. a per-report quality judgment (`{"type": "object", "properties": {"verdict": {"enum": ["good", "bad", "unsure"]}, "reason": {"type": "string"}}, "required": ["verdict", "reason"]}`). The root must be `"type": "object"`. Setting a schema turns the structured-output channel on: the run prompt renders the schema and every submitted record is validated against it and recorded in the project as a `$scout_structured_output` event, queryable like any event. The channel also requires emit — a dry-run scout has nowhere to record to. Cardinality is the scout's call (one record per run, one per judged entity, ...). Null = channel off. Setting a schema requires skill-authoring authorization (the `llm_skill:write` scope and skill editor access) since the scout reads it verbatim in its prompt; clearing it needs only the config write. Records validate against the schema in force when the run was dispatched.
* @nullable
*/
export type SignalScoutConfigOptionsApiStructuredOutputSchema = { [key: string]: unknown } | null
/**
* Schedule, enablement, and delivery options accepted while creating a scout.
*/
export interface SignalScoutConfigOptionsApi {
/** Whether this scout runs on its schedule. Defaults to true. */
enabled?: boolean
/** Whether the scout writes findings to the inbox. False = dry-run: it runs and logs but emits nothing. Defaults to true. */
emit?: boolean
/**
* Minutes between runs (30–43200). Defaults to 1440 (every 24 hours).
* @minimum 30
* @maximum 43200
*/
run_interval_minutes?: number
/** Destinations that receive each finding or report this scout emits. Empty by default. */
output_destinations?: SignalScoutOutputDestinationsApi
/** What the scout's sandbox can reach over the network while it runs. Defaults to `trusted`, the platform's trusted-domain allowlist (Insights, GitHub, common package registries). Set `full` to let this scout reach any site, for skills that read external sources such as documentation or papers.
*
* * `trusted` - Trusted domains only
* * `full` - Full */
network_access?: ScoutConfigNetworkAccessEnumApi
/** Exempt this scout from the inactivity pause, which otherwise switches off a scout that goes a fortnight without surfacing anything anyone engages with. Set it on watchdog scouts whose value is staying quiet. Defaults to false. */
auto_pause_exempt?: boolean
/**
* Optional five-field cron expression, e.g. '30 9 * * *' (daily at 09:30), '0 9,17 * * *' (twice daily), or '0 9 * * 1-5' (weekday mornings). Evaluated in the project timezone. Takes precedence over `run_interval_minutes`; occurrences must be at least 30 minutes apart.
* @maxLength 100
* @nullable
*/
run_cron_schedule?: string | null
/**
* Free-form labels for grouping the fleet, e.g. `["revenue", "on-call"]`. Normalized to lowercase kebab-case (`On Call` and `on_call` both become `on-call`), deduped, and stored sorted; at most 10 tags, each at most 50 characters once normalized. Pass the full desired set — a write replaces the existing tags rather than merging into them. Filter the config list with the `tags` query parameter.
* @maxItems 10
*/
tags?: string[]
/**
* Optional JSON Schema (draft 2020-12) describing ONE structured record this scout produces via `scout-record-output` — e.g. a per-report quality judgment (`{"type": "object", "properties": {"verdict": {"enum": ["good", "bad", "unsure"]}, "reason": {"type": "string"}}, "required": ["verdict", "reason"]}`). The root must be `"type": "object"`. Setting a schema turns the structured-output channel on: the run prompt renders the schema and every submitted record is validated against it and recorded in the project as a `$scout_structured_output` event, queryable like any event. The channel also requires emit — a dry-run scout has nowhere to record to. Cardinality is the scout's call (one record per run, one per judged entity, ...). Null = channel off. Setting a schema requires skill-authoring authorization (the `llm_skill:write` scope and skill editor access) since the scout reads it verbatim in its prompt; clearing it needs only the config write. Records validate against the schema in force when the run was dispatched.
* @nullable
*/
structured_output_schema?: SignalScoutConfigOptionsApiStructuredOutputSchema
}
/**
* Create a runnable custom scout and its config in one atomic request.
*/
export interface SignalScoutCreateApi {
/**
* Unique scout name. Must start with `signals-scout-` and contain only lowercase letters, numbers, and hyphens.
* @maxLength 64
*/
name: string
/**
* Short description of the signal or behavior this scout investigates.
* @maxLength 4096
*/
description: string
/** Complete markdown prompt executed on every scout run. Include any project-specific signal names, thresholds, investigation steps, and report criteria here. */
body: string
/** Optional reference files bundled with the scout prompt. */
files?: LLMSkillFileInputApi[]
/** Optional schedule, enablement, dry-run posture, and delivery settings. Defaults to an enabled, emitting scout on the daily interval with no external destination. */
config?: SignalScoutConfigOptionsApi
}
export interface SignalScoutSkillSummaryApi {
readonly id: string
readonly name: string
readonly description: string
readonly version: number
/** Server-managed report tools granted to this scout. */
readonly allowed_tools: readonly string[]
}
export type ScoutOriginEnumApi = (typeof ScoutOriginEnumApi)[keyof typeof ScoutOriginEnumApi]
export const ScoutOriginEnumApi = {
Canonical: 'canonical',
Custom: 'custom',
} as const
/**
* * `active` - Active
* * `pending_pause` - Pending pause
* * `paused_by_system` - Paused by system
* * `paused_by_user` - Paused by user
*/
export type ScoutConfigStatusEnumApi = (typeof ScoutConfigStatusEnumApi)[keyof typeof ScoutConfigStatusEnumApi]
export const ScoutConfigStatusEnumApi = {
Active: 'active',
PendingPause: 'pending_pause',
PausedBySystem: 'paused_by_system',
PausedByUser: 'paused_by_user',
} as const
/**
* * `no_output` - No output
* * `ignored` - Ignored
* * `repeated_failures` - Repeated failures
*/
export type ScoutConfigPauseReasonEnumApi =
(typeof ScoutConfigPauseReasonEnumApi)[keyof typeof ScoutConfigPauseReasonEnumApi]
export const ScoutConfigPauseReasonEnumApi = {
NoOutput: 'no_output',
Ignored: 'ignored',
RepeatedFailures: 'repeated_failures',
} as const
/**
* Optional JSON Schema (draft 2020-12) describing ONE structured record this scout produces via `scout-record-output` — e.g. a per-report quality judgment (`{"type": "object", "properties": {"verdict": {"enum": ["good", "bad", "unsure"]}, "reason": {"type": "string"}}, "required": ["verdict", "reason"]}`). The root must be `"type": "object"`. Setting a schema turns the structured-output channel on: the run prompt renders the schema and every submitted record is validated against it and recorded in the project as a `$scout_structured_output` event, queryable like any event. The channel also requires emit — a dry-run scout has nowhere to record to. Cardinality is the scout's call (one record per run, one per judged entity, ...). Null = channel off. Setting a schema requires skill-authoring authorization (the `llm_skill:write` scope and skill editor access) since the scout reads it verbatim in its prompt; clearing it needs only the config write. Records validate against the schema in force when the run was dispatched.
* @nullable
*/
export type SignalScoutConfigApiStructuredOutputSchema = { [key: string]: unknown } | null
/**
* Read shape for a per-(team, skill) scout config.
*
* One row per `signals-scout-*` skill on the team. The coordinator auto-creates a row
* when it discovers a scout skill; this serializer lets agents tune the row.
*/
export interface SignalScoutConfigApi {
readonly id: string
/** The `signals-scout-*` skill this config controls. Set at creation, not editable. */
readonly skill_name: string
/** Human-readable summary of what this scout investigates, sourced from the scout skill's `description` metadata. Use it for a quick steer on the scout's focus without loading the full skill body. Empty if the skill is not currently present on the team or carries no description. */
readonly description: string
/** Where this scout came from: `canonical` for a scout Insights ships and maintains (seeded from `products/signals/skills/`), or `custom` for one a team hand-authored on this project. Use it to badge built-in vs custom scouts instead of a hardcoded name list. Defaults to `custom` if the skill is not currently present on the team. */
readonly scout_origin: ScoutOriginEnumApi
/** Whether this scout runs on its schedule. Disabled scouts are skipped by the coordinator. Derived from `status`: true for `active` and `pending_pause`, false for the paused statuses. */
readonly enabled: boolean
/** Lifecycle status. `active`: runs on its schedule. `pending_pause`: still running, but flagged by the system to pause soon unless something changes (any config edit clears it). `paused_by_system`: paused automatically, see `pause_reason`; set `enabled=true` to resume. `paused_by_user`: switched off by a person and never resumed automatically.
*
* * `active` - Active
* * `pending_pause` - Pending pause
* * `paused_by_system` - Paused by system
* * `paused_by_user` - Paused by user */
readonly status: ScoutConfigStatusEnumApi
/** Why the system paused (or warned) this scout: `no_output` (it emitted nothing over the evaluation window), `ignored` (its output received no human engagement), or `repeated_failures` (consecutive failed runs). Null unless `status` is `pending_pause` or `paused_by_system`.
*
* * `no_output` - No output
* * `ignored` - Ignored
* * `repeated_failures` - Repeated failures */
readonly pause_reason: ScoutConfigPauseReasonEnumApi | null
/** Whether the scout writes findings to the inbox. False = dry-run: it runs and logs but emits nothing. */
readonly emit: boolean
/**
* Minutes between runs (30–43200). The scout runs once this interval has elapsed since its last run.
* @minimum 30
* @maximum 43200
*/
readonly run_interval_minutes: number
/**
* Optional five-field cron expression evaluated in the project timezone, e.g. '30 9 * * *'. Takes precedence over `run_interval_minutes` when set. Null means the rolling interval schedule.
* @nullable
*/
readonly run_cron_schedule: string | null
/** Destinations that receive each finding or report this scout emits. Empty when none is configured. */
readonly output_destinations: SignalScoutOutputDestinationsApi
/**
* Optional JSON Schema (draft 2020-12) describing ONE structured record this scout produces via `scout-record-output` — e.g. a per-report quality judgment (`{"type": "object", "properties": {"verdict": {"enum": ["good", "bad", "unsure"]}, "reason": {"type": "string"}}, "required": ["verdict", "reason"]}`). The root must be `"type": "object"`. Setting a schema turns the structured-output channel on: the run prompt renders the schema and every submitted record is validated against it and recorded in the project as a `$scout_structured_output` event, queryable like any event. The channel also requires emit — a dry-run scout has nowhere to record to. Cardinality is the scout's call (one record per run, one per judged entity, ...). Null = channel off. Setting a schema requires skill-authoring authorization (the `llm_skill:write` scope and skill editor access) since the scout reads it verbatim in its prompt; clearing it needs only the config write. Records validate against the schema in force when the run was dispatched.
* @nullable
*/
readonly structured_output_schema: SignalScoutConfigApiStructuredOutputSchema
/** What the scout's sandbox can reach over the network while it runs. `trusted` (the default) restricts runs to the platform's trusted-domain allowlist (Insights, GitHub, common package registries). `full` lets the scout reach any site, for skills that read external sources such as documentation or papers.
*
* * `trusted` - Trusted domains only
* * `full` - Full */
readonly network_access: ScoutConfigNetworkAccessEnumApi
/**
* When the coordinator last dispatched this scout. Null if it has never run.
* @nullable
*/
readonly last_run_at: string | null
/** How many of this scout's runs have failed in a row. Back to 0 after a successful run or any config edit. At the failure limit the scout pauses itself (`status` becomes `paused_by_system` with `pause_reason` `repeated_failures`) and retries about once a day; a successful retry resumes it, and so does setting `enabled=true`. */
readonly consecutive_failure_count: number
/**
* When `status` last changed. For `pending_pause` this is when the warning was issued (an `ignored` warning pauses about a week later unless someone acts on the scout's reports; a `no_output` warning only flags the scout); for the paused statuses it is when the scout was paused. Null if the status never changed.
* @nullable
*/
readonly status_changed_at: string | null
/** Whether this scout is exempt from the inactivity sweep, meaning both the `ignored` pause and the `no_output` quiet warning. Set it on watchdog scouts whose value is staying quiet. Also set automatically when someone re-enables a scout the inactivity sweep paused, so the sweep never overrules a person twice. */
readonly auto_pause_exempt: boolean
/** Free-form labels for grouping the fleet, e.g. `["revenue", "on-call"]`. Normalized to lowercase kebab-case (`On Call` and `on_call` both become `on-call`), deduped, and stored sorted; at most 10 tags, each at most 50 characters once normalized. Pass the full desired set — a write replaces the existing tags rather than merging into them. Filter the config list with the `tags` query parameter. */
tags?: string[]
readonly created_at: string
}
export interface SignalScoutCreateResponseApi {
/** True when this request created the missing scout skill or config; false when both already existed. */
created: boolean
skill: SignalScoutSkillSummaryApi
config: SignalScoutConfigApi
}
/**
* Optional JSON Schema (draft 2020-12) describing ONE structured record this scout produces via `scout-record-output` — e.g. a per-report quality judgment (`{"type": "object", "properties": {"verdict": {"enum": ["good", "bad", "unsure"]}, "reason": {"type": "string"}}, "required": ["verdict", "reason"]}`). The root must be `"type": "object"`. Setting a schema turns the structured-output channel on: the run prompt renders the schema and every submitted record is validated against it and recorded in the project as a `$scout_structured_output` event, queryable like any event. The channel also requires emit — a dry-run scout has nowhere to record to. Cardinality is the scout's call (one record per run, one per judged entity, ...). Null = channel off. Setting a schema requires skill-authoring authorization (the `llm_skill:write` scope and skill editor access) since the scout reads it verbatim in its prompt; clearing it needs only the config write. Records validate against the schema in force when the run was dispatched.
* @nullable
*/
export type SignalScoutConfigCreateApiStructuredOutputSchema = { [key: string]: unknown } | null
/**
* Request body for registering a scout config without waiting for the coordinator tick.
*
* Upsert keyed on `skill_name`: if the coordinator (or a concurrent caller) already
* registered the row, the provided tunables are applied to it instead.
*/
export interface SignalScoutConfigCreateApi {
/** Whether this scout runs on its schedule. Defaults to true. */
enabled?: boolean
/** Whether the scout writes findings to the inbox. False = dry-run: it runs and logs but emits nothing. Defaults to true. */
emit?: boolean
/**
* Minutes between runs (30–43200). Defaults to 1440 (every 24 hours).
* @minimum 30
* @maximum 43200
*/
run_interval_minutes?: number
/** Destinations that receive each finding or report this scout emits. Empty by default. */
output_destinations?: SignalScoutOutputDestinationsApi
/** What the scout's sandbox can reach over the network while it runs. Defaults to `trusted`, the platform's trusted-domain allowlist (Insights, GitHub, common package registries). Set `full` to let this scout reach any site, for skills that read external sources such as documentation or papers.
*
* * `trusted` - Trusted domains only
* * `full` - Full */
network_access?: ScoutConfigNetworkAccessEnumApi
/** Exempt this scout from the inactivity pause, which otherwise switches off a scout that goes a fortnight without surfacing anything anyone engages with. Set it on watchdog scouts whose value is staying quiet. Defaults to false. */
auto_pause_exempt?: boolean
/**
* Optional five-field cron expression, e.g. '30 9 * * *' (daily at 09:30), '0 9,17 * * *' (twice daily), or '0 9 * * 1-5' (weekday mornings). Evaluated in the project timezone. Takes precedence over `run_interval_minutes`; occurrences must be at least 30 minutes apart.
* @maxLength 100
* @nullable
*/
run_cron_schedule?: string | null
/**
* Free-form labels for grouping the fleet, e.g. `["revenue", "on-call"]`. Normalized to lowercase kebab-case (`On Call` and `on_call` both become `on-call`), deduped, and stored sorted; at most 10 tags, each at most 50 characters once normalized. Pass the full desired set — a write replaces the existing tags rather than merging into them. Filter the config list with the `tags` query parameter.
* @maxItems 10
*/
tags?: string[]
/**
* Optional JSON Schema (draft 2020-12) describing ONE structured record this scout produces via `scout-record-output` — e.g. a per-report quality judgment (`{"type": "object", "properties": {"verdict": {"enum": ["good", "bad", "unsure"]}, "reason": {"type": "string"}}, "required": ["verdict", "reason"]}`). The root must be `"type": "object"`. Setting a schema turns the structured-output channel on: the run prompt renders the schema and every submitted record is validated against it and recorded in the project as a `$scout_structured_output` event, queryable like any event. The channel also requires emit — a dry-run scout has nowhere to record to. Cardinality is the scout's call (one record per run, one per judged entity, ...). Null = channel off. Setting a schema requires skill-authoring authorization (the `llm_skill:write` scope and skill editor access) since the scout reads it verbatim in its prompt; clearing it needs only the config write. Records validate against the schema in force when the run was dispatched.
* @nullable
*/
structured_output_schema?: SignalScoutConfigCreateApiStructuredOutputSchema
/**
* The `signals-scout-*` skill to register a config for. The skill must already exist on this project — author it via the skills store first.
* @maxLength 200
*/
skill_name: string
}
/**
* Optional JSON Schema (draft 2020-12) describing ONE structured record this scout produces via `scout-record-output` — e.g. a per-report quality judgment (`{"type": "object", "properties": {"verdict": {"enum": ["good", "bad", "unsure"]}, "reason": {"type": "string"}}, "required": ["verdict", "reason"]}`). The root must be `"type": "object"`. Setting a schema turns the structured-output channel on: the run prompt renders the schema and every submitted record is validated against it and recorded in the project as a `$scout_structured_output` event, queryable like any event. The channel also requires emit — a dry-run scout has nowhere to record to. Cardinality is the scout's call (one record per run, one per judged entity, ...). Null = channel off. Setting a schema requires skill-authoring authorization (the `llm_skill:write` scope and skill editor access) since the scout reads it verbatim in its prompt; clearing it needs only the config write. Records validate against the schema in force when the run was dispatched.
* @nullable
*/
export type PatchedSignalScoutConfigUpdateApiStructuredOutputSchema = { [key: string]: unknown } | null
/**
* Editable schedule, enablement, and emit posture for one scout config.
*/
export interface PatchedSignalScoutConfigUpdateApi {
/** Whether this scout runs on its schedule. Disabled scouts are skipped by the coordinator. Turning this off records a user pause (`status` becomes `paused_by_user`, which the system never overrides); turning it on resumes the scout from any pause. Only a change of value is a lifecycle action: re-sending the current value leaves the existing status and its ownership untouched. */
enabled?: boolean
/** Whether the scout writes findings to the inbox. False = dry-run: it runs and logs but emits nothing. */
emit?: boolean
/**
* Minutes between runs (30–43200). Use 1440 for a daily schedule.
* @minimum 30
* @maximum 43200
*/
run_interval_minutes?: number
/**
* Optional five-field cron expression, e.g. '30 9 * * *' (daily at 09:30), '0 9,17 * * *' (twice daily), or '0 9 * * 1-5' (weekday mornings). Evaluated in the project timezone. Takes precedence over `run_interval_minutes`; occurrences must be at least 30 minutes apart. Set null to return to the rolling interval schedule.
* @maxLength 100
* @nullable
*/
run_cron_schedule?: string | null
/** Destinations that receive each finding or report this scout emits. Pass an empty object to disable delivery. */
output_destinations?: SignalScoutOutputDestinationsApi
/**
* Optional JSON Schema (draft 2020-12) describing ONE structured record this scout produces via `scout-record-output` — e.g. a per-report quality judgment (`{"type": "object", "properties": {"verdict": {"enum": ["good", "bad", "unsure"]}, "reason": {"type": "string"}}, "required": ["verdict", "reason"]}`). The root must be `"type": "object"`. Setting a schema turns the structured-output channel on: the run prompt renders the schema and every submitted record is validated against it and recorded in the project as a `$scout_structured_output` event, queryable like any event. The channel also requires emit — a dry-run scout has nowhere to record to. Cardinality is the scout's call (one record per run, one per judged entity, ...). Null = channel off. Setting a schema requires skill-authoring authorization (the `llm_skill:write` scope and skill editor access) since the scout reads it verbatim in its prompt; clearing it needs only the config write. Records validate against the schema in force when the run was dispatched.
* @nullable
*/
structured_output_schema?: PatchedSignalScoutConfigUpdateApiStructuredOutputSchema
/** What the scout's sandbox can reach over the network while it runs. `trusted` (the default) restricts runs to the platform's trusted-domain allowlist (Insights, GitHub, common package registries). Set `full` to let this scout reach any site, for skills that read external sources such as documentation or papers. Applies from the scout's next run.
*
* * `trusted` - Trusted domains only
* * `full` - Full */
network_access?: ScoutConfigNetworkAccessEnumApi
/** Exempt this scout from the inactivity sweep, meaning both the `ignored` pause and the `no_output` quiet warning. Set it on watchdog scouts whose value is staying quiet. */
auto_pause_exempt?: boolean
/**
* Free-form labels for grouping the fleet, e.g. `["revenue", "on-call"]`. Normalized to lowercase kebab-case (`On Call` and `on_call` both become `on-call`), deduped, and stored sorted; at most 10 tags, each at most 50 characters once normalized. Pass the full desired set — a write replaces the existing tags rather than merging into them. Filter the config list with the `tags` query parameter.
* @maxItems 10
*/
tags?: string[]
}
/**
* Response for an on-demand (`run now`) scout dispatch.
*
* The run executes asynchronously on the Temporal worker, so there is no `SignalScoutRun`
* row yet at response time — the bridge row is created once the run's first turn starts.
* Poll the scout's runs (`scout-runs-list`) to see the resulting run and its findings.
*/
export interface SignalScoutManualRunApi {
/** The `signals-scout-*` skill that was dispatched. */
skill_name: string
/** Temporal workflow id for the dispatched run. The run executes asynchronously; poll the scout's runs to see the resulting run row, its status, and any emitted findings. */
workflow_id: string
/** True when a new run was dispatched. The endpoint returns 409 instead when a run for this scout is already in progress. */
started: boolean
}
/**
* One project member's routing identity, for picking a `suggested_reviewers` entry on a report.
*/
export interface ScoutMemberApi {
/** The member's stable Insights user UUID — the same id that appears as `created_by.uuid` on entities they own. A durable handle for this person across runs. */
user_uuid: string
/** The member's email — use to match a finding's owner by name/email. */
email: string
/** The member's first name (may be empty). */
first_name: string
/** The member's last name (may be empty). */
last_name: string
/**
* The member's resolved GitHub login (lowercased), already resolved server-side — put this value in a report's `suggested_reviewers` once you've matched the finding's owner to this row. Null when the member has no linked GitHub identity: a null-login member can't be routed to at all (neither a login nor a uuid resolves), so pick a different owner or leave `suggested_reviewers` empty.
* @nullable
*/
github_login: string | null
}
/**
* A team's enforced scout run caps and current usage.
*
* These are the values the coordinator actually applies at dispatch (resolved per-team override →
* fleet-wide default → code constant), so the UI can show the real throttle rather than what a
* user thinks they configured.
*/
export interface ScoutLimitsApi {
/** Most scout runs the team can start in a single 30-minute coordinator tick. */
max_runs_per_tick: number
/**
* Most scout runs the team can start per rolling 24 hours, or null when uncapped.
* @nullable
*/
max_runs_per_day: number | null
/** Scout runs the team has started in the trailing 24 hours. */
runs_today: number
/**
* Runs still allowed in the trailing 24h window (max_runs_per_day − runs_today), or null when uncapped.
* @nullable
*/
runs_remaining_today: number | null
}
/**
* Team-scoped scout metadata for the inbox / Code-app UIs: enrollment, the alpha banner, and
* the enforced limits. Sourced from the `signals-scout` flag payload so the banner and caps can
* change without a deploy to either app.
*/
export interface ScoutMetadataApi {
/** Whether this project runs scouts. True when the project is in the signals-scout flag's enrollment set — either listed explicitly in guaranteed_team_ids or covered by the "*" wildcard (every project that turns scouts on) — and not in skip_team_ids. */
enrolled: boolean
/**
* Free-form announcement banner to show above the scout UI (e.g. alpha run-limit notice), or null when unset.
* @nullable
*/
banner_message: string | null
/** The team's enforced scout run caps and current usage. */
limits: ScoutLimitsApi
}
/**
* `SignalScoutNote` projection used by `notes-list` and `notes-create`.
*/
export interface ScoutNoteApi {
/** Note UUID. Pass to `scout-notes-delete` to retire the note. */
id: string
/** Target scout skill (`signals-scout-*`), or blank for a general note addressed to every scout on the fleet. */
skill_name: string
/** The note's prose, read verbatim by scout runs. */
content: string
/**
* ISO-8601 creation timestamp.
* @nullable
*/
created_at: string | null
/**
* ISO-8601 expiry, or null for a note that stays active until deleted.
* @nullable
*/
expires_at: string | null
/**
* Display name of the user who left the note, or null when unavailable.
* @nullable
*/
created_by_name: string | null
/** Where the note came from. `human` for one left directly through this API. `report_dismissal` for one forwarded from the note someone typed when they dismissed, snoozed, or restored one or more inbox reports: one reviewer's verdict on the reports its content names, so weigh it as evidence about those reports rather than as fleet-level steering. `report_discussion` for the question someone asked when they opened a discussion on a report: context to weigh, neither a verdict on the report nor a directive. `report_feedback` for the note someone left when rating a report useful or not: one reader's rating of the named report, context to weigh rather than a directive. */
origin: string
}
/**
* Request body for `notes-create`.
*/
export interface ScoutNoteCreateRequestApi {
/**
* The note's prose — feedback, a pointer, or a nudge for the scout(s) to weigh on their next runs (e.g. 'we shipped a new checkout on Tuesday, watch conversion closely', 'stop flagging the staging traffic spike'). Write it in Markdown; scouts read it verbatim.
* @maxLength 10000
*/
content: string
/**
* Address the note to one scout by its skill name (`signals-scout-*`, exact match against an existing scout skill on the project — check `scout-config-list` for the roster). Omit or leave blank for a general note every scout sees.
* @maxLength 200
*/
skill_name?: string
/**
* Optional ISO-8601 expiry. After this time the note drops out of the default list view, so time-boxed steering ('watch closely this week') retires itself. Omit for a note that stays active until deleted.
* @nullable
*/
expires_at?: string | null
}
/**
* `inventory.project_context` — free-form orientation about the project's product.
*/
export interface ProjectContextApi {
/**
* Human-set product description on the project (max 1000 chars). When present, the most direct "what does this team's product do" answer. `null` when unset.
* @nullable
*/
product_description: string | null
/** Registered app URLs for this team (toolbar / replay). The team's actual product surface; complements `$pageview.$host` discovery via `read-data-schema`. */
app_urls: string[]
}
/**
* One row in `inventory.product_intents`.
*/
export interface ProductIntentEntryApi {
/** Product key the team signaled intent to use. */
product_type: string
/**
* ISO-8601 timestamp the team activated the product, or null if intent only.
* @nullable
*/
activated_at: string | null
/**
* ISO-8601 timestamp the intent was first recorded.
* @nullable
*/
created_at: string | null
}
/**
* One row in `inventory.integrations`. Sensitive config is intentionally excluded.
*/
export interface IntegrationEntryApi {
/** Integration kind (e.g. `slack`, `github`, `linear`). */
kind: string
/**
* ISO-8601 timestamp the integration was connected.
* @nullable
*/
created_at: string | null
}
/**
* One row in `inventory.external_data_sources`.
*/
export interface ExternalDataSourceEntryApi {
/** Warehouse source type (e.g. `Stripe`, `Postgres`, `BigQuery`). */
source_type: string
/** Current sync status (`Running`, `Failed`, `Paused`, etc.). */
status: string
/** Schema prefix used by this source, if any. */
prefix: string
/**
* ISO-8601 timestamp the source was connected.
* @nullable
*/
created_at: string | null
/**
* ISO-8601 timestamp of the most recent completed sync job, or null if this source has never completed a sync. Use this to tell a healthy source apart from one stuck in `Running` that has imported zero rows — `status` alone conflates the two.
* @nullable
*/
last_run_at: string | null
/**
* Newest schema-level sync error for this source, or null if no schema is erroring.
* @nullable
*/
latest_error: string | null
}
/**
* One row in either bucket of `inventory.signal_source_configs`.
*/
export interface SignalSourceConfigEntryApi {
/** Source product the config applies to. */
source_product: string
/** Source type within the product. */
source_type: string
}
/**
* `inventory.signal_source_configs` split into enabled and disabled buckets.
*/
export interface SignalSourceConfigsBucketsApi {
/** Source configs the team has explicitly enabled. */
enabled: SignalSourceConfigEntryApi[]
/** Source configs the team has explicitly disabled (different from never wired up). */
disabled: SignalSourceConfigEntryApi[]
}
/**
* `inventory.emit_eligibility` — whether scout findings can reach the inbox for this team.
*/
export interface EmitEligibilityApi {
/** Whether the organization has approved AI data processing (an org-level gate on all scout emits). */
ai_processing_approved: boolean
/** Whether the `signals_scout` signal source is enabled for this team. */
source_enabled: boolean
/** True only when both team/org-level gates pass, so scout findings (signal and report channels alike) actually reach the inbox. When False, every emit is silently dropped — quick-close instead of doing throwaway investigation. Does not account for a scout's own dry-run `emit` toggle, which is per-config, not team-wide. */
can_emit: boolean
/**
* One-line next step to unblock emits when `can_emit` is False; null when emits can flow.
* @nullable
*/
remediation: string | null
}
/**
* One scout in either bucket of `inventory.scout_fleet`.
*/
export interface ScoutFleetEntryApi {
/** The `signals-scout-*` skill this config schedules. */
skill_name: string
/** Minutes between runs when no cron schedule is set (default 1440, every 24 hours). */
run_interval_minutes: number
/**
* Optional cron expression, evaluated in the project timezone. Takes precedence over the interval.
* @nullable
*/
run_cron_schedule: string | null
/** Whether this scout's findings actually reach the inbox. False means dry-run: it runs and logs but emits nothing, so its silence says nothing about the surface it watches. */
emit: boolean
/**
* ISO-8601 timestamp the coordinator last dispatched this scout, or null if it has never run.
* @nullable
*/
last_run_at: string | null
/**
* ISO-8601 timestamp this scout last produced output on either channel (a finding, or an authored/edited report), within `emitted_lookback_days`. Null means quiet for at least that window, not never.
* @nullable
*/
last_emitted_at: string | null
/**
* Why this scout is in the `disabled` bucket: `turned_off` (a person or seed posture set it off), `auto_paused` (the system paused it), or `skill_unavailable` (left on, but its skill was deleted, superseded, or withheld, so it never dispatches). Null for scouts that actually run.
* @nullable
*/
not_running_reason: string | null
/**
* The cause behind an `auto_paused` entry: `no_output`, `ignored`, or `repeated_failures`. Null for every other entry.
* @nullable
*/
pause_reason: string | null
}
/**
* `inventory.scout_fleet` — the other scouts running on this project, split by enablement.
*/
export interface ScoutFleetApi {
/** Scouts that actually run on this team: enabled, with a live skill the coordinator dispatches. */
enabled: ScoutFleetEntryApi[]
/** Scouts that do not run, each carrying a `not_running_reason` — turned off, or left on with a skill that can't dispatch. Different from a surface no scout ever covered. */
disabled: ScoutFleetEntryApi[]
/** The window `last_emitted_at` was resolved over, so a null reads as 'quiet', not 'never'. */
emitted_lookback_days: number
}
/**
* One bucket in `inventory.existing_inbox_reports.by_status`.
*/
export interface InboxReportStatusBucketApi {
/** Report status (e.g. `potential`, `candidate`, `ready`). */
status: string
/** Number of reports in this status (excludes deleted/suppressed). */
count: number
}
/**
* `inventory.existing_inbox_reports` — what's already been surfaced to the inbox.
*/
export interface ExistingInboxReportsApi {
/** Total non-deleted, non-suppressed reports for this team. */
total: number
/** Per-status breakdown of inbox reports. */
by_status: InboxReportStatusBucketApi[]
}
/**
* One row in `inventory.recent_activity.by_scope`.
*/
export interface ScopeActivityEntryApi {
/** Activity-log scope (entity type), e.g. `FeatureFlag`, `Dashboard`, `Survey`. */
scope: string
/** Total activity-log entries for this scope in the window (write velocity). */
edits: number
/** Distinct users who edited this scope in the window. */
users: number
/**
* ISO-8601 timestamp of the most recent edit in the window.
* @nullable
*/
last_edit: string | null
}
/**
* `inventory.recent_activity` — per-scope counts off the activity log.
*/
export interface RecentActivityApi {
/** Lookback window in days the per-scope counts cover. */
window_days: number
/** Per-scope activity rows, busiest scope first. Triage which entity type the team has worked in lately. */
by_scope: ScopeActivityEntryApi[]
}
/**
* One row in `inventory.recent_reviewer_corrections.corrections`.
*/
export interface ReviewerCorrectionEntryApi {
/** UUID of the report whose reviewers a human edited. */
report_id: string
/**
* Report title at the time of the edit.
* @nullable
*/
report_title: string | null
/** GitHub logins on the report before the human edit (lowercased). */
before: string[]
/** GitHub logins on the report after the human edit (lowercased). */
after: string[]
/**
* ISO-8601 timestamp of the edit.
* @nullable
*/
at: string | null
}
/**
* `inventory.recent_reviewer_corrections` — human edits to report reviewer lists.
*/
export interface RecentReviewerCorrectionsApi {
/** Lookback window in days the corrections cover. */
window_days: number
/** Human reviewer edits, newest first. A human swapping a report's suggested reviewers is authoritative ownership precedent — route to who they chose. */
corrections: ReviewerCorrectionEntryApi[]
}
/**
* One row in `inventory.recent_dashboards`.
*/
export interface RecentDashboardEntryApi {
/** Dashboard ID — pass to `dashboard-get` to pull the full payload. */
id: number
/** Dashboard name (may be blank if unnamed). */
name: string
/**
* ISO-8601 timestamp of the most recent view in the Insights UI.
* @nullable
*/
last_accessed_at: string | null
/**
* ISO-8601 timestamp of the most recent data refresh. Distinct from access — a dashboard can be refreshed without anyone viewing it.
* @nullable
*/
last_refresh: string | null
/**
* ISO-8601 timestamp the dashboard was created.
* @nullable
*/
created_at: string | null
}
/**
* One row in `inventory.recent_surveys.recent`.
*/
export interface RecentSurveyEntryApi {
/** Survey UUID — pass to `survey-get` for full question shape. */
id: string
/** Survey name (may be blank if unnamed). */
name: string
/** Survey mode: `popover`, `widget`, `external_survey`, or `api`. */
type: string
/** Derived status: `draft`, `running`, `stopped`, or `archived`. */
status: string
/**
* ISO-8601 last-modified timestamp.
* @nullable
*/
updated_at: string | null
}
/**
* `inventory.recent_surveys` — total + active count, plus the 5 most recently modified.
*/
export interface RecentSurveysApi {
/** Total surveys on the team. */
total_count: number
/** Surveys that are live (not archived, started, and not yet ended). */
active_count: number
/** The 5 most recently updated surveys. */
recent: RecentSurveyEntryApi[]
}
/**
* One row in `inventory.recent_feature_flags.recent`.
*/
export interface RecentFeatureFlagEntryApi {
/** Feature flag ID. */
id: number
/** Flag key used in code (`insights.isFeatureEnabled('<key>')`). */
key: string
/** Human-set description; falls back to the key when blank. */
name: string
/** Whether the flag is currently evaluating (a user could be hitting it). */
active: boolean
/**
* ISO-8601 last-modified timestamp.
* @nullable
*/
updated_at: string | null
}
/**
* `inventory.recent_feature_flags` — total + active count, plus the 5 most recently modified.
*/
export interface RecentFeatureFlagsApi {
/** Total non-deleted feature flags on the team. */
total_count: number
/** Flags currently evaluating (`active=true`). */
active_count: number
/** The 5 most recently updated non-deleted flags. */
recent: RecentFeatureFlagEntryApi[]
}
/**
* One row in `inventory.recent_experiments.recent`.
*/
export interface RecentExperimentEntryApi {
/** Experiment ID. */
id: number
/** Experiment name. */
name: string
/** Derived status: `draft`, `running`, `stopped`, or `archived`. */
status: string
/**
* Key of the experiment's feature flag — cross-ref into `recent_feature_flags`. Null if unlinked.
* @nullable
*/
feature_flag_key: string | null
/**
* ISO-8601 last-modified timestamp.
* @nullable
*/
updated_at: string | null
}
/**
* `inventory.recent_experiments` — total + currently-running count, plus the 5 most recently modified.
*/
export interface RecentExperimentsApi {
/** Total experiments on the team. */
total_count: number
/** Experiments currently running (started, not ended, not archived). */
running_count: number
/** The 5 most recently updated experiments. */
recent: RecentExperimentEntryApi[]
}
/**
* One row in `inventory.recent_alerts.recent`.
*/
export interface RecentAlertEntryApi {
/** Alert configuration UUID. */
id: string
/** Alert name. */
name: string
/** Whether the alert is currently armed. */
enabled: boolean
/** Alert state (e.g. `not_firing`, `firing`). */
state: string
/**
* How often the alert is evaluated (e.g. `daily`, `hourly`); null if unset.
* @nullable
*/
calculation_interval: string | null
/**
* ID of the insight the alert watches; null if none.
* @nullable
*/
insight_id: number | null
/**
* ISO-8601 creation timestamp.
* @nullable
*/
created_at: string | null
}
/**
* `inventory.recent_alerts` — total + currently-enabled count, plus the 5 most recently created.
*/
export interface RecentAlertsApi {
/** Total insight alerts on the team. */
total_count: number
/** Alerts currently armed (`enabled=true`). */
enabled_count: number
/** The 5 most recently created alerts. */
recent: RecentAlertEntryApi[]
}
/**
* One row in `inventory.recent_insights_functions.recent`.
*/
export interface RecentInsightsFunctionEntryApi {
/** Script function UUID. */
id: string
/** Script function name. */
name: string
/**
* Function type: `destination`, `transformation`, `site_app`, etc. Null if unset.
* @nullable
*/
type: string | null
/**
* Function kind sub-classifier; null if unset.
* @nullable
*/
kind: string | null
/** Whether the function is currently enabled. */
enabled: boolean
/**
* ISO-8601 last-modified timestamp.
* @nullable
*/
updated_at: string | null
}
/**
* `inventory.recent_insights_functions` — total + enabled count, plus the 5 most recently modified.
*/
export interface RecentInsightsFunctionsApi {
/** Total non-deleted script functions on the team. */
total_count: number
/** Script functions currently enabled (`enabled=true`). */
enabled_count: number
/** The 5 most recently updated script functions. */
recent: RecentInsightsFunctionEntryApi[]
}
/**
* One row in `inventory.recent_script_flows.recent`.
*/
export interface RecentInsightsFlowEntryApi {
/** Script flow UUID. */
id: string
/** Script flow name. */
name: string
/** Flow lifecycle state (e.g. `draft`, `active`, `archived`). */
status: string
/**
* ISO-8601 last-modified timestamp.
* @nullable
*/
updated_at: string | null
}
/**
* `inventory.recent_script_flows` — total + non-archived count, plus the 5 most recently modified.
*/
export interface RecentInsightsFlowsApi {
/** Total script flows on the team. */
total_count: number
/** Script flows that are not archived. */
active_count: number
/** The 5 most recently updated script flows. */
recent: RecentInsightsFlowEntryApi[]
}
/**
* One row in `inventory.recent_notebooks.recent`.
*/
export interface RecentNotebookEntryApi {
/** Notebook short ID — pass to the notebooks API to open it. */
short_id: string
/** Notebook title (may be blank if untitled). */
title: string
/**
* ISO-8601 last-modified timestamp.
* @nullable
*/
last_modified_at: string | null
}
/**
* `inventory.recent_notebooks` — total + the 5 most recently modified.
*/
export interface RecentNotebooksApi {
/** Total non-deleted notebooks on the team. */
total_count: number
/** The 5 most recently modified notebooks. */
recent: RecentNotebookEntryApi[]
}
/**
* One row in `inventory.recent_cohorts.recent`.
*/
export interface RecentCohortEntryApi {
/** Cohort ID. */
id: number
/** Cohort name. */
name: string
/** True for a one-shot snapshot cohort; false for a dynamic-filter cohort. */
is_static: boolean
/**
* Membership size when last calculated; null if never calculated.
* @nullable
*/
count: number | null
/**
* ISO-8601 creation timestamp.
* @nullable
*/
created_at: string | null
}
/**
* `inventory.recent_cohorts` — total + the 5 most recently created.
*/
export interface RecentCohortsApi {
/** Total non-deleted cohorts on the team. */
total_count: number
/** The 5 most recently created cohorts. */
recent: RecentCohortEntryApi[]
}
/**
* One row in `inventory.recent_actions.recent`.
*/
export interface RecentActionEntryApi {
/** Action ID. */
id: number
/** Action name. */
name: string
/**
* ISO-8601 last-modified timestamp.
* @nullable
*/
updated_at: string | null
}
/**
* `inventory.recent_actions` — total + the 5 most recently modified.
*/
export interface RecentActionsApi {
/** Total non-deleted actions on the team. */
total_count: number
/** The 5 most recently updated actions. */
recent: RecentActionEntryApi[]
}
/**
* One row in `inventory.top_events`.
*/
export interface TopEventEntryApi {
/** Rolling lookback window (in days) that every count and timestamp on this row is measured over — these are windowed figures, NOT lifetime totals. A capture gap can collapse a real, high-volume project's in-window counts to near-zero, so a thin `count` here does not by itself mean the project is low-volume: rule out an ingestion gap (compare against a trailing baseline via a direct `execute-sql`) before closing out a surface as unused. */
window_days: number
/** Event name as captured. */
event: string
/** Number of occurrences within the last `window_days` (windowed, not lifetime). */
count: number
/** `uniq(person_id)` over the window — reach. Distinguishes a high-count event firing on one power user from one firing on many users. */
distinct_users: number
/** Count in just the last 24 hours. Compare to `count / window_days` to spot bursts: a ratio well above `1 / window_days` means the event is concentrated in the last day. */
recent_24h_count: number
/** `uniq(person_id)` over just the last 24 hours. A burst across many users is qualitatively different from one user in a loop. */
recent_24h_users: number
/**
* ISO-8601 timestamp of the earliest occurrence within the `window_days` window. Compare to the window start to spot new event types: close to `now` ⇒ likely new or recently bursting; close to the window edge ⇒ has been around at least that long (the window can't tell you when the event *truly* first appeared).
* @nullable
*/
first_seen_in_window: string | null
/**
* ISO-8601 timestamp of the most recent occurrence within the `window_days` window.
* @nullable
*/
last_seen_in_window: string | null
}
/**
* The deterministic inventory layer of a project profile.
*
* Read this to orient on the team's product mix, integrations, warehouse sources, signal
* coverage, and existing inbox surface in one tool call. Distinct from `SignalScratchpad`:
* profile is ground truth from authoritative tables; memory is agent inference.
*/
export interface ProjectProfileInventoryApi {
/** Free-form orientation: human-set product description + registered app URLs. */
project_context: ProjectContextApi
/** Product keys this team has completed onboarding for, sorted alphabetically. */
products_in_use: string[]
/** Products the team signaled intent to use; useful for spotting stuck onboardings. */
product_intents: ProductIntentEntryApi[]
/** Connected integrations (kind + connection time only — config never surfaced). */
integrations: IntegrationEntryApi[]
/** Connected warehouse sources (excludes soft-deleted). */
external_data_sources: ExternalDataSourceEntryApi[]
/** Signal source configs split into enabled / disabled buckets. */
signal_source_configs: SignalSourceConfigsBucketsApi
/** Whether scout findings can actually reach the inbox for this team — the org-level AI data-processing consent gate and the `signals_scout` source toggle, plus a one-line remediation pointer. Read at cold start to quick-close before doing throwaway work. */
emit_eligibility: EmitEligibilityApi
/** The other scouts configured on this project, split into enabled / disabled, each with its cadence, dry-run posture, last run, and last emit. Read it to see who else is watching this project before investigating a surface a sibling already covers. */
scout_fleet: ScoutFleetApi
/** Counts of reports already in the inbox, grouped by status. */
existing_inbox_reports: ExistingInboxReportsApi
/** Per-scope counts off the activity log over the recent-activity window — cross-cutting orientation across every entity type (surveys, feature flags, experiments, dashboards, insights, cohorts, notebooks, actions, etc.). Each scope reports `edits` (total log entries), `users` (distinct user count), and `last_edit` (ISO-8601). Use to triage which scope a team has been working in lately before drilling down via the per-entity readers or `advanced-activity-logs-list`. */
recent_activity: RecentActivityApi
/** Recent human edits to report reviewer lists (before/after GitHub logins). The strongest ownership precedent available — check it before setting `suggested_reviewers` and fold what it shows into `reviewer:` memory keys. */
recent_reviewer_corrections: RecentReviewerCorrectionsApi
/** Up to 20 dashboards on this team sorted by `last_accessed_at` desc — what the team is currently looking at, not necessarily the most-trafficked. We don't have per-dashboard view counts in Postgres, only the timestamp of the most recent access. */
recent_dashboards: RecentDashboardEntryApi[]
/** Surveys orientation: total + active count, plus the 5 most recently updated surveys with id, name, type, status (draft / running / stopped / archived), and updated_at. */
recent_surveys: RecentSurveysApi
/** Feature flag orientation: total + active count, plus the 5 most recently updated non-deleted flags with id, key, name, active, and updated_at. */
recent_feature_flags: RecentFeatureFlagsApi
/** Experiment orientation: total + running count, plus the 5 most recently updated experiments. The feature_flag_key on each row lets the scout correlate experiments with the `recent_feature_flags` section. */
recent_experiments: RecentExperimentsApi
/** Alert orientation: total + enabled count, plus the 5 most recently created alerts with their state and threshold metadata. */
recent_alerts: RecentAlertsApi
/** Script function orientation: total + enabled count, plus the 5 most recently updated destinations / transformations the team has wired up via the CDP pipelines. */
recent_insights_functions: RecentInsightsFunctionsApi
/** Script flow orientation: total + non-archived count, plus the 5 most recently updated automation flows. */
recent_script_flows: RecentInsightsFlowsApi
/** Notebook orientation: total + the 5 most recently modified notebooks — useful signal for what the team has been investigating. */
recent_notebooks: RecentNotebooksApi
/** Cohort orientation: total + the 5 most recently created cohorts on the team. */
recent_cohorts: RecentCohortsApi
/** Action orientation: total + the 5 most recently updated actions — useful to anchor agent reasoning about what the team treats as a meaningful interaction. */
recent_actions: RecentActionsApi
/**
* Top ~50 events by count over a recent rolling window (each row carries `window_days`), with first/last seen timestamps within that window. These are WINDOWED counts, not lifetime totals: a capture gap can collapse a real, high-volume project's counts to near-zero here, so rule out an ingestion gap (compare against a trailing baseline via a direct `execute-sql`) before reading thinness as a genuinely low-volume project. `null` if the underlying Datastore query failed or timed out (distinct from `[]`, which means the team has no captures in the window). Use the gap between `first_seen_in_window` and `now` to spot new event types or recent bursts.
* @nullable
*/
top_events: TopEventEntryApi[] | null
}
/**
* Top-level `payload` shape on a `SignalProjectProfile` row.
*
* v1 carries `inventory` only. Phase 7 will add `deltas`, `activity_notes`, and
* `narrative` slots — they're absent (not null) in v1 responses.
*/
export interface ProjectProfilePayloadApi {
/** Deterministic snapshot of what's true about the project. */
inventory: ProjectProfileInventoryApi
}
/**
* Wire shape for the project profile returned by `signals-scout-harness-project-profile-list`.
*
* Read this once at the start of a run (after `skill-get`) to orient on the team. Cache
* is per-team with a soft TTL (`PROFILE_TTL`); the response always reflects either the
* latest cached profile or a freshly-built one if the cache was stale or the caller passed
* `force_refresh=true`.
*/
export interface ProjectProfileApi {
/** UUID of the `SignalProjectProfile` row. */
profile_id: string
/** ISO-8601 timestamp the profile was built. */
computed_at: string
/** ISO-8601 timestamp after which the profile is considered stale. */
expires_at: string
/** Schema version of the inventory builder. Bumps invalidate older cached rows. */
source_version: string
/** Structured profile content. v1 has `inventory` only. */
payload: ProjectProfilePayloadApi
}
export type SignalScoutRunSummaryApiMetadataDerived = {
has_emit_report: boolean
has_edit_report: boolean
has_self_improvement: boolean
has_chart: boolean
has_self_validation: boolean
has_structured_output: boolean
}
/**
* Scout-owned per-run context, in two regions. Top-level keys are stamped by the runner at run start. Always present: `harness_prompt_version` (id of the harness prompt build the run was given), `report_channel` (which report tools the run held: `none`, `emit`, `edit`, or `both`), `skill_origin` (`canonical` or `custom`), and `github_guidance` (whether the run got the GitHub evidence section) — the provenance set that says which instructions the run actually got, so runs are only compared against runs of the same shape. Present only when the run departed from a default: `model`, `runtime_adapter`, and `reasoning_effort` (routing overrode the agent-server default), and `network_access` (`full` when the scout's config lifted the trusted-domain network restriction for this run). The nested `derived` object is the harness's own map of boolean run dimensions, computed server-side at finalize: `has_emit_report`, `has_edit_report`, `has_self_improvement`, `has_chart`, and `has_self_validation`. Use `derived` to answer 'what kind of run was this?' instead of parsing the `summary` prose. Note the flags describe the reports the run authored as they stand now, so charts attached to someone else's report via an edit are not counted. A missing `derived` object is unknown, not all-false: the run predates the field, never finalized, or its stamp failed.
*/
export type SignalScoutRunSummaryApiMetadata = {
harness_prompt_version?: string
report_channel?: string
skill_origin?: string
github_guidance?: boolean
model?: string
runtime_adapter?: string
reasoning_effort?: string
network_access?: string
derived?: SignalScoutRunSummaryApiMetadataDerived
[key: string]: unknown
}
/**
* * `not_started` - not_started
* * `queued` - queued
* * `in_progress` - in_progress
* * `completed` - completed
* * `failed` - failed
* * `cancelled` - cancelled
*/
export type RunStatusEnumApi = (typeof RunStatusEnumApi)[keyof typeof RunStatusEnumApi]
export const RunStatusEnumApi = {
NotStarted: 'not_started',
Queued: 'queued',
InProgress: 'in_progress',
Completed: 'completed',
Failed: 'failed',
Cancelled: 'cancelled',
} as const
/**
* Lightweight projection of a `SignalScoutRun` row used by `search-recent-runs`.
*
* Status and timestamps flow from the linked `tasks.TaskRun`.
*/
export interface SignalScoutRunSummaryApi {
/** UUID of the bridge row. */
run_id: string
/** Canonical skill name the run executed (e.g. `signals-scout-general`). */
skill_name: string
/** Skill version snapshotted at run start. */
skill_version: number
/** Status from the linked TaskRun.
*
* * `not_started` - not_started
* * `queued` - queued
* * `in_progress` - in_progress
* * `completed` - completed
* * `failed` - failed
* * `cancelled` - cancelled */
status: RunStatusEnumApi
/** ISO-8601 timestamp the bridge row was created — the field `date_from` / `date_to` filter and order on. Use this (not `started_at`) as the `date_to` cursor when walking past the 100-row cap, so runs created in the gap between a boundary run's TaskRun and its bridge row aren't skipped. */
created_at: string
/** ISO-8601 timestamp the TaskRun was created. */
started_at: string
/**
* ISO-8601 timestamp the TaskRun completed; null while still running.
* @nullable
*/
completed_at: string | null
/**
* UUID of the Tasks `Task` the scout span ran inside.
* @nullable
*/
task_id?: string | null
/**
* UUID of the Tasks `TaskRun`. Pairs with `task_id` to deep-link.
* @nullable
*/
task_run_id?: string | null
/**
* Relative deep-link to the Tasks UI for this run, e.g. `/project/{team_id}/tasks/{task_id}?runId={task_run_id}`.
* @nullable
*/
task_url?: string | null
/** One-paragraph close-out the scout wrote at end-of-run. Empty string for runs that errored before close-out. The dedupe key for non-emitting runs. */
summary: string
/**
* Full `error_message` from the linked TaskRun, surfaced only for failed/cancelled runs (null otherwise, including on success). Use `failure_reason` for a concise scan-friendly summary.
* @nullable
*/
error?: string | null
/**
* Concise derived reason the run didn't complete cleanly — the first line of `error` (bounded), or a status-derived fallback. Null unless the run terminated failed/cancelled. Read this to see at a glance *why* a run emitted nothing without pulling full stack traces.
* @nullable
*/
failure_reason?: string | null
/** Number of findings this run actually emitted to the inbox. 0 for runs that investigated but surfaced nothing, or ran dry-run / before AI approval. `> 0` means the run produced at least one `Signal`. */
emitted_count: number
/** The `finding_id`s behind `emitted_count`, in emit order. Each maps to a `Signal` with `source_id = run:<run_id>:finding:<finding_id>`. Empty for non-emitting runs. */
emitted_finding_ids: string[]
/** The `SignalReport` ids this run authored directly via the `emit_report` channel, in emit order. Separate from `emitted_finding_ids` (weak `emit_signal` findings) — a report-authoring scout writes a full report here instead. Empty for runs that authored no report. */
emitted_report_ids: string[]
/** The `SignalReport` ids this run mutated via the `edit_report` channel (rewrote title/summary and/or appended a note), deduped. Distinct from `emitted_report_ids`: edit can target any inbox report, so these are generally not reports the run authored. Empty for runs that edited no report. */
edited_report_ids: string[]
/** Scout-owned per-run context, in two regions. Top-level keys are stamped by the runner at run start. Always present: `harness_prompt_version` (id of the harness prompt build the run was given), `report_channel` (which report tools the run held: `none`, `emit`, `edit`, or `both`), `skill_origin` (`canonical` or `custom`), and `github_guidance` (whether the run got the GitHub evidence section) — the provenance set that says which instructions the run actually got, so runs are only compared against runs of the same shape. Present only when the run departed from a default: `model`, `runtime_adapter`, and `reasoning_effort` (routing overrode the agent-server default), and `network_access` (`full` when the scout's config lifted the trusted-domain network restriction for this run). The nested `derived` object is the harness's own map of boolean run dimensions, computed server-side at finalize: `has_emit_report`, `has_edit_report`, `has_self_improvement`, `has_chart`, and `has_self_validation`. Use `derived` to answer 'what kind of run was this?' instead of parsing the `summary` prose. Note the flags describe the reports the run authored as they stand now, so charts attached to someone else's report via an edit are not counted. A missing `derived` object is unknown, not all-false: the run predates the field, never finalized, or its stamp failed. */
metadata: SignalScoutRunSummaryApiMetadata
}
export type SignalScoutRunDetailApiMetadataDerived = {
has_emit_report: boolean
has_edit_report: boolean
has_self_improvement: boolean
has_chart: boolean
has_self_validation: boolean
has_structured_output: boolean
}
/**
* Scout-owned per-run context, in two regions. Top-level keys are stamped by the runner at run start. Always present: `harness_prompt_version` (id of the harness prompt build the run was given), `report_channel` (which report tools the run held: `none`, `emit`, `edit`, or `both`), `skill_origin` (`canonical` or `custom`), and `github_guidance` (whether the run got the GitHub evidence section) — the provenance set that says which instructions the run actually got, so runs are only compared against runs of the same shape. Present only when the run departed from a default: `model`, `runtime_adapter`, and `reasoning_effort` (routing overrode the agent-server default), and `network_access` (`full` when the scout's config lifted the trusted-domain network restriction for this run). The nested `derived` object is the harness's own map of boolean run dimensions, computed server-side at finalize: `has_emit_report`, `has_edit_report`, `has_self_improvement`, `has_chart`, and `has_self_validation`. Use `derived` to answer 'what kind of run was this?' instead of parsing the `summary` prose. Note the flags describe the reports the run authored as they stand now, so charts attached to someone else's report via an edit are not counted. A missing `derived` object is unknown, not all-false: the run predates the field, never finalized, or its stamp failed.
*/
export type SignalScoutRunDetailApiMetadata = {
harness_prompt_version?: string
report_channel?: string
skill_origin?: string
github_guidance?: boolean
model?: string
runtime_adapter?: string
reasoning_effort?: string
network_access?: string
derived?: SignalScoutRunDetailApiMetadataDerived
[key: string]: unknown
}
/**
* Full `SignalScoutRun` projection used by `get-run`. Same shape as the summary
* today; kept distinct so future detail-only extensions (linked Signal rows,
* LLMA token-cost join) can land here without bloating the list response.
*/
export interface SignalScoutRunDetailApi {
/** UUID of the bridge row. */
run_id: string
/** Canonical skill name the run executed (e.g. `signals-scout-general`). */
skill_name: string
/** Skill version snapshotted at run start. */
skill_version: number
/** Status from the linked TaskRun.
*
* * `not_started` - not_started
* * `queued` - queued
* * `in_progress` - in_progress
* * `completed` - completed
* * `failed` - failed
* * `cancelled` - cancelled */
status: RunStatusEnumApi
/** ISO-8601 timestamp the bridge row was created — the field `date_from` / `date_to` filter and order on. Use this (not `started_at`) as the `date_to` cursor when walking past the 100-row cap, so runs created in the gap between a boundary run's TaskRun and its bridge row aren't skipped. */
created_at: string
/** ISO-8601 timestamp the TaskRun was created. */
started_at: string
/**
* ISO-8601 timestamp the TaskRun completed; null while still running.
* @nullable
*/
completed_at: string | null
/**
* UUID of the Tasks `Task` the scout span ran inside.
* @nullable
*/
task_id?: string | null
/**
* UUID of the Tasks `TaskRun`. Pairs with `task_id` to deep-link.
* @nullable
*/
task_run_id?: string | null
/**
* Relative deep-link to the Tasks UI for this run, e.g. `/project/{team_id}/tasks/{task_id}?runId={task_run_id}`.
* @nullable
*/
task_url?: string | null
/** One-paragraph close-out the scout wrote at end-of-run. Empty string for runs that errored before close-out. The dedupe key for non-emitting runs. */
summary: string
/**
* Full `error_message` from the linked TaskRun, surfaced only for failed/cancelled runs (null otherwise, including on success). Use `failure_reason` for a concise scan-friendly summary.
* @nullable
*/
error?: string | null
/**
* Concise derived reason the run didn't complete cleanly — the first line of `error` (bounded), or a status-derived fallback. Null unless the run terminated failed/cancelled. Read this to see at a glance *why* a run emitted nothing without pulling full stack traces.
* @nullable
*/
failure_reason?: string | null
/** Number of findings this run actually emitted to the inbox. 0 for runs that investigated but surfaced nothing, or ran dry-run / before AI approval. `> 0` means the run produced at least one `Signal`. */
emitted_count: number
/** The `finding_id`s behind `emitted_count`, in emit order. Each maps to a `Signal` with `source_id = run:<run_id>:finding:<finding_id>`. Empty for non-emitting runs. */
emitted_finding_ids: string[]
/** The `SignalReport` ids this run authored directly via the `emit_report` channel, in emit order. Separate from `emitted_finding_ids` (weak `emit_signal` findings) — a report-authoring scout writes a full report here instead. Empty for runs that authored no report. */
emitted_report_ids: string[]
/** The `SignalReport` ids this run mutated via the `edit_report` channel (rewrote title/summary and/or appended a note), deduped. Distinct from `emitted_report_ids`: edit can target any inbox report, so these are generally not reports the run authored. Empty for runs that edited no report. */
edited_report_ids: string[]
/** Scout-owned per-run context, in two regions. Top-level keys are stamped by the runner at run start. Always present: `harness_prompt_version` (id of the harness prompt build the run was given), `report_channel` (which report tools the run held: `none`, `emit`, `edit`, or `both`), `skill_origin` (`canonical` or `custom`), and `github_guidance` (whether the run got the GitHub evidence section) — the provenance set that says which instructions the run actually got, so runs are only compared against runs of the same shape. Present only when the run departed from a default: `model`, `runtime_adapter`, and `reasoning_effort` (routing overrode the agent-server default), and `network_access` (`full` when the scout's config lifted the trusted-domain network restriction for this run). The nested `derived` object is the harness's own map of boolean run dimensions, computed server-side at finalize: `has_emit_report`, `has_edit_report`, `has_self_improvement`, `has_chart`, and `has_self_validation`. Use `derived` to answer 'what kind of run was this?' instead of parsing the `summary` prose. Note the flags describe the reports the run authored as they stand now, so charts attached to someone else's report via an edit are not counted. A missing `derived` object is unknown, not all-false: the run predates the field, never finalized, or its stamp failed. */
metadata: SignalScoutRunDetailApiMetadata
}
/**
* One suggested reviewer — identified by `github_login`, `user_uuid`, or both.
*
* The server canonicalizes each entry to a lowercased GitHub login: a `user_uuid` is resolved to the
* org member's linked GitHub login (and wins over a supplied `github_login` when both are given). A
* `user_uuid` that isn't an org member of this team with a linked GitHub identity is rejected — so a
* reviewer is never silently dropped.
*/
export interface SuggestedReviewerApi {
/**
* GitHub login (case-insensitive, stored lowercased) — e.g. `octocat`, no `@`, no display name. Resolve one via `scout-members-list` (each member row carries a resolved `github_login`) or git history when you only have a name.
* @maxLength 200
*/
github_login?: string
/** Insights user UUID (e.g. from `scout-members-list`, or an entity's `created_by`). Resolved server-side to the member's linked GitHub login — use this when you know the Insights user but not their GitHub handle. Must be a concrete UUID; the `@me` alias is not valid here. */
user_uuid?: string
/**
* One sentence of evidence for WHY this person: what ties them to the affected surface (e.g. 'authored 4 of the last 10 commits touching products/tracing/mcp/', 'human correction routed the prior tracing report to them'). Persisted on the report so the routing is auditable — always set it when you can name the evidence; 'precedent' alone is weak, prefer code-derived ownership.
* @maxLength 500
* @nullable
*/
reason?: string | null
}
/**
* Request body for `edit-report`. Can target ANY of the team's inbox reports, not just scout-authored ones.
*/
export interface EditReportRequestApi {
/** Id of the report to edit (must belong to this project). */
report_id: string
/**
* Optional new title. Conventional-commit style (`type(scope): description`) renders with type/scope styling. The pipeline may later re-research and overwrite it.
* @maxLength 300
* @nullable
*/
title?: string | null
/**
* Optional new summary. Markdown is supported (headings, lists, code, links; images are not rendered); lead with one plain declarative sentence — it becomes the inbox card headline. The pipeline may later re-research and overwrite it.
* @nullable
*/
summary?: string | null
/**
* Optional free-form note to append to the report's work log (attributed to this scout).
* @nullable
*/
append_note?: string | null
/**
* Optional reviewers to set on the report (each a `github_login` and/or `user_uuid`), replacing any existing list. Use this to route a report that surfaced with no reviewer — it re-runs autostart, so a report that was missing a qualifying reviewer can now open a draft PR. An empty list is a no-op (existing reviewers are left untouched, never cleared).
* @maxItems 10
*/
suggested_reviewers?: SuggestedReviewerApi[]
/**
* The full set of charts the report should show. Replaces the report's charts rather than adding to them, the way `summary` replaces the summary — so send every chart you want kept. Omit the field (or send null) to leave the report's existing charts untouched, and send an empty list to take them all down.
* @maxItems 20
* @nullable
*/
charts?: ReportChartApi[] | null
}
export interface EditReportResponseApi {
/** Id of the edited report. */
report_id: string
/** Which presentation fields changed (e.g. `title`, `summary`); empty if only a note was appended. */
updated_fields: string[]
/** Whether a note artefact was appended. */
note_appended: boolean
/** Whether the report's suggested reviewers were replaced. */
reviewers_set: boolean
/**
* How many charts the report now shows, or null if the edit left its charts as they were (the field omitted, or a re-send of what was already stored). 0 means the edit took the report's charts down.
* @nullable
*/
charts_set: number | null
}
/**
* * `P0` - P0
* * `P1` - P1
* * `P2` - P2
* * `P3` - P3
* * `P4` - P4
*/
export type AutonomyPriorityEnumApi = (typeof AutonomyPriorityEnumApi)[keyof typeof AutonomyPriorityEnumApi]
export const AutonomyPriorityEnumApi = {
P0: 'P0',
P1: 'P1',
P2: 'P2',
P3: 'P3',
P4: 'P4',
} as const
/**
* One finding a scout run emitted to the inbox — the persisted, queryable record of
* *what* the run surfaced, returned by `scout-runs-emissions-list`. The emitted text
* lives in `description`; `source_id` is the join key (`run:<run_id>:finding:<finding_id>`)
* back into the underlying signal store.
*/
export interface SignalScoutEmissionApi {
readonly id: string
/** UUID of the `SignalScoutRun` that emitted this finding. */
run_id: string
/** Stable id the finding was emitted under; matches an entry in the run's `emitted_finding_ids`. */
finding_id: string
/** The emitted finding prose — the signal's `description` as surfaced to the inbox. */
description: string
/**
* Agent's weight for the signal in [0, 1]. Drives ranking in the inbox.
* @minimum 0
* @maximum 1
*/
weight: number
/**
* Agent's confidence the finding is real in [0, 1].
* @minimum 0
* @maximum 1
*/
confidence: number
/** Optional severity tag — one of P0, P1, P2, P3, P4 — or null if the run didn't set one.
*
* * `P0` - P0
* * `P1` - P1
* * `P2` - P2
* * `P3` - P3
* * `P4` - P4 */
severity: AutonomyPriorityEnumApi | null
/** Slug tags the scout attached to this finding (lowercase kebab-case, e.g. `cost-spike`). Empty list when the run set none. */
tags: string[]
/** Deterministic `run:<run_id>:finding:<finding_id>` — the join key into the underlying signal store. */
source_id: string
/** ISO-8601 timestamp the finding was emitted. */
emitted_at: string
}
/**
* Minimal inbox `SignalReport` projection for the scout reverse lookup — just enough
* for the scout UI to render a clickable chip and deep-link into the inbox, which loads
* the full report itself.
*/
export interface LinkedSignalReportApi {
/** UUID of the linked `SignalReport`. */
id: string
/**
* LLM-generated report title, or null if the report hasn't been summarised yet.
* @nullable
*/
title: string | null
/** Current report status (e.g. `potential`, `ready`, `resolved`). */
status: string
}
/**
* One finding the run emitted, paired with the inbox report (if any) its signal grouped into.
*
* Best-effort reverse of the report -> signals link: `report` is null when the finding hasn't
* grouped into a report yet, was de-duplicated away, or its signal was deleted.
*/
export interface ScoutEmissionReportLinkApi {
/** Stable id the finding was emitted under. */
finding_id: string
/** Deterministic `run:<run_id>:finding:<finding_id>` join key into the signal store. */
source_id: string
/** The inbox report this finding linked to, or null if none could be resolved. */
report: LinkedSignalReportApi | null
}
/**
* One observation backing an authored report — becomes a bound signal row on the report.
*/
export interface ReportEvidenceApi {
/** Prose for this observation. Embedded and rendered to the safety/research surfaces. */
description: string
/** Stable id for this observation within the report (lets a later edit address it). */
source_id: string
/**
* Optional per-signal weight (defaults to 1.0). Scouts rarely need to set this.
* @minimum 0
*/
weight?: number
}
/**
* * `immediately_actionable` - immediately_actionable
* * `requires_human_input` - requires_human_input
* * `not_actionable` - not_actionable
*/
export type ActionabilityEnumApi = (typeof ActionabilityEnumApi)[keyof typeof ActionabilityEnumApi]
export const ActionabilityEnumApi = {
ImmediatelyActionable: 'immediately_actionable',
RequiresHumanInput: 'requires_human_input',
NotActionable: 'not_actionable',
} as const
/**
* Request body for `emit-report`. Run attribution is taken from the URL path.
*/
export interface EmitReportRequestApi {
/**
* One-line report title the inbox shows. Conventional-commit style (`type(scope): description`, e.g. `fix(insights): missing series color`) renders with type/scope styling.
* @maxLength 300
*/
title: string
/** The report body the inbox shows. Markdown is supported (headings, lists, code, links; images are not rendered). Lead with one plain declarative sentence — the inbox card uses your first line verbatim as the headline (~140 chars, emphasis stripped), then renders the full markdown in the detail view. */
summary: string
/**
* The observations backing the report — each becomes a bound signal. At least one.
* @minItems 1
*/
evidence: ReportEvidenceApi[]
/** 2-3 sentence evidence-grounded justification for the actionability call below. */
actionability_explanation: string
/** The scout's actionability call: `immediately_actionable` -> the report surfaces READY; `requires_human_input` -> PENDING_INPUT; `not_actionable` -> suppressed. A safety-judge failure suppresses the report regardless.
*
* * `immediately_actionable` - immediately_actionable
* * `requires_human_input` - requires_human_input
* * `not_actionable` - not_actionable */
actionability: ActionabilityEnumApi
/** Whether the issue is already being handled — fixed in recent changes, or with a fix in flight (an open PR, a recently active branch, an assigned / in-progress issue or agent task). Gates autostart, so a wrong `false` opens a duplicate PR. Tracked separately. */
already_addressed?: boolean
/**
* Optional repo for autostart (opening a draft PR): `owner/repo` targets that repo, the `NO_REPO` sentinel opts out (report lands without a PR), and omitting it triggers free-form selection across the team's repos — the slow path on a many-repo team, so pass `owner/repo` when you know it.
* @nullable
*/
repository?: string | null
/** Optional priority (`P0`-`P4`). Required for autostart; pair with `priority_explanation`.
*
* * `P0` - P0
* * `P1` - P1
* * `P2` - P2
* * `P3` - P3
* * `P4` - P4 */
priority?: AutonomyPriorityEnumApi | null
/**
* 2-3 sentence justification for `priority`. Required when `priority` is set.
* @nullable
*/
priority_explanation?: string | null
/**
* Optional reviewers to route the report to (each a `github_login` and/or `user_uuid`). This is the primary way a report reaches a human — the inbox floats a reviewer's own reports to the top of their inbox even when no PR is involved — so set it whenever you can name a plausible owner. It also gates autostart: a PR opens only if at least one reviewer clears their autonomy threshold.
* @maxItems 10
*/
suggested_reviewers?: SuggestedReviewerApi[]
/**
* Optional charts to attach to the report — the inbox renders them inline, so a metric move is something the reader sees rather than a number they take on trust. Attach one whenever the finding rests on a trend, a spike, or a comparison you already queried.
* @maxItems 20
*/
charts?: ReportChartApi[]
}
export interface EmitReportResponseApi {
/**
* The authored report's id (null only when a preflight gate skipped the call). Returned even when suppressed, so you can edit/dedup against it.
* @nullable
*/
report_id: string | null
/**
* Birth status: `ready` | `pending_input` | `suppressed`, or null when gate-skipped.
* @nullable
*/
report_status: string | null
/** True when the report actually surfaced in the inbox (READY or PENDING_INPUT). */
emitted: boolean
/**
* `scout_config_missing` | `scout_emit_disabled` | `ai_processing_not_approved` | `source_disabled` | null when not gate-skipped.
* @nullable
*/
skipped_reason: string | null
/**
* When the safety judge suppressed the report, why; null when safe.
* @nullable
*/
safety_explanation: string | null
/**
* One-line, actionable next step when `skipped_reason` is set and the block is fixable (e.g. an org admin must approve AI data processing). Null when the report was authored or the skip isn't something the scout can act on.
* @nullable
*/
remediation: string | null
}
/**
* One citation attached to a finding. Mirrors `SignalsScoutEvidenceEntry`.
*/
export interface EvidenceEntryApi {
/** Source the citation came from (`error_tracking`, `session_replay`, `logs`, ...). */
source_product: string
/** One-sentence prose about why this evidence supports the finding. */
summary: string
/**
* Optional ID of the cited entity (issue id, recording id, log query id).
* @nullable
*/
entity_id?: string | null
}
export interface TimeRangeApi {
/** ISO-8601 inclusive lower bound for the finding's window. */
date_from: string
/** ISO-8601 inclusive upper bound for the finding's window. */
date_to: string
}
/**
* Request body for `emit-finding`. Run attribution is taken from the URL path.
*/
export interface EmitFindingRequestApi {
/**
* Canonical evidence-bundle prose. Becomes the signal's `description`.
* @maxLength 50000
*/
description: string
/**
* Agent's confidence the finding is real in [0, 1]. Persisted in `extra`.
* @minimum 0
* @maximum 1
*/
confidence: number
/**
* Citations supporting the finding. Capped at 20 entries.
* @maxItems 20
*/
evidence: EvidenceEntryApi[]
/**
* Optional one-line hypothesis the finding tests.
* @nullable
*/
hypothesis?: string | null
/** Optional severity tag — one of P0, P1, P2, P3, P4. Informational only.
*
* * `P0` - P0
* * `P1` - P1
* * `P2` - P2
* * `P3` - P3
* * `P4` - P4 */
severity?: AutonomyPriorityEnumApi | null
/** Optional keys for downstream dedupe (e.g. `error_tracking_issue:<id>`). */
dedupe_keys?: string[]
/**
* Optional category tags as lowercase kebab-case slugs (e.g. `cost-spike`, `silent-failure`), max 10. Reuse the vocabulary in your `tags:<domain>:taxonomy` scratchpad entry when a tag fits; coin a new slug when a genuinely new category emerges. Near-miss formats are normalized to slugs; persisted in the signal's `extra.tags` and on the emission row.
* @maxItems 10
* @items.maxLength 50
*/
tags?: string[]
/** Optional time window the finding refers to. */
time_range?: TimeRangeApi | null
/**
* Optional MCP trace id for cross-system debugging.
* @nullable
*/
mcp_trace_id?: string | null
/**
* Stable id for this finding, baked into the signal's source_id for traceability. NOT a dedupe key — re-emitting the same id creates another signal.
* @maxLength 100
* @nullable
*/
finding_id?: string | null
}
export interface EmitFindingResponseApi {
/** Stable id for the finding (echoed back from request, or generated). */
finding_id: string
/** Whether `emit_signal` was actually fired. */
emitted: boolean
/**
* `ai_processing_not_approved` | `source_disabled` | null when emitted normally.
* @nullable
*/
skipped_reason: string | null
/**
* One-line, actionable next step when `skipped_reason` is set and the block is fixable (e.g. an org admin must approve AI data processing). Null when emitted normally or the skip isn't something the scout can act on.
* @nullable
*/
remediation: string | null
}
/**
* The record itself, as a JSON object. Must validate against the scout config's `structured_output_schema` (shown in the run prompt); any invalid record fails the whole call with nothing written.
*/
export type StructuredOutputRecordApiPayload = { [key: string]: unknown }
/**
* One record submitted through `scout-record-output`.
*/
export interface StructuredOutputRecordApi {
/** The record itself, as a JSON object. Must validate against the scout config's `structured_output_schema` (shown in the run prompt); any invalid record fails the whole call with nothing written. */
payload: StructuredOutputRecordApiPayload
/**
* Optional key naming what this record is about — a report id, URL, account key — so per-entity lookups don't need to parse `payload`. Omit for a run-level record.
* @maxLength 200
* @nullable
*/
subject?: string | null
}
/**
* Request body for `scout-record-output`: a batch of schema-validated records.
*/
export interface RecordStructuredOutputRequestApi {
/**
* Records to record, each validated against the scout config's `structured_output_schema`. All-or-nothing: if any record fails validation, nothing is written and the error names the failing records. Capped at 100 per call; batch per-entity judgments rather than calling once per record.
* @minItems 1
* @maxItems 100
*/
records: StructuredOutputRecordApi[]
}
/**
* Outcome of an accepted `scout-record-output` call.
*/
export interface RecordStructuredOutputResponseApi {
/** How many records were recorded (all of them, or none). */
recorded_count: number
/** Deterministic event ids of the recorded `$scout_structured_output` events, in submission order. Stable across a resubmission of the identical batch, which is what makes retrying a failed delivery safe. */
record_ids: string[]
}
/**
* Request body for the batched emissions / emission-reports lookups: the set of run UUIDs to
* resolve in one call. Collapses the findings UI's old per-run fan-out (one request — and for the
* reports lookup, one Datastore round-trip — per emitted run) into a single request.
*/
export interface ScoutRunIdsBatchRequestApi {
/**
* UUIDs of the `SignalScoutRun` rows to resolve in one batch. Run ids belonging to another team are silently ignored (they contribute no rows) rather than failing the whole request. Capped at 200 ids per call.
* @maxItems 200
*/
run_ids: string[]
}
/**
* Fleet-wide tally of recent scout output — legacy `emit_signal` findings plus reports
* authored/edited via the report channel. Backs the "Scout findings" callout so it renders
* from one cheap query instead of the client walking the whole paginated runs window.
*/
export interface FleetFindingsSummaryApi {
/** Total findings the fleet emitted in the window — the sum of each run's `emitted_count`, over the most recent 120 runs that produced output. */
count: number
/** Number of distinct scouts (skills) that produced output in the window — emitted a finding, or authored/edited an inbox report that survives the 50-report cap (a report-only scout whose touched reports all fell outside the cap is not counted, matching the findings page's scout filter). */
scout_count: number
/** Number of distinct inbox reports scouts authored via `emit_report`, deduped across runs, over the same most-recent-120-output-runs set as `count`, capped to the 50 most recently touched reports (the same slice the findings page lists). */
authored_report_count: number
/** Number of distinct inbox reports scouts edited via `edit_report`, deduped across runs, over the same most-recent-120-output-runs set as `count`, capped to the 50 most recently touched reports (the same slice the findings page lists) and excluding reports also authored within that set (authoring supersedes an edit; a report whose authoring run falls outside the cap counts as edited). */
edited_report_count: number
/**
* ISO-8601 timestamp of the most recent output run (TaskRun completion, falling back to run creation), or null when nothing was produced in the window.
* @nullable
*/
latest_at: string | null
}
/**
* `SignalScratchpad` projection used by `search-memory` and `remember`.
*/
export interface ScratchpadEntryApi {
/** Agent-chosen semantic key, unique per team. */
key: string
/** Prose content for prompt injection. Blank when the search projected it out (`keys_only=true`); truncated to a preview when `content_max_chars` was set. */
content: string
/**
* ISO-8601 creation timestamp.
* @nullable
*/
created_at: string | null
/**
* ISO-8601 last-write timestamp.
* @nullable
*/
updated_at: string | null
/**
* Run that wrote this entry, or null if human-authored.
* @nullable
*/
created_by_run_id: string | null
/**
* Canonical skill name of the scout that created this entry (e.g. `signals-scout-apm`), or null if human-authored.
* @nullable
*/
created_by_skill?: string | null
/**
* Relative Tasks UI deep-link to the run that created this entry, or null if the run linkage isn't captured.
* @nullable
*/
created_by_run_url?: string | null
}
/**
* Request body for `remember`.
*/
export interface RememberRequestApi {
/**
* Agent-chosen semantic key, unique per team; re-using a key overwrites the entry in place. Key off the *stable identity* of what you're tracking — never embed a date, timestamp, or run id (that mints a new row every run and breaks dedupe). For run state/cursors, use one fixed key and keep the timestamp in `content`.
* @maxLength 300
*/
key: string
/**
* Prose to write. Read verbatim into future prompts.
* @maxLength 50000
*/
content: string
/**
* Run that authored this memory; persisted as `created_by_run_id` for lineage. Best-effort — a `run_id` that isn't a run on this project is dropped (lineage left null), not rejected, so the memory write is never lost.
* @nullable
*/
run_id?: string | null
}
/**
* Request body for `forget`.
*/
export interface ForgetRequestApi {
/**
* Memory key to delete.
* @maxLength 300
*/
key: string
}
export interface ForgetResponseApi {
/** Whether a row was actually removed (false if the key didn't exist). */
deleted: boolean
}
/**
* * `session_replay` - Session replay
* * `llm_analytics` - LLM analytics
* * `github` - GitHub
* * `linear` - Linear
* * `jira` - Jira
* * `zendesk` - Zendesk
* * `conversations` - Conversations
* * `error_tracking` - Error tracking
* * `pganalyze` - pganalyze
* * `signals_scout` - Signals scout
* * `logs` - Logs
* * `health_checks` - Health checks
* * `endpoints` - Endpoints
* * `replay_vision` - Replay Vision
* * `analytics` - Product analytics
* * `freshdesk` - Freshdesk
* * `freshservice` - Freshservice
* * `front` - Front
* * `gorgias` - Gorgias
* * `kustomer` - Kustomer
* * `dixa` - Dixa
* * `plain` - Plain
* * `gitlab` - GitLab
* * `gitea` - Gitea
* * `shortcut` - Shortcut
* * `sentry` - Sentry
* * `rollbar` - Rollbar
* * `bugsnag` - Bugsnag
* * `honeybadger` - Honeybadger
* * `raygun` - Raygun
* * `snyk` - Snyk
* * `sonarqube` - SonarQube
* * `semgrep` - Semgrep
* * `rapid7_insightvm` - Rapid7 InsightVM
* * `featurebase` - Featurebase
* * `frill` - Frill
* * `aha` - Aha
* * `uservoice` - UserVoice
* * `productboard` - Productboard
* * `canny` - Canny
* * `asknicely` - AskNicely
* * `retently` - Retently
* * `appfigures` - Appfigures
* * `appfollow` - AppFollow
* * `judgeme_reviews` - Judge.me
* * `intercom` - Intercom
* * `hubspot` - HubSpot
* * `engineering_analytics` - Engineering analytics
* * `google_search_console` - Google Search Console
*/
export type SignalSourceConfigSourceProductEnumApi =
(typeof SignalSourceConfigSourceProductEnumApi)[keyof typeof SignalSourceConfigSourceProductEnumApi]
export const SignalSourceConfigSourceProductEnumApi = {
SessionReplay: 'session_replay',
LlmAnalytics: 'llm_analytics',
Github: 'github',
Linear: 'linear',
Jira: 'jira',
Zendesk: 'zendesk',
Conversations: 'conversations',
ErrorTracking: 'error_tracking',
Pganalyze: 'pganalyze',
SignalsScout: 'signals_scout',
Logs: 'logs',
HealthChecks: 'health_checks',
Endpoints: 'endpoints',
ReplayVision: 'replay_vision',
Analytics: 'analytics',
Freshdesk: 'freshdesk',
Freshservice: 'freshservice',
Front: 'front',
Gorgias: 'gorgias',
Kustomer: 'kustomer',
Dixa: 'dixa',
Plain: 'plain',
Gitlab: 'gitlab',
Gitea: 'gitea',
Shortcut: 'shortcut',
Sentry: 'sentry',
Rollbar: 'rollbar',
Bugsnag: 'bugsnag',
Honeybadger: 'honeybadger',
Raygun: 'raygun',
Snyk: 'snyk',
Sonarqube: 'sonarqube',
Semgrep: 'semgrep',
Rapid7Insightvm: 'rapid7_insightvm',
Featurebase: 'featurebase',
Frill: 'frill',
Aha: 'aha',
Uservoice: 'uservoice',
Productboard: 'productboard',
Canny: 'canny',
Asknicely: 'asknicely',
Retently: 'retently',
Appfigures: 'appfigures',
Appfollow: 'appfollow',
JudgemeReviews: 'judgeme_reviews',
Intercom: 'intercom',
Hubspot: 'hubspot',
EngineeringAnalytics: 'engineering_analytics',
GoogleSearchConsole: 'google_search_console',
} as const
/**
* * `session_analysis_cluster` - Session analysis cluster
* * `evaluation` - Evaluation
* * `evaluation_report` - Evaluation report
* * `issue` - Issue
* * `ticket` - Ticket
* * `issue_created` - Issue created
* * `issue_reopened` - Issue reopened
* * `issue_spiking` - Issue spiking
* * `cross_source_issue` - Cross source issue
* * `alert_state_change` - Alert state change
* * `health_issue` - Health issue
* * `endpoint_execution_failed` - Endpoint execution failed
* * `endpoint_breakdown_limit_exceeded` - Endpoint breakdown limit exceeded
* * `scanner_finding` - Scanner finding
* * `anomaly_investigation` - Anomaly investigation
* * `ci_flaky_check` - CI flaky check
* * `ci_broken_default_branch` - CI broken default branch
* * `ci_duration_regression` - CI duration regression
*/
export type SignalSourceConfigSourceTypeEnumApi =
(typeof SignalSourceConfigSourceTypeEnumApi)[keyof typeof SignalSourceConfigSourceTypeEnumApi]
export const SignalSourceConfigSourceTypeEnumApi = {
SessionAnalysisCluster: 'session_analysis_cluster',
Evaluation: 'evaluation',
EvaluationReport: 'evaluation_report',
Issue: 'issue',
Ticket: 'ticket',
IssueCreated: 'issue_created',
IssueReopened: 'issue_reopened',
IssueSpiking: 'issue_spiking',
CrossSourceIssue: 'cross_source_issue',
AlertStateChange: 'alert_state_change',
HealthIssue: 'health_issue',
EndpointExecutionFailed: 'endpoint_execution_failed',
EndpointBreakdownLimitExceeded: 'endpoint_breakdown_limit_exceeded',
ScannerFinding: 'scanner_finding',
AnomalyInvestigation: 'anomaly_investigation',
CiFlakyCheck: 'ci_flaky_check',
CiBrokenDefaultBranch: 'ci_broken_default_branch',
CiDurationRegression: 'ci_duration_regression',
} as const
export interface SignalSourceConfigApi {
readonly id: string
source_product: SignalSourceConfigSourceProductEnumApi
source_type: SignalSourceConfigSourceTypeEnumApi
enabled?: boolean
config?: unknown
readonly created_at: string
readonly updated_at: string
/** @nullable */
readonly status: string | null
}
export interface PaginatedSignalSourceConfigListApi {
count: number
/** @nullable */
next?: string | null
/** @nullable */
previous?: string | null
results: SignalSourceConfigApi[]
}
export interface PatchedSignalSourceConfigApi {
readonly id?: string
source_product?: SignalSourceConfigSourceProductEnumApi
source_type?: SignalSourceConfigSourceTypeEnumApi
enabled?: boolean
config?: unknown
readonly created_at?: string
readonly updated_at?: string
/** @nullable */
readonly status?: string | null
}
export type BlankEnumApi = (typeof BlankEnumApi)[keyof typeof BlankEnumApi]
export const BlankEnumApi = {
'': '',
} as const
export interface SignalUserAutonomyConfigApi {
readonly id: string
readonly user: _UserApi
autostart_priority?: AutonomyPriorityEnumApi | BlankEnumApi | null
/**
* ID of the Slack Integration to deliver inbox-item notifications through, or null when notifications are disabled.
* @nullable
*/
readonly slack_notification_integration_id: number | null
/**
* Slack channel target in the same `channel_id|#channel-name` shape Insights uses elsewhere (only the channel id is required). Null disables Slack notifications.
* @maxLength 255
* @nullable
*/
slack_notification_channel?: string | null
/** Minimum report priority that triggers a Slack notification. P0 is highest. Null means notify on every priority (and reports without a priority judgment).
*
* * `P0` - P0
* * `P1` - P1
* * `P2` - P2
* * `P3` - P3
* * `P4` - P4 */
slack_notification_min_priority?: AutonomyPriorityEnumApi | BlankEnumApi | null
readonly created_at: string
readonly updated_at: string
}
export type SignalsProcessingListParams = {
/**
* Number of results to return per page.
*/
limit?: number
/**
* The initial index from which to return the results.
*/
offset?: number
}
export type SignalsReportsListParams = {
/**
* Filter reports by whether a shipped implementation pull request exists. 'true' keeps only reports with a PR; 'false' keeps only those without. Pair with limit=1 to count PR reports cheaply.
*/
has_implementation_pr?: boolean
/**
* When true, the list includes reports in every status with no default exclusions applied — currently that adds suppressed (dismissed) reports, which are otherwise hidden. Use it to see the full inbox state (e.g. deduplicating before creating a report) and read each row's status (plus dismissal_reason/dismissal_note on dismissed rows) before acting. Deleted reports are terminal and never returned. Defaults to false, which keeps the existing default exclusions. Ignored when an explicit 'status' filter is set — that filter alone decides which statuses are returned.
*/
include_all_statuses?: boolean
/**
* Number of results to return per page.
*/
limit?: number
/**
* The initial index from which to return the results.
*/
offset?: number
/**
* Comma-separated ordering clauses. Each clause is a field name optionally prefixed with '-' for descending. Allowed fields: status, is_suggested_reviewer, signal_count, total_weight, priority, created_at, updated_at, id. Defaults to '-is_suggested_reviewer,status,-updated_at'.
*/
ordering?: string
/**
* Comma-separated list of priorities to include. Valid values: P0, P1, P2, P3, P4. Reports without a priority assignment are excluded when this filter is set.
*/
priority?: string
/**
* Comma-separated list of scout skill_name slugs (e.g. signals-scout-error-tracking). Reports are kept if at least one of their contributing signals was authored by one of these scouts. Combines with source_product as an AND.
*/
scout?: string
/**
* Scout skill_name prefix (e.g. signals-scout-customer-analytics). Reports are kept if at least one of their contributing signals was authored by a scout whose skill_name starts with this prefix — new scouts in the family match without callers listing every name. Combines with the other filters as an AND.
*/
scout_prefix?: string
/**
* Case-insensitive substring match against report title and summary.
*/
search?: string
/**
* Comma-separated list of source record ids. Reports are kept if at least one of their contributing signals came from one of these records — e.g. pass a support ticket's UUID to see what the inbox already found for that ticket. Requires exactly one source_product, since a source id is only unique within its product.
*/
source_id?: string
/**
* Comma-separated list of source products to include. Reports are kept if at least one of their contributing signals comes from one of these products (e.g. error_tracking, session_replay).
*/
source_product?: string
/**
* Comma-separated list of statuses to include. Valid values: potential, candidate, in_progress, pending_input, ready, resolved, failed, suppressed. Defaults to all statuses except suppressed.
*/
status?: string
/**
* Comma-separated list of Insights user UUIDs. Reports are kept if their suggested reviewers include any of the given users.
*/
suggested_reviewers?: string
/**
* Only reports associated with this task (via the report's task associations).
*/
task_id?: string
}
export type SignalsReportArtefactsListParams = {
/**
* Number of results to return per page.
*/
limit?: number
/**
* The initial index from which to return the results.
*/
offset?: number
}
export type SignalsScoutConfigListParams = {
/**
* Comma-separated tags, e.g. `revenue,on-call`. Returns the scouts carrying at least one of them. Values are normalized the same way stored tags are, so `On Call` matches `on-call`. Omit for the whole fleet.
* @minLength 1
*/
tags?: string
}
export type SignalsScoutMembersListParams = {
/**
* Case-insensitive substring filter over member email and first/last name. Use it to narrow a large project's roster to the owner you're trying to match instead of pulling every member.
* @minLength 1
*/
search?: string
}
export type SignalsScoutNotesListParams = {
/**
* Truncate each note's `content` to the first N characters (a preview). Omit for the full body — use this on wide scans so stacked notes can't dominate your context.
* @minimum 0
*/
content_max_chars?: number
/**
* ISO-8601 inclusive lower bound on `created_at`. Omit to skip the lower bound.
*/
date_from?: string
/**
* ISO-8601 exclusive upper bound on `created_at`. Pass the `created_at` of the oldest note from the prior page to walk back past the result cap.
*/
date_to?: string
/**
* Include notes whose `expires_at` has passed. Off by default so time-boxed steering retires itself.
*/
include_expired?: boolean
/**
* Only meaningful with `skill_name`: when false, exclude the general fleet-wide notes and return the skill's own notes only.
*/
include_general?: boolean
/**
* Max rows to return (default 20, hard cap 500).
* @minimum 1
* @maximum 500
*/
limit?: number
/**
* Return the notes addressed to this scout (`signals-scout-*`) plus the general (blank-target) notes for the whole fleet. Omit to browse every note on the project.
* @minLength 1
*/
skill_name?: string
}
export type SignalsScoutProjectProfileGetParams = {
/**
* When true, skip the cache and rebuild the profile from authoritative sources before responding. Use after seeding events, importing data, or any other change the caller knows just landed but hasn't surfaced through natural cache expiry yet. Honored only for the internal scout token — public read callers get the cached profile regardless. Concurrent forced rebuilds are serialized by the team-keyed advisory lock — at most one extra `build_inventory` per simultaneous request.
*/
force_refresh?: boolean
}
export type SignalsScoutRunsListParams = {
/**
* ISO-8601 inclusive lower bound on `created_at`. Omit to skip the lower bound.
*/
date_from?: string
/**
* ISO-8601 exclusive upper bound on `created_at`. Pass to walk back past the result cap on subsequent calls (cursor-style: set to the `created_at` of the oldest run from the prior page).
*/
date_to?: string
/**
* Filter by emit outcome. `true` returns only runs that emitted at least one finding (`emitted_count > 0`); `false` returns only runs that emitted nothing. Omit for both.
* @nullable
*/
emitted?: boolean | null
/**
* Max rows to return (default 20, hard cap 100).
* @minimum 1
* @maximum 100
*/
limit?: number
/**
* Exact-match filter on the scout skill (e.g. `signals-scout-errors`). Narrows the run dump to a single scout — the primary scoping path when a specialist dedupes against its own past runs. Omit to span every scout on the team.
* @minLength 1
*/
skill_name?: string
/**
* Exact-match filter on the skill version. Pair with `skill_name` to pin one version; omit for all.
* @minimum 1
*/
skill_version?: number
/**
* Case-insensitive substring match on the scout's end-of-run `summary`. Omit to skip the filter.
* @minLength 1
*/
text?: string
}
export type SignalsScoutRunsRecentEmissionsParams = {
/**
* ISO-8601 inclusive lower bound on `emitted_at`. Omit to skip the lower bound.
*/
date_from?: string
/**
* ISO-8601 exclusive upper bound on `emitted_at`. Pass to walk back past the result cap on subsequent calls (cursor-style: set to the `emitted_at` of the oldest emission from the prior page).
*/
date_to?: string
/**
* Max rows to return (default 50, hard cap 200).
* @minimum 1
* @maximum 200
*/
limit?: number
/**
* Exact-match filter on the emitting scout's skill (e.g. `signals-scout-errors`). Narrows to findings one specialist surfaced; omit to span every scout on the team.
* @minLength 1
*/
skill_name?: string
}
export type SignalsScoutRunsFindingsSummaryParams = {
/**
* Lookback window in hours over runs' `created_at` (default 72, hard cap 168).
* @minimum 1
* @maximum 168
*/
window_hours?: number
}
export type SignalsScoutScratchpadSearchParams = {
/**
* Truncate each entry's `content` to the first N characters (a preview). Omit for the full body. Ignored when `keys_only=true`.
* @minimum 0
*/
content_max_chars?: number
/**
* ISO-8601 inclusive lower bound on `updated_at`. Omit to skip the lower bound.
*/
date_from?: string
/**
* ISO-8601 exclusive upper bound on `updated_at`. Pass to walk back past the result cap on subsequent calls (cursor-style: set to the `updated_at` of the oldest entry from the prior page).
*/
date_to?: string
/**
* Exact key match — returns the single entry with this key, or nothing. Use this to re-read a known entry; `text` searches key *and* content, so it can push the row you asked for past the limit.
* @minLength 1
*/
key?: string
/**
* When true, blank each entry's `content` and return only keys + metadata. Use to scan which memories exist without pulling their (potentially large) bodies, then re-query the ones worth a full read. Takes precedence over `content_max_chars`.
*/
keys_only?: boolean
/**
* Max rows to return (default 20, hard cap 1000).
* @minimum 1
* @maximum 1000
*/
limit?: number
/**
* ILIKE substring match against `content`. Omit to return the most recent entries.
*/
text?: string
}
export type SignalsSourceConfigsListParams = {
/**
* Number of results to return per page.
*/
limit?: number
/**
* The initial index from which to return the results.
*/
offset?: number
}