# Presets

> Read your saved node presets, preset folders and the built-in preset catalog over REST, apply a preset to a node, and manage preset favorites.

Source: https://nodaro.ai/docs/developers/api/presets

The **Presets API** reads the node presets you saved in the editor and the built-in preset catalog of each node type. A preset is a named node configuration, such as a model, a prompt, an aspect ratio and a quality, that you apply to a node in one step. For API tokens the routes are read-only: you create and edit presets in the editor.

The routes work on every edition. They take a bearer token: a personal API token (`ndr_…`), an OAuth app token with the `presets:read` scope, or your session token. See [Authentication](https://nodaro.ai/docs/developers/api/authentication) and [Presets](https://nodaro.ai/docs/concepts/presets) for what presets do in the editor.

## Endpoints

| Method | Path | What it does |
| --- | --- | --- |
| `GET` | `/v1/node-presets` | Your custom presets, newest first. Optional `nodeType` filter. |
| `GET` | `/v1/node-preset-groups` | Your preset folders and sections. Optional `nodeType` filter. |
| `GET` | `/v1/node-presets/factory` | The built-in catalog of one node type. `nodeType` is required. |
| `GET` | `/v1/node-presets/favorites` | The preset ids you starred for one node type. `nodeType` is required. |
| `POST` | `/v1/node-presets/favorites` | Star a preset. Browser session only. |
| `DELETE` | `/v1/node-presets/favorites` | Remove a star. Browser session only. |

## Read your presets

`GET /v1/node-presets` returns `{ data: NodePreset[] }`, newest first. Pass `nodeType`, for example `generate-image`, to list the presets of one node type.

**curl**

```bash
curl "https://app.nodaro.ai/v1/node-presets?nodeType=generate-image" \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

**TypeScript SDK**

```ts

const client = createClient({
baseUrl: 'https://app.nodaro.ai',
auth: new StaticTokenAuth(process.env.NODARO_API_KEY!),
})

const presets = await client.presets.list('generate-image')
const cinematic = presets.find((p) => p.name === 'Cinematic Portrait')
const groups = await client.presets.listGroups('generate-image')
```

```json
{
"data": [
{
"id": "7e2c4a9f-1b3d-4f6a-8c5e-2d9b7a1f3c4e",
"nodeType": "generate-image",
"name": "Cinematic Portrait",
"description": "Warm key light, shallow depth of field",
"data": {
"provider": "nano-banana-pro",
"aspectRatio": "3:4",
"resolution": "2K",
"prompt": "cinematic portrait, warm key light, 85mm, shallow depth of field"
},
"groupId": null,
"tags": ["portrait"],
"sortOrder": 0,
"createdAt": "2026-09-14T08:12:40Z",
"updatedAt": "2026-09-14T08:12:40Z"
}
]
}
```

| Field | What it holds |
| --- | --- |
| `id` | The preset's uuid. |
| `nodeType` | The node type the preset belongs to. |
| `name`, `description` | The preset's name and an optional description. |
| `data` | The captured node configuration. This is what you apply. |
| `groupId` | The folder the preset is in, from `GET /v1/node-preset-groups`, or `null`. |
| `tags`, `sortOrder` | Your tags and the preset's position in its list. |
| `createdAt`, `updatedAt` | Timestamps. |

## Read the built-in catalog

`GET /v1/node-presets/factory?nodeType=generate-image` returns `{ data: FactoryPreset[] }`, the presets that ship with Nodaro for that node type. Each entry is `{ id, name, description?, group?, groupKind?, data }`. A factory id has the form `<node-type>/<name>`, for example `generate-image/character-board` or `generate-video/orbit-360`.

**curl**

```bash
curl "https://app.nodaro.ai/v1/node-presets/factory?nodeType=generate-video" \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

**TypeScript SDK**

```ts
const { data } = await client.presets.listFactory('generate-video')
const orbit = data.find((p) => p.id === 'generate-video/orbit-360')
```

## Apply a preset

A preset's `data` is a captured node configuration. To apply a preset, merge its `data` into the node's data when you create or update a workflow. Values you set on the node after the merge win.

```ts
const node = {
id: 'portrait-1',
type: 'generate-image',
data: { ...cinematic.data, prompt: 'a lighthouse keeper at dawn' },
}
```

A preset can also carry `promptPrefix` and `promptSuffix`: text that is added before and after the prompt when the node runs. See [Prompt pre and post text](https://nodaro.ai/docs/concepts/prompt-pre-post-text).

Over MCP, find a preset with `list_node_presets` and read it with `get_node_preset`, or pass its `presetId` straight to a generation tool such as `generate_image`. The server then applies the preset, wraps your prompt with the preset's prefix and suffix, and lets any field you pass explicitly override it. See the [MCP tools reference](https://nodaro.ai/docs/mcp/tools).

## Favorites

Favorites put starred presets at the top of the editor's preset list. A favorite id is either a factory preset id or a custom preset's uuid. Reads accept OAuth app tokens with `presets:read`; the writes accept only a browser session, and no OAuth scope grants them.

| Method | Path | Query or body | Returns |
| --- | --- | --- | --- |
| `GET` | `/v1/node-presets/favorites` | `nodeType` (required) | `{ data: string[] }`, the most recent first |
| `POST` | `/v1/node-presets/favorites` | Body `{ nodeType, presetId }` | `{ data: { success: true } }`. Adding a favorite twice changes nothing. |
| `DELETE` | `/v1/node-presets/favorites` | `nodeType` and `presetId` (both required) | `{ data: { success: true } }` |

Factory ids contain a `/`, so URL-encode `presetId` in the `DELETE` query string:

```bash
curl -X DELETE "https://app.nodaro.ai/v1/node-presets/favorites?nodeType=generate-image&presetId=generate-image%2Fcharacter-board" \
  -H "Authorization: Bearer $SESSION_TOKEN"
```

## Create and edit presets

Creating, renaming and deleting presets is limited to the Nodaro web app's own signed-in session: `POST /v1/node-presets` creates a preset, `PATCH /v1/node-presets/:id` renames it or replaces its data, and `DELETE /v1/node-presets/:id` deletes it. An API token or an OAuth app token gets `403 forbidden`.

These writes accept an optional `expectedUpdatedAt`: in the body of a `PATCH`, and as a query parameter of a `DELETE`. Send the timestamp the library returned. When the preset changed since then, the route answers `409 conflict`; read it again before you retry.

### Recast render presets

The same library stores Recast generation settings under the `recast-render` namespace. It is not a node type: it saves a complete set of [Recast](https://nodaro.ai/docs/developers/api/recast) render settings so you can reuse them. The factory entries are read-only, and your own entries stay private to you on every device.

A `recast-render` preset's `data` is a strict, complete snapshot:

```json
{
"schemaVersion": 1,
"provider": "seedance-2-5",
"resolution": "480p",
"segmentSec": "max",
"renderMethod": "extend",
"anchorMode": "upfront",
"citeStyle": "bare",
"promptTiming": true,
"textOnly": false,
"interactive": true,
"anchorGates": false,
"musicGates": true,
"musicSource": "generated"
}
```

| Field | Accepted values |
| --- | --- |
| `segmentSec` | `max`, `scenes-max` (Long) or `scenes` (Short). |
| `resolution` | `480p`, `720p`, `1080p` or `4k`. |
| `renderMethod` | `extend` or `keyframes`. |
| `anchorMode` | `upfront`, `progressive` or `none`. |
| `citeStyle` | `bare` or `rich`. |
| `musicSource` | `generated`, `original` or `upload`. |
| `promptTiming`, `textOnly`, `interactive`, `anchorGates`, `musicGates` | `true` or `false`. |

Unknown fields and unknown schema versions are refused. The snapshot never contains source media, cast references, prompts, uploaded tracks, rights confirmations or results. Applying a preset changes the settings and refreshes the price quote; it never starts a generation. An Original or Upload music choice uses the target project's own media. Check the chosen model's current capabilities before you generate.

## Errors

| Status | Code | Meaning |
| --- | --- | --- |
| `400` | `validation_error` | `nodeType` is missing where it is required, or a field is invalid. |
| `401` | `unauthorized` | The token is missing, invalid or revoked. |
| `403` | `forbidden` | A write was sent with an API token or an OAuth app token. Writes need the browser session. |
| `403` | `insufficient_scope` | An OAuth app token lacks `presets:read`. |
| `409` | `conflict` | The preset changed since `expectedUpdatedAt`. Read it again. |

## Frequently asked questions

### Can I create or edit presets with an API token?

No. Over the API, presets are read-only for API tokens and OAuth app tokens. You create, rename and delete presets in the editor, where your browser session is signed in.

### How do I apply a preset to a node from code?

Read the preset and merge its data object into the node's data when you build or update a workflow. Over MCP, pass presetId to a generation tool such as generate_image and the preset is applied on the server.

### What is the difference between a custom preset and a factory preset?

A custom preset is one you saved, identified by a uuid. A factory preset is part of the built-in catalog for a node type, identified by an id such as generate-image/character-board.

### Which OAuth scope do the preset routes need?

OAuth app tokens need presets:read. Personal API tokens and session tokens need no scope, because you own the presets.
