コンテンツにスキップ

ClickFunnels SDK: 外部ページと外部ファネル

ClickFunnels SDK を使うと、あなた自身がウェブ上のどこかでホストするページを、ネイティブの ClickFunnels ファネルステップのように動作させられます。訪問者はトラッキングされ、フォームは ClickFunnels に送信されて、コンタクトの作成、オプトインイベントの発火、オートメーションのトリガーが行われます。さらに、コンタクトと訪問のアイデンティティはステップをまたいで引き継がれます — ステップが別々のドメインにあってもです。

必要な設定は 3 つのタグと 1 つの属性 だけです。公開されている接点はこれがすべてで、それ以外はすべて SDK の内部で自動的に処理されます。

SDK の用途は、ClickFunnels がホストするファネルに外部ページを 1 枚差し込むことに限られません。この方法で登録されたページは外部ページと呼ばれ、アイデンティティがステップとドメインをまたいで引き継がれるため、外部ページは次の 3 つの形に組み合わせられます。

  • ファネル全体を外部でホストする — すべてのステップ(オプトイン、注文フォーム、ワンタイムオファー、サンキューページ)を、それぞれ独自ドメイン上の外部ページとして登録します。その背後で ClickFunnels がファネルロジック、トラッキング、コンタクト、決済、オートメーションを動かします。

  • 既存サイトにファネル機能を追加する。 新しく何かを作る必要はありません。すでに運用しているマーケティングサイト、アプリ、シングルページアプリに SDK を追加し、既存のフォームを ClickFunnels のオプトインステップやチェックアウトステップに変えられます。

  • 単一の外部ページを 1 ステップとして使う — たとえば、標準的なファネルの途中に、自作のカスタム注文フォームを挟むケースです。

ホスト先は自由です。独自ドメイン、静的ホスティング、Netlify や Vercel のサイト、開発中は http://localhost でも構いません。

外部ページが現在キャプチャできるのは、オプトインとチェックアウトの 2 つです。

  • ページを配置するファネル。 外部ページはファネルステップです。ステップを持つ前にページを登録することもできますが(ステップを持つ前にページを登録するを参照)、ファネルに参加するまではトラッキングしか動作しません。

  • HTML を自分で編集できるページ。 2 つのタグと 1 つの属性を追加できる必要があります。マークアップを編集できないページは、外部ページにはできません。

  • ファネルに設定された決済ゲートウェイ — チェックアウトを追加する場合のみ必要です。Payments AI(Payments AI を通じて接続した Stripe を含む)と既存の Stripe の直接接続の両方に対応しており、ページのマークアップはどちらでも同じです。

AI アシスタントと一緒に構築する

Section titled “AI アシスタントと一緒に構築する”

AI アシスタントに作業を任せることは、この機能の「近道」ではありません — それがこの機能の設計上、想定されている使い方です。

SDK のドキュメントは、AI コーディングエージェント向けに書かれた機械可読なスキルドキュメントとして最初に公開されています。記事末尾のリファレンス資料からリンクしており、本記事はそれらを人間向けに書き下ろしたものです。ページが満たすべき契約 — meta タグ、sdk.js、各フィールドに必要な data-cf-element の値 — は、エージェントが読み取ってマークアップに適用できるように書かれています。

ClickFunnels MCP サーバーは、この設計のもう半分を担います。Claude や ChatGPT などのアシスタントをワークスペースに接続し、同じ外部ページガイドとチェックアウトガイドを提供します。これによりアシスタントは、マークアップを推測したりウェブ上のドキュメントをかき集めたりするのではなく、実際の契約に基づいて作業できます — しかも、ワークスペースについて読むだけでなく、ワークスペースに対して操作を実行できます。

コネクタを接続すると、アシスタントに次のような依頼ができます。

  • 外部ページの登録。 ホスト予定の URL からページを作成し、ステップとしてファネルに接続し、既存ステップの後ろに配置し、マークアップに貼り付けるトークンを報告してもらう — 完全な外部ファネルなら、これを各ステップぶん繰り返します。

  • ページマークアップの共同執筆。 オプトインフォーム、チェックアウトフォーム、ワンタイムオファーを、当て推量ではなく実際の要素契約に基づいて構築します。

  • チェックアウトに必要な設定。 商品と価格を作成し、メイン商品またはオーダーバンプとしてステップに接続します。

  • ファネル構造の読み取り。 ステップ、順序、種類、ページを含む完全なステップツリーを確認し、各外部ページがどこに配置され、訪問者が次にどこへ進むかを確かめられます。

  • ページの成果レポート。 トラフィックが流れ始めたら、ファネルとページの統計を確認できます。

アシスタント自身がウェブページを構築・ホストできる場合 — ネイティブに、あるいはデプロイを行う別の接続済みインテグレーションを通じて — さらに進んで、ページを自分で構築して外部ページとして登録することを提案できます。その際、ClickFunnels ホスト型と外部ホスト型のどちらのホスティングモデルを使うつもりかを伝え、何かを作成する前にあなたに確認します。

