Website voice widget
Put a talk button on your website so visitors can speak to your AI agent. Choose the allowed websites, install the code and see how it affects your credit.
What you will achieve
You will add a floating button to your own website. A visitor presses it, allows their microphone, and talks to the same agent you test in the dashboard: the same instructions, voice, knowledge and tools. There is no second copy of the agent to keep up to date.
Prerequisites
- A published agent. An unpublished agent shows nothing to visitors.
- Credit on your workspace, or free minutes left on the Free plan. See How it affects your credit below.
- A website where you can add a line of code, served over https. Browsers only allow the microphone on secure pages.
Turn it on
- Open the agent, choose the Website tab, and then the Voice Widget card. (The other card, Website Form, is the callback form: see Website callback form.)
- On the Configuration tab, under Website, type the address of your website (for example
example.com) and press Enter or Add. Repeat for each website that will show the widget. Each one appears as a row in the list, and the × on a row removes it. - Switch on Show the widget on my website.
- Wait for Widget settings saved at the top right of Live Preview.
There is no Save button. Every change is saved automatically a moment after you stop typing. If something cannot be saved yet (for example the switch is on but no website is listed), a closable notice at the top of the settings says why. The corner shows Saving with moving dots, then Widget settings saved. Once a change is saved with the switch on, the widget is live for visitors straight away.
To stop it later, switch it off and wait for Widget settings saved. New calls are refused straight away and the button disappears from pages within a few minutes (browsers keep the settings for up to 30 seconds and the script for up to five minutes). A call already in progress is not cut off.
Members of your workspace can look at the tab, but only an owner or admin can change it.
Choose how it looks
Open the Appearance tab for the icon, position and colours. The words (title, button label, greeting and privacy link) are on the Configuration tab, below Website.
- Icon: phone, microphone or chat.
- Button colour and Icon colour: the round button and the icon on it.
- Panel colour: the background of the popup panel. The text, borders and soft shades inside it adjust by themselves, so a dark panel gets light text. Any colours you pick are saved as chosen, so check the preview to make sure the icon and button stay easy to see.
- Position: bottom right or bottom left.
- Widget title and Button label: the heading of the panel and the text on the button that starts a call.
- Greeting message: shown in the panel before the call starts. What the agent says out loud is its own opening line, set on the Assistant tab.
- Privacy policy link (optional): an https address shown under the panel.
The Live Preview beside the settings is the real widget, drawn on a mock page. Calls cannot start from the preview.
Choose the allowed websites
The widget works only on websites you list. A page on any other website that has your code gets nothing. You can paste several addresses into the box at once, separated by spaces or commas.
| You write | It allows |
|---|---|
example.com | https://example.com only |
www.example.com | https://www.example.com only |
*.example.com | every subdomain (app.example.com, shop.example.com), but not example.com itself |
example.com:8443 | that address on that port |
Rules worth knowing:
- Subdomains are never trusted unless you write the
*.line. If you want both the bare domain and its subdomains, add both lines. - Only https websites work.
httpis refused. - Addresses are tidied when you save: capitals, a leading
https://and any path are removed. - IP addresses are refused, and so are
*.lines that would cover a whole public domain such as*.comor*.co.uk. - You can list up to 20 websites.
- For testing on your own computer, the person who runs your Phynxa server can allow
localhost(see For the person who runs the server). It is off by default.
What the website check can and cannot do
Browsers tell a server which website a request came from, and a page on another website cannot change that. So your widget cannot be borrowed by someone else's site. A program that is not a browser can claim to be any website, so the list is not a lock against someone determined. What limits that is everything else: each address and each widget can only start so many calls a minute, only a few calls can be open at once, calls are shorter than phone calls, and every call needs credit set aside first. If you see calls you do not recognise, turn the widget off and write to support.
Install the widget on your website
Installing means pasting one short piece of code into your website. Follow the four steps in order.
Step 1. Get your code
- In Phynxa, open Voice agents, choose your agent, open the Website tab and choose the Voice Widget card.
- Check that your website is listed on the Configuration tab and the switch is on (see Turn it on above).
- Open the Embed Code tab and press Copy.
The code looks like this, with your own widget ID. It holds only your public widget ID. There are no keys or passwords in it, and the ID alone lets nobody make a call: every call is checked against your list of websites and your switch.
<script
src="https://your-phynxa-address/widget.js"
data-widget-id="wgt_..."
async
></script>Step 2. Paste it into your website
Find your kind of website below and follow its steps. In every case the code goes on every page where you want the button, and the best place is just before the closing </body> tag (the footer, or "end of body").
A website made of files (plain HTML)
- Open your page file, for example
index.html, in a text editor. If all your pages share one template or footer file, open that instead, so the button appears on every page. - Find the line with
</body>near the bottom. - Paste the code on the line just above it.
- Save the file and upload it to your web host (with FTP, your host's file manager, or however you normally publish).
Next.js
- Open
app/layout.tsx(App Router). With the Pages Router, openpages/_app.tsxinstead. - At the top, add
import Script from "next/script";. - Inside
<body>, after{children}, add theScriptshown below, with your own address and ID. Inpages/_app.tsx, put it after<Component {...pageProps} />. - Save and deploy as usual.
import Script from "next/script";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://your-phynxa-address/widget.js"
data-widget-id="wgt_..."
strategy="afterInteractive"
/>
</body>
</html>
);
}React (Vite or Create React App)
- The easiest way: open
index.html(in the project folder for Vite, in thepublicfolder for Create React App) and paste the code from Step 1 just before</body>. - Rebuild and deploy your site.
If you would rather add it in code, put this component once in your top-level component (App.tsx):
import { useEffect } from "react";
export function VoiceWidget() {
useEffect(() => {
const script = document.createElement("script");
script.src = "https://your-phynxa-address/widget.js";
script.dataset.widgetId = "wgt_...";
script.async = true;
document.body.appendChild(script);
return () => script.remove();
}, []);
return null;
}WordPress
- In the WordPress admin, go to Plugins, Add New Plugin.
- Search for WPCode (WPCode Insert Headers and Footers), then install and activate it.
- Go to Code Snippets, Header & Footer.
- Paste the code into the Footer box and press Save Changes.
Shopify
- In the Shopify admin, go to Online Store, Themes.
- Next to your live theme, press ... and choose Edit code.
- Open
layout/theme.liquid. - Paste the code on the line just above
</body>and press Save.
Wix
- In your site's dashboard, go to Settings, Custom Code (this needs a paid plan with a connected domain).
- Press Add Custom Code and paste the code. Name it "Phynxa voice widget".
- Under Add Code to Pages, choose All pages. Under Place Code in, choose Body - end.
- Press Apply, then publish your site.
Squarespace
- Go to Settings, Developer Tools, Code Injection (on older sites, Settings, Advanced, Code Injection). This needs a plan that allows code injection.
- Paste the code into the Footer box.
- Press Save.
Webflow
- Open Site settings, Custom code (this needs a paid site plan).
- Paste the code into the Footer code box.
- Press Save, then publish your site.
Google Tag Manager
- Create a new Tag of type Custom HTML and paste the code.
- Choose the trigger All Pages.
- Save, then Submit and publish the container.
Any other builder
Look for a setting called "custom code", "code injection", "header and footer" or "scripts". Choose the footer or end-of-body position, paste the code, and save. If you cannot find one, send the code from Step 1 to whoever looks after your website and ask them to add it before </body> on every page.
Menu names change from time to time. If one of these does not match what you see, look for the same words nearby.
Step 3. Publish your website
Pasting the code is not enough on its own. Most platforms only show a change to visitors after you publish, deploy or upload it. Do that now. If you use a caching plugin or a CDN, clear its cache.
Step 4. Check that it works
- Open your website in a normal browser window, on one of the addresses you listed. Refresh a few times if you do not see a change.
- Look for the round button in the corner you chose.
- Press it, press the start button, allow the microphone, and say hello. The agent should answer.
- Back in Phynxa, open the Embed Code tab and look at the Installation panel below the code: Not seen yet changes to Installed, with the website's address. Press Check again if it has not changed yet. Copying the code does not count: it changes only after a visitor's browser has loaded your page with the code on it.
If the button does not appear, see Troubleshooting below.
If your website has a Content Security Policy
A strict policy can block the widget. Allow your Phynxa address for scripts (script-src) and for connections including wss: (connect-src). The widget loads its audio helper from the same address as a normal script file, not from a blob: address.
What a visitor sees
- A small round button in the corner you chose, with your widget title in a small label beside it.
- Pressing it opens a compact panel with your title, your greeting as a speech bubble, and one large round start button with your button label under it.
- Pressing the start button asks the server for a call. Only then does the browser ask for the microphone, and the panel says Allow microphone access while the browser's own pop-up is open. Nothing is recorded or sent before the visitor allows it.
- The visitor talks and hears the agent. The panel shows whether the assistant is listening, thinking or speaking, a call timer, and the agent's latest line as text. The round buttons below mute the microphone or end the call. Closing the panel also ends the call.
- When the agent ends the call, or the time limit is reached, the panel shows Call ended with a Close button. Pressing the round button again starts a new call.
The panel says it is an AI assistant and that the conversation is transcribed. Phynxa does not record call audio; it keeps the transcript, shown in History like any other call. If you need to tell visitors more, add your privacy policy link.
The words in the panel follow the agent's language where Phynxa has them (English, Spanish and Arabic); other languages show English.
Calls in your history
A website call appears in History with the Website label and the website it came from. Everything else works as for other calls: transcript, summary, cost, tool use and your webhook. The call's direction is widget in webhook data.
If a visitor needs a person
Transfer to a person is not available on a website call. The visitor is in a browser, so there is no phone line to move over to your team. If a visitor asks for a person, the agent says it cannot transfer from there and keeps helping. Handing the call to another agent of your workspace is available on website calls.
To get a visitor to a person afterwards, you can:
- Have the agent collect the visitor's name and phone number with a form, so your team can call them back. See Forms and appointments.
- Attach the Slack action, so your team is alerted in a channel or by direct message when a visitor needs a person. See Slack.
- Put your opening hours, phone number and email in the agent's instructions, so it can tell the visitor how to reach you. See Instructions and opening line.
Transfer to a person still works on phone calls on a Twilio number. See Transfers on the phone.
How it affects your credit
Website calls use the same price per minute as your other calls, worked out from the agent's own model, hearing and voice. Nothing new is charged for the widget.
- Before a call connects, Phynxa sets credit aside for its first stretch, exactly as for a phone call, and tops it up while the call runs. When the call ends you are charged the real cost and the rest is released. A duplicate disconnect cannot charge twice.
- If there is not enough credit, the visitor sees "This voice assistant cannot take calls right now". They are not told about your plan or balance.
- On the Free plan, website calls count as live minutes (the 5 minutes of real calls), not test minutes.
- The Longest call setting (default 10 minutes) ends a call politely at that length. Phone calls keep their own, longer limit.
- Your plan's limit on calls at one time applies to the widget too, and one widget also has its own ceiling on open calls. Visitors who arrive when it is full are asked to try again in a minute. A callback from the website form waits for a free slot instead of ringing someone who would then be refused: see Use it together with the callback form.
Troubleshooting
The button does not appear. Check, in order: the switch is on and Widget settings saved is showing; the agent is published; the address of the page is in Website exactly (www. counts; http does not); the code is on the page (view the page source and search for widget.js); the page is not blocking the script with a Content Security Policy; an ad or privacy blocker is not removing it.
"Not seen yet" after installing. Check that you published your website (Step 3), open a page of the allowed website that has the code in a normal window, then press Check again. A cached page may still be the old one.
The microphone is blocked. The panel says so. Click the lock or camera icon in the browser's address bar, allow the microphone for the site, and press Try again. On a phone, also check the browser's permission in the phone's settings. Another app using the microphone (a video call, for example) can also stop it.
"Couldn't connect". The visitor's internet connection dropped, or something between them and Phynxa (a company firewall, a strict policy) blocks the connection. WebSockets (wss:) must be allowed.
The call cut out. Press Reconnect. This starts a new call; it cannot continue the old one, and the new call is charged like any other.
It says the browser cannot make voice calls. The visitor needs a current Chrome, Edge, Firefox or Safari, on an https page.
No sound from the agent. Check the device volume and that the tab is not muted. Some phones need the screen touched first, which pressing the start button does.
Use it together with the callback form
You can run the voice widget and the callback form on the same website at the same time. They are separate: each has its own switch, settings, allowed websites and code, and each can be on or off without touching the other.
- Install both codes. Paste the voice widget's code and the form's code on the same pages (for example both just before
</body>). Each script draws its own button inside its own isolated container, so neither can restyle the other or your page. Pasting either one twice still shows it once. - Two buttons, one corner. If both are set to the same side of the window, the voice button stacks above the callback button by itself, 12 pixels apart, so they never sit on top of each other. If they are set to different sides, nothing moves. To place a button yourself, add
data-offset-xanddata-offset-y(0 to 200 pixels from the corner) to its code. To switch the automatic stacking off for one of them, adddata-stack="off"to its code: that button then keeps exactly the offsets you gave it, and the other one ignores it. - Popups. Opening the callback form while the voice panel is open does not close the panel or end a call: the form opens in the middle of the page, above it. Closing either one leaves the other as it was. A voice call carries on while a visitor fills in the form, and a callback request can be sent while the voice panel is open.
- Different websites. Each feature checks its own list of allowed websites, so a website can be allowed for one and not the other.
- Shared limits. Both use the same workspace credit and your plan's limit on calls at one time. A callback waits (it stays Pending on the Callbacks tab) while every call your plan allows is open, including voice calls on your website, and goes out as soon as one ends.
- Turn one off. Switch off Show the widget on my website to stop the voice button, or Enable Website Form to stop the form, and wait for the saved message. The other keeps working. A browser may keep the voice widget's settings for up to 30 seconds, so its button can take that long to disappear.
- If a button is missing or covered. Check that feature's own switch and allowed websites, then view the page source and search for
widget.jsandform.js. If a button sits under a cookie banner or chat bar, move it withdata-offset-y. If something on your page covers both, raisedata-z-indexon each script tag.
Turn it off or remove it
- Pause it: turn off Show the widget on my website and wait for Widget settings saved. The code can stay on your site; it does nothing.
- Remove it: delete the code from your pages. Deleting the agent also deletes its widget; the old code then shows nothing.
For the person who runs the server
- No extra setup is needed for a normal install. The widget's pages and calls use the same address as the dashboard, and the provided proxy configuration already sends
/api/*and/ws/*to the backend. WIDGET_ALLOW_LOCALHOST=1lets pages onlocalhostuse a widget that listslocalhost. Leave it unset in production.WIDGET_MAX_CONCURRENT(default 10) is how many website calls one widget may have open at once. Each address can have two.SERVER_SECRETshould be the same on every instance, andREDIS_URLset when you run more than one, so a visitor's one-time call token and the limits work across all of them. Without Redis they are counted per instance.- The three files
widget.js,widget-voice.jsandwidget-worklet.jsare served from the frontend with a short cache and open cross-origin access, which the audio helper needs. - Provider keys (speech, language model, voice) stay on the server. A visitor's browser never receives one.
Known limits
- Real-time audio runs over a WebSocket, as in the dashboard test call. There is no WebRTC, so very poor networks may sound choppy.
- A dropped call cannot be resumed, only restarted.
- Phynxa has not been checked with every browser and phone. If the widget misbehaves on one, tell us which.
- Spanish and Arabic panel wording was not reviewed by a native speaker.
Something here does not match what you see in the dashboard? Let us know.
