Signals

For instructions on how to authenticate to use this endpoint, see API overview.

Endpoints

GET
POST
PATCH
DELETE
POST
POST
GET
GET
GET
POST
DELETE
GET
GET
GET
POST
GET
GET
POST
POST
POST

List all signals scout config

List the per-(team, skill) scout configs for this project. Each row includes its schedule (rolling run_interval_minutes, or a project-local run_cron_schedule when set), enabled, and emit posture. A freshly authored scout skill appears here once its config is registered, either explicitly via create or by the coordinator's next tick.

Required API key scopes

signal_scout:read

Example request

GET /api/projects/:project_id/signals/scout/configs
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/configs/

Example response

Status 200 Per-scout configs for this project, ordered by skill name.
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"skill_name": "string",
"description": "string",
"scout_origin": "canonical",
"enabled": true,
"emit": true,
"run_interval_minutes": 30,
"run_cron_schedule": "string",
"output_destinations": {
"slack": {
"integration_id": 1,
"channel": "string"
}
},
"last_run_at": "2019-08-24T14:15:22Z",
"created_at": "2019-08-24T14:15:22Z"
}

Create signals scout config

Register the config for a signals-scout-* skill immediately, without waiting for the coordinator to auto-register it. The same call can optionally set run_interval_minutes, a cron run_cron_schedule, enabled, emit, and output destinations. The skill must already exist on this project. Upsert: if a config already exists for the skill, the provided fields are applied to it.

Required API key scopes

signal_scout:write

Request parameters

  • enabled
    boolean
  • emit
    boolean
  • run_interval_minutes
    integer
  • output_destinations
  • run_cron_schedule
    stringnull
  • skill_name
    string

Response


Example request

POST /api/projects/:project_id/signals/scout/configs
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/configs/\
-d skill_name="string"

Example response

Status 200 A config already existed for this skill; the provided fields were applied to it.
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"skill_name": "string",
"description": "string",
"scout_origin": "canonical",
"enabled": true,
"emit": true,
"run_interval_minutes": 30,
"run_cron_schedule": "string",
"output_destinations": {
"slack": {
"integration_id": 1,
"channel": "string"
}
},
"last_run_at": "2019-08-24T14:15:22Z",
"created_at": "2019-08-24T14:15:22Z"
}
Status 201 Created config.
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"skill_name": "string",
"description": "string",
"scout_origin": "canonical",
"enabled": true,
"emit": true,
"run_interval_minutes": 30,
"run_cron_schedule": "string",
"output_destinations": {
"slack": {
"integration_id": 1,
"channel": "string"
}
},
"last_run_at": "2019-08-24T14:15:22Z",
"created_at": "2019-08-24T14:15:22Z"
}
Status 400 No such skill on this project, the name lacks the `signals-scout-` prefix, or the project is already at its enabled-scouts maximum.

Update signals scout config

Tune one scout: change its schedule (rolling run_interval_minutes, or a cron run_cron_schedule that takes precedence when set), enabled, or emit (dry-run) posture, or output destinations. skill_name is fixed. Enabling records enabled_by and is activity-logged since it drives spend.

Required API key scopes

signal_scout:write

Path parameters

  • id
    string

Request parameters

  • enabled
    boolean
  • emit
    boolean
  • run_interval_minutes
    integer
  • run_cron_schedule
    stringnull
  • output_destinations

Response


Example request

PATCH /api/projects/:project_id/signals/scout/configs/:id
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl -X PATCH \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/configs/:id/\
-d enabled="boolean"

Example response

Status 200 Updated config.
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"skill_name": "string",
"description": "string",
"scout_origin": "canonical",
"enabled": true,
"emit": true,
"run_interval_minutes": 30,
"run_cron_schedule": "string",
"output_destinations": {
"slack": {
"integration_id": 1,
"channel": "string"
}
},
"last_run_at": "2019-08-24T14:15:22Z",
"created_at": "2019-08-24T14:15:22Z"
}
Status 400 Invalid fields, or enabling would exceed the project's enabled-scouts maximum.
Status 404 Config not found for this project.

Delete signals scout config