ここから先は、その同じ契約を人が読めるように文書化したものです。手作業でページを構築するときも、アシスタントが構築したものをレビュー・デバッグ・拡張するときも、この内容に従ってください。

ページを外部ページにする要素は 3 つです。

  1. 登録。 ページの公開 URL を ClickFunnels に伝えます。ClickFunnels がそのページのページトークンを発行します。

  2. ページ上の 2 つのタグ。 meta タグに入れたトークンと、SDK スクリプトです。

  3. フィールドに付ける 1 つの属性。 data-cf-element が、各フィールドが何であるかを SDK に伝えます。

SDK は登録済みのページでしか動作しません — トークンを発行するのが登録だからです。SDK がページを識別するのは URL ではなくトークンです。ただし URL にも意味があります。SDK は、ブラウザが実際に開いている URL を登録済みの URL と照合するため、誤ったページに貼り付けられたトークンは拒否されます。

ステップ 1: ClickFunnels でページを登録する

Section titled “ステップ 1: ClickFunnels でページを登録する”

ファネルを開き、ステップを配置したい位置のコネクタで + をクリックし、Add Step(ステップを追加) パネルの Funnel カテゴリから External page(外部ページ) を選びます。パネルには「Use the ClickFunnels SDK to include a page hosted outside ClickFunnels」(ClickFunnels SDK を使って、ClickFunnels の外でホストされるページを組み込む)と説明されています。

ステップ設定を入力します。

フィールド 内容
Name ファネルビルダー内でのステップ名。必須。
External URL ページの公開 URL。http / https のプロトコル選択付き。必須。
Description 自分用のメモ。任意。

保存すると、ステップ設定に Page token セクションが表示され、ページのトークンと Copy ボタン、そしてアカウントの SDK ホストを指す script タグが確認できます。ページに貼り付けるのは、この 2 つのスニペットそのままです。

自分でホストするステップごとに、この手順を繰り返してください。 ClickFunnels ホスト型のファネルに外部ステップを 1 つ入れるのが最も単純なケースですが、ファネルの全ステップをこの方法で登録することもできます — ファネル全体を外部でホストするを参照してください。

External URL は、ページが実際に置かれている場所と、オリジンもパスも一致している必要があります。/oto と /oto.html は別のページです。両者がずれていると、SDK の初期化呼び出しは 403 url_mismatch で失敗します。ページを後から移動した場合は、ステップの External URL を更新してください — トークンは変わらないため、meta タグの貼り直しは不要です。

ステップを持つ前にページを登録する

Section titled “ステップを持つ前にページを登録する”

ページは、ファネルステップを持たないスタンドアロンの状態でも登録できます。トークンは発行され、訪問トラッキングは動作しますが、ページがファネルステップになるまで、フォーム送信とチェックアウトは page_not_in_funnel エラーを返します。これは、後からスプリットテストのバリアントや条件分岐(Conditional split)のブランチに入れるページを準備しておく方法です — それらのスロットが受け取るのはページ ID であり、URL ではありません。

登録用の API もあり、現在はクローズドアルファ段階です。アクセスは Developer Hub からリクエストしてください。ClickFunnels V2 API を Bearer トークンで使用します。

POST /api/v2/workspaces/:workspace_id/pages/external
Authorization: Bearer <access_token>
Content-Type: application/json
{
"page": {
"name": "Upsell",
"external_url": "https://acme.example.com/upsell",
"funnel": { "funnel_id": "<funnel_public_id>" }
}
}
  • 必須フィールドは external_url のみです。

  • funnel ブロックは任意です。funnel.funnel_id はそのファネルへの追加または挿入、funnel.show_page_step_id は既存ステップへのページの差し替えを行います。ブロックを丸ごと省略するとスタンドアロン登録になります。

  • 位置は sort_order(0 始まりのインデックス)か、特定ステップの直後に挿入する funnel.after_show_page_step_id で指定します。両方省略すると末尾に追加されます。funnel.funnel_id なしで位置パラメータを指定すると 422 になります。

レスポンスには、ページに必要なトークンと URL を含む sdk ブロックが入っています。

{ "sdk": { "token": "cfp_aB3xY7zQ9pK2mNvT8r5JcDef", "external_url": "https://acme.example.com/upsell" } }

外部ページには ClickFunnels がホストする URL はありません — アドレスはあなたがホストする external_url なので、current_path は null です。登録後は、PATCH /api/v2/pages/:id で名前・説明・外部 URL・位置を更新でき、DELETE /api/v2/pages/:id でステップから切り離して削除できます。

ステップ 2: トークンと SDK スクリプトを追加する

Section titled “ステップ 2: トークンと SDK スクリプトを追加する”

ClickFunnels が発行したトークンを、<head> 内の meta タグに貼り付けます。

<head>
<meta name="cf-page-token" content="cfp_aB3xY7zQ9pK2mNvT8r5JcDef">
</head>

トークンの形式は cfp_ に 24 文字の英数字が続いたものです。トークンは 1 ページに 1 つ — 同じトークンを 2 つの異なるページに貼り付けないでください。また、SDK がトークンを読み取るのはこの meta タグだけです。URL パラメータ、JavaScript のグローバル変数、script タグの属性に入れても何も起こりません。

