Agent docs

The AmphiVox Voice Agent

On this pageHow it fits together

This page describes what the AmphiVox voice agent does while it fills a form: which questions it asks, the shape of every answer it hands over, when it moves to the next page, and how a session ends. It is the same agent everywhere AmphiVox runs, so the same rules apply whichever way you use it.

It describes behaviour you can observe and rely on, not how the agent is built. For the code that connects your site, read the JavaScript guide (opens in a new tab) or the React guide (opens in a new tab); each links back here for the details.

How it fits together

There are three parties in every session:

  • The respondent speaks their answers.
  • The agent asks the questions, understands the answers, checks them against each field's rules, and hands over clean values.
  • The form is separate from the agent and stays in charge. On an AmphiVox form it is the form page itself; on a website it is your page; with the extension it is the Google or Microsoft form being filled.

The agent never owns the form. It does not submit it, it does not navigate it by itself, and it does not decide what the form holds. It asks; the form shows what it actually holds; the agent believes the form.

AmphiVox runs in three places:

SurfaceWhat it isWho connects it
AmphiVox FormsA form built or imported in AmphiVox and shared as a link or a QR code. Respondents answer it on amphivox.com, by voice or by typing.Nobody: AmphiVox provides both the agent and the form.
AmphiVox WidgetsA voice button on your own site, added with widget.js or the @amphivox/react package.You, the site owner.
AmphiVox ExtensionThe AmphiVox browser extension for Chrome and Edge (coming soon), which fills Google Forms and Microsoft Forms in the respondent's own browser.Nobody: the respondent installs it.

On an AmphiVox form there is nothing to connect. AmphiVox's own form page is the form side of everything below, and it follows every rule described here. When the conversation completes on a form with no fields the respondent fills in by hand, that page submits the answers itself; otherwise the respondent submits. The agent never does.

The rest of this page is written for developers connecting the widget, but it applies to all three. Where something differs, the page says which.

Words used on this page

Session. One voice conversation, from the moment the respondent starts talking to the moment it ends.

Field. One question on the form. Every field has an id (a stable name like full_name, which code uses), a label (the question as the respondent reads it), and a type (what kind of answer it takes).

Page. Forms can have several pages. Pages are numbered from 0. The agent only works on the page that is on screen.

Page snapshot. A report from the form to the agent: which page is showing, and what every field on it holds right now.

Write. The agent handing the form a value to put in a field. Every write carries a write id, a label that identifies that one attempt.

Refusal. The form telling the agent "I didn't take that value", with the reason.

Skip. The respondent choosing not to answer an optional question.

Manual field. A field the agent never fills by voice. The respondent fills it in on the form themselves.

Ending. How a session finished: completed, handed back to the respondent, timed out, stopped, or failed.

Field types

Every field has one of these types. The value has the same shape in both directions: what the agent writes, and what the form reports back in a snapshot.

TypeAsks forValueAvailable on
strOne line of textstringall
longLonger text, several linesstringall
intA whole numbernumberall
floatA number that can have decimalsnumberall
boolYes or notrue / falseAmphiVox forms, widget
dateA calendar date"DD-MM-YYYY", e.g. "07-03-2026"all
timeA time of day"HH:MM", 24-hour, e.g. "14:30"extension
durationA length of time"HH:MM:SS", e.g. "01:30:00"extension
emailAn email addressstringall
phoneA phone numberE.164 string, e.g. "+919876543210"AmphiVox forms, widget
enumOne choice from a listone option string, exactly as declaredall
enum with multiAny number of choices from a listarray of option stringsall
rankingPut every item of a list in orderarray of all the options, most preferred firstextension
addressA postal addressobject of parts, e.g. { "street": "…", "city": "…", "zip": "…", "country": "IN" }AmphiVox forms, widget
urlA linknever asked by voice, see belowall
fileA file uploadnever asked by voice, see belowall
legalAccepting terms or giving consentnever asked by voice, see belowAmphiVox forms, widget

"All" means AmphiVox forms, the website widget and the browser extension. The extension's time, duration and ranking come from question types Google Forms and Microsoft Forms have; neither an AmphiVox form nor a widget schema can declare them. address, bool, phone and legal have no equivalent question type on those platforms: a phone or yes/no question there reaches the agent as plain text or a choice.

Text collected by voice is in Latin script. Whatever language the respondent speaks, names are romanised and free text is translated to English before it is stored. An answer the respondent types on the form stays as typed, in whatever script they used.

Rules a field can carry

Every rule is optional. When one is left out, its default applies.

