---
title: "POST /orders/chunks/preview — Preview Chunk Split"
url: https://lumean.app/de/developers/docs/api/post-orders-chunks-preview
locale: de
description: "Preview Chunk Split"
generated: 2026-10-17
operationId: order.previewChunks
method: POST
path: /orders/chunks/preview
scopes: [orders.read]
spec_version: 0.0.1
---

# POST /orders/chunks/preview

**Preview Chunk Split**

Dry-run of the text-to-chunks split and cost calculation — without creating an order.
The same computation `POST /orders` uses internally: check your split/price here
before the real submission. **Does not spend the key's token quota** — preview is free.

A single ready envelope covers both the sync and async paths:

- **200** `status=ready` — TTS text ≤ 50,000 characters (and any non-TTS text): the
  result is ready immediately, the body carries `summary` (totals for the whole text),
  the first page of `chunks[]`, and `page` (pagination meta).
- **202** `status=processing` — TTS text past the threshold: the calculation runs in
  the background, the body carries only `preview_id`. The result arrives as a `.ready`
  WebSocket event on the `private-chunks.preview.{userId}` channel (see
  `POST /broadcasting/auth`), or fetch it manually via
  `GET /orders/chunks/preview/{previewId}`.

**Incremental re-preview:** pass the previous response's `preview_id` as
`previous_preview_id` along with the new `input_text` — only the edited neighborhood
is recomputed, and the result is identical to a full recalculation.

**Order settings:** pass the same `config_override` you will send to `POST /orders` —
the preview splits by the template config with it applied, exactly like the order
(settings that affect the split apply here too). A value that order creation would
reject with `422` is rejected by the preview as well.

**Pauses/caps:** exceeding the pause limits (>500 pauses, >3600s total, >10s single) or the
order size (>1500 fragments) does NOT fail the request — you get
`summary.caps_ok=false` + `caps_exceeded`. This is what sets preview apart from
`POST /orders`, where the same overage returns `422`.

The preview cache is shared with a web session of the same account (if the key owner
also uses the website): the same text+template+mode, already computed in the browser,
comes back instantly.

**Required key permission:** `orders.read` (preview does not mutate data — it is a read).

- Benötigtes Recht: `orders.read`

## Anfrage

```bash
curl -X POST 'https://api.lumean.app/api/public/orders/chunks/preview' \
  -H 'Accept: application/json' \
  -H 'X-API-KEY: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "template_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
  "task_type": "string",
  "input_text": "string",
  "previous_preview_id": "string",
  "text_hash": "string",
  "generation_mode": "default",
  "config_override": []
}'
```

## Parameter

Keine Parameter.

## Request-Body

Chunk-preview parameters — a dry-run of the text-to-chunks split and its cost, without
creating an order. Free of charge, does not spend the key's token quota.

Content-Type: `application/json`

| Feld | Typ | Pflicht | Beschreibung |
| --- | --- | --- | --- |
| `template_id` | `string` — uuid | Pflicht |  |
| `input_text` | `string` | Pflicht |  |
| `task_type` | `string` — nullable | — |  |
| `previous_preview_id` | `string` — nullable | — | The `preview_id` of a prior preview response, for incremental re-preview: only the edited neighborhood is recomputed. Works for sync texts (≤50,000 characters); for larger ones the edit still goes through a full async recalculation. If it has expired, just omit it — you get a full recalculation instead. |
| `text_hash` | `string` — Muster: ^[a-f0-9]{64}$ — nullable | — | Not required for `POST` — the server computes the hash itself from `input_text`. Send it only if you already know it for some other reason; it is otherwise ignored here (used by `GET /orders/chunks/preview` for the preflight lookup instead). |
| `generation_mode` | `string` — `default` · `paragraph` — nullable | — | Generation mode — the same value accepted by `config_override.generation_mode` when creating an order. `paragraph` splits the text on blank lines first, and each paragraph becomes its own generation unit (see `POST /orders` for the full explanation). Omitted — a continuous split, the previous behavior. |
| `config_override` | `object` | — | Generation settings on top of the template config — the same JSON object you will send to `POST /orders`. Settings that affect the split apply to the preview exactly as to the order; keys the service does not declare are dropped. Omitted — the split follows the template config alone. |

**Beispiel**

```json
{
  "template_id": "9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c",
  "task_type": "string",
  "input_text": "string",
  "previous_preview_id": "string",
  "text_hash": "string",
  "generation_mode": "default",
  "config_override": []
}
```

## Antworten

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

**Antwortfelder**

