Contact forms & leads
Build lead-capture forms with a custom field editor, embed them inline on any page, and work the submissions in a dedicated leads inbox.
Not every visitor wants to start a chat. Contact forms let you build classic "get in touch" forms with a drag-and-drop field editor, embed them inline on your own website, and collect every submission as a lead in a dedicated inbox your team can work through.
Unlike the chat widget, a contact form isn't a floating bubble - it renders in the page, where you want it, styled to match. And unlike a ticket, a lead is a lightweight record with its own simple lifecycle.
Build a form
Open Admin → Contact forms → New form. Every new form starts from a sensible default template - Name, Email, Phone, Subject and Message - which you can reorder, edit, delete or replace entirely.
The field builder supports:
| Field type | Use it for |
|---|---|
| Short text | Names, company, subject lines |
| Long text | Messages, descriptions |
| The submitter's email (drives the confirmation reply) | |
| Phone | Optional phone number |
| Dropdown | A single choice from a list you define |
| Radio buttons | A single visible choice |
| Checkbox | Consent boxes ("I agree…") |
Mark a field's role - Contact name, Contact email or Subject - and that answer is promoted into a column in the leads inbox, so a lead is readable at a glance no matter how the rest of the form is laid out.
You can build as many forms as you like - a "Contact us" form, a "Request a quote" form, a campaign landing-page form - each with its own embed key, fields, branding and confirmation email.
Embed it inline
Copy the snippet from the form editor and drop it where you want the form to appear - it renders into the target element, in your page flow:
<div id="nsupport-contact-form"></div>
<script src="https://<your-app-host>/contact-form.js" data-key="pk_cf_…" async></script>Optional attributes:
| Attribute | Purpose |
|---|---|
data-target | CSS selector to render into (default #nsupport-contact-form). |
data-color | Accent color override. |
data-api-base | Backend base URL (defaults to the script's origin + /api/v1). |
The script is a single zero-dependency bundle (~6 kB) that renders inside a shadow DOM, so your site's CSS and the form never interfere with each other. A built-in live preview in the admin shows exactly how the form will look on your site before you ship it.
Work the leads
Every submission lands in Admin → Contact forms → Leads:
- A filterable, searchable, paginated inbox with per-status counts.
- Each lead moves through New → In progress → Completed, plus Archived for spam or anything irrelevant, and can be assigned to a teammate.
- Open a lead to see every answered field, the contact details, and act on it.
Confirmation email (optional)
Turn on the per-form confirmation email and, whenever someone leaves an email address, they get an automatic "we received your message" reply - with your workspace name and a "someone will be in touch" line you can customize. It uses the same email delivery as the rest of the platform and is rate-limited per recipient to prevent abuse.
Built to be safe on a public page
A contact form sits on the open internet, so it's locked down the same way the other embeddable widgets are:
- An origin allow-list - only the domains you list can load and submit the form.
- A rotating public key you can regenerate at any time.
- A hidden honeypot field and rate limiting to keep bots out.
Two kinds of form
Everything above describes a form we build and render for you. There is a second kind for when you'd rather build the frontend yourself.
| Embedded form | API form | |
|---|---|---|
| Who builds the markup | We do | You do |
| How it goes on your site | One <script> tag | Your own HTML, React, Vue, mobile app… |
| Styling | Our field builder + your accent colour | Entirely yours |
| File uploads | ✗ | ✓ |
| Leads land in | The same inbox | The same inbox |
Pick which one you're creating from the New form menu. The rest of this page applies to both — the same inbox, statuses, assignment, internal notes, notifications and confirmation emails.
API forms: build your own frontend
An API form gives you an access key and an endpoint. Design the form however you like; post to us when someone submits.
1. Create the form and copy the key
Contact forms → New form → API form (build your own). The editor shows a step-by-step guide with your real key already filled into every snippet, so you can copy and paste rather than adapt.
The key is safe to put in public HTML. It can only create submissions — it can never read them.
2. Point your form at the endpoint
It behaves like an ordinary API: you post the data, it creates one lead, and it
answers { ok: true, id: "…" }. That's all it does — a lead's status and
deletion belong to the inbox, behind your login, so a leaked key can only ever
add submissions, never change or remove them.
const res = await fetch("https://your-workspace.example/api/v1/public/forms/submit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ access_key: "pk_cf_…", name: "Asha", email: "asha@example.com" }),
});
const { ok, id } = await res.json();FormData works exactly the same way and also answers JSON — that's the form
to use when you're attaching a file.
There is one exception, and it's the version that needs no JavaScript and no build step at all:
<form action="https://your-workspace.example/api/v1/public/forms/submit" method="POST">
<input type="hidden" name="access_key" value="pk_cf_…">
<input type="hidden" name="redirect" value="https://yoursite.com/thank-you">
<label>Name <input type="text" name="name" required></label>
<label>Email <input type="email" name="email" required></label>
<label>Message <textarea name="message"></textarea></label>
<button type="submit">Send</button>
</form>When a browser submits a form like that it navigates away — it never reads the response — so this one case answers with a redirect to your thank-you page instead of JSON.
Set Redirect after submit if you use the no-JavaScript version, or the visitor lands on raw JSON after posting. Calls from your own code are unaffected: they always get JSON, whichever content type they use.
3. Name your fields however you like
Send whatever fields make sense for you — all of them arrive in the inbox,
under your own names. Four of them can also fill the inbox's dedicated columns:
name, email, phone and subject. If your form calls them something else
(applicant_name, your_email), map them under Field names and the inbox
will still show them properly.
4. Accept files (job applications, claims, portfolios)
Switch on Accept file uploads and any file part in the submission is stored and attached to the lead. Use a normal file input:
<form action="…" method="POST" enctype="multipart/form-data">
<input type="hidden" name="access_key" value="pk_cf_…">
<input type="text" name="applicant_name">
<input type="file" name="cv">
<button type="submit">Apply</button>
</form>Up to 10 MB per file, a limit you set on how many files per submission, and a document/image type allow-list you can narrow further. Attachments appear on the lead with a Download button; they are never publicly linkable, so only owners and admins can open them.
Test it before you write any frontend
Contact forms → Test a form sends a genuine submission to your own form from the browser and shows you exactly what came back — status, timing, response body — plus the lead it created. It's the fastest way to confirm a key works and see which of your field names land where.
Submissions made there are real leads, so delete them afterwards if you're tidying up.
Locking an API form down
By default an API form accepts calls from anywhere, still rate-limited — which is what makes it usable from a server, a mobile app or curl, where there is no browser origin at all. Once you know which site will be posting, list it under Allowed origins and everything else is refused.
Who can do what
| Action | Member | Agent | Admin | Owner |
|---|---|---|---|---|
| Build & configure forms | ✗ | ✗ | ✓ | ✓ |
| View & work the leads inbox | ✗ | ✗ | ✓ | ✓ |
| Submit a form (as a website visitor) | - | - | - | - |
Embeddable widgets
Put your AI support and feedback collection on any website with two tiny, zero-dependency scripts - one line each, sandboxed in a shadow DOM.
Booking & availability
Let customers book appointments, tables or rentals - set your availability once, then take bookings through a widget, a public link or the AI chat, with confirmations and calendar invites.