Skip to main content
Every shared agent can be embedded as an <iframe> on any website. The avatar then connects directly from your visitor’s browser to the agent — no backend required on your side.

Prerequisites

1

Enable embedding

Open the agent in the dashboard and turn on Allow embedding. Only shared agents can be embedded. Toggling this switch is saved immediately — you can test right away without saving the agent manually first.
2

Allow your origin

Under Allowed origins, add the address(es) of the page(s) that may run the avatar — for example https://www.your-domain.com.
If the embedding page’s origin is not on the list, the browser blocks the iframe (via frame-ancestors) and all messaging commands are rejected. Enter the exact origin (scheme + host + port), without a path.
3

Copy the snippet

Copy the ready-made embed code from the dashboard, or build it yourself as described below.

Quickstart

<iframe
  src="https://avaluma.ai/agent/AGENT_ID/embed"
  allow="microphone; autoplay"
  style="width:100%;height:600px;border:0"
></iframe>
Replace AGENT_ID with your agent’s UUID (visible in the dashboard).
The allow="microphone; autoplay" attribute is required: without microphone the visitor cannot speak, and without autoplay audio playback will not start automatically in some browsers (the “Enable sound” overlay takes over instead).
Don’t want to set up your own page first? Use the built-in embedding test page at avaluma.ai/embed-test: enter an agent UUID, embed it, and try the postMessage bridge (handshake, send text, microphone on/off, event log) right away. Just add https://avaluma.ai to the agent’s Allowed origins once.

Fullscreen displays

Embedding always renders the avatar inline, inside the iframe container you place on your page. For a standalone, fullscreen display — for example an unattended kiosk stand — use the dedicated Kiosk mode instead of an iframe. It opens directly as a top-level page (no iframe) and adds a screen wake lock, an idle auto-reset, and a home-screen web app for true fullscreen.

Query parameters

All options are appended as query parameters to the /embed URL. Invalid values fall back to their defaults automatically.
border
string
default:"000000"
Background / letterbox color as a 6-digit hex value without # (e.g. f5f5f5).
lang
string
default:"en"
UI language of the controls. de or en.
hideTextChat
boolean
default:"false"
With true the text-chat input is hidden (voice/avatar-only mode).
endCall
boolean
default:"false"
With true the “Hang up” button is shown.
The microphone stays open for the duration of a conversation. If you want to mute or re-enable it from your page (e.g. your own push-to-talk button), control it via postMessage — see Messaging.

Example with multiple options

<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>

Messaging

Beyond simply embedding the avatar, you can control it programmatically and react to its behavior. An embedded agent runs inside an <iframe>, so your page and the avatar live in different browser contexts. They communicate through the standardized window.postMessage API: you send messages to the iframe and receive events from it. Every message carries the avaluma: prefix so that foreign messages (e.g. from third-party scripts) are safely ignored.
Messaging only works if your page’s origin is listed under the agent’s Allowed origins (see Prerequisites above). Messages from or to a disallowed origin are rejected by the browser.

Get a reference to the iframe

Give your embed an id so you can address its contentWindow:
<iframe
  id="avaluma-agent"
  src="https://avaluma.ai/agent/AGENT_ID/embed"
  allow="microphone; autoplay"
  style="width:100%;height:600px;border:0"
></iframe>
const frame = document.getElementById("avaluma-agent");
const AVALUMA_ORIGIN = "https://avaluma.ai";

Flow (handshake)

The bridge announces itself with a ready beacon on every (re)mount. The parent page confirms with init, registering its origin as the target for outbound events.
1

Receive ready

As soon as the bridge inside the iframe is ready, it sends { type: "avaluma:ready" }.
2

Send init back

The parent page replies with { type: "avaluma:init" }. Only then does the bridge know the target origin and can deliver events.
3

Send & receive

From now on the page can send sendMessage and receives connected, agentMessage, and disconnected.
Always respond to every ready beacon with init. After “hang up” and reconnecting, the bridge restarts and sends ready again — the repeated init keeps events flowing seamlessly.

