---
title: "PUT /templates/{template} — Update Template"
url: https://lumean.app/en/developers/docs/api/put-templates-template
locale: en
description: "Update Template"
generated: 2026-10-17
operationId: template.update
method: PUT
path: "/templates/{template}"
scopes: [templates.write]
spec_version: 0.0.1
---

# PUT /templates/{template}

**Update Template**

Partial update: send only the fields you are changing. Both `PUT` and `PATCH` are
accepted and behave identically.

`config` is replaced **wholesale**, not merged: to change one setting, send the whole
object containing it. `service_key` may only change together with `config`, otherwise
the config will not validate against the new service's schema.

Editing a template does not affect orders already created: an order runs, is retried
(`POST /orders/{order}/retry`) and is shown (`template.config` in `GET /orders/{order}`)
with the settings it had at creation time. The exception is orders created before late
September 2026: they have no settings snapshot, follow the template's current
configuration (with `config_override` applied), and editing the template does affect
them.

**Required key permission:** `templates.write`.

- Required scope: `templates.write`

- The same handler also answers to method `PATCH`.

## Request

```bash
curl -X PUT 'https://api.lumean.app/api/public/templates/{template}' \
  -H 'Accept: application/json' \
  -H 'X-API-KEY: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "name": "Диктор — женский, тёплый"
}'
```

## Parameters

| Field | In | Type | Req. | Description |
| --- | --- | --- | --- | --- |
| `template` | path | `string` — uuid | required | Template UUID — 36 characters, not a number. Take it from the `id` field: `POST /templates` returns it on creation, and `GET /templates` and `GET /templates/browse` list it. default: `4d3e9f8b-2c1a-4f5a-8b9c-0d1e2f3a4b5c` |

## Request body

The update is partial: send only the fields you are changing, the rest are kept.

Two quirks account for most `422` responses here:

- **`service_key` and `config` travel together.** Validation rejects one without the
  other, so even when the service stays the same you have to repeat its code.
- **`config` is deep-merged into the current one, not swapped for it.** A key you
  omit keeps its previous value; resetting it to the default is not possible through
  an update, only by creating the template anew.

Content-Type: `application/json`

| Field | Type | Req. | Description |
| --- | --- | --- | --- |
| `folder_id` | `string` — uuid — nullable | — | UUID of the folder to move the template into. `null` moves it to the root. |
| `service_key` | `string` | — | New service code. Changes only together with `config` — the config is validated against the new service's schema. |
| `name` | `string` — ≤ 255 chars | — | New template name, up to 255 characters. |
| `is_public` | `boolean` | — | Publish the template in the shared catalogue, or unpublish it. |
| `config` | `object` | — | The complete new generation settings (not a partial patch). The accepted structure comes from `GET /templates/config-options?service=<code>`. |

**Example**

```json
{
  "name": "Диктор — женский, тёплый"
}
```

## Body scenarios

### Rename only

A partial update: only the changed fields are sent, every other template setting is preserved.

```json
{
  "name": "Диктор — женский, тёплый"
}
```

### Adjust the voice settings

config is deep-merged into the current one, so only the keys you change need to be sent. service_key is still required: validation rejects one without the other.

```json
{
  "service_key": "elevenlabs",
  "config": {
    "tts_settings": {
      "voice_settings": {
        "stability": 0.7,
        "speed": 1.1
      }
    }
  }
}
```

### Switch the service

Send the new service’s config in full: the old keys are not dropped (the merge goes deep), but they mean nothing under the new service’s schema.

```json
{
  "service_key": "heygen",
  "config": {
    "tts_settings": {
      "voice_id": "1bd001e7e50f421d891986aad5158bc8",
      "speed": 1
    }
  }
}
```

## Responses

### 200 — Success. The payload is in `data`.

**Response fields**