次に SDK を読み込みます。SDK はフォームを継続的に発見・拡張し、後から追加されたフォームも扱えるため、ページ内のどこに置いても動作します。

<script src="https://sdk.myclickfunnels.com/sdk.js" defer></script>

ファネルビルダーに表示された script タグをそのまま使ってください — アカウントの SDK ホストを指しています。defer を付けると SDK の実行前にページの解析が完了するため、タグの位置に関係なく競合状態を避けられます。

シングルページアプリと既存サイト

Section titled “シングルページアプリと既存サイト”

これが、すでに運用中のサイトにファネル機能を現実的に追加できる理由です。シングルページアプリでは、sdk.js はアプリ全体で一度だけ読み込みます — SDK はクライアントサイドのルート変更をまたいで、フォームとトークンの変化を監視し続けるため、ルートごとに追加し直す必要はありません。

アプリが別の論理ページに切り替わったら、meta タグの content 属性をそのページのトークンに更新してください。SDK は meta タグを監視しており、新しいトークンに対して再初期化します。

ステップ 3: data-cf-element でフィールドをマークアップする

Section titled “ステップ 3: data-cf-element でフィールドをマークアップする”

SDK が認識するすべての要素は単一の属性で識別され、その値が要素の役割を表します。

<form>
<label for="signup-email">Email <span aria-hidden="true">*</span></label>
<input id="signup-email" name="email" data-cf-element="email" type="email" autocomplete="email" required>
<label for="signup-first-name">First name</label>
<input id="signup-first-name" name="first_name" data-cf-element="first-name" autocomplete="given-name">
<label for="signup-last-name">Last name</label>
<input id="signup-last-name" name="last_name" data-cf-element="last-name" autocomplete="family-name">
<button type="submit">Continue</button>
</form>

値はフラットなケバブケースの識別子です — フローのプレフィックスはありません。フィールドは、どのファネルに属していてもフィールドだからです。ラベル・スタイリング・アクセシビリティのために、name と id 属性は自分の流儀のまま維持してください。SDK はフィールドのアイデンティティを name や id ではなく data-cf-element から読み取り、送信ペイロードを自身で構築します。

フォームレベルのマーカーはありません。 マークするのはフォームではなくフィールドです — SDK は、マークされた要素からフォームまで DOM をさかのぼって見つけます。

値 タグ 用途
email <input> メールアドレスの取得
first-name <input> 名
last-name <input> 姓
name <input> 1 フィールドでの氏名(姓・名の代わり)
phone <input> 電話番号
custom-attribute:<key> <input>、<select>、<textarea> その他のコンタクト属性 — <key> は自由

固定フィールド以外はすべてカスタムコンタクト属性です。コロンに続けて任意のキーを指定します。キーはそのまま渡され、初めて登場した時点でワークスペースに属性が自動作成されるため、フィールドを事前に作っておく必要はありません。コロンを含む要素値はこれだけです。

<select data-cf-element="custom-attribute:business_type">...</select>
<input data-cf-element="custom-attribute:monthly_revenue">

オプトインには特別な送信要素は不要です — SDK がフォームの送信を乗っ取るため、プレーンな <button type="submit"> で動作します。ページから販売する場合は要素が増えます。チェックアウトを追加するを参照してください。

送信するフィールドは、必ず <form> の中に置いてください — フォームが送信のスコープです。

  • フォーム単位のスコープ。 data-cf-element を含む各 <form> は、それぞれ独立した送信になります。そのようなフォームが 2 つあれば送信も 2 つで、SDK がフォームをまたいで値をマージすることはありません。

  • 送信は SDK が所有します。 送信時にデフォルト動作を止め、ペイロードを自身で構築し、自身のエンドポイントに POST し、次のファネルステップへのリダイレクトに従います。action や method を設定する必要はありません。この POST は fetch ではなく本物のフォームナビゲーションです — だからこそリダイレクトに従えるのですが、そのため Content Security Policy を配信しているページでは、form-action ディレクティブに SDK ホストを含める必要があります。

  • SDK フォームに SDK 以外のフィールドを混ぜないでください。 data-cf-element を 1 つでも含むフォームは SDK の所有物です。別のエンドポイントに送るフィールドは、別のフォームに置いてください。隠し UTM フィールドも不要です — 訪問トラッキングが、ランディングページの URL から UTM をすでに取得しています。

