KonvoAI Docs
Custom widget

Quickstart

Create a custom widget channel, connect the visitor SDK, and exchange your first messages in a few minutes.

1. Create the channel

In the dashboard, go to Settings → Channels → Widget → Add widget and pick Custom widget. The configurator opens on the Install tab, which shows your integration snippet with the channel id (live_chat_…) already filled in.

2. Allowlist your origins

A fresh channel accepts every origin. Under Install → Advanced security settings, enable the allowed-domains gate and list the origins your frontend runs on (e.g. https://www.example.com). Requests from anywhere else are rejected at the CORS layer.

During local development, add your dev origin (e.g. http://localhost:3000) too — or leave the gate off until you go live.

3. Connect and exchange messages

Import the SDK as an ES module and create a client with your channel id:

<script type="module">
  import { createChatClient } from "https://widget.k2.konvoai.com/sdk.js";

  const chat = createChatClient({ channelId: "live_chat_YOUR_CHANNEL_ID" });

  chat.on("message", (message) => {
    // An agent (human or AI) replied — render it in your UI.
    console.log(message.body);
  });

  await chat.connect();
  chat.sendMessage({ body: "Hello!" });
</script>

connect() runs the auth handshake and resolves once the realtime connection is open. After that, sendMessage returns immediately with a message id; the ack event fires with the same id once the server has durably accepted the message.

4. Restore history on page load

Visitors expect their conversation to survive a reload. Fetch it before (or while) connecting:

const { streamId, messages } = await chat.getHistory();
for (const message of messages) {
  // message.author is "user" (the visitor) or "agent".
  render(message);
}

streamId is null when the visitor has no open conversation yet.

5. Wire up the rest

A production chat UI usually also wants:

// Typing indicators — both directions.
chat.on("typing", ({ active, displayName }) => showTypingIndicator(active));
input.addEventListener("input", () => chat.sendTyping(true));

// Read receipts on agent messages you render.
chat.on("message", (message) => {
  render(message);
  chat.markDelivered(message.id);
  chat.markSeen(message.id); // when it's actually on screen
});

// Attachments: upload first, then reference the uploadId.
const upload = await chat.uploadAttachment(file);
chat.sendMessage({ body: "", attachmentIds: [upload.uploadId] });

// Link the visitor's email once you know it.
await chat.identify("visitor@example.com");

// Connection state for your "reconnecting…" hint.
chat.on("connection", ({ status, issue }) => updateBanner(status, issue));

The full method and event surface — including error codes and limits — is in the SDK reference.

On this page