Delete one scout config by its id, removing the per-(team, skill) schedule/emit row outright. The point is cleaning up an orphaned config whose signals-scout-* skill was archived or deleted — it lingers in list with an empty description, never runs (the coordinator skips it and the skill can't load), but can't otherwise be removed over the API. Deletion is activity-logged. Note: if the skill still exists, the coordinator re-creates a default-schedule config on its next tick — to retire a live scout, archive its skill (or set enabled=false to make it inert) rather than deleting the config.

Required API key scopes

signal_scout:write

Path parameters

  • id
    string

Example request

DELETE /api/projects/:project_id/signals/scout/configs/:id
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl -X DELETE \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/configs/:id/

Example response

Status 204 Config deleted.
Status 404 Config not found for this project.

Create signals scout config

Dispatch one on-demand run of this scout immediately, regardless of its schedule. Useful to test a scout right after authoring it, or to refresh its findings on demand. The run executes asynchronously on the worker and inherits every guard the scheduled path has: it is forbidden if scouts are not enabled for the project (403), and skipped if the project is over its Signals credits quota or daily run budget (429) or a run for this scout is already in progress (409). A manual run counts against the same daily run budget as scheduled runs, so repeated manual runs of the same scout can exhaust the project's daily allowance. A manual run does not change the scout's schedule or last_run_at. A disabled scout can still be run this way (to test before enabling). Returns immediately with the workflow id — poll the scout's runs for the result.

Required API key scopes

signal_scout:write

Path parameters

  • id
    string

Response


Example request

POST /api/projects/:project_id/signals/scout/configs/:id/run
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/configs/:id/run/

Example response

Status 202 A run was dispatched. It executes asynchronously; poll the scout's runs for the result.
RESPONSE
{
"skill_name": "string",
"workflow_id": "string",
"started": true
}
Status 403 Signals scouts are not enabled for this project.
Status 404 Config not found for this project (or the scout is withheld).
Status 409 A run for this scout is already in progress.
Status 429 The project is over its Signals credits quota or daily scout run budget; try again later.

Create signals scout config

Materialize the scout fleet for this project on demand (idempotent): seed the canonical signals-scout-* skills, create a default-schedule config for any scout lacking one, and return all scout configs. Normally the Temporal coordinator does this on its next tick; this action exists so setup flows (e.g. the wizard's self-driving program) can hand the user a tunable fleet immediately.

Required API key scopes

signal_scout:write

Example request

POST /api/projects/:project_id/signals/scout/configs/sync
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/configs/sync/

Example response

Status 200 The team's full scout fleet after the sync, ordered by skill name.
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"skill_name": "string",
"description": "string",
"scout_origin": "canonical",
"enabled": true,
"emit": true,
"run_interval_minutes": 30,
"run_cron_schedule": "string",
"output_destinations": {
"slack": {
"integration_id": 1,
"channel": "string"
}
},
"last_run_at": "2019-08-24T14:15:22Z",
"created_at": "2019-08-24T14:15:22Z"
}

List all signals scout members

Return the people who can review work on this project — one row per member with access to it, each with their user_uuid, email, first_name/last_name, and resolved GitHub login (null when they have no linked GitHub identity). The cold-start reviewer-routing path: when a finding's owner can't be read off a fetched entity's created_by and there's no cached reviewer:<area> memory or inbox precedent, list members, match the owner by email/name, then put their resolved github_login in suggested_reviewers on emit-report / edit-report. Pass search to narrow a large roster; the result is capped at 200. Strictly team-scoped.

Required API key scopes

signal_scout_internal:read

Query parameters

  • search
    string

Example request

GET /api/projects/:project_id/signals/scout/members
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/members/

Example response

Status 200 The project's members, each with their routing identity.
RESPONSE
{
"user_uuid": "string",
"email": "user@example.com",
"first_name": "string",
"last_name": "string",
"github_login": "string"
}

Retrieve signals scout metadata

Return the project's scout metadata: whether it is enrolled, the current announcement banner (e.g. an alpha run-limit notice, or null when unset), and the enforced run limits with current usage. Limits reflect what the coordinator actually applies at dispatch, so a user can see the real throttle rather than what they assume they set. All values come from the signals-scout flag payload, so the banner and caps can change with no deploy.

Required API key scopes

signal_scout:read

Response


Example request

GET /api/projects/:project_id/signals/scout/metadata/current
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/metadata/current/

Example response

Status 200 This project's scout enrollment, announcement banner, and enforced run limits.
RESPONSE
{
"enrolled": true,
"banner_message": "string",
"limits": {
"max_runs_per_tick": 0,
"max_runs_per_day": 0,
"runs_today": 0,
"runs_remaining_today": 0
}
}

List all signals scout notes

Return the steering notes left for this project's scouts, newest first. Pass skill_name to get the notes addressed to one scout plus the general (blank-target) fleet-wide notes — the shape a scout run reads at cold start. Omit skill_name to browse every note. Expired notes are excluded unless include_expired=true. date_from / date_to are a half-open window on created_at (>= date_from, < date_to); pass date_to (the created_at of the oldest note seen) to walk past the cap. Results capped at 500.

Required API key scopes

signal_scout:read

Query parameters

  • content_max_chars
    integer
  • date_from
    string
  • date_to
    string
  • include_expired
    boolean
    Default: false
  • include_general
    boolean
    Default: true
  • limit
    integer
  • skill_name
    string

Example request

GET /api/projects/:project_id/signals/scout/notes
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/notes/

Example response

Status 200 Matching notes newest-first.
RESPONSE
{
"id": "string",
"skill_name": "string",
"content": "string",
"created_at": "string",
"expires_at": "string",
"created_by_name": "string",
"origin": "string"
}

Create signals scout notes

Leave a steering note the scout fleet reads on its next runs. Address it to one scout via skill_name (signals-scout-*), 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:write

Request parameters

  • content
    string
  • skill_name
    string
  • expires_at
    stringnull

Response


Example request

POST /api/projects/:project_id/signals/scout/notes
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/notes/\
-d content="string"

Example response

Status 201 The note as created.
RESPONSE
{
"id": "string",
"skill_name": "string",
"content": "string",
"created_at": "string",
"expires_at": "string",
"created_by_name": "string",
"origin": "string"
}
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:write

Path parameters

  • id
    string

Example request

DELETE /api/projects/:project_id/signals/scout/notes/:id
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl -X DELETE \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/notes/:id/

Example 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. 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 cached profile, or 404 if none has been built yet — they never trigger a rebuild. 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:read

Query parameters

  • force_refresh
    boolean
    Default: false

Response


Example request

GET /api/projects/:project_id/signals/scout/project_profile/current
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/project_profile/current/

Example response

Status 200 The team's current project profile (cached, or freshly built for the internal scout token).
RESPONSE
{
"profile_id": "string",
"computed_at": "string",
"expires_at": "string",
"source_version": "string",
"payload": {
"inventory": {
"project_context": {
"product_description": "string",
"app_urls": [
"string"
]
},
"products_in_use": [
"string"
],
"product_intents": [
{
"product_type": "string",
"activated_at": "string",
"created_at": "string"
}
],
"integrations": [
{
"kind": "string",
"created_at": "string"
}
],
"external_data_sources": [
{
"source_type": "string",
"status": "string",
"prefix": "string",
"created_at": "string",
"last_run_at": "string",
"latest_error": "string"
}
],
"signal_source_configs": {
"enabled": [
{
"source_product": "string",
"source_type": "string"
}
],
"disabled": [
{
"source_product": "string",
"source_type": "string"
}
]
},
"emit_eligibility": {
"ai_processing_approved": true,
"source_enabled": true,
"can_emit": true,
"remediation": "string"
},
"scout_fleet": {
"enabled": [
{
"skill_name": "string",
"run_interval_minutes": 0,
"run_cron_schedule": "string",
"emit": true,
"last_run_at": "string",
"last_emitted_at": "string",
"not_running_reason": "string"
}
],
"disabled": [
{
"skill_name": "string",
"run_interval_minutes": 0,
"run_cron_schedule": "string",
"emit": true,
"last_run_at": "string",
"last_emitted_at": "string",
"not_running_reason": "string"
}
],
"emitted_lookback_days": 0
},
"existing_inbox_reports": {
"total": 0,
"by_status": [
{
"status": "string",
"count": 0
}
]
},
"recent_activity": {
"window_days": 0,
"by_scope": [
{
"scope": "string",
"edits": 0,
"users": 0,
"last_edit": "string"
}
]
},
"recent_reviewer_corrections": {
"window_days": 0,
"corrections": [
{
"report_id": "string",
"report_title": "string",
"before": [
"string"
],
"after": [
"string"
],
"at": "string"
}
]
},
"recent_dashboards": [
{
"id": 0,
"name": "string",
"last_accessed_at": "string",
"last_refresh": "string",
"created_at": "string"
}
],
"recent_surveys": {
"total_count": 0,
"active_count": 0,
"recent": [
{
"id": "string",
"name": "string",
"type": "string",
"status": "string",
"updated_at": "string"
}
]
},
"recent_feature_flags": {
"total_count": 0,
"active_count": 0,
"recent": [
{
"id": 0,
"key": "string",
"name": "string",
"active": true,
"updated_at": "string"
}
]
},
"recent_experiments": {
"total_count": 0,
"running_count": 0,
"recent": [
{
"id": 0,
"name": "string",
"status": "string",
"feature_flag_key": "string",
"updated_at": "string"
}
]
},
"recent_alerts": {
"total_count": 0,
"enabled_count": 0,
"recent": [
{
"id": "string",
"name": "string",
"enabled": true,
"state": "string",
"calculation_interval": "string",
"insight_id": 0,
"created_at": "string"
}
]
},
"recent_hog_functions": {
"total_count": 0,
"enabled_count": 0,
"recent": [
{
"id": "string",
"name": "string",
"type": "string",
"kind": "string",
"enabled": true,
"updated_at": "string"
}
]
},
"recent_hog_flows": {
"total_count": 0,
"active_count": 0,
"recent": [
{
"id": "string",
"name": "string",
"status": "string",
"updated_at": "string"
}
]
},
"recent_notebooks": {
"total_count": 0,
"recent": [
{
"short_id": "string",
"title": "string",
"last_modified_at": "string"
}
]
},
"recent_cohorts": {
"total_count": 0,
"recent": [
{
"id": 0,
"name": "string",
"is_static": true,
"count": 0,
"created_at": "string"
}
]
},
"recent_actions": {
"total_count": 0,
"recent": [
{
"id": 0,
"name": "string",
"updated_at": "string"
}
]
},
"top_events": [
{
"window_days": 0,
"event": "string",
"count": 0,
"distinct_users": 0,
"recent_24h_count": 0,
"recent_24h_users": 0,
"first_seen_in_window": "string",
"last_seen_in_window": "string"
}
]
}
}
}
Status 404 No profile has been built for this team yet, and the caller is not the internal scout token (which builds on cache miss). Public read callers never trigger a build.

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:read

Query parameters

  • date_from
    string
  • date_to
    string
  • emitted
    booleannull
  • limit
    integer
  • skill_name
    string
  • skill_version
    integer
  • text
    string

Example request

GET /api/projects/:project_id/signals/scout/runs
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/runs/

Example response

Status 200 Recent run summaries newest-first.
RESPONSE
{
"run_id": "string",
"skill_name": "string",
"skill_version": 0,
"status": "not_started",
"created_at": "string",
"started_at": "string",
"completed_at": "string",
"task_id": "string",
"task_run_id": "string",
"task_url": "string",
"summary": "string",
"error": "string",
"failure_reason": "string",
"emitted_count": 0,
"emitted_finding_ids": [
"string"
],
"emitted_report_ids": [
"string"
],
"edited_report_ids": [
"string"
],
"metadata": {
"property1": "string",
"property2": "string"
}
}

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:read

Path parameters

  • run_id
    string

Response


Example request

GET /api/projects/:project_id/signals/scout/runs/:run_id
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/runs/:run_id/

Example response

Status 200 Full run detail.
RESPONSE
{
"run_id": "string",
"skill_name": "string",
"skill_version": 0,
"status": "not_started",
"created_at": "string",
"started_at": "string",
"completed_at": "string",
"task_id": "string",
"task_run_id": "string",
"task_url": "string",
"summary": "string",
"error": "string",
"failure_reason": "string",
"emitted_count": 0,
"emitted_finding_ids": [
"string"
],
"emitted_report_ids": [
"string"
],
"edited_report_ids": [
"string"
],
"metadata": {
"property1": "string",
"property2": "string"
}
}
Status 404 Run not found or not visible to this project.

Create signals scout edit

Rewrite a report's title/summary, append a note, and/or set its suggested reviewers. Can target ANY of the project's inbox reports, not just scout-authored ones — so the edit is attributed to this scout. Setting reviewers is how you rescue a report that surfaced routed to no one: it replaces the reviewer list and re-runs autostart, so a report missing a qualifying reviewer can open a draft PR. Title/summary edits are best-effort: the pipeline may later re-research them.

Required API key scopes

signal_scout_report:write

Path parameters

  • run_id
    string

Request parameters

  • report_id
    string
  • title
    stringnull
  • summary
    stringnull
  • append_note
    stringnull
  • suggested_reviewers
    Click to open
    array

Response


Example request

POST /api/projects/:project_id/signals/scout/runs/:run_id/edit-report
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/runs/:run_id/edit-report/\
-d report_id="string"

Example response

Status 200 Report edited.
RESPONSE
{
"report_id": "string",
"updated_fields": [
"string"
],
"note_appended": true,
"reviewers_set": true
}
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), weight, confidence, 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:read