誤ったタグに付いた要素、<form> の外に置かれた送信フィールド、SDK フォーム内にある SDK 以外の <input>・<select>・<textarea> は、SDK がコンソールに警告を出して無視します。コンソールを開いてください — 何をなぜスキップしたのか、正確に教えてくれます。

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Join the list</title>
<!-- 1. ClickFunnels のページトークン -->
<meta name="cf-page-token" content="cfp_aB3xY7zQ9pK2mNvT8r5JcDef">
</head>
<body>
<form>
<!-- 3. SDK が操作するフィールド。ラベル用に自分の id/name は維持する。 -->
<label for="first">First name <span aria-hidden="true">*</span></label>
<input id="first" name="first_name" type="text" data-cf-element="first-name" autocomplete="given-name" required />
<label for="email">Email <span aria-hidden="true">*</span></label>
<input id="email" name="email" type="email" data-cf-element="email" autocomplete="email" required />
<label for="biz">Business type</label>
<select id="biz" name="business_type" data-cf-element="custom-attribute:business_type">
<option value="agency">Agency</option>
<option value="ecom">E-commerce</option>
</select>
<button type="submit">Continue</button>
</form>
<!-- 2. SDK -->
<script src="https://sdk.myclickfunnels.com/sdk.js" defer></script>
</body>
</html>

ファネル全体を外部でホストする

Section titled “ファネル全体を外部でホストする”

SDK には、外部ステップを 1 つに制限する仕組みはありません。各ステップをそれぞれ外部ページとして登録すれば、ファネル全体をあなたのインフラ上に置き、その背後で ClickFunnels にファネルロジックを動かさせることができます。コンタクトと訪問のアイデンティティは SDK がステップ間で引き継ぐため、ページからページへの移動は、ドメインが異なっていても維持されます。

完全な外部ファネルは、他のファネルと同じように振る舞います。ステップの並べ替え、スプリットテスト、分岐が可能で、ファネルの統計、コンタクト、注文、オートメーションもすべて通常どおり動作します。

スプリットテストや条件分岐は、訪問者がどのページを見るかを決めます。その決定がファネルの最初のステップである場合、まだ着地するページが存在しません — 訪問者が特定のページに到達する前に選択を行う必要があります。ClickFunnels はこの決定をファネル自身の URL(ファネルビルダーの Start of Funnel URL)で行い、選ばれたステップの外部 URL へ訪問者を 302 リダイレクトします。

したがって、入口ステップがスプリットである場合、宣伝する入口はバリアントの URL ではなく、ファネル URL でなければなりません。バリアントページを直接開いた訪問者は決定をバイパスし、単にそのバリアントを見るだけです — ページの読み込みで実行されるのはそのページ自身の訪問トラッキングのみで、スプリット自体はファネル URL で評価されます。これは両方の種類のスプリットに当てはまります。スプリットテストは重みに応じてバリアントを割り当て、条件分岐は保存されたフィルタを評価して一致/不一致のブランチにルーティングします。

選択は訪問者ごとに固定され、訪問者の Cookie で維持されます。リロードや再訪問でも同じ人は同じバリアントに送られるため、スプリットテストがリロードのたびに A と B の間で切り替わることはなく、条件分岐は、すでに条件を満たしたコンタクトを同じブランチにルーティングし続けます。

独自ドメインで入口をブランディングする

Section titled “独自ドメインで入口をブランディングする”

訪問者にはあなたのドメインから入ってほしいのが普通です。しかし、ホスト名の解決先は 1 か所だけです — あなたの外部ホストか ClickFunnels のどちらかであり、両方にはできません。そしてファネル URL は ClickFunnels が配信する必要があります。この 2 つはリダイレクトでつなぎます。ファネル URL は ClickFunnels 配信のホストに置いたまま、あなたのドメインのルートをそこに向けます。

  1. 各入口バリアントを、ルート以外のパスで登録します。 ルート / はリダイレクトになるため、ページを兼ねることはできません — たとえばバリアント A を https://www.example.com/a.html、バリアント B を https://www.example.com/b.html に置き、これらをスプリットの 2 つのブランチとして設定します。

  2. ファネル URL を ClickFunnels のホストに置きます。 ファネルの公開 URL をそのまま使うか、独自のサブドメインを ClickFunnels に追加し、Start-of-Funnel ステップの Set Domain で割り当てます。

  3. ルートをファネル URL にリダイレクトします — 対象は正確にルートパスのみです。Netlify なら netlify.toml に次のように書きます。

[[redirects]]
from = "/"
to = "https://hostedonclickfunnels.example.com/your-funnel"
status = 302
force = true

リダイレクトするのは / だけです — バリアントのパスをリダイレクトするとループになります(ルート → ファネル → バリアント → ルート → …)。これで訪問者は https://www.example.com/ から入り、2 回のリダイレクトでループなくバリアントに到達します。

外部ファネルを新しいドメインへ移行する

Section titled “外部ファネルを新しいドメインへ移行する”

ページを識別するのは URL ではなくトークンなので、何も貼り直すことなく外部ページをオリジン間で移動できます。新しいオリジンの同じパスにページを立ち上げ、ClickFunnels で各ページの外部 URL を更新するだけです。どのページもトークン、ファネルのブランチ、訪問トラッキング、フォーム送信をそのまま維持します。スプリットテストやネストされたブランチを含むファネルでの安全な作業順序については、専用ガイド Migrate an External Page Domain があります。

チェックアウトは、登録済みの外部ページを注文フォームに変えます。SDK がゲートウェイのカード UI をマウントし、カートの金額をサーバー側で計算し(税・割引・配送料)、3-D セキュアのチャレンジを含めて購入者に課金し、ファネルの次のステップへリダイレクトします。

