> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thunderphone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Investigations

> AI-assisted root-cause analysis: generate hypotheses and prompt fixes for recurring call issues, test them, and apply the winner.

An **investigation** takes a cluster of related
[issue reports](/api-reference/issue-reports) on one agent and works
toward a prompt fix: the AI generates **hypotheses** about the root
cause, drafts **prompt candidates** (you can add your own), each
candidate can be tested in an **experiment** (replaying the failing
calls' inputs against the candidate prompt), and the winning candidate
is **applied** to the agent.

Status flow: `idle` → `generating_hypotheses` → `awaiting_candidates`
→ `generating_candidates` → `ready` → `applied` (or `dismissed` at any
point). Generation steps run asynchronously — poll the detail endpoint.

## Endpoints

| Method  | Path                                                                          | Description                                    |
| ------- | ----------------------------------------------------------------------------- | ---------------------------------------------- |
| `GET`   | `/v1/investigations`                                                          | List investigations (`?agent_id=`, `?status=`) |
| `POST`  | `/v1/investigations`                                                          | Start an investigation                         |
| `GET`   | `/v1/investigations/{investigation_id}`                                       | Full detail with candidates + experiments      |
| `PATCH` | `/v1/investigations/{investigation_id}`                                       | Update `experiment_config`, or dismiss         |
| `POST`  | `/v1/investigations/{investigation_id}/hypotheses`                            | Kick off AI hypothesis generation              |
| `POST`  | `/v1/investigations/{investigation_id}/candidates`                            | Kick off AI candidate generation               |
| `POST`  | `/v1/investigations/{investigation_id}/candidates/manual`                     | Add your own candidate prompt                  |
| `POST`  | `/v1/investigations/{investigation_id}/candidates/{candidate_id}/experiments` | Run an experiment for a candidate              |
| `GET`   | `/v1/investigations/{investigation_id}/experiments/{experiment_id}`           | Poll one experiment                            |
| `POST`  | `/v1/investigations/{investigation_id}/apply`                                 | Apply a candidate's prompt to the agent        |

All ids on this page are **UUIDs** except `agent_id` and `call_ids`.

## Investigation object

```json theme={null}
{
  "id": "e7a91c2d-…",
  "issue_pattern_id": "long-pauses-before-answering",
  "issue_trace_ids": ["5f9a…", "77b1…"],
  "agent_id": 12,
  "baseline_prompt": "You are a helpful support agent…",
  "call_ids": [987654321, 987654322],
  "status": "ready",
  "experiment_config": { "sample_size": 5 },
  "hypotheses": [
    { "id": "h1", "title": "Prompt over-constrains responses", "rationale": "…" }
  ],
  "baseline_results": null,
  "candidates": [
    {
      "id": "c3d4…",
      "source": "ai",
      "hypothesis_id": "h1",
      "label": "Loosen response constraints",
      "prompt": "You are a helpful support agent… (revised)",
      "rationale": "…",
      "created_at": "2026-04-20T18:30:00.000Z",
      "latest_experiment_id": "a1b2…"
    }
  ],
  "experiments": [
    {
      "id": "a1b2…",
      "candidate_id": "c3d4…",
      "status": "completed",
      "config": { "sample_size": 5 },
      "samples": [
        {
          "id": "s1…",
          "call_id": 987654321,
          "sample_index": 0,
          "user_input": "…",
          "original_output": "…",
          "replayed_output": "…",
          "grade": { "verdict": "improved" },
          "created_at": "2026-04-20T18:31:00.000Z"
        }
      ],
      "summary": { "improved": 4, "regressed": 0, "unchanged": 1 },
      "created_at": "2026-04-20T18:30:30.000Z",
      "completed_at": "2026-04-20T18:32:10.000Z",
      "error_message": null
    }
  ],
  "applied_candidate_id": null,
  "applied_agent_version": null,
  "error_message": null,
  "created_at": "2026-04-20T18:24:10.113Z",
  "updated_at": "2026-04-20T18:32:10.000Z"
}
```

The **list** endpoint returns `{"results": [...]}` with the same
fields minus the nested `candidates` and `experiments`.

| Field                   | Type             | Description                                                                                                      |
| ----------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------- |
| `id`                    | UUID             | Investigation id                                                                                                 |
| `issue_pattern_id`      | string           | Your identifier for the issue cluster (≤ 255 chars)                                                              |
| `issue_trace_ids`       | array of string  | The issue reports under investigation                                                                            |
| `agent_id`              | integer          | The agent being fixed                                                                                            |
| `baseline_prompt`       | string           | Snapshot of the agent prompt when the investigation started                                                      |
| `call_ids`              | array of integer | Example failing calls used as evidence                                                                           |
| `status`                | string           | `idle`, `generating_hypotheses`, `awaiting_candidates`, `generating_candidates`, `ready`, `applied`, `dismissed` |
| `experiment_config`     | object           | Default config passed to experiments                                                                             |
| `hypotheses`            | array            | AI-generated root-cause hypotheses                                                                               |
| `candidates`            | array            | Prompt candidates — `source` is `ai` or `manual`; `hypothesis_id` links back to a hypothesis                     |
| `experiments`           | array            | All experiments across candidates, newest first. Sample `grade` compares `original_output` vs `replayed_output`  |
| `applied_candidate_id`  | UUID \| null     | Set once applied                                                                                                 |
| `applied_agent_version` | integer \| null  | The [agent version](/api-reference/agents#version-history) revision the apply created                            |

***

## Start an investigation

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.thunderphone.com/v1/investigations \
    -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "issue_pattern_id": "long-pauses-before-answering",
      "issue_trace_ids": ["5f9a…", "77b1…"],
      "agent_id": 12,
      "call_ids": [987654321, 987654322]
    }'
  ```
</CodeGroup>

| Field               | Type             | Required | Description                               |
| ------------------- | ---------------- | -------- | ----------------------------------------- |
| `issue_pattern_id`  | string           | yes      | ≤ 255 chars                               |
| `issue_trace_ids`   | array of string  | yes      | Non-empty                                 |
| `agent_id`          | integer          | yes      | Must belong to your org (`404` otherwise) |
| `call_ids`          | array of integer | yes      | Non-empty                                 |
| `experiment_config` | object           | no       | Defaults to `{}`                          |

Returns `201` with the investigation in `status="idle"`.

## Drive the workflow

1. **`POST …/hypotheses`** — starts async hypothesis generation
   (status → `generating_hypotheses`, then `awaiting_candidates`).
   Returns the investigation immediately; `409` if generation is
   already running or the investigation is in a terminal state.
2. **`POST …/candidates`** — starts async AI candidate generation
   (status → `generating_candidates`, then `ready`). Same `409`
   semantics.
3. **`POST …/candidates/manual`** — add your own candidate at any
   non-terminal point: `{"label", "prompt", "rationale"?}` → `201`
   with the new candidate.
4. **`POST …/candidates/{candidate_id}/experiments`** — body
   `{"config": {…}}` (optional). Replays the failing calls' inputs
   against the candidate prompt asynchronously. Returns `201` with the
   experiment (`status: "running"`); poll
   `GET …/experiments/{experiment_id}` until `completed` / `failed`.
5. **`POST …/apply`** — body `{"candidate_id": "…"}`. Writes the
   candidate's prompt to the agent, records an agent config version,
   sets status `applied`, and auto-resolves the investigation's
   linked issue reports. Allowed from `ready` / `awaiting_candidates`
   (`409` otherwise). Returns the final investigation.

To abandon an investigation:
`PATCH /v1/investigations/{id}` with `{"status": "dismissed"}` —
`dismissed` is the only status a client may set directly.

<Note>
  `apply` stages the fix in the agent's **draft** (your other pending
  draft edits are preserved; the live prompt is untouched until you
  deploy). Pass `"deploy": true` to deploy in the same call, and
  `"with_ack": true` on a tier-upgrade candidate to enable
  acknowledgements at the same time. A `409` means the prompt changed
  since the fix was tested — re-run the investigation against the
  current prompt.
</Note>

## Run the fix search

`POST /v1/investigations/{investigation_id}/search`

Runs the autonomous fix search: the AI drafts diverse prompt changes (and,
for eligible agents, a tier upgrade), screens each one in parallel replays of
the AI turn, culls weak variants, and confirms the best result on fresh
replays plus calls without the issue. The investigation's `search_result`
carries the outcome — `tier` is `fixed` (eliminated in a confirmation round),
`improved` (a confirmed reduction), or `null` when nothing held up, in which
case the cluster is flagged to ThunderPhone with the replay evidence.

***

## Related

<CardGroup cols={2}>
  <Card title="Issue Reports" icon="triangle-exclamation" href="/api-reference/issue-reports">
    The raw material investigations start from.
  </Card>

  <Card title="Test Scenarios" icon="list-check" href="/api-reference/test-scenarios">
    Guard the fix with a promoted regression scenario.
  </Card>

  <Card title="Agents" icon="robot" href="/api-reference/agents">
    Applied fixes appear in the agent's version history.
  </Card>

  <Card title="Calls" icon="phone" href="/api-reference/calls">
    Evidence calls are ordinary call logs.
  </Card>
</CardGroup>