| Field | Type | Req. | Description |
| --- | --- | --- | --- |
| `success` | `boolean` | required | Success flag. Branch on the HTTP status code instead: any 2xx response carries `true` here, while an error comes back in a different shape that need not contain this field at all. |
| `message` | `string` | required | A human-readable note in the account language. Fine to show to a user; do not parse it — the wording changes without notice and is not an error code. |
| `data` | `Template` | required | The template after the update, with `config` already merged. Check it when you sent a partial configuration. |
| `data.id` | `string` | required | Template UUID. Put it into `template_id` when creating an order. |
| `data.user_id` | `integer` | required | ID of the template owner. |
| `data.slug` | `string` | required | Short public identifier used for sharing. `null` on a template that was never shared. |
| `data.name` | `string` | required | Template name — for humans only; it does not affect generation. |
| `data.service_key` | `string` | required | Code of the service whose schema validates `config`: `elevenlabs`, `lumimg`, and so on. |
| `data.service` | `Service \| null` — nullable | required | The template’s service for reference: name and pricing, without configuration schemas. `null` means no service with that code exists any more. |
| `data.config` | `object` | required | Generation settings: voice, model, speed and so on — the set of keys is defined by the service. Look up allowed values in `GET /templates/config-options?service=<code>`; anything outside the schema is rejected with `422`. Inside an order (`template` in `GET /orders/{order}`) this is the order’s own settings — the snapshot taken at creation with `config_override` applied, not the template’s current configuration (for orders created before late September 2026 — the current template configuration with `config_override`). |
| `data.is_public` | `boolean` | required | Whether the template is published in the shared catalogue. |
| `data.folder_id` | `string` | required | UUID of the folder holding the template, or `null` for the root. The folder tree is visible in `GET /templates/browse`. |
| `data.created_at` | `string` — date-time — nullable | required | When the template was created, ISO 8601. |
| `data.updated_at` | `string` — date-time — nullable | required | When the template last changed, ISO 8601. |

**Example**

```json
{
  "success": true,
  "message": "Template updated",
  "data": {
    "id": "7e2a4c1d-8b3f-4a5e-9c6d-1f2e3a4b5c6d",
    "user_id": 501,
    "slug": null,
    "name": "Narrator Voice (EN) v2",
    "service_key": "elevenlabs",
    "config": {
      "tts_settings": {
        "model_id": "eleven_multilingual_v2",
        "voice_id": "21m00Tcm4TlvDq8ikWAM",
        "mode": "mode_v1",
        "language_code": "en",
        "advanced_voice_settings": true,
        "voice_settings": {
          "stability": 0.6,
          "similarity_boost": 0.75,
          "use_speaker_boost": true
        }
      }
    },
    "is_public": false,
    "folder_id": null,
    "created_at": "2026-06-01T10:00:00+00:00",
    "updated_at": "2026-07-17T16:45:00+00:00"
  }
}
```

### 401 — The key is missing, unknown, disabled or expired.

**Response fields**

| Field | Type | Req. | Description |
| --- | --- | --- | --- |
| `success` | `boolean` | required | Always `false` in error responses. |
| `message` | `string` | required | Error text in the key's language (set through the `X-Locale` header). |
| `data` | `null` — nullable | required | Always `null` when the request is rejected before processing. |

**Example**

```json
{
  "success": false,
  "message": "API key is missing. Provide it in the X-API-KEY header.",
  "data": null
}
```

### 403 — The key lacks the required permission, or the object belongs to another user.

**Response fields**

| Field | Type | Req. | Description |
| --- | --- | --- | --- |
| `message` | `string` | required | Error overview. |

**Example**

```json
{
  "success": false,
  "message": "This API key does not have permission to perform this action."
}
```

### 404 — Not found, or not reachable with this key.

**Response fields**

| Field | Type | Req. | Description |
| --- | --- | --- | --- |
| `success` | `boolean` | required | Always `false` in error responses. |
| `message` | `string` | required | Error text in the key's language (set through the `X-Locale` header). |
| `reason` | `string` | — | Machine-readable reason — present on business refusals (for example, `invalid_order_status`); other errors omit it. Branch on it, not on the message text. |
| `error_code` | `string` | — | The same machine-readable code as in `reason` — a second name for the field. Arrives together with `reason`; the values always match. |

**Example**

```json
{
  "success": false,
  "message": "Resource not found."
}
```

### 422 — Validation error. Offending fields are listed in `errors`.

**Response fields**

| Field | Type | Req. | Description |
| --- | --- | --- | --- |
| `message` | `string` | required | Errors overview. |
| `errors` | `object` | required | A detailed description of each field that failed validation. |
| `errors.*` | `array<string>` — arbitrary keys | — |  |

**Example**

```json
{
  "message": "string",
  "errors": {
    "key": [
      "string"
    ]
  }
}
```

---

- This page in the browser: https://lumean.app/en/developers/docs/api/put-templates-template
- Full machine-readable reference: https://api.lumean.app/docs/llm.md
