Skip to content

The window on your page: what the script itself can do ​

This page is for whoever edits the site's code. A business owner doesn't need it: everything described here has sensible defaults and works without a single line.

You need it in three cases: when your site has user accounts and the bot should recognise the person; when the site is an SPA and the window should know what's currently open; when the chat should be opened by your own button.

The line the dashboard gives you ​

html
<script src="https://app.youselfbot.com/widget.js"
        data-bot-key="pk_..." data-api="https://api.youselfbot.com"></script>

data-bot-key is the agent's public key — it's meant to be on the page. The dashboard adds data-api itself when the API lives on a different hostname.

Take the line from the dashboard, not from here

In the panel, open the Channels row — the "Website code" block shows the line with your key and the current addresses, with a "Copy" button. The builder gives you the same line if you say "give me the website code". Retyped from memory, it silently goes out of date.

Who this visitor is ​

The bot can remember a regular customer and perform actions on their behalf — but only if your site tells it who they are.

Just an identifier ​

html
<script src="…/widget.js" data-bot-key="pk_..." data-user-token="user-42"></script>

or, if it's in a cookie:

html
<script … data-user-token-cookie="uid"></script>

This value goes into the {{user_token}} placeholder of your actions — that is, the other service receives a request with this particular person's identifier. It's never shown to the model. The placeholder has no other name.

This isn't proof yet

Anyone can substitute a token on the page: the page is open. So on its own it grants no rights — it only marks whose data this is in subsequent calls.

Verification by the owner's server ​

A simpler way than the signed envelope to make the token proof: give the platform the address of your server, which knows whether the token is genuine.

Verify the visitor's token with our server: https://api.firma.com/assistant/verify

Then, for every new token, the platform sends a POST to that address with the body {"token": "…", "agent": "…"} and the header X-YouSelfBot-Signature: sha256=… — an HMAC-SHA256 of the body using the agent's signing key (the same one as for the envelope below), so your server can make sure it's really the platform asking. The response:

json
{ "valid": true, "user_id": "u42" }

Only a confirmed token makes the person identified: {{user_token}} substitutes it in actions, "identified only" actions (known_only) open up for them, and the assistant remembers the person between conversations by user_id. A token for which the server said "valid": false, returned an error or didn't answer within 4 seconds is a guest, as if there were no token. The platform remembers a "yes" for 5 minutes and a "no" for a minute; after a failure it asks again.

Signed envelope (when rights depend on it) ​

