# Presets (/api/presets)



A preset is a ready-made recipe, such as Show Your App or Fashion Try On. It is a capability with a `preset_` id, so it is priced with `POST /estimates` and run with `POST /generations` like any other. What these two routes add is the **template list**: a template-driven preset has a required `config.template_id`, and this is where its legal values come from.

| Endpoint                      | Scope | Rate limit per key          | What it does                                                          |
| ----------------------------- | ----- | --------------------------- | --------------------------------------------------------------------- |
| `GET /presets`                | Read  | 120 a minute (`agent_read`) | Every preset this workspace can see, each with its first 20 templates |
| `GET /presets/{id}/templates` | Read  | 120 a minute (`agent_read`) | One preset's whole template gallery                                   |

* Both are reads. They cost nothing.
* MCP: `list_presets` (pass `capability_id` for one preset's full gallery). [MCP tools](/mcp/tools).

<Callout type="info" title="A preset is not a separate submit path">
  There is no preset endpoint that starts work. You run a preset with `POST /generations`, `capability_id` set to its `preset_` id, and `config.template_id` set to a `template_id` from these lists.
</Callout>

## List presets [#list-presets]

<Endpoint method="GET" path="/presets" />

| Parameter | Type    | Default | Notes                                         |
| --------- | ------- | ------- | --------------------------------------------- |
| `limit`   | integer | 25      | Clamped to 1 to 50                            |
| `cursor`  | string  | none    | The previous `next_cursor`, sent back exactly |

```bash title="Terminal"
curl -s https://app.riffads.com/api/v1/presets \
  -H "Authorization: Bearer $RIFFADS_API_KEY"
```

```json title="Response: 200 (trimmed)"
{
  "ok": true,
  "presets": [
    {
      "capability_id": "preset_show_your_app",
      "name": "Show Your App",
      "description": "Put your app screenshot on a real person's phone, then bring it to life.",
      "output_kind": "video",
      "status": "available",
      "requires_template": true,
      "templates": [
        {
          "template_id": "captpl_0193c8f0a1b24e7f9d3c5a6b7e8f00a1",
          "name": "Couch scroll",
          "description": "Relaxed, at home, thumb on the screen",
          "category": "Lifestyle",
          "preview_url": "https://<public-preview-host>/templates/couch-scroll.mp4",
          "preview_kind": "video"
        }
      ],
      "templates_total": 1,
      "next_action": "Call get_capability_schema with capability_id \"preset_show_your_app\" to see its other fields, then call submit_generation with capability_id \"preset_show_your_app\" and config.template_id set to one of the template_id values listed with it. A preset is not a separate tool: submit_generation runs it like any other capability."
    }
  ],
  "total": 2,
  "next_cursor": null
}
```

Template names, counts and ids are examples. Read the live list, never hardcode one.

### Preset fields [#preset-fields]

| Field               | Type                                            | Notes                                                                     |
| ------------------- | ----------------------------------------------- | ------------------------------------------------------------------------- |
| `capability_id`     | string                                          | `preset_` prefix. Send it as `capability_id` to estimate and submit       |
| `name`              | string                                          | Display name. Can change. Never match on it                               |
| `description`       | string                                          | Omitted, not null, when empty                                             |
| `output_kind`       | `image` \| `video` \| `audio` \| `text`         | What comes out                                                            |
| `status`            | `available` \| `requires_plan` \| `coming_soon` | Only `available` can run. Retired presets are not listed                  |
| `required_plan`     | string                                          | Only with `requires_plan`. The plan that includes it                      |
| `requires_template` | boolean                                         | `true`: `config.template_id` is required, so pick one of `templates`      |
| `templates`         | array                                           | The first 20 live templates, in gallery order                             |
| `templates_total`   | integer                                         | All live templates. More than `templates.length` means this row is capped |
| `next_action`       | string                                          | How to run it, written for an agent. Advice only                          |

* **Order and cursor:** the same as `GET /capabilities`. `next_cursor` is the last row's `capability_id`, and `null` on the last page.
* **`total`** counts the presets this workspace can see.
* **`requires_template: true` with `templates_total: 0`** means the preset cannot run right now. Its `next_action` says so.

### Template fields [#template-fields]

| Field          | Type               | Notes                                                                                                  |
| -------------- | ------------------ | ------------------------------------------------------------------------------------------------------ |
| `template_id`  | string             | `captpl_` prefix. The value for `config.template_id`                                                   |
| `name`         | string             | Display name                                                                                           |
| `description`  | string \| null     |                                                                                                        |
| `category`     | string \| null     | Gallery grouping. Free text: display only                                                              |
| `preview_url`  | string             | A **permanent public** preview of RiffAds' own template art. Not a signed link, and it does not expire |
| `preview_kind` | `video` \| `image` | What `preview_url` holds                                                                               |

<Callout type="warn" title="A template id is a database id, not a slug">
  Read it from these routes every time. An id copied from an old list can be withdrawn by the time you submit, and the answer is then a refusal, not a render.
</Callout>

## One preset's templates [#one-presets-templates]

<Endpoint method="GET" path="/presets/{id}/templates" />

The path `id` is a preset `capability_id`. Returns every live template, with no cap and no paging.

```bash title="Terminal"
curl -s https://app.riffads.com/api/v1/presets/preset_show_your_app/templates \
  -H "Authorization: Bearer $RIFFADS_API_KEY"
```

```json title="Response: 200 (templates trimmed)"
{
  "ok": true,
  "capability_id": "preset_show_your_app",
  "name": "Show Your App",
  "status": "available",
  "requires_template": true,
  "templates": [
    {
      "template_id": "captpl_0193c8f0a1b24e7f9d3c5a6b7e8f00a1",
      "name": "Couch scroll",
      "description": "Relaxed, at home, thumb on the screen",
      "category": "Lifestyle",
      "preview_url": "https://<public-preview-host>/templates/couch-scroll.mp4",
      "preview_kind": "video"
    }
  ],
  "next_action": "Call get_capability_schema with capability_id \"preset_show_your_app\" to see its other fields, then call submit_generation with capability_id \"preset_show_your_app\" and config.template_id set to one of the template_id values listed with it. A preset is not a separate tool: submit_generation runs it like any other capability."
}
```

`required_plan` appears only when `status` is `requires_plan`.

```json title="Response: 404"
{
  "error": {
    "ok": false,
    "code": "not_found",
    "message": "No such preset. Call list_presets for the presets this workspace can use, or list_capabilities for everything else.",
    "retryable": false,
    "credits_charged": 0
  }
}
```

`not_found` covers a non-preset id (like `veo_31`), a typo, and a retired or internal preset.

### REST names for the MCP tools in next\_action [#rest-names-for-the-mcp-tools-in-next_action]

`next_action` is written for an agent and names MCP tools. Over REST:

| `next_action` says                  | REST call                     |
| ----------------------------------- | ----------------------------- |
| `get_capability_schema`             | `GET /capabilities/{id}`      |
| `submit_generation`                 | `POST /generations`           |
| `list_presets` with `capability_id` | `GET /presets/{id}/templates` |
| `list_capabilities`                 | `GET /capabilities`           |

## Run a preset [#run-a-preset]

Show Your App puts your app's screenshot on the phone a creator is holding. One upload plus one template.

<Steps>
  <Step>
    ### Pick a template [#pick-a-template]

    `GET /presets/preset_show_your_app/templates`. Show the person the `preview_url` of each and keep the `template_id` they choose.
  </Step>

  <Step>
    ### Read the schema [#read-the-schema]

    `GET /capabilities/preset_show_your_app`. It lists `template_id` and `reference_images` as required. Locked preset settings are absent: they come from the template. [Capabilities API](/api/capabilities).
  </Step>

  <Step>
    ### Upload the screenshot [#upload-the-screenshot]

    `POST /uploads`, PUT the bytes, finalize, and keep the `ast_` id. [Uploads](/api/uploads).
  </Step>

  <Step>
    ### Estimate [#estimate]

    ```bash title="Terminal"
    curl -s https://app.riffads.com/api/v1/estimates \
      -H "Authorization: Bearer $RIFFADS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "capability_id": "preset_show_your_app",
        "config": {
          "template_id": "captpl_0193c8f0a1b24e7f9d3c5a6b7e8f00a1",
          "reference_images": [
            { "assetId": "ast_0193c8f0a1b24e7f9d3c5a6b7e8f0011", "alias": "image1", "role": "reference" }
          ]
        }
      }'
    ```

    The estimate checks the config exactly as submit does, so a wrong key or a missing `template_id` is refused here, before anything is spent.
  </Step>

  <Step>
    ### Submit and wait [#submit-and-wait]

    Send the same `capability_id` and `config` to `POST /generations` with `max_credits` set to the estimate's `max_credits_needed`. Then wait on the `generation_id`, or read the batch when the answer carries a `batch_group_id`. [Generations](/api/generations).
  </Step>
</Steps>

### Preset submit refusals [#preset-submit-refusals]

The usual submit refusals apply. These are the preset-specific ones:

| Cause                                               | Code                     | HTTP | Retry |
| --------------------------------------------------- | ------------------------ | ---- | ----- |
| `template_id` missing on a preset that requires one | `invalid_config`         | 400  | no    |
| `template_id` is not one of this preset's templates | `not_found`              | 404  | no    |
| The template was withdrawn since you listed it      | `capability_unavailable` | 404  | no    |
| The preset needs a higher plan                      | `required_plan`          | 403  | no    |

On a template refusal, list the templates again and pick another.

## Not here [#not-here]

* **No preset authoring.** Presets and templates are made by RiffAds.
* **No preview files over the API** other than `preview_url`, which is template art, not your media.
* **No per-template price.** Price the full config with `POST /estimates`.
