Documentation
Where the tag goes, by platform
One script tag on your website records page reads, where each visitor came from, and the signup forms it can hear — without cookies and without touching your forms. This says where that tag goes in Webflow, Framer, WordPress, Squarespace, Carrd and Next.js, and what to do where a builder owns its forms too completely to be heard.
Six short notes, written from the founder's side, for Settings → Your website → "Where does this
go?". They are rendered from PLATFORMS in src/lib/setup-prompt.ts — by PlatformNotes on the
screen and by buildSetupPrompt inside the prompt below; this file is where the words are agreed
and reviewed. Change one, change all three.
Each note answers two questions and nothing else: where the script tag goes, and whether the
platform's own forms will take the data-kl-list attribute — because when they will not, the
answer is the "Catch every signup form" switch, and the founder should be told so on the same
screen rather than left to find out from an empty heartbeat.
Nothing here promises a plan a platform does not sell. Where custom code needs a paid plan, the note says so, because a founder on the free tier will otherwise paste the tag into a box that silently drops it.
Webflow
The tag. Site settings → Custom code → Head code, then publish. Custom code needs a paid site plan.
Your forms. Select the Form Block in the Designer, then Settings panel → Custom attributes →
add data-kl-list with your list's name. Webflow's own form submission is untouched; Kicklace
only listens.
Framer
The tag. Site settings → General → Custom code → End of <head>.
Your forms. Framer's form is its own component and will not take an attribute, so turn on Catch every signup form instead.
WordPress
The tag. The theme's header.php, or any "insert headers and scripts" plugin.
Your forms. Contact Form 7 and Gravity Forms let you edit the form's HTML, so add
data-kl-list to the <form> tag there. A theme's own signup form usually does not, so use
Catch every signup form.
Squarespace
The tag. Settings → Advanced → Code injection → Header. Code injection needs a Business plan or higher.
Your forms. Squarespace form blocks do not take attributes, so use Catch every signup form.
Carrd
The tag. Settings → Code → Head, on a Pro plan.
Your forms. Carrd's forms do not take attributes either, so use Catch every signup form.
Next.js, Astro, Hugo, or plain HTML
The tag. In the root layout (app/layout.tsx), either as the tag shown on the screen or as
<Script src="…" strategy="afterInteractive" /> from next/script; in <head> in a plain HTML
template or a static site generator's base layout.
Your forms. Add data-kl-list to your own <form> tag. If a form is submitted on the server
and never reaches the browser, post to /api/v1/events from your route handler with a secret key
instead — that is under "For your developers".
When the builder takes neither
Some builders own the form completely: you cannot add an attribute to it, and its submit never
reaches a listener of ours, so Catch every signup form has nothing to catch either. For those,
Kicklace hosts the form itself at kicklace.com/f/<workspace>/<list>. It wears the workspace's
name, logo and accent, prints the list's consent sentence as the label of a box that has to be
ticked, and stores that sentence with the signup — the same record a post from your own site
makes. The link and the one-line <iframe> are on the list's own page in the app, under "Share a
signup form".
- Carrd. Its forms take no attributes and its own handler is closed, so link a button to the hosted form, or paste the iframe into an Embed element.
- Framer. Its form is a component with no attribute of its own; use an Embed layer with the iframe, or point a button at the link.
- Squarespace. Form blocks take no attributes; a Code block holds the iframe on a Business plan, and a button can carry the link on any plan.
- Webflow, WordPress, and your own HTML. The attribute works here, so the hosted form is only for a page you would rather not touch — a landing page somebody else owns, or a link in a bio.
The form follows each visitor's own light or dark setting, which inside an iframe can leave a dark
form on a light page; ?theme=light or ?theme=dark on the address pins it. It also tells whatever
framed it how tall it is (postMessage, { kicklace: "height", height }) on load and again when it
becomes the thank-you, so a builder that listens can size the frame; the iframe in the app carries a
sensible height for one that does not.
Two things it is not. It is not a replacement for the tag: the tag is what ties a signup to the visit that led to it, and the hosted form knows only what the person typed. And it is not a way around consent — the sentence on the page is the list's own, and a signup without the tick is refused.
Or ask your AI
Working on your site with Claude Code, Cursor, Copilot, or ChatGPT? Copy this and paste it there. It carries everything: the line to add, your lists, the words people agree to, and how to check it worked.
That frame is "2. Let your AI do it" on Settings → Your website, one step above the tag since
#228 — a founder building a product today has an AI open beside their site, and handing it one
prompt is less work than finding the file the tag goes in. The frame also points at Settings →
Claude: a workspace that has connected Claude can be asked to set the website up instead of pasting
anything. The prompt behind the button is built by buildSetupPrompt in src/lib/setup-prompt.ts
at render time from the workspace alone — the tag with its own public key, the addresses the workspace allows, its lists with the
exact sentence each one stores, and whether it catches every signup form or only the marked ones.
Two rules hold it honest, and src/lib/__tests__/setup-prompt.test.ts keeps them:
- Every fact comes from the workspace. Nothing is invented, and a workspace with no site or no list says so and tells the agent what to do instead of the part it cannot do yet.
- Never a secret key. The public key is already in every page that carries the tag; a secret key belongs to a server. The prompt says so, and tells the agent to stop and tell the owner if it finds one in the site's code.
The prompt is prose in seven numbered steps — add the tag, the addresses this workspace allows, signups, forms that are not plain HTML, do not, check it worked, report back — wrapped to 78 columns so it reads wherever it is pasted. It is tool-agnostic: nothing in it names a particular agent or its file layout.