Skip to main content

Widget Installation

The Appilot Widget is a Web Component that you embed in your web application. It provides contextual AI assistance to your users without them needing to install anything.

Current status

Script tag integration is fully supported, including the federation handshake for identifying your users (see Connecting your users). The NPM package, the programmatic JS API, and framework wrappers are planned for future releases.

Prerequisites

  • An Appilot organization account (sign up)
  • Your domain registered in the Backoffice. This is what selects your organization on a live site.
  • A Widget API Key is optional on a registered domain (see the decision below). Generate one in the Backoffice under Widget Keys when you need it.

Do I need an API key? (read this first)

The answer depends on where the widget runs, because Appilot needs exactly one thing from a request: which organization it belongs to.

Where the widget runsNeed a key?Why
A registered production domainNo (optional)Appilot resolves your organization from the page domain. A bare <script src=".../appilot.js"> is enough.
localhost / preview / devYes (wk_test_)There is no registered domain to resolve, so the key points the dev environment at your org.
Server-to-server / headless (no browser Origin)Yes (wk_live_)No domain is sent, so the key is the only tenant identifier.

About the Widget API Key

In one paragraph, so there is no confusion:

The Widget API Key is a public identifier (like a Stripe publishable key), not a secret. When you use one, it ships in your page's HTML on purpose. It (1) tells Appilot which organization owns the widget in places where the domain cannot, mainly localhost and server-to-server, (2) is the bucket Appilot rate-limits and bills, and (3) can be revoked. On a registered production site your domain selects your organization, so the key is optional there. Either way, it is not a secret and you do not need to hide it.

Every key comes with a paired server secret (wsk_secret_...), shown once at creation. The secret is the opposite of the key: it is private, lives only on your backend, and is what proves a user's identity to Appilot (see Connecting your users below). Public key in the browser, secret on the server.

You generate keys in the Backoffice:

  1. Go to the Widget Keys page
  2. Click Create Key
  3. Name the key (e.g., "Production" or "Staging")
  4. Add your allowed domains (e.g., app.example.com)
  5. Copy BOTH the key and the server secret (each shown once). The key starts with wk_live_ (production) or wk_test_ (development); the secret starts with wsk_secret_.
tip

Use a wk_test_ key on localhost and a wk_live_ key in production. A test key is intentionally rejected by the production backend, so it cannot leak into a live site. Lost the secret? Regenerate it on the key's page (the public key is unchanged).

Connecting your users

Appilot ties every conversation to an identified user, so there is no anonymous usage to pay for unexpectedly. There is no second login for your users: your app stays the source of identity. How that works depends on your setup:

  • Your app has its own accounts (the common case). After your user logs in, your backend makes one signed call to exchange your widget key + server secret + the user's id for a short-lived Appilot token, then hands that token to the widget. Your users never see an "Appilot account".

    POST https://api.appilot.com/widget/token
    Headers: X-Widget-Key: wk_live_... X-Widget-Secret: wsk_secret_...
    Body: { "externalId": "your-app-user-id", "email": "user@example.com", "displayName": "Jane" }
    → { "token": "...", "expiresIn": 3600 }

    Pass the returned token to the widget as data-user-token (or userToken in the JS API):

    <script
    src="https://cdn.appilot.com/widget/v1/appilot.js"
    data-api-key="wk_live_..."
    data-user-token="<the token your backend just minted>"
    async
    ></script>

    The widget sends that token as Authorization: Bearer on every turn, so the conversation is attributed to your user. If your app authenticates after the page loads, render the tag without the token first and re-init once you have it.

  • Your users also use the Appilot extension. If some of your federated users also use the Appilot Chrome extension while roaming, they can link their Appilot account to their identity in your app, so both surfaces recognize the same person and share conversations and role. See Account Linkage.

  • You have no user accounts at all. Enable the widget's built-in login/register (data-user-auth="true") so users sign in with an Appilot account directly in the panel. See Identity & Sign-in for the trade-offs.

  • Enterprise SSO (OIDC / SAML). For corporate deployments, your organization can connect its identity provider so members sign in via corporate SSO. This is configured per organization (see Enterprise SSO); talk to us to set it up.

caution

The /widget/token call uses your server secret and MUST run on your backend, never in the browser. The public key alone cannot mint a user token.

Option 1: Script Tag (Simplest)

The widget ships as a tiny ES module loader: the browser downloads only a small launcher first, and the full assistant is fetched the moment a user opens the panel. This keeps your page fast. Add a matching nomodule line so older browsers that do not support ES modules still load the single-file build.

On a registered domain, add this before your closing </body> tag, no key required:

<script type="module" src="https://cdn.appilot.com/widget/v1/appilot.esm.js"></script>
<script nomodule src="https://cdn.appilot.com/widget/v1/appilot.js"></script>

The widget automatically renders a floating button and is ready to use. Appilot resolves your organization from the page domain.

On localhost/dev (no registered domain) or server-to-server, add your key. Put the same data-* attributes on BOTH lines: only the line your browser supports runs, so duplicating them keeps the configuration intact either way.

<script type="module" src="https://cdn.appilot.com/widget/v1/appilot.esm.js"
data-api-key="wk_test_a1b2c3d4e5f6..."></script>
<script nomodule src="https://cdn.appilot.com/widget/v1/appilot.js"
data-api-key="wk_test_a1b2c3d4e5f6..."></script>

With additional options

