Quickstart: from zero to mp3
Five steps from an empty terminal to a downloaded file — voice, template, order, waiting, link.
Übersetzung in Arbeit
Speech synthesis in Lumean is a pipeline of five links, and each link takes an ID from the
previous one's response: voice → template → order → waiting → file. No link can be skipped: the
voice is set only through a template (there is no orders.voice_id field), and the result
is delivered only as a temporary link from POST /storage/url — the order holds paths, not
URLs.
What you need first
An API key with the voices.read, templates.write, orders.write, and orders.download
scopes. You create it in My API keys; every request uses the same header,
X-API-KEY.
Stolperfalle
The built-in automation preset does not include templates.write (only
templates.read), so you cannot complete this walkthrough with it: step 2 will return 403.
Use the full preset, or add templates.write manually when you create the key.
Vorsicht
Step 3 creates a real order: it consumes your subscription quota and is billed like any
other. You can cancel an order after it is created (POST /orders/{order}/cancel), but what was
spent on completed chunks is not refunded — start with one short phrase, not a book chapter.
Durchgängiges Szenario
Die Schritte laufen mit Ihrem Schlüssel. Ohne Schlüssel gibt es nur den Code — kopieren geht trotzdem.
# 1. Stimme wählen → voice_id
curl -X GET 'https://api.lumean.app/api/public/voices/elevenlabs/library?page=0&page_size=20' \
-H 'Accept: application/json' \
-H 'X-API-KEY: YOUR_API_KEY'
# 2. Vorlage erstellen → template_id
curl -X POST 'https://api.lumean.app/api/public/templates' \
-H 'Accept: application/json' \
-H 'X-API-KEY: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"service_key": "elevenlabs",
"name": "API quickstart",
"config": {
"tts_settings": {
"mode": "mode_v1",
"model_id": "eleven_multilingual_v2",
"voice_id": "{{voice_id}}",
"advanced_voice_settings": true,
"voice_settings": {
"stability": 0.5,
"similarity_boost": 0.75,
"use_speaker_boost": true,
"speed": 1.0
}
}
}
}'
# 3. Auftrag erstellen → order_id
curl -X POST 'https://api.lumean.app/api/public/orders' \
-H 'Accept: application/json' \
-H 'X-API-KEY: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"template_id": "{{template_id}}",
"input_text": "Hello from the Lumean public API."
}'
# 4. Auf das Ergebnis warten → path
curl -X GET 'https://api.lumean.app/api/public/orders/{{order_id}}' \
-H 'Accept: application/json' \
-H 'X-API-KEY: YOUR_API_KEY'
# 5. Link zur Datei holen → url
curl -X POST 'https://api.lumean.app/api/public/storage/url' \
-H 'Accept: application/json' \
-H 'X-API-KEY: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"path": "{{path}}",
"download": true
}'- 1
Stimme wählen
Suchen Sie eine Stimme im Katalog und merken Sie sich ihre voice_id — die Vorlage braucht sie.
curl -X GET 'https://api.lumean.app/api/public/voices/elevenlabs/library?page=0&page_size=20' \ -H 'Accept: application/json' \ -H 'X-API-KEY: YOUR_API_KEY' - 2
Vorlage erstellen
Die Stimme wird ausschließlich über die Vorlage gesetzt: voice_id gehört in config.tts_settings.
curl -X POST 'https://api.lumean.app/api/public/templates' \ -H 'Accept: application/json' \ -H 'X-API-KEY: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "service_key": "elevenlabs", "name": "API quickstart", "config": { "tts_settings": { "mode": "mode_v1", "model_id": "eleven_multilingual_v2", "voice_id": "{{voice_id}}", "advanced_voice_settings": true, "voice_settings": { "stability": 0.5, "similarity_boost": 0.75, "use_speaker_boost": true, "speed": 1.0 } } } }' - 3
Auftrag erstellen
Der Text gehört in das oberste Feld input_text, die Vorlage in template_id. Die Antwort ist 201 mit Status created.
Dieser Schritt erzeugt einen echten Auftrag und verbraucht Tokens Ihres Abonnements.
curl -X POST 'https://api.lumean.app/api/public/orders' \ -H 'Accept: application/json' \ -H 'X-API-KEY: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "template_id": "{{template_id}}", "input_text": "Hello from the Lumean public API." }' - 4
Auf das Ergebnis warten
Aufträge sind asynchron: Status abfragen oder per WebSocket zuhören. partially_completed ist NICHT terminal.
curl -X GET 'https://api.lumean.app/api/public/orders/{{order_id}}' \ -H 'Accept: application/json' \ -H 'X-API-KEY: YOUR_API_KEY' - 5
Link zur Datei holen
result.files[] sind Pfade. Den Download-Link liefert POST /storage/url.
curl -X POST 'https://api.lumean.app/api/public/storage/url' \ -H 'Accept: application/json' \ -H 'X-API-KEY: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "path": "{{path}}", "download": true }'
The full script
A ready-made run from voice to file. You need curl and jq; substitute your own KEY.
#!/usr/bin/env bash
set -euo pipefail
BASE=https://api.lumean.app/api/public
KEY=<your_api_key>
AUTH=(-H "X-API-KEY: $KEY" -H "Content-Type: application/json")
# 1. Voice: take the first voice_id from the ElevenLabs public library.
# External provider catalogs are wrapped in data.voices[]; pages start at ZERO.
VOICE_ID=$(curl -s "$BASE/voices/elevenlabs/library?page=0&page_size=20" \
-H "X-API-KEY: $KEY" | jq -r '.data.voices[0].voice_id')
# 2. A TTS template with this voice. tts_settings.mode is required,
# language_code (ISO-639-1) is optional.
TEMPLATE_UUID=$(curl -s -X POST "$BASE/templates" "${AUTH[@]}" -d '{
"service_key": "elevenlabs",
"name": "My TTS",
"config": {
"tts_settings": {
"mode": "mode_v1",
"model_id": "eleven_multilingual_v2",
"voice_id": "'"$VOICE_ID"'",
"advanced_voice_settings": true,
"voice_settings": {
"stability": 0.5, "similarity_boost": 0.75,
"use_speaker_boost": true, "speed": 1.0
}
}
}
}' | jq -r '.data.id')
# 3. An order from the template. The text goes in top-level input_text, NOT in task_data.
ORDER_UUID=$(curl -s -X POST "$BASE/orders" "${AUTH[@]}" -d '{
"template_id": "'"$TEMPLATE_UUID"'",
"input_text": "Привет! Это тестовая озвучка через Lumean API."
}' | jq -r '.data.id')
# → status: "created" — this response has no result, and it cannot have one
# 4. Wait until it is ready. Terminal statuses: completed, result_delivered,
# failed, compensated, cancelled. partially_completed is NOT terminal.
while :; do
STATUS=$(curl -s "$BASE/orders/$ORDER_UUID" -H "X-API-KEY: $KEY" | jq -r '.data.status')
echo "status: $STATUS"
case "$STATUS" in
completed|partially_completed|result_delivered) break ;;
failed|cancelled|compensated) echo "order failed"; exit 1 ;;
esac
sleep 5
done
# 5. File: result.files[] are PATHS. Exchange the path for a temporary link and download it.
PATHNAME=$(curl -s "$BASE/orders/$ORDER_UUID" -H "X-API-KEY: $KEY" \
| jq -r '.data.result.files[0]')
URL=$(curl -s -X POST "$BASE/storage/url" "${AUTH[@]}" \
-d '{ "path": "'"$PATHNAME"'", "download": true }' | jq -r '.data.url')
curl -sL "$URL" -o result.mp3What is easy to get wrong here
| Pitfall | How it actually works |
|---|---|
Looking for voice_id in the order body | The voice lives in the template's config.tts_settings.voice_id; POST /orders has no such field |
Putting the text in task_data.text | For template-based TTS, the text goes in the top-level input_text |
Treating data.result.files[] as links | They are paths; a link is issued by POST /storage/url, and it is short-lived |
Waiting for completed forever | partially_completed is not a terminal status: some chunks are done, others are waiting for your decision |
| Pagination of provider catalogs | For ElevenLabs and HeyGen, page is counted from zero; for regular API lists, from one |
| Leaving the file “for later” | Order storage is purged according to retention (by default, 7 days after a terminal status), while the order keeps showing the paths, now dead |
If you get an error instead of 201
Three codes come up at step 3 more often than others, and each one is handled differently.
- Was passiert ist
- The order was not created and no money was charged. The code is returned by `POST /orders` and `POST /orders/{order}/retry` — they go through the same billing flow. The body always contains `reason`, and that is what determines whether this is an error or an offer to top up.
- Was jetzt zu tun ist
- Branch on `reason`, not on `message`: message wording changes without notice and is not an error code. Do not retry blindly: a retry without confirmation returns a fresh 402 with a new `quote_token`, while a blind retry WITH confirmation charges money without the user knowing.
402 is not a rejection but a payment fork: payg_topup_required brings the top-up price and a
quote_token, which you must send back together with confirm_payg_topup: true. A blind retry
here is either useless or charges money without the user's knowledge — see
LMC, quotas and PAYG.
429 is five different limits under one code; they are told apart by reason and by the
presence of Retry-After (Limits and the 429 code). A 422 without
errors but with reason is a domain rejection: the request shape is not the problem, and
repeating the same body will get the same response.
Next steps
- Keys, scopes and headers — eleven scopes, a wildcard, and three presets.
- The asynchronous order model — nine statuses and what counts as completion.
- Realtime — the same statuses as events, without polling.
- Templates — service config and
GET /templates/config-options. - Voiceover text — pauses, markup, and which of it is not billed.
- Files, links and storage — subtitles in
result.service_files[]and retention. - Conventions — the response envelope and 422 outside it.
- Pagination — two page numbering bases.
- Voices — three catalogs and where to get a
voice_id. - Limits and the 429 code — five limits under one code.
- Tokens, quotas, LMC and PAYG — what you pay for and with what.
- Retries and idempotency — four different kinds of “retry”.
- Scenario operations: POST /orders, POST /templates, POST /storage/url.
Diese Seite einer KI geben
Aktualisiert 04.09.2026 · Spezifikation 0.0.1