# Playground API

The Playground API manages project-scoped model comparison canvases. Each Playground contains
multiple Runs; each Run captures model selection, messages, parameters, and execution results.
Executions are dispatched through the AI Gateway using standard provider/model values, so Playground
is decoupled from the local model runtime.

Base URL:

```text
https://api.provon.dev/v1
```

## Authentication

Playground routes are project-qualified. API-key callers need `models:manage` (or `workspace:write`)
for writes and `workspace:read` for reads. Signed-in Workbench users need the corresponding project
permission (`project:data:read` or `project:data:write`).

## Playgrounds

| Method   | Path                                                | Purpose             |
| -------- | --------------------------------------------------- | ------------------- |
| `GET`    | `/v1/projects/:projectId/playgrounds`               | List Playgrounds    |
| `POST`   | `/v1/projects/:projectId/playgrounds`               | Create a Playground |
| `GET`    | `/v1/projects/:projectId/playgrounds/:playgroundId` | Read one Playground |
| `PATCH`  | `/v1/projects/:projectId/playgrounds/:playgroundId` | Update label        |
| `DELETE` | `/v1/projects/:projectId/playgrounds/:playgroundId` | Delete Playground   |

Create a Playground:

```bash
curl -X POST "$PROVON_API_URL/projects/$PROJECT_ID/playgrounds" \
  -H "Authorization: Bearer $PROVON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label": "Compare reasoning models"}'
```

## Runs

| Method   | Path                                                                    | Purpose       |
| -------- | ----------------------------------------------------------------------- | ------------- |
| `POST`   | `/v1/projects/:projectId/playgrounds/:playgroundId/runs`                | Create a Run  |
| `PATCH`  | `/v1/projects/:projectId/playgrounds/:playgroundId/runs/:runId`         | Update a Run  |
| `DELETE` | `/v1/projects/:projectId/playgrounds/:playgroundId/runs/:runId`         | Delete a Run  |
| `POST`   | `/v1/projects/:projectId/playgrounds/:playgroundId/runs/:runId/execute` | Execute a Run |

Create a Run:

```bash
curl -X POST "$PROVON_API_URL/projects/$PROJECT_ID/playgrounds/$PLAYGROUND_ID/runs" \
  -H "Authorization: Bearer $PROVON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "gpt-5-mini tool call",
    "model": "openai/gpt-5-mini",
    "request": {
      "system_prompt": "You are a helpful assistant.",
      "messages": [
        { "role": "user", "content": "What is 2 + 2?" }
      ],
      "params": {
        "temperature": 0.5,
        "max_tokens": 256
      }
    }
  }'
```

Run fields:

| Field     | Type   | Notes                                                               |
| --------- | ------ | ------------------------------------------------------------------- |
| `label`   | string | Optional display name; defaults to "Run".                           |
| `model`   | string | Required. Any Gateway-resolvable model such as `openai/gpt-5-mini`. |
| `request` | object | Required. Contains `system_prompt`, `messages`, `params`.           |

`messages` use `{ "role": "system" | "user" | "assistant", "content": "..." }` objects.
`params` supports `temperature`, `top_p`, `max_tokens`, `frequency_penalty`, `presence_penalty`,
`seed`, and `stop_sequences`.

## Execute A Run

Execute a Run against the Gateway. The response is the raw Gateway response for the requested path
(`/v1/chat/completions`).

```bash
curl -X POST "$PROVON_API_URL/projects/$PROJECT_ID/playgrounds/$PLAYGROUND_ID/runs/$RUN_ID/execute" \
  -H "Authorization: Bearer $PROVON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"stream": false}'
```

Request body:

| Field     | Type    | Notes                                                  |
| --------- | ------- | ------------------------------------------------------ |
| `stream`  | boolean | Optional. Return a streaming SSE response when `true`. |
| `request` | object  | Optional. Override the Run's stored request.           |

When `stream` is `false` and the Gateway returns a JSON response, the result is stored on the Run as
`lastResult` and `lastRunAt` is updated.

## Errors

Playground errors use either the shared API error envelope or a compact `{ error, message }` body:

```json
{
  "error": "not_found",
  "message": "playground \"pg_123\" not found"
}
```

| Status | Meaning                                |
| ------ | -------------------------------------- |
| `400`  | Invalid request body or missing field  |
| `401`  | Missing or invalid credential          |
| `403`  | Credential lacks capability/permission |
| `404`  | Playground or Run not found            |
| `502`  | Gateway execution failed               |