RuleApplies toDefaultWhat it does
requiredany typefalseThe respondent must answer before the form can be finished.
piiany typefalseMarks personal data. The agent never asks for it by voice; the respondent fills it in on the form.
descany typenoneExtra context the agent can use when asking (up to 300 characters).
optionsenum, rankingnone (required)The exact option strings, up to 30. Each is trimmed and cut to 100 characters; a blank one is dropped.
multienumfalsetrue lets the respondent pick more than one.
maxLengthstr, longAmphiVox forms and widget: 150 (str), 1000 (long). Extension: the form's own limit, if it sets one (Microsoft Forms caps every text answer at 4000 anyway)The longest answer accepted, in characters: 50 to 5000. A long answer is stored in full, every point the respondent made; it is condensed only when it would exceed this limit.
min / maxint, float, durationnoneThe smallest and largest number accepted. Both ends are included: min: 1, max: 10 accepts 1 and 10. When min equals max, that one number is the only accepted answer. Whole numbers for int. For duration, they limit the hours.
regionphone"US"The country (ISO code such as "IN") used to read a number spoken without a country code.
componentsaddressnone (required)Which parts to ask for: street, apt, city, state, zip, country, each with its own optional desc and required. A required country is always added; its default (an ISO code) is the country assumed when the respondent doesn't say.
maxSizeMb / extensionsfile10 MB / anyThe largest file and the allowed file types, checked by your form.

What you can rely on in a value

  • Values arrive in one standard shape. A date is always DD-MM-YYYY, a phone number always E.164, a choice always one of the option strings exactly as declared, never the respondent's own wording of it.
  • Rules are checked before a value reaches you. A value the agent writes already satisfies the field's options, maxLength and min/max.
  • Lists are always complete. A multiple-choice value is the whole selection, not just what changed, and a ranking always contains every option exactly once. Treat the array as the final state: untick anything no longer in it.

Fields the agent never asks for

Some fields are never filled by voice. The agent tells the respondent to fill them in on the form. On a page with more pages after it, it waits until the form shows them filled, then moves on. On the last page it does not wait: it tells the respondent what is left and ends. These are:

  • file, url and legal fields;
  • any field marked pii;
  • on the extension, any question it can't fill reliably (a question type it doesn't recognise, for example). Where the extension can't read such a question either, nothing on the form can show it filled, so the agent accepts the respondent saying it is done.

The agent never learns their contents. It only ever learns that the field has an answer, never what it is. On AmphiVox forms and the widget, the value is replaced with a plain "filled in" marker before anything leaves the browser. On the extension, a link typed into a link question is sent with the page's other answers and reduced to that marker on arrival.

Because of that, only report one of these fields as filled when your form would accept its value. If an invalid link is reported as filled, the agent moves on while your form still refuses to continue. For a file field, check the file against maxSizeMb and extensions and store it first, then report it as uploaded.

Pages

A form can have several pages. The agent only asks about fields on the page that is on screen. Answers from earlier pages are kept, but the agent can't change them, because they are no longer on screen to change.

Pages are numbered 0, 1, 2…, with no gaps. The last page is the one that submits.

Page snapshots

The form tells the agent what it holds by sending a page snapshot: the page number on screen, and the value of every field on that page.

{
  "page": 0,
  "values": { "full_name": "Asha Rao", "email": null, "newsletter": null }
}
  • Always the whole page, never just what changed. If one snapshot goes missing, the next one fixes everything.
  • Exactly the page's fields. A snapshot that leaves out one of the page's fields, or names a field from another page, is rejected.
  • null means no answer. So do an empty string and an empty list.
  • Send one before the first session, after every change, and on every page change. That includes changes the respondent makes by typing.
  • The form is the truth. A write is only a request; the next snapshot is the fact. If the respondent edits a field the agent filled, the agent goes with the edit.

Before turning the page, and before finishing, the agent asks for one fresh snapshot, so that the respondent's last edit on that page is included.

Known answers

Before a session starts, the form can give the agent answers it already has, from any page, keyed by field id: a returning respondent's details, say, or answers collected earlier. The agent doesn't ask for those again.

  • Same shapes, same checks. Known answers use the value shapes above and are checked like a snapshot: an invalid one is dropped, and the agent asks for that field as usual. A key that is not one of the form's field ids is different: it rejects the whole set with widget_page_invalid.
  • They can carry skips. A skip marker among them keeps an earlier skip.
  • They apply when a session starts. Changing them affects the next session, not one already running.

On an AmphiVox form, the form keeps a respondent's unfinished answers in their browser for up to a day. If they come back and choose to resume, those answers are the known answers. On the extension there is nothing to send: what the form already shows is the known answers.

Writes