Send messages to the avatar

Send a message to the iframe’s contentWindow. Always pass the Avaluma origin as the second argument so the message is only delivered to the avatar:
function sendToAvatar(message) {
  frame.contentWindow.postMessage(message, AVALUMA_ORIGIN);
}

// Complete the handshake (respond to every ready beacon)
sendToAvatar({ type: "avaluma:init" });

// Send text to the agent (requires an active conversation)
sendToAvatar({ type: "avaluma:sendMessage", text: "Hello!" });

// Mute / re-enable the microphone
sendToAvatar({ type: "avaluma:setMicrophone", enabled: false });
sendToAvatar({ type: "avaluma:setMicrophone", enabled: true });
MessagePayloadEffect
avaluma:initRegisters the parent page’s origin and completes the handshake. Send on every ready beacon.
avaluma:sendMessage{ text }Sends text to the agent — as if the user had typed it into the chat field. Empty messages are ignored.
avaluma:setMicrophone{ enabled }Turns the visitor’s microphone on (true) or mutes it (false). Use this to build your own push-to-talk button on your page, for example.
A conversation is started by the visitor via “Start conversation” inside the iframe. avaluma:sendMessage only takes effect while a conversation is active.

Receive events from the avatar

Listen for message events on window. Always check event.origin before trusting the data:
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":
      // Bridge is ready → confirm the handshake
      sendToAvatar({ type: "avaluma:init" });
      break;
    case "avaluma:connected":
      console.log("Conversation started");
      break;
    case "avaluma:disconnected":
      console.log("Conversation ended");
      break;
    case "avaluma:agentMessage":
      // Streamed: same id, growing text; final === true at the end
      if (data.final) console.log("Agent:", data.text);
      break;
  }
});
EventDataMeaning
avaluma:readyThe bridge is ready. Sent on every (re)mount — respond with init.
avaluma:connectedA conversation has been established (avatar connected).
avaluma:disconnectedThe conversation has ended.
avaluma:agentMessage{ id, text, final }A message from the agent. Transcripts are streamed — the same id is sent multiple times with growing text.
Typed chat messages are immediately final: true. Streamed speech transcripts are considered complete once the text stops changing for about 1 second — then exactly one closing final: true event with the full text follows. Multiple events with the same id belong to the same message; use the id to update the display instead of appending. If you only need the final result, filter for final === true.

Full test example

The quickest way without writing your own code: the built-in embedding test page at avaluma.ai/embed-test. It embeds an agent from the avaluma.ai domain and visualizes the full bridge (handshake, send text, microphone on/off, event log). Just add https://avaluma.ai to the agent’s Allowed origins.
A standalone HTML page that embeds the avatar, sends text to it, and shows all events in a small log. Serve it locally with a static server (e.g. python3 -m http.server 8080) and add its origin (http://localhost:8080) to the agent’s Allowed origins.
<!DOCTYPE html>
<html lang="en">
<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="Message to the avatar…" />
    <button id="send">Send to avatar</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);
    }

    // Receive the iframe's events. The bridge sends a ready beacon on every
    // (re)mount; we register our origin via init in response.
    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 (register origin)");
        sendToAvatar({ type: "avaluma:init" });
      }

      // Only act on the final agent reply:
      if (data.type === "avaluma:agentMessage" && data.final) {
        // e.g. write into your own UI here:
        // showAgentReply(data.text);
      }
    });

    // Send text to the agent (conversation must be started inside the iframe).
    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>
Replace AGENT_ID with your agent’s UUID. For sending to work, the test page’s origin must be listed under the agent’s Allowed origins.

Security checklist

1

Always specify the target origin

Never use "*" as the target origin in postMessage. Pass https://avaluma.ai so messages can’t reach other frames.
2

Always verify the source origin

In your message listener, reject every event whose event.origin is not https://avaluma.ai.
3

Keep allowed origins tight

Under Allowed origins in the Dashboard, list only the origins that actually host the avatar.