When the bot should answer as it would an admin (show other people's orders, change data), you need a signature from your backend:

html
<script … data-user-auth="v1.eyJ1c2VyIjoi….a3f9…"></script>

The envelope is built like this: v1.<base64url(JSON)>.<HMAC-SHA256 hex>, where the JSON is

json
{ "user": "user-42", "role": "admin", "bot": "b_your_agent", "exp": 1787400000 }

and the signature is computed over the string v1.<base64url(JSON)> with this agent's visitor signing key. You'll find the key in the dashboard (agent panel → "Channels" row → "New visitor signing key" — the same switch reissues it); next to it the dashboard also shows the agent identifier for the bot field.

The envelope must be built by the backend — it can't be in the page code: otherwise the key ends up in open HTML and anyone could issue themselves the "admin" role. Keep the lifetime (exp) short — an envelope copied from someone else's browser works exactly that long. There's no point setting it above 24 hours: the server doesn't accept a longer lifetime, and such an envelope doesn't work at all — it doesn't break the signature, so you won't see an error, the visitor simply stays without their role.

If the envelope doesn't match, the person stays an ordinary visitor rather than getting an error: the chat keeps working.

What goes into actions. As soon as the site sends an envelope, {{user_token}} in your actions becomes the user value from the envelope, not the user_token the page passed earlier. If an action needs a different string (the person's account key in your system, a session token), put it in the token field of the same envelope:

json
{ "user": "user-42", "token": "acc_7f3c…", "role": "visitor", "bot": "b_your_agent", "exp": 1787400000 }

Then actions get token, while memory about the person and the conversation across devices follow user. The field is signed together with everything else: the page can't keep proof of one person while running actions on behalf of another.

The conversation from another device ​

The signed envelope isn't only for rights. With it — and with "role": "visitor" for an ordinary shopper — a person who's signed in to your site sees the same conversation on their phone and their computer, and the assistant remembers them between conversations.

How it works: on start, the window sends the envelope to POST /v1/widget/thread and, if the server returns a conversation newer than the one already stored in this browser, shows it and keeps writing into it. The server returns only the latest conversation of the user that your backend signed for this agent, and only within a day of the last message. If the envelope is invalid, expired or issued for another agent, the response is 204 — no conversation.

data-user-token and data-user-name won't do for this: they're written by the page, that is, by anyone — and the conversation under them is neither stored nor returned.

Sign the envelope on every page where the person is signed in — otherwise the window on a page without an envelope works as for a guest. After sign-out, call YouSelfBot.reset() (see below).

Name for the greeting ​

html
<script … data-user-name="Oleksii"></script>

The assistant will address the person by name. It's a word for the greeting and nothing more: it grants no rights and isn't substituted into actions — that's what data-user-token and the signed envelope above are for. It reaches the model as one short line: letters, space, hyphen, apostrophe, up to 40 characters.

The WordPress plugin sets this attribute itself — for a person who's signed in to the site. The plugin (from version 1.3.0) also sets the signed envelope, if its settings contain the "Visitor signing key": user — wp:<user number>, role — always visitor, lifetime — one hour.

window.YouSelfBot — control from the page ​

The script leaves exactly one global name. Below is the complete list: the window has nothing else.

Show, hide, remove ​

CallWhat it does
YouSelfBot.open() / YouSelfBot.close() / YouSelfBot.toggle()open, minimise, toggle the window — so that your button opens the chat. open() on a hidden window shows it first
YouSelfBot.hide()hide the chat button and all windows. This isn't "close": the conversation, whatever's typed in the field, open windows and unread messages stay. A hidden window doesn't chime and doesn't write to the tab title
YouSelfBot.show()show what was hidden — everything goes back to how it was. Also shows the window where an address pattern hid it (see data-hide-on) — until navigation to another page
YouSelfBot.hiddentrue if the window is currently hidden (by you or by an address pattern)
YouSelfBot.destroy()remove the window from the page completely: nodes, listeners, timers, the server connection. The conversation stays in the browser. An answer that was arriving is cut off — like when a tab is closed
YouSelfBot.init()bring the window back after destroy() — with the same conversation. Does nothing while the window is on the page
YouSelfBot.mountedtrue while the window is on the page; false after destroy()
YouSelfBot.windowshow many chat windows there are right now (a visitor can keep several conversations)

Editor, account area, checkout

You don't need code to keep the window off part of your site: the owner tells the builder "don't show the window on /app pages" — or you set the attribute data-hide-on="/app". hide() and show() are for cases an address can't describe: your modal opened, screen recording started, the person turned on "do not disturb".

Language ​

CallWhat it does
YouSelfBot.setLang('en')show the window in this language — without a reload. Stronger than data-lang and the page language. Returns the language the window now speaks: it won't switch to a language it doesn't have. setLang('') — choose automatically again
YouSelfBot.langthe language the window speaks right now (uk, en…)

The window picks its language itself, in this order: setLang(…) → the data-lang attribute → the page language (<html lang>) → the visitor's browser language → the owner's main language. It takes the first one the window has something to show in: a set of texts the owner provided in that language, or the window's own dictionary (Ukrainian and English). A single-page site that changes <html lang> when switching languages doesn't need to do anything — the window notices by itself. The owner sets texts in other languages in words ("make the window in English too"), see Chat window.

Who the visitor is and what they see ​

CallWhat it does
YouSelfBot.identify('user-42')same as data-user-token, but from code
YouSelfBot.identify({ token, auth })the same with a signed envelope
YouSelfBot.identify({ name })how to address the person — same as data-user-name
YouSelfBot.setContext('Step 2: payment')tell the bot what the person is looking at right now — for an SPA where the address doesn't change
YouSelfBot.registerTool('apply_settings', fn)give the assistant a page tool: fn(args) runs here, on the page, and its result (can be a Promise) goes to the assistant; returns a function that removes the handler
YouSelfBot.unregisterTool('apply_settings')remove a page tool handler
YouSelfBot.clearContext()remove that hint
YouSelfBot.sessionIdthe current conversation's identifier (to cross-check in "Conversations")
YouSelfBot.reset()start the conversation from scratch: after sign-out on your site. The copy of the conversation in the browser is erased; it stays on the person's other devices. The window also forgets who the person was identified as: identify(…) and the snippet's data-user-* attributes no longer apply until the page reloads — identify the new person with a new identify(…)

setContext takes precedence over what the window collects from the page itself. Use it where there's one page but many states: cart, checkout, a wizard step.

An object is also a value for actions. setContext({ project: 'p-42', lang: 'uk' }) doesn't just describe the page to the assistant: each flat "key — string, number or true/false" pair becomes a value that your action substitutes into its request in place of {{context.project}} — in the address, a header, a fixed body value. The model doesn't see these values in the request and doesn't write them in. Up to 20 keys (Latin letters, digits, _, -), values up to 300 characters; nested objects and lists are dropped. A header waiting for a value the page didn't pass isn't sent at all.

Call reset() after sign-out

Otherwise the next person at the same computer will continue someone else's conversation. If the next person also signs in (the site signs them as a different user), the window won't show them the other conversation on its own — but it will show it to a guest who isn't signed in.

Page tools ​

The assistant can do something itself on your page: apply settings in an editor, read code from a field, start an export in the browser.

  1. Declare the tool for the owner — in words to the builder ("add a page tool apply_settings: applies the settings; parameter lang — the language") or through the API as an action with client: true. You write the description and parameters for the model there, not in the page code.
  2. On the page, register a handler with the same name:
js
YouSelfBot.registerTool('apply_settings', async (args) => {
  editor.setLanguage(args.lang);
  return { applied: true };
});

The assistant gets the tool only on a page where the handler is registered: with every message, the window tells the server which handlers exist. When the model calls it, the window runs the handler and returns the result (any JSON value; an error — as a throw). The handler must answer within 30 seconds — otherwise the assistant hears that nothing was done. Works in a window with a streamed answer (the default).

Events ​

js
const stop = YouSelfBot.on('message', (e) => {
  if (e.from === 'visitor') analytics.track('chat_question');
});
// …
stop(); // or YouSelfBot.off('message', fn)

YouSelfBot.on(name, function) subscribes, YouSelfBot.off(name, function) unsubscribes; on also returns a function that unsubscribes. Subscriptions survive destroy() and init(). An error in your handler doesn't break the window — it just goes to the console.

EventWhenWhat's in the object
'ready'the window started (and again — after init())open, hidden, windows, lang
'open'the chat was opened: by the button, your open(), the invitation badgewindows — how many windows are on screen
'close'the chat was minimised or the last window was closedwindows
'message'a new message in the conversationfrom: visitor, assistant or operator; text — the message text as is (Markdown not rendered); window — the window number
'action_result'the assistant performed an action; a separate event per call, in call ordername — the action name; ok — the service answered without an error; buttons, files — what the action showed under the answer; args and result — the arguments and raw result, only if "pass the result to the page" is turned on for the action; window
'handoff'the state of the conversation with a person changedstatus: waiting (called), operator (the person arrived), resolved (closed), bot; window
'lang'the window switched to another languagelang
'hide' / 'show'the window was hidden or shownby: site (your call) or page (an address pattern)
'destroy'the window was removed from the page—

Example — apply on the page what an action returned:

js
YouSelfBot.on('action_result', (e) => {
  if (e.name === 'apply_settings' && e.ok) editor.apply(e.result);
});

The message text arrives in 'message' as is. The page the window sits on already has access to it anyway (the conversation is in its storage), so this event doesn't reveal anything new — but you still shouldn't send these texts to third-party services without the person's consent.

All snippet attributes ​

About the visitor — data-user-token, data-user-token-cookie, data-user-auth, data-user-name (the "Who this visitor is" section above); the API address — data-api. The rest — in the table below.

Attributes that override the dashboard ​

These values are stronger than what you tell the builder — so set them only when you really need to lock something:

AttributeValue
data-titlewindow title
data-greetingfirst message
data-accentaccent colour #12a150
data-positionleft or bottom-left — bottom left corner; right or bottom-right — bottom right. Without the attribute — as set in the dashboard; the widget skips an unknown value, writes a warning to the browser console and uses the dashboard setting
data-themeauto, light, dark
data-fit-site="off"don't adopt the site's palette
data-page-context="off"don't read the page the window is on
data-langthe window's language on this page: uk, en… Stronger than the page and browser language. The window won't speak a language it doesn't have (neither an owner's set nor its own dictionary) — it takes the next one in line
data-hide-onaddress patterns where there's no window, comma-separated: data-hide-on="/app, /checkout/*". /app — the page itself and everything under it (/app/edit, but not /apple); / — only the home page; an asterisk — anything (/docs/*, */edit); a site that navigates by hash — /#/editor. Works together with the patterns the owner gave the builder. On a single-page site the window disappears and comes back on navigation without a reload
data-noncethe nonce value of your CSP, if it's strict. Only needed for old browsers (Safari before 16.4): in the rest, the window sets its style in a way the CSP doesn't block

What's set here can't be changed from the conversation

This is the most common confusion: the owner asks "make the accent coffee-coloured", the builder honestly reports "done", and nothing changes on the site — because the colour is locked by an attribute. Remove the attribute if you want to control the colour from the dashboard.

Product on the page ​

The window reads the product by itself — from schema.org/Product markup (JSON-LD) or OpenGraph (og:type=product, product:price:amount). If the markup is there, you don't need to do anything; if it isn't, add either of the two, and a product card with the price will appear in the window by itself.

The "add to cart" button on the card clicks your site's buy button. The window recognises it by the markup of common shop engines, and after that — by the text on the button following the product title ("Купити", "В кошик", "Add to cart"). If your button is called something else, add the data-add-to-cart attribute to it — that's enough.

On a single-page site the product changes without a reload: the window re-reads the page every time it's opened.

Side panel and the site page ​

When the owner has set the chat as a side panel, the open panel pushes the page aside: the window sets a margin on <html> (margin-right or margin-left) as wide as the panel and removes it when the panel is closed, hidden (hide()), expanded to full screen or removed (destroy()). Your own <html> margin, if there was one, is restored as it was.

Fixed parts of the page (position: fixed) don't respond to the margin, so the window narrows edge-to-edge bars (header, bottom bar) itself for that time — with inline max-width, width, left, right — and restores them as they were. It looks for them only in the first three levels of markup below <body>. If your header sits deeper, or you calculate its width from the viewport (100vw), adjust it yourself — or ask the owner to set the panel "over the site" (then the window doesn't touch the page at all).

On screens up to 720 pixels wide there's no panel: a regular window is shown there.

Look and custom style ​

The window's look — from its shape to custom CSS — the owner changes in words, and it comes from the agent's settings, not from attributes (see Chat window). For anyone writing their own API client: all of this is in the response of GET /v1/widget/config — the fields design, css, texts, contacts, note.

A custom style only applies inside the window (it lives in its own Shadow DOM). The window strips rules about :host, @import, @font-face and non-https:// addresses from it; and if, in the minimised state, the style covered the page, the window removes it entirely and writes about it to the browser console.

WordPress ​

A ready-made plugin puts the window on your site without editing the theme. The API itself serves the archive — openly, without a key:

GET /wordpress-plugin.zip

It's built from the code of the same build that serves it, so it can't drift from the product.

Remember the warning above: filled-in plugin fields (title, greeting, colour) become attributes and override the dashboard; so does the position, if "Left" is selected. Leave the fields empty and the position at "Right" — and all of this can still be changed in the conversation. And check the API address if your setup keeps the site and the API on different hostnames.

YouSelfBot platform documentation