# Atria by BeMagic: developer docs > Atria is a humanized AI support chat. It answers your customers with warmth, learns your business from your knowledge base, and hands the conversation to a human operator the moment it matters. Embed it on any website with one script tag. Source: https://atria.bemagic.online/developers ## What Atria does - An AI support agent answers website visitors in a chat widget, in their own language, using your knowledge base (uploaded documents and edited entries) and the tone you configure. - When a visitor needs a person, or the agent can't help, it hands the conversation to an online human operator, who sees the full history. Operators can also take over any conversation at any time. - While chatting, operators get the Atria assistant side by side: it analyzes the conversation, suggests what to do and translates suggested replies, and they can search the knowledge base directly. - Resolved conversations become knowledge suggestions that the team reviews before they're added, so the agent learns without drifting from its identity. - Sessions show who the visitor is, the page they're on, their browser and the whole conversation. Idle or finished conversations close on their own. - The widget is one script tag, works with any frontend framework, and keeps the conversation across page loads. ## See it in action Recorded from the product on a demo coffee shop, Lumen Coffee: - [Video: A visitor asks, the agent answers](https://atria.bemagic.online/showcase/visitor-chat.mp4): A visitor on a coffee shop's website opens the Atria chat and asks about shipping. The agent greets her by name and answers from the shop's knowledge base. She minimizes the chat, and the next reply shows up as an unread badge on the launcher. - [Video: A person steps in with the whole story](https://atria.bemagic.online/showcase/handoff.mp4): A visitor on a phone asks for a person. The agent helps right away and calls the team. An operator sees the request, takes over the conversation, asks Atria for the next step, and replies. The visitor sees who joined and gets the answer in the same chat. - [Screenshot: Atria at your operator's side](https://atria.bemagic.online/showcase/operator-console.png): Search what the agent knows, get a ready-to-send answer with its sources, and a suggested next step for this exact conversation. - [Screenshot: Learns from chats your team solved](https://atria.bemagic.online/showcase/admin-learning.png): When a person solves something the agent didn't know, Atria drafts it as knowledge. You review and edit it before the agent ever uses it. - [Screenshot: Know who you're talking to](https://atria.bemagic.online/showcase/admin-sessions.png): Every session shows the visitor, the page they were on, their device, and how the conversation ended. - [Screenshot: Teach it what it knows](https://atria.bemagic.online/showcase/admin-agent.png): Upload documents or write answers. Atria organizes them by subject and looks up the right one before every reply. - [Screenshot: Your support at a glance](https://atria.bemagic.online/showcase/admin-agents.png): Agents, what each one knows, what's waiting for review, and who's talking to them right now. - [Screenshot: Bring your team](https://atria.bemagic.online/showcase/admin-team.png): Invite operators. They see conversations and step in, while agents, knowledge and settings stay with you. ## Quick start Atria is one classic script tag. It adds a support launcher to the corner of your page and opens the chat in an isolated iframe. - In the Atria admin, open Agents, pick an agent and copy its snippet. It already contains your agent ID (it starts with `agt_`). - Paste it right before the closing `` tag of every page that should show the chat. A `defer` script in `` works too. `index.html`: ```html ``` - Prefer attributes over query strings? `data-agent-id` works the same way. ```html ``` - Reload your site and click the launcher. Messages show up live in Admin → Sessions. ## How the widget works Built to be a polite guest on any page: tiny, isolated, and lazy. - The loader is about 3.5 kB gzipped, with no dependencies. It renders the launcher inside its own shadow root (`#atria-support`), so your CSS can't break it and its CSS can't leak into your page. - The chat itself is an iframe served from `https://atria.bemagic.online`, created only when a visitor opens the launcher. Nothing heavy loads until then. - All network requests happen inside the iframe. Your page only needs to allow loading the script and framing the chat. - The visitor gets an anonymous ID stored in your site's `localStorage` (`atria:visitor-id`). The open conversation and whether the chat is open are kept in `sessionStorage`, so the chat follows visitors across pages. - When the chat is minimized, new replies show up as an unread badge on the launcher. Caveats: - It must be a classic script: not `type="module"`, not imported into your bundle, and not moved into a web worker (Partytown or similar). The loader reads its own ` ``` Caveats: - Limits: `id` up to 200 characters, `name` up to 120, a valid `email`, and up to 20 `metadata` fields whose values are strings (up to 500 characters), numbers, booleans or `null`. - Never put secrets, tokens or passwords in `AtriaSettings`. Anyone who opens your page can read it. - These fields are shown to your team as provided. Treat them as hints, not as proof of identity. ## Content Security Policy If your site sends a CSP header, allow Atria in two directives. That's all. - Add Atria's origin to `script-src` (to load the loader) and `frame-src` (to show the chat). No `connect-src`, `style-src` or `img-src` changes are needed: the launcher's styles use constructable stylesheets, which `style-src` doesn't block. ```http Content-Security-Policy: script-src 'self' https://atria.bemagic.online; frame-src https://atria.bemagic.online; ``` - Using nonces with `'strict-dynamic'`? Add your nonce to the Atria tag. The loader never injects other scripts. ```html ``` Caveats: - If you have no `frame-src`, browsers fall back to `child-src`, then `default-src`. Add Atria's origin to whichever one you use. - `X-Frame-Options` on your site doesn't matter. It controls who can frame your pages, not which frames you show. ## Single-page apps and page transitions Client-side routing, view transitions and body swaps are handled for you. - Load the script once, in your root layout or `index.html`. Route changes in React, Vue, Svelte, Angular and friends don't touch it. - If a framework replaces the whole `` (Turbo, htmx boost, Astro view transitions, Livewire `wire:navigate`, swup, Barba), the launcher puts itself back automatically. - On full page loads, the open conversation and the panel state are restored, so visitors pick up where they left off. ## Privacy and consent What the widget stores, and when it talks to Atria. - Until a visitor opens the chat, the only request to Atria is loading `widget.js`. The widget sets no cookies on your site. - It stores an anonymous visitor ID in `localStorage`, plus the current conversation ID and panel state in `sessionStorage`. A session is created only when the visitor sends a first message, together with the page URL, referrer, browser language and time zone. - If your consent banner must approve support tools first, inject the script after consent: ```js function loadAtria() { const script = document.createElement("script"); script.src = "https://atria.bemagic.online/widget.js?agentId=agt_YOUR_AGENT_ID"; script.defer = true; document.body.appendChild(script); } ``` ## Troubleshooting The usual suspects, in order. - No launcher at all? Open the console. A CSP error mentioning `script-src` means you need to allow `https://atria.bemagic.online`. No error? Check that the tag isn't `type="module"` and wasn't bundled, combined or delayed by an optimizer. - Launcher shows, but the panel is blank? Add `https://atria.bemagic.online` to `frame-src` (or `child-src` / `default-src`). - The chat says the agent is unavailable? The `agentId` is missing or wrong. Copy the snippet again from Admin → Agents. - React hydration warnings in development? Something added nodes to `` before hydration. Load Atria after hydration (see your framework guide) and the warning goes away. - The launcher covers your own floating button? It sits in the bottom-right corner, above everything else. Move your button, or hide one of them on small screens. # Framework guides ## Meta-frameworks ### Next.js (App Router) Use `next/script` in the root layout, so the chat loads after hydration on every route. - Add the script to `app/layout.tsx`: `app/layout.tsx`: ```tsx import Script from "next/script"; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ``` - If you use `kit.csp`, add Atria to the directives: `svelte.config.js`: ```js const config = { kit: { csp: { directives: { "script-src": ["self", "https://atria.bemagic.online"], "frame-src": ["https://atria.bemagic.online"], }, }, }, }; export default config; ``` Caveats: - `%sveltekit.nonce%` only matters when `kit.csp` uses nonces. It's harmless otherwise. - Don't put the tag in a `.svelte` component. Scripts rendered by components don't run on client-side navigation. ### Astro Add it to your base layout with `is:inline`. - `is:inline` tells Astro to leave the tag exactly as written: `src/layouts/Layout.astro`: ```astro ``` Caveats: - Without `is:inline`, Astro may process the tag as part of your bundle. - With view transitions (``), Astro swaps the body on every navigation. The launcher re-attaches and the conversation continues: you don't need `transition:persist` or `data-astro-rerun`. - Works in every output mode: static, server and hybrid. ### Angular Add it to `src/index.html`, not to a component template. - Paste the tag before the closing body tag: `src/index.html`: ```html ``` - Loading it from code (for example, after consent)? Run it outside the Angular zone, so the widget's events don't trigger change detection: ```ts this.ngZone.runOutsideAngular(() => { const script = document.createElement("script"); script.src = "https://atria.bemagic.online/widget.js?agentId=agt_YOUR_AGENT_ID"; document.body.appendChild(script); }); ``` Caveats: - Angular removes ` ``` - Need to load it conditionally? Use an effect, and don't remove it on cleanup: ```tsx useEffect(() => { if (!user || document.getElementById("atria-script")) return; window.AtriaSettings = { visitor: { id: user.id, name: user.name } }; const script = document.createElement("script"); script.id = "atria-script"; script.src = "https://atria.bemagic.online/widget.js?agentId=agt_YOUR_AGENT_ID"; document.body.appendChild(script); }, [user]); ``` Caveats: - Keep Atria's tag a classic script (no `type="module"`), even though your own entry is a module. - React Strict Mode runs effects twice in development. The widget starts only once per page anyway. - For TypeScript, declare `interface Window { AtriaSettings?: unknown }` in a `.d.ts` file. ### Vue Add the tag to `index.html` in Vite projects. - Paste it before the closing body tag: `index.html`: ```html
``` Caveats: - Vue doesn't run ` ``` Caveats: - Using SvelteKit? See the SvelteKit guide: the file is `src/app.html`. ### Solid, Preact and Lit Any Vite-based app works the same way: one tag in `index.html`. - Paste it before the closing body tag: `index.html`: ```html ``` Caveats: - Lit and other web-component apps need nothing special: the widget lives in its own shadow root and won't collide with your components. ### Ember Add the tag to `app/index.html`. - Paste it after `{{content-for "body-footer"}}`: `app/index.html`: ```html {{content-for "body-footer"}} ``` Caveats: - With `ember-cli-content-security-policy`, add Atria to `script-src` and `frame-src`. ### Ionic and Capacitor Works in hybrid apps through the WebView, with the same `index.html` tag. - Paste it before the closing body tag of `src/index.html` (Angular) or `index.html` (React and Vue): `index.html`: ```html ``` Caveats: - The device must be online: the widget always loads from Atria, never from bundled assets. - If the app sets a CSP `` tag, add Atria to `script-src` and `frame-src`. ## Static sites & docs ### Plain HTML, jQuery and Alpine.js Paste the tag on every page, before ``. - That's the whole install: `index.html`: ```html ``` Caveats: - A server include or a shared footer partial keeps it in one place. - htmx with `hx-boost` swaps the body on navigation. The launcher re-attaches automatically. ### Hugo, Jekyll and Eleventy Add the tag to your base layout. - Hugo: `layouts/_default/baseof.html`: ```html {{ block "main" . }}{{ end }} ``` - Jekyll: `_layouts/default.html`: ```html {{ content }} ``` - Eleventy: `_includes/base.njk`: ```html {{ content | safe }} ``` Caveats: - If you set a CSP in host headers (`_headers` on Netlify and Cloudflare Pages, `vercel.json`), add Atria to `script-src` and `frame-src`. ### Docusaurus Register the script in the site config. - Add it to `scripts`: `docusaurus.config.ts`: ```ts const config = { scripts: [{ src: "https://atria.bemagic.online/widget.js?agentId=agt_YOUR_AGENT_ID", defer: true }], }; export default config; ``` ### VitePress Add the script to the site `head`. - Add it to `head`: `.vitepress/config.ts`: ```ts import { defineConfig } from "vitepress"; export default defineConfig({ head: [["script", { src: "https://atria.bemagic.online/widget.js?agentId=agt_YOUR_AGENT_ID", defer: "" }]], }); ``` ## Server-rendered ### Ruby on Rails (with Turbo) Add the tag to your application layout. - Put it in ``, so Turbo loads it once and keeps it across visits: `app/views/layouts/application.html.erb`: ```erb <%= javascript_include_tag "https://atria.bemagic.online/widget.js?agentId=agt_YOUR_AGENT_ID", defer: true, nonce: true %> ``` - Allow Atria in your CSP initializer: `config/initializers/content_security_policy.rb`: ```ruby Rails.application.configure do config.content_security_policy do |policy| policy.script_src :self, "https://atria.bemagic.online" policy.frame_src "https://atria.bemagic.online" end end ``` Caveats: - Turbo Drive replaces `` on every visit. The launcher re-attaches automatically and the conversation continues. - Don't pin it with importmap: it's a classic script, not an ES module. ### Laravel (Blade, Livewire, Inertia) Add the tag to your main Blade layout. - Blade and Livewire: `resources/views/layouts/app.blade.php`: ```blade ``` - Inertia: add it to the root view, outside `@inertia`: `resources/views/app.blade.php`: ```blade @inertia ``` Caveats: - Livewire's `wire:navigate` swaps the body. `data-navigate-once` keeps Livewire from re-running the tag, and the launcher re-attaches by itself. - With `spatie/laravel-csp`, add Atria to the script and frame directives of your policy. ### Django and Flask Add the tag to your base template. - Paste it before the closing body tag: `templates/base.html`: ```html {% block content %}{% endblock %} ``` - With `django-csp` 4, allow Atria in settings: `settings.py`: ```python CONTENT_SECURITY_POLICY = { "DIRECTIVES": { "script-src": ["'self'", "https://atria.bemagic.online"], "frame-src": ["https://atria.bemagic.online"], }, } ``` Caveats: - Flask (Jinja) is identical: add the tag to `templates/base.html`. With Flask-Talisman, add Atria to `script-src` and `frame-src` in `content_security_policy`. ### Phoenix (LiveView) Add the tag to the root layout. - Paste it before the closing body tag: `lib/my_app_web/components/layouts/root.html.heex`: ```html {@inner_content} ``` Caveats: - LiveView only patches its own container, so live navigation never touches the widget. - If you set a CSP with `put_secure_browser_headers`, add Atria to `script-src` and `frame-src`. ### ASP.NET Core (MVC, Razor Pages, Blazor) Add the tag to the shared layout or the Blazor host page. - MVC and Razor Pages use `Views/Shared/_Layout.cshtml` (or `Pages/Shared/_Layout.cshtml`). Blazor uses `Components/App.razor` or `wwwroot/index.html`. `Views/Shared/_Layout.cshtml`: ```html @RenderBody() ``` Caveats: - Blazor's enhanced navigation patches the page. If the launcher is ever removed, it puts itself back. ## Website builders & CMS ### WordPress Enqueue the script from your theme, or paste it with a header and footer plugin. - With a plugin such as WPCode, add the tag as a site-wide footer snippet. Or enqueue it from your child theme: `functions.php`: ```php add_action('wp_enqueue_scripts', function () { wp_enqueue_script( 'atria', 'https://atria.bemagic.online/widget.js?agentId=agt_YOUR_AGENT_ID', [], null, ['strategy' => 'defer', 'in_footer' => true] ); }); ``` Caveats: - Pass `null` as the version, or WordPress appends `ver=` to the URL. Harmless, but noisy. - Exclude `atria` from "delay JavaScript", "combine JS" and similar features in WP Rocket, Autoptimize, LiteSpeed Cache, Perfmatters and friends. - Cloudflare Rocket Loader can rewrite the tag: add `data-cfasync="false"` to it. - Edit a child theme, not the parent theme, or the next theme update erases your change. ### Shopify Add the tag to `theme.liquid`. - In Online Store → Themes → … → Edit code, open `layout/theme.liquid` and paste the tag before ``: `layout/theme.liquid`: ```liquid {{ content_for_layout }} ``` Caveats: - Checkout pages don't load theme code, so the chat isn't shown during checkout. - Switching themes replaces `theme.liquid`. Add the tag again after changing themes. ### Webflow Use site-wide custom code. - In Site settings → Custom code, paste the tag in Footer code, save, and publish. ```html ``` Caveats: - Custom code needs a paid site plan and runs only on the published site, not inside the Designer. ### Wix Use Settings → Custom code. - Click Add custom code, paste the tag, choose All pages and Load code once, and place it in Body - end. ```html ``` Caveats: - Wix requires a premium plan with a connected domain for custom code. - Don't use the Embed HTML element: it sandboxes the code in a small iframe, so the launcher would be stuck inside a box. ### Squarespace Use Code Injection. - In Website → Pages → Website tools → Code Injection, paste the tag in Footer and save. ```html ``` Caveats: - Code Injection is available on plans that include custom code. Code blocks inside a page only load on that page. ### Framer Use the site's custom code settings. - In Site settings → General → Custom Code, paste the tag in End of tag, then publish. ```html ``` Caveats: - The launcher appears on the published site, not in the canvas. ### Ghost Use Code injection. - In Settings → Code injection, paste the tag in Site footer and save. ```html ``` ### HubSpot CMS Use the site footer HTML. - In Settings → Website → Pages, paste the tag in Site footer HTML and save. ```html ``` Caveats: - Running HubSpot's own chat too? Pick one: two launchers in the same corner overlap. ### Google Tag Manager Add a Custom HTML tag that fires on all pages. - Create a Custom HTML tag with the snippet and the All Pages trigger (or your consent trigger), then publish the container. ```html ``` Caveats: - A CSP on your site must allow both Google Tag Manager and Atria. - History Change triggers are unnecessary: the widget starts once per page and survives route changes.