Skip to main content
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

Trigger a Server-Side Form

Forms created in the dashboard can be triggered by their tool name:
// Show a form defined in the dashboard
ansa.showForm("contact_form");

Trigger a Client-Side 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: "[email protected]", message: "I need help with..." }
  }
});

Form Schema Reference

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

TypeDescriptionExample Use
textSingle-line text inputName, company
emailEmail with validationContact email
telPhone numberPhone callback
numberNumeric inputQuantity, budget
textareaMulti-line textMessage, description
selectDropdown single selectCategory, plan
multiselectDropdown multi-selectInterests, features
dateDate pickerAppointment date
datetimeDate and time pickerMeeting schedule
fileFile uploadDocuments
imageImage uploadProfile 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

Contact Form

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)
    });
  }
});

Lead Qualification Form

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)
    });
  }
});

Dynamic Forms

Using registerFormSchema

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: "[email protected]", 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: [email protected]
- 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.
  1. Progressive disclosure — Ask for minimal info first, then follow up for details
  2. Pre-fill when possible — Use identify() to pass user data for pre-filling
  3. Validate client-side — Use validation rules to catch errors before submission
  4. Provide context — Use title and description to explain why you need the info
  5. Handle errors gracefully — Throw errors in onSubmit to show error state in form

Next Steps