FAB Customer Experience

GuideDocs
k
Integeration
On Website
In Flutter App
In Android App
In React-Native
Lead Form

Build by FAB Builder. The source code is available on GitHub.

ContactSponsor

Translate

Choose language

Select your preferred language for this documentation.

 

Overview

When a visitor fills in the form and clicks submit, the form sends their details to yourFAB CRM dashboard, where they appear as a new lead. Once it's saved, the form resets and thanks the visitor; if anything goes wrong, a short error message is shown so they can try again.

Every lead is tied to yourapplication id, so FAB CRM knows which account it belongs to. Copy yours from the CS app underPage Pilot app → pagepilot.fabbuilder.com/tenantand paste it into the form'sLEAD_APIURL in place ofYOUR_WORKSPACE_ID.

Integration

Getting the form working takes three short steps. Follow them in order and you'll be capturing leads in a few minutes.

  1. 1. Add the component— copy theLeadFormcode below into your project, for example ascomponents/LeadForm.tsx, and render it wherever you want the form to appear.
  2. 2. Connect your account— open the CS app atPage Pilot app → pagepilot.fabbuilder.com/tenant, copy your application id, and paste it into theLEAD_APIURL in place ofYOUR_WORKSPACE_ID.
  3. 3. Label your leads— set thetagsarray (for example['CX']) so you can tell at a glance which form a lead came from on your FAB CRM dashboard.

That's all you need for a working form. To stop spam submissions, you can also add Google reCAPTCHA — see thereCAPTCHAsection below.

The full component

Copy this in as-is. The list of inputs lives in a singleFIELDSarray near the top, so you can add, remove, or rename fields without touching the rest of the code.

LeadForm.tsx

A complete, ready-to-use lead capture form.

 

Already have a form?

If you've already built your own form, you don't need the component above — just call this helper with your form values to send the lead

 
import { useState } from 'react';

// Replace YOUR_WORKSPACE_ID with your application id.
const LEAD_API = 'https://cs.fabbuilder.com/api/tenant/YOUR_WORKSPACE_ID';

// Dynamic field config — add / remove / reorder inputs here.
const FIELDS = [
  { name: 'firstName',     label: 'First Name',   type: 'text' },
  { name: 'businessEmail', label: 'Email',        type: 'email' },
  { name: 'phoneNumber',   label: 'Phone Number', type: 'tel' },
  { name: 'companyName',   label: 'Company Name', type: 'text' },
  { name: 'message',       label: 'Message',      textarea: true },
];

const EMPTY = Object.fromEntries(FIELDS.map((f) => [f.name, '']));

export default function LeadForm() {
  const [values, setValues] = useState(EMPTY);
  const [status, setStatus] = useState('idle'); // idle | submitting | success | error
  const [error, setError] = useState('');

  const onChange = (e) => {
    const { name, value } = e.target;
    setValues((v) => ({ ...v, [name]: value }));
  };

  const onSubmit = (e) => {
    e.preventDefault();
    setStatus('submitting');
    setError('');

    const payload = {
      firstName: values.firstName,
      phone: values.phoneNumber,
      email: values.businessEmail,
      companyName: values.companyName,
      description: `${values.companyName}-${values.message}`,
      source: 'website',
    };
    const body = {
      data: {
        ...payload,
        messageInfo: { text: values.message },
        tags: ['CX'],
        sourceDetail: { pageUrl: window.location.href },
      },
    };

    const qs = new URLSearchParams(payload).toString();
    const xhr = new XMLHttpRequest();
    xhr.open('POST', `${LEAD_API}/lead?${qs}`, true);
    xhr.setRequestHeader('Content-Type', 'application/json');

    xhr.onreadystatechange = () => {
      if (xhr.readyState !== XMLHttpRequest.DONE) return;
      if (xhr.status === 200) {
        setStatus('success');
        setValues(EMPTY);
      } else {
        const data = JSON.parse(xhr.responseText || '{}');
        setStatus('error');
        setError(data.message || 'Submission failed. Please try again.');
      }
    };

    xhr.onerror = () => {
      setStatus('error');
      setError('Network error. Please check your connection and try again.');
    };

    xhr.send(JSON.stringify(body));
  };

  if (status === 'success') return <p>Thanks — we got your details!</p>;

  return (
    <form onSubmit={onSubmit}>
      {FIELDS.map((f) => (
        <div key={f.name}>
          <label htmlFor={f.name}>{f.label}</label>
          {f.textarea ? (
            <textarea id={f.name} name={f.name} value={values[f.name]} onChange={onChange} />
          ) : (
            <input
              id={f.name}
              name={f.name}
              type={f.type || 'text'}
              value={values[f.name]}
              onChange={onChange}
            />
          )}
        </div>
      ))}
      {error && <p>{error}</p>}
      <button type="submit" disabled={status === 'submitting'}>
        {status === 'submitting' ? 'Submitting…' : 'Submit'}
      </button>
    </form>
  );
} 

Integrate using AI

Prefer to let your AI assistant do the work? Copy the prompt below into Cursor, Claude, or GitHub Copilot and it will build the form and connect it to FAB CRM for you.

 

Integrate using AI

Copy this prompt into Cursor, Claude, or GitHub Copilot and let it wire PagePilot into your codebase, step by step.

 
 You are helping me add a lead capture form to my React app. Build a single, self-contained component called LeadForm that submits to the FAB CRM lead API. Follow every instruction below.

GOAL
Create LeadForm.tsx — a contact form that, on submit, creates a lead in FAB CRM.