| Feld | Typ | Pflicht | Beschreibung |
| --- | --- | --- | --- |
| `success` | `boolean` | Pflicht | 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` | Pflicht | 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` | `object` | Pflicht | The response payload. |
| `data.status` | `string` — `ready` | Pflicht | Always `ready` for a 200 response — the calculation is done. |
| `data.preview_id` | `string` | Pflicht | A content-based preview id (sha256 of text+template+mode+algorithm version). Use it for incremental re-preview (`previous_preview_id`) and for pagination (`GET .../{previewId}`). |
| `data.summary` | `object` | Pflicht | Totals for the whole text — not just the current page of chunks. |
| `data.summary.chunks_count` | `integer` | Pflicht | Total number of chunks in the split. |
| `data.summary.total_effective_length` | `integer` | Pflicht | Total billable length across all chunks, in characters (pauses and stage directions are excluded). |
| `data.summary.total_price_units` | `integer` | Pflicht | Total cost in tokens — the same amount `POST /orders` would charge for the identical text. |
| `data.summary.voiced_length` | `integer` | Pflicht | Actual voiced length BEFORE topping up to the minimum billable volume — for an honest character count display. |
| `data.summary.min_billable_chars` | `integer` | Pflicht | The minimum billable volume (0 — no minimum). If `voiced_length` is below this threshold, the cost is still computed against the threshold. |
| `data.summary.pause_count` | `integer` | Pflicht | Number of pauses in the text (`{{pause}}`/`<break/>`). Pauses are free — excluded from the cost. |
| `data.summary.total_pause_seconds` | `number` | Pflicht | Total silence duration, in seconds. |
| `data.summary.caps_ok` | `boolean` | Pflicht | The order sanity caps are not exceeded. `false` does NOT fail the preview (unlike `POST /orders`, where the same overage returns `422`) — it is only a warning. |
| `data.summary.caps_exceeded` | `string` — nullable | Pflicht | Which cap was exceeded: `count` (more than 500 pauses), `single` (one pause longer than 10s), `total` (total silence over 3600s) or `chunks` (the order yields more than 1500 fragments — more than the system can assemble into one file). `null` means the caps are fine. |
| `data.summary.voiced_empty` | `boolean` | Pflicht | Nothing speakable is left in the text (only tags/pauses/stage directions). `POST /orders` with this text would return `422`. |
| `data.summary.blocked_chunks` | `array<integer>` | Pflicht | Indexes of chunks whose text already tripped the content policy and sits in the blocklist. An empty array means no blocks. |
| `data.summary.has_blocked_content` | `boolean` | Pflicht | Whether at least one chunk is blocked — the same thing as `blocked_chunks !== []`, as a boolean for convenience. |
| `data.summary.paragraphs` | `array<object>` — nullable | Pflicht | `null` means the preview is not paragraph-mode (the same `generation_mode` value used for orders drives this field). In paragraph mode, an aggregate per paragraph. |
| `data.summary.paragraphs[].paragraph_index` | `integer` | Pflicht | The paragraph's index, zero-based. |
| `data.summary.paragraphs[].chunks_count` | `integer` | Pflicht | How many chunks fall inside this paragraph (a long paragraph can span several chunks). |
| `data.summary.paragraphs[].first_chunk_index` | `integer` | Pflicht | The cross-text `index` of the paragraph's first chunk — use it to locate the paragraph within the paginated `data.chunks`, even if its chunks landed on a different page. |
| `data.summary.paragraphs[].text_length` | `integer` | Pflicht | The raw length of the paragraph text in characters, including tags/pauses. |
| `data.summary.paragraphs[].effective_length` | `integer` | Pflicht | The paragraph's billable length — in paragraph mode the minimum billable volume is enforced PER PARAGRAPH, so short paragraphs can cost more than the same text split continuously. |
| `data.summary.paragraphs[].price_units` | `integer` | Pflicht | The paragraph's cost in tokens. |
| `data.summary.paragraphs_count` | `integer` — nullable | Pflicht | Number of paragraphs, or `null` outside paragraph mode. Stays a number even when `paragraphs` is collapsed by the WS cap. |
| `data.summary.cost_lmc` | `number` | — | Cost in LMC (currency-independent base). The field may be ABSENT (not `null`) if the cost anchor could not be computed — this is best-effort and not critical for a preview. |
| `data.summary.cost_display` | `object` — nullable | — | The cost converted to the account's display currency — the same multi-currency breakdown used across the public API (`amount`/`amount_formatted` per currency code). The field may be ABSENT if `cost_lmc` was not computed or the rate is unavailable. |
| `data.chunks` | `array<object>` | Pflicht | Only the CURRENT page (see `data.page`), not the full list — use pagination (`GET .../{previewId}?page=`) for the rest. |
| `data.chunks[].index` | `integer` | Pflicht | The chunk's index across the whole text (does not reset at paragraph boundaries). |
| `data.chunks[].text` | `string` | Pflicht | The chunk text exactly as the voice provider will receive it. |
| `data.chunks[].start` | `integer` | Pflicht | The chunk's start offset (in characters) within the NORMALIZED input text — the same normalization `POST /orders` applies before splitting. |
| `data.chunks[].end` | `integer` | Pflicht | The chunk's end offset in the normalized text (half-open interval: `end` is exclusive). |
| `data.chunks[].text_length` | `integer` | Pflicht | The raw length of the chunk text in characters, INCLUDING tags/pauses. |
| `data.chunks[].effective_length` | `integer` | Pflicht | The chunk's billable length (pauses excluded, topped up to the minimum where applicable) — the sum across a page's chunks need not equal `total_effective_length` (which covers ALL chunks, not just this page). |
| `data.chunks[].price_units` | `integer` | Pflicht | The chunk's cost in tokens. |
| `data.chunks[].paragraph_index` | `integer` — nullable | Pflicht | The index of the paragraph this chunk belongs to, or `null` outside paragraph mode. |
| `data.page` | `object` | Pflicht | Pagination meta for the CURRENT page of `data.chunks`. |
| `data.page.current_page` | `integer` | Pflicht | The current page number, 1-based. |
| `data.page.per_page` | `integer` | Pflicht | The page size (how many chunks were requested, or the default). |
| `data.page.total` | `integer` | Pflicht | Total chunks in the split (across all pages). |
| `data.page.has_more` | `boolean` | Pflicht | Whether more pages follow the current one. |

