# List Playground runs

```http
GET /api/public/v1/competitions/mathematics-distillation-challenge-equational-theories-stage2/playground/runs
```

**Scope**: `playground.read`.

Returns compact summaries of Stage 2 Runs created by the calling account, ordered by creation time from newest to oldest. Use [Get a Playground Run](./get-playground-run.md) for the full solver and problem snapshots, and [List Playground Run Results](./list-playground-results.md) for per-problem records.

Filters are combined with AND and are applied before cursor pagination. The response does not include a total count.

## Query parameters

| Parameter | Type | Required | Default | Description |
| :--- | :--- | :---: | :--- | :--- |
| `status` | string | No | — | `pending`, `running`, `done`, `failed`, or `cancelled`. |
| `track` | string | No | — | Exact track ID: `solo` or `marathon`. |
| `modelId` | string | No | — | Keep Runs whose snapshotted `allowedModels` contain this exact ID. |
| `problemId` | string | No | — | Keep Runs whose ordered problem snapshot contains this exact ID. |
| `cursor` | string | No | — | Opaque cursor from the previous `nextCursor`. |
| `limit` | integer | No | `25` | Page size from `1` through `100`. |

`modelId` and `problemId` match stored Run snapshots, so an ID can still match after it leaves the live catalog. An unknown ID returns an empty page rather than an error. Follow the shared [cursor traversal rules](../../../pagination.md), and reuse a cursor only with the same filters.

## Response fields

| Field | Type | Description |
| :--- | :--- | :--- |
| `items` | [Stage2RunSummary](#run-summary-fields)[] | Compact Run summaries. |
| `nextCursor` | string \| null | Cursor for the next page, or `null` after the final page. |

### Run summary fields

| Field | Type | Description |
| :--- | :--- | :--- |
| `runId` | string | Stable Run ID. |
| `status` | string | `pending`, `running`, `done`, `failed`, or `cancelled`. |
| `track` | string | Snapshotted `solo` or `marathon` execution protocol. |
| `solverName` | string | Snapshotted solver display name. |
| `problemCount` | integer | Number of selected problem snapshots. |
| `completedProblemCount` | integer | Problems with a terminal outcome recorded so far. |
| `allowedModels` | string[] | Snapshotted public model IDs available to the solver. |
| `summary` | [RunOutcomeSummary](#outcome-summary-fields) | Aggregate per-problem outcome counts. |
| `creditsHeld` | number | Maximum credits reserved when the Run was created. |
| `creditsCharged` | number \| null | Settled charge for a terminal Run; `null` before settlement. |
| `canCancel` | boolean | Whether the service currently confirms that this queued Run can be cancelled. |
| `createdAt` | string | ISO 8601 UTC creation time. |
| `startedAt` | string \| null | Evaluator claim time, or `null` before execution starts. |
| `finishedAt` | string \| null | Terminal time, or `null` while active. |

`canCancel` is authoritative. Do not infer cancellation eligibility from `status`; a `pending` Run can become non-cancellable as the evaluator claims it.

### Outcome summary fields

| Field | Type | Description |
| :--- | :--- | :--- |
| `accepted` | integer | Problems whose proof was accepted. |
| `rejected` | integer | Completed problems whose proof was rejected. |
| `errors` | integer | Problems that ended because solver execution, a model call, or judging failed. |
| `notAttempted` | integer | Marathon problems finalized without an attempt; always `0` for Solo Runs. |

`completedProblemCount` equals the sum of the four outcome fields. A `done` Run has `completedProblemCount == problemCount`; a `failed` or `cancelled` Run can stop with fewer completed problems.

## Example request

```bash
curl "https://api.sair.foundation/api/public/v1/competitions/mathematics-distillation-challenge-equational-theories-stage2/playground/runs?status=done&track=solo&modelId=openai-gpt-oss-120b&limit=25" \
  -H "Authorization: Bearer $SAIR_API_KEY"
```

## Example response

```json
{
  "ok": true,
  "data": {
    "items": [
      {
        "runId": "run_01JSTAGE200000000000000001",
        "status": "done",
        "track": "solo",
        "solverName": "equivalence-prover",
        "problemCount": 2,
        "completedProblemCount": 2,
        "allowedModels": [
          "openai-gpt-oss-120b"
        ],
        "summary": {
          "accepted": 1,
          "rejected": 1,
          "errors": 0,
          "notAttempted": 0
        },
        "creditsHeld": 0.2,
        "creditsCharged": 0.2,
        "canCancel": false,
        "createdAt": "2026-08-30T08:21:30Z",
        "startedAt": "2026-08-30T08:21:32Z",
        "finishedAt": "2026-08-30T08:22:14Z"
      }
    ],
    "nextCursor": null
  }
}
```

An empty result returns `200 OK` with `items: []` and `nextCursor: null`.

## Errors

See [Errors](../../../errors.md).

| HTTP | Code | When |
| :---: | :--- | :--- |
| `400` | `MALFORMED_BODY` | `cursor` or `limit` is invalid. |
| `404` | `NOT_FOUND` | The Stage 2 competition is unavailable. |
| `422` | `RESOURCE_FIELD_INVALID` | `status` or `track` is not one of the documented values. |