API
- Endpoint: const LEAD_API = "https://cs.fabbuilder.com/api/tenant/YOUR_WORKSPACE_ID";
- Replace YOUR_WORKSPACE_ID with my workspace/application id (found in the CS app under Settings -> General Settings: https://pagepilot.fabbuilder.com/tenant).
- Method: POST, header Content-Type: application/json, sent with XMLHttpRequest.

FIELDS (drive these from a single config array so they're easy to change)
- firstName     (label "First Name",   text)
- businessEmail (label "Email",        email)
- phoneNumber   (label "Phone Number", tel)
- companyName   (label "Company Name", text)
- message       (label "Message",      textarea)

ON SUBMIT
1. Build a flat payload that maps the inputs to the API's field names:
     firstName     -> firstName
     phoneNumber   -> phone
     businessEmail -> email
     companyName   -> companyName
     description   -> `${companyName}-${message}`
     source        -> "website"
2. Build the request body as: { data: { ...payload, messageInfo: { text: message }, tags: ["CX"], sourceDetail: { pageUrl: window.location.href } } }
3. Append the flat payload to the URL as a query string AND send the body as JSON:
     POST `${LEAD_API}/lead?${new URLSearchParams(payload)}`

STATES & UX
- Track a status: "idle" | "submitting" | "success" | "error".
- While submitting, disable the submit button and show a loading label.
- On status 200: clear the form and show a friendly thank-you message.
- On any other status: read the error message from xhr.responseText (JSON, "message" field) and show it inline.
- On a network error (xhr.onerror): show a generic "check your connection" message.

CONSTRAINTS
- Use plain React with hooks only — no form libraries, no antd, no styled-components.
- Keep it dependency-free apart from React.
- Add short comments explaining each step.

Form fields

Out of the box the form asks for the details below. The fields are listed in one place at the top of the component, so you can drop any you don't need or add new ones to suit your use case.

  • First name—Who the lead is.
  • Email—Where to reach them.
  • Phone—An alternate way to contact them.
  • Company—The company they represent.
  • Message—What they want to talk to you about.

What gets sent

You usually won't need to touch this, but it helps to know what leaves the browser. Before sending, the form renames its inputs to the names FAB CRM expects (for examplephoneinstead ofphoneNumber) and bundles them up into the request below.

 

The request

How the form's inputs are packaged before they're sent.

 
 // Form input → API field mapping
const payload = {
  firstName:   values.firstName,            // firstName  -> firstName
  phone:       values.phoneNumber,          // phoneNumber -> phone
  email:       values.businessEmail,        // businessEmail -> email
  companyName: values.companyName,          // companyName -> companyName
  description: `${values.companyName}-${values.message}`,
  source:      'website',
  // token:    gReCaptchaToken,             // optional reCAPTCHA token
};

// Sent BOTH as the query string AND nested under "data" in the body.
const body = {
  data: {
    ...payload,
    messageInfo: { text: values.message },  // message -> messageInfo.text
    tags: ['CX'],
    sourceDetail: { pageUrl: window.location.href },
  },
};

const qs = new URLSearchParams(payload).toString();
const xhr = new XMLHttpRequest();
xhr.open('POST', `${LEAD_API}/lead?${qs}`, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(body));

Two extra pieces of information ride along automatically:

string[]optionaldefault:['CX']
tags— a label that helps you group and filter leads in FAB CRM.
{ pageUrl: string }optionaldefault:window.location.href
sourceDetail— records which page the lead came from, so you know where your leads are coming from. Add your own tracking details here if you like.

 

Stopping spam (optional)

Public forms attract bots. If you start seeing junk submissions, add Google reCAPTCHA: it quietly checks each submission in the background and attaches a one-timetokento the request so FAB CRM can verify it's a real person. This step is optional — only add it if your account requires it or you run into spam.

 

Adding reCAPTCHA

Get a token when the form is submitted, then send it along with the lead.

 
import { useGoogleReCaptcha } from 'react-google-recaptcha-v3';

function LeadFormWithCaptcha() {
  const { executeRecaptcha } = useGoogleReCaptcha();

  const onSubmit = async (e) => {
    e.preventDefault();
    if (!executeRecaptcha) return; // not ready yet

    // Generate a v3 token for this action…
    const token = await executeRecaptcha('enquiryFormSubmit');

    // …and include it in the payload.
    await submitLead(values, { token });
  };

  // …render the same form, calling onSubmit
}

// Wrap your app once with the provider:
// <GoogleReCaptchaProvider reCaptchaKey="YOUR_SITE_KEY">
//   <App />
// </GoogleReCaptchaProvider> 

After submitting

Here's what the visitor experiences once they hit submit, and how the form handles each outcome:

  • It worked— the lead is saved, the form clears, and a thank-you message appears. You can also redirect to a thank-you page or track the conversion here.
  • Something was rejected— FAB CRM sends back a reason, which the form shows beneath the submit button so the visitor can fix it.
  • The request didn't reach the server— usually a connection problem; the form shows a "please check your connection and try again" message.

API reference

The technical details, for reference. If you're using the component or helper above, this is all handled for you — these tables are here for anyone integrating the endpoint directly.

Endpoint

PropertyDescriptionTypeDefault
POST /leadCreates a lead. The base URL is scoped to your application id.https://cs.fabbuilder.com/api/tenant/YOUR_WORKSPACE_ID/lead—
Content-TypeRequest body is JSON.application/json—
query stringThe flat payload fields, URL-encoded, appended to the URL.?firstName=…&email=…—

Body fields (under `data`)

PropertyDescriptionTypeDefault
firstNameLead's first name.string—
emailEmail address.string—
phonePhone number, digits only.string—
companyNameCompany name.string—
descriptionCombined `${companyName}-${message}` summary.string—
sourceLead source.string'website'
messageInfo.textThe visitor's free-text message.string—
tagsLead categories.string[]—
sourceDetail.pageUrlURL of the page the form was submitted from.string—
tokenreCAPTCHA v3 token (optional).string—