---
title: "The asynchronous order model"
url: https://lumean.app/de/developers/docs/guide/async
locale: de
description: "POST /orders answers 201 and brings no result. Nine statuses, five terminal ones, and one that looks final but isn't."
updated: 2026-09-04
source_locale: en
sources: [post-orders, get-orders, get-orders-order, get-orders-order-items, post-orders-order-cancel]
errors: [invalid-order-status, policy-flagged, service-not-found, storage-expired]
spec_version: 0.0.1
---

# The asynchronous order model

`POST /orders` doesn't generate anything. It validates the request, locks the funds and the token quota,
queues the task and answers **201 with status `created`** — in this response `result`
is `null`, and there are no files and cannot be any. The result appears minutes later, and finding out
about it is a separate job for the client.

## What the 201 response contains

| Field | What it holds at creation |
| --- | --- |
| `id` | The order UUID. The only thing you must store: every call goes through `/orders/{order}/…` |
| `status` | `created` (the operation description also allows `pending` — right after the task is queued) |
| `result` | `null`. Neither `files[]` nor `service_files[]` is present in the creation response |
| `total_chunks` | How many fragments the server cut the text into. `0` means splitting hasn't happened yet |
| `completed_chunks`, `progress_percent` | Zeros |
| `eta_seconds`, `eta_at` | The estimate exists while the order is running; for an order that hasn't started and for a finished one it is `null` (in spec examples it is sometimes already filled in the 201 response) |
| `price_units` | The locked amount in LMC minor units (the chunk field with the same name holds tokens) |

### POST /orders

Create Order

Справочник: https://lumean.app/de/developers/docs/api/post-orders

## Four order types, not nine

`POST /orders` requires **one of two**: `template_id` or `task_type`. A template defines the service
and all the settings — that's how TTS is created. A bare `task_type` works only where the service code
matches the task type.

| `task_type` | How it's created | Where the parameters go | Files in `result.files[]` |
| --- | --- | --- | --- |
| `tts` | only via `template_id` | `input_text` at the request root | 1 (the merged track) + subtitles in `service_files[]` |
| `sfx` | without a template | `task_data.text`, plus `duration_seconds`, `prompt_influence`, `loop`, `output_format` | exactly 1 |
| `music` | without a template | `task_data.prompt`, plus `n_variants` (1..4), `music_length_ms`, `lyrics_text`, `force_instrumental` | `n_variants` files, not merged |
| `voice_search` | without a template | audio in `input_files` + `task_data.top` (1..50) | no files: the result is a list of similar voices |

The description key for SFX is `text`, for Music it's `prompt`; they're easy to mix up, and the rejection will
say “field is required”. Put the keys **at the root** of `task_data`, without the `sfx_settings` /
`music_settings` wrappers from the [configuration reference](/developers/docs/api/get-templates-config-options):
in a template the wrapper gives `422`, in an order it is silently dropped.

```bash
curl -s -X POST "$BASE/orders" -H "X-API-KEY: $KEY" -H "Content-Type: application/json" -d '{
  "task_type": "music",
  "task_data": { "prompt": "Lo-fi hip hop, calm, rainy night", "n_variants": 3,
                 "music_length_ms": 30000, "force_instrumental": true }
}'
```

The price here depends on the **number of generations, not their size**: `sfx` and `voice_search` are one
rate per order, `music` is the rate x `n_variants`. A five-minute track costs exactly as much
as a ten-second one. The server mixes the service's `default_config` in on top of your values
(disabled with the `skip_service_defaults: true` flag), so the order's saved `task_data`
will contain keys you never sent.

> [!WARNING]
> The `TaskType` schema in the spec lists eight values, and `voice_search` isn't among them — even though the type
> is accepted and orders of that type get created (the enum of the `task_type` field itself in the `POST /orders` body
> has nine values). The flip side: `image`, `txt2img`, `remix`, `image_edit` and `voice_clone`
> pass validation but fail at service resolution with `422` `service_not_found` — they can't be
> created with an API key. As a filter, `GET /orders?task_type[]=…` accepts all nine values; only
> the four from the table above can be created.

### 422 · `service_not_found` — service_not_found — no such service

| Что произошло | Что делать |
| --- | --- |
| 422 with `reason: "service_not_found"` and without `errors`: the request passed shape validation but failed at service resolution. The order was not created. | Check against the list of creatable types and do not send the rest. The same `reason` appears on `GET /usage/{service}` as a 404: there, `service` is the `service_code` from `GET /usage`, not an arbitrary string. |

`Retry-After`: нет. Разбор: https://lumean.app/de/developers/docs/errors/service-not-found

## Nine statuses

Значения `OrderStatus` — в справочнике объектов: https://lumean.app/de/developers/docs/objects#o-OrderStatus

**Terminal — five:** `completed`, `result_delivered`, `failed`, `compensated`,
`cancelled`. An order in these statuses no longer changes, polling it further is pointless, and file
retention (7 days by default) is counted from the moment of this transition.

**Non-terminal — four:** `created`, `pending`, `in_progress`, `partially_completed`.

> [!WARNING]
> The description of the `Order.status` field in the spec lists only four terminal statuses and spells
> `canceled` with one `l`. The fuller list from the `GET /orders` description is correct: there are five terminal
> statuses, `result_delivered` among them. In code, rely on the enum — there and in all response examples
> the value is spelled `cancelled`, with two `l`s; the single-l spelling appears only in prose.

