---
title: "Create Custom HTML Page"
method: POST
path: "/workspaces/{workspace_id}/pages/custom_html"
tags: ["Page"]
---

# Create Custom HTML Page

`POST /workspaces/{workspace_id}/pages/custom_html`

## Request

- Base URL: `https://myworkspace.myclickfunnels.com/api/v2`
- URL: `https://myworkspace.myclickfunnels.com/api/v2/workspaces/{workspace_id}/pages/custom_html`
- Auth: one of:
  - HTTP bearer
  - OAuth 2
    - Authorization code: `https://accounts.myclickfunnels.com/oauth/authorize`
    - Token URL: `https://accounts.myclickfunnels.com/oauth/token`
- TypeScript: <https://skmtc.dev/myclickfunnels/apis/clickfunnels-api/docs/workspaces/:workspace_id/pages/custom_html/post.ts> (a function with typed inputs; its response is checked against the schema)

Create a ClickFunnels-hosted custom HTML page: you supply the complete HTML document and ClickFunnels serves it verbatim at a path on the funnel's domain — no page editor, no PML. Ideal for AI-generated pages: generate the HTML, POST it here, and the page is live as a funnel step.

**Required fields:**
- `name`: The page name.
- `custom_html`: The complete HTML document (max 2MB). Served byte-for-byte — include your own CSS/JS inline.

**Making the page work with ClickFunnels (opt-ins, tracking):** the response includes an `sdk` block with a `token` (prefixed `cfp_`). The HTML should contain:
1. `<meta name="cf-page-token" content="<token>">` inside `<head>`.
2. The ClickFunnels SDK script tag (`<script src="https://sdk.<root-domain>/sdk.js" defer></script>`).
3. Form fields marked with `data-cf-element` attributes (`email`, `first-name`, `last-name`, `name`, `phone`, `custom-attribute:<key>`) inside a plain `<form>`.