Path parameters

  • run_id
    string

Example request

GET /api/projects/:project_id/signals/scout/runs/:run_id/emissions
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/runs/:run_id/emissions/

Example response

Status 200 Findings this run emitted to the inbox, newest first.
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"run_id": "string",
"finding_id": "string",
"description": "string",
"weight": 1,
"confidence": 1,
"severity": "P0",
"tags": [
"string"
],
"source_id": "string",
"emitted_at": "2019-08-24T14:15:22Z"
}
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:read

Path parameters

  • run_id
    string

Example request

GET /api/projects/:project_id/signals/scout/runs/:run_id/emissions/reports
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/runs/:run_id/emissions/reports/

Example response

Status 200 Per-finding inbox report links for this run, newest finding first.
RESPONSE
{
"finding_id": "string",
"source_id": "string",
"report": {
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"title": "string",
"status": "string"
}
}
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. NOT idempotent — a retry authors a second report; use reports to find a prior report and edit-report to update it instead.

Required API key scopes

signal_scout_report:write

Path parameters

  • run_id
    string

Request parameters

  • title
    string
  • summary
    string
  • evidence
    Click to open
    array
  • actionability_explanation
    string
  • actionability
  • already_addressed
    boolean
    Default: false
  • repository
    stringnull
  • priority
  • priority_explanation
    stringnull
  • suggested_reviewers
    Click to open
    array

