--- name: clickfunnels-sdk-external-page description: > Add the ClickFunnels SDK to a page you host yourself - your own domain, a static host, a Netlify/Vercel site, or even a local file:// - so it tracks visits, captures opt-ins, and carries contact identity across funnel steps. Use this skill when embedding the SDK into an HTML page: the cf-page-token meta tag, the sdk.js script, and the data-cf-element form markup the SDK reads. The page is registered with ClickFunnels - as a funnel step, or standalone (registered for tracking now, placed in a funnel later). version: "1.0" author: ClickFunnels references: - path: https://accounts.myclickfunnels.com/llms.txt description: Product overview + skills index. - path: https://accounts.myclickfunnels.com/.well-known/funnels/skill.md description: Build/modify the funnel that hosts this page (incl. placing standalone external pages in split branches). - path: https://accounts.myclickfunnels.com/.well-known/pages/skill.md description: Author CF-hosted pages programmatically (companion to externally-hosted SDK pages). - path: https://accounts.myclickfunnels.com/.well-known/sdk/add-checkout/skill.md description: Sell products from an external page - checkout, order bumps, one-click one-time offers. --- # ClickFunnels SDK - Create an External Page An **external page** is a page you host anywhere on the web that behaves like a native ClickFunnels funnel step: visitors are tracked, forms submit into CF (contact creation, opt-in events, automations), and contact + visit identity carries across steps - even when steps live on different domains. You wire this up with **three tags and one attribute**. That is the entire public surface. Everything else is internal to the SDK and handled for you. ## When to use this skill - You have an HTML page (own domain, static host, Netlify/Vercel, or localhost for local development, possibly also `file://` with certain limitations, like form submission not working) and want it to act as a ClickFunnels opt-in / capture step. - You're fixing a page where the SDK "isn't doing anything" - usually a missing `data-cf-element`, a stray form-level marker, or `sdk.js` pointing at the wrong host. - You're adding a new field to an SDK-driven form. ## Register the page in ClickFunnels first The SDK only works on a registered page: registration mints the page token. The page can be placed into a funnel at registration time, or registered **standalone** (no funnel step yet) and placed later. Two ways to do it: - **Funnel builder (UI).** Open your funnel, add a step, and pick **External page**. Enter the page's public URL and a name. ClickFunnels then shows the exact two snippets to paste - the token meta tag and the script tag for **your** account's SDK host. - **API.** `POST /api/v2/workspaces/:workspace_id/pages/external` returns the same token and URL in an `sdk` block - see [Create the external page via the API](#create-the-external-page-via-the-api). ## The entire public surface ### 1. Page token - `` Paste the `cfp_...` token ClickFunnels issued for this page into a meta tag in ``: ```html ``` The token (format `cfp_` + 24 alphanumeric characters) is how the SDK identifies the page - not the URL. **One token per page**; never paste the same token onto two different pages. For a single-page app that swaps routes without a full navigation, update the `content` attribute when the active "page" changes - the SDK observes the meta tag and re-initializes against the new token. ### 2. The SDK script Load the SDK once per page. Anywhere on the page works (it discovers and enhances forms continuously, including ones added later): ```html ``` `defer` lets the page finish parsing before the SDK runs, which avoids race conditions no matter where the tag sits. In a single-page app, load it **once** for the whole app - the SDK keeps watching for forms (and for `cf-page-token` changes) across client-side route changes, so you don't re-add it per route. ### 3. Mark elements with `data-cf-element` A single attribute identifies every SDK-aware element. Its value names what the element is: ```html
``` - Values are **flat, kebab-case** identifiers (`email`, `first-name`, `phone`). No flow prefixes - a field is a field regardless of which funnel it's in. - Keep your own `name`/`id` attributes for labels, styling, and accessibility. The SDK reads **identity** from `data-cf-element`, not from `name`/`id`, and builds the submission payload itself. - There is **no** form-level marker. The SDK finds the form by walking up from the `data-cf-element` elements inside it. #### Custom contact attributes - `custom-attribute:` Anything beyond the fixed fields is a custom contact attribute. Use a colon followed by your attribute key (the key is passed through verbatim, and the attribute is created in your workspace on the fly the first time it's seen): ```html ``` This is the only element value that contains a colon. ## Forms and submission Any field that submits **must be inside a `
`** - the form is the submission scope. - **Per-form scoping.** Each `` that contains a `data-cf-element` is an independent submission. Two such forms produce two separate submissions; the SDK never merges values across forms. - **The SDK owns submission.** On submit it prevents the default, builds the submission payload itself, and POSTs to its own endpoint, then follows the redirect to the next funnel step. You don't set an `action` or `method`. That POST is a **real form navigation**, not a `fetch` - which is exactly what lets it follow the redirect to the next step - so a page served with a [Content Security Policy](#content-security-policy) must name the SDK host in `form-action`. - **Don't mix non-SDK fields into an SDK form.** A form containing any `data-cf-element` is SDK-owned; put fields destined for a different endpoint in a separate form. (You don't need hidden UTM fields - visit tracking already captures UTMs from the landing-page URL.) The SDK logs a console warning and ignores: an element on the wrong tag, a submission field placed outside any ``, and a non-SDK `/