Zum Inhalt
Lumean API

Realtime: Ereignisse über WebSocket

Auftragsstatus, Fortschritt beim Stimmklonen und Geschenkcodes müssen nicht abgefragt werden: Der Server sendet Ereignisse über WebSocket. Protokoll ist Pusher, Server ist Reverb, jedes Abo ist privat und wird mit Ihrem API-Schlüssel autorisiert. Unten: Verbindungsparameter, die vier Autorisierungsschritte, die Kanäle und die häufigsten Stolpersteine.

Verbindungsparameter

Die Werte stammen aus der AsyncAPI, nicht aus Fließtext. Host, Port und Schema müssen manuell gesetzt werden: Es gibt keinen Pusher-Cloud-Cluster, der Server ist selbst betrieben.

ParameterWertWas es bedeutet
Hostws.lumean.appAdresse des WebSocket-Servers: wsHost in pusher-js, wsHost und wssHost in Laravel Echo.
Port443Derselbe Wert für wsPort und wssPort — einen separaten Port ohne TLS gibt es nicht.
Schemawsswss bedeutet forceTLS: true. Eine reine ws-Verbindung von einer Produktionsdomain schlägt fehl.
App-Key7c4dd881d8116ac76fe89aff71d8c2ffDie Reverb-Anwendungs-ID. Weder ein Geheimnis noch Ihr API-Schlüssel — alle Clients nutzen denselben Wert.
Abo-Autorisierunghttps://api.lumean.app/api/public/broadcasting/authHierhin sendet der Client socket_id und Kanalnamen, um eine Signatur zu erhalten. Ihr API-Schlüssel wird nur in dieser HTTP-Anfrage übertragen.

Tipp

Der App-Key ist öffentlich: Er steckt im Frontend-Bundle und ist im Browser jedes Pusher-Clients sichtbar. Es gibt genau ein Geheimnis — Ihren API-Schlüssel — und der wird im Header X-API-KEY der Abo-Autorisierung übertragen, nicht über den Socket.

Wie ein Abo autorisiert wird

Einen privaten Kanal kann man nicht einfach mithören. Die Client-Bibliothek durchläuft vier Schritte — und nur wer sie kennt, weiß, an welchem der 403 entstanden ist.

  1. Der Socket wird geöffnet

    Der Client verbindet sich per App-Key mit dem Host. Ihr API-Schlüssel spielt hier noch keine Rolle — der Socket öffnet sich auch mit einem ungültigen Schlüssel.

  2. socket_id trifft ein

    Der Server antwortet mit pusher:connection_established samt socket_id, die genau diese Verbindung identifiziert.

  3. Das Abo wird signiert

    Der Client sendet socket_id und Kanalnamen per POST /broadcasting/auth mit dem Header X-API-KEY. Der Server prüft Schlüssel, Recht und Kanalzugehörigkeit und liefert eine Signatur. Genau hier entstehen 401, 403 und 429.

  4. Abo bestätigt, Ereignisse fließen

    Der Client übergibt die Signatur, der Kanal bestätigt mit pusher:subscription_succeeded — erst danach treffen die Ereignisse des Kanals ein.

orders.readvoices.readreseller.read— die in Schritt drei geprüften Rechte: Jeder Kanal hat sein eigenes, siehe Kanaltabelle unten.

Verbindungs-Snippets

Drei Clients, dieselbe Abfolge. Der Schlüssel im Code ist ein Platzhalter: Setzen Sie Ihren ein, aber committen Sie ihn nicht.

import Pusher from 'pusher-js';

const pusher = new Pusher('7c4dd881d8116ac76fe89aff71d8c2ff', {
  wsHost: 'ws.lumean.app',
  wsPort: 443, wssPort: 443,
  forceTLS: true,
  enabledTransports: ['ws', 'wss'],
  cluster: '',
  channelAuthorization: {
    endpoint: 'https://api.lumean.app/api/public/broadcasting/auth',
    headers: { 'X-API-KEY': 'YOUR_API_KEY' },
  },
});

const channel = pusher.subscribe('private-orders.{userId}');
channel.bind('policy_cooldown.applied', (data) => {
  console.log(data);
});

Kanäle

Die Kanäle sind privat: Die Namen beginnen mit private-, den Parameter in geschweiften Klammern setzen Sie selbst ein. Jeder Kanal erfordert sein eigenes Recht — es steht in der Tabelle.

Zwei Fallen

Das Ereignis .progress gibt es auf zwei Kanälen — mit unterschiedlichem Payload

Gleicher Name, andere Form. Auf dem Auftragskanal des Nutzers zählt es fertige Chunks eines Auftrags; auf dem Kanal eines einzelnen Auftrags ist es der Task-Fortschritt in Prozent samt ETA. Ein Handler für beide Kanäle liest das zweite Ereignis wie das erste und erhält stillschweigend undefined.

// private-orders.{userId} → .progress
{ "order_id": "…", "completed": 3, "total": 7, "percent": 43 }
// private-order.task.{orderId} → .progress
{ "task_id": "…", "progress": 43, "chunks_done": 3, "chunks_total": 7, "eta_seconds": 12 }