ここまでの内容はすべてそのまま適用されます — ページは、トークンと sdk.js を持つ登録済みの外部ページステップである必要があります。チェックアウトでは、ClickFunnels 側の商品設定と、ページ上のいくつかの要素が追加になります。

  • 販売する商品をステップの商品エリアでステップに接続します。

  • 各バリアントの product_type を実際の商品に合わせて設定します。 physical にするとチェックアウトが配送先住所と配送オプションを収集し、digital は両方をスキップします。未設定の product type は digital と同じ扱いになるため、未設定のまま物理商品を販売すると、配送先住所のない注文が静かに生まれます。

  • オーダーバンプは、ステップの Order bump セクションにドラッグした商品です。バンプとしてマークされた商品だけがバンプとしてページに提供されます — 通常の方法で接続した商品がバンプとして表示されることはありません。

  • ワンタイムオファーは次のステップに接続します。そのステップ自体も、もう 1 つの外部ページです。

  • ファネルには設定済みの決済ゲートウェイが必要です。ない場合、SDK は products(): no checkout configuration for this page をログに出し、決済 UI はマウントされません。

チェックアウトも同じ単一属性の規約を使い、すべてを 1 つの <form> に入れます。

<form aria-labelledby="checkout-heading">
<h2 id="checkout-heading">Checkout</h2>
<div id="checkout-errors" role="alert" aria-live="assertive" aria-atomic="true" hidden></div>
<label for="checkout-email">Email <span aria-hidden="true">*</span></label>
<input id="checkout-email" name="email" data-cf-element="email" type="email" autocomplete="email" required>
<label for="checkout-first-name">First name</label>
<input id="checkout-first-name" name="first_name" data-cf-element="first-name" autocomplete="given-name">
<label for="checkout-last-name">Last name</label>
<input id="checkout-last-name" name="last_name" data-cf-element="last-name" autocomplete="family-name">
<div data-cf-element="shipping"></div>
<div data-cf-element="billing"></div>
<div data-cf-element="payment-method"></div>
<button data-cf-element="pay" type="submit">Pay now</button>
</form>
値 タグ 用途
payment-method <div>(空) マウントポイント — SDK がゲートウェイのカード UI をレンダリング
pay <button> または <input type="submit"> 購入を送信。課金の実行中は無効化される
shipping <div>(空) 配送先住所のマウントポイント(チェックアウトフォームまたは OTO ページ)
billing <div>(空) 請求先住所のマウントポイント
billing-same-as-shipping <input type="checkbox"> 任意。チェックすると配送先を請求先にミラーリング
oto-accept <button> OTO ページ専用 — ワンクリック購入。value="variantId|priceId"
oto-decline <button> OTO ページ専用 — 購入せずに次へ進む

マウントポイントの <div> は、HTML 上では空のままにしてください — 書くのはタグと属性だけで、中身は SDK に任せます。SDK がそこにマウントしたものを読み取ったり、置き換えたり、動作を追加したりしないでください。

今日の商品がデジタルであっても、shipping 要素は常に含めてください。 SDK は、カートとゲートウェイが必要とする間だけ住所フォームを表示するため、非表示の間この要素にコストはありません — そして、後から商品を physical に切り替えても、ページを変更することなくそのまま動きます。省略した場合、その同じ切り替えによってチェックアウトは配送先住所エラーで壊れ、住所を入力する場所がなくなります。data-cf-visible を追加すると、商品タイプに関係なく住所フォームが常時表示されます。配送先住所が不要なデジタル商品でも住所を集めたいマーチャント向けです。

コンタクトのアイデンティティフィールドは、同じセッションでファネルを進んでいる訪問者に対して自動的にプリフィルされます — 前のオプトインステップで入力された内容が、ここでまだ空の入力欄を埋めます。成功時に成功イベントはありません。ブラウザがサーバーのリダイレクトに従って次のステップへ進みます。

マークアップは、SDK がマウントして決済を送信する場所を与えますが、接続されたどの商品・価格を売るのかまでは推測しません。単一商品の注文フォームであれば、その 1 行を選択することが、必要な唯一のカスタム JavaScript です。

<script>
document.addEventListener('DOMContentLoaded', () => {
CfSdk.products().then(({ products }) => {
const variant = products[0]?.variants.find(({ prices }) => prices.length > 0)
const price = variant?.prices[0]
if (price) CfSdk.checkout.setQuantity(variant.id, price.id, 1)
})
})
</script>

商品ピッカー、数量、オーダーバンプ、カスタム合計、クーポン、配送オプション選択がページに必要でない限り、ここで止めてください。

window.CfSdk がサポート対象のホストページ API です — SDK 内部へのアクセスではありません。script タグが実行された時点から呼び出せます(SDK の起動が完了するまで呼び出しはキューに入ります)。defer を使っている場合は、DOMContentLoaded 以降にコードを実行してください。