When the respondent answers, the agent sends a write: the field id, the value, and a write id.

  • Taking the value needs no reply. Put it in the field; your next snapshot confirms it.
  • The same write id can arrive twice. Applying it a second time must change nothing.
  • A write is always a valid value for the field's type and rules (see What you can rely on in a value).

Refusals

If your form doesn't take a value, refuse the write with its write id and a reason. There are two kinds of refusal, and only you know which one applies:

KindWhenWhat the agent does
validationYour own rule rejected the answer, one the field's rules can't express ("that email is already registered", "that coupon has expired").Tells the respondent your reason and asks for a different answer. The rejected value is never sent again.
writeThe value couldn't be put into the field at all.Sends the same value once more, without telling the respondent. Any refusal that doesn't say validation is treated this way.
  • Attempts are limited per field. A write refusal gets two tries: the value, then the one resend. A validation refusal gets three, each a different answer. After that, the agent stops and asks the respondent to fill that field in on the form.
  • Rejections are announced together. When you refuse several answers as validation at once, the agent tells the respondent every reason in one reply and asks for all of them again.
  • Keep reporting the truth. After a refusal, your snapshots should show whatever the field really holds, the rejected value included. The agent never picks up a value you refused from a snapshot.
  • The reason is spoken aloud and kept in AmphiVox's session logs. Keep it short, and never put the rejected value or anything personal in it. Reasons longer than 200 characters are cut.

Skips

A respondent can skip any optional field by saying so. The agent then reports a skip for that field, using a special marker value, "__CLEAR__". Both SDKs give it a name, SKIP, so you never have to type the string.

  • A skip is not an answer. Never put the marker in your form, validate it, or submit it. Leave the field as it is.
  • It has no write id. Nothing was written, so there is nothing to refuse or confirm.
  • To remember a skip in a later session, send the marker back as that field's value, among your known answers or in a snapshot while the field is still empty. Otherwise the next session asks the question again. A real answer the respondent gives later always replaces it.
  • Required fields can't be skipped. Sending the marker for one changes nothing.

On an AmphiVox form, skips are remembered with the respondent's unfinished answers. On the extension, skips need nothing from anyone: the form's field is simply left alone.

Page turns

When every field on a page is answered and more pages follow, the agent asks before moving on: "Anything to change before I move on?" If the respondent agrees, the agent asks the form to go to the next page.

  • The form runs its own Next. The same checks and navigation as its own Next button, then a snapshot of the new page.
  • If Next didn't move, the form reports the unchanged page and says the move was blocked. If a field on the page is still unanswered, or was left for the respondent, that explains it: the agent goes back to that field and carries on. If nothing on the page explains it, the session ends: the agent tells the respondent it is stuck and disconnects, ending as hands_off. A block the agent can't see the reason for can't be fixed by voice, so it hands the form back rather than guess. Since the agent only asks to move on once the page looks complete, a block from your own rule usually ends the session.
  • If the form says nothing, neither a new page nor "blocked", the agent waits ~15 seconds and then treats the move as blocked. A form that never handles page turns therefore ends every multi-page session on its first page, after that pause.
  • Manual fields hold the page. While a manual field on the page is empty, the agent won't ask to move on. It asks the respondent to fill it in first, or, if it's optional, to say they want to skip it.

Finishing

When everything it can fill is filled, the agent reads the answers back for the respondent to confirm, then ends the session. It never submits the form. Submitting is the form's job, and only after an ending of complete.

Endings

Every session that started ends exactly once, with one of these:

EndingWhat happenedWhat to doExtra detail
completeThe agent has every answer it needs. A form with any field the agent never asks for, or any field it gave up on after repeated attempts, never ends this way: it ends hands_off, even if the respondent has since filled everything, and outstanding may then be empty.Run your own checks and submit.—
hands_offThe agent filled what it could and is handing the form back: some fields need the respondent, or the agent could not continue.Don't submit. Let the respondent review and finish on the form.outstanding: the labels of the fields left to fill, which can be empty
timeoutThe session ended after silence, or reached its time limit.Let the respondent continue on the form, or start again.timeout: idle or max_duration
manualThe respondent stopped the session, or your code closed it.Nothing.—
errorA live session failed. Answers already written stay valid.Let the respondent continue, or start again.code and message

Silence and time limits

These are the current server settings. Treat them as approximate.

WhatWhen
First nudge (the agent repeats the question)~15 seconds of silence after the agent stops speaking
Second nudge (the agent warns it will disconnect)~15 more seconds of silence
Session ends with timeout: idle~15 more seconds of silence (about 45 seconds in all)
Longest session, ending with timeout: max_duration10 minutes
A page turn the form never answers counts as blocked~15 seconds after the agent asks for it
A browser that stops responding is disconnectedafter 2 missed checks in a row; checks run every ~4 seconds, starting ~10 seconds into the session

