Embed the web widget
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.
1. Create a publishable key
Section titled “1. Create a publishable key”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 examplehttps://www.example.com; https://*.example.commatches exactly one subdomain level;http://localhost:3000must 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.
2. Add the script
Section titled “2. Add the script”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. |
Or place the element yourself
Section titled “Or place the element yourself”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. |
What visitors get
Section titled “What visitors get”- 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.
Anonymous visitors
Section titled “Anonymous visitors”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_conversationsper agent (default 200). Past a limit the visitor sees a polite “try again later”.
3. Signed-in users (verified)
Section titled “3. Signed-in users (verified)”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 });});# Flask. `login_required` and `current_user` come from your own auth (e.g. Flask-Login).import osimport requestsfrom flask import Flask
app = Flask(__name__)
@app.post("/kagent/token")@login_requireddef kagent_token(): r = requests.post( "https://api.k-agent.kerneltics.com/v1/client_tokens", headers={"Authorization": f"Bearer {os.environ['KAGENT_API_KEY']}"}, json={ "agent": "agt_01k6rz1m3w8q4t7v9x2b5c0dnf", "end_user": { "external_id": current_user.customer_id, # never an email or phone "name": current_user.first_name, "traits": {"plan": current_user.plan}, }, "ttl_seconds": 900, }, timeout=10, ) if not r.ok: return {"error": "token_unavailable"}, 502 data = r.json() return {"token": data["token"], "expires_at": data["expires_at"]}, 200, {"Cache-Control": "no-store"}- 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_idfor 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_settablevariables, add"variables": {…}. - A token stops working when it expires, when you revoke the key that minted it, or when you erase the end user.
Content Security Policy
Section titled “Content Security Policy”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;Troubleshooting
Section titled “Troubleshooting”| 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. |