Website callback form
Add a callback form to your website. Visitors leave their details and your AI agent phones them back now or at a time they choose. Install it and manage requests.
What you will achieve
You will add a floating button to your own website, in the bottom right or bottom left corner. When a visitor presses it, a callback form opens in a popup in the middle of the page. They enter their name, country and phone number, says what they are interested in, agrees to be called by an AI assistant, and chooses whether they want the call as soon as possible or at a time they pick. Phynxa saves the request and your agent phones them, using the same outbound calling as every other call you place: the same provider, the same agent instructions, voice, knowledge and tools, and the same credit.
The button and the form never place a call by themselves. Everything happens on the Phynxa server: the page only sends what the visitor typed.
Prerequisites
- An agent that can make outbound calls (its call direction is Outbound or Both). An agent that only takes incoming calls cannot be used. Set the direction on the agent's Assistant tab.
- The agent is published.
- A phone number to call from, and a connected phone provider (Twilio or Exotel), as for any outbound call. See Outbound calls.
- 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.
If one of these is missing, the Website form card says exactly what to fix and the form cannot be switched on. A visitor is never shown a setting, only "This form cannot take callback requests right now".
Create a form
- Open the agent and choose the Website tab.
- Choose the Website Form card. (The Voice Widget card next to it is the website voice button, and works as before. Press the Website tab again at any time to come back to the two cards.)
- On the Configuration tab, under Add a website, type the address of your website (for example
example.com) and press Enter or Add. - Switch on Enable Website Form.
- Wait for Form settings saved at the top right of Live Preview.
The settings are in tabs, with a Live Preview beside them: Configuration (agent, callback timing, daily limit, allowed websites and the form fields), Appearance (theme, colours, button position and label), Content (the words), Embed Code (the code, and below it Installation, which shows whether the form has been seen on your website) and Callbacks. There is no Save button. Every change is saved automatically a moment after you stop typing, and once it is saved with the switch on, the form is live for visitors straight away. If something cannot be saved yet, a closable notice at the top of the settings says why. The corner shows Saving with moving dots, then Form settings saved.
Members of your workspace can look at the tab, but only an owner or admin can change it.
Choose the agent
A form is attached to one agent, like the voice widget. The Agent list on the Configuration tab shows only the agents of your workspace that can make outbound calls. Choosing another agent opens that agent's own form. The agent keeps all of its own settings: instructions and personality, model, voice, hearing, knowledge, tools, integrations, language and its outbound number. The form changes none of them.
If you later change the agent to inbound-only, pause or delete it, or remove its phone provider, the form stops taking requests and the card says why.
Choose the fields
The form always asks for:
- Country, with names, flags and calling codes.
- Phone number, entered without the country code. Phynxa checks it against the country chosen and stores it in international form (for example
+447700900123). - Consent to receive the call. The visitor must tick it.
You can show, hide and require the others: First name (required by default), Last name, What are you interested in? and Additional details. A hidden field is never asked for or stored. For the interest field you can add a list of options; without options the visitor types a short answer.
Under Visitors can ask for a call choose whether visitors may pick now or a chosen time, as soon as possible only, or a chosen time only.
The form asks for nothing else. There is no email address and no other personal detail.
Choose how it looks
- Button position: bottom right or bottom left, and the Floating button label (the default is Request a Callback; clear it to get a round button showing only the phone icon). The button takes the Button colour you choose below.
- Theme: light or dark.
- Background, Text, Primary and Button colours. Any colours you pick are saved as chosen, so check the preview to make sure the text stays easy to read. The text on the button picks black or white by itself.
- Font: the form uses the same clean system font as the voice widget, whatever your website uses. Nothing to set.
- Form title, Short description, Send button label (the default is Request My AI Callback, the button inside the form that sends it) and Thank-you message.
- Consent line: the wording the visitor agrees to. The exact wording shown is saved with each request.
- Brand name and Logo address (optional, an https image).
The Preview beside the settings is the real button and popup, drawn on a mock page. Nothing is sent from the preview, and pressing the send button shows your thank-you message. Move the button to the other corner or rename it and the preview follows.
Language. The form's own words (field names, placeholders, scheduling text, error messages and the confirmation lines) are in English, Spanish or Arabic: the language of the agent that makes the calls. Arabic is laid out right to left, with the close button in the top left corner. To force one language on a page, add data-lang="es" (or en, ar) to the code. The title, description, button, consent line and thank-you message are your own words and are always shown as you wrote them; if you leave one at its original English default, it is shown in the form's language instead. The preview uses the agent's language too.
Choose the allowed websites
The form appears only on the websites you list, with the same rules as the voice widget:
| You write | It allows |
|---|---|
example.com | https://example.com only |
www.example.com | https://www.example.com only |
*.example.com | every subdomain, but not example.com itself |
example.com:8443 | that address on that port |
Subdomains are never trusted unless you write the *. line, and only https websites work. The check runs on the server for every load and every submission. Browsers tell the server which website a request came from and a page cannot change that, but a program that is not a browser can claim to be any website, so the list is not the only defence: see Protection against abuse below.
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.
Install the form on your website
Installing means pasting one short piece of code into your website. The button is drawn in the corner of the window, so it does not matter where in the page the code sits: put it on every page where the button should appear.
Step 1. Get your code
Open the Embed Code tab and press Copy. The code looks like this, with your own form ID. It holds only your public form ID: no keys, passwords or tokens, and the ID alone lets nobody place a call.
<script
src="https://your-phynxa-address/form.js"
data-form-id="frm_..."
async
></script>Step 2. Paste it into your pages
The best place is just before the closing </body> tag, or in a footer or template that every page shares, so the button appears on all of them. If the button sits where a cookie banner or chat bar already is, add data-offset-x="80" and data-offset-y="80" (a distance from the corner in pixels, 0 to 200) to the code, and data-z-index="..." if something on your page covers it. data-lang="en", "es" or "ar" sets the form's language (see Choose how it looks).
- Plain HTML: paste it into the page file just above
</body>, and upload the file. - WordPress: use a code plugin such as WPCode and paste the code into its Footer box, or add a Custom HTML block to a page.
- Next.js and React: add a
Script(or a small component that adds the script tag) once, in your layout. Keep thedata-form-idattribute. The Embed Code tab has ready-to-copy HTML, React and Next.js versions: pick yours above the code. - Shopify, Wix, Squarespace, Webflow: use the builder's custom code or footer code setting and paste the code.
- Google Tag Manager: a Custom HTML tag on All Pages works.
Menu names change from time to time. Look for the same words nearby.
The button and the popup are drawn inside an isolated container, the same way as the voice widget, so your site's styles cannot break them and they add no styles to your page. Loading the code twice, or pasting it twice for one form, shows one button. Two different forms on one page show two buttons, so give one of them an offset. The voice widget's button on the same side stacks above the form's button by itself: see Together with the voice widget.
Step 3. Publish and check
Publish or deploy your website, clear any cache, and open the page on one of the websites you listed. On the Embed Code tab, in the Installation panel below the code, Not seen yet changes to Installed with the website's address. Press Check again if it has not. Copying the code does not count: it changes only after a visitor's browser has loaded your page with the code on it.
If your website has a Content Security Policy, allow your Phynxa address for scripts (script-src) and connections (connect-src), and https images if you use a logo (img-src).
What a visitor sees and what happens next
- The visitor presses the floating button. The form opens in a popup in the middle of the page, with the page dimmed behind it. They fill it in and tick the consent box. Mistakes are shown beside the field.
- If you allow it they choose As soon as possible or Schedule a call. For a scheduled call they pick a date (within the next 30 days) and a time. The form shows their time zone, taken from their device, and repeats the choice in words ("We will call you on Tuesday 20 October, 14:30 (Asia/Karachi)").
- Pressing the button sends the request. A second press while it is sending, or sending the same form twice after a connection failure, is one request.
- They see your thank-you message inside the popup and, for a scheduled call, the confirmed date and time in their own zone. Closing the popup afterwards leaves the next opening a fresh form.
The popup closes with its × button, a press on the dimmed area around it, or Escape. What the visitor typed is kept if they close it without sending and open it again. On a phone it fits the screen, and when the form is taller than the window it scrolls inside the popup with the close button staying in view.
If the same number already has a request waiting on this form, a new request is not created and the visitor is told one already exists.
Immediate callbacks
An immediate request is saved and the sweep that places scheduled calls is woken at once, so the call normally goes out within seconds. If another server of yours holds that sweep it goes out within half a minute. Whether it can go out also depends on your credit and your plan's limits on calls at one time.
Scheduled callbacks and time zones
The visitor's time and time zone are turned into one exact moment (UTC) on the server and stored with the zone name. The server refuses a time in the past, less than 10 minutes ahead, more than 30 days ahead, or one that does not exist in that zone (the hour skipped when clocks change). The call is not placed before that moment. If the server was down at that moment the call goes out when it is back, up to 2 hours late. After that the request is marked Failed with the reason not placed in time, rather than calling at an hour nobody asked for. You can then use Retry.
Phynxa has no workspace-wide calling hours, so a visitor can choose any time. If some hours are not suitable, cancel or reschedule those requests from the Callbacks tab.
Consent and visitor information
- The visitor must tick the consent box. A submission without it is refused by the server, whatever the page sent.
- Each request saves the time they agreed and the exact wording they agreed to, so you can show what they consented to.
- Phynxa saves what the visitor typed (name, country, phone number, interest, details), the choice of timing, the website the request came from, and the status of the call. It does not save their address (IP), email or device details.
- Everything typed is treated as plain text. The agent is told it is information about the person and not instructions, so a visitor cannot change how the agent behaves by what they write.
- A member of your workspace sees a request with the phone number partly hidden. Owners and admins see the full number.
- Deleting the agent deletes its form and its requests, and deleting your account deletes all of them.
- A number that is on your do-not-call list (a Contacts entry with the status blocked) is never called.
You are responsible for having a lawful basis to call people, for what your consent line says, and for any rules about automated calls where your visitors live. Phynxa saves the consent; it does not decide what you must ask for.
The agent on the call
The call is placed with your agent as usual. Before it speaks, the agent is told that this is a callback the person asked for through your website form, their name, what they are interested in and what they wrote, and that it should say who it is and help with that. Nothing else about the visitor is passed. The agent's opening line, voice and everything else are its own.
The call appears in History as an outbound call labelled Website form, with its transcript, summary and cost like any other.
Manage the callbacks
Open the Callbacks tab of the form panel. Choose This agent or All agents, and filter by status. Each row shows the visitor, phone number, interest, call time and zone, status and result. Select a row for the full details, including what they wrote and a link to the call's record.
| Status | Meaning |
|---|---|
| Pending | Waiting to be called as soon as possible |
| Scheduled | Waiting for the time the visitor chose |
| Processing | Being placed right now |
| Calling | Dialled; ringing or in the conversation |
| Completed | The call was answered and finished |
| Failed | Not placed or not answered; the reason is shown |
| Cancelled | Stopped by you |
You can, as an owner or admin:
- Cancel a callback that is Pending or Scheduled. It is never dialled afterwards.
- Reschedule a Pending or Scheduled callback to another date and time. It is the same request, so there is never a second call.
- Retry a Failed callback. It goes back to Pending and is called as soon as possible. A retry is refused if the agent cannot call right now or the same number already has another request waiting.
A callback that is Processing or Calling cannot be cancelled or moved: it is on the phone.
If the visitor did not answer, Phynxa tries once more about 30 minutes later, then marks it Failed (not answered). If the phone provider could not be reached it also tries once more later. Other failures are not retried by themselves. A request is dialled once per attempt however often the server restarts: if the server stops at the very moment a call is being placed, the request is marked Failed (not retried, to avoid calling twice) and you decide whether to Retry.
Switching the form off stops new requests but does not cancel requests already accepted. Cancel those from the Callbacks tab if you do not want them called.
How it affects your credit
A callback is an ordinary outbound call. It is priced per minute from the agent's own model, hearing and voice, with nothing added for the form.
- Before a visitor's request is accepted, Phynxa checks the workspace can take calls. If not, the visitor sees "This form cannot take callback requests right now" and nothing is saved.
- Before each call goes out, credit is set aside for its first stretch, exactly as for any outbound call. When the call ends you are charged the real cost and the rest is released. If the call is never answered, the credit is released and nothing is charged. A call can be charged only once.
- If your credit has run out by the time the call is due, the request is marked Failed (not enough credit). Add credit and press Retry.
- On the Free plan the call counts as live minutes.
- Your plan's limit on calls at one time applies. A request waits as Pending while every call your plan allows is open (including voice calls from the website widget), and is dialled as soon as one ends.
All of this is worked out on the server. Nothing the browser sends can change a price or skip a check.
Protection against abuse
Several layers stop a script from using your credit, because the website is not the only defence:
- The page asks for a short-lived token when it loads, and a submission without a valid one is refused. A person cannot fill in the form in under three seconds.
- A hidden field that people never see traps scripts that fill in every field.
- Each internet connection (address) can have two requests accepted by one form an hour, and one phone number three a day; one form takes at most 60 an hour and one workspace 300, and a form takes at most the daily limit you set. Only a request that is really saved counts: a typing mistake, a repeat of a request already sent, or a refusal for another reason does not use up a place. A visitor over the limit is told to try again in about an hour. If your server sits behind more than one proxy (for example a CDN in front of Caddy), set
TRUST_PROXY_HOPSso the limit sees the visitor's own address, or every visitor will share one. - A number can ask for a callback at most three times in 24 hours.
- Each form has a Daily limit (default 200 requests in 24 hours, on the Configuration tab).
- The server decides the agent, the workspace and the status. The browser cannot choose them.
Together with the voice widget
The form and the voice widget can be on the same website at once, each with its own switch, settings and allowed websites. If both buttons are on the same side of the window they stack instead of overlapping; add data-stack="off" to a script's code if you place the button yourself. Opening the form does not close a voice panel or end a call, and a callback waits as Pending while your plan's calls are all in use. The full details are in Use it together with the callback form in the voice widget guide.
Troubleshooting
The button does not appear. Check, in order: the switch is on and Form settings saved is showing; the address of the page is in Allowed websites exactly (www. counts; http does not); the code is on the page (search the page source for form.js); a Content Security Policy is not blocking it. If the agent cannot place a call right now (paused, inbound-only, no number or provider) the button still appears, but sending the form shows "cannot take callback requests right now" and nothing is saved.
"Not seen yet" after installing. Check that you published your website, open a page of an allowed website that has the code, then press Check again.
The visitor sees "cannot take callback requests right now". The agent cannot place a call (paused, inbound-only, no number or provider) or the workspace has no credit. The Website form card in the dashboard says which.
A request is Failed. Open it: the reason is shown. Not enough credit: add credit and Retry. The agent was deleted, paused or can no longer make outbound calls: fix the agent and Retry. On your do-not-call list: the number is blocked in Contacts. Not answered: the person did not pick up after two attempts. Not placed in time: the server could not call within two hours of the chosen time. A provider message (for example about the number) is shown as it came.
A request is Failed and says Twilio did not confirm the call. The phone provider did not answer in time, so Phynxa cannot know whether the call went out. It is not retried by itself, to avoid ringing the person twice. Check your provider's call log for that number: if no call was made, press Retry.
A request stays Calling for a long time. A call that was dialled but never connected is moved to Failed (not answered) a few minutes later.
A visitor says they got no call. Look for their request in Callbacks: its status and reason tell what happened. If it says Completed, the call was answered and has a record in History.
For the person who runs the server
- No new service, queue or worker. Callbacks are placed by the scheduled-call sweep that already runs inside the backend (every 30 seconds, and straight away for an immediate request). With several instances, only the one holding the sweep lock places calls, as before.
- Two additive migrations,
0012_website_forms(two new tables and one new column oncalls) and0013_form_popup_button(two new columns on the forms table: the button's corner and label). Nothing existing changes, so rolling back to the previous release is safe. - No new environment variables. The existing ones apply:
PUBLIC_URL(the address your phone provider calls back),WIDGET_ALLOW_LOCALHOST=1(development only, lets a form listlocalhost),SERVER_SECRET(the same on every instance, so tokens verify everywhere),REDIS_URL(when you run more than one instance, so limits are shared) andTRUST_PROXY=1behind a proxy, so limits count each visitor and not the proxy. form.jsis served by the frontend with a short cache and open cross-origin access, like the widget's scripts. The visitor's browser calls/api/form/...on the same address; the proxy configuration already forwards/api/*.- Calls are placed over Twilio or Exotel exactly as other outbound calls. Other providers are not used for outbound calls, so a form cannot use them.
Known limits
- Phynxa has not placed a real call through this form in testing: the calling provider is replaced by a stand-in in the automated tests. Place a test request yourself before relying on it (see the steps below).
- The fixed words of the form are available in English, Spanish and Arabic only. An agent that speaks another language shows the form in English unless you set
data-lang. - The phone number check confirms the number looks right for the country and is stored in international form; it cannot tell whether a well-formed number belongs to anyone.
- There are no calling hours. A visitor can choose any time within 30 days.
- The button and popup have not been checked in every browser and website builder, or on every phone.
Try it end to end
- Open an outbound agent, choose Website then Website Form, add your website and switch the form on.
- Paste the code into a test page on that website and open it. Wait for Installed.
- Press the floating button, fill in the form with your own number, tick consent, choose As soon as possible and send it.
- Open the Callbacks tab: the request moves from Pending to Calling. Your phone rings and the agent says it is returning your request.
- After the call, the request is Completed and the call shows in History with the label Website form.
- Repeat with Schedule a call a few minutes ahead to check the time and zone.
Something here does not match what you see in the dashboard? Let us know.
