---
title: "Issue Reports"
description: "Org-wide feed of issue reports filed against calls."
---

Issue reports flag specific calls for quality review. They are created
on a per-call basis via
[`POST /v1/calls/{call_id}/issue-reports`](/api-reference/calls#file-a-report)
and can also be filed automatically by [call grading](/api-reference/calls#ai-call-grading)
when it detects an issue pattern. This page documents the **org-wide feed**
— use it to build dashboards and route issues to your QA workflow.

## Endpoints

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/issue-reports` | List issue reports across all calls (paginated) |
| `PATCH` | `/v1/issue-reports` | Bulk-update status by `trace_id` |
| `PATCH` | `/v1/issue-reports/{trace_id}` | Update one report's status |

## Issue report object

```json
{
  "id": 4321,
  "trace_id": "5f9a...",
  "call_id": 987654321,
  "agent_id": 12,
  "source": "user",
  "severity": "warning",
  "status": "open",
  "title": "Agent paused too long",
  "description": "Five-second silence before responding to the main question.",
  "what_went_wrong": "",
  "suggested_fix": "",
  "timestamp_seconds": 48,
  "failing_turn_index": null,
  "detected_issue_ref": null,
  "metadata": {},
  "reported_by_id": 7,
  "linked_test_cases": [],
  "regression_guarded": false,
  "created_at": "2026-04-20T18:25:11.002Z",
  "updated_at": "2026-04-20T18:25:11.002Z"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Issue report id |
| `trace_id` | UUID | Stable id for external linking — also the key for status updates |
| `call_id` | integer | Parent call |
| `agent_id` | integer \| null | Agent that handled the parent call |
| `source` | string | `user` (manually filed from the UI / API) or `system` (automatically filed — e.g. by call grading) |
| `severity` | string | `info`, `warning`, or `critical` |
| `status` | string | `open`, `acknowledged`, or `resolved` |
| `title` | string | Short description, 1–255 chars |
| `description` | string | Free-form body |
| `what_went_wrong` | string | Grader analysis of the failure (system reports) |
| `suggested_fix` | string | Grader-suggested remediation (system reports) |
| `timestamp_seconds` | integer \| null | Offset in seconds into the call the issue refers to |
| `failing_turn_index` | integer \| null | Transcript turn index where the issue manifested — used by [investigations](/api-reference/investigations) to replay from the right point |
| `detected_issue_ref` | string \| null | Free-form id set by grading to correlate recurring patterns |
| `metadata` | object | Free-form bag for your own app data |
| `reported_by_id` | integer \| null | User id of the reporter. `null` when created by the system |
| `linked_test_cases` | array | [Test scenarios](/api-reference/test-scenarios) created from this issue: `{id, agent_id, title, source, latest_verdict, created_at}` |
| `regression_guarded` | boolean | True when the issue has linked scenarios and every one currently passes |
| `created_at`, `updated_at` | timestamp | ISO 8601 UTC |

---

## List issue reports

<CodeGroup>
```bash cURL
curl 'https://api.thunderphone.com/v1/issue-reports?severity=critical' \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
```

```python Python
issues = requests.get(
    "https://api.thunderphone.com/v1/issue-reports",
    headers={"Authorization": "Bearer sk_live_YOUR_API_KEY"},
    params={"severity": "critical"},
).json()
```
</CodeGroup>

### Query parameters

| Param | Type | Description |
|-------|------|-------------|
| `severity` | string | Filter: `info`, `warning`, `critical` |
| `status` | string | Filter: `open`, `acknowledged`, `resolved` |
| `call_id` | integer | Filter to a single call |
| `agent_id` | integer | Filter to reports on calls handled by this agent |
| `start_date` | ISO 8601 | Earliest `created_at` |
| `end_date` | ISO 8601 | Latest `created_at` |
| `limit` | integer | Page size (default 100, capped at 500) |
| `offset` | integer | |

Returns `200 OK` with a paginated envelope of
[Issue report objects](#issue-report-object), ordered by severity
(`critical` → `warning` → `info`) and then newest first:

```json
{
  "results": [ /* Issue report objects */ ],
  "total": 42,
  "limit": 100,
  "offset": 0
}
```

---

## Update a report's status

Reports move through `open` → `acknowledged` → `resolved` (any
transition is allowed). Both update endpoints accept only `status`.

### Single report

<CodeGroup>
```bash cURL
curl -X PATCH https://api.thunderphone.com/v1/issue-reports/5f9a... \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "resolved"}'
```
</CodeGroup>

The path key is the report's **`trace_id`** (not the integer `id`).
Returns `200 OK` with the updated
[Issue report object](#issue-report-object), or `404` if no report
with that `trace_id` exists in your org.

### Bulk

<CodeGroup>
```bash cURL
curl -X PATCH https://api.thunderphone.com/v1/issue-reports \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "trace_ids": ["5f9a...", "77b1..."],
    "status": "acknowledged"
  }'
```
</CodeGroup>

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `trace_ids` | array of UUID | yes | 1–500 ids |
| `status` | string | yes | `open`, `acknowledged`, or `resolved` |

```json Response
{ "updated": 2 }
```

Unknown `trace_ids` are skipped silently — `updated` counts the rows
actually changed.

---

## Issue clusters

Reports that share a failure mechanism are grouped into **clusters** — the
rows on the dashboard's Issues page. Each cluster carries a name, a
failure-mode description, a fix lane, aggregate counts, and (when one exists)
a summary of its latest investigation.

### List issue clusters

`GET /v1/issue-clusters`

Returns `{ "results": [...], "total": n, "limit": n, "offset": n }`. Each
cluster includes `fix_lane` (`prompt`, `audio`, `runtime`, `model`, `config`,
`infra`, `other`), `severity`, `call_count`, `report_count`,
`investigation_summary`, and `flagged_at` — when the cluster was flagged to
ThunderPhone (platform-side lanes are flagged automatically; so are prompt
issues whose investigation ended without a confirmed fix).

### Update a cluster

`PATCH /v1/issue-clusters/{cluster_id}`

Accepts `status` (`open`, `acknowledged`, `resolved`) and/or `fix_lane` with
`fix_lane_source`. Setting a status cascades to the cluster's reports;
reclassifying onto a platform-side lane flags the cluster to ThunderPhone
automatically.

### Escalate a cluster

`POST /v1/issue-clusters/{cluster_id}/escalate`

Body: `{ "note": "..." }`. Sends the cluster to ThunderPhone with a full
evidence bundle (reports, evidence values, recent investigation results).
Escalating again appends your note to the open escalation. Emits the
`issue.escalated` webhook.

---

## Related

<CardGroup cols={2}>
  <Card title="Call issue reports" icon="triangle-exclamation" href="/api-reference/calls#issue-reports">
    File a report on a specific call.
  </Card>
  <Card title="Call grading" icon="chart-line" href="/api-reference/calls#ai-call-grading">
    AI-detected issues automatically produce reports here.
  </Card>
</CardGroup>
