Widget Embed SDK

Control the Agentkit chat widget from browser code, send proactive messages, apply page-only options, and listen for chat events.

The embed SDK lets your page open, close, reset, configure, and observe the Agentkit chat bubble. It is available on the script embed only. The iframe embed does not define window.agentkit.

Install the queue bootstrap

Use the snippet from your agent's Channels page. It creates a small command queue before the widget bundle downloads, so calls made before and after the async script loads behave the same way.

<script>
  (function (w) {
    if (w.agentkit) return;
    var q = [];
    var agentkit = function () { q.push(arguments); };
    agentkit.q = q;
    w.agentkit = agentkit;
  })(window);
</script>
<script async data-chatbot="YOUR_CHATBOT_ID" src="https://YOUR_WIDGET_HOST/agentkit-chatbot.js"></script>

The original one-tag embed still works. Add the bootstrap when your page needs to call the SDK before the bundle finishes loading.

Open and close the widget

window.agentkit('open');
window.agentkit('close');
window.agentkit('open', { message: 'Can you help me choose a plan?' });

Both calls are safe to repeat. close does nothing while a Salesforce live-chat takeover is active, because minimizing the widget would end the live session. The third call opens the widget and sends one visitor message through the normal composer checks.

Set hideMessage for a proactive prompt. The visitor prompt stays out of the transcript and the widget remains closed until the assistant starts replying.

window.agentkit('open', {
  message: 'Explain the current page in one sentence.',
  hideMessage: true,
});

The SDK refuses empty messages, concurrent sends, rate-limited sends, and sends when the composer is hidden or disabled. It also refuses sends during live chat. A refused hidden send does not open the widget later.

Send while the widget stays closed

Use send when your page supplies its own compact question UI and should show the answer outside the widget. The message is added to the normal widget conversation, but the presentation stays closed. Listen for the assistant event to render the answer, then call open to continue the same conversation.

let pendingQuestion = null;
let questionAccepted = false;

function onUserMessage(event) {
  if (event.detail.content === pendingQuestion) questionAccepted = true;
}

function showAnswer(event) {
  if (!questionAccepted) return;
  document.querySelector('#answer').textContent = event.detail.content;
  pendingQuestion = null;
  questionAccepted = false;
}

window.agentkit('addEventListener', 'user-message', onUserMessage);
window.agentkit('addEventListener', 'assistant-message', showAnswer);

pendingQuestion = 'Do you offer a free plan?';
window.agentkit('send', { message: pendingQuestion });

// Later, from a “Continue in chat” control:
window.agentkit('open');

send uses the same composer checks, rate limits, persistence, and message events as open({ message }). It only changes whether the presentation opens.

Center Stage

Center Stage opens the same agent as a centered, full-focus dialog over your page instead of the corner bubble. It is its own channel on the Channels page, with its own display, voice, instructions, and actions, and it is off until you enable it there.

Install it with the same bootstrap and add data-display="center-stage" to the script tag. That attribute sits next to data-chatbot and data-locale:

<script async data-chatbot="YOUR_CHATBOT_ID" data-display="center-stage" src="https://YOUR_WIDGET_HOST/agentkit-chatbot.js"></script>

The launcher stays the default way to open it, and every SDK call works the same: open reveals the dialog, close dismisses it, and resetChat, setOptions, identify, events, and browser tools behave exactly as they do for the bubble. The dialog traps focus, closes on Escape or its close button, returns focus to whatever opened it, and fills the screen on phones.

Only one Agentkit presentation can run on a page. If two embed scripts load, the first script's data-display wins and a mismatching duplicate logs a console warning instead of mounting a second widget. Center Stage settings and chat load from their own endpoints, so disabling the channel on the Channels page stops it on every site even while the bubble stays enabled.

Reset the chat

window.agentkit('resetChat');

This removes the cached transcript and starts a new conversation id. It keeps the current verified identity and any page-only options.

Apply page-only options

setOptions changes the current page in memory. It never updates the saved agent settings or local storage.

window.agentkit('setOptions', {
  title: 'Checkout assistant',
  conversation: {
    messagePlaceholder: 'Ask about checkout',
    initialMessages: ['Need help finishing your order?'],
    defaultPrompts: {
      prompts: ['Delivery times', 'Returns', 'Payment methods'],
    },
  },
});
window.agentkit('resetOptions');
window.agentkit('resetOptions', [
  'title',
  'conversation.initialMessages',
]);

Supported keys are:

KeyValue
titleNon-empty string
conversation.messagePlaceholderNon-empty string
conversation.initialMessagesArray of non-empty strings
conversation.defaultPrompts.promptsArray of non-empty strings

The SDK validates each key separately. Valid keys still apply when a neighboring key is invalid. Call resetOptions without keys to restore every saved value, or pass the keys to restore selected values.

Changing initial messages updates a conversation that has no visitor message yet. Otherwise, the new initial messages appear after the next resetChat.

Listen for events

The SDK emits user-message after it accepts a send and assistant-message once the assistant response completes.

function onAssistantMessage(event) {
  console.log(event.detail.conversationId);
  console.log(event.detail.content);
}

window.agentkit(
  'addEventListener',
  'assistant-message',
  onAssistantMessage,
);

// Clean up when this page or route no longer needs the listener.
window.agentkit(
  'removeEventListener',
  'assistant-message',
  onAssistantMessage,
);

Always pass the same function reference to removeEventListener. This matters in single-page applications where routes mount more than once. An exception in one handler does not interrupt the widget or other handlers.

The event names are:

EventDetail
user-message{ conversationId, content }
assistant-message{ conversationId, content }
tool-call{ conversationId, toolCallId, toolName, input }
tool-result{ conversationId, toolCallId, toolName, output }

tool-call fires before a registered browser tool runs. tool-result fires after its promise settles, including normalized error results.

Register browser tools

Create a client-side action in the dashboard, then register a handler with the exact same action name on every page where the widget can appear. Registering again replaces the complete registry, which makes route-level cleanup predictable in single-page applications.

window.agentkit('registerTools', {
  checkInventory: async ({ args, signal }) => {
    const response = await fetch(`/api/inventory?sku=${args.sku}`, { signal });

    if (!response.ok) {
      return { status: 'error', error: 'Inventory is unavailable.' };
    }

    return { status: 'success', data: await response.json() };
  },
});

Handlers receive { args, toolCallId, signal }. Return { status: 'success', data } or { status: 'error', error }; a plain JSON-compatible return value is treated as successful. The widget cancels work after 60 seconds and turns missing handlers, thrown exceptions, timeouts, and invalid results into bounded error results so the assistant can continue the conversation.

Treat tool arguments as untrusted input. Keep privileged credentials on your own server, pass the supplied abort signal to network requests, and never return secrets or unnecessary personal data. Client tools require the script embed; the iframe embed cannot access functions on its parent page.

Update or clear identity

Identity commands remain compatible with the queue bootstrap:

window.agentkit('identify', { token: newToken });
window.agentkit('resetUser');

See Identity Verification for token signing and privacy rules.

TypeScript types

Projects that consume the Agentkit widget package can import the Agentkit browser contract from @kit/chatbot-widget/sdk. Use optional access only when you do not install the queue bootstrap. With the bootstrap, window.agentkit exists before the async widget script loads.