# Run result snapshots

> Read-only attempt payload for a single marking event in a test run.

Each time a case **completes** in a run (case-level mark or per-step mark that leaves the
case no longer `untested`), the API appends a `test_results` row and companion
`test_result_step_snapshots` rows. Frozen step rows point at that result via
`snapshot_id`.

Use this endpoint to render the read-only attempt panel in clients and integrations.

## Get snapshot

`GET /api/v1/runs/{runUlid}/results/{resultUlid}`

Requires `viewer`+ on the run's project. Returns **404** when the run or result is outside
the caller's organization.

### Response

| Field               | Description                                                                                                                                                                                                                                                                                |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `result`            | The append-only log row (`status`, `executedAt`, `executedByUlid`, `executedByApiToken`, `hasSnapshot`, …); see [Who executed it](#who-executed-it)                                                                                                                                        |
| `case`              | Snapshotted case header (`titleSnapshot`, `caseNumber`, pre/post conditions, …)                                                                                                                                                                                                            |
| `steps`             | Frozen per-step rows with `status`, `actualResult` and `actualResultDocument`, `attachments[]`, and `keyword` (the Gherkin keyword, `null` on a classic step)                                                                                                                              |
| `resultAttachments` | The result's current attachments, each with its `stepIndex` (`null` for a result-level file); see [Result attachments](https://docs.probara.net/en/developers/api/result-attachments/)                                                                                                                             |
| `parameters`        | The parameters a CI report sent with the result, `[{ name, value }]` in the order sent; `[]` when none                                                                                                                                                                                     |
| `links`             | The links a CI report sent with the result, `[{ url, name }]` in the order sent (`name` is `null` when none was sent; `url` is always an absolute `http:`/`https:` URL); `[]` when none                                                                                                    |
| `resultSteps`       | The steps a CI report sent with the result, flattened to depth-first pre-order: `{ position, parentPosition, action, status, durationMs, error, expected, data }`, where `position` is the step's index from 0 and `parentPosition` its parent's (`null` at the top level); `[]` when none |

`steps` is `[]` when the case had no steps. `parameters`, `resultSteps` and `links` are returned
independently of `steps`: a result a CI report recorded carries them even when its case has no steps
of its own. `resultSteps` is not `steps`: `steps` are the case's
own steps frozen at mark time, `resultSteps` what the execution reported. Rebuild the tree from
`parentPosition`; a step's `position` is the `stepIndex` its attachments carry.

### Who executed it

`result.executedByUlid` is the member who recorded the result: the signed-in user, or the owner of
the personal API token that wrote it. `result.executedByApiToken` is the API token that wrote it,
`{ ulid, name, app }`: `app` is the CI reporter (`junit`, `playwright`, `jest`, `cypress`, `vitest`, `cucumber`) of an
[app token](https://docs.probara.net/en/developers/authentication/#app-tokens) and `null` for a personal API token. It is
`null` for a result recorded in the app, and once the token is deleted. An app token has no user,
so a result it reported has `executedByUlid: null` and names only the token.

### Example

```bash
curl -sS \
  -H "Authorization: Bearer $PROBARA_API_TOKEN" \
  "https://app.probara.net/api/v1/runs/$RUN/results/$RESULT"
```

```json
{
  "result": {
    "ulid": "01J...",
    "runUlid": "01J...",
    "caseUlid": "01J...",
    "status": "failed",
    "durationMs": 120000,
    "notes": null,
    "executedByUlid": "01J...",
    "executedByApiToken": null,
    "executedAt": 1700000000000,
    "hasSnapshot": true
  },
  "case": {
    "runCaseUlid": "01J...",
    "caseNumber": 12,
    "titleSnapshot": "Checkout",
    "description": null,
    "precondition": null,
    "postcondition": null,
    "priority": "high"
  },
  "steps": [
    {
      "ulid": "01J...",
      "runCaseStepUlid": "01J...",
      "position": 1,
      "action": "Pay",
      "data": null,
      "expected": "Receipt",
      "status": "failed",
      "actualResult": "HTTP 500",
      "attachments": []
    }
  ]
}
```

## Listing results

`GET /api/v1/runs/{runUlid}/results` is unchanged except each item now includes
`hasSnapshot: boolean` and `executedByApiToken` (see [Who executed it](#who-executed-it)). History UIs typically filter to `hasSnapshot: true` for run-wide
lists while per-case disclosures may show every row.
