---
title: Playground
description: Try any model in the web app before you integrate it, then reproduce it through the API.
---

# Playground

Use the web app to choose inputs, inspect a result and decide which model belongs in your integration. The same model ids, request fields, jobs and assets are available through the API.

## Try it on nolgia.ai

Open a creation page, choose a model and enter your prompt. The creation pages and Library ask anonymous visitors to sign in.

| Start here | What to try |
| --- | --- |
| [Image](https://nolgia.ai/ai/image) | Generate an image from a prompt or supported references. |
| [Video](https://nolgia.ai/ai/video) | Choose a video model and its supported duration, quality and inputs. |
| [Audio](https://nolgia.ai/ai/audio) | Generate speech, music or sound effects with an audio model. |
| [Presets](https://nolgia.ai/presets) | Start from a prepared generation recipe. |
| [Models](https://nolgia.ai/models) | Browse the model catalog. |
| [Veo 3.1 Lite](https://nolgia.ai/models/veo-3.1-lite) | Inspect the video model used in the Quick Start. |
| [Library](https://nolgia.ai/assets) | Find completed assets and inspect their details. |

For an API integration, start with the same model and prompt, then consult [Common model arguments](./model-arguments.html) for the supported settings. A model name alone does not guarantee support for every size, duration or reference type.

## What the web app shows

The app shows the price before the run, the job's status while it runs, and the asset when it finishes. The price comes from the same `POST /jobs/cost` quote and confirmation token you can use from the API.

| In the web app | API equivalent | What to keep |
| --- | --- | --- |
| Price before generation | `JobCostQuote.credits`, `settings`, `confirmation_token`, `expires_at` | The quoted request and its token until you submit it. |
| Queued or running generation | `Job.status`, `progress`, `status_detail`, `status_message` | The job id so you can follow it later. |
| Finished asset | `Job.asset` | Its asset id; download from its time-limited `signed_url`. |
| The prompt that actually ran | `Asset.enhanced_prompt` | The composed image prompt, when it differs from your original. |

`enhanced_prompt` is an image field. It is absent or null when your words ran unchanged, on video or audio, and on older assets that predate the field; its absence does not mean the job failed.

<!-- gen:fields schema=Asset only=id,prompt,enhanced_prompt,signed_url,expires_at -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | Yes |  |
| `prompt` | string, nullable | No | The prompt as the customer submitted it.… |
| `enhanced_prompt` | string, nullable | No | Image generations only: the server-composed prompt that was actually rendered, present only when it differs from `prompt` (the Aura layer enhanced the prompt, or a character's or element's canonical description was folded in).… |
| `signed_url` | string | Yes | Time-limited GCS signed URL for download.… |
| `expires_at` | string | Yes | Expiry of `signed_url`. |
<!-- /gen -->

![The same quote and confirmation gate used by the web app and API](../assets/diagrams/confirmation-gate.svg)

## The same job underneath

Model generations made in the app are jobs and assets the API can return in the same account or organization context. Read `GET /jobs/{id}` for the generation and `GET /assets/{id}` for the file; there is no separate web-only result to export before an integration can use it.

| Attribution | What it records |
| --- | --- |
| `preset_slug` | The preset that launched the generation, when supplied. |
| `agent_session_id` | The conversation responsible for the job or asset, when present. |
| `X-Nolgia-Surface` | The calling surface's attribution header; the CLI sets its own value. |

These fields explain where a generation came from; they do not change how you wait for its result. See [Platform headers](./headers.html) for surface attribution and [Agent Sessions API](./agent-api.html) for collecting a conversation's media.

## Comparing and sharing

Quote the same inputs on several models before choosing. Each call to `POST /jobs/cost` validates the requested settings and returns their exact credit cost without creating a job or reserving credits.

```bash tab="curl"
$ curl --fail-with-body -sS https://api.nolgia.ai/v1/jobs/cost \
  -H "Authorization: Bearer $NOLGIA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"kind":"image","image":{"model":"flux-pro","prompt":"a paper-cut mountain range at dawn"}}'
$ curl --fail-with-body -sS https://api.nolgia.ai/v1/jobs/cost \
  -H "Authorization: Bearer $NOLGIA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"kind":"image","image":{"model":"gpt-image-2","prompt":"a paper-cut mountain range at dawn"}}'
```

```ts tab="TypeScript"
import { createNolgiaClient } from "@nolgia/sdk";

const nolgia = createNolgiaClient(process.env.NOLGIA_TOKEN!);
for (const model of ["flux-pro", "gpt-image-2"] as const) {
  const { data: quote, error } = await nolgia.POST("/jobs/cost", {
    body: { kind: "image", image: { model, prompt: "a paper-cut mountain range at dawn" } },
  });
  if (error) throw new Error(`${error.title}: ${error.detail ?? ""}`);
  console.log(quote);
}
```

```python tab="Python"
import os
from nolgia import AuthenticatedClient
from nolgia.api.jobs import quote_job_cost
from nolgia.models import GenerateImageRequest, ImageModel, JobCostKind, JobCostQuote, JobCostRequest

client = AuthenticatedClient(base_url="https://api.nolgia.ai/v1", token=os.environ["NOLGIA_TOKEN"])
for model in ("flux-pro", "gpt-image-2"):
    quote = quote_job_cost.sync(client=client, body=JobCostRequest(
        kind=JobCostKind("image"),
        image=GenerateImageRequest(model=ImageModel(model), prompt="a paper-cut mountain range at dawn"),
    ))
    if not isinstance(quote, JobCostQuote):
        raise SystemExit(f"refused: {quote}")
    print(quote.to_dict())
```

This captured `flux-pro` quote shows the response shape; the confirmation token is redacted. Run the calls above for current prices and fresh tokens rather than reusing the captured quote.

```json title="200 OK — captured image quote"
{
  "balance_credits": 2573,
  "basis": "one_generation",
  "confirmation_token": "…",
  "credits": 4,
  "expires_at": "2026-09-21T03:43:14.171728407Z",
  "kind": "image",
  "model": "flux-pro",
  "settings": [
    {
      "label": "Model",
      "value": "flux-pro"
    }
  ],
  "sufficient_credits": true
}
```

<!-- gen:fields schema=JobCostQuote -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | `JobCostKind` | Yes |  |
| `model` | string | Yes | The model id the quote is for, after defaulting — not necessarily the one you sent. |
| `credits` | integer | Yes | Display credits. This is the number the submit will hold, computed by the same pricer the submit path uses, for exactly these settings. It is what to show the customer. |
| `basis` | one of `one_generation`, `set_run` | Yes | Whether `credits` covers one generation or a whole set run. |
| `members` | integer, nullable | No | Number of set members `credits` covers. Present only when `basis` is `set_run`. |
| `duration_seconds` | integer, nullable | No | The billed duration the price is for, on a video quote. |
| `quality` | string, nullable | No | The resolved quality tier the price is for, when the model has one. |
| `settings` | array of `JobCostSetting` | Yes | The settings that made this price, in the order a dialog should show them. Human-readable; do not parse it. |
| `balance_credits` | integer, nullable | No | The wallet balance this quote was compared against, when one could be read. |
| `sufficient_credits` | boolean | Yes | Whether the balance covers `credits` right now. Advisory — the submit re-checks. |
| `confirmation_token` | string | Yes | Short-lived, single-request proof that this price was quoted. Send it back as `confirmation_token` on the matching generate request. Opaque: do not parse or construct it. |
| `expires_at` | string | Yes | After this the token is refused and a submit carrying it fails with `confirmation_rejected`. Re-quote. |
<!-- /gen -->

### Parameters

Set `kind` and the matching request object. Keep that object unchanged when you send its confirmation token to the generation endpoint; changing the model or settings requires another quote.

<!-- gen:fields schema=JobCostRequest -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | `JobCostKind` | Yes |  |
| `image` | `GenerateImageRequest` | No |  |
| `video` | `GenerateVideoRequest` | No |  |
| `audio` | `GenerateAudioRequest` | No |  |
| `three_d` | `Generate3DRequest` | No |  |
| `set` | `GenerateSetRequest` | No |  |
<!-- /gen -->

> [!NOTE]
> A quote does not reserve your balance. Submission re-checks it, and a token that is expired or does not match the request is refused with `422 confirmation_rejected`.

### Error responses

| Status | Code | What to do |
| --- | --- | --- |
| `400` | `validation` | Correct the model or unsupported settings before quoting again. |
| `401` | No generation code is required | Replace the missing, expired or invalid token. |
| `422` on submission with a quote token | `confirmation_rejected` | Quote the final request again and use the fresh token. |

Once you have a result worth sharing, create a share link instead of sending its expiring download URL. [File access controls](../api/#tag/sharing) covers share-link creation and revocation in the API reference.

There is no side-by-side compare view and no "copy this form as code" button; the [Quick Start tabs](./getting-started.html) are the code.

## Next steps

:::cards
- [Model APIs](./models.html): Browse models and published credit prices by modality.
- [Quick Start](./getting-started.html): Run the same workflow in your own language.
:::
