Forms let you collect structured data from users during conversations. You can trigger forms programmatically from your application code, define forms client-side with custom schemas, or use server-side forms created in the dashboard.
Quick Start
Forms created in the dashboard can be triggered by their tool name:
// Show a form defined in the dashboard
ansa . showForm ( "contact_form" );
Define and display forms entirely in your frontend code:
ansa . showForm ({
fields: [
{ name: "email" , label: "Email" , type: "email" },
{ name: "message" , label: "How can we help?" , type: "textarea" }
],
submitButtonText: "Send Message"
}, {
onSubmit : ( data ) => {
console . log ( "Form submitted:" , data );
// { email: "user@example.com", message: "I need help with..." }
}
});
Client-side forms are defined using a schema object:
interface FormSchema {
fields : FormField [];
title ?: string ;
description ?: string ;
submitButtonText ?: string ;
showLabels ?: boolean ;
successMessage ?: string ;
errorMessage ?: string ;
}
Field Types
Type Description Example Use textSingle-line text input Name, company emailEmail with validation Contact email telPhone number Phone callback numberNumeric input Quantity, budget textareaMulti-line text Message, description selectDropdown single select Category, plan multiselectDropdown multi-select Interests, features dateDate picker Appointment date datetimeDate and time picker Meeting schedule fileFile upload Documents imageImage upload Profile photo
Field Definition
interface FormField {
name : string ; // Field identifier (used in form data)
label : string ; // Display label
type : FormFieldType ; // One of the types above
placeholder ?: string ; // Input placeholder text
defaultValue ?: string ; // Pre-filled value
options ?: FormFieldOption []; // For select/multiselect
validation ?: FormFieldValidation ;
disabled ?: boolean ;
}
interface FormFieldOption {
label : string ; // Display text
value : string ; // Submitted value
}
Validation Rules
interface FormFieldValidation {
required ?: { value : boolean ; message : string };
minLength ?: { value : number ; message : string };
maxLength ?: { value : number ; message : string };
min ?: { value : number ; message : string };
max ?: { value : number ; message : string };
pattern ?: { value : string ; message : string }; // Regex pattern
}
Complete Examples
ansa . showForm ({
title: "Contact Us" ,
description: "We'll get back to you within 24 hours" ,
fields: [
{
name: "name" ,
label: "Full Name" ,
type: "text" ,
placeholder: "John Smith" ,
validation: {
required: { value: true , message: "Name is required" }
}
},
{
name: "email" ,
label: "Email Address" ,
type: "email" ,
validation: {
required: { value: true , message: "Email is required" }
}
},
{
name: "company" ,
label: "Company" ,
type: "text" ,
placeholder: "Optional"
},
{
name: "message" ,
label: "Message" ,
type: "textarea" ,
placeholder: "How can we help?" ,
validation: {
required: { value: true , message: "Please enter a message" },
minLength: { value: 10 , message: "Message must be at least 10 characters" }
}
}
],
submitButtonText: "Send Message" ,
successMessage: "Thanks! We'll be in touch soon."
}, {
onSubmit : async ( data ) => {
await fetch ( "/api/contact" , {
method: "POST" ,
headers: { "Content-Type" : "application/json" },
body: JSON . stringify ( data )
});
}
});
ansa . showForm ({
title: "Get a Custom Quote" ,
fields: [
{
name: "email" ,
label: "Work Email" ,
type: "email" ,
validation: { required: { value: true , message: "Required" } }
},
{
name: "company_size" ,
label: "Company Size" ,
type: "select" ,
options: [
{ label: "1-10 employees" , value: "1-10" },
{ label: "11-50 employees" , value: "11-50" },
{ label: "51-200 employees" , value: "51-200" },
{ label: "201-1000 employees" , value: "201-1000" },
{ label: "1000+ employees" , value: "1000+" }
]
},
{
name: "use_case" ,
label: "Primary Use Case" ,
type: "select" ,
options: [
{ label: "Customer Support" , value: "support" },
{ label: "Sales" , value: "sales" },
{ label: "Internal Tools" , value: "internal" },
{ label: "E-commerce" , value: "ecommerce" },
{ label: "Other" , value: "other" }
]
},
{
name: "budget" ,
label: "Monthly Budget" ,
type: "select" ,
options: [
{ label: "Under $100" , value: "<100" },
{ label: "$100 - $500" , value: "100-500" },
{ label: "$500 - $2000" , value: "500-2000" },
{ label: "$2000+" , value: "2000+" }
]
}
],
submitButtonText: "Get Quote"
}, {
onSubmit : ( data ) => {
// Send to CRM
analytics . track ( "Lead Qualified" , data );
},
sendToAgent: true // Agent can see the submitted data
});
Appointment Booking
ansa . showForm ({
title: "Schedule a Demo" ,
fields: [
{
name: "name" ,
label: "Your Name" ,
type: "text" ,
validation: { required: { value: true , message: "Required" } }
},
{
name: "email" ,
label: "Email" ,
type: "email" ,
validation: { required: { value: true , message: "Required" } }
},
{
name: "date" ,
label: "Preferred Date" ,
type: "date" ,
validation: { required: { value: true , message: "Please select a date" } }
},
{
name: "time" ,
label: "Preferred Time" ,
type: "select" ,
options: [
{ label: "9:00 AM" , value: "09:00" },
{ label: "10:00 AM" , value: "10:00" },
{ label: "11:00 AM" , value: "11:00" },
{ label: "2:00 PM" , value: "14:00" },
{ label: "3:00 PM" , value: "15:00" },
{ label: "4:00 PM" , value: "16:00" }
]
},
{
name: "notes" ,
label: "Anything specific you'd like to discuss?" ,
type: "textarea"
}
],
submitButtonText: "Book Demo"
}, {
onSubmit : async ( data ) => {
await fetch ( "/api/book-demo" , {
method: "POST" ,
headers: { "Content-Type" : "application/json" },
body: JSON . stringify ( data )
});
}
});
Register form schema providers that generate forms dynamically based on context:
ansa . registerFormSchema ({
// Form name -> schema provider function
"product_inquiry" : async ( args , user ) => {
// args: parameters from agent tool call
// user: { visitorId, userId?, userMetadata?, conversationId? }
// Fetch products from your API
const products = await fetch ( "/api/products" ). then ( r => r . json ());
return {
title: "Product Inquiry" ,
fields: [
{
name: "email" ,
label: "Email" ,
type: "email" ,
// Pre-fill from user identity
defaultValue: user . userMetadata ?. email
},
{
name: "product" ,
label: "Product" ,
type: "select" ,
// Dynamic options from API
options: products . map ( p => ({
label: ` ${ p . name } - $ ${ p . price } ` ,
value: p . id
})),
// Pre-select from agent args
defaultValue: args . productId
},
{
name: "quantity" ,
label: "Quantity" ,
type: "number" ,
defaultValue: "1" ,
validation: {
min: { value: 1 , message: "Minimum 1" },
max: { value: 100 , message: "Maximum 100" }
}
}
],
submitButtonText: "Request Quote"
};
}
});
The agent can then trigger this form:
Agent: I can help you get a quote for that product. Let me pull up the form.
[Shows product_inquiry form with productId pre-selected]
Context-Aware Pre-filling
ansa . registerFormSchema ({
"support_ticket" : async ( args , user ) => {
// Get user's recent orders for context
let orders = [];
if ( user . userId ) {
orders = await fetch ( `/api/users/ ${ user . userId } /orders` ). then ( r => r . json ());
}
return {
title: "Create Support Ticket" ,
fields: [
{
name: "email" ,
label: "Email" ,
type: "email" ,
defaultValue: user . userMetadata ?. email ,
disabled: !! user . userMetadata ?. email // Lock if known
},
{
name: "order" ,
label: "Related Order (optional)" ,
type: "select" ,
options: [
{ label: "No specific order" , value: "" },
... orders . map ( o => ({
label: `# ${ o . id } - ${ o . date } ($ ${ o . total } )` ,
value: o . id
}))
]
},
{
name: "issue" ,
label: "Issue Type" ,
type: "select" ,
options: [
{ label: "Order issue" , value: "order" },
{ label: "Product question" , value: "product" },
{ label: "Billing" , value: "billing" },
{ label: "Other" , value: "other" }
]
},
{
name: "description" ,
label: "Describe your issue" ,
type: "textarea" ,
validation: {
required: { value: true , message: "Please describe your issue" }
}
}
]
};
}
});
Handling Submissions
Client-Side Callback
ansa . showForm ( schema , {
onSubmit : async ( data ) => {
// data is an object with field names as keys
console . log ( data );
// { email: "user@example.com", message: "Hello..." }
// Send to your backend
const response = await fetch ( "/api/forms/submit" , {
method: "POST" ,
headers: { "Content-Type" : "application/json" },
body: JSON . stringify ({
formType: "contact" ,
data ,
visitorId: ansa . getVisitorId ()
})
});
if ( ! response . ok ) {
throw new Error ( "Submission failed" ); // Shows error in widget
}
},
onCancel : () => {
console . log ( "User closed the form" );
}
});
Send to Agent
When sendToAgent: true, the form data is also sent to the agent as a chat message:
ansa . showForm ({
fields: [
{ name: "email" , label: "Email" , type: "email" },
{ name: "question" , label: "Question" , type: "textarea" }
]
}, {
sendToAgent: true ,
onSubmit : ( data ) => {
// Still fires, but agent also receives the data
analytics . track ( "Form Submitted" , data );
}
});
The agent sees:
User submitted form:
- Email: user@example.com
- Question: How do I integrate with my CRM?
Triggering Patterns
On Page Load (Delayed)
// Show lead capture after 30 seconds on pricing page
if ( window . location . pathname === "/pricing" ) {
setTimeout (() => {
if ( ! ansa . isOpen ()) {
ansa . open ();
ansa . showForm ( "pricing_inquiry" );
}
}, 30000 );
}
On User Action
// Trigger quote form when user clicks CTA
document . querySelectorAll ( "[data-quote-form]" ). forEach ( btn => {
btn . addEventListener ( "click" , ( e ) => {
e . preventDefault ();
const product = btn . dataset . product ;
ansa . showForm ({
fields: [
{ name: "email" , label: "Email" , type: "email" },
{ name: "product" , label: "Product" , type: "text" , defaultValue: product , disabled: true }
]
}, { sendToAgent: true });
});
});
Exit Intent
let exitFormShown = false ;
document . addEventListener ( "mouseleave" , ( e ) => {
if ( e . clientY < 0 && ! exitFormShown && ! ansa . isOpen ()) {
exitFormShown = true ;
ansa . open ();
ansa . showForm ({
title: "Before you go..." ,
description: "Get 10% off your first order" ,
fields: [
{ name: "email" , label: "Email" , type: "email" }
],
submitButtonText: "Get Discount"
}, {
onSubmit : ( data ) => {
// Send discount code
}
});
}
});
Best Practices
Keep forms short. Each additional field reduces completion rates. Start with 2-3 essential fields.
Progressive disclosure — Ask for minimal info first, then follow up for details
Pre-fill when possible — Use identify() to pass user data for pre-filling
Validate client-side — Use validation rules to catch errors before submission
Provide context — Use title and description to explain why you need the info
Handle errors gracefully — Throw errors in onSubmit to show error state in form
Next Steps
Custom Events Trigger automations based on user actions
Identity Pre-fill forms with user data