Add a Voice AI Agent to a Phoenix App
Add the AnveVoice voice agent to an Elixir Phoenix 1.7 or 1.8 app by editing root.html.heex, with notes for LiveView navigation and security headers.
How to add AnveVoice to a Phoenix (Elixir) app
Phoenix wraps every page in a root layout, root.html.heex, found in lib/your_app_web/components/layouts. Add the AnveVoice tag to it just before the closing body tag. Because LiveView does not re-render the root layout on live navigation, the voice button stays put as visitors move around. Steps checked against the official documentation on 2026-10-14.
At a glance
- Where the code goes: lib/your_app_web/components/layouts/root.html.heex, before </body>
- Coding needed: One line in a HEEx template; no dependency in mix.exs
- Plan limits: None from Phoenix; any host that runs your release (Fly.io, Gigalixir, containers, bare servers)
- Time: About 3 minutes
Root layout vs app layout
Phoenix guides split the page shell in two. The root layout is described as a barebone layout with mostly the head tag and a structure shared across all pages, and it renders your template through @inner_content. The application layout carries richer features such as sidebars and menus, and from Phoenix 1.8 it is invoked explicitly as a function component, <Layouts.app flash={@flash}>. Both live under lib/your_app_web/components/layouts. For a script that should exist on every page exactly once, the root layout is the right place. The LiveView guide explains why: the root layout is rendered only on the initial request, and its content stays the same as you live-navigate between LiveViews. A script in the app layout, by contrast, belongs to the content that LiveView patches and could be re-rendered. Edit root.html.heex, not app.html.heex, unless you deliberately want the agent on only some layouts. The approach works for apps generated with Phoenix 1.7 and 1.8; they use the same folder.
- 1. Get your install tag — At app.anvevoice.app create a bot for your site and copy its embed ID into the URL. HEEx passes the query string through as written, so nothing needs escaping. Code: <script src="https://anvevoice.app/embed.js?embedId=YOUR_EMBED_ID" async></script>
Method 1: edit root.html.heex
Older Phoenix 1.6 apps used a LayoutView and templates under lib/your_app_web/templates/layout. The tag goes in the same kind of file, root.html.heex there too.
- 1. Open the file — In your project, open lib/your_app_web/components/layouts/root.html.heex, the file with the html, head and body elements.
- 2. Add the tag below the inner content — Put it near the end of the body, after the line that renders @inner_content, so it never blocks the first paint. Code: <body class="bg-white"> {@inner_content} <script src="https://anvevoice.app/embed.js?embedId=YOUR_EMBED_ID" async></script> </body> </html>
- 3. Recompile — In development, code reloading picks up template changes automatically. For a production release, rebuild it (mix release) and redeploy. Layout templates are compiled into the app.
Method 2: add it for specific scopes only
The router's :browser pipeline calls put_root_layout, and a pipeline or a controller can set a different root layout for a group of routes. If you want the voice agent only on public marketing pages, create a second root layout (for instance root_marketing.html.heex) containing the tag, and select it with put_root_layout in a dedicated scope's pipeline. Routes for the signed-in dashboard then keep the original root layout without the tag.
- 1. Create the extra root layout — Copy root.html.heex to root_marketing.html.heex in the same folder and add the tag as above. The layouts module normally embeds every template in the folder through embed_templates, so recompiling picks the new file up.
- 2. Select it in a pipeline — Add a pipeline that overrides the root layout, and pipe the marketing scope through it after :browser. Code: pipeline :marketing do plug :put_root_layout, html: {MyAppWeb.Layouts, :root_marketing} end
Phoenix specifics: headers, assets and LiveView
- Security headers: the router's :browser pipeline includes put_secure_browser_headers. Its documentation describes the default content-security-policy value as equivalent to base-uri 'self'; frame-ancestors 'self', which does not restrict script sources, so the tag loads. If you pass a stricter content-security-policy map, add https://anvevoice.app to script-src.
- Assets: Phoenix bundles your own JavaScript from assets/js/app.js with esbuild into priv/static/assets. A third-party script with a full address does not go through esbuild and needs no entry in app.js. Do not copy embed.js into priv/static, which would stop it receiving updates.
- LiveView navigation: since the root layout is not re-rendered when navigating between LiveViews, the script is not reloaded; the AnveVoice script also ignores a second copy on one page.
- Releases: after editing, run your normal build; the digest step (mix phx.digest) only concerns your own assets.
Check that it works
- 1. Inspect the HTML — Run the app, load a page in a private window and view source. embed.js should appear once before the closing body tag.
- 2. Navigate and test — Click through a few LiveView links to confirm the button remains, then click it, allow the microphone (HTTPS or localhost) and ask a question.
Common problems
- Tag missing in production only: the deployed release was built before the template change. Rebuild and redeploy.
- Tag added to app.html.heex: pages that do not render <Layouts.app> will not have it. Use the root layout.
- Blocked by CSP: a custom content-security-policy header omits anvevoice.app in script-src.
- Compile error in HEEx: the pasted tag had an unclosed element; the script element needs its closing tag.
Sources
- Phoenix guides: Request life-cycle (layouts, root layout) (https://phoenix.hexdocs.pm/request_lifecycle.html) — checked 2026-10-14
- Phoenix LiveView docs: Live layouts (root layout behavior) (https://phoenix-live-view.hexdocs.pm/live-layouts.html) — checked 2026-10-14
- Phoenix docs: Phoenix.Controller (put_secure_browser_headers) (https://phoenix.hexdocs.pm/Phoenix.Controller.html) — checked 2026-10-14
- Phoenix guides: Asset management (https://phoenix.hexdocs.pm/asset_management.html) — checked 2026-10-14
Related
- How to add AnveVoice to a Ruby on Rails app
- How to add AnveVoice to a Laravel application
- How to add AnveVoice to a Django project
- How to add AnveVoice to a Flask application
Try AnveVoice on your site
Free plan: $0, 50,000 tokens a month, no card. Get your embed ID in the dashboard, then paste one tag.