---
title: "Upload Detect Run"
method: POST
path: "/api/v1/detect-runs"
---

# Upload Detect Run

`POST /api/v1/detect-runs`

## Request

- Base URL: `https://api.stacktrace.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.stacktrace.ai/api/v1/detect-runs`
- Auth: none declared

Accept one detect run, for one agent (ADR-0025).

A sibling of `/boms` rather than a field on it: runtime reporting must not
be gated on composition sync, or an agent that skipped a BOM upload reports
no alerts at all — and silence would be indistinguishable from safety.

Always 201. Every accepted run creates a row, including a byte-identical
replay, because a repeated collection event that leaves no trace makes
freshness lie (ADR-0012's property, applied here).

## Headers

- `authorization` string, nullable

## Request body

- DetectRunUpload: One accepted detect run, for one agent. The agent is the unit, exactly as it is for BOMs (ADR-0018, ADR-0022): the collector splits its run output by agent kind before uploading, the same split the scanner already performs to emit one BOM per agent.
  - `asset_id` string, required
  - `agent_kind` string, required
  - `agent_instance_id` string, nullable
  - `stacktrace_version` string, required
  - `window_start` string, date-time, required
  - `window_end` string, date-time, required
  - `detections` DetectionPayload[]
    - `rule_id` string, required
    - `scope` 'call' | 'session', required
    - `finding_targets` FindingTargetPayload[]
      - `schema_version` 1
      - `kind` 'credential' | 'package' | 'stored_credential' | 'deleted_folder' | 'switched_credential' | 'spend_limit' | 'safety_check' | 'rate_limit' | 'access_denied' | 'personal_data', required
      - `fingerprint` string
      - `masked_label` string
      - `credential_kind` string
      - `ecosystem` string
      - `package_name` string
      - `version` string
      - `span` string
      - `folder` string
      - `service` string
      - `status` string
      - `account` string
      - `session_id` string
      - `previous_label` string
      - `reasons` FindingReasonPayload[]
        - `id` string, required
        - `description` string, required
        - `severity` string
        - `fixed_in` string, nullable
    - `severity` 'low' | 'medium' | 'high', required
    - `confidence` 'low' | 'medium' | 'high', required
    - `session` SessionRefPayload, required: Enough to find the session on the machine that produced it, and to say what it was and whose it was. Upstream's `SessionRef` minus `agent_kind`, which the request already carries. `title` is the run's short title and `person` the Claude account's display name or the part of its email before the `@` (`stacktrace` ADR-0109, ADR-0079). Both are left out when the client does not know them. No field here holds a prompt, an argument, a result or a path.
      - `session_id` string, required
      - `surface` 'chatgpt-work', nullable
      - `started_at` string, date-time, nullable
      - `turn_count` integer
      - `title` string, nullable
      - `opening_words` string, nullable
      - `person` string, nullable
      - `account` string, nullable
    - `observed_capabilities` string[]
    - `components` ComponentPayloadRef[]
      - `identity` string, nullable
      - `name` string, required
      - `version` string, nullable
      - `component_type` string, nullable
    - `subject` ParticipantPayload: One named party to a finding, labelled by how well it is known. Distinct from `ComponentPayloadRef` on purpose, and the distinction is the whole point of the field. A component reference asserts *this is installed and here is its identity*; a participant asserts only *this is what the finding was about*, which for three of the four kinds is something Fleet holds no inventory for at all -- the agent's own built-in tool, a server absent from every composition, an unplaced skill. **`identity` is null for every kind except `component`.** A name is not an identity, so an `unplaced` or `builtin` participant must never resolve a lookup or render as a link: two machines can call unrelated servers `support`. The validator below enforces that rather than trusting a producer to, because the alternative failure is silent -- an unverified name displayed among verified ones reads as inventory, which is worse than the blank it replaced.
      - `kind` string, required
      - `role` string
      - `name` string, required
      - `component_type` string, nullable
      - `identity` string, nullable
    - `involved` ParticipantPayload[]
      - `kind` string, required
      - `role` string
      - `name` string, required
      - `component_type` string, nullable
      - `identity` string, nullable
    - `target` TargetPayload: What one remediation fixes: the credential a finding is about. `key` groups findings and is never shown. `label` is shown and never groups, because two credentials can share it.
      - `key` string, required
      - `label` string, required
    - `evidence` EvidencePayload[]
      - `span` string, required
      - `kind` string, required
      - `detail` string
      - `turn_occurred_at` string, date-time, nullable
    - `occurred_start` string, date-time, nullable
    - `occurred_end` string, date-time, nullable
    - `action` 'blocked' | 'allowed' | 'observed', nullable
    - `decided_by` DecidedByPayload: What decided a gateway finding: the client release whose rules ran, and the organization policy revision it held, if any.
      - `rules` string, required
      - `policy_revision` integer, nullable
    - `where` 'hook' | 'gateway' | 'scan', nullable
  - `unknowns` UnknownPayload[]
    - `session_id` string, required
    - `reason` string, required
    - `spans` string[]
  - `activity` ActivityPayload[]
    - `session_id` string, required
    - `session_started_at` string, date-time, nullable
    - `category` 'mcp' | 'skill' | 'subagent' | 'command' | 'tool' | 'unresolved', required
    - `component_identity` string, nullable
    - `component_name` string, nullable
    - `component_type` string, nullable
    - `invocations` integer, required
    - `denied` integer
    - `last_invoked_at` string, date-time, nullable
  - `accounts` AccountCardPayload[]
    - `account` string, required
    - `person_key` string, nullable
    - `display_name` string, nullable
    - `name_from` 'account' | 'email', nullable
    - `plan` string, nullable
    - `seen_at` string, date-time, required

## Response `201`

Successful Response

- DetectRunResponse
  - `detect_run_id` string, required
  - `agent_id` string, required
  - `detections_ingested` integer, required
  - `detections_new` integer, required
  - `activity_rows` integer, required
  - `dashboard_url` string, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/stacktrace/apis/stacktrace-fleet-api.md) - [All operations](https://skmtc.dev/stacktrace/apis/stacktrace-fleet-api/llms.txt) - [OpenAPI document](https://skmtc.dev/stacktrace/apis/stacktrace-fleet-api/revisions/0ee5484d1d0b?raw)
