Features
Forms — Lead Capture Engine
Build data collection forms, publish them publicly, and have every submission automatically create a CRM lead. The direct pipeline from marketing into the CRM — no manual data entry, no spreadsheet handoffs.
- Free: 3 forms, 100 submissions, builder + public forms (
forms:builder,forms:public) - Sales: 20 forms, 5,000 submissions, + lead capture (
forms:lead-capture) and form duplication (forms:duplicate) - Caps scale up the plate ladder — Growth 50 / Full Loop 100 / Agency 250 forms.
Planned: AI form builder (forms:ai-form-builder) is a reserved key (Studio + ai-pack add-on) — no generate-form action or UI ships yet.
Forms management
Forms list (/forms): Table with name, status (Draft/Published/Closed), submission count, last updated, actions. Row actions: Edit, View (preview + share link), View submissions, Publish/Unpublish, Duplicate (Sales), Delete.
Duplicate (Sales): Creates a full copy of the form as a Draft. Requires forms:duplicate feature flag.
Form builder
The full-page form builder (/forms/new or /forms/[formId]) has two panels: the field editor on the left and a live preview on the right.
Field types
| Type | Input | When to use |
|---|---|---|
| Text | Single-line input | Name, company, short answers |
| Email input | Contact email (required for lead capture) | |
| Number | Number input | Budget, team size |
| Long Text | Textarea | Project description, questions |
| Dropdown | Select | Industry, product interest |
| Checkbox | Checkbox | Multi-select options, consent |
| Radio | Radio group | Single-select from options |
| Date | Date picker | Preferred meeting date |
Deprecated — File Upload. The file field type is no longer selectable in the builder (parity TASK-CUT-01). Existing forms with file fields keep rendering for backwards compatibility, but new fields cannot be created with this type — public-form uploads open a storage/security surface not justified at this stage.Lead capture configuration
When “Create lead on submit” is enabled:
- A CRM Lead is created for each submission
- Field mapping: connect form fields to lead attributes (name, email, phone, company)
- Both a Name field and Email field must be mapped
- The created lead's
sourcedefaults to'website'—'form'is not a validleads.source. Form origin is recorded onleads.source_form_id(FK →forms), andform_submissions.lead_idlinks the submission back to the created lead.
Public forms
Published forms are accessible at /[locale]/f/[formId] (e.g. /en/f/<uuid>) — no authentication required. Copy the embeddable <iframe> snippet from the “View form” dialog to embed on any external website. The public page (getPublicForm on load, submitPublicForm on submit) is rate-limited per IP (10 submissions/minute per form) and strips HTML tags from string fields.
Submission processing:
- Per-IP rate-limit gate + HTML-tag stripping on every string field
- Validates submission against form field definitions
- Creates a
form_submissionsrow - If lead capture is enabled, the
submit_public_formRPC creates aleadsrow (sourcedefaults towebsite) withsource_form_idset to the originating form, and back-fillsform_submissions.lead_id - Returns success or validation error
Technical reference
| Item | Detail |
|---|---|
| Action files | actions/forms.ts (authenticated), actions/public-forms.ts (public) |
| Server actions | getForms, getForm, createForm, updateForm, duplicateForm, deleteForm, getFormSubmissions, getPublicForm, submitPublicForm |
| Feature flags | forms:builder (Free, capped), forms:submissions (Free, capped), forms:public (Free), forms:lead-capture (Sales), forms:duplicate (Sales) |
| Planned (reserved key, no UI) | forms:ai-form-builder (Studio + ai-pack) |
| Migration | 00003_domain_tables.sql (forms, form_submissions) |
| Audit actions | form.created, form.updated, form.deleted, form.submission |