**Beispiel**

```json
"string"
```

### 202 — Accepted for processing. The result arrives asynchronously.

**Antwortfelder**

| Feld | Typ | Pflicht | Beschreibung |
| --- | --- | --- | --- |
| `success` | `boolean` | Pflicht | 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` | Pflicht | 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` | `object` | Pflicht | The response payload. |
| `data.status` | `string` — `processing` | Pflicht | Always `processing` for a 202 response — the calculation is running in the background. |
| `data.preview_id` | `string` | Pflicht | The preview id. Use it to subscribe to the `private-chunks.preview.{userId}` WS channel (the `.ready` event), or to poll `GET /orders/chunks/preview/{previewId}` manually. |

**Beispiel**

```json
"string"
```

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

**Antwortfelder**

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

**Beispiel**

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

**Antwortfelder**

| Feld | Typ | Pflicht | Beschreibung |
| --- | --- | --- | --- |
| `success` | `boolean` | Pflicht | Always `false` in error responses. |
| `message` | `string` | Pflicht | Error text in the key's language (set through the `X-Locale` header). |

**Beispiel**

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

### 422 — Validation error (`input_text` is required), or `reason: template_model_outdated` — the template or `config_override` carries settings of the service's previous schema, the same refusal as order creation (paths in `fields`).

**Antwortfelder**

| Feld | Typ | Pflicht | Beschreibung |
| --- | --- | --- | --- |
| `message` | `string` | Pflicht | Errors overview. |
| `errors` | `object` | Pflicht | A detailed description of each field that failed validation. |
| `errors.*` | `array<string>` — beliebige Schlüssel | — |  |

**Beispiel**

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

### 429 — Limit exceeded: either the key's request rate or its token quota. Retry no earlier than `Retry-After`.

**Antwortfelder**

| Feld | Typ | Pflicht | Beschreibung |
| --- | --- | --- | --- |
| `success` | `boolean` | Pflicht | Always `false` in error responses. |
| `message` | `string` | Pflicht | Error text in the key's language (set through the `X-Locale` header). |
| `reason` | `string` | Pflicht | Machine-readable failure reason — branch on this, not on the message text. |
| `error_code` | `string` | Pflicht | The same machine-readable code as in `reason` — a second name for the field. The values always match; branch on either. |
| `retry_after` | `integer` — nullable | — | How many seconds to wait before retrying (the same value is in the `Retry-After` header). |

**Beispiel**

```json
{
  "success": false,
  "message": "Too many attempts. Please wait a moment and try again.",
  "reason": "rate_limit_exceeded",
  "error_code": "rate_limit_exceeded",
  "retry_after": 30
}
```

---

- Diese Seite im Browser: https://lumean.app/de/developers/docs/api/post-orders-chunks-preview
- Vollständige maschinenlesbare Referenz: https://api.lumean.app/docs/llm.md
