Getting Started
To get started with Veform you running you need to:
- Setup secure authentication for end users.
- Import the Veform library into your app.
- Setup conversation flows inside your app.
Veform lets users complete forms with pure audio conversations. It can be used in any web, iOS or Android app.
To get started with Veform you running you need to:
Requirements:
Veform API key - contact us to discuss setting one up.
Backend Api endpoint - a secure endpoint that hits the veform api with your key to generate a short lived token.
See the Authentication Example to see how to set up authentication.
Tokens are short lived and single use, each conversation needs a new token.
Can be setup with npm:
npm install veform-js
Or you can use the browser version:
https://unpkg.com/veform-js/dist/veform.browser.min.js
The `veform` package is a library that powers conversations on top of your forms. It provides utility functions to catch conversational events and control conversation flow.
new Veform(config: VeformConfig, builder: VeformBuilder)
The `Veform` class is the main entry point for running conversational experiences on top of a form. It takes a form definition and connects to the server, provides helper functions to control the conversation and exposes lifecycle events through a set of `on_____` callbacks.
VeformConfig
The configuration for the Veform instance.
Parameters
VoiceOptions - The voice to use for the conversation. Will use default voice if not provided.VoiceOptions
The options for the voice to use for the conversation.
Parameters
Voice options:
Language options:
start(token: string): Promise<boolean>
Establishes microphone access and starts the conversation
Parameters
stop(): void
This method should not be needed in most cases.
Ends conversation and cleans up related resources. Veform conversations will naturally stop when the user completes the form or indicates they want to stop.
onLoadingStarted(cb: () => void)
Called after `start()` has obtained microphone access and is setting up connections.
onRunningStarted(cb: () => void)
Invoked when the conversation is live and initial audio has begun being output to the user.
onFocusChanged(cb: (previousName: string, nextName: string) => boolean)
Triggered when the conversation intends to move from one field to another. If the callback returns true, `Veform` blocks the focus change and logs that it was interrupted.
If not blocked, `Veform` finds the next field in your form and, if that field has its own `eventHandlers.onFocus`, invokes it with the previous field name.
onFieldValueChanged(cb: (fieldName: string, value: string | number | boolean) => void)
Called after the value for a field has been changed.
onAudioInStart(cb: () => void)
Triggered when user speech has been started.
onAudioInEnd(cb: (input: string) => boolean)
Triggered when the user stops speaking. If the callback returns true, Veform will block further processing of this input and will not automatically continue the conversation.
When you interrupt in this way, you are responsible for resuming the flow by calling changeField or emitAudio manually.
onAudioOutStart(cb: (chunk: string) => boolean)
Triggered before a new segment of audio is played to the user. If the callback returns true, `Veform` will block that audio from being emitted and from progressing the conversation.
When blocking, you must manually decide how to continue, typically via changeField or emitAudio.
onAudioOutEnd(cb: () => void)
Triggered when audio output has finished playing.
onError(cb: (error: string, critical: boolean) => void)
Called when recoverable errors are encountered.
Parameters
emitAudio(audio: string, interrupt: boolean): void
This method should not be needed in most cases, you should configure needed output in the form builder.
Emits audio to the user. If interrupt is true this will cut current output and play immediately, otherwise it will be queued behind existing output.
Parameters
changeField(fieldName: string): void
This method should not be needed in most cases, you should configure how fields flow between each other in the form builder.
Programmatically moves the conversation focus to a specific field in the form. This will cut current output and play the new field's question.
Can be paired with emitAudio('we have to go back to ...', true) to interrupt current speech and provide context before jumping to the new field.
Parameters
VeformBuilder is a class that helps you build and edit the conversation flow for your form.
new VeformBuilder()
addField({name, question, type}: {name: string, question: string, type: FieldType}): Field | null
Adds a new field to the builder based on the provided field's name, question, and type. Field names must be unique to the form.
Parameters
FieldType - The type of field to add.Returns
Field - The field instance that was added.null - If a field with the same name already exists or the name/question are invalid.getField(name: string): Field | undefined
Retrieves a field from the builder by its name.
Parameters
Returns
Field - The matching field instance, if found.undefined - If no field with that name exists.getFields(): Field[]
Returns the list of all fields currently in the builder, in the order they were added.
removeField(name: string): boolean
Removes a field from the builder by name.
Parameters
Returns
true - If the field was found and removed.false - If no field with that name exists.new Field(name: string, question: string, type: FieldType)
The `Field` class is the base class for all fields in the form. You can visualize a field as a node on the "graph" that represents your form conversation. Each field contains info about its own state and how to navigate between other fields based on user input.
builder.addField({
name: "name",
question: "What is your name?",
type: FieldType.TEXT
}).onChange((value) => {
updateMyFormState("name-field", value)
});
Parameters
FieldType - The type of field to add.addBehavior(event: FieldEvent, behavior: FieldBehavior): Field
Adds a behavior to the field for a specific event.
Parameters
FieldEvent - The event to add the behavior for.FieldBehavior - The behavior to add.Returns
onFocus(callback: (previousName: string) => void): Field
Sets a callback to be invoked when the field is focused.
Parameters
onChange(callback: (value: string | number | boolean) => void): Field
Sets a callback to be invoked when the field's value changes.
Parameters
addValidation(validation: FieldValidation): Field
Adds validation to the field. Validation behaves differently based on the field type. Consult the FieldType Table for more details.
FieldType
| type | behavior | value | validation |
|---|---|---|---|
text |
Single-line free-form text question. | string |
TextFieldValidation |
number |
Numeric question | number |
NumberFieldValidation |
textarea |
Multi-line free-form text | string |
TextAreaFieldValidation |
select |
Single choice from a set of options. Behaviors can be attached to each option. | string |
SelectFieldValidation |
multiselect |
Multiple choices from a set of options. | string |
MultiselectFieldValidation |
yesNo |
Yes/No question | boolean |
YesNoFieldValidation |
intent |
Placeholder for non-answer intents. | N/A | IntentFieldValidation |
info |
Informational message only. | N/A | N/A |
FieldEvents Enum
Enum of events a field can subscribe to.
validAnswer - Triggered when the field's value is valid.invalidAnswer - Triggered when the field's value is invalid.endRequested - Triggered when the user wants to end the conversation at this field.validYesAnswer - Triggered when the user answers yes to a yes/no question.validNoAnswer - Triggered when the user answers no to a yes/no question.FieldBehavior
moveTo - Moves the conversation focus to a specific field, outputs question for that field.output - Outputs a string to the user.end - Ends the conversation.TextFieldValidation: `addValidation(validation: TextFieldValidation)`
addValidation parameters for a text type field.
Fields:
Strict Validation:
If validate: true we will not accept input that does not match the pattern
Patterns:
When a pattern is provided the return value will be only the string that matches the pattern. If the user says "I think my email is doghouse @ gmail" the returned value will be "doghouse@gmail.com".
This is not strictly enforced if validate: false and we will return the user's input if unable to parse a value that matches the pattern.
email - Returns extracted valid email.phone - Returns extracted phone number.url - Returns extracted URL.date - Returns extracted date in YYYY-MM-DD format.name - Returns extracted name.TextAreaFieldValidation: `addValidation(validation: TextAreaFieldValidation)`
addValidation parameters for a textarea type field.
Fields:
NumberFieldValidation: `addValidation(validation: NumberFieldValidation)`
addValidation parameters for a number type field.
Fields:
SelectFieldValidation: `addSelectOption(option: SelectOption)`
Validation for select type fields. Different from other fields in that the answer is either an option from the list or not.
Select Option Parameters:
Example:
builder.addField({name: "job-type", question: "Which role are you applying for?", type: FieldType.SELECT})
.addSelectOption({
label: "Software Engineer",
value: "software-engineer",
behaviors: [
{type: FieldBehaviorType.MOVE_TO, moveToFieldName: "engineer-welcome-info"},
]
})
.addSelectOption({
label: "Designer",
value: "designer",
behaviors: [
{type: FieldBehaviorType.MOVE_TO, moveToFieldName: "designer-welcome-info"},
]
})
.addSelectOption({
label: "None",
value: "none",
behaviors: [
{type: FieldBehaviorType.END},
]
})
MultiselectFieldValidation: `addSelectOption(option: MultiselectOption)`
Validation for multiselect type fields.
Multiselect Option Parameters:
YesNoFieldValidation: `addValidation(validation: YesNoFieldValidation)`
Validation for yes/no type fields.
Fields:
IntentField
Intent type fields do not return a value but let you check in on users without requiring a specific answer. They are useful for validating past inputs and naturally ending conversations or sections of a conversation.
Example:
builder.addField({name: "check-in", question: "Do you have anything else to add?", type: FieldType.INTENT})
Builder.addField({name: "check-answer-change", question: "Do you want me to repeat any of your answers?", type: FieldType.INTENT})
Setting up authentication for a conversation.
Backend:
app.post('/conversation-token', (req, res) => {
const response = await fetch(`https://app.veform.co/veform-api/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.VEFORM_API_KEY}`,
},
});
const data = await response.json();
res.json({token: data.token});
});
Frontend:
const builder = new Veform.VeformBuilder();
const veform = new Veform.Veform(builder);
const token = await fetch('https://mybackend.com/conversation-token', {
method: 'POST',
}).then(response => response.json()).then(data => data.token);
veform.start(token);
<div class="container">
<button id="start" class="start">Start conversation</button>
<label for="name">Name</label>
<input id="name" type="text"/>
<label for="email">Email</label>
<input id="email" type="email"/>
<label for="phone">Phone</label>
<input id="phone" type="tel"/>
<label for="url">Website</label>
<input id="url" type="url"/>
<label for="date">Date</label>
<input id="date" type="date"/>
</div>
<script>
const builder = new Veform.VeformBuilder();
builder.addField({name: 'name', question: 'What is your name?', type: 'text'}).addValidation({
pattern: "name"
})
builder.addField({name: 'email', question: 'What is your email?', type: 'text'}).addValidation({
pattern: "email"
})
builder.addField({name: 'phone', question: 'What is your phone number?', type: 'text'}).addValidation({
pattern: "phone"
})
builder.addField({name: 'url', question: 'What is your website?', type: 'text'}).addValidation({
pattern: "url"
})
builder.addField({name: 'date', question: 'What is the date?', type: 'text'}).addValidation({
pattern: "date"
})
builder.addField({name: 'goodbye', question: 'Thanks for your time, Goodbye!', type: 'info'});
const veform = new Veform.Veform(builder);
veform.onFocusChanged((previous, next) => {
document.getElementById(next).focus();
});
veform.onFieldValueChanged((fieldName, value) => {
document.getElementById(fieldName).value = value;
});
veform.start('https://mybackend.com/conversation-token');
</script>