Embedding an agent
Оваа содржина сè уште не е достапна на вашиот јазик.
Put an agent on a website. Visitors chat with it anonymously — no account, no login — inside a panel you style from the Embed tab.
1. Create the embed
Section titled “1. Create the embed”Open the agent, then the Embed tab, and press Create embed. You need a name and at least one allowed origin.
An origin is the scheme, host and (if it is not the default) port of the site
the widget will run on — https://help.example.com, not
https://help.example.com/support. Add every origin the widget will appear
on, including www and non-www if you serve both.
2. Paste the tag
Section titled “2. Paste the tag”The Install block gives you the whole thing:
<script src="https://your-kovac-host/embed/v1/embed.js" data-embed-key="kve_…" defer></script>Paste it once, before the closing </body> tag. The tag carries only the
key: the theme, greeting, starter prompts and corner are all read from the
Embed tab at runtime, so restyling never needs your site redeployed.
The key is public, and that is deliberate
Section titled “The key is public, and that is deliberate”An embed key starts kve_ and is meant to sit in your page’s HTML where
anyone can read it. It is not masked anywhere in the interface, and it is not
a secret. What protects it is the origin allowlist and the limits, not
secrecy — so treat it like a public site identifier, not like an API key.
Tenant API keys are the opposite: those carry a role and are shown once. Never put one in a web page.
3. Understand what the allowlist does
Section titled “3. Understand what the allowlist does”The allowlist stops the agent being mounted on another website. A widget loaded on an origin you did not list renders its own chrome and never receives a session, so it cannot answer anything.
It is not a defence against someone who copies the key out of your page and calls the API directly from a script. Nothing in a browser can be, because the browser is the thing making the claim. That is what the limits are for.
4. Set the limits
Section titled “4. Set the limits”Three ceilings, all counted in runs — one run is one turn the model takes, whatever triggered it:
| limit | what it stops |
|---|---|
| Runs / min per visitor | one visitor hammering the widget |
| Runs / day per visitor | one visitor’s total daily cost |
| Runs / day, this embed | the whole embed’s daily cost |
The Embed tab shows today’s count against the last of these. When it is reached, the widget reports itself unavailable rather than failing on a visitor’s first message.
5. Style it
Section titled “5. Style it”The Appearance section starts from the platform’s own palette, in a light
and a dark half, and mode decides which a visitor sees (auto follows
their own system setting). Every one of the twenty colours can be overridden;
anything you do not touch tracks the platform default.
The preview beside the form is the real widget, fed your unsaved changes. Messages you send in it are real runs and count against the embed’s daily limit.
Where a colour combination falls below the contrast the text needs, the preview says so and names the ratio. Those warnings never block saving — they are there so you find out before your visitors do.
A worked custom theme
Section titled “A worked custom theme”The header background is the one structured value, so a gradient is expressible without free-form CSS:
{ "header_bg": { "type": "gradient", "from": "#0d2d59", "to": "#1e4d8c", "angle": 135 }, "bg": "#ffffff", "surface": "#f4f7fb", "border": "#dbe3ec", "text": "#19212b", "text_muted": "#57606b", "accent": "#123b72", "header_text": "#ffffff", "user_bubble": "#123b72", "user_text": "#ffffff"}6. Rotating and retiring a key
Section titled “6. Rotating and retiring a key”Regenerate issues a new key and kills the old one immediately — every page still carrying the old tag goes quiet until you paste the new one.
Revoke retires the embed. The row stays visible as a record, and the key stops working everywhere. There is no un-revoke: create a new embed instead.
Embedding a workflow
Section titled “Embedding a workflow”A workflow embeds the same way. Open the workflow, then its Embed tab, and follow the steps above. The tag, the allowlist, the limits and the theme are the same, and one embed is one workflow.
Replies stream with the name of the agent that wrote them. Under
Agents, the switch Show which agent is answering decides whether
visitors see that name. With it on, a Label for field appears for each
member, so you can show Billing team instead of support-desk-billing.
Labels are up to 40 characters, and a blank label shows the agent’s own
name. See Workflows.
Citations
Section titled “Citations”When the agent answers from a collection, the widget numbers its sources
inline and lists them as chips under the answer. A source is only linked if
it has an http(s) URI; anything else is still named.
Suggested questions
Section titled “Suggested questions”When the agent or workflow has follow-up questions turned on (see Agents), the widget shows the latest answer’s suggested questions under it as stacked buttons, with a small “Suggested questions” label in English or Macedonian, following the widget’s language. A tap sends that question. They are hidden while an answer is being written and while an approval, question or page action is waiting. They use the theme’s Follow-up buttons colours, the same as starter prompts.
Page actions
Section titled “Page actions”The assistant can act on your page, within what you allow. In the Embed tab, On the page lists the actions your page can perform: each has a name, a title the visitor reads, a description for the assistant and the parameters it takes. Your page provides the code. The Install block shows a second snippet with one handler per action; paste it after the tag:
<script> window.kovacEmbed = window.kovacEmbed || []; kovacEmbed.push({ actions: { apply_filter: async ({ brand }, ctx) => { await ctx.type('#search', brand); await ctx.click('#apply'); return { results: document.querySelectorAll('.product').length }; }, }});</script>A handler receives the validated arguments and ctx: ctx.point,
ctx.click, ctx.type and ctx.scrollTo take an element or a CSS
selector, move a visible cursor and perform the step. ctx.type fills a
field the way a person would (React and Vue forms see it) and never types
into a password, file, read-only, disabled or non-text field. ctx.click
fires a click event only, without the pointer and mouse down and up events,
so a control that listens for those will not react. ctx.signal is aborted
when the visitor presses Escape or the action runs too long. Return a JSON
value (under 16 KB) for the assistant to read, or nothing.
ctx.navigate(url) takes the visitor to another page of the same site,
such as a map or a product page. It waits a moment so your handler’s answer
reaches the assistant first, and refuses another site’s address. On a
laptop or desktop the chat opens again on the new page; on a phone it stays
closed, so the page is in view.
Before an action runs, the visitor sees a card with its title and
arguments and chooses Allow or Not now. An action you mark as not needing
confirmation runs at once, unless an earlier automatic attempt failed, when
the card appears: use that setting only for something the visitor would
not need to undo, such as a filter. Handlers run without a user
activation, so a pop-up, the clipboard or a file picker is blocked; use
ctx.navigate to change pages, and the answer still reaches the assistant.
Pressing Escape stops a running action and leaves the chat open. The live
preview in the Embed tab answers every action with a stub and lists the
calls, so you can try the assistant before your page has the code.
Page control
Section titled “Page control”Page control lets the assistant look at the page the visitor is on and act on it, without you writing a handler for each action. Switch it on under On the page in the Embed tab; it is off by default.
With it on, the assistant can read the page’s visible text and controls, point at a part of the page and propose a short plan of clicks, typed values and navigations. The visitor allows each plan on a card that lists every step in plain words, with the values the assistant chose marked as such; nothing runs until they choose Allow, and Escape stops a plan that is running.
What the assistant cannot do: it stays on the same origin as your page (same scheme, host and port: it never follows a link or submits a form to another origin, never opens a new tab, starts a download or follows a link that runs script). It never shows or fills a card-related field (card number, expiry, security code or one-time code, a drop-down included), a password, a file picker, or a disabled or read-only field. It can clear a text field by filling it with an explicitly empty value.
The visible text of the page, and the names of its controls, are sent to
the model and appear in the conversation’s traces. Mark anything that must
not be sent with data-kovac-private; the assistant then sees neither that
region’s text nor its controls:
<section data-kovac-private> <p>Account number: 1234 5678</p></section>Hidden elements, aria-hidden content and field values that look secret
are left out as well, and the page address is shown without its query or
hash. The live preview in the Embed tab answers page control from a small
sample page and lists a plan instead of running it.
Pinning the chat to the side
Section titled “Pinning the chat to the side”On a window at least 640 pixels wide, visitors get a pin button in the widget’s header. Pinned, the chat becomes a full-height panel on the right edge of the page and stays open when the visitor moves to another page of your site. Closing it keeps the pin, so the next time it opens it is a side panel again; unpinning returns it to its corner. Escape does not close a pinned panel. On a narrower window the chat already fills the screen, and there is no pin.
Two switches in the Appearance section control this:
- Let visitors pin the chat to the side turns the pin button on or off.
- Pinned chat pushes the page aside moves your page to the left to make room instead of covering it. Headers and other elements fixed to the window stay where they are, so they may be covered by the panel.
Conversation history
Section titled “Conversation history”A history button in the header lists the visitor’s earlier conversations with this widget — up to 50, newest first. A visitor can filter the list by title, reopen a conversation or delete one. A new conversation is titled with the start of its first message. History is per browser: it shows only that visitor’s own conversations on this embed.
Searching a conversation
Section titled “Searching a conversation”The search button in the header opens a search field for the conversation that is open. It highlights every match, shows how many there are, and steps forward and back through them. It searches the open conversation only, not the whole history.
The widget’s language
Section titled “The widget’s language”Under Content, Widget language sets the language of everything the widget says — buttons, hints, messages and errors. Automatic uses the visitor’s browser language, Macedonian when that is Macedonian and English otherwise; you can also fix it to English or Macedonian. It is separate from the dictation language, which only tells the speech model what to expect.
