Forms live under Design → Forms. Open the Design tab in the rail, pick Forms from the section list at the top of the left panel — its blurb reads Lead-capture forms — and the panel lists every form in your workspace with a search box, an A–Z sort control and a New form button. Selecting a form opens it on the canvas with two tabs: Form Details, which is everything on this page, and Submissions.
Settings → Forms shows the same list read-only, with a publish indicator on each row and an Open form link. It is a good place to notice that something is unpublished; it is not a place to edit. The Settings map explains what each card there is for.
Creating and editing a form needs the form:write privilege — Content Writer and up. Publishing one goes through Deploy, which needs deployment:publish — Content Publisher and up. So a writer can build a form all day and still not be able to make it live; the privilege matrix has the full grid.
Creating a form
New form asks for one thing: a name. Everything else Leed fills in for you, and every one of those defaults is worth knowing because two of them look like features and are not:
- Form Prototype is the slugified name — “Contact Us” becomes
contact-us. - Default Submit Button is
Submit. - Success Callback is
formSubmissionSucceededand Failure Callback isformSubmissionFailed. - Leed Autofill is on.
- The form has no fields at all. A new form renders as a heading and a button until you add some.
The two callback names are conventions, not implementations. Leed writes them onto the rendered <form> element and calls window["formSubmissionSucceeded"] if such a function exists on your site — and if it does not, nothing happens and nothing breaks. They are hooks waiting for you, not behavior you have been given.
Form settings
The top of Form Details is the whole configuration surface. Every field autosaves as you leave it, and each save raises the toast “Form Saved. Publish to use.” — which is the CMS telling you, every single time, that saving and publishing are two different things.
| Setting | Stored field | What it does | Where it shows up on the site |
|---|---|---|---|
| Form Name | formName | Names the form in the CMS, in pickers and in the Insert Form dialog | Not rendered — but it is the subject line of the internal notification email |
| Form Prototype | formPrototype | A slug that groups forms sharing one visual design | As a class on the wrapper (<prototype>-container) and on the <form> element (<prototype>) |
| Description | description | Internal note, 400 characters maximum | Never — this one is genuinely CMS-only |
| Welcome Back Message | welcomeBackMessage | Greeting shown to a visitor Leed recognizes; supports {{fieldname}} placeholders | A hidden <div> above the fields, revealed by Leed Autofill when the visitor is known |
| Heading | heading | Headline above the form | A <div> above the fields, only rendered when set |
| Details | details | Supporting copy under the heading | A second <div> above the fields |
| Default Submit Button | button | The submit button’s resting label | The button’s label — the other five states (Sending …, Email Sent!, Try again!, Thank you!, Downloading …) are fixed |
| Success Callback | successCallback | Names a global JS function called after a successful submission | A data-successcallback attribute on the <form>; invoked as window[name](formId) |
| Failure Callback | failureCallback | Names a global JS function called after a failed submission | A data-failurecallback attribute, invoked the same way |
| Leed Autofill | leedAutofill | Lets Leed pre-fill or hide fields for a recognized visitor | Emits an autofill script call and the welcome-back element |
| Autofill | autofill | Third-party autofill provider: None or LinkedIn | With LinkedIn, loads LinkedIn’s autofill.js and an IN/Form2 mapping script |
Every field you add below these settings is documented option by option in the form field reference — and Leed Autofill deserves particular attention before you turn it off, because it decides how much of the form a returning visitor even sees.
Form prototype
The prototype is a styling hook and nothing else. It is slugified on every write, so typing Landing Page stores landing-page, and it lands on two elements: the container div gets landing-page-container and the <form> itself gets landing-page. The Form Prototype control is a combobox over the prototypes already in use, so picking an existing one is a deliberate act of joining a group.
Success and failure callbacks
Both settings name a global function on your published site — Leed looks up window[name] and, if it finds a function, calls it with the formId as its only argument. If it finds nothing, the submission is unaffected.
The success callback runs after a 200. The failure callback runs on a failed challenge response and on any other non-2xx answer from the capture endpoint. Two cases deliberately run neither: a submission on a preview site, where the endpoint answers 204, shows the “Thank you!” state and returns before either callback is considered. If you are testing a callback and nothing fires, check which site you are on — preview and live behave differently here.
Notification settings
The Notification Settings block is subtitled “Overridable when implemented on pages associated with a campaign.” Read that as: what you set here is the form’s default, and a campaign can override it per page.
External Notifications email the person who filled the form. You choose an Email Template — one of your email layouts — and optionally an Asset, a document from your library delivered with it. That pairing is how you gate content, and its rules are sharp enough to need their own page: a recognized visitor gets the file immediately rather than by email, and the email itself is sent to any given contact once per form and never again.
Internal Notifications email your team. It is a plain list of email addresses — type one and press Enter, or use Save; each is validated as an email address and a bad one shows Not a valid email rather than being accepted. Added addresses appear as chips you can remove individually. The mail they receive comes from forms-noreply@leed.ai under the name “Leed Forms”, with the subject Leed (<site title>) - Form Submission - <form name>, a table of the submitted values, and a button linking back to /forms/<formId>/submissions in the CMS — which is the Submissions tab, the same view you would open by hand.
Because the subject line carries the form name, distinct names across your forms are worth the trouble: they are what makes an inbox rule possible.
Publishing a form
Editing a form marks it dirty. The form canvas header then shows an amber Unpublished changes chip next to the form’s name; clicking it takes you to Deploy.
flowchart LR
A["Edit a setting or field"] --> B["Autosave — form marked dirty"]
B --> C["Deploy shows a pending item<br/>Form: <name>"]
C --> D["Deployment with reason Forms<br/>targets the live branch"]
D --> E["Definition merged into<br/>src/_data/leedForms.json"]
E --> F["Next site build renders it"]
F --> G["Live on your site"]
In Deploy the pending item reads Form: <name>, the deployment’s reason badge reads Forms, and it targets the live branch. Publishing writes only the forms you selected into src/_data/leedForms.json, merged one entry per formId, and clears their dirty flag — other people’s unpublished form edits stay unpublished. Publishing changes covers the deployment flow itself, and deployment history and status is where you confirm the build finished.
There is no per-form publish button anywhere in the CMS, and no way to publish a form over MCP. An AI client can create and update forms for you, but a person has to publish them through Deploy.
For a form you intend to place in a template you maintain, the Form Details tab offers a copyable snippet just below the Autofill controls:
{{> leed/form formId="a1b2c3d4" }}That pastes into any template. The form partial documents everything it accepts and emits, and placing a form on a page compares this against the two ways that need no template work at all.
When Leed will not delete a form
The Delete Form button sits at the bottom of Form Details, and it refuses in three distinct ways.
| Condition | Response | What the CMS shows | How to clear it |
|---|---|---|---|
| Protected system form | 403 form_protected | Button disabled, with Protected — this form cannot be removed. | You cannot; these are owned by a Leed service |
| Referenced by a page’s Form setting | 409 form_in_use | Button disabled, with Used in N pages — remove from pages first. | Clear the Form setting on each of those pages |
| Has one or more submissions | 409 form_has_responses | Nothing up front — the button is enabled, the dialog offers Delete, and the request fails with an error toast | No self-serve fix today |
What “used by a page” actually counts
The in-use check looks at exactly one thing: the page’s Form setting, on either its latest or its published revision. Nothing else counts.
That means a form embedded in page body content with the editor’s Form block, and a form called from a template with {{> leed/form formId="…" }}, are both invisible to this check. Deleting such a form succeeds — and then the template renders nothing where the form used to be. Before you delete, search your templates as well as trusting the counter.
Deleted and protected forms
A deleted form is hidden, not gone. It disappears from the forms list and from every picker, but its URL still works, and opening it shows a red banner: “This form has been deleted. It’s hidden from the forms list and pickers. Submissions are still queryable. Contact an admin if you need to restore it.” Its submissions remain readable. There is no restore button in the CMS — restoring is an API-only operation (POST /api/forms/:formId/restore), which is what that last sentence in the banner is pointing you at.
Once a form is live and taking submissions, the interesting question stops being how it is built and starts being who is filling it in — that is the Submissions tab and, one step further out, the lead profile each submission attaches itself to.