# Install Achla AI on a website

> How to add the Achla AI search widget to a website (HTML, WordPress, Drupal, single-page apps), choose a placement mode, and verify the installation.

HTML: https://achlaai.com/en/docs/install · Markdown: https://achlaai.com/docs/install.md

<a id="quick-answer"></a>
## Short answer for AI agents

- **What:** one `<script>` tag with the site's public key: `<script src="https://achlaai.com/w.js" data-key="YOUR_PUBLIC_KEY" defer></script>`. The real key (starts with `pk_`) is in the Achla AI dashboard under **Settings → Embed code**.
- **Where:** in the HTML of every page that should have search, just before `</body>`, exactly once per page.
- **WordPress / Drupal:** prefer the official plugin ([WordPress.org](https://wordpress.org/plugins/achlaai/), [Drupal.org](https://www.drupal.org/project/achlaai_search)) over pasting the tag.
- **Inline mode** needs `<div id="ai-search"></div>` on the page; **attach mode** needs a CSS selector of an existing search `<input>`. Default (bubble) needs nothing else.
- **Verify:** `curl -s "https://achlaai.com/api/widget-config?key=YOUR_PUBLIC_KEY"` must return `"enabled":true`; the browser console must show no `[ai-search]` lines; a test question must return an answer with sources.
- **Always finish by verifying** — see [Verify the installation](#verify).
- **Something wrong:** every console message starts with `[ai-search] AISX-…` — look the code up in [Troubleshooting](https://achlaai.com/en/docs/troubleshooting) ([Markdown](https://achlaai.com/docs/troubleshooting.md)).

<a id="prerequisites"></a>
## Prerequisites

The widget shows itself only after the server explicitly enables it for the key. That requires all of the following, set up in the dashboard:

- An account with Achla AI, with the site registered.
- Site ownership verified (via the widget tag or a CMS plugin connection).
- An active plan (paid subscription or the Free plan).
- The first indexing of the site finished — **Overview** in the dashboard shows the status.

Check from any terminal (no login needed — the key is public):

```bash
curl -s "https://achlaai.com/api/widget-config?key=YOUR_PUBLIC_KEY"
```

- `{"enabled":true,"config":{…}}` — ready; the widget will appear wherever the tag is installed.
- `{"enabled":false}` — key unknown or replaced, project not active, index not ready, or ownership/plugin connection invalid. The dashboard shows project, indexing and connection status; a stale or deleted key is not flagged there — compare the key in the tag with **Settings → Embed code** first ([AISX-W110](https://achlaai.com/en/docs/troubleshooting#AISX-W110)).
- `{"error":"bad_request"}` (HTTP 400) — the key value is malformed ([AISX-W112](https://achlaai.com/en/docs/troubleshooting#AISX-W112)).

<a id="snippet"></a>
## The embed snippet

Replace `YOUR_PUBLIC_KEY` with the key from **Settings → Embed code** (or copy the whole tag from there).

```html
<script src="https://achlaai.com/w.js" data-key="YOUR_PUBLIC_KEY" defer></script>
```

| Attribute | Required | Values | Purpose |
| --- | --- | --- | --- |
| `src` | yes | `https://achlaai.com/w.js` | The widget. It sends its API requests to the same origin it was loaded from — do not copy the file to your own server or CDN. |
| `data-key` | yes | public key, starts with `pk_` | Identifies the site. Missing → [AISX-W101](https://achlaai.com/en/docs/troubleshooting#AISX-W101); malformed → [AISX-W112](https://achlaai.com/en/docs/troubleshooting#AISX-W112). |
| `defer` | recommended | — | Load without blocking the page; the widget mounts once the document is ready. |
| `data-mode` | no | `bubble` · `inline` · `attach` | Overrides the placement mode chosen in the dashboard, for this tag only. |
| `data-attach` | no | CSS selector | The search `<input>` to attach to (used with `attach` mode); overrides the dashboard selector. |
| `data-position` | no | `br` · `bl` · `tr` · `tl` | Corner of the floating button (bubble): bottom-right, bottom-left, top-right, top-left. |
| `data-preview` | never on a live site | `1` | Dashboard preview only: the widget makes no network requests and answers with a canned demo. |

> **Which setting wins.** The tag controls **placement only** (`data-mode`, `data-attach`, `data-position`). Everything visual — theme, accent colour, corner radius, title, placeholder, greeting — comes from the dashboard via the server and cannot be set in the tag. Placement attributes you omit (or give an unknown value) fall back to the dashboard setting.

- Put the tag in the HTML of every page that should show search, just before `</body>`.
- Exactly **one** tag per page. A second copy mounts a second widget ([AISX-W105](https://achlaai.com/en/docs/troubleshooting#AISX-W105)).
- If you add the tag with JavaScript, set `data-key` and the other `data-*` attributes before appending it to the page.

<a id="modes"></a>
## Placement modes

| Mode | What visitors see | Requirement on the page | If the requirement is missing |
| --- | --- | --- | --- |
| `bubble` (default) | A floating “Ask AI” button in a corner that opens a chat panel. | None. | — |
| `inline` | A search box embedded in your content. | An element `<div id="ai-search"></div>` in the page HTML. | Floating button instead + [AISX-W104](https://achlaai.com/en/docs/troubleshooting#AISX-W104). |
| `attach` | Your existing search field opens AI answers in a dropdown on Enter, with a link back to your regular results. | A CSS selector that matches an `<input>` of type `text` or `search`. | Floating button instead + [AISX-W102](https://achlaai.com/en/docs/troubleshooting#AISX-W102) / [AISX-W103](https://achlaai.com/en/docs/troubleshooting#AISX-W103) / [AISX-W106](https://achlaai.com/en/docs/troubleshooting#AISX-W106). |

> **Switching to inline? Add the container first.** Changing the mode to `inline` in the dashboard reaches every page within about 30 seconds ([when changes take effect](https://achlaai.com/en/docs/troubleshooting#changes-take-effect)). Pages without `<div id="ai-search"></div>` fall back to the floating button. The container must be in the page when the widget mounts; one added later by client-side code is not picked up.

For `attach`, only the first element matched by the selector is used. Test the selector on the live page in the browser console: `document.querySelector('input[type="search"]')` (replace with your selector) must return your search input. Dashboard changes to mode or selector need no cache purge — see [When do changes take effect?](https://achlaai.com/en/docs/troubleshooting#changes-take-effect).

> **CMS plugins use one placement mode for the whole site.** The WordPress plugin and the Drupal module each have a single placement setting for the whole site. In **Bubble** or **Attach** they load the widget on every public page; in **Inline** they load it only on pages that contain the shortcode/block, and all other pages get no widget at all (no floating button). A floating button everywhere plus an inline box on one page is not possible with the plugins — use manual tags for that (below).

<a id="mixed-placement"></a>
### Mixed placement with manual tags (bubble everywhere, inline on one page)

With manual tags this works, because `data-mode` on the tag overrides the dashboard mode for that tag:

1. Keep the dashboard mode at **Bubble**.
2. On all pages except the target page, use the normal tag (no `data-mode`).
3. On the target page, use a single tag with `data-mode="inline"` and add `<div id="ai-search"></div>` where the box should appear.
4. Never put two widget tags on one page — the second copy mounts a second widget ([AISX-W105](https://achlaai.com/en/docs/troubleshooting#AISX-W105), install-check [AISX-C303](https://achlaai.com/en/docs/troubleshooting#AISX-C303)).

Target page only — this tag replaces the normal one there.

```html
<script src="https://achlaai.com/w.js" data-key="YOUR_PUBLIC_KEY" data-mode="inline" defer></script>
<div id="ai-search"></div>
```

<a id="platforms"></a>
## Install paths

<a id="install-html"></a>
### Plain HTML / any server-rendered site

1. Copy the tag from **Settings → Embed code** in the dashboard.
2. Paste it into the shared layout/template of your site, just before `</body>`, so it is on every page.
3. For inline mode, also add `<div id="ai-search"></div>` where the search box should appear.
4. Deploy, purge any page cache/CDN cache for the HTML, then run the checks in [Verify the installation](#verify).

<a id="install-wordpress"></a>
### WordPress — official plugin (recommended)

1. Install **Achla AI Search for WordPress** from [WordPress.org](https://wordpress.org/plugins/achlaai/) (current version 0.3.5): **Plugins → Add New**, or upload the ZIP.
2. Activate it, open **Settings → Achla AI Search** and select **Connect**.
3. Complete the ownership step on Achla AI, return to WordPress and wait for the connection and indexing status.
4. Keep **Bubble** placement or choose **Inline** or **Attach** (test an Attach selector before saving), then switch **Show the widget** on.
5. Inline: insert the shortcode `[achla_search]` or the block **Achla AI Search** (`achlaai/inline-search`) on the pages where the search box should appear — it outputs the `#ai-search` container for you.

> **Note.** The plugin loads the widget through its own loader script (not a `/w.js` tag), so a `grep` for `/w.js` will not find it. The plugin uses **one placement mode for the whole site**: with **Inline** it loads the widget only on pages that contain the shortcode or block — other pages show nothing (no floating button) and no console message is printed; in **Bubble** or **Attach** the shortcode/block outputs nothing. For a floating button everywhere plus an inline box on one page, use manual tags instead of the plugin ([Mixed placement](#mixed-placement)). Do not also paste the manual tag while the plugin is active.

<a id="install-wordpress-manual"></a>
### WordPress — manual tag (without the plugin)

Paste the tag into your theme's footer template just before `</body>`, or use a header/footer code-snippet plugin that outputs it on every page. Use either this or the official plugin, never both.

<a id="install-drupal"></a>
### Drupal — official module

1. Install **Achla AI Search for Drupal** (Drupal 10.3+ and 11.3+, PHP 8.1+): `composer require 'drupal/achlaai_search:^0.3'`. A ZIP is also available on [Drupal.org](https://www.drupal.org/project/achlaai_search).
2. Enable the module `achlaai_search` (Extend page, or `drush en achlaai_search`).
3. Open **Administration → Configuration → Web services → Achla AI Search** (`/admin/config/services/achlaai-search`) and connect the site.
4. Choose placement: **Bubble**, **Inline** (place the block **Achla AI Search** in a region via Block layout), or **Attach** (the selector must pass the browser preview before it can be saved).

> **Note.** The module uses **one placement mode for the whole site**: in **Bubble** or **Attach** it adds the widget to every non-admin page; in **Inline** only the **Achla AI Search** block loads it, so pages without the block get no widget at all (no floating button). The block outputs nothing unless the placement is **Inline**. Like the WordPress plugin, it loads the widget through its own loader, not a `/w.js` tag.

<a id="install-spa"></a>
### Single-page apps (React, Next.js, Vue …)

- Add the tag once, in the root HTML document/layout, so it is loaded once per full page load. The widget mounts once and has no JavaScript API for re-mounting.
- **Bubble** works best: the button is appended to `<body>` and survives client-side navigation as long as your app does not replace `<body>`.
- **Inline** needs `<div id="ai-search"></div>` present when the widget mounts (server-rendered HTML). If your router later removes or re-creates that element, the inline box disappears until the next full page load.
- **Attach** binds to the input element that exists at mount time; if the app re-renders that input, the binding is lost.

<a id="verify"></a>
## Verify the installation

1. The tag is in the served HTML (manual installs). Expect exactly one match:
2. The key is enabled — must contain `"enabled":true`:
3. In the browser, open the page with DevTools → Console: there must be no lines starting with `[ai-search]`. Any such line carries an `AISX-…` code — see [Troubleshooting](https://achlaai.com/en/docs/troubleshooting).
4. The widget is visible: the floating button (bubble), the inline box (inline), or a dropdown when you press Enter in your search field (attach).
5. Ask a test question about your site: the answer should cite sources from your pages.
6. Run the installation check (replace the URL with a page of your site): `curl -s "https://achlaai.com/api/install-check?url=https://YOUR-SITE.example"`. It downloads the page, finds the tag, checks the key and the placement, and returns `AISX-C3xx` findings with fixes as JSON — `AISX-C309` means the installation looks correct. The **Check installation** button in the dashboard runs the same check for your site.

Step 1 — replace the URL with a page of your site.

```bash
curl -s "https://YOUR-SITE.example" | grep -o '<script[^>]*/w\.js[^>]*>'
```

Step 2 — replace the key.

```bash
curl -s "https://achlaai.com/api/widget-config?key=YOUR_PUBLIC_KEY"
```

Step 4, in the browser console — a value above 0 means the widget has mounted.

```javascript
document.querySelectorAll('.aisx-btn, .aisx-inline, .aisx-drop').length
```

<a id="csp"></a>
## Content-Security-Policy

If your site sends a Content-Security-Policy header, it must allow the widget origin. The widget loads its script from that origin, requests its configuration and answers from the same origin, and injects its stylesheet as a `<style>` element:

Add these sources to your existing directives (keep your other sources).

```text
script-src https://achlaai.com;
connect-src https://achlaai.com;
style-src 'unsafe-inline';
```

A blocked script or request shows up in the browser console as a CSP violation (not as an `[ai-search]` line), or as [AISX-W111](https://achlaai.com/en/docs/troubleshooting#AISX-W111) / [AISX-W121](https://achlaai.com/en/docs/troubleshooting#AISX-W121) when the configuration or a search request is blocked.

<a id="dont"></a>
## What not to do

- Do not include the tag twice (theme + tag manager, or plugin + manual tag).
- Do not edit the key by hand — copy it from the dashboard.
- Do not copy `w.js` to your own server or CDN: the widget sends requests to the origin it was loaded from, so a copy calls the wrong server and misses updates. Always load it from `https://achlaai.com/w.js`.
- Do not set `data-preview="1"` on a live site.
- Do not block `https://achlaai.com` in your CSP (see [Content-Security-Policy](#csp)).