呼び出し 用途
CfSdk.products() ステップのカタログ — メイン商品とオーダーバンプ
CfSdk.checkout.setQuantity(variantId, priceId, qty) カート行を設定(0 で削除)
CfSdk.checkout.updateState({...}) 部分更新: coupon_codes、shipping_address、billing_address
CfSdk.checkout.setShipping(optionId) 現在の state から配送オプションを選択
CfSdk.checkout.currentState() 現在の state を読み取り
CfSdk.checkout.onStateChange(fn) すべてのコミット済み state で発火 — 描画経路はここに一本化
CfSdk.checkout.onPending(fn) ミューテーションまたは課金が進行中
CfSdk.checkout.onError(fn) エラーを含む state がコミットされた
CfSdk.checkout.onOtoOutcome(fn) OTO ページ専用

正しく押さえておきたい点が 2 つあります。

  • 描画は各ミューテータの戻り値ではなく、onStateChange から行ってください。 onStateChange は SDK 内部の再計算 — たとえば住所変更による配送料の再見積もり — でも発火するため、合計金額が古くならない唯一の場所です。

  • 2 つの金額フォーマットを混同しないでください。 チェックアウト state の金額は整数の最小通貨単位(セント。JPY のようなゼロ小数通貨は ×1)で、描画に使うべき正となる数値です。products() のカタログ価格は amount を小数の文字列で持ち、オファーの表示には使えますが、合計は state から取ります。

空のカートは no_products バリデーションエラーを報告します — これは正常な初期状態であり、購入者に警告を出すようなものではありません。

オーダーバンプは特別な API ではありません。カタログ商品として bumpProducts に提供され、選択は setQuantity をもう 1 行呼ぶだけです。金額はサーバーが計算します。バンプのチェックボックス — およびあなた自身のその他のコントロール — は、SDK フォームの外に置いてください。SDK フォーム内の SDK 以外のフィールドはコンソール警告の対象になり、サーバー側で破棄されます。

カートに配送が必要な場合、SDK は shipping コンテナにゲートウェイの住所 UI を表示し、サーバーが見積もった配送オプションが state に現れます。最安のオプションが自動選択されるため、料金ピッカーの実装は任意です。

OTO ページは、チェックアウトの次のファネルステップです。購入者のカードは直前の購入で保存済みのため、承諾はワンクリックです — メール入力もカード入力も <form> も不要です。OTO ページ自体を外部ページとして登録し、オファーする商品をそのステップに接続し、ページに 2 つのボタンと shipping 要素を置いてください。

  • オファーする商品行は、承諾ボタンの value 属性に variantId|priceId の形で載せます。クリックすると保存済みの決済手段に課金され、次のステップへのリダイレクトに従います。oto-decline は課金せずに先へ進みます。pay と同様、成功はイベントではなくリダイレクトです。

  • OTO 要素は意図的にフォームレスです — 「送信要素には <form> が必要」というルールの例外です。

  • ワンクリックの権利は、訪問者が直前ステップのチェックアウト成功リダイレクト経由で到着した場合にのみ存在します。OTO ページを直接開くと、onOtoOutcome がステータス unavailable で発火します — 必ず注文フォームへのリダイレクトで処理してください。さもないと、直接来訪した訪問者は成立しないオファーの前で立ち往生します。

  • すべての OTO ページには oto-decline か、フォールバックのチェックアウトフォーム(payment-method と pay)が必要です。 どちらもない場合、SDK はコンソールで警告します。保存済みカードへの課金に失敗した購入者に、先へ進む手段がなくなるからです。

  • OTO ページにも必ず shipping 要素を含めてください。 物理的なオファー商品はどこかへ配送する必要があり、購入者の住所が記録にない場合があります — デジタル講座の後に T シャツをアップセルするのが典型例です。SDK は読み込み時にオファーの金額を計算し、最初のチェックアウトで収集しなかった住所が必要な場合にのみ、住所フォームを表示します。

ファネルが公開前の間、決済はテストモードで動作します。ゲートウェイのテストカードを使ってください。テストモードでは、SDK はページ上にデバッグオーバーレイバッジも表示します — 開いてみてください。トークン、初期化ステータス、ページの URL と期待される URL の対比、失敗理由が表示され、「なぜ動かないのか」という疑問の大半に直接答えてくれます。

ファネルは購入者と同じ流れでテストしてください。注文フォームから始め、テストカードで支払い、OTO に着地し、承諾または辞退し、サンキューページに到達する、という流れです。OTO ページにディープリンクしてワンクリックが動くことを期待しないでください。ファネルがテストモードの間は、OTO ページを直接開いても動作します — オファーの金額計算と表示は正常に行われ、承諾/辞退は無効化されます — ので、unavailable のリダイレクト処理は条件を付けずに実装したままにしてください。ファネルが公開されたら、その処理が効いてきます。

ほとんどのホストは CSP をまったく送信せず、その場合は何も設定する必要がありません。しかし、ページが Content-Security-Policy — レスポンスヘッダーまたは <meta http-equiv="Content-Security-Policy"> — 付きで配信されている場合は、3 つのディレクティブで SDK ホストを許可する必要があります。