Trennen Sie Handler nach Kanal, nicht nach Ereignisnamen. Die vollständigen Feldtabellen stehen auf den Kanalseiten.

Der führende Punkt: in Echo nötig, in pusher-js falsch

Laravel Echo stellt Ereignisnamen den Anwendungs-Namespace voran; der führende Punkt hebt das auf. pusher-js kennt keinen Namespace, dort steht der Name ohne Punkt. Verwechselt man beides, funktioniert das Abo, es kommen keine Ereignisse — und keine Fehlermeldung.

echo.private(channel).listen('.status.changed', handler)
channel.bind('status.changed', handler)

Autorisierungsfehler

Alle drei kommen als HTTP-Antwort auf POST /broadcasting/auth, nicht über den Socket. Der Socket ist zu diesem Zeitpunkt bereits offen — „verbunden“ sagt daher nichts über Ihre Rechte aus.

CodeWann es auftrittWas zu tun ist
401Der Header X-API-KEY fehlt, oder der Schlüssel ist widerrufen bzw. unvollständig kopiert.Prüfen Sie den Schlüssel mit GET /user. Die Bibliothek erhält ihn in den Headern der Abo-Autorisierung, nicht im Konstruktor.
403Der Schlüssel funktioniert, hat aber nicht das Recht für diesen Kanal (orders.read für Aufträge, voices.read für Stimmklone, reseller.read für Geschenkcodes) — oder Sie abonnieren einen fremden Kanal: userId und orderId im Namen müssen dem Schlüsselinhaber gehören.Ergänzen Sie das Recht im Konto und setzen Sie Ihre eigene Kennung ein. Ein fremder Auftrag wird nie ausgeliefert.
429Zu viele Autorisierungsversuche hintereinander — meist eine Reconnect-Schleife mit falschem Schlüssel.Stoppen Sie den Reconnect, korrigieren Sie den Schlüssel, verbinden Sie neu. Exponentielles Backoff hilft, die Ursache ist aber fast immer der 401 eine Zeile darüber.

POST /broadcasting/auth

Dieser Endpunkt fehlt bewusst in der Referenz: Niemand ruft ihn von Hand auf, das erledigt die Client-Bibliothek. Wenn ein Abo aber 403 liefert, debuggen Sie genau diese Anfrage — deshalb steht sie hier.

POST/broadcasting/authorders.read

Authorizes a subscription to a private WebSocket channel using your API key. Client libraries (Laravel Echo, pusher-js) call this endpoint themselves — point their authEndpoint at it and add X-API-KEY to the auth headers.

Available channels are your own orders (private-orders.{userId}, private-order.task.{orderId}) and chunk-preview results (private-chunks.preview.{userId}, see POST /orders/chunks/preview) — permission orders.read — plus your Cartesia voice clones (private-cartesia-voice-clones.{userId}, see POST /voices/cartesia/clones) and Fish Audio voice clones (private-fishaudio-voice-clones.{userId}, see POST /voices/fishaudio/clones) — permission voices.read. Someone else's channel returns 403.

Subscribing removes the need to poll GET /orders/{order}: progress and completion arrive as events.

Required key permission: orders.read.

Anfrage-Body

socket_iderforderlich

The WebSocket connection id delivered to the client in the pusher:connection_established event.

string
Beispiel 12345.67890
channel_nameerforderlich

Channel name. A key may only reach its own channels: orders private-orders.{userId} and private-order.task.{orderId}, chunk previews private-chunks.preview.{userId} (permission orders.read), Cartesia voice clones private-cartesia-voice-clones.{userId} and Fish Audio voice clones private-fishaudio-voice-clones.{userId} (permission voices.read), reseller gift codes private-giftcodes.{userId} (permission reseller.read).

string
Beispiel private-orders.42

Mögliche Antworten

200

Subscription authorized. The client receives the subscription auth token.

Beispiel für den Body
{
  "auth": "a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890"
}
Felder der Antwort
autherforderlich

The channel subscription signature. Hand it back to the Pusher/Echo client verbatim — it fills the value in itself. This response carries no envelope: the protocol dictates the shape.

string
Beispiel a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890
401

The key is missing, unknown, disabled or expired.

Diese Antwort analysieren
Beispiel für den Body

Diese Antwort hat keinen Body.

403

The key lacks the permission the channel requires (orders.read for orders and chunk previews, voices.read for Cartesia and Fish Audio voice clones), or the channel belongs to someone else.

Diese Antwort analysieren
Beispiel für den Body

Diese Antwort hat keinen Body.

422

Invalid socket_id/channel_name (for example a channel_name without the private- prefix).

Diese Antwort analysieren
Beispiel für den Body

Diese Antwort hat keinen Body.

429

Rate limit exceeded — 1200 requests per minute.

Diese Antwort analysieren
Beispiel für den Body

Diese Antwort hat keinen Body.

Authentifizierung und Rechte·Leitfaden: Realtime statt Polling

Diese Seite einer KI geben

.md öffnenllm.md

Spezifikation 0.0.1