Zum Inhalt
Lumean API

Retries and idempotency

The API has no idempotency keys. Which of the four “retries” is free, which is billed as a new order, and which is pointless to call at all.

Übersetzung in Arbeit

Diese Seite ist noch nicht in Ihre Sprache übersetzt — die Fassung unten ist auf English. Text und Codebeispiele sind dieselben.

The public API has no idempotency keys. There is no Idempotency-Key header, no deduplication field in the POST /orders body, and no server that would recognize a repeated submission. Two identical requests mean two orders and two funds holds. Everything below follows from this.

Four “retries” you must not confuse

CallWorks onWhat it doesCost
POST /orders/{order}/items/retry-failedall failed chunks at oncequeues them for retry in a single call: 202 + a queued/skipped planfree, from the held remainder
POST /orders/{order}/items/{item}/retrya single failed or policy_flagged chunkretries the chunk, optionally with new textfree within free_length
POST /orders/{order}/items/{item}/regeneratea completed chunkredoes a finished fragment with new textpaid, new quota
POST /orders/{order}/retrythe whole ordercreates a new order with the same parameterspaid, like creation

The first three work on an order in partially_completed status; the last one, by contrast, does not accept that status.

Order-level retry does not mean “try again”

POST /orders/{order}/retry does not revive the order. It reads its parameters and creates a new order from them: a new id, a new funds hold, a new token quota check. It is billed in full: this is a second order, not a second attempt.

Conditions: the original order is in completed or result_delivered status and has no failed chunks. The body is the same as for creation: empty in the normal case, with confirm_payg_topup + quote_token only after a 402. The billing flow and the codes are the same: 402 payg_topup_required, 409 quote_mismatch, 429 for the token quota.

It fits exactly one scenario: “I need another identical result”, for example when the files were deleted under the retention policy. It is unsuitable for completing a partially_completed order, both technically and economically.

Playbook: partially_completed → completed

Step 1. One call picks up all technical failures. The body is empty.

curl -X POST 'https://api.lumean.app/api/public/orders/{order}/items/retry-failed' \
  -H 'Accept: application/json' \
  -H 'X-API-KEY: YOUR_API_KEY'

A 202 response means the plan was accepted and the work runs in the background. data contains queued[] (what was queued) and skipped[] with a machine-readable reason for each skipped chunk:

reason in skipped[]What to do
policy_flaggedRetry the chunk individually with corrected text (step 2)
max_attempts_reachedThere were 5 attempts and there won't be a sixth; only a new order will do
insufficient_fundsNo held remainder is left; only a new order will do
active_retry_existsA retry is already in progress; wait for it
supersededThe chunk already has a successful version; it is healthy, nothing to do

Next to it is reason_label: the same code in words, in the account's language. Don't parse it programmatically: the wording changes whenever the localization is edited.

Step 2. Each policy_flagged chunk individually, with different text.

curl -s "$BASE/orders/<ORDER>/items/<ITEM>/text" -H "X-API-KEY: $KEY"
curl -s -X POST "$BASE/orders/<ORDER>/items/<ITEM>/retry" -H "X-API-KEY: $KEY" \
  -H "Content-Type: application/json" -d '{"text": "<corrected text>"}'

GET .../items/{item}/text returns the limits along with the text: free_length (within it a retry is free), max_billable_length, max_raw_length (the technical ceiling for a chunk; beyond it you get 422), and the surcharge_applies flag, meaning “length above the norm incurs a surcharge”. The original_text_length field is a synonym for free_length, kept for compatibility.

Hinweis

The LLM reference puts it more strictly: “text length ≤ original_text_length”. The spec allows longer text, it's just paid; go by surcharge_applies and check it before sending so the charge doesn't come as a surprise.

Step 3. Wait. A bulk retry has no synchronous result: poll GET /orders/{order}/items or listen for the bulk_retry.completed event.

422invalid_order_statusinvalid_order_status — order is in the wrong status
Was passiert ist
An order-level refusal of bulk retry: `POST /orders/{order}/items/retry-failed` → 422 `{"success": false, "message": "…", "reason": "invalid_order_status"}`. No chunks were queued.
Was jetzt zu tun ist
Check the order status before calling. Neighboring refusals from the same endpoint differ by `reason`: 409 `stitch_in_progress` (the order is being stitched — wait and retry), 422 `generative_not_supported` (`sfx`/`music`: they have no single-chunk retry), 422 `nothing_to_retry` (no failed chunks were found).
Code-Seite öffnen

retry vs. regenerate

retry accepts only a failed chunk (failed or policy_flagged); text is optional, and without it the same text is retried. It is free within the length already paid for (free_length); longer text incurs a surcharge.

regenerate accepts only a finished (completed) chunk, text is required, and it is always paid: new funds are held, the quota is pre-checked, and if the quota is exceeded you get 429.

Neither is available for sfx and music: their generation is one-shot.

Stolperfalle

The can_retry / can_regenerate flags are not a guarantee. The spec claims that can_retry accounts for the status, the task type, and exhausted attempts; llm.md §7.11 refutes this: the flag looks only at the chunk status. can_retry: true happily coexists with a 400 on the actual call.

Retrying content_blocked is pointless

422content_blockedcontent_blocked — text is on the blocklist
Was passiert ist
The order was not created. Body: `{"success": false, "message": "…", "reason": "content_blocked"}` — WITHOUT `errors`, because the request shape has nothing to do with it. Single-chunk retry and chunk regeneration return the same `reason`.
Was jetzt zu tun ist
Retrying is pointless and costly: the outcome is deterministic, and every attempt consumes resources and moves the counter toward `policy_cooldown_active`. The only action that works is to edit the offending fragment; because matching is per chunk, it is enough to fix that one fragment without rewriting the whole text. The API does not tell you why the policy triggered: that is admin-only, there is no reason field in the response and there will not be one — do not look for it.
Code-Seite öffnen

The text has already been rejected by the policy and is on the global blocklist: a retry returns the same 422. Worse, every attempt moves the counter toward policy_cooldown_active: five blocked tasks in a row stop new ones from being created for 5 minutes, then for 10 and 15.

Richtig

Edit the problematic fragment. Matching is done per chunk and by exact match, so in a long text it's enough to fix one piece instead of rewriting everything.

Falsch

Put the same text into a retry queue with exponential backoff. After five attempts the whole account gets a cooldown, and the order still won't be created.

The API doesn't return the block reason in any field, so don't look for it.

Network drop after POST /orders

There is no response: the order may or may not have been created. A blind retry here is exactly what causes duplicates and double charges.

You need to check before retrying, with GET /orders. There are two traps: the list has no time filter at all (only status, template_id, task_type[], page, per_page), and the spec doesn't document the sort order. Fetch the first page and compare created_at yourself.

There is one way to make this check easier in advance: put your own request identifier in the name field. It affects nothing except the file name, but it makes the order unambiguously identifiable in the list.

# at creation
-d '{"template_id": "…", "input_text": "…", "name": "job-2026-09-04-0041"}'
# after a drop, before retrying: look for this name on the first page of the list
curl -s "$BASE/orders?per_page=20" -H "X-API-KEY: $KEY"

There is no server-side deduplication by name: this is your client's own convention. It is also another argument for subscribing to private-orders.{userId}: the created event arrives regardless of whether the HTTP response reached you.

Diese Seite einer KI geben

.md öffnenllm.md

Aktualisiert 04.09.2026 · Spezifikation 0.0.1