ディレクティブ SDK が必要とする理由
script-src sdk.js は小さなローダーで、同じホストから本体の SDK バンドルを注入する
connect-src 初期化、送信ノンス、訪問/イベントトラッキングは fetch/XHR 呼び出し
form-action オプトインの送信は fetch ではなくフォームナビゲーション

SDK のすべてのリクエストはこの 1 つのホストに向かうため、各ディレクティブにオリジンを 1 つ書けばすべてカバーできます。

Content-Security-Policy:
script-src 'self' https://sdk.myclickfunnels.com;
connect-src 'self' https://sdk.myclickfunnels.com;
form-action 'self' https://sdk.myclickfunnels.com;

チェックアウトページには追加の許可が必要です。 カードフィールドは決済プロバイダの iframe なので、プロバイダ自身のホスト — ゲートウェイに応じて https://js.stripe.com または https://framepay.payments.ai — を script-src と frame-src に加える必要があり、FramePay はスタイルシートも読み込むため style-src も必要です。各プロバイダが CSP 要件の全容をドキュメント化しています。フィールドが空白でレンダリングされたら、まずそこを確認してください。

ローカルおよびセルフホストでのテスト

Section titled “ローカルおよびセルフホストでのテスト”
  • http://localhost(推奨)。 トラッキングも送信も動作するため、次のステップへのリダイレクトを含むフロー全体をエンドツーエンドで試せます。

  • file://(ディスクから直接開く)。 SDK は読み込まれ、訪問トラッキングは発火しますが、送信は動作しません — file:// の POST が従うべき 302 リダイレクトをブラウザがブロックするためです。スクリプトの読み込みとトラッキングの確認だけに使ってください。

file:// のページは登録もできません。external_url が受け付けるのは http と https だけです。純粋に、ローカルで SDK の読み込みを確かめるための便宜です。

はじめるためのチェックリスト

Section titled “はじめるためのチェックリスト”
  1. ファネルビルダーの Add Step に External page が表示されることを確認する。

  2. 形を決める。ファネル全体を外部にするか、既存サイトにファネル機能を追加するか、外部ステップを 1 つだけにするか。

  3. ファネルを作成または選択し、自分でホストするステップごとに Add Step → External page を実行して、Name と External URL を設定する。

  4. 各ステップの Page token をステップ設定からコピーする。

  5. 各トークンを、そのページの <head> 内の <meta name="cf-page-token"> に貼り付ける。

  6. ステップ設定に表示された sdk.js の script タグを追加する — ページごとに 1 回、シングルページアプリならアプリごとに 1 回。

  7. フォームフィールドに data-cf-element を追加する。

  8. ページを読み込み、ブラウザのコンソールを開く — SDK が初期化し、見つけたフォームを報告することを確認する。

  9. フォームを送信し、ClickFunnels にコンタクトが現れ、訪問者が次のステップに着地することを確認する。

  10. ページから販売する場合は、商品をステップに接続し、チェックアウト要素を追加し、カート行を選択し、テストモードで注文する。

  11. CSP を配信している場合は、SDK ホストを script-src、connect-src、form-action に追加する — チェックアウトページでは決済プロバイダのホストも。

AI アシスタントと作業する場合は、先に ClickFunnels MCP サーバーを接続してください。ステップ 3〜4 の実行、ステップ 5〜7 のマークアップ作成の支援、ステップ 10 の商品接続をアシスタントが担えます。ページを読み込んでテスト注文を行うのは、引き続きあなたがブラウザで行う作業です。

SDK は、AI コーディングエージェント向けの機械可読な「スキル」ドキュメントを公開しています。本記事はそれらから派生したものであり、マークアップと API の詳細については、スキルドキュメントが常に最新の一次情報です。

フォームは送信されるのに、ページが止まったままです。何が悪いのでしょうか?

これはほぼ確実に、Content Security Policy の form-action ディレクティブに SDK ホストが含まれていないケースです。

SDK の送信は fetch ではなく本物のフォームナビゲーションです — だからこそ次のファネルステップへのリダイレクトに従えます。ページが SDK ホストを含まない CSP 付きで配信されていると、ブラウザがそのナビゲーションをブロックし、見た目には何も起こりません。コンソールにはディレクティブ名が表示されます: “Sending form data to … violates the following Content Security Policy directive: form-action”。

これが分かりにくい失敗である理由は、それ以外がすべて正常に見えるからです。SDK は初期化成功をログに出し、見つけたフォームを報告し、Cookie を設定し、各フォームの action を書き換えます。ブロックされるのは最後のナビゲーションだけです。

このディレクティブは、あなたが書いたものではなくホスティングプラットフォームが注入していることも多いため、実際に配信されている内容を確認してください: curl -sSI https://your-page.example.com | grep -i content-security-policy

デバッグ中に知っておきたい関連事項がひとつ: SDK の二重送信ガードは最初の送信でラッチされるため、ブロックされた後の 2 回目のクリックは意図的に無視されます。試行のあいだにページをリロードしてください。