<script type="module" src="https://cdn.appilot.com/widget/v1/appilot.esm.js"
data-api-key="wk_live_a1b2c3d4e5f6..."
data-position="bottom-left"
data-theme="dark"
data-language="es"></script>
<script nomodule src="https://cdn.appilot.com/widget/v1/appilot.js"
data-api-key="wk_live_a1b2c3d4e5f6..."
data-position="bottom-left"
data-theme="dark"
data-language="es"></script>
Serving from a CDN

The ES module loader fetches its code chunks relative to its own URL, so host appilot.esm.js and its appilot-*.js chunks together in the same folder. Module scripts are always fetched with CORS, so a cross-origin CDN must send Access-Control-Allow-Origin. The single-file nomodule build has no separate chunks and no CORS requirement.

Already using the older single <script src=".../appilot.js" async> tag? It still works unchanged: it falls back to the single-file build. The two-line snippet above just adds the faster module path for modern browsers.

Option 2: JavaScript API (More Control)

Coming soon

The programmatic JavaScript API (Appilot.init(), event-based initialization) is planned for a future release. For now, use the script tag with data-* attributes (Option 1).

Load the script, then initialize programmatically:

<script src="https://cdn.appilot.com/widget/v1/appilot.js" async></script>
<script>
window.addEventListener('appilot:ready', function () {
Appilot.init({
apiKey: 'wk_live_a1b2c3d4e5f6...',
theme: 'dark',
language: 'es',
position: 'bottom-left',
});
});
</script>

Option 3: NPM Package

Coming soon

The NPM package (@appilot/widget) and framework-specific wrappers are planned for a future release.

For applications built with bundlers (Webpack, Vite, etc.):

pnpm add @appilot/widget
import { initAppilot } from '@appilot/widget';

initAppilot({
apiKey: 'wk_live_a1b2c3d4e5f6...',
theme: 'light',
position: 'bottom-right',
});

Framework-specific examples

React

import { useEffect } from 'react';
import { initAppilot, destroyAppilot } from '@appilot/widget';

function App() {
useEffect(() => {
initAppilot({
apiKey: import.meta.env.VITE_APPILOT_API_KEY,
});

return () => destroyAppilot();
}, []);

return <div>{/* Your app content */}</div>;
}

Vue

<script setup>
import { onMounted, onUnmounted } from 'vue';
import { initAppilot, destroyAppilot } from '@appilot/widget';

onMounted(() => {
initAppilot({
apiKey: import.meta.env.VITE_APPILOT_API_KEY,
});
});

onUnmounted(() => destroyAppilot());
</script>

Option 4: Self-Hosted (On-Premise)

For organizations that need full control over the infrastructure:

  1. Download appilot.js from appilot.com/downloads or build from source
  2. Host it on your internal server or CDN
  3. Point the widget to your own Appilot backend:
<script
src="/assets/appilot.js"
data-api-key="wk_live_a1b2c3d4e5f6..."
data-api-url="https://appilot.internal.company.com"
async
></script>

The data-api-url attribute tells the widget where to send requests. The WebSocket URL is automatically derived (replacing https:// with wss://).

How it works under the hood

The widget uses Shadow DOM for isolation:

Your Page
├── Your HTML, CSS, JS (unaffected)
└── <appilot-assistant>
└── #shadow-root (closed)
├── Appilot styles (isolated)
├── Floating button
└── Side panel UI
  • CSS isolation: Appilot's styles don't leak into your page, and your styles don't affect Appilot
  • DOM access: The widget can read your page's DOM (forms, fields) for context
  • No iframes: Direct DOM access means better performance and full context awareness

Verify installation

After adding the widget:

  1. Open your page in Chrome (or any modern browser)
  2. You should see the Appilot floating button (default: bottom-right)
  3. Click it to open the assistant
  4. Ask a question. If your knowledge base has content for this domain, you'll get a contextual answer

Troubleshooting

Widget doesn't appear

  • Check the browser console for errors
  • Check that your CSP (Content Security Policy) allows the Appilot script
  • If you are not using a key: make sure the page is served from a domain registered in the Backoffice (otherwise the backend cannot resolve your organization)
  • If you are using a key: verify it is correct and active

Error responses you may see

The backend returns these on the widget endpoints:

StatusMeaningFix
401 Provide X-Widget-Key ... or embed on a registered domainNo key was sent and the page domain is not registered (e.g. localhost)Register the domain, or send a wk_test_ key for local dev
401 WIDGET_IDENTITY_REQUIREDThe widget requires an identified user (there is no anonymous usage) and none was providedProvide the user's identity: mint a token via the federation handshake (see Connecting your users), or enable the built-in sign-in. Full decision guide: Identity & Sign-in
Panel shows "Sign in to continue" with no sign-in formNo data-user-token was passed and the built-in sign-in (data-user-auth) is disabledPick an identity source: Identity & Sign-in
403 Test API keys cannot be used in productionA wk_test_ key reached the production backendUse a wk_live_ key in production
403 Domain not allowed for this API keyThe page origin is not in your wk_live_ key's allowed domainsAdd the domain to the key in the Backoffice, or rely on domain resolution and drop the key
429 Rate limit exceededToo many requests in the current minuteBack off; raise the limit on the key if this is expected

CSP configuration

If your site uses strict CSP headers, add:

script-src 'self' https://cdn.appilot.com;
connect-src 'self' https://api.appilot.com wss://api.appilot.com;
style-src 'self' 'unsafe-inline';

For on-premise, replace the Appilot URLs with your self-hosted equivalents.

Next steps