Response


Example request

POST /api/projects/:project_id/signals/scout/runs/:run_id/emit-report
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/runs/:run_id/emit-report/\
-d title="string",\
-d summary="string",\
-d evidence="array",\
-d actionability_explanation="string",\
-d actionability=undefined

Example response

Status 200 Report authored (READY/PENDING_INPUT/suppressed), or skipped by a preflight gate.
RESPONSE
{
"report_id": "string",
"report_status": "string",
"emitted": true,
"skipped_reason": "string",
"safety_explanation": "string",
"remediation": "string"
}
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:write

Path parameters

  • run_id
    string

Request parameters

  • description
    string
  • confidence
    number
  • evidence
    Click to open
    array
  • hypothesis
    stringnull
  • severity
  • dedupe_keys
    array
  • tags
    array
  • time_range
  • mcp_trace_id
    stringnull
  • finding_id
    stringnull

Response


Example request

POST /api/projects/:project_id/signals/scout/runs/:run_id/emit-signal
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/runs/:run_id/emit-signal/\
-d description="string",\
-d confidence="number",\
-d evidence="array"

Example response

Status 200 Finding emitted, or skipped by a preflight gate.
RESPONSE
{
"finding_id": "string",
"emitted": true,
"skipped_reason": "string",
"remediation": "string"
}
Status 400 Invalid emit shape (description, weight, confidence, evidence cap).
Status 404 Run not found for this project.

Create signals scout runs emissions

Batched form of the per-run emissions endpoint: return the findings every requested SignalScoutRun emitted, flattened newest-first, in a single request. Each row carries its run_id, so the caller can regroup by run. The findings UI uses this to load the whole recent window in one round-trip instead of one request per run. Strictly team-scoped — run ids belonging to another team contribute no rows (no per-run 404; one stale id never fails the batch).

Required API key scopes

signal_scout:read

Request parameters

  • run_ids
    array

Example request

POST /api/projects/:project_id/signals/scout/runs/emissions/batch
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/signals/scout/runs/emissions/batch/\
-d run_ids="array"

Example response

Status 200 Findings emitted across all requested runs, newest first.
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"run_id": "string",
"finding_id": "string",
"description": "string",
"weight": 1,
"confidence": 1,
"severity": "P0",
"tags": [
"string"
],
"source_id": "string",
"emitted_at": "2019-08-24T14:15:22Z"
}

Community questions