Building a form
Forms capture data from visitors, customers, or your own team. Every form gets a public URL at /f/{public-token} that works standalone and as an embed on any website page. This page walks through the build flow, from field picker to action chain to publish.
Field types
The form builder supports 12 input types plus 2 layout primitives:
| Type | Captures |
|---|---|
| Text | Single-line free text with configurable max length |
| Validated email format | |
| Phone | International phone with country-code dropdown |
| Number | Integer or decimal with min/max |
| URL | Validated URL |
| Textarea | Multi-line free text |
| Date | Date picker; configurable future/past constraints |
| Select | Dropdown with fixed options |
| Radio | Mutually-exclusive option list |
| Checkbox | Single "accept terms" or multi-select array |
| Rating | 1-5 star rating |
| File upload | One or multiple file uploads (size + MIME allowlist) |
| Heading | Display-only section break (h2/h3/h4) |
| Divider | Horizontal rule for visual grouping |
Every field carries: field key (unique within the form), label, help text, placeholder, required flag, and an optional conditional rule that shows/hides it based on the value of another field.
Layout
Fields stack in a single column by default. Each field has a Width picker (Full / Half / Third / Quarter / Two-thirds / Three-quarters) on a 12-column grid; give two or more fields the same Row group number to place them side-by-side (collapses to 1-column on mobile). Reorder fields with the ▲/▼ buttons.
For surveys with repeating structure, start from a template: the form module ships 8 general defaults you can clone (Contact us, Support request, Quote request, Newsletter signup, NPS survey, Event registration, Customer feedback, GDPR data request) plus 4 field-service templates (electrical minor works, plumbing leak, HVAC service record, general site risk assessment).
Conditional logic
Any field can be shown/hidden based on another field's value. Conditional rules use 6 operators:
equals / not equals: exact string match
contains / does not contain: substring match
is empty / is not empty: null/empty check
Example: a "Where did you hear about us?" dropdown with options Referral / Google / Social / Other. Add a "Who referred you?" text field with rule show if referral_source equals Referral. Visitors picking anything else never see the follow-up field and never have to skip it.
Conditional fields are skipped during required-field validation when hidden, so a required_for_job_completion field that's conditionally hidden doesn't block a mobile-engineer completing the job.
Submit actions
When a visitor submits, the form runs a chain of actions in order. Stack as many as you need; each has its own config.
| Action | What it does |
|---|---|
| Send email | Deliver a templated email to a fixed address or to an email captured in a form field. Uses your tenant's SMTP provider. |
| Create contact | Upsert a CRM contact from the form data (match by email_hash, update if exists). Optional tag application. |
| Create ticket | Open a helpdesk ticket with the submission payload as the description. Assign to a queue. |
| Create deal | Open a CRM deal in a specific pipeline + stage. |
| Fire webhook | POST the submission as JSON to a URL. HMAC-signed; see webhooks docs. |
| Auto-reply | Email the submitter a templated confirmation. |
| Team alert | Notify a list of users (email + in-app notification). |
| Add to segment | Tag the submitted contact into a segment for campaign targeting. |
| Redirect | Send the visitor to a URL after submit (thank-you page, Stripe checkout, etc.). |
| Email PDF copy | Generate a PDF of the completed submission and email it to a list of staff users and/or ad-hoc email addresses. Optional subject + intro message overrides. Uses the tenant's SMTP provider via the standard mail pipeline (logged in EmailLog, retries on failure). |
If any action in the chain fails, the "continue on error" flag on each action determines whether the chain halts or proceeds. The form submission itself is always persisted regardless.
Publishing
Every form has two states: draft (edits don't affect the public URL) and active (live). Toggle is_active from the form settings. An inactive form's public URL returns 404.
The public URL is /f/{public_token}, globally unique, not the tenant-scoped slug. This prevents cross-tenant routing collisions (two tenants with a form slug of contact would otherwise route to each other's forms). The token is minted on create and never changes, so the URL is safe to paste into print materials and QR codes.
Embedding
On a concorbit website, drop a Form section or canvas element on any page. Configure the slug of the form you want (the section resolves slug → public-token automatically) plus iframe height. Sandbox attributes are baked in so the embed can submit forms but can't break out of the iframe.
On a non-concorbit site:
<iframe
src="https://{your-subdomain}.concorbit.com/f/{public-token}"
style="width: 100%; height: 600px; border: 0;"
loading="lazy"
sandbox="allow-forms allow-scripts allow-same-origin allow-popups"
></iframe>
Spam guards
Every public form has:
Honeypot field: a hidden input bots fill but humans don't. Submissions with a non-empty value are silently discarded.
Rate limiting: 10 submissions per minute per IP (tightened from 20/min after an audit).
Time-trap: a submission that arrives under 2 seconds after the form renders is flagged (bots often submit instantly). Flagged submissions still persist but they're quarantined and don't fire the action chain by default.
reCAPTCHA: optional, configurable per form. Requires a Google reCAPTCHA v3 key in tenant settings.
Submissions admin
Every form has a Submissions tab at /forms/{form}/submissions. Filter by date, field value, or action success/failure. Click any row for the full payload, the chain execution log, and the option to re-fire a failed action.
Required permission: forms.submissions.view (read).