Conventions
The response envelope and its single exception, the base URL, four different identifier types and the date format.
Übersetzung in Arbeit
Everything that's the same across all operations in the spec: the response shape, addresses, identifiers and time. Read it once, and it saves you effort on every endpoint afterward.
Response envelope
Success (200/201) and most rejections arrive in the same envelope:
{ "success": true, "message": "<human-readable>", "data": <payload> }
{ "success": false, "message": "<reason>" }In errors, data is either absent or null (401 and 403 send it explicitly).
The errors key is not used in ordinary rejections — it appears only with 422.
Some domain 4xx responses carry machine-readable fields on top of the envelope: reason almost always,
plus shortfall_tokens/quote_token/expires_at with 402, actual_lmc_minor
and quoted_lmc_minor with 409, and window/limit/used/reset_at/retry_after with the token quota.
The exception: 422 arrives outside the envelope
Validation uses the standard Laravel format, and there's no success field in it at all:
{
"message": "<summary message>",
"errors": { "config.tts_settings.voice_id": ["error text", "..."] }
}The response schema declares both fields required — message and errors. The errors keys are
dotted request field paths, not top-level names.
Vorsicht
if (!res.success) on a 422 compares against undefined and behaves unpredictably: the client sees
“not an error” where the order wasn't created. Check the HTTP code, and read success only
after you've made sure the code isn't 422. The spec's introductory text (“all responses are in the format
{ success, message, data, errors? }”) doesn't account for this case.
- Was passiert ist
- The only API response that comes OUTSIDE the common envelope: `{"message": "…", "errors": {"field": ["…"]}}` — it has no `success` field. Code like `if (!res.success)` works on a 422 only by coincidence, because it is comparing against `undefined`.
- Was jetzt zu tun ist
- First check whether `errors` is present. If it is, fix the request according to the listed fields. If it is not, but there is a `reason`, the request shape is not the problem, and repeating the same body gets the same refusal; read the page for that specific code.
Two different sources live under 422. If there's errors, the request shape is broken — fix the fields.
If there's no errors but there is a reason (content_blocked, text_empty, service_not_found…),
the shape isn't the problem, and repeating the same body will get the same rejection.
Base URL and environments
| What | Value |
|---|---|
| REST base | https://api.lumean.app/api/public |
| WebSocket | wss://ws.lumean.app:443, app key 7c4dd881d8116ac76fe89aff71d8c2ff |
| WS auth | POST https://api.lumean.app/api/public/broadcasting/auth |
| OpenAPI | https://api.lumean.app/docs/public-openapi.json (EN: …/public-openapi.en.json) |
| AsyncAPI | https://api.lumean.app/docs/public-asyncapi.json |
There's only one environment: the public API has no sandbox or staging host, and the spec declares
a single server. Test runs go to the same address and are billed the same way —
to check the request shape without being charged, use the free chunking preview
(POST /orders/chunks/preview).
All requests are HTTPS. The body is JSON (Content-Type: application/json), except for file uploads
(multipart/form-data). Responses are JSON in UTF-8.
Identifier types
There are four, each of a different nature. Mixing them up is costly: a client that validates “any id” as a UUID breaks on three of the four.
| Resource | Type | Format |
|---|---|---|
| Order, Template | UUID (string) | 8f14e45f-ceea-467a-9c0e-4d2b9f1b2c3d |
| User, ApiKey, Subscription, Service | integer | 42 |
| External voice (ElevenLabs, HeyGen) | the provider's string voice_id | format set by the provider |
Chunk preview (previewId) | sha256, 64 hex characters, no hyphens | 3f9a1c… |
Three traps in a row:
preview_idis not a resource identifier but a content hash. It is deterministic for the triple “text + template + mode”: the same text gives the samepreview_idand is served from cache instantly — including when that text has already been processed by a web session of the same account (the cache is shared per owner, not per sign-in method). Waiting for a “new id” on a repeated request is pointless.- An external provider's
voice_iddoesn't go into the order. There is noorders.voice_idfield: the voice lives in the template'sconfig.tts_settings.voice_id. - The numeric user
idis needed for WebSocket. The private channel name isprivate-orders.{userId}, and{userId}here is exactly the integer fromGET /user, not the order UUID.
Dates and time
All points in time are ISO-8601 with an explicit offset: 2026-07-08T12:34:56+00:00.
By default this is UTC; the X-Timezone header with an IANA zone name rewrites the same moments
in your zone (more in the Keys, scopes and headers guide).
Calendar dates in Y-m-d format — the boundaries of validity periods — are not converted by zone:
they have no zone.
Pagination in brief
Regular lists put an object in data: { "items": [...], "current_page": 1, "last_page": 5, "per_page": 20, "total": 93 }, with the parameters page (starting at 1) and per_page. External voice
catalogs use their own wrapper { voices, total, page, page_size, has_more },
where page counts from zero.
Stolperfalle
The data.meta field doesn't exist, even though four descriptions in the spec refer to it: the counters
sit next to items, directly in data. This and the other pagination pitfalls are covered in the
Pagination guide.
How to read errors
The order is exactly this:
- HTTP code — it alone distinguishes 422 (outside the envelope) from everything else.
reasonin the body — the machine-readable rejection code. Branch on it and nothing else.errors— only with 422, and only when the request shape is broken: a dictionary of “field path → array of messages”.message— a human-readable string in the key's language. The wording changes without notice, is localized by theX-Localeheader and is not an error code.
There's no catalog of reason codes in the OpenAPI: only four values are described by machine schemas,
and the remaining couple dozen live in prose. Each one is broken down in the error catalog,
which also has a diagnostician you can feed the entire response body to.
Diese Seite einer KI geben
Aktualisiert 04.09.2026 · Spezifikation 0.0.1