```text
Девять статусов заказа. Опрашивать дальше имеет смысл только в НЕтерминальных.

АКТИВНЫЕ (не терминальные)
  created  →  pending  →  in_progress
                              │
                              ├─→ completed             ТЕРМИНАЛЬНЫЙ
                              │      └─→ result_delivered   ТЕРМИНАЛЬНЫЙ
                              │
                              ├─→ partially_completed   НЕ ТЕРМИНАЛЬНЫЙ ← ловушка:
                              │      часть чанков готова, сбойные ждут
                              │      POST /orders/{order}/items/retry-failed
                              │
                              ├─→ failed                ТЕРМИНАЛЬНЫЙ
                              │      └─→ compensated        ТЕРМИНАЛЬНЫЙ
                              │
                              └─→ cancelled             ТЕРМИНАЛЬНЫЙ

Отменить (cancelled) можно из любого активного статуса, а не только из in_progress.
Терминальные: completed, result_delivered, failed, compensated, cancelled.
```

| Status | Terminal | What to do next |
| --- | --- | --- |
| `created`, `pending` | no | Wait. While the order is here, you can still cancel it |
| `in_progress` | no | Wait, watch `completed_chunks` / `progress_percent` |
| `partially_completed` | **no** | Finished files can already be collected; failed chunks still need to be brought to completion |
| `completed` | yes | Collect `result.files[]` via `POST /storage/url` without delay |
| `result_delivered` | yes | Same: the files are still there until retention expires |
| `failed`, `compensated` | yes | `result` = `null`, no error details in the user context. You need a new order |
| `cancelled` | yes | The order was withdrawn via `POST /orders/{order}/cancel`; funds and token quota were returned |

The spec doesn't explain the difference between `failed` and `compensated`; what they share is `result: null`.
Cancellation is accepted only in `created`, `pending` and `partially_completed`, otherwise `400`.

## The trap: `partially_completed` is not final

An order moves to `partially_completed` when some of its chunks failed: the finished fragments
are in place, the failed ones await your decision. A handler that treats everything except
`created`/`pending`/`in_progress` as terminal will quietly abandon such an order half-done — even though
it can be brought to `completed` with two calls.

### 202 · `policy_flagged` — policy_flagged — chunk rejected by policy

| Что произошло | Что делать |
| --- | --- |
| This is not an HTTP error. The code arrives in two ways: as the value of `OrderItem.status`, and as the `reason` in the `skipped[]` array of the 202 response to `POST /orders/{order}/items/retry-failed`. The order itself moves to `partially_completed` — a NON-terminal status: completed chunks stay in place, failed ones wait for action. | Fetch the current text of the chunk, rephrase the offending fragment, and retry the chunk individually with the corrected text. The rejection reason is not exposed in any field — edit at your own discretion. Retry constraints: the new text must be ≤ `original_text_length`, no more than 5 attempts, and single-chunk retry is not available at all for `sfx` and `music`. The order-level `POST /orders/{order}/retry` will not help here — it does not accept `partially_completed`. |

`Retry-After`: нет. Разбор: https://lumean.app/de/developers/docs/errors/policy-flagged

The playbook for finishing it is in the [Retries and idempotency](/developers/docs/guide/retries) guide.
The order-level `POST /orders/{order}/retry` won't do for this: it doesn't accept
`partially_completed` and creates a new paid order.

## Chunks

Text is split into chunks (`OrderItem`) before generation, and the work proceeds chunk by chunk. “Chunk” and “fragment”
mean the same thing in this documentation: in API fields they are `chunk_index`, `total_chunks`,
`completed_chunks`. A chunk has its own
statuses that don't match the order statuses: `pending`, `processing`, `completed`,
`failed` (a technical error) and `policy_flagged` (rejected by the content policy).

The list comes in `items[]` of `GET /orders/{order}`; the `GET /orders` list doesn't have this field.
Retries and regenerations live as **separate records** with the same `chunk_index` and a
`parent_item_id` reference, so the length of `items[]` doesn't equal `total_chunks`. Calculate progress from
`completed_chunks` / `total_chunks` — only the original chunks count there, otherwise the percentage
would climb above a hundred.

### GET /orders/{order}/items

List Order Items

Справочник: https://lumean.app/de/developers/docs/api/get-orders-order-items

## Readiness: subscribe or poll

**Правильно**

Subscribe to `private-order.task.{orderId}` (a single order) or `private-orders.{userId}`
(all orders of the account) and listen for `status.changed`, `item.status.changed` and `progress`.
The event arrives at the moment of the transition, with no extra requests at all.

**Неправильно**

Poll `GET /orders/{order}` once a second until it's done. That's dozens of useless
requests per order, the risk of hitting the key's rate limit (which has no `Retry-After`),
and still up to a second of delay on the most important transition.

Polling isn't forbidden and works perfectly well for short orders — just do it carefully:
an interval of 2–5 seconds (the spec doesn't set a rate, but every poll consumes the key's window),
base the pause on `eta_seconds` / `eta_at`, check a dozen orders with a single
`GET /orders?status=in_progress` instead of N requests, and always set a cap on attempts —
`failed` is an answer too.

Bulk chunk retry is the same case: `202` means “plan accepted”, and the result arrives
later via the `bulk_retry.completed` event or by polling `GET /orders/{order}/items`.

> [!TIP]
> The OpenAPI introduction knows two order channels out of three. There are three: `private-orders.{userId}`,
> `private-order.task.{orderId}` and `private-chunks.preview.{userId}`; the voice clone and gift code
> channels are not about orders — see the Realtime section.

## Files appear later

`result.files[]` are **paths**, not links: you exchange them for a temporary URL via
`POST /storage/url`, and it's best to do so right at `completed` — the files live only until retention.
How to tell a live path from a dead one is covered in the
[Files, links and storage](/developers/docs/guide/files) guide.

---

- Diese Seite im Browser: https://lumean.app/de/developers/docs/guide/async
- Vollständige maschinenlesbare Referenz: https://api.lumean.app/docs/llm.md
