# Skyboxes

> 360° environments — an 8K HDRI for lighting a 3D scene, an equirectangular PNG and a depth map.

360° environments — an 8K HDRI for lighting a 3D scene, an equirectangular PNG and a depth map. One flat price per generation, whatever you ask for.

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

## Generate a 360 environment

`POST /v1/skybox`

Accepts the request, charges for it and returns immediately with a `gen_…` id. Poll `GET /v1/skybox/{id}`.

**One flat price per generation.** **`skybox.url` is a 32-bit Radiance `.hdr`** and will not render in a browser; use `skybox.preview_url` for a 360 viewer. A failure is refunded in full.

```bash title="curl"
curl -X POST "https://api.lumavox.ai/v1/skybox" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"A salt flat at blue hour, thin cloud, distant mesas"}'
```

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

| Field | Type | Required | Description |
|---|---|---|---|
| `prompt` | string | yes | The environment to build. Describe the whole surrounding — sky, ground, horizon and light — rather than a subject. 1–550 characters. |
| `negative_text` | string or null |  | What to keep out of it. At most 200 characters. |
| `style_id` | integer or null |  | One of the provider's styles — fetch the current list from `GET /v1/skybox/styles`. Omit for their default. At least 1. |
| `seed` | integer or null |  | Reuse a seed to get the same environment again. **Note this starts at 1, not 0** — the provider uses zero for random. 1–2147483647. |
| `enhance_prompt` | boolean |  | Default `false`. |
| `init_image` | string or null |  | `ast_…` — an existing **equirectangular** image in your library to build from. |
| `init_strength` | number |  | How much the init image influences the result: 0 ignores it, 0.97 is the provider's maximum. Only has an effect alongside `init_image`. 0–0.97. Default `0.5`. |
| `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/skybox/{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 |  |

## List skybox styles

`GET /v1/skybox/styles`

The provider's current styles, each with the id to pass as `style_id`, every one in one page. **Fetched live and cached briefly**; an empty page if the provider is unreachable, since no style is a working request.

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

**Responses**

| Status | Description |
|---|---|
| `200` | `application/json` — The styles available right now |
| `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 |
| `422` | The request failed validation |
| `429` | Rate limit exceeded |
| `503` | Skybox generation is not configured here |

**`200` response body**

| Field | Type | Description |
|---|---|---|
| `data` | array of SkyboxStyle |  |
| `data[].id` | integer |  |
| `data[].name` | string |  |
| `data[].max_prompt_chars` | integer or null |  |
| `data[].max_negative_chars` | integer or null |  |
| `data[].model_version` | string or null | Which of the provider's model generations it runs on. |
| `data[].image` | string or null | A sample image of the style, if they gave one. |
| `next_cursor` | string or null | Pass as `cursor` for the next page; `null` when this is the last. |

## Check a skybox

`GET /v1/skybox/{generation_id}`

```bash title="curl"
curl "https://api.lumavox.ai/v1/skybox/$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 environment 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 |  |