The silence clock only runs while it is the respondent's turn: not while the agent is speaking or thinking. Muting is not pausing. A muted microphone sounds exactly like a silent respondent.

On the website widget, the voice runs inside an iframe that must stay rendered. Hiding it with display: none makes the browser slow it down until it looks unresponsive and is disconnected; use the SDK's own hidden option instead.

Languages and voices

The agent speaks 45 languages, in two groups. Pass the code; the live list is also available as JSON from GET https://api.amphivox.com/api/voice/languages.

Worldwide. The agent also understands English, so a respondent can switch to English mid-sentence.

CodeLanguageCodeLanguage
enEnglishlvLatviešu
esEspañolltLietuvių
frFrançaisnoNorsk
deDeutschplPolski
itItalianoptPortuguês
nlNederlandsroRomână
ja日本語ruРусский
arالعربيةskSlovenčina
bgБългарскиslSlovenščina
hrHrvatskisvSvenska
csČeštinathไทย
daDansktrTürkçe
etEestiukУкраїнська
fiSuomiviTiếng Việt
elΕλληνικάzh中文(简体)
huMagyarzh-TW中文(繁體)
idBahasa Indonesiako한국어

Bharat. Indian languages; the agent detects which one the respondent is speaking.

CodeLanguage
en-INEnglish (India), tuned for Indian accents and names; different from en
hiहिन्दी
taதமிழ்
teతెలుగు
knಕನ್ನಡ
mlമലയാളം
bnবাংলা
mrमराठी
guગુજરાતી
paਪੰਜਾਬੀ
odଓଡ଼ିଆ

Voice. The agent speaks with a female voice (female, the default) or a male one (male). Any other value given at start falls back to female; one given when changing the voice later is ignored, and the current voice stays.

The language and the voice are set when a session starts. Changing either one applies to the next session, not the one already running.

Errors (website widget)

The widget reports problems as an error with a code and a message. The message is plain text that is safe to show; decide what to do from the code, and keep a fallback for codes you don't recognise, because this list can grow.

These stop a session before it starts, or reject a message your page sent (a live session carries on after those):

CodeWhat happened
invalid_widget_keyThe key doesn't exist, or was deleted.
origin_not_allowedYour site isn't on the key's list of allowed websites.
widget_mode_mismatchThe mode you passed doesn't match the mode the key was created with.
widget_request_invalidA voice request carried data the key's mode doesn't accept. Only reachable by calling the API directly.
widget_session_forbiddenA reconnect named a session that belongs to a different widget key. Only reachable by calling the API directly.
widget_page_requiredNo page snapshot was sent before the session was opened.
widget_page_invalidA page snapshot didn't match the form's fields.
widget_payload_too_largeA schema or page snapshot was over the size limit.
widget_not_readyA control was used before the widget finished loading. Try again, for example from a click.
invalid_voice_token / voice_token_expiredThe session couldn't be authorised, or took too long to start. Open it again.
session_not_foundThe session a reconnect asked for has already ended. Open a new one.
initial_state_invalid / initial_state_too_largeThe known answers were malformed, or over the size limit.
session_limit_exceeded / session_busy / session_spawn_failedVoice capacity is full, or the session couldn't start. Try again shortly.
token_fetch_failed / connect_timeout / connect_failed / webrtc_unavailableThe connection failed, or this browser can't make voice calls.
widget_origin_unavailableThe browser didn't say which site the widget is embedded in, so it can't run there.

Only with a form-linked key (the form is stored in AmphiVox):

CodeWhat happened
widget_schema_changedThe form was edited while the widget was loading. The next open loads the new version and reuses your last page snapshot if it still fits; otherwise send the page again.
form_not_foundThe key's form no longer exists.
widget_schema_forbiddenA schema was sent, but this key's form is the stored one. Don't send a schema.

Only with a developer-defined key (your code sends the form):

CodeWhat happened
widget_schema_requiredNo form yet: none was given, or a snapshot or known answers arrived before a valid one.
widget_schema_invalidThe form breaks one of the rules above. onError gives this code and a general message. Where the server can name the field and the rule (never a value), that is in the failed request's HTTP response body, not in onError.
widget_schema_lockedThe form was changed while a session was running. The change is discarded; send it again once no session is running.

A session that started and then failed doesn't report an error; it ends with error. Its code is one of session_ended_unexpectedly, reconnect_failed, voice_service_unavailable, voice_service_busy, voice_service_error or voice_session_error.