---
title: Presets
description: Outcome-named workflows you can list, read, assemble a prompt from and get suggestions for.
---

# Presets

Presets describe an outcome and the model or Studio lane that makes it. Read the public catalog to choose one, resolve its intake to collect the right inputs, and assemble a prompt before generating. The catalog's time and credit hints help you browse; a current estimate or quote supplies the price for a run.

![Read a preset, assemble its prompt, submit to its model and follow the job](../assets/diagrams/workflow-presets.svg)

<!-- gen:endpoints tag=Presets -->
| Method | Path | What it does |
| --- | --- | --- |
| [GET](../api/#tag/presets/get/presets) | `/presets` | List presets ordered by `sort_order`. Anonymous callers see the public catalog of ROOT presets (children hang under their parent and carry `parent_slug`; each root carries `children_count`); admin accounts see every preset, private (held) ones and children included, so a console can draw the tree. |
| [GET](../api/#tag/presets/get/presets/{slug}) | `/presets/{slug}` | Get a preset's detail, with its family (`parent`, `children`, `inherited`). Anonymous callers read public presets; private presets 404 for anonymous and non-admin callers. |
| [GET](../api/#tag/presets/get/presets/{slug}/intake/context) | `/presets/{slug}/intake/context` | Everything the app needs to render a preset's guided intake flow in one read (signed in). |
| [POST](../api/#tag/presets/post/presets/{slug}/intake/estimate) | `/presets/{slug}/intake/estimate` | Price one run of a preset for the customer's own answers (signed in). |
| [POST](../api/#tag/presets/post/presets/{slug}/assemble) | `/presets/{slug}/assemble` | Assemble a preset's final generation prompt from intake answers. |
| [POST](../api/#tag/presets/post/presets/{slug}/suggestions) | `/presets/{slug}/suggestions` | Ask the AI for three to five directions tailored to a preset and the customer's brief (signed in; 1 credit). |
<!-- /gen -->

## List and read

The public catalog is anonymous. `GET /presets` lists public root presets in ascending `sort_order`, with slug as the tiebreaker. Children belong to a parent's detail instead of appearing as root cards. Admin accounts can also see private presets and children.

| Property | Value |
| --- | --- |
| Catalog | `GET /presets` |
| Detail | `GET /presets/{slug}` |
| Authentication | Optional for public catalog reads |
| Featured filter | `featured=true` for featured only, `false` for non-featured only; omit for both |
| Private preset | Omitted from the public catalog; direct reads return `404` for anonymous and non-admin callers |
| Price hint | `estimated_credits` is human-written; use intake estimates or a cost quote for the actual settings |

The next two commands select a few fields for readability. Their JSON is built from the `Preset` schema and illustrates a catalog entry; it is not a captured catalog response. Choose `PRESET_SLUG` from your own catalog result.

```bash title="example — read the anonymous catalog"
$ curl -sS https://api.nolgia.ai/v1/presets
```

```json title="200 OK — example projection of Preset[]"
[
  {
    "slug": "logo-design",
    "name": "Design a logo",
    "output_type": "image",
    "target": {
      "kind": "create_image",
      "params": {"model": "gpt-image-2.5-flare"}
    },
    "estimated_credits": "See the current intake estimate"
  }
]
```

<!-- gen:fields schema=Preset only=slug,name,output_type,target,estimated_credits -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `slug` | string | Yes | Stable identity of the preset; also the studio intent for agent-driven presets. |
| `name` | string | Yes |  |
| `output_type` | `PresetOutputType` | Yes |  |
| `estimated_credits` | string | Yes | Rough human-written credit hint, e.g. "~300–600 credits". Authoritative per-model costs live on `GET /models`. |
| `target` | `PresetTarget` | Yes |  |
<!-- /gen -->

<!-- gen:fields schema=PresetTarget -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | `PresetTargetKind` | Yes |  |
| `params` | object | Yes | Kind-specific link parameters.… |
<!-- /gen -->

```bash title="example — read one preset's page content"
$ curl -sS "https://api.nolgia.ai/v1/presets/$PRESET_SLUG"
```

```json title="200 OK — example projection of Preset"
{
  "slug": "logo-design",
  "long_description": "Create a logo from your brief and reference images.",
  "use_cases": ["Explore a wordmark for a coffee roaster"],
  "model_ids": ["gpt-image-2.5-flare"],
  "options": [{"label": "Wordmark", "description": "The name set in type, nothing else."}]
}
```

<!-- gen:fields schema=Preset only=slug,long_description,use_cases,model_ids,options,parent,children,inherited -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `slug` | string | Yes | Stable identity of the preset; also the studio intent for agent-driven presets. |
| `options` | array of `PresetOption` | No | Customer-visible pick-one chips surfacing the option menus the preset's guardrails carry (named styles, archetypes, effects, casting, formats). Empty when the preset has no options surface. |
| `long_description` | string | No | Long-form customer copy for the preset's own page (preset pages phase 2): what the preset makes, how it works, what to bring.… |
| `use_cases` | array of string | No | Short customer-facing use cases, one per entry, rendered as a list on the preset page ("Launch teasers for a new drop").… |
| `model_ids` | array of string | No | Machine ids of the models this preset runs on, as published by `GET /models` (e.g.… |
| `parent` | `PresetParentRef`, nullable | No | The parent of a CHILD preset; absent for a root. Emitted on `GET /presets/{slug}` and on the write responses, not on the catalog list. |
| `children` | array of `PresetChildSummary` | No | The directions under this preset ascending by `sort_order`: the "Choose a direction" list a preset page renders above its "describe your own" path.… |
| `inherited` | `PresetInheritance` | No | What a CHILD carries verbatim from its parent, so the page can say "shares the family guardrails". Emitted on `GET /presets/{slug}` for a child; absent for a root. |
<!-- /gen -->

The same detail contains the preset family, authored examples and page content. `/presets/{slug}/page` is a `PATCH` route for authorized page authors; there is no separate `GET` page route. Customers read page content through `GET /presets/{slug}`.

## Intake and assemble

### Read the intake context

| Property | Value |
| --- | --- |
| Endpoint | `GET /presets/{slug}/intake/context` |
| Authentication | Signed-in JWT or PAT |
| Intake | The preset's own flow, or its parent's when it has none |
| Model limits | Current reference slots, supported input kinds and identity capabilities |
| Estimate | One run at the preset's default settings; a Studio intent estimates an agent turn and bills its generated media separately |
| Missing flow | `404` for a missing or inaccessible preset, or one with no intake to resolve |

Use `intake.steps` to build the questions and `model` to enforce supported input choices. The following response is a shortened, schema-built form of the spec's `logo-design` intake example, with one text step to keep the sequence readable.

```bash title="example — resolve the guided intake"
$ curl --fail-with-body -sS "https://api.nolgia.ai/v1/presets/$PRESET_SLUG/intake/context" \
  -H "Authorization: Bearer $NOLGIA_TOKEN"
```

```json title="200 OK — example from PresetIntakeContext"
{
  "slug": "logo-design",
  "intake_from_parent": false,
  "intake": {
    "version": 1,
    "steps": [
      {
        "id": "brand",
        "kind": "text",
        "label": "What is the brand?",
        "required": true,
        "max_chars": 600,
        "binds_to": "prompt_slot:THE BRAND"
      }
    ],
    "submit": {"kind": "generate"}
  },
  "target": {
    "kind": "create_image",
    "model": "gpt-image-2.5-flare",
    "endpoint": "POST /generate/image"
  },
  "options": [],
  "prompt_slots": ["THE BRAND"],
  "assembly": {"doctrine": false, "doctrine_from_parent": false, "hold_credits": 0}
}
```

<!-- gen:fields schema=PresetIntakeContext -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `assembly` | `PresetIntakeContextAssembly` | No |  |
| `slug` | string | Yes |  |
| `intake` | `PresetIntake` | Yes |  |
| `intake_from_parent` | boolean | Yes | `true` when `intake` is the parent's (the preset has none of its own). |
| `target` | `PresetIntakeContextTarget` | Yes |  |
| `model` | `PresetIntakeModelLimits`, nullable | No | The target model's limits; null for a `studio_intent` target or a model the registry does not know. |
| `options` | array of `PresetOption` | Yes | The card's option chips, what `option:<key>` bindings resolve against. |
| `prompt_slots` | array of string | Yes | The labelled slots found in the baked prompt, in order of first appearance (`THE MARK`, `THE STYLE`); empty for a `studio_intent`. |
| `estimate` | `PresetIntakeEstimate`, nullable | No |  |
<!-- /gen -->

<!-- gen:fields schema=PresetIntakeContextTarget -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `members` | array of `OutputSetMemberInput` | No |  |
| `set_kind` | `OutputSetKind` | No |  |
| `kind` | `PresetTargetKind` | Yes |  |
| `model` | string | No | The target model id (`target.params.model`) for a `create_*` target. |
| `intent` | string | No | The studio intent (`target.params.intent`) for a `studio_intent` target. |
| `endpoint` | string | No | The generate endpoint a `generate` submit calls, for display and routing. |
<!-- /gen -->

### Assemble the prompt

| Property | Value |
| --- | --- |
| Endpoint | `POST /presets/{slug}/assemble` |
| Authentication | Signed-in JWT or PAT; paid doctrine assembly also requires an organization library writer |
| Supported targets | `create_image`, `create_video`, `create_audio`, including intakes that submit through an agent |
| Invalid target | Studio-intent targets and targets without a model return `400` |
| Private preset | `404` for non-admin callers |
| Output model | Always the preset target model; assembly never changes it |
| References | Library-scoped image bindings get fresh signed URLs; outside scope is `404`, unavailable resolution is `503` |

Send answers keyed by the actual step ids from the returned intake. `chips` contains published option labels; unknown labels are refused and repeated choices are deduplicated. The example below continues the illustrative one-step intake above.

```bash title="example — assemble without generating media"
$ curl --fail-with-body -sS "https://api.nolgia.ai/v1/presets/$PRESET_SLUG/assemble" \
  -H "Authorization: Bearer $NOLGIA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"answers":{"brand":"Ember, a small coffee roaster: warm, quiet, honest"},"brief":"A simple wordmark on a plain background","count":1}' \
  | tee assembled.json
```

```json title="200 OK — example from AssemblePresetPromptResponse"
{
  "prompt": "THE BRAND: Ember, a small coffee roaster: warm, quiet, honest. A simple wordmark on a plain background",
  "params": {"model": "gpt-image-2.5-flare", "num_images": 1},
  "notes": ["Your answers were placed into the preset's own prompt as written."],
  "mode": "slot_fill",
  "credits_charged": 0,
  "model": "gpt-image-2.5-flare",
  "endpoint": "POST /generate/image"
}
```

<!-- gen:fields schema=AssemblePresetPromptResponse -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `prompt` | string | Yes |  |
| `negative_prompt` | string | No |  |
| `params` | object | Yes | Generate body without prompt and negative_prompt. Its model is always the preset target model. |
| `notes` | array of string | Yes |  |
| `mode` | one of `slot_fill`, `doctrine` | Yes |  |
| `credits_charged` | integer | Yes |  |
| `model` | string | Yes | The preset target model, never the assembly writer model. |
| `endpoint` | string | Yes |  |
| `variants` | array of string | No | Further complete prompts, count minus one, only when doctrine is present and count exceeds one. |
| `writer_model` | string | No |  |
| `doctrine_from_parent` | boolean | No |  |
<!-- /gen -->

<!-- gen:fields schema=AssemblePresetPromptRequest -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `answers` | object | No | Step id to an intake answer (text, string list, or table), using the same loose shapes as nolgia_run_preset. |
| `brief` | string | No |  |
| `chips` | array of string | No | Card option labels; unknown labels are refused and repeated selections are deduplicated. |
| `reference_asset_ids` | array of string | No | Extra library image references, mapped to reference_asset_ids for images or element_asset_ids for video; refused for audio. |
| `count` | integer | No | How many prompts to write (1 to 4, 1 when omitted); more than one needs the preset's prompt doctrine. |
| `project_id` | string | No |  |
<!-- /gen -->

### slot_fill

| Property | Value |
| --- | --- |
| Used when | The preset and its parent have no authored prompt doctrine |
| Behavior | Place answers into the baked prompt and append the brief |
| Cost | Zero credits; no AI call and no assembly rate limit |
| Variations | A `count` above one adds a note; no `variants` are returned |

### doctrine

| Property | Value |
| --- | --- |
| Used when | The preset has non-empty prompt doctrine, or inherits it from its parent |
| Behavior | A writer follows that doctrine, guardrails and model limits to produce the final prompt |
| Cost | Hold one credit, then meter provider cost with a one-credit floor |
| Variations | `count` 2–4 returns `count - 1` further complete prompts in `variants` |
| Refusals | `402` for insufficient credits; shared suggestions rate limit returns `429`, or `503` if unavailable |
| Failure | Provider or validation failure refunds the hold and returns `502` |

> [!NOTE]
> Assembly does not generate media. `credits_charged` is the assembly charge only; the subsequent image, video or audio job has its own price. A doctrine writer's model can differ from the target model, but `params.model` stays fixed to the preset target.

### Generate from the assembled result

For an assembly whose `endpoint` is `POST /generate/image`, merge `prompt` and optional `negative_prompt` into `params`. Keep the returned reference and model settings intact. Choose the corresponding video or audio endpoint when assembly names one of those instead.

```bash title="example — submit an assembled image request"
$ curl -sS https://api.nolgia.ai/v1/generate/image \
  -H "Authorization: Bearer $NOLGIA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"model":"<params.model from the assembled response>","prompt":"<prompt from the assembled response>"}'
```

```json title="202 Accepted — example from Job"
{
  "id": "9b09f1a0-e856-4275-bb83-7a6cc5e304d8",
  "user_id": "75976388-3210-42e6-9b39-bf6290a15d4c",
  "model": "gpt-image-2.5-flare",
  "modality": "image",
  "status": "queued",
  "created_at": "2026-09-21T04:00:00Z",
  "updated_at": "2026-09-21T04:00:00Z"
}
```

<!-- gen:fields schema=Job only=id,user_id,model,modality,status,created_at,updated_at -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | Yes |  |
| `user_id` | string | Yes |  |
| `modality` | `Modality` | Yes |  |
| `model` | string | Yes |  |
| `status` | `JobStatus` | Yes |  |
| `created_at` | string | Yes |  |
| `updated_at` | string | Yes |  |
<!-- /gen -->

Follow that job through [submit and poll](./jobs.html). If the intake's target kind is `set`, use the [set request](./sets.html) instead; the assembly operation documented here supports the three `create_*` targets.

## Suggestions

Ask for directions before choosing a final prompt. This is `POST /presets/{slug}/suggestions`; it is not a catalog read.

| Property | Value |
| --- | --- |
| Authentication | Signed-in caller; private presets return `404` to non-admins |
| Input | Optional `brief` and `count` from 3 to 5, default 4 |
| Output | Validated titles, descriptions, runnable prompts, selected option chips and an optional matching public child slug |
| Rate limit | 20 calls per minute per account; `429` when exceeded |
| Credit hold | One credit before calling the writer; `402` if unavailable |
| Settlement | `ceil(provider cost / $0.018)`, minimum one credit; provider failure refunds the hold |

The writer uses the preset's own description, guardrails, options and children. Invalid suggestions are dropped, so fewer than `count` can be returned. A `child_slug` points to an existing public child when one matches; it is `null` for a new direction. The following is an illustrative schema-built response with one validated direction remaining.

```bash title="example — ask for directions"
$ curl --fail-with-body -sS "https://api.nolgia.ai/v1/presets/$PRESET_SLUG/suggestions" \
  -H "Authorization: Bearer $NOLGIA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"brief":"Ember, a small coffee roaster: warm, quiet, honest","count":3}'
```

The response excerpt omits `model`, which names the suggestion writer, not the generation model the preset selects.

```json title="200 OK — schema-built PresetSuggestionsResponse excerpt"
{
  "suggestions": [
    {
      "title": "Quiet wordmark",
      "description": "A warm, restrained wordmark for the coffee roaster.",
      "prompt": "Design a logo for Ember, a small coffee roaster. Set the word Ember in warm, restrained lettering on a plain background. Keep the letterforms clear at a small size.",
      "options": {},
      "child_slug": null
    }
  ],
  "credits_charged": 1
}
```

<!-- gen:fields schema=PresetSuggestionsResponse -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `suggestions` | array of `PresetSuggestion` | Yes | The validated suggestions, at most `count`.… |
| `credits_charged` | integer | Yes | Credits taken for this call: `ceil(provider cost / $0.018)` with a floor of 1. On the current brain a call is 1 credit. |
| `model` | string | Yes | The brain that wrote the suggestions. |
<!-- /gen -->

<!-- gen:fields schema=PresetSuggestion -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `title` | string | Yes | A short name for the direction. |
| `description` | string | Yes | One or two plain-English sentences on what the customer would get. |
| `prompt` | string | Yes | A complete prompt the customer can run on this preset as it is, written to the preset's guardrails and runnable on its model. |
| `options` | object | Yes | The preset's option chips this direction picks, keyed by the chip's `label` with the chip's published `description` as the value.… |
| `child_slug` | string, nullable | Yes | The existing public child preset that is the best match for this direction, when there is one; `null` when the direction is new. |
<!-- /gen -->

<!-- gen:fields schema=PresetSuggestionsRequest -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `brief` | string | No | What the customer wants, in their own words: the brand, the mood, the occasion, anything. Optional; without it the suggestions are directions the preset itself is good for. |
| `count` | integer | No | How many suggestions to return (3 to 5). |
<!-- /gen -->

The ledger labels this charge `Preset suggestions · {slug}`. Suggestions return prompt options; they do not create a generation job or a finished asset.

## Presets that run in an app on your computer

Film assistant presets (`page_category` `film-assistant`, target kind `desktop_app`) do their work inside a desktop app on your own computer, such as Blender. NOLGIA generates nothing and charges nothing when one runs: the preset carries a written workflow, `instructions`, which your agent follows in the app through the NOLGIA plugin for that app. Anything the workflow makes with NOLGIA along the way, like a 3D model or a background, is priced as usual.

| Property | Value |
| --- | --- |
| Target | `desktop_app`; `target.params` names the `app`, an optional `min_app_version` and `beta` |
| Workflow | `instructions`: the app's shared rules first, then the preset's own steps |
| Starting it | `starter_prompt`: the sentence to paste into your agent |
| Intake and estimate | None: `/presets/{slug}/intake/context` and `/presets/{slug}/intake/estimate` answer `404` |
| Assemble and suggestions | Not available: both answer `400` |

<!-- gen:fields schema=Preset only=instructions,starter_prompt -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `instructions` | string | No | The workflow a `desktop_app` preset hands the agent, markdown: the app's shared rules (every workflow for that app follows them: check the app is connected, read the open document, make a safety copy, keep the work editable, preview every change, say the credit cost before generating, how to finish) followed by this preset's own steps.… |
| `starter_prompt` | string | No | The sentence or two a person pastes into their agent to start this preset, shown on the preset page and used as the MCP prompt text (in Claude Code the preset appears as `/mcp__nolgia__<slug>`).… |
<!-- /gen -->

In an MCP client, every public Film assistant preset is also a prompt; see [Run MCP](./mcp.html#prompts).

## Next steps

:::cards
- [Workflows](./workflows.html): Choose between preset preparation, sets, compositions and edit sessions.
- [Playground](./playground.html): Explore the model and settings behind a preset before integrating it.
:::
