# Playground

> Pick a task, a model and your settings, then run a real API call and see the generated asset immediately.

The playground at [/playground](/playground) issues real requests against the
live API. There is no sandbox: a generation you run there is queued, costs
credits, and appears in your usage and request logs like any other call.

## The layout

The left side is the work: pick **Image**, **Video** or **Edit**, choose a model,
write a prompt, press **Run**. The result appears below it.

The right side is **Settings & code**: aspect, quality or resolution, duration,
sound, reference images, how you authenticate — and a live code panel showing
exactly what your settings mean on the wire.

## Task, not endpoint

You choose what you want to make; the endpoint follows from that plus your auth
mode.

| Task | My session | API key |
| --- | --- | --- |
| Image | `POST /api/generations/image` | `POST /v1/images/generate` |
| Video | `POST /api/generations/video` | `POST /v1/videos/generate` |
| Edit | — | `POST /v1/images/edit` |

> [!NOTE]
> **Edit has no session endpoint**, so selecting it switches you to API key mode
> automatically. The status strip at the bottom always names the exact endpoint
> the current combination resolves to.

## Two ways to authenticate

**My session** is the default. It uses the cookie you are already signed in with,
and the server picks one of your active keys. No secret enters the browser. Use
this to answer *"does generation work at all?"*

**API key** sends `Authorization: Bearer pxsk_…` to `/v1/*` — byte-for-byte what
an external client does. Use this to answer *"does **this** key work, and are its
scopes right?"*

> [!CAUTION]
> Keys are stored as SHA-256 hashes, so a raw secret cannot be listed — it exists
> once, at creation. The panel shows your key prefixes for reference; paste the
> value you saved, or make a new key on the [API page](/api). A pasted secret is
> held in memory for that page only and cleared on reload.

## Controls follow the model

Options are read from the live model config, not hardcoded, so they always match
what the selected model accepts:

- **Aspect** comes from that model's allow-list — some offer 5 ratios, others 15.
- **Quality** or **Resolution** uses the model's own control, including its
  labels. One model calls `1K/2K/4K` "Low / Medium / High"; another exposes only
  `2K/4K` as "2K / 4K".
- **Duration** is the model's supported set of seconds.
- **Credits** are the real per-resolution cost, and for video are multiplied by
  duration and adjusted for sound.

Switching model re-clamps any setting the new one does not support, so the form
can never send a value the API would reject.

Costs are shown in **credits**, not dollars — the API bills in credits and never
returns a currency amount.

## The code panel

Five tabs, all live:

- **request** — the exact JSON body
- **response** — the full body from your last run
- **cURL** — the same call for a terminal
- **CLI** — the equivalent `picx` command
- **MCP** — the same call as an MCP tool invocation

## Async jobs

Video and both session endpoints answer `202` and are polled every two seconds
for up to two minutes. **Stop** ends the polling; it does **not** cancel the job,
which continues server-side and will still consume credits.
