Signals
For instructions on how to authenticate to use this endpoint, see API overview.
Endpoints
POST | |
DELETE | |
GET | |
GET | |
POST | |
GET | |
GET | |
POST | |
POST | |
GET | |
GET | |
POST | |
POST | |
POST | |
POST | |
POST | |
POST | |
GET | |
GET |
Create signals scout notes
Leave a steering note the scout fleet reads on its next runs. Address it to one scout via skill_name (a configured scout), to one stage of the report pipeline via a reserved audience (pipeline:report-research), or omit it for a general note every scout sees. Each call creates a new note (no upsert); delete retires one. Attributed to the authenticated user.
Required API key scopes
signal_scout:writellm_skill:writeRequest parameters
- contentstring
- skill_namestring
- expires_atstring | null
Response
Example request
POST /api /projects /:project_id /signals /scout /notesExample response
Status 201 The note as created.
Status 400 Invalid note (empty content, oversized, bad skill_name target).
Delete signals scout notes
Delete one note by its id, retiring it from every scout's view. Use this when a note has been acted on or no longer applies; time-boxed notes can instead carry an expires_at and retire themselves.
Required API key scopes
signal_scout:writellm_skill:writePath parameters
- idstring
Example request
DELETE /api /projects /:project_id /signals /scout /notes /:idExample response
Status 204 Note deleted.
Status 404 Note not found for this project.
Retrieve signals scout project profile
Return the team's deterministic project profile. The response opens with a compact summary envelope carrying the emit gate and the inbox report counts, then the full payload. The inventory runs to tens of kilobytes, so a client that truncates a long tool result still keeps the gate. Pass summary_only=true to omit payload entirely. For the internal scout token the response reflects the newest non-expired cached row or a freshly-built one (lazy compute on cache miss); force_refresh=true skips the cache and rebuilds from authoritative sources. Public read callers (session auth or a signal_scout:read PAK) get the newest stored profile whatever its age, with stale=true when it is past expires_at. They never build inline: when the team has no profile, the call starts a background build and returns 404, so retry in a minute. Read this at the start of a run to orient on the team's product mix, integrations, warehouse sources, signal coverage, and existing inbox surface.
Required API key scopes
signal_scout:readQuery parameters
- force_refreshbooleanDefault:
false - run_idstring | null
- summary_onlybooleanDefault:
false
Response
Example request
GET /api /projects /:project_id /signals /scout /project_profile /currentExample response
Status 200 The team's current project profile: freshly built for the internal scout token, or the newest stored profile for other callers, with `stale=true` when it is past `expires_at`.
Status 404 The team has no project profile yet, and the caller is not the internal scout token (which builds on cache miss). A background build starts, so retry in a minute.
Retrieve signals scout rubrics
Required API key scopes
signal_scout:readPath parameters
- idstring
Response
Example request
GET /api /projects /:project_id /signals /scout /rubrics /:idExample response
Status 200
Create signals scout rubrics
Required API key scopes
signal_scout:writePath parameters
- idstring
Request parameters
- contextstringDefault:
Response
Example request
POST /api /projects /:project_id /signals /scout /rubrics /:id /generateExample response
Status 202
List all signals scout runs
Return the most recent SignalScoutRun summaries for this project, newest first. Used by the headless scout to dedupe against work other runs already covered. ILIKE matches on summary. date_from / date_to are a half-open window on created_at (>= date_from, < date_to); pass date_to on subsequent calls to walk past the 100-row cap. Pass emitted=true to see only runs that surfaced at least one finding. Pass skill_name (optionally with skill_version) to scope to a single scout. Results capped at 100.
Required API key scopes
signal_scout:readQuery parameters
- date_fromstring
- date_tostring
- emittedboolean | null
- limitinteger
- skill_namestring
- skill_versioninteger
- textstring
Example request
GET /api /projects /:project_id /signals /scout /runsExample response
Status 200 Recent run summaries newest-first.
Retrieve signals scout runs
Return the full SignalScoutRun row. Status, timing, and error flow from the linked tasks.TaskRun. Strictly team-scoped — a UUID belonging to another team returns 404.
Required API key scopes
signal_scout:readPath parameters
- run_idstring
Response
Example request
GET /api /projects /:project_id /signals /scout /runs /:run_idExample response
Status 200 Full run detail.
Status 404 Run not found or not visible to this project.
Create signals scout record check
Close the follow-up check this run was dispatched to answer. The run note carries the check id and what to establish; this call is the only thing that records the answer, so a run that investigates and says nothing leaves the check unanswered. The verdict lands on the report as a check_result entry people read in the inbox. failed retires the check, passed re-arms a recurring one, and errored retries it. inconclusive with the awaiting_data reason looks again later, and any other reason ends the check. Send the outcome you actually reached rather than the one that closes the loop. A run may close the check it was dispatched for, or a check on its own scout that is due or waiting on a run.
Required API key scopes
signal_scout_internal:writePath parameters
- run_idstring
Request parameters
- check_idstring
- outcome
- reason
- explanationstring
- observed_valuenumber | null
Response
Example request
POST /api /projects /:project_id /signals /scout /runs /:run_id /check-resultExample response
Status 200 Verdict recorded on the report, and the check advanced or retired.
Status 400 The check does not exist for this project, already finished, waits for its report to resolve, is measured by the coordinator rather than a run, runs on another scout, or is neither waiting on a run nor due.
Status 404 Run not found for this project.
Create signals scout edit
Rewrite a report's title/summary, append a note or fresh evidence, set its suggested reviewers, and/or point it at another repository. Can target ANY of the project's inbox reports, not just scout-authored ones — so the edit is attributed to this scout. Reviewers and repository are how you rescue a report that surfaced routed to no one or against the wrong codebase: each replaces what the report holds and re-runs autostart, so a report that was missing a qualifying reviewer or a repository can open a draft PR. The response carries the repository the report holds after the edit, and the call fails when a repository it named did not land. Set actionability and/or priority (each with its explanation) when new evidence changed your judgment: each replaces the report's decision and re-runs autostart, without changing the report's inbox status. Title/summary edits are best-effort: the pipeline may later re-research them. Set supersedes_implementation alongside a rewrite when the fix changed. Verified automated predecessor PRs close only after the replacement completes with verified open PRs.
Required API key scopes
signal_scout_report:writePath parameters
- run_idstring
Request parameters
- expected_reviewers
- reviewer_outcomeDefault:
selected - reviewer_retry_reasonstring
- report_idstring
- titlestring | null
- summarystring | null
- append_notestring | null
- corroboration_onlyboolean
- append_evidencearray | null
- suggested_reviewersarray
- repositorystring | null
- chartsarray | null
- metricsarray | null
- suggested_promptsarray | null
- linksarray
- supersedes_implementationboolean
- actionability
- actionability_explanationstring | null
- already_addressedboolean | null
- priority
- priority_explanationstring | null
Response
Example request
POST /api /projects /:project_id /signals /scout /runs /:run_id /edit-reportExample response
Status 200 Report edited.
Status 400 Nothing to edit, empty note, or report not found for this project.
Status 404 Run not found for this project.
Retrieve signals scout runs
Return the findings a SignalScoutRun emitted to the inbox, newest first — one row per emit with its description (the finding text as surfaced), severity, and the deterministic source_id that joins back to the underlying signal. Lets a team and its agents see what a run surfaced without parsing emitted_finding_ids or scanning the signal store. Strictly team-scoped — a run UUID belonging to another team returns 404.
Required API key scopes
signal_scout:readPath parameters
- run_idstring
Example request
GET /api /projects /:project_id /signals /scout /runs /:run_id /emissionsExample response
Status 200 Findings this run emitted to the inbox, newest first.
Status 404 Run not found or not visible to this project.
Retrieve signals scout runs emission
Best-effort reverse of the report -> signals link. For each finding the run emitted, resolve the inbox SignalReport (if any) its underlying signal grouped into by walking the deterministic source_id back through the signal store. report is null when the finding hasn't grouped into a report yet, was de-duplicated away, or its signal was deleted. Lets the scout UI surface which inbox report a finding contributed to — the reverse of the report's evidence list. Strictly team-scoped — a run UUID belonging to another team returns 404.
Required API key scopes
signal_scout:readtask:readPath parameters
- run_idstring
Example request
GET /api /projects /:project_id /signals /scout /runs /:run_id /emissions /reportsExample response
Status 200 Per-finding inbox report links for this run, newest finding first.
Status 404 Run not found or not visible to this project.
Create signals scout emit
The second emit channel: author a complete SignalReport directly instead of emitting a weak signal. The report passes the safety judge, then surfaces at the status the scout's actionability call implies (or is suppressed). Backing evidence is written as bound signals so the report behaves like a pipeline report. Safe to retry: resending an emission returns the report the first call authored (idempotent_replay true) rather than a second one, keyed on idempotency_key or, without one, on the report's content. Use reports to find a report from an earlier run and edit-report to update it instead of authoring a near-duplicate.
Required API key scopes
signal_scout_report:writePath parameters
- run_idstring
Request parameters
- titlestring
- summarystring
- evidencearray
- actionability_explanationstring
- actionability
- already_addressedbooleanDefault:
false - repositorystring | null
- priority
- priority_explanationstring | null
- suggested_reviewersarray
- chartsarray
- metricsarray
- suggested_promptsarray
- linksarray
- idempotency_keystring | null
Response
Example request
POST /api /projects /:project_id /signals /scout /runs /:run_id /emit-reportExample response
Status 200 Report authored (READY/PENDING_INPUT/suppressed), or skipped by a preflight gate.
Status 400 Invalid report shape (empty title/summary/evidence, bad actionability).
Status 404 Run not found for this project.
Create signals scout emit
Fire emit_signal with source_product = signals_scout. The finding_id is baked into the deterministic Signal.source_id = run:<id>:finding:<id> for traceability, but this is NOT idempotent — a second call with the same finding_id emits a second signal, so do not retry an emit that may have already succeeded.
Required API key scopes
signal_scout_internal:writePath parameters
- run_idstring
Request parameters
- descriptionstring
- evidencearray
- hypothesisstring | null
- severity
- dedupe_keysarray
- tagsarray
- time_range
- mcp_trace_idstring | null
- finding_idstring | null
Response
Example request
POST /api /projects /:project_id /signals /scout /runs /:run_id /emit-signalExample response
Status 200 Finding emitted, or skipped by a preflight gate.
Status 400 Invalid emit shape (description, evidence cap).
Status 404 Run not found for this project.
Create signals scout lighthouse
Load one page in a real browser and return what makes it slow — most usefully the element the browser chose as the Largest Contentful Paint, and where the LCP time went. Field data says a route is slow; this says which element and why, so a finding can name it instead of guessing from source. Restricted to public PostHog pages: the browser signs in to nothing, so a page behind a login would report the login screen's numbers. One throttled cold load is not a p75 over real users — corroborate a field finding with it, never replace one. Capped at 5 audits per run.
Required API key scopes
signal_scout_internal:writePath parameters
- run_idstring
Request parameters
- urlstring
- form_factorDefault:
desktop
Response
Example request
POST /api /projects /:project_id /signals /scout /runs /:run_id /lighthouse-auditExample response
Status 200 The audit ran and the reduced report is attached.
Status 400 The url is not on an allowed host, is not https, redirected off the allowed hosts (the login-wall case), the run is not in progress, or the run has spent its audit budget. Also returned when the page could not be loaded at all. Every message ends with how many audits the run has left, so a rejection is distinguishable from an exhausted budget.
Status 404 No such run in this project, or the run belongs to a different scout.
Status 429 Audit rate limit exceeded; retry later.
Status 501 Lighthouse audits are not configured on this deployment.
Status 503 The browser fleet is at capacity, so no audit ran and the run keeps the slot. Retry later.
Create signals scout record
The structured-output channel: record schema-validated records this run produced. Opt-in via the scout config's structured_output_schema (a JSON Schema describing one record) — without it the call fails closed, as it does for a dry-run scout (emit off). All-or-nothing: any invalid record fails the whole call with nothing written, so fix and resubmit the batch. Each accepted record lands in the project's event stream as a $scout_structured_output event — query them like any event (insights, SQL over events). Recording is idempotent: event ids are deterministic, so resubmitting an identical batch (e.g. retrying after a 503) cannot double-count.
Required API key scopes
signal_scout_internal:writePath parameters
- run_idstring
Request parameters
- recordsarray
Response
Example request
POST /api /projects /:project_id /signals /scout /runs /:run_id /record-outputExample response
Status 200 All records validated against the configured schema and recorded as events.
Status 400 No structured_output_schema configured for this scout, the channel is off (dry-run scout, org AI-processing consent missing, or the signals_scout source disabled), a record failed schema validation (nothing written), or a batch/size cap was exceeded.
Status 404 Run not found for this project.
Status 503 The records validated but event delivery failed; nothing was recorded. Retry the same call — delivery is idempotent, so a retry cannot double-count.
Create signals scout report check
Stop a check that is no longer worth running — the claim it re-measures has changed, or a better check replaces it. Results it already recorded stay on the report. A check that has already finished cannot be cancelled.
Required API key scopes
signal_scout_report:writePath parameters
- run_idstring
Request parameters
- check_idstring
Response
Example request
POST /api /projects /:project_id /signals /scout /runs /:run_id /report-check-cancelExample response
Status 200 Check cancelled.
Status 400 The check does not exist for this project, or already finished and cannot be cancelled.
Status 404 Run not found for this project.
Create signals scout report check
Schedule a re-measurement of a report's claim, so whether the fix held becomes a stored fact instead of something a future run has to remember to look for. A metric_threshold check runs one bounded Trends query and compares the result. An agent check runs a scout instead, for a claim no single number settles.
Required API key scopes
signal_scout_report:writePath parameters
- run_idstring
Request parameters
- titlestring
- rationalestring
- kind
- config
- next_run_atstring
- run_interval_minutesinteger | null
- runs_remaininginteger
- expires_atstring
- report_idstring
Response
Example request
POST /api /projects /:project_id /signals /scout /runs /:run_id /report-check-createExample response
Status 200 Check written on the report.
Status 400 The report does not exist for this project, the config does not match the kind, the named metric is not on the report, or the report is already at its check limit.
Status 404 Run not found for this project.
List all signals scout report checks
Every check on one report, newest first. Read this before writing one: a report already carrying a check for the same claim needs no second one, and a report holds at most five open checks at a time.
Required API key scopes
signal_scout_report:writePath parameters
- run_idstring
Query parameters
- report_idstring
Example request
GET /api /projects /:project_id /signals /scout /runs /:run_id /report-checksExample response
Status 200 The report's checks, newest first.
Status 400 The report does not exist for this project.
Status 404 Run not found for this project.
Retrieve signals scout runs
Return what every scout on this project spent on model calls over the last window_days, with how many runs it started, how many of those had spend attributed, and how many inbox reports it filed or added to. Cost per day, per run, and per report are derived from those numbers by the caller, so the endpoint stays a fact table and the definitions live in one place. Spend is summed from the $ai_generation events the runs' sandboxes produced and joined to the run rows by task run id, because a team-authored scout's generations all carry the same stage tag and so cannot name it. Cached per project for 15 minutes: the window's trailing edge moves and the newest runs may still be settling, so this is a roughly current number, not a live one. available is false where the internal AI observability project holding those events can't be read, so an unknown spend never reads as zero. Staff-only, same gate as the per-run cost read. Strictly team-scoped.
Required API key scopes
signal_scout:readQuery parameters
- window_daysinteger
Response
Example request
GET /api /projects /:project_id /signals /scout /runs /costs










