# 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 `
` (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 set security headers in `next.config.ts`, allow Atria: `next.config.ts`: ```ts import type { NextConfig } from "next"; const csp = [ "default-src 'self'", "script-src 'self' https://atria.bemagic.online", "frame-src https://atria.bemagic.online", ].join("; "); const nextConfig: NextConfig = { async headers() { return [ { source: "/:path*", headers: [{ key: "Content-Security-Policy", value: csp }], }, ]; }, }; export default nextConfig; ``` - Using a nonce-based CSP from `proxy.ts` (`middleware.ts` before Next.js 16)? Read the nonce in the layout and pass it on: `app/layout.tsx`: ```tsx import { headers } from "next/headers"; import Script from "next/script"; export default async function RootLayout({ children }: { children: React.ReactNode }) { const nonce = (await headers()).get("x-nonce") ?? undefined; return ( {children} ); } ``` Caveats: - Don't use `strategy="worker"`. It runs scripts in a web worker through Partytown, where the widget can't render. - Prefer `afterInteractive` (or `lazyOnload`). `beforeInteractive` works, but only puts the chat in front of your own code. - Don't `import` the script into a component, and don't copy it into `public/`. It must load from Atria to stay up to date. - No `images` or `remotePatterns` changes are needed. ### Next.js (Pages Router) Load it once in `_app` with `next/script`. - Add the script next to your page component: `pages/_app.tsx`: ```tsx import type { AppProps } from "next/app"; import Script from "next/script"; export default function App({ Component, pageProps }: AppProps) { return ( <>` 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 `
``` - 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