> ## Documentation Index
> Fetch the complete documentation index at: https://docs.avaluma.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Einen Avaluma-Agent in deine Webseite einbetten

> Bette einen freigegebenen Avaluma-Agent per iframe in jede Webseite ein und passe Verhalten und Aussehen über Query-Parameter an.

Jeder freigegebene Agent lässt sich als `<iframe>` in eine beliebige Webseite einbetten. Der Avatar verbindet sich dann direkt aus dem Browser deiner Besucher mit dem Agenten — auf deiner Seite ist kein Backend nötig.

## Voraussetzungen

<Steps>
  <Step title="Embedding aktivieren">
    Öffne den Agenten im [Dashboard](https://avaluma.ai) und aktiviere **Einbettung erlauben**. Nur freigegebene Agenten können eingebettet werden. Das Umschalten dieses Schalters wird **sofort gespeichert** — du kannst direkt testen, ohne den Agenten erst manuell zu speichern.
  </Step>

  <Step title="Origin freigeben">
    Trage unter **Erlaubte Origins** die Adresse(n) der Seite(n) ein, auf denen der Avatar laufen darf — z. B. `https://www.deine-domain.de`.

    <Warning>
      Steht der Origin der einbettenden Seite **nicht** auf der Liste, blockiert der Browser den iframe (über `frame-ancestors`) und alle [Messaging](#messaging)-Befehle werden abgewiesen. Trage exakt den Origin ein (Schema + Host + Port), ohne Pfad.
    </Warning>
  </Step>

  <Step title="Snippet kopieren">
    Kopiere den fertigen Einbettungs-Code aus dem Dashboard oder baue ihn wie unten beschrieben selbst zusammen.
  </Step>
</Steps>

## Schnellstart

```html theme={null}
<iframe
  src="https://avaluma.ai/agent/AGENT_ID/embed"
  allow="microphone; autoplay"
  style="width:100%;height:600px;border:0"
></iframe>
```

Ersetze `AGENT_ID` durch die UUID deines Agenten (im Dashboard sichtbar).

<Note>
  Das Attribut `allow="microphone; autoplay"` ist erforderlich: Ohne `microphone` kann der Besucher nicht sprechen, ohne `autoplay` startet die Audiowiedergabe in manchen Browsern nicht automatisch (es greift dann das „Ton aktivieren"-Overlay).
</Note>

<Tip>
  Du willst nicht erst eine eigene Seite aufsetzen? Nutze die eingebaute **[Embedding-Testseite](https://avaluma.ai/embed-test)** auf `avaluma.ai/embed-test`: Agent-UUID eintragen, einbetten und die postMessage-Brücke (Handshake, Text senden, Mikrofon ein/aus, Event-Log) direkt ausprobieren. Trage dafür einmalig `https://avaluma.ai` bei den **Erlaubten Origins** des Agenten ein.
</Tip>

## Startbildschirm-Vorschau

Bevor der Besucher „Gespräch starten" klickt, ist der echte Avatar noch nicht verbunden — es wird also keine Session aufgebaut und nichts gesprochen. Hat der **Avatar** des Agenten ein Vorschauvideo hinterlegt, zeigt der Startbildschirm dieses Video als **stumme Endlosschleife** (vor und zurück), bis der Besucher startet. Ohne hinterlegtes Video bleibt der Startbildschirm einfarbig (siehe `border`).

Das Vorschauvideo wird am Avatar konfiguriert und gilt automatisch für jeden Agenten, der diesen Avatar nutzt — du musst dafür nichts am Einbettungs-Code ändern. Empfohlen ist ein kleines, bereits als Boomerang (vor + zurück) exportiertes **MP4**.

## Vollbild-Displays

Die Einbettung rendert den Avatar immer **inline**, innerhalb des iframe-Containers, den du auf deiner Seite platzierst. Für ein eigenständiges Vollbild-Display — etwa einen unbeaufsichtigten Aufsteller — nutze statt eines iframes den dedizierten **[Kiosk-Modus](/de/agents/kiosk)**. Er wird direkt als Top-Level-Seite geöffnet (kein iframe) und ergänzt einen Display-Wachhalter (Wake-Lock), einen Auto-Reset bei Inaktivität und eine Startbildschirm-Web-App für echtes Vollbild.

## Query-Parameter

Alle Optionen werden als Query-Parameter an die `/embed`-URL gehängt. Ungültige Werte fallen automatisch auf den Standard zurück.

<ParamField query="border" type="string" default="000000">
  Hintergrund-/Letterbox-Farbe als 6-stelliger Hex-Wert **ohne** `#` (z. B. `f5f5f5`).
</ParamField>

<ParamField query="fit" type="string" default="contain">
  Darstellung von Avatar-Vorschau und Live-Stream im iframe. `contain` zeigt das vollständige Bild (ggf. mit schmalen Rändern in der `border`-Farbe); `cover` füllt die Fläche formatfüllend aus und beschneidet die Ränder. Gilt für das Startbildschirm-Video und den laufenden Avatar gleichermaßen.
</ParamField>

<ParamField query="lang" type="string" default="de">
  UI-Sprache der Bedienelemente. `de` oder `en`.
</ParamField>

<ParamField query="hideTextChat" type="boolean" default="false">
  Mit `true` wird die Text-Chat-Eingabe ausgeblendet (reiner Sprach-/Avatar-Modus).
</ParamField>

<ParamField query="endCall" type="boolean" default="false">
  Mit `true` wird der „Auflegen"-Button eingeblendet.
</ParamField>

<Note>
  Das Mikrofon ist während eines Gesprächs dauerhaft offen. Möchtest du es von deiner Seite aus stummschalten oder wieder aktivieren (z. B. ein eigener Push-to-Talk-Button), steuere es per `postMessage` — siehe [Messaging](#messaging).
</Note>

### Beispiel mit mehreren Optionen

```html theme={null}
<iframe
  src="https://avaluma.ai/agent/AGENT_ID/embed?border=f5f5f5&lang=en&hideTextChat=true"
  allow="microphone; autoplay"
  style="width:100%;height:600px;border:0"
></iframe>
```

## Ton & Lautstärke

Wie im Kiosk optimiert der eingebettete Agent die Wiedergabe automatisch: Auf iPad/iPhone läuft der Ton in voller Lautstärke, auf Android schaltet er auf den lauten Systemlautsprecher. Über ein **Zahnrad-Symbol** oben rechts im iframe lassen sich Audioverarbeitung und Ausgabe-Gerät anpassen — die Details stehen im Kiosk-Abschnitt [Ton & Lautstärke](/de/agents/kiosk#ton--lautstärke).

## Messaging

Über das reine Einbetten hinaus kannst du den Avatar programmatisch steuern und auf sein Verhalten reagieren. Ein eingebetteter Agent läuft in einem `<iframe>` — deine Seite und der Avatar leben also in unterschiedlichen Browser-Kontexten. Sie kommunizieren über die standardisierte [`window.postMessage`](https://developer.mozilla.org/docs/Web/API/Window/postMessage)-API: Du **sendest Nachrichten** an den iframe und **empfängst Events** vom iframe.

Alle Nachrichten tragen das Präfix `avaluma:`, damit fremde Messages (z. B. von Drittanbieter-Skripten) sicher ignoriert werden.

<Warning>
  Messaging funktioniert nur, wenn der Origin deiner Seite unter **Erlaubte Origins** des Agenten eingetragen ist (siehe [Voraussetzungen](#voraussetzungen) oben). Nachrichten von oder an einen nicht erlaubten Origin werden vom Browser abgewiesen.
</Warning>

### Referenz auf den iframe holen

Gib deinem Embed eine `id`, damit du sein `contentWindow` ansprechen kannst:

```html theme={null}
<iframe
  id="avaluma-agent"
  src="https://avaluma.ai/agent/AGENT_ID/embed"
  allow="microphone; autoplay"
  style="width:100%;height:600px;border:0"
></iframe>
```

```js theme={null}
const frame = document.getElementById("avaluma-agent");
const AVALUMA_ORIGIN = "https://avaluma.ai";
```

### Ablauf (Handshake)

Die Brücke meldet sich bei jedem (Re-)Mount mit einem `ready`-Beacon. Die Parent-Seite bestätigt mit `init` und registriert damit ihren Origin als Ziel für ausgehende Events.

<Steps>
  <Step title="ready empfangen">
    Sobald die Brücke im iframe bereit ist, sendet sie `{ type: "avaluma:ready" }`.
  </Step>

  <Step title="init zurücksenden">
    Die Parent-Seite antwortet mit `{ type: "avaluma:init" }`. Erst dadurch kennt die Brücke den Ziel-Origin und kann Events zustellen.
  </Step>

  <Step title="senden & empfangen">
    Ab jetzt kann die Seite `sendMessage` schicken und empfängt `connected`, `agentMessage` und `disconnected`.
  </Step>
</Steps>

<Tip>
  Reagiere immer auf **jeden** `ready`-Beacon mit `init`. Nach „Auflegen" und erneutem Verbinden startet die Brücke neu und sendet erneut `ready` — durch das wiederholte `init` laufen die Events nahtlos weiter.
</Tip>

### Nachrichten an den Avatar senden

Sende eine Nachricht an das `contentWindow` des iframes. Übergib immer den Avaluma-Origin als zweites Argument, damit die Nachricht nur an den Avatar zugestellt wird:

```js theme={null}
function sendToAvatar(message) {
  frame.contentWindow.postMessage(message, AVALUMA_ORIGIN);
}

// Handshake abschließen (auf jeden ready-Beacon antworten)
sendToAvatar({ type: "avaluma:init" });

// Text an den Agenten senden (erfordert eine aktive Konversation)
sendToAvatar({ type: "avaluma:sendMessage", text: "Hallo!" });

// Mikrofon aus- / wieder einschalten
sendToAvatar({ type: "avaluma:setMicrophone", enabled: false });
sendToAvatar({ type: "avaluma:setMicrophone", enabled: true });

// Konversation programmatisch beenden (wie „Auflegen")
sendToAvatar({ type: "avaluma:disconnect" });
```

| Nachricht               | Payload       | Wirkung                                                                                                                                                                                                                                                                                                                                  |
| ----------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `avaluma:init`          | —             | Registriert den Origin der Parent-Seite und schließt den Handshake ab. Auf jeden `ready`-Beacon senden.                                                                                                                                                                                                                                  |
| `avaluma:sendMessage`   | `{ text }`    | Sendet Text an den Agenten — als hätte der Nutzer ihn ins Chat-Feld getippt. Leere Nachrichten werden ignoriert.                                                                                                                                                                                                                         |
| `avaluma:setMicrophone` | `{ enabled }` | Schaltet das Mikrofon des Besuchers an (`true`) oder aus (`false`). Beim Ausschalten wird das Mikrofon nur stummgeschaltet, bleibt aber geräteseitig geöffnet — der Mikrofon-Indikator bleibt an, das Wiedereinschalten wirkt dadurch sofort. Damit baust du z. B. einen eigenen Push-to-Talk-Button auf deiner Seite auf.               |
| `avaluma:disconnect`    | —             | Beendet die laufende Konversation sofort — als hätte der Besucher auf „Auflegen" geklickt. Der Avatar trennt sich und die Brücke meldet anschließend `avaluma:disconnected`. Nützlich, wenn du das iframe selbst schließt oder minimierst, damit die Session nicht im Hintergrund weiterläuft. Ohne aktive Konversation passiert nichts. |

<Note>
  Eine Konversation startet der Besucher selbst über „Gespräch starten" im iframe. `avaluma:sendMessage` wirkt nur, solange eine Konversation aktiv ist.
</Note>

### Events vom Avatar empfangen

Lausche auf `message`-Events am `window`. **Prüfe immer `event.origin`**, bevor du den Daten vertraust:

```js theme={null}
window.addEventListener("message", (event) => {
  if (event.origin !== AVALUMA_ORIGIN) return;

  const data = event.data;
  if (!data || typeof data.type !== "string") return;

  switch (data.type) {
    case "avaluma:ready":
      // Brücke ist bereit → Handshake bestätigen
      sendToAvatar({ type: "avaluma:init" });
      break;
    case "avaluma:connected":
      console.log("Konversation gestartet");
      break;
    case "avaluma:disconnected":
      console.log("Konversation beendet");
      break;
    case "avaluma:agentMessage":
      // Gestreamt: gleiche id, wachsender text; final === true am Ende
      if (data.final) console.log("Agent:", data.text);
      break;
  }
});
```

| Event                  | Daten                 | Bedeutung                                                                                                                   |
| ---------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `avaluma:ready`        | —                     | Die Brücke ist bereit. Bei jedem (Re-)Mount gesendet — mit `init` beantworten.                                              |
| `avaluma:connected`    | —                     | Eine Konversation wurde aufgebaut (Avatar verbunden).                                                                       |
| `avaluma:disconnected` | —                     | Die Konversation wurde beendet.                                                                                             |
| `avaluma:agentMessage` | `{ id, text, final }` | Eine Nachricht des Agenten. Transkripte werden gestreamt — derselbe `id`-Wert wird mit wachsendem `text` mehrfach gesendet. |

<Accordion title="Wie wird „final&#x22; bestimmt?">
  Getippte Chat-Nachrichten sind sofort `final: true`. Gestreamte Sprach-Transkripte gelten als fertig, sobald sich der Text rund 1 Sekunde nicht mehr ändert — dann folgt genau ein abschließendes `final: true`-Event mit dem kompletten Text. Mehrere Events mit gleicher `id` gehören zur selben Nachricht; nutze die `id`, um die Anzeige zu aktualisieren statt anzuhängen. Wer nur das Endergebnis braucht, filtert auf `final === true`.
</Accordion>

### Vollständiges Testbeispiel

<Tip>
  Schnellster Weg ohne eigenen Code: die eingebaute **[Embedding-Testseite](https://avaluma.ai/embed-test)** auf `avaluma.ai/embed-test`. Sie bettet einen Agenten von der avaluma.ai-Domain ein und visualisiert die komplette Brücke (Handshake, Text senden, Mikrofon ein/aus, Event-Log). Trage dafür `https://avaluma.ai` bei den **Erlaubten Origins** des Agenten ein.
</Tip>

Eine eigenständige HTML-Seite, die den Avatar einbettet, Text an ihn sendet und alle Events in einem kleinen Log anzeigt. Lokal mit einem statischen Server ausliefern (z. B. `python3 -m http.server 8080`) und dessen Origin (`http://localhost:8080`) bei den **Erlaubten Origins** des Agenten eintragen.

<CodeGroup>
  ```html test.html theme={null}
  <!DOCTYPE html>
  <html lang="de">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Avaluma Embed-Test</title>
    <style>
      body { margin: 0; min-height: 100vh; font-family: system-ui, sans-serif;
             background: #0f172a; color: #f8fafc; padding: 1rem; box-sizing: border-box; }
      h1 { font-size: 2rem; }
      .row { display: flex; gap: 0.5rem; margin: 0.75rem 0; }
      input { flex: 1; padding: 0.5rem; border-radius: 6px; border: 0; }
      button { padding: 0.5rem 1rem; border-radius: 6px; border: 0;
               background: #554998; color: #fff; cursor: pointer; }
      #log { background: #1e293b; border-radius: 8px; padding: 0.75rem; height: 140px;
             overflow: auto; font-family: monospace; font-size: 12px; white-space: pre-wrap; }
      iframe { width: 100%; height: 600px; border: 0; margin-top: 1rem; border-radius: 8px; }
    </style>
  </head>
  <body>
    <h1>Avaluma Embed-Test</h1>

    <div class="row">
      <input id="text" type="text" placeholder="Nachricht an den Avatar…" />
      <button id="send">An Avatar senden</button>
    </div>
    <div id="log"></div>

    <iframe
      id="avatar"
      src="https://avaluma.ai/agent/AGENT_ID/embed"
      allow="microphone; autoplay"
    ></iframe>

    <script>
      const AVALUMA_ORIGIN = "https://avaluma.ai";
      const iframe = document.getElementById("avatar");
      const logEl = document.getElementById("log");
      const input = document.getElementById("text");

      function log(msg) {
        logEl.textContent += msg + "\n";
        logEl.scrollTop = logEl.scrollHeight;
      }

      function sendToAvatar(message) {
        iframe.contentWindow.postMessage(message, AVALUMA_ORIGIN);
      }

      // Events des iframes entgegennehmen. Die Brücke sendet bei jedem (Re-)Mount
      // einen ready-Beacon; darauf registrieren wir unseren Origin per init.
      window.addEventListener("message", (event) => {
        if (event.origin !== AVALUMA_ORIGIN) return;
        const data = event.data;
        if (!data || typeof data.type !== "string" || !data.type.startsWith("avaluma:")) return;
        log("← " + JSON.stringify(data));

        if (data.type === "avaluma:ready") {
          log("→ init (Origin registrieren)");
          sendToAvatar({ type: "avaluma:init" });
        }

        // Nur die fertige Agent-Antwort auswerten:
        if (data.type === "avaluma:agentMessage" && data.final) {
          // z. B. hier in deine eigene UI schreiben:
          // showAgentReply(data.text);
        }
      });

      // Text an den Agenten senden (Gespräch muss im iframe gestartet sein).
      document.getElementById("send").addEventListener("click", () => {
        const text = input.value.trim();
        if (!text) return;
        log("→ sendMessage: " + text);
        sendToAvatar({ type: "avaluma:sendMessage", text });
        input.value = "";
      });
      input.addEventListener("keydown", (e) => {
        if (e.key === "Enter") document.getElementById("send").click();
      });
    </script>
  </body>
  </html>
  ```
</CodeGroup>

<Warning>
  Ersetze `AGENT_ID` durch die UUID deines Agenten. Damit das Senden funktioniert, muss der Origin der Testseite bei den **Erlaubten Origins** des Agenten eingetragen sein.
</Warning>

### Sicherheits-Checkliste

<Steps>
  <Step title="Immer den Ziel-Origin angeben">
    Verwende niemals `"*"` als Ziel-Origin in `postMessage`. Gib `https://avaluma.ai` an, damit Nachrichten nicht an andere Frames gelangen können.
  </Step>

  <Step title="Immer den Quell-Origin prüfen">
    Weise in deinem `message`-Listener jedes Event ab, dessen `event.origin` nicht `https://avaluma.ai` ist.
  </Step>

  <Step title="Erlaubte Origins eng halten">
    Trage unter **Erlaubte Origins** im Dashboard nur die Origins ein, die den Avatar tatsächlich hosten.
  </Step>
</Steps>
