# Templates

> Generations somebody has already designed — the prompt, the size, every parameter — leaving only the media inputs for you to fill.

Generations somebody has already designed — the prompt, the size, every parameter — leaving only the media inputs for you to fill. Priced up front, and polled on the capability's own endpoint.

When and why to call these: the [templates guide](https://docs.lumavox.ai/media/templates.md).

## List templates

`GET /v1/templates`

Every published template, with the inputs each one takes and what a generation from it costs your organisation.

A template is a generation somebody has already designed — the prompt, the size, the style, every parameter — leaving some of its inputs for you to fill: a picture or two, or the prompt itself. `capability` says what it makes and which scope your key needs; `inputs` says what to supply, and each one's `kind` says whether that is an asset id or text. Every template is in one page.

```bash title="curl"
curl "https://api.lumavox.ai/v1/templates" \
  -H "Authorization: Bearer $API_KEY"
```

**Responses**

| Status | Description |
|---|---|
| `200` | `application/json` — The published templates, priced for your organisation |
| `401` | Missing or invalid credentials |
| `422` | The request failed validation |
| `429` | Rate limit exceeded |

**`200` response body**

| Field | Type | Description |
|---|---|---|
| `data` | array of TemplateInfo |  |
| `data[].id` | string | `tpl_…`. |
| `data[].name` | string |  |
| `data[].description` | string or null |  |
| `data[].capability` | `"image"`, `"video"`, `"skybox"` | What it generates, and so which scope the key needs to generate from it and which endpoint the result is polled on. |
| `data[].inputs` | array of TemplateInput | What to supply, in the order they are drawn. |
| `data[].inputs[].slot` | string | What to name it in `inputs` when generating. |
| `data[].inputs[].kind` | `"image"`, `"text"` | `image` takes an `ast_…` id from your library; `text` takes what you type — the prompt, when the template leaves it to you. |
| `data[].inputs[].label` | string |  |
| `data[].inputs[].hint` | string or null |  |
| `data[].inputs[].required` | boolean |  |
| `data[].inputs[].example_url` | string or null | For an image input: a picture of the kind of thing that belongs here — the one the example output was generated from. |
| `data[].inputs[].example` | string or null | For a text input: an example of what to write — the text the example output was generated from. |
| `data[].example_url` | string or null | What it produces: a real output, generated by the platform from the example inputs shown beside each slot. |
| `data[].credits_micros` | integer or null | What one generation from it costs your organisation, in micro-credits (1 credit is 1,000,000). Knowable up front because every parameter is frozen — but on an image template your own pictures are charged on top, each at the output's per-megapixel rate and the size sent. Null when the platform has no rate for the capability. |
| `data[].fixed_megapixels` | number | On an image template, the megapixels of the pictures the template itself sends with every generation, each charged at the output's per-megapixel rate and already in `credits_micros` — so an estimate that moves the output's size keeps this term as it is, and adds your own pictures at the size they are sent. 0 on a video or skybox template, whose price no picture changes. |
| `data[].parameters` | map of any | The request the template froze, minus the prompt. Every entry is decided for you unless its name is in `exposed`, in which case it is the default you may override. |
| `data[].exposed` | array of string | The parameters you may change, by name — pass a new value in `parameters` when generating. A change to a size, a duration or a resolution changes the price from the one quoted here. |
| `data[].created_at` | string (date-time) |  |
| `data[].updated_at` | string (date-time) |  |
| `next_cursor` | string or null | Pass as `cursor` for the next page; `null` when this is the last. |

## Read a template

`GET /v1/templates/{template_id}`

```bash title="curl"
curl "https://api.lumavox.ai/v1/templates/$TEMPLATE_ID" \
  -H "Authorization: Bearer $API_KEY"
```

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `template_id` | string | yes |  |

**Responses**

| Status | Description |
|---|---|
| `200` | `application/json` — The template, priced for your organisation |
| `400` | The request could not be read: malformed JSON |
| `401` | Missing or invalid credentials |
| `404` | No such template |
| `422` | The request failed validation |
| `429` | Rate limit exceeded |

**`200` response body**

| Field | Type | Description |
|---|---|---|
| `id` | string | `tpl_…`. |
| `name` | string |  |
| `description` | string or null |  |
| `capability` | `"image"`, `"video"`, `"skybox"` | What it generates, and so which scope the key needs to generate from it and which endpoint the result is polled on. |
| `inputs` | array of TemplateInput | What to supply, in the order they are drawn. |
| `inputs[].slot` | string | What to name it in `inputs` when generating. |
| `inputs[].kind` | `"image"`, `"text"` | `image` takes an `ast_…` id from your library; `text` takes what you type — the prompt, when the template leaves it to you. |
| `inputs[].label` | string |  |
| `inputs[].hint` | string or null |  |
| `inputs[].required` | boolean |  |
| `inputs[].example_url` | string or null | For an image input: a picture of the kind of thing that belongs here — the one the example output was generated from. |
| `inputs[].example` | string or null | For a text input: an example of what to write — the text the example output was generated from. |
| `example_url` | string or null | What it produces: a real output, generated by the platform from the example inputs shown beside each slot. |
| `credits_micros` | integer or null | What one generation from it costs your organisation, in micro-credits (1 credit is 1,000,000). Knowable up front because every parameter is frozen — but on an image template your own pictures are charged on top, each at the output's per-megapixel rate and the size sent. Null when the platform has no rate for the capability. |
| `fixed_megapixels` | number | On an image template, the megapixels of the pictures the template itself sends with every generation, each charged at the output's per-megapixel rate and already in `credits_micros` — so an estimate that moves the output's size keeps this term as it is, and adds your own pictures at the size they are sent. 0 on a video or skybox template, whose price no picture changes. |
| `parameters` | map of any | The request the template froze, minus the prompt. Every entry is decided for you unless its name is in `exposed`, in which case it is the default you may override. |
| `exposed` | array of string | The parameters you may change, by name — pass a new value in `parameters` when generating. A change to a size, a duration or a resolution changes the price from the one quoted here. |
| `created_at` | string (date-time) |  |
| `updated_at` | string (date-time) |  |

## Generate from a template

`POST /v1/templates/{template_id}/generate`

Fill the template's inputs and it generates exactly what its author designed. Accepted, charged and returned immediately with a `gen_…` id — poll it on the endpoint for the template's `capability`: `GET /v1/images/{id}`, `GET /v1/videos/{id}` or `GET /v1/skybox/{id}`.

**The key needs the capability's scope**, not a scope of its own: a key that may make images may use image templates. **Charged when accepted**, at the price the listing showed, and refunded in full if the generation fails.

`inputs` names each slot the template opens and what fills it: the `ast_…` asset in your library for an `image` input, your text for a `text` one. A required input left out, an input the template does not open, or a picture slot given something that is not an asset id is a 422 before anything is charged; an asset that is missing, is not yours or is not an image is a 400, likewise before anything is charged.

`parameters` overrides the ones the template lists in `exposed`, by name; anything else is decided by the template and a 422 if sent. **An exposed size, duration or resolution changes the price**: the listing quotes the defaults, and the 202 says what was charged. **So do your own pictures on an image template**: each is charged on top of the listing's price, at the output's per-megapixel rate and the size sent.

**Reference images are sent at the largest size that fits.** An image template shares the provider's 9 megapixel budget between its output, the pictures it fixes and your references, so a phone photograph is sent at a smaller copy when the original would not fit — you never have to pick the size.

```bash title="curl"
curl -X POST "https://api.lumavox.ai/v1/templates/$TEMPLATE_ID/generate" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `template_id` | string | yes |  |

**Request body** (`application/json`)

| Field | Type | Required | Description |
|---|---|---|---|
| `inputs` | map of string |  | Slot name to value, one entry per input you are filling: an `ast_…` asset id for an `image` input, your text for a `text` one. Every `required` input must be present; an input the template does not open is a 422. Default `{}`. |
| `parameters` | map of any |  | Overrides for the parameters the template lists in `exposed`, by name, as `POST /v1/images` (or the capability's own route) would take them. A parameter the template did not expose is a 422 before anything is charged; a value the capability refuses is the same 422 that route would give. Default `{}`. |
| `folder_id` | string or null |  | `fld_…` — the folder in your asset library the finished file is filed under. Omit it for the root. A folder that is not yours is a 400, never a silent fall back to the root. |

**Responses**

| Status | Description |
|---|---|
| `202` | `application/json` — Accepted and charged. Poll the capability's own endpoint |
| `400` | An input names an asset that is missing, is not yours, is not an image, or is still being processed |
| `401` | Missing or invalid credentials |
| `402` | Out of credits, or the organisation is suspended — nothing was charged |
| `403` | The key is valid but its scopes do not cover this capability — a key limited to `tts` gets this from `/v1/images` rather than a 401 |
| `404` | No such template |
| `422` | The request failed validation |
| `429` | Rate limit exceeded |

**`202` response body**

| Field | Type | Description |
|---|---|---|
| `id` | string | `gen_…`. Poll this. |
| `status` | `"queued"`, `"submitted"`, `"running"`, `"succeeded"`, `"failed"`, `"moderated"` | `queued`, `submitted` and `running` mean keep polling. `succeeded`, `failed` and `moderated` are terminal — stop. |
| `capability` | `"tts"`, `"image"`, `"video"`, `"stt"`, `"skybox"` |  |
| `model` | string or null |  |
| `prompt` | string or null |  |
| `aspect_ratio` | string or null |  |
| `width` | integer or null |  |
| `height` | integer or null |  |
| `output_format` | string or null |  |
| `seed` | integer or null |  |
| `megapixels` | number or null |  |
| `safety_tolerance` | integer or null |  |
| `enhance_prompt` | boolean or null |  |
| `folder_id` | string or null | `fld_…` the result lands in, or null for the root. |
| `duration` | integer or null | Seconds asked for. Video only. |
| `resolution` | string or null | `hd` or `fhd`. Video only. |
| `generate_audio` | boolean or null |  |
| `start_frame` | string or null |  |
| `end_frame` | string or null |  |
| `negative_text` | string or null |  |
| `style_id` | integer or null |  |
| `init_image` | string or null |  |
| `init_strength` | number or null |  |
| `voice_id` | string or null |  |
| `speech` | TTSOptions or null | The voice and model a speech generation ran with. |
| `speech.voice_id` | string | An id from `GET /v1/voices` — the built-in voice, a platform voice, or one your organisation uploaded. |
| `speech.model` | string | Which model speaks: one of the voice's `models` in `GET /v1/voices` — `eleven_flash_v2_5` (the fastest, and the default), `eleven_multilingual_v2` or `eleven_v3`. A model the voice does not list is refused. |
| `speech.provider_options` | ProviderVoiceSettingsInfo or null | The provider's voice settings sent on top of the voice's own; `null` took theirs. |
| `speech.provider_options.stability` | number or null | Low is more expressive and less consistent between generations; high is flatter and more repeatable. |
| `speech.provider_options.similarity_boost` | number or null | How closely to cling to the original voice. High can reproduce artefacts in the source. |
| `speech.provider_options.style` | number or null | Style exaggeration. Costs latency; leave unset on anything conversational. |
| `speech.provider_options.use_speaker_boost` | boolean or null | Similarity at the cost of latency. |
| `speech.provider_options.speed` | number or null | 1.0 is the voice's own pace. |
| `speech.format` | `"wav"`, `"pcm"` |  |
| `speech.sample_rate` | `8000`, `16000`, `22050`, `24000`, `44100` or null | Hz of the returned audio — one of 8000, 16000, 22050, 24000, 44100, each served natively by the provider. Unset means 24000. `POST /v1/tts` says what it produced in the `X-Sample-Rate` header. Queued speech (`POST /v1/audio`) does not take it: each of its formats has its own rate. |
| `credits_micros` | integer or null | What was debited when this was accepted, in micro-credits (1 credit is 1,000,000). A failure refunds it in full; a transcription's is corrected to the measured length when it finishes. |
| `template_id` | string or null |  |
| `image` | GenerationImage or null |  |
| `image.asset_id` | string or null | The library entry this generation produced. |
| `image.url` | string or null | A short-lived link to the file. Minted per response and not stored — fetch the asset again for a fresh one. |
| `image.width` | integer or null |  |
| `image.height` | integer or null |  |
| `image.content_type` | string or null |  |
| `image.size_bytes` | integer or null |  |
| `video` | GenerationVideo or null |  |
| `video.asset_id` | string or null | The library entry this generation produced. |
| `video.url` | string or null | A short-lived link to the file. Minted per response and not stored — fetch the asset again for a fresh one. |
| `video.width` | integer or null |  |
| `video.height` | integer or null |  |
| `video.duration_seconds` | number or null |  |
| `video.content_type` | string or null |  |
| `video.size_bytes` | integer or null |  |
| `video.has_thumbnail` | boolean or null |  |
| `audio` | GenerationAudio or null |  |
| `audio.asset_id` | string or null | The library entry this generation produced. |
| `audio.url` | string or null | A short-lived link to the file. Minted per response and not stored — fetch the asset again for a fresh one. |
| `audio.duration_seconds` | number or null |  |
| `audio.content_type` | string or null |  |
| `audio.size_bytes` | integer or null |  |
| `audio.peaks` | array of integer or null | Waveform magnitudes, 0-100, for a mirrored strip; null when never computed. |
| `transcript` | GenerationTranscript or null |  |
| `transcript.text` | string or null | The transcript, as one string. |
| `transcript.language_code` | string or null |  |
| `transcript.language_probability` | number or null |  |
| `transcript.duration_seconds` | number or null |  |
| `transcript.word_count` | integer or null |  |
| `transcript.asset_id` | string or null | The library entry holding the full JSON. |
| `transcript.content_type` | string or null |  |
| `transcript.size_bytes` | integer or null |  |
| `transcript.url` | string or null |  |
| `skybox` | GenerationSkybox or null |  |
| `skybox.asset_id` | string or null | The library entry this generation produced. |
| `skybox.url` | string or null | A short-lived link to the file. Minted per response and not stored — fetch the asset again for a fresh one. |
| `skybox.preview_url` | string or null | A short-lived link to the 2K WebP, which is what to load in a 360 viewer. |
| `skybox.width` | integer or null |  |
| `skybox.height` | integer or null |  |
| `skybox.content_type` | string or null |  |
| `skybox.size_bytes` | integer or null |  |
| `skybox.has_thumbnail` | boolean or null |  |
| `skybox.files` | array of SkyboxFile | Every file the environment was stored as, in plan order. |
| `skybox.files[].name` | string | `hdr-4k`, `hdr-2k`, `png-8k`, `png-4k`, `png-2k`, `preview-2k`, `depth-2k`. |
| `skybox.files[].content_type` | string |  |
| `skybox.files[].url` | string or null | Short-lived link. Minted per response and not stored. |
| `skybox.files[].width` | integer or null |  |
| `skybox.files[].height` | integer or null |  |
| `skybox.files[].size_bytes` | integer or null |  |
| `error` | string or null |  |
| `created_at` | string (date-time) |  |
| `finished_at` | string (date-time) or null |  |

## A template's example image

`GET /v1/templates/{template_id}/files/{name}`

Public — every tenant sees the same templates. `name` is `example` for the picture of what the template produces, or `input-<slot>` for the example beside one of its inputs; the URLs come from the template itself and are not worth constructing by hand. `v` is the token in those URLs; supplying it makes the response immutable-cacheable.

```bash title="curl"
curl "https://api.lumavox.ai/v1/templates/$TEMPLATE_ID/files/$NAME" \
  -H "Authorization: Bearer $API_KEY"
```

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `template_id` | string | yes |  |
| `name` | string | yes |  |

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `v` | string |  |  |

**Responses**

| Status | Description |
|---|---|
| `200` | `application/json` — The image, or a redirect to where object storage serves it |
| `400` | The request could not be read: malformed JSON |
| `404` | No such template or file |