Since the token is minted by this call, either create the page first and then PATCH the HTML with the token inserted, or create with placeholder HTML and swap it. The [Create an External Page Skill](https://accounts.myclickfunnels.com/.well-known/sdk/create-external-page/skill.md) documents the exact form markup convention (it applies to custom HTML pages too — the only difference is ClickFunnels hosts the file).

**Standalone vs in a funnel:** the `funnel` block is optional and behaves exactly like `POST /pages/external`:
- Omit it to create a **standalone** page.
- `funnel.funnel_id` + optional `sort_order` or `funnel.after_show_page_step_id`: create as a step in the funnel.
- `funnel.show_page_step_id`: swap an existing step onto this page.

**Path:** pass `current_path` (e.g. `/my-landing`) to choose the URL path on the funnel's domain; omitted, the path derives from the name. Change it later via `PATCH /pages/{id}` with `current_path`.

**Limitation:** viewing a custom HTML page does not advance the visitor's funnel run (form submissions do), so run-based view attribution treats un-submitted custom HTML steps as unvisited.

**Rejected fields** (returns 422 if present): `markup`, `theme_id`, `seo_title`, `seo_description`, `seo_index`, `block_ai_crawler`, `block_ai_search`, `block_ai_assistant`, `head_code`, `footer_code`, `custom_css`, `live_data_changes`, `external_url`, `show_page_step.product_ids`, `show_page_step.bumps`. To list the page in the domain's `sitemap.xml` or opt it out of AI crawlers, set `seo_index` or the `block_ai_*` flags afterwards via `PATCH /pages/{id}`.

## Path parameters

- `workspace_id` string, required

## Request body

- object
  - `page` object, required
    - `name` string, required: Page name.
    - `description` string, nullable: A short description of the page (max 1000 characters).
    - `custom_html` string, required: The complete HTML document to serve (max 2MB).
    - `current_path` string: Optional URL path on the funnel's domain (e.g. `/my-landing`). Derived from the name when omitted.
    - `sort_order` integer: Optional zero-based position within the funnel when using `funnel.funnel_id` (422 without it). `0` inserts before the first step; omit to append. Ignored when `funnel.show_page_step_id` is used.
    - `funnel` object: Optional funnel association — provide either `funnel_id` or `show_page_step_id`. Omit the whole block to create a standalone page.
      - `funnel_id` union: The ID (or public ID) of the funnel to attach this page to.
        - integer
        - string
      - `after_show_page_step_id` union: With `funnel_id`: insert the new step right after this show page step. Alternative to `sort_order`.
        - integer
        - string
      - `show_page_step_id` union: The ID of an existing show page step to swap this page onto.
        - integer
        - string

## Response `201`

Created

- PageAttributes: Pages
  - `id` integer: Page ID
  - `public_id` string: The public identifier of the page
  - `workspace_id` integer: Workspace ID that the page belongs to.
  - `name` string: Page name of the editable page element. Want to filter by it? See our <i><u><a href="https://developers.myclickfunnels.com/docs/filtering-by-page" target="_blank">help guide on that topic</a><u><i>.
  - `description` string, nullable: A short description of the page (max 1000 characters).
  - `theme_id` integer, nullable: Theme
  - `created_at` string, date-time: Created at
  - `seo_title` string, nullable: Custom title for search engine results. Falls back to the site's SEO title if not set.
  - `seo_description` string, nullable: Custom meta description for search engine results. Falls back to the site's SEO description if not set.
  - `seo_index` boolean: Whether search engines should index this page. Defaults to false.
  - `block_ai_crawler` boolean: Whether AI training crawlers (GPTBot, ClaudeBot, Google-Extended, Applebot-Extended, CCBot and others) are blocked from using this page's content to train models. Defaults to false. Enforced by a `Disallow` rule in the domain's robots.txt — see [Crawler access (AI opt-out)](https://accounts.myclickfunnels.com/.well-known/pages/skill.md#crawler-access-ai-opt-out) in the [Pages Skill](https://accounts.myclickfunnels.com/.well-known/pages/skill.md) for coverage limits. Independent of `seo_index` and of the other two AI flags.
  - `block_ai_search` boolean: Whether AI answer engines (OAI-SearchBot, Claude-SearchBot, PerplexityBot and others) are blocked from indexing this page and citing it when people ask questions. Defaults to false. Enforced via robots.txt. See [Crawler access (AI opt-out)](https://accounts.myclickfunnels.com/.well-known/pages/skill.md#crawler-access-ai-opt-out) in the [Pages Skill](https://accounts.myclickfunnels.com/.well-known/pages/skill.md) for coverage limits.
  - `block_ai_assistant` boolean: Whether AI assistants (ChatGPT-User, Claude-User, Perplexity-User and others) are blocked from opening this page when a person asks them to — for example pasting the page's link into a chat. Defaults to false. Enforced via robots.txt. See [Crawler access (AI opt-out)](https://accounts.myclickfunnels.com/.well-known/pages/skill.md#crawler-access-ai-opt-out) in the [Pages Skill](https://accounts.myclickfunnels.com/.well-known/pages/skill.md) for coverage limits.
  - `updated_at` string, date-time: Updated at
  - `type` 'landing_page' | 'site_page' | 'funnel_page' | 'theme_page' | 'null', nullable: The type of page. Can be one of `landing_page`, `site_page`, `funnel_page` or `theme_page`. May be `null` for a page that matches none of these categories.
  - `custom_html_page` boolean: Whether this is a ClickFunnels-hosted custom HTML page — a page whose entire body is author-supplied HTML served verbatim (no page editor, no PML). Read the HTML via `GET /pages/{id}/custom_html` and replace it by sending the write-only `custom_html` string on `PATCH /pages/{id}`. Create these via `POST /workspaces/{workspace_id}/pages/custom_html`. (Deliberately named differently from the `custom_html` request field so echoing a GET payload back into PATCH never collides.)
  - `current_path` string, nullable: The current URL path for the page resource. This is the canonical URL for landing and site pages. `null` for externally-hosted (SDK) pages. **For funnel pages this is NOT the customer-facing URL** — that's `url` (or `show_page_step.current_path` for the path component). The page-level current_path on a funnel page does not route the visitor through the workflow step, so a `<checkout/>` element on it will not see the step's attached products. See [Setting a page's path (`current_path`)](https://accounts.myclickfunnels.com/.well-known/pages/skill.md#setting-a-pages-path-current_path) in the [Pages Skill](https://accounts.myclickfunnels.com/.well-known/pages/skill.md).
  - `url` string, nullable: The customer-facing URL for the page on the live site. For funnel pages this routes through the workflow step (which feeds `<checkout/>` its IncludedProducts). Always prefer this over `current_path` when constructing links.
  - `show_page_step` object, nullable: The show page step (the workflow step's _function_) associated with this page. The `products` array is the live list of products attached to this step's checkout — main products and order bumps alike, each flagged by its own `bump` field; it is mutated via `show_page_step.product_ids` / `show_page_step.bumps` on page create/update (additive) and `DELETE /api/v2/pages/:id/products` (removal).
    - `id` integer: The ID of the show page step (function). Pass this as `show_page_step_id` when positioning split-test or conditional-split steps via `/api/v2/funnels/:id/split_test_steps` and `/api/v2/funnels/:id/conditional_split_steps`.
    - `public_id` string: The public identifier of the show page step (function). May be passed as `show_page_step_id` alongside other `public_id` references in your code.
    - `name` string: The name of the step in the funnel. This is the page name that is visible in the funnel builder for a show page step.
    - `current_path` string, nullable: The current URL path for the step in the funnel. This is the path component of the live, customer-facing URL for funnel pages. `null` for externally-hosted (SDK) pages.
    - `sort_order` integer: The zero-based position of this step within the funnel. The first step is 0, the second is 1, and so on. Returned in every page response; pass `sort_order` on create/update to control positioning.
    - `products` object[]: Products currently attached to this step's checkout, main products and order bumps together in display order. Mutated via `show_page_step.product_ids` (main) and `show_page_step.bumps` (bumps) on page create/update (additive) and `DELETE /api/v2/pages/:id/products` (removal, either kind).
      - `id` integer: Product ID
      - `public_id` string: Product public ID
      - `name` string: Product name
      - `bump` boolean: Whether this product is attached as an **order bump** (an optional add-on offered next to the main products) rather than a main product. `true` for products served to `<checkout/>` and to the SDK as `bumpProducts`. Set via `show_page_step.bumps` on page create/update.
      - `bump_preheadline` string, nullable: The short line of copy shown above this order bump's offer, when one was set via that bump's `preheadline` in `show_page_step.bumps`. Always `null` for products that are not bumps.
  - `funnel` object, nullable: The funnel associated with this page (if the page is part of a funnel)
    - `id` integer: The unique identifier of the funnel
    - `public_id` string: The public identifier of the funnel
    - `name` string: The name of the funnel
  - `sdk` object, nullable: Present for SDK-enabled pages — externally-hosted (SDK) pages and ClickFunnels-hosted custom HTML pages; `null` for other pages. Contains the SDK integration token, plus the external URL for externally-hosted pages.
    - `token` string: The SDK page token (prefixed `cfp_`). Embed this in the page via a `<meta name="cf-page-token" content="<token>">` tag and include the SDK script tag to register the page with ClickFunnels. See [The entire public surface](https://accounts.myclickfunnels.com/.well-known/sdk/create-external-page/skill.md#the-entire-public-surface) in the [Create an External Page Skill](https://accounts.myclickfunnels.com/.well-known/sdk/create-external-page/skill.md) for the exact tags to paste.
    - `external_url` string: The fully-qualified URL of the externally-hosted page. Only present for externally-hosted pages (absent for custom HTML pages, which live on the funnel's own domain).
  - `head_code` string, nullable: **Expandable** Custom HTML/JavaScript code injected into the page head section. Only returned when expand[]=head_code is provided. Note: expanding may add latency, especially on list endpoints. Use sparingly.
  - `footer_code` string, nullable: **Expandable** Custom HTML/JavaScript code injected before the closing body tag. Only returned when expand[]=footer_code is provided. Note: expanding may add latency, especially on list endpoints. Use sparingly.
  - `custom_css` string, nullable: **Expandable** Raw CSS applied at the page level (no <style> wrapper). Maps to the CSS tab in the page editor's Code Editor panel. Internal pages only. Only returned when expand[]=custom_css is provided. Note: expanding may add latency, especially on list endpoints. Use sparingly.
  - `markup` string: **Expandable** The PML (Page Markup Language) representation of the page's visual tree. Only returned when expand[]=markup is provided. Note: expanding may add latency, especially on list endpoints. This serialization is lossy: elements PML has no equivalent for are omitted with no error and no placeholder, so a round-tripped read is not a safe basis for rewriting a page (see [the approval guardrail](https://accounts.myclickfunnels.com/.well-known/page-markup/skill.md#stop-get-approval-before-overwriting-an-existing-page)). An empty string therefore does not prove the page is empty: a page built entirely from elements PML cannot express also reads back empty. The value `<!-- markup unavailable: page tree could not be serialized -->` means the read failed; treat the page as unreadable and do not write that string back. See the [Page Markup Skill](https://accounts.myclickfunnels.com/.well-known/page-markup/skill.md) for the full DSL reference.

## Other responses

- `400`: Bad Request — a referenced funnel or step id could not be resolved.
- `401`: Unauthorized
- `404`: Not Found
- `422`: Unprocessable Entity

## Changes

- **2026-11-03** `511d88b30aea`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/myclickfunnels/apis/clickfunnels-api/changes/workspaces/:workspace_id/pages/custom_html/post.md)

---

[API](https://skmtc.dev/myclickfunnels/apis/clickfunnels-api.md) - [All operations](https://skmtc.dev/myclickfunnels/apis/clickfunnels-api/llms.txt) - [OpenAPI document](https://skmtc.dev/myclickfunnels/apis/clickfunnels-api/revisions/511d88b30aea?raw)
