# Images

> Image generation.

Image generation.

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

## Generate an image

`POST /v1/images`

Accepts the request, charges for it and returns immediately with a `gen_…` id. Poll `GET /v1/images/{id}` until `status` is `succeeded`, `failed` or `moderated`.

**Charged when accepted, not when finished.** The price is per megapixel: the output's, with a one-megapixel minimum, plus each reference's at the same rate. A generation that fails or is refused by the content filter is refunded in full.

```bash title="curl"
curl -X POST "https://api.lumavox.ai/v1/images" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"A weathered brass diving helmet on wet slate, low winter light"}'
```

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

| Field | Type | Required | Description |
|---|---|---|---|
| `prompt` | string | yes | What to generate. 1–4000 characters. |
| `aspect_ratio` | string |  | One of: 21:9, 16:9, 4:3, 1:1, 3:4, 9:16. Default `"1:1"`. |
| `megapixels` | number |  | Output size. **This and your references are what you are charged on** — the price is per megapixel, the output's with a one-megapixel minimum and each reference's at the same rate on its own size, so a request's cost is known before it is made. 0.25–4. Default `1`. |
| `output_format` | string |  | One of: jpeg, png, webp. Default `"jpeg"`. |
| `seed` | integer or null |  | Reuse a seed to get the same image again from the same prompt. Omit for a different one each time. At least 0. |
| `safety_tolerance` | integer |  | 0 is strictest. Content refused at any setting is refused. 0–5. Default `2`. |
| `enhance_prompt` | boolean |  | Let the model rewrite your prompt before rendering. On by default, which is the provider's own behaviour. Turn it off to send your words exactly as written. Default `true`. |
| `references` | array of Reference |  | Up to eight images from your library for the model to work from. **Input and output share a 9 megapixel budget** — at a 1 MP output that is eight references, at 2 MP it is seven — so send a smaller `variant` when you need more of them. Over budget is a 400 naming the numbers, never a silent drop. **Each reference is charged** at the output's per-megapixel rate, on the megapixels of the copy sent, with no minimum. At most 8 items. Default `[]`. |
| `references[].asset_id` | string | yes | `ast_…` — an image in your library. |
| `references[].variant` | string |  | Which copy to send: `original`, or one of `50`, `25`, `12.5` — the smaller versions made when the image was stored. Smaller copies spend less of the 9 megapixel budget shared with the output, and cost less: a reference is charged on the copy sent. A variant that does not exist falls back to the original, which may put the request over budget. Default `"original"`. |
| `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 `GET /v1/images/{id}` |
| `400` | The request could not be read: malformed JSON |
| `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 |
| `422` | The request failed validation |
| `429` | Rate limit exceeded |
| `503` | The capability's provider is not configured here |

**`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 |  |

## Check an image

`GET /v1/images/{generation_id}`

Poll every couple of seconds until `status` is terminal. On success `image.url` is a short-lived link; the image is also in your asset library under `image.asset_id`.

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

**Path parameters**

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

**Responses**

| Status | Description |
|---|---|
| `200` | `application/json` — The generation — the image once it succeeded |
| `400` | The request could not be read: malformed JSON |
| `401` | Missing or invalid credentials |
| `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 generation |
| `422` | The request failed validation |
| `429` | Rate limit exceeded |
| `503` | The capability's provider is not configured here |

**`200` 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 |  |