SDK に必要なディレクティブは form-action だけでなく、script-src、connect-src、form-action の 3 つすべてであることも忘れないでください。

SDK がまったく何もしていないようです。どこから調べればいいですか?

よくある原因を順に確認してください。

最も多いのは、data-cf-element がどこにもないケースです。SDK がフィールドを発見するのはこの属性だけで、フィールドの name や id だけでは何も起こりません。

次に多いのは、フォームレベルのマーカーを付けているケースです。フォームをオプトインさせる属性は存在しません — マークするのはフォームではなくフィールドです。

続いて、トークンが meta タグに(そして meta タグにだけ)入っていること、sdk.js がアカウントの SDK ホストを指していること、ブラウザの URL が登録した URL と一致していることを確認してください。

ブラウザのコンソールを開いてください。SDK は、何をなぜスキップしたのかを正確に報告します。ファネルがテストモードなら、デバッグオーバーレイバッジも開いてください — トークン、初期化ステータス、ページの URL と期待される URL の対比、失敗理由が表示されます。

初期化が 403 url_mismatch で失敗します。どういう意味ですか?

ブラウザが開いている URL が、そのページに登録された URL と正確に一致していません — パスも含めてです。末尾の拡張子にも意味があります。/oto と /oto.html は別のページです。

登録済みの外部 URL か、訪問者をそこへ送ったリンクの、どちらかを修正してください。

なお、初期化のレスポンスはエラーも含めて約 2 分間ブラウザにキャッシュされます。登録を変更した後はハードリフレッシュしてください。さもないと、直したはずの修正が効いていないように見えることがあります。

ページをディスクから直接、file:// URL で開いてテストできますか?

部分的にだけ可能です。SDK は読み込まれ、訪問トラッキングは発火しますが、送信は動作しません — file:// の POST が従うべき 302 リダイレクトをブラウザがブロックするためです。

代わりに http://localhost を使ってください。トラッキングも送信も動作するため、フロー全体をエンドツーエンドで試せます。

file:// のページは外部ページとして登録することもできません。登録が受け付けるのは http と https の URL だけです。

外部ページを含むファネルを複製・共有できますか?

いいえ。ファネルに外部ページが含まれている間は、複製、共有、リソースパッケージへの同梱がすべてブロックされます。

同じ外部ページを複数のファネルステップで使えますか?

いいえ — 外部ページが所属できるファネルステップは正確に 1 つで、ページごとに固有のトークンが必要です。

外部 URL にはステップを区別する情報がないため、1 つの URL を 2 つのステップで使い回すと、送信がどちらのステップを進めるべきか曖昧になってしまいます。

ページを登録しましたが、送信が page_not_in_funnel を返します。なぜですか?

そのページはスタンドアロンで登録されています — トークンは発行され、訪問トラッキングは動作しますが、ページがまだファネルステップではないため、フォーム送信とチェックアウトには進むべき次のステップがありません。

ファネルに接続するか、スプリットテストのバリアントまたは条件分岐のブランチに配置してください。それらのスロットが受け取るのは URL ではなくページ ID で、スタンドアロン登録はまさにそのためにあります。

チェックアウトページに決済フィールドや住所フィールドが表示されません。何を確認すべきですか?

何もマウントされず、コンソールにこのページのチェックアウトが未設定と表示される場合は、初期化が失敗しているか、このページのステップに商品が接続されていないか、ファネルに決済ゲートウェイがないか、ワークスペースでチェックアウトが有効になっていないかのいずれかです。

住所フィールドだけが表示されない場合、デジタル商品のみのカートではそれが正常です — 住所フィールドはカートに配送が必要なときにだけマウントされます。物理商品を販売しているなら、バリアントの product_type を確認してください。未設定のタイプは digital と同じ扱いになるため、physical である必要があります。

Content Security Policy のあるページでカードフィールドが空白になる場合は、決済プロバイダのホスト — js.stripe.com または framepay.payments.ai — を script-src と frame-src に追加し、FramePay の場合は style-src にも追加してください。

オーダーバンプが表示されません。

その商品は、ほぼ間違いなくバンプではなくメイン商品として接続されています。バンプとしてマークされた商品だけがバンプとしてページに提供されます。

ファネルビルダーでステップの Order bump セクションに商品を移動するか、API でステップのバンプリストに送信してください。すでに接続済みの商品はバンプに昇格されます。

トークン化が、読み取れないコンソールエラーで失敗します。

コンソールのメッセージには根本原因が含まれていません。チェックアウト state のエラーを — onStateChange または onError から — 常にページ上の見える要素に描画してください。本当の理由はそちらに現れます。

よくある原因: 決済プロセッサがカードのトークン化に購入者の姓と名を要求するため、email しか収集していないフォームは、first-name と last-name のフィールドを追加するまで失敗します。

ドキュメントにあるもの以外に、使える SDK 属性はありますか?

いいえ。ページ作成者向けの属性は、正確に data-cf-element と data-cf-visible の 2 つだけです。

それ以外の属性は何もせず、静かに無視されます — コンソール警告も出ません。