# Playground

Use Playground endpoints to discover available models and problem sets and to run practice evaluations for a competition, before sending a formal entry through [`POST /api/public/v1/competitions/{competitionId}/submissions`](./competitions.md#submit).

Call Playground endpoints only when the competition's `capabilities.playground` is `true`.

## Playground API guides

Start with the guide for the Playground workflow you are automating:

- [Mathematics Distillation Stage 1](./playground/mathematics-distillation-stage1.md): cheatsheet practice runs.
- [Mathematics Distillation Stage 2](./playground/mathematics-distillation-stage2.md): Lean solver practice runs.
- [Modular Arithmetic Challenge](./playground/modular-arithmetic-challenge.md): model-reference evaluation workflow.

Some Playground workflows use supporting resources with their own CRUD endpoints:

- [Cheatsheets](./cheatsheets.md) store reusable text configurations for `cheatsheet` runs.
- [Solver templates](./solver-templates.md) store reusable Lean 4 solver code and custom problems for `solver-participation` runs.

The run shape is decided by the competition's `submissionSpec.kind`:

| Competition kind                 | Run inputs                                                     | Per-cell result                            |
| :------------------------------- | :------------------------------------------------------------- | :----------------------------------------- |
| `cheatsheet`                     | `models × problems × configurations`, optionally repeated.     | Correct / incorrect against a gold answer. |
| `solver-participation`           | One Lean 4 `solverCode` against a list of problem IDs.         | `proved` / `failed` / `timeout`.           |
| `model-reference`                | One pinned model artifact against one evaluation problem set.  | Per-case prediction and correctness.       |

## List models

```http
GET /api/public/v1/competitions/{competitionId}/playground/models
```

**Scope**: `playground.read`. This endpoint is cursor-paginated.

The model IDs accepted in a run for this competition. The response metadata includes the per-cell credit price for the competition.

Model IDs are opaque strings. Some competitions use OpenRouter-style `provider/name` IDs (e.g. `meta-llama/llama-3.3-70b-instruct`); others use hyphenated IDs (e.g. `google-gemma-4-31b-it`). Treat the value as an opaque token — do not parse it.

### Path parameters

| Parameter       | Type   | Required | Description                 |
| :-------------- | :----- | :------: | :-------------------------- |
| `competitionId` | string | Yes      | Competition slug from [List competitions](./competitions.md#list-competitions). |

### Query parameters

| Parameter | Type    | Required | Description                                      |
| :-------- | :------ | :------: | :----------------------------------------------- |
| `cursor`  | string  | No       | Opaque cursor from the previous `nextCursor`.    |
| `limit`   | integer | No       | Page size. Default `25`, maximum `100`.          |

### Response fields

| Field                    | Type          | Description                                  |
| :----------------------- | :------------ | :------------------------------------------- |
| `items[]`                | [PlaygroundModel](#playground-model-fields)[] | Model records accepted in run requests.      |
| `nextCursor`             | string \| null | Cursor for the next page, or `null`.         |
| `meta`                   | [PlaygroundModelListMeta](#playground-model-list-metadata-fields) | Pricing metadata for run requests.           |

### Playground model fields

| Field  | Type   | Description                       |
| :----- | :----- | :-------------------------------- |
| `id`   | string | Model ID to send in run requests. |
| `name` | string | Human-readable model name.        |

### Playground model list metadata fields

| Field        | Type   | Description                             |
| :----------- | :----- | :-------------------------------------- |
| `creditRate` | number | Credit price per executed cell/problem. |

**Example response**

```json
{
  "ok": true,
  "data": {
    "items": [
      { "id": "meta-llama/llama-3.3-70b-instruct", "name": "Meta: Llama 3.3 70B Instruct" }
    ],
    "nextCursor": null,
    "meta": { "creditRate": 0.001 }
  }
}
```

## List problem sets

```http
GET /api/public/v1/competitions/{competitionId}/playground/problem-sets
```

**Scope**: `playground.read`. This endpoint is cursor-paginated.

Lists the problem sets available for practice runs in this competition.

### Path parameters

| Parameter       | Type   | Required | Description                 |
| :-------------- | :----- | :------: | :-------------------------- |
| `competitionId` | string | Yes      | Competition slug from [List competitions](./competitions.md#list-competitions). |

### Query parameters

| Parameter | Type    | Required | Description                                   |
| :-------- | :------ | :------: | :-------------------------------------------- |
| `cursor`  | string  | No       | Opaque cursor from the previous `nextCursor`. |
| `limit`   | integer | No       | Page size. Default `25`, maximum `100`.       |

### Response fields

| Field             | Type          | Description                          |
| :---------------- | :------------ | :----------------------------------- |
| `items[]`         | [ProblemSetSummary](#problem-set-summary-fields)[] | Problem set summaries.               |
| `nextCursor`      | string \| null | Cursor for the next page, or `null`. |

### Problem set summary fields

| Field   | Type    | Description                    |
| :------ | :------ | :----------------------------- |
| `id`    | string  | Problem set ID.                |
| `title` | string  | Human-readable title.          |
| `count` | integer | Number of problems in the set. |

## Get a problem set

```http
GET /api/public/v1/competitions/{competitionId}/playground/problem-sets/{problemSetId}
```

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

Returns every problem in the set. This endpoint is not paginated.

### Path parameters

| Parameter       | Type   | Required | Description                 |
| :-------------- | :----- | :------: | :-------------------------- |
| `competitionId` | string | Yes      | Competition slug from [List competitions](./competitions.md#list-competitions). |
| `problemSetId`  | string | Yes      | Problem set ID.             |

### Response fields

| Field        | Type   | Description                                      |
| :----------- | :----- | :----------------------------------------------- |
| `id`         | string | Problem set ID.                                  |
| `title`      | string | Human-readable title.                            |
| `items[]`    | [EquationProblem](#equation-problem-fields)[] \| [LeanProblem](#lean-problem-fields)[] | Problem rows. Shape depends on competition kind. |

`cheatsheet` competitions return [EquationProblem](#equation-problem-fields) rows; `solver-participation` competitions return [LeanProblem](#lean-problem-fields) rows. Model-reference competitions do not return problem-set rows here — their per-case structure is surfaced through [run results](#model-reference-result-fields).

### Equation problem fields

For `cheatsheet` competitions, each item carries:

| Field          | Type    | Description                                   |
| :------------- | :------ | :-------------------------------------------- |
| `index`        | integer | Problem index to send in run requests.        |
| `equation1`    | string  | Left equation text.                           |
| `equation2`    | string  | Right equation text.                          |
| `goldAnswer`   | boolean | Expected equivalence answer.                  |

**Example response for a `cheatsheet` competition**

```json
{
  "ok": true,
  "data": {
    "id": "pset_hard3",
    "title": "Hard v3",
    "items": [
      { "index": 0, "equation1": "...", "equation2": "...", "goldAnswer": true }
    ]
  }
}
```

### Lean problem fields

For `solver-participation` competitions, each item carries:

| Field       | Type    | Description                                                   |
| :---------- | :------ | :------------------------------------------------------------ |
| `id`        | string  | Problem ID to send in `problemIds`.                           |
| `lhsName`   | string  | Name of the left-side Lean declaration.                       |
| `lhsText`   | string  | Left-side Lean source.                                        |
| `rhsName`   | string  | Name of the right-side Lean declaration.                      |
| `rhsText`   | string  | Right-side Lean source.                                       |
| `isCustom`  | boolean | `true` for private custom problems created by the key owner.  |

**Example response for a `solver-participation` competition**

```json
{
  "ok": true,
  "data": {
    "id": "pset_lean-easy",
    "title": "Lean — Easy",
    "items": [
      {
        "id": "prob_01HX...",
        "lhsName": "add_zero",
        "lhsText": "theorem add_zero (n : Nat) : n + 0 = n := by ...",
        "rhsName": "add_zero_canonical",
        "rhsText": "theorem add_zero_canonical (n : Nat) : n + 0 = n := by ...",
        "isCustom": false
      }
    ]
  }
}
```

## Submit a run

```http
POST /api/public/v1/competitions/{competitionId}/playground/runs
```

**Scope**: `playground.write`.

Creates a practice run. The request body shape depends on the competition's `submissionSpec.kind`.

### Path parameters

| Parameter       | Type   | Required | Description                 |
| :-------------- | :----- | :------: | :-------------------------- |
| `competitionId` | string | Yes      | Competition slug from [List competitions](./competitions.md#list-competitions). |

### Request fields for `cheatsheet`

| Field                  | Type    | Required | Description                                                                 |
| :--------------------- | :------ | :------: | :-------------------------------------------------------------------------- |
| `models`               | string[] | Yes     | Model IDs from [List models](#list-models).                                 |
| `problems`             | [RunProblem](#run-problem-fields)[] | Yes     | Set references or inline custom problems.                                   |
| `configurations`       | [RunConfiguration](#run-configuration-fields)[] | No      | Conditions to compare. Omit for an unconditioned baseline.                  |
| `repeat`               | integer | No       | Re-runs each cell. Default `1`, maximum `5`.                                |

When `configurations` is present, every item must reference a cheatsheet. To create the unconditioned baseline, omit `configurations` instead of sending `{}`. An explicit empty array is accepted and creates a run with zero equation cells.

### Run problem fields

`RunProblem` is either a problem-set reference or an inline custom problem. Problem-set references must provide both `problemSet` and `index`; inline custom problems must provide only `custom`.

| Field        | Type           | Required | Description                                                            |
| :----------- | :------------- | :------: | :--------------------------------------------------------------------- |
| `problemSet` | string         | Conditional | Problem set ID for a set reference. Required with `index`.          |
| `index`      | integer        | Conditional | Problem index for a set reference. Required with `problemSet`.      |
| `custom`     | [InlineProblem](#inline-problem-fields) | Conditional | Inline custom problem. Cannot be combined with `problemSet` / `index`. |

### Inline problem fields

| Field        | Type    | Required | Description                                     |
| :----------- | :------ | :------: | :---------------------------------------------- |
| `equation1`  | string  | Yes      | Left equation text for an inline custom problem. |
| `equation2`  | string  | Yes      | Right equation text for an inline custom problem. |
| `goldAnswer` | boolean | Yes      | Expected equivalence answer.                    |

### Run configuration fields

| Field        | Type   | Required | Description                         |
| :----------- | :----- | :------: | :---------------------------------- |
| `cheatsheet` | string | Yes      | Cheatsheet ID owned by the key owner. |

**Example request body for `cheatsheet`**

```json
{
  "models": ["meta-llama/llama-3.3-70b-instruct"],
  "problems": [
    { "problemSet": "pset_hard3", "index": 0 },
    {
      "custom": {
        "equation1": "x + y = y + x",
        "equation2": "y + x = x + y",
        "goldAnswer": true
      }
    }
  ],
  "configurations": [{ "cheatsheet": "cs_01HXYZ..." }],
  "repeat": 1
}
```

### Request fields for `solver-participation`

| Field           | Type     | Required | Description                                                                 |
| :-------------- | :------- | :------: | :-------------------------------------------------------------------------- |
| `solverCode`    | string   | Yes      | Lean 4 source. The run stores a copy.                                       |
| `solverName`    | string   | No       | Label shown in the run list.                                                |
| `problemIds`    | string[] | Yes      | Set problem IDs or [custom problem](./solver-templates.md#list-custom-problems) IDs. |
| `allowedModels` | string[] | No       | Restricts the model pool. Defaults to every ID from [List models](#list-models). |

Later edits to a saved [solver template](./solver-templates.md) do not affect an existing run.

**Example request body for `solver-participation`**

```json
{
  "solverCode": "theorem solver : ... := by ...",
  "solverName": "induction-v3",
  "problemIds": ["prob_01HX...", "prob_01HY..."],
  "allowedModels": ["openai-gpt-oss-120b"]
}
```

### Request fields for `model-reference`

| Field          | Type   | Required | Description                                                 |
| :------------- | :----- | :------: | :---------------------------------------------------------- |
| `modelName`    | string | Yes      | Hugging Face model repository in `<owner>/<name>` form.     |
| `commitHash`   | string | Yes      | Full 40-character lowercase hex commit SHA.                 |
| `problemSetId` | string | Yes      | Evaluation problem set ID from [List problem sets](#list-problem-sets). |
| `hfToken`      | string | No       | Hugging Face token for private or gated repositories. Never returned by the API. |
| `note`         | string | No       | Private note shown in run history. Maximum `500` characters. |

**Example request body for `model-reference`**

```json
{
  "modelName": "your-team/your-model",
  "commitHash": "0123456789abcdef0123456789abcdef01234567",
  "problemSetId": "mps_eval_01",
  "hfToken": "hf_...",
  "note": "Candidate revision for hard-tier evaluation."
}
```

### Response fields

| Field    | Type   | Description                                 |
| :------- | :----- | :------------------------------------------ |
| `runId`  | string | Run ID for status, results, and events.     |
| `status` | string | Initial run status. Usually `pending`. The full set of run statuses is `pending`, `running`, `done`, `failed`, `cancelled` (see [Playground run fields](#playground-run-fields)). |

**Example response**

```json
{
  "ok": true,
  "data": {
    "runId": "run_01HXZAB...",
    "status": "pending"
  }
}
```

## List runs

```http
GET /api/public/v1/competitions/{competitionId}/playground/runs
```

**Scope**: `playground.read`. This endpoint is cursor-paginated.

Returns runs created by the key owner for this competition.

### Path parameters

| Parameter       | Type   | Required | Description                 |
| :-------------- | :----- | :------: | :-------------------------- |
| `competitionId` | string | Yes      | Competition slug from [List competitions](./competitions.md#list-competitions). |

### Query parameters

| Parameter    | Type    | Required | Description                                                               |
| :----------- | :------ | :------: | :------------------------------------------------------------------------ |
| `status`     | string  | No       | `pending`, `running`, `done`, `failed`, or `cancelled`.                   |
| `modelId`    | string  | No       | Returns runs that include this model ID.                                  |
| `problemSet` | string | No       | Equation-style competitions only. Returns runs referencing this problem set. |
| `cursor`     | string  | No       | Opaque cursor from the previous `nextCursor`.                             |
| `limit`      | integer | No       | Page size. Default `25`, maximum `100`.                                   |

### Response fields

| Field        | Type          | Description                          |
| :----------- | :------------ | :----------------------------------- |
| `items[]`    | [PlaygroundRun](#playground-run-fields)[] | Run records.                         |
| `nextCursor` | string \| null | Cursor for the next page, or `null`. |

**Example response**

```json
{
  "ok": true,
  "data": {
    "items": [
      {
        "runId": "run_01HXZAB...",
        "status": "done",
        "params": { "...": "..." },
        "summary": { "...": "..." },
        "createdAt": "2026-05-20T09:14:02Z",
        "finishedAt": "2026-05-20T09:16:48Z"
      }
    ],
    "nextCursor": null
  }
}
```

Each item is a [PlaygroundRun](#playground-run-fields); Lean runs also include `verdicts[]`.

## Get a run

```http
GET /api/public/v1/competitions/{competitionId}/playground/runs/{runId}
```

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

The response carries `params` (the original body), `summary`, and timing. For Lean runs it additionally includes `verdicts[]`.

### Path parameters

| Parameter       | Type   | Required | Description                 |
| :-------------- | :----- | :------: | :-------------------------- |
| `competitionId` | string | Yes      | Competition slug from [List competitions](./competitions.md#list-competitions). |
| `runId`         | string | Yes      | Run ID from Submit a run.   |

### Response fields

Returns a [PlaygroundRun](#playground-run-fields).

For Lean competitions, `summary` is `{ "accepted": <int>, "rejected": <int>, "errors": <int> }` and `verdicts[]` is included. Equation-style and model-reference runs omit `verdicts[]`.

### Playground run fields

| Field        | Type          | Description                                                        |
| :----------- | :------------ | :----------------------------------------------------------------- |
| `runId`      | string        | Run ID.                                                            |
| `status`     | string        | `pending`, `running`, `done`, `failed`, or `cancelled`.            |
| `params`     | [EquationRunParams](#equation-run-params-fields) \| [LeanRunParams](#lean-run-params-fields) \| [ModelReferenceRunParams](#model-reference-run-params-fields) | Original run request body. |
| `summary`    | [EquationRunSummary](#equation-run-summary-fields) \| [LeanRunSummary](#lean-run-summary-fields) \| [ModelReferenceRunSummary](#model-reference-run-summary-fields) | Shape depends on competition kind. |
| `verdicts[]` | [LeanVerdict](#lean-verdict-fields)[] | Lean competitions only. Per-problem verdicts.                      |
| `createdAt`  | string        | ISO 8601 UTC timestamp.                                            |
| `finishedAt` | string \| null | ISO 8601 UTC timestamp when terminal, otherwise `null`.            |

### Equation run params fields

| Field            | Type   | Description                                            |
| :--------------- | :----- | :----------------------------------------------------- |
| `models`         | string[] | Model IDs from [List models](#list-models).          |
| `problems`       | [RunProblem](#run-problem-fields)[] | Set references or inline custom problems. |
| `configurations` | [StoredRunConfiguration](#stored-run-configuration-fields)[] | Conditions compared in the run. |
| `repeat`         | integer | Re-runs each cell.                                    |

### Stored run configuration fields

Run responses return stored configurations. A cheatsheet-backed configuration includes `cheatsheet`; an unconditioned baseline created by omitting `configurations` from the request is returned as `{}`.

| Field        | Type   | Required | Description                         |
| :----------- | :----- | :------: | :---------------------------------- |
| `cheatsheet` | string | No       | Cheatsheet ID for a cheatsheet-backed configuration. Missing for the unconditioned baseline. |

### Lean run params fields

| Field           | Type     | Description                          |
| :-------------- | :------- | :----------------------------------- |
| `solverCode`    | string   | Lean 4 source copied into the run.   |
| `solverName`    | string   | Label shown in the run list.         |
| `problemIds`    | string[] | Problem IDs included in the run.     |
| `allowedModels` | string[] | Model IDs allowed for this run.      |

### Model-reference run params fields

| Field          | Type    | Description                                                |
| :------------- | :------ | :--------------------------------------------------------- |
| `modelName`    | string  | Hugging Face model repository.                             |
| `commitHash`   | string  | Full 40-character commit SHA.                              |
| `problemSetId` | string  | Evaluation problem set ID.                                 |
| `hasToken`     | boolean | Whether a Hugging Face token was supplied. Raw tokens are never returned. |
| `note`         | string \| null | Private note, or `null` when omitted.              |

### Equation run summary fields

| Field        | Type   | Description                                   |
| :----------- | :----- | :-------------------------------------------- |
| `groups[]`   | [EquationRunSummaryGroup](#equation-run-summary-group-fields)[] | Aggregate metrics grouped by model and configuration. |
| `creditRate` | number | Credit conversion rate used by the run.       |

### Equation run summary group fields

| Field             | Type    | Description                         |
| :---------------- | :------ | :---------------------------------- |
| `model`           | string  | Model ID.                           |
| `cheatsheet`      | string \| null | Cheatsheet ID for the configuration, or `null` for an unconditioned baseline. |
| `count`           | integer | Result count in this group.         |
| `accuracy`        | number  | Accuracy for this group, from `0` to `1`. |
| `meanCost`        | number  | Mean execution cost before credit conversion. |
| `totalCost`       | number  | Total execution cost before credit conversion. |
| `totalCreditCost` | number  | Total credits charged for this group. |
| `meanElapsed`     | number  | Mean elapsed time in milliseconds.  |

### Lean run summary fields

| Field      | Type    | Description            |
| :--------- | :------ | :--------------------- |
| `accepted` | integer | Accepted proof count.  |
| `rejected` | integer | Rejected proof count.  |
| `errors`   | integer | Error count.           |

### Model-reference run summary fields

| Field                | Type    | Description                                      |
| :------------------- | :------ | :----------------------------------------------- |
| `overallAccuracy`    | number \| null | Overall accuracy after scoring, or `null` before completion. |
| `highestTierAbove90` | integer \| null | Highest tier with at least 90% accuracy, or `null` before completion. |
| `tiers[]`            | [ModelReferenceTierScore](#model-reference-tier-score-fields)[] | Per-tier score breakdown. |
| `deterministic`      | boolean \| null | Whether the evaluated model was deterministic.   |
| `artifactBytes`      | integer \| null | Downloaded artifact size in bytes.               |
| `inferenceTime`      | number \| null | Total model inference time in seconds.           |
| `error`              | string \| null | Failure message when the run failed.             |

### Model-reference tier score fields

| Field       | Type    | Description                         |
| :---------- | :------ | :---------------------------------- |
| `tierId`    | integer | Difficulty tier ID.                 |
| `total`     | integer | Cases in the tier.                  |
| `correct`   | integer | Correct predictions in the tier.    |
| `accuracy`  | number  | Tier accuracy from `0` to `1`.      |
| `completed` | boolean | Whether every tier case was scored. |

### Lean verdict fields

| Field          | Type          | Description                           |
| :------------- | :------------ | :------------------------------------ |
| `problemId`    | string        | Problem ID.                           |
| `verdict`      | string        | `proved`, `failed`, or `timeout`.     |
| `elapsedMs`    | integer       | Elapsed time in milliseconds.         |
| `errorSnippet` | string \| null | Lean parser or judge message, if any. |

**Example response for a cheatsheet run**

```json
{
  "ok": true,
  "data": {
    "runId": "run_01HXZAB...",
    "status": "done",
    "params": { "...": "..." },
    "summary": {
      "groups": [
        {
          "model": "meta-llama/llama-3.3-70b-instruct",
          "cheatsheet": "cs_01HXYZ...",
          "count": 200,
          "accuracy": 0.78,
          "meanCost": 0.0012,
          "totalCost": 0.24,
          "totalCreditCost": 280.5,
          "meanElapsed": 1820
        }
      ],
      "creditRate": 0.001
    },
    "createdAt": "2026-05-20T09:14:02Z",
    "finishedAt": "2026-05-20T09:16:48Z"
  }
}
```

## Cancel a run

```http
POST /api/public/v1/competitions/{competitionId}/playground/runs/{runId}/cancel
```

**Scope**: `playground.write`.

Cancels a pending or running run. Already-terminal runs return `RUN_NOT_CANCELLABLE` (409).

### Path parameters

| Parameter       | Type   | Required | Description                 |
| :-------------- | :----- | :------: | :-------------------------- |
| `competitionId` | string | Yes      | Competition slug from [List competitions](./competitions.md#list-competitions). |
| `runId`         | string | Yes      | Run ID from Submit a run.   |

### Response fields

| Field    | Type   | Description                    |
| :------- | :----- | :----------------------------- |
| `runId`  | string | Run ID.                        |
| `status` | string | Always `cancelled` on success. |

## List results

```http
GET /api/public/v1/competitions/{competitionId}/playground/runs/{runId}/results
```

**Scope**: `playground.read`. This endpoint is cursor-paginated.

Returns one row per executed cell/problem.

### Path parameters

| Parameter       | Type   | Required | Description                 |
| :-------------- | :----- | :------: | :-------------------------- |
| `competitionId` | string | Yes      | Competition slug from [List competitions](./competitions.md#list-competitions). |
| `runId`         | string | Yes      | Run ID from Submit a run.   |

### Query parameters

| Parameter | Type    | Required | Description                                   |
| :-------- | :------ | :------: | :-------------------------------------------- |
| `cursor`  | string  | No       | Opaque cursor from the previous `nextCursor`. |
| `limit`   | integer | No       | Page size. Default `25`, maximum `100`.       |

### Response fields

| Field        | Type          | Description                           |
| :----------- | :------------ | :------------------------------------ |
| `items[]`    | [EquationResult](#equation-result-fields)[] \| [LeanResult](#lean-result-fields)[] \| [ModelReferenceResult](#model-reference-result-fields)[] | Result rows. Shape depends on kind.   |
| `nextCursor` | string \| null | Cursor for the next page, or `null`.  |

### Equation result fields

For equation-style competitions, each row carries:

| Field           | Type           | Description                               |
| :-------------- | :------------- | :---------------------------------------- |
| `resultId`      | string         | Result row ID.                            |
| `modelId`       | string         | Model ID.                                 |
| `problemSet`    | string         | Problem set ID for set references. Inline custom problems are reported as `custom`. |
| `problemIndex`  | integer        | Problem index inside the set. Inline custom problems are reported as `0`. |
| `cheatsheet`    | string \| null | Cheatsheet ID for the configuration.      |
| `repeatIndex`   | integer        | Zero-based repeat index.                  |
| `answer`        | boolean        | Model answer.                             |
| `scoredCorrect` | integer \| null | Correct count for this row.               |
| `scoredTotal`   | integer        | Scored total for this row.                |
| `elapsedMs`     | integer        | Elapsed time in milliseconds.             |
| `output`        | string \| null | Raw model output. `null` when the model produced no response (e.g. pre-flight failure or LLM error). |

**Example response** (one `EquationResult` row inside the paginated envelope)

```json
{
  "ok": true,
  "data": {
    "items": [
      {
        "resultId": "res_01HX...",
        "modelId": "meta-llama/llama-3.3-70b-instruct",
        "problemSet": "pset_hard3",
        "problemIndex": 0,
        "cheatsheet": "cs_01HXYZ...",
        "repeatIndex": 0,
        "answer": true,
        "scoredCorrect": 1,
        "scoredTotal": 1,
        "elapsedMs": 1820,
        "output": "..."
      }
    ],
    "nextCursor": null
  }
}
```

### Lean result fields

For Lean competitions, each row carries:

| Field          | Type          | Description                              |
| :------------- | :------------ | :--------------------------------------- |
| `resultId`     | string        | Result row ID.                           |
| `problemId`    | string        | Problem ID.                              |
| `verdict`      | string        | `proved`, `failed`, or `timeout`.        |
| `elapsedMs`    | integer       | Elapsed time in milliseconds.            |
| `errorSnippet` | string \| null | Lean parser or judge message, if any.    |

### Model-reference result fields

For model-reference competitions, each row carries one evaluated case:

| Field       | Type           | Description                            |
| :---------- | :------------- | :------------------------------------- |
| `resultId`  | string         | Result row ID.                         |
| `caseId`    | string         | Evaluation case ID.                    |
| `tierId`    | integer        | Difficulty tier ID.                    |
| `prediction` | string \| null | Model prediction, or `null` if unavailable. |
| `correct`   | boolean        | Whether the prediction was correct.    |
| `expected`  | string         | Expected canonical answer.             |

## Stream events

```http
GET /api/public/v1/competitions/{competitionId}/playground/runs/{runId}/events
```

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

Available only for `solver-participation` competitions. Returns a Server-Sent Events stream of verdicts as the model finishes each problem. The connection closes when the run reaches a terminal status.

Clients should send `Accept: text/event-stream`; successful responses use `Content-Type: text/event-stream`.

This endpoint does not use the `{ ok, data }` envelope. Events are unnamed SSE `message` events — one `data:` line of compact JSON per [LeanVerdict](#lean-verdict-fields), separated by a blank line, with no `event:` name:

```text
data: {"problemId":"prob_01HX...","verdict":"proved","elapsedMs":1234,"errorSnippet":null}
```

### Path parameters

| Parameter       | Type   | Required | Description                 |
| :-------------- | :----- | :------: | :-------------------------- |
| `competitionId` | string | Yes      | Competition slug from [List competitions](./competitions.md#list-competitions). |
| `runId`         | string | Yes      | Run ID from Submit a run.   |

### Event data fields

| Field          | Type          | Description                           |
| :------------- | :------------ | :------------------------------------ |
| `problemId`    | string        | Problem ID.                           |
| `verdict`      | string        | `proved`, `failed`, or `timeout`.     |
| `elapsedMs`    | integer       | Elapsed time in milliseconds.         |
| `errorSnippet` | string \| null | Lean parser or judge message, if any. |

## Usage

```http
GET /api/public/v1/competitions/{competitionId}/playground/usage
```

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

Your accumulated practice spend on this competition. Usage counters are scoped to this competition; `credits` is the balance snapshot for the corresponding Playground ledger.

### Path parameters

| Parameter       | Type   | Required | Description                 |
| :-------------- | :----- | :------: | :-------------------------- |
| `competitionId` | string | Yes      | Competition slug from [List competitions](./competitions.md#list-competitions). |

### Response fields

| Field               | Type                                      | Description                                      |
| :------------------ | :---------------------------------------- | :----------------------------------------------- |
| `credits`           | [CreditBalance](#credit-balance-fields)  | Credit balance snapshot for this competition ledger. |
| `runs`              | [RunUsage](#run-usage-fields)            | Run counters.                                    |
| `cellsExecuted`     | [UsageCounter](#usage-counter-fields)    | Present for equation-style competitions.         |
| `problemsAttempted` | [UsageCounter](#usage-counter-fields)    | Present for Lean and model-reference competitions. |
| `updatedAt`         | string                                    | ISO 8601 UTC timestamp for the usage snapshot.   |

### Credit balance fields

| Field      | Type   | Description                               |
| :--------- | :----- | :---------------------------------------- |
| `balance`  | number | Remaining credit balance.                 |
| `currency` | string | Currency label. `credit`.                 |
| `rate`     | number | Credit price per executed cell/problem.   |

### Run usage fields

| Field        | Type    | Description                                           |
| :----------- | :------ | :---------------------------------------------------- |
| `total`      | integer | Total run count.                                      |
| `last30Days` | integer | Runs created in the last 30 days.                     |

### Usage counter fields

| Field        | Type    | Description                   |
| :----------- | :------ | :---------------------------- |
| `total`      | integer | Total count.                  |
| `last30Days` | integer | Count from the last 30 days.  |

**Example response for an equation-style competition.** Depending on the competition kind, a usage response carries either `cellsExecuted` or `problemsAttempted` (not both); see the field table above.

```json
{
  "ok": true,
  "data": {
    "credits": { "balance": 124.5, "currency": "credit", "rate": 0.001 },
    "runs": {
      "total": 187,
      "last30Days": 41
    },
    "cellsExecuted": { "total": 74800, "last30Days": 16400 },
    "updatedAt": "2026-05-20T02:14:00Z"
  }
}
```

## Errors

See [Errors](../errors.md). Endpoint-specific:

| Code                             | HTTP | When                                                                           |
| :------------------------------- | :--: | :----------------------------------------------------------------------------- |
| `RUN_PARAMS_INVALID`             | 422  | Unknown model, problem-set / index / problem ID, or cheatsheet.                |
| `RUN_BUDGET_EXCEEDED`            | 403  | Run would exceed your remaining credit balance.                                |
| `RUN_NOT_CANCELLABLE`            | 409  | Run is already terminal.                                                       |
| `LEAN_SOURCE_INVALID`            | 422  | `solverCode` failed the Lean 4 parser. `solver-participation` competitions only. |
| `INVALID_MODEL_REFERENCE`        | 422  | `modelName`, `commitHash`, or `problemSetId` is invalid for a model-reference run. |
| `ACTIVE_RUN_EXISTS`              | 409  | A model-reference run is already queued or running for this account.           |
| `COMPANION_COMPETITION_REQUIRED` | 403  | `solver-participation` competitions are gated to participants enrolled in a companion competition. |
