Skip to content

Embed the web widget

View as Markdown

The widget is a chat bubble for your website: one <script> tag, no build step, and it works on any site. It is a Web Component (<k-agent-chat>) in its own shadow DOM, so your styles and its styles never clash. It speaks Arabic (right to left) and English, streams replies, shows your team’s replies live during a handoff, and stays under 60 KB gzipped.

In the dashboard, open API keys → Create publishable key and list the allowed origins — the exact sites that may load the widget:

  • write each origin as scheme://host[:port], for example https://www.example.com;
  • https://*.example.com matches exactly one subdomain level;
  • http://localhost:3000 must be listed explicitly for local development;
  • at least one origin is required (422 allowed_origins_required).

A publishable key (kt_pk_live_…) is safe to put in your page: it can only read the widget’s settings and start widget sessions, and only from those origins.

Paste this before </body>:

<script
src="https://api.k-agent.kerneltics.com/widget/v1.js"
data-agent="agt_01k6rz1m3w8q4t7v9x2b5c0dnf"
data-key="kt_pk_live_…"
defer
></script>

That’s all for anonymous visitors. The widget loads the agent’s published settings — bot_name, greeting, launcher_label, theme.accent and theme.position — so you change its look from the agent editor, not your code.

Script attribute Description
data-agent The agent’s ID.
data-key Your publishable key.
data-api Optional. The API origin, if it differs from the origin that serves the script.

To control where and how the chat appears, load the script without data-agent and add the element:

<script src="https://api.k-agent.kerneltics.com/widget/v1.js" defer></script>
<k-agent-chat
agent="agt_01k6rz1m3w8q4t7v9x2b5c0dnf"
publishable-key="kt_pk_live_…"
lang="ar"
position="end"
></k-agent-chat>
Element attribute Description
agent The agent’s ID. Required.
publishable-key Your publishable key.
token-endpoint A URL on your site that returns a client token for the signed-in user (step 3).
api-base Optional API origin.
lang ar or en. Defaults to the element’s lang, then <html lang>, then the agent’s language. Arabic is laid out right to left.
position start or end (default). Follows the page direction: end is bottom-right in English and bottom-left in Arabic.
open Present to start with the chat open.
  • Replies stream in as they are written; messages from visitors and agents are shown with the right direction for their language.
  • The conversation is kept per browser: a visitor who comes back on the same device continues where they left off. A New conversation item starts fresh.
  • During a handoff the header shows that a person is now in the chat, and your team’s replies appear live.
  • The widget is keyboard- and screen-reader-friendly, and goes full screen on phones.

Visitors who are not signed in are anonymous end users. For their safety and yours:

  • tools with side effects are off unless the agent sets tools.allow_anonymous_actions;
  • tools that need a verified identity never run;
  • limits apply: per IP, 20 conversations and 60 messages an hour; at most 40 messages per conversation; and widget.anonymous_daily_conversations per agent (default 200). Past a limit the visitor sees a polite “try again later”.

When the visitor is logged in to your site, let them chat as themselves: the agent can then use identity-bound tools (“where is my order?”), remember their past actions, and your team sees who they are.

Your server mints a short-lived client token for the user with your secret key; the widget fetches it from a token-endpoint on your site. The secret key never reaches the browser.

<script src="https://api.k-agent.kerneltics.com/widget/v1.js" defer></script>
<k-agent-chat
agent="agt_01k6rz1m3w8q4t7v9x2b5c0dnf"
publishable-key="kt_pk_live_…"
token-endpoint="/kagent/token"
></k-agent-chat>

The widget sends a POST to token-endpoint from your page (with your site’s cookies, as a same-origin request) and expects JSON with token and expires_at, exactly as POST /v1/client_tokens returns them. It asks again before the token expires.

// Node.js + Express. `requireLogin` is your own authentication middleware.
import express from 'express';
const app = express();
app.post('/kagent/token', requireLogin, async (req, res) => {
const r = await fetch('https://api.k-agent.kerneltics.com/v1/client_tokens', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KAGENT_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
agent: 'agt_01k6rz1m3w8q4t7v9x2b5c0dnf',
end_user: {
external_id: req.user.customerId, // your stable ID — never an email or phone
name: req.user.firstName,
traits: { plan: req.user.plan },
},
ttl_seconds: 900,
}),
});
if (!r.ok) return res.status(502).json({ error: 'token_unavailable' });
const { token, expires_at } = await r.json();
res.set('Cache-Control', 'no-store').json({ token, expires_at });
});
  • Protect the endpoint with your normal login, and mint tokens only for the logged-in user. A token lets its holder chat as that person for up to ttl_seconds (at most 3600; 900 is a good default).
  • Use a stable, opaque external_id for the user. If your only key is an email or phone number, hash it.
  • To continue one specific conversation, add "session": "<sess_ or external_id>" when minting.
  • To pass values the agent declares as client_settable variables, add "variables": {…}.
  • A token stops working when it expires, when you revoke the key that minted it, or when you erase the end user.

If your site sends a CSP header, allow the widget’s script and API calls:

script-src 'self' https://api.k-agent.kerneltics.com;
connect-src 'self' https://api.k-agent.kerneltics.com;
Error Cause and fix
403 origin_not_allowed The page’s origin isn’t on the key’s list. Add it exactly, including the port.
403 origin_required The request didn’t come from a browser page. Publishable keys only work in browsers.
403 principal_not_allowed Browser credentials called a route they can’t use. Do that call from your server with a secret key.
401 token_expired Your token endpoint returned an expired token, or the clock on your server is off.
429 anonymous_limit_reached An anonymous visitor hit a limit. Signed-in users with client tokens are not limited this way.