ガイド

ガイド4 分で読めます

新しい SaaS のウェイトリストを作る方法

自分のランディングページにローンチ前のウェイトリストを作ります。フォーム、スパム対策、重複、確認メール、ローンチ日のきれいなエクスポートまで扱います。

Preet SutharIkiform 創業者

ランディングページにプレーンな HTML フォームを置き、訪問者のメールアドレスをフォームバックエンドに送信すれば、ローンチ前にサーバーを動かす必要はありません。登録は 1 件ずつ Webhook でローンチ時に使うメールツールに転送し、確認メールはそのツールに送らせます。ローンチ日にはリストを CSV でエクスポートし、メールアドレスで重複を取り除いて、ローンチのメールを 1 通送ります。

ウェイトリストは、SaaS のアイデアを試すいちばん安上がりな方法です。まだ存在しないもののために見知らぬ人がメールアドレスを残してくれるなら、作る理由があり、リリース時に知らせる相手のリストも手に入ります。このガイドでは、データベースもサーバーも使わずに自分のランディングページにウェイトリストを作り、ローンチ日に備えます。

ウェイトリストの仕組み

どのウェイトリストも 3 つの部分でできています。ページ上のフォーム、登録を 1 件ずつ保存する場所、そしてリストにメールを送るツールです。ここではフォームの送信先を Ikiform にします。Ikiform は送信をスパムチェックして保存し、あなたにメールで通知し、署名付き Webhook を送ります。小さなリレーか自動化ツールが、そのアドレスをメールツールに渡します。

ウェイトリストの登録がランディングページからメールツールに届くまでランディングページ自分のフォーム、任意のサイトやビルダーIkiformスパムをチェックして登録を保存ダッシュボード検索、絞り込み、CSVエクスポート通知メール登録者ではなくあなたへ署名付きWebhooksubmission.createdメールツール確認し、ローンチ時に送信送信リレーウェイトリストの登録がランディングページからメールツールに届くまでランディングページ自分のフォーム、任意のサイトやビルダーIkiformスパムをチェックして登録を保存ダッシュボード検索、絞り込み、CSVエクスポート通知メール登録者ではなくあなたへ署名付き Webhooksubmission.createdメールツール確認し、ローンチ時に送信送信リレー
  1. 訪問者がランディングページでメールアドレスを入力すると、フォームが Ikiform のエンドポイントに送信します。
  2. Ikiform はハニーポット、スパムスコア、レート制限をチェックしてから登録を保存します。
  3. 登録はダッシュボードに表示され、そこで検索したり、リストを CSV でエクスポートしたりできます。
  4. Ikiform はあなたとチームにメールを送ります。登録した本人にメールを送ることはありません。
  5. Ikiform が署名付きの submission.created Webhook を送ります。
  6. リレーか自動化ツールがアドレスをメールツールに追加し、メールツールが確認メールと、のちにローンチのメールを送ります。
初日の時点ではメールツールはなくてもかまいません。何週間も集めてから CSV をインポートしてもよく、Webhook を使えば最初から両方を同期できます。

ウェイトリストを運用する 4 つの方法

唯一の正解となるツールはありません。この表では、よくある 4 つの構成を、得意なことと後々のコストで比べます。

方法得意なことトレードオフ
LaunchList や Waitlister などのウェイトリスト SaaS紹介リンク、リーダーボード、順番待ちの順位が最初から使えます。まだサイトがなくてもホストされたページがあります。リストは他社のプロダクトの中にあります。料金を払うかスタイルを変えない限り、ウィジェットは相手の見た目のままです。料金はたいてい登録数に応じて増えます。
Typeform や Tally などのフォームビルダーの埋め込み洗練された 1 問ずつのフローを、コードなしで作れます。ページに馴染まない iframe で、独自のスクリプトを読み込みます。ブランド表示、回答数の上限、Webhook はプランしだいです。
自前のデータベースと API ルートデータと、紹介コードなどのロジックを完全に制御できます。スパム対策、レート制限、通知、エクスポートを自分で作る必要があり、ランディングページが静的でもサーバーが必要です。
Ikiform などのフォームバックエンドどのサイトでも自分の HTML を使えます。スパムフィルタリング、保存、メール通知、署名付き Webhook、CSV エクスポートがサーバーなしで使えます。紹介のリーダーボードはなく、登録した本人へのメールもありません。それらはメールツールが担います。

紹介の仕組みが計画のすべてなら、ウェイトリスト SaaS を選びましょう。フォームをサイトの他の部分と同じ見た目にし、リストを持ち運べるようにして、ローンチ後にどのみち使うツールからメールを送りたいなら、フォームバックエンドを選びましょう。このガイドの残りではフォームバックエンドを使います。

フォーム

最初はメールアドレスだけを尋ねましょう。フィールドが増えるたびに登録は減ります。質問は、すでに登録してくれた人にあとからメールで聞けます。下でスタックを選んでください。選んだスタックは、このガイドのすべてのコードサンプルに引き継がれます。

登録フォームを表示したい場所にこのフォームを貼り付けます。通常の方法で送信してリダイレクトするので、JavaScript がなくても動きます。

index.html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label for="email">Email</label>
  <input id="email" name="email" type="email" placeholder="you@company.com" autocomplete="email" required />
  <input type="hidden" name="ref" value="homepage" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Join the waitlist</button>
</form>

訪問者をページにとどめるには、フォームのあとにこのスクリプトを追加します。fetch で送信し、フォームをお礼の一文に差し替え、サーバーが返すフィールドエラーを表示します。

waitlist.js
const form = document.querySelector('form[action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx"]');
const status = document.createElement("p");
status.setAttribute("role", "alert");
form.append(status);

const labels = {
  email: "Email",
};

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  const button = form.querySelector("button");
  button.disabled = true;
  status.textContent = "";
  try {
    const response = await fetch(form.action, {
      method: "POST",
      headers: { Accept: "application/json" },
      body: new FormData(form),
    });
    const result = await response.json();
    if (result.ok) {
      form.replaceWith("You're on the list. We'll email you at launch.");
      return;
    }
    const fields = Object.entries(result.error.fields ?? {});
    status.textContent = fields.length
      ? fields.map(([id, messages]) => `${labels[id] ?? id}: ${messages[0]}`).join(" ")
      : result.error.message;
  } catch {
    status.textContent = "Couldn't reach the server. Try again.";
  }
  button.disabled = false;
});

HTML でさらに詳しく知るには プレーンな HTML でランディングページにウェイトリストフォームを追加する方法 を読んでください。

  • エンドポイントはフォームの URL です。frm_xxxxxxxxxxxxxxxx をダッシュボードの ID に置き換えてください。
  • name="email" がフィールド ID です。email のままにしてください。Ikiform はこれを通知メールの返信先アドレスに使い、使い捨てメールのドメインかどうかもチェックします。
  • 非表示の _gotcha 入力欄がハニーポットです。人には見えません。すべての入力欄を埋めるボットは自らスパムだと示すことになり、サーバー版はこの値に手を加えずにそのまま渡します。
  • 非表示の ref 入力欄は、登録がどこから来たかを記録します。ページやキャンペーンごとに値を変えてください。
  • Fields タブでフィールドを定義していると、定義にない入力欄は 422 と unknown_field エラーで拒否されます。そのため、下のように email に加えて ref も登録してください。
  • プレーンな HTML の送信は、成功後にブラウザーをリダイレクトします。フォームの設定で Redirect after submit に自分のサンクスページを設定するか、設定しなければ Ikiform のホストされたページが表示されます。
フィールド
{
  "version": 1,
  "title": "Waitlist",
  "fields": [
    { "id": "email", "type": "email", "label": "Email", "required": true, "placeholder": "you@company.com" },
    { "id": "ref", "type": "text", "label": "Source", "validation": { "max_length": 40 } }
  ]
}
サーバー側のバージョンはシークレットキーを付けて送信するので、Ikiform は Allowed domains とその人間確認をスキップします。その場合、すべての送信がサーバーの IP から届くため、訪問者ごとに 1 分あたり 5 件という Standard の制限が全訪問者の合計に適用されてしまいます。サーバー経由で送信するフォームには Relaxed か Custom のレート制限を選び、IKIFORM_SECRET_KEY はサーバー専用の環境変数に置いてください。

サーバー版はブラウザーの Origin ヘッダーを引き継ぎます。Ikiform は無料の localhost テストをこのヘッダーで判別しますが、サーバー自身の fetch はこれを送らないからです。

ドキュメントのフレームワーク別ガイドではさらに詳しく説明しています: Next.js、Astro、SvelteKit、Nuxt、React、Vue、プレーンな HTML。SDK はブラウザーからのリクエストを submit() としてまとめており、ウェイトリストのフォームテンプレートでは 4 フィールド版を生成できます。

尋ねる価値のあるフィールド

必要なフィールドはメールアドレスだけです。ローンチ前にリストを分類したいなら、追加するのは多くても 2 つまでにしましょう。下の 4 フィールド版を試してみてください。Ikiform のバリデーターをブラウザーで実行し、保存される内容か、返ってくる 422 を表示します。

プレビュー

Ikiform のバリデーターをブラウザーで実行します。何も送信されません。

ウェイトリストのテンプレートのフィールドに ref を加えたものです。これは見える入力欄ではなく、ページが非表示の入力欄で埋めます。フォームの Fields タブに貼り付けると、Ikiform がサーバー側でこれを適用します。
フィールド尋ねる理由注意点
emailリストそのもの必須。スマートフォンで適切なキーボードが出るよう type="email" を使います
name個人宛てのローンチメール任意。名だけで十分です
role創業者を先に招待したり、エンジニアには別のメールを書いたりできますselect なら、絞り込みに使いやすいきれいな回答になります
sourceお金をかける前に、どのチャネルが効くかがわかります自由記述は正直ですが散らかります。非表示の ref 入力欄のほうがきれいです。下を参照してください
ref尋ねずにページやキャンペーンごとの登録数を数えられます非表示の入力欄。任意の text で、max_length は 40

尋ねずに登録の流入元を追跡するには、ページごとかキャンペーンごとに非表示の入力欄を追加します。_ で始まる名前は Ikiform への指示で保存されないので、ref のような普通の名前を使ってください:

非表示の入力欄
<input type="hidden" name="ref" value="homepage" />
<input type="hidden" name="_redirect" value="https://example.com/thanks" />

各送信データにはページの Origin とリファラーも記録されるので、ref がなくても登録がどのページから来たかがわかります。_redirect 入力欄はブラウザーを自分のサンクスページに移動させます。これは、フォームの Allowed domains のいずれかにある https: の URL でのみ機能します。

ニュースレターなど、ローンチのお知らせ以外も送るなら、約束する内容をそのままラベルにした必須の同意チェックボックスを追加しましょう。Ikiform は送信データのタイムスタンプとともに true を保存するので、その人がいつ同意したかが記録されます。ニュースレターのテンプレートにはこれが含まれています。

ボットをリストに入れない

メールアドレスの入力欄がある公開フォームには、数日のうちにボットが寄ってきます。偽の登録は数字を水増しするだけでなく、ローンチのメールがスパムトラップに届いて送信者の評価を下げてしまいます。Ikiform は 4 つの防御を重ねており、最初の 2 つは設定なしで有効です。

防御防げるもの設定
ハニーポットすべての入力欄を埋める単純なボット上のフォームにある非表示の _gotcha 入力欄
スパムスコア使い捨てアドレス(+2)、同一内容の繰り返し(+3)、リンク、既知のスパムフレーズ。5 に達した登録はスパムとして保存されます不要
レート制限ループで送信するスクリプトデフォルトで有効: Standard で、訪問者ごとに 1 分あたり 5 件、フォーム全体で 1 分あたり 300 件
人間確認本物のブラウザーを動かすボットSpam check を Ikiform に設定します。キーの管理なしで Cloudflare Turnstile が動きます

スパムは通常の成功の応答で保存されるので、ボットは何も学べません。スパムはメールも Webhook も送らず、使用量にもカウントされません。最初の 1 か月は週に 1 回、ダッシュボードの Spam フィルターを確認しましょう。本物の登録が入っていたらスパムではないとマークすると、その時点で Webhook が送られます。

Allowed domains にも自分のドメインを追加しましょう。他のサイトからの送信はスパムとしてマークされるか、Reject を選んだ場合は 403 origin_not_allowed で拒否されます。人間確認をオンにしても、HTML フォームは変更不要です。fetch 版は、先に @ikiform/sdk の challenge() でトークンを取得する必要があります。その 4 行はスパム対策のドキュメントにあります。

ローンチ時の送信はまとめて押し寄せます。大きな場所でページを紹介すると、個々の訪問者の制限より先に、フォーム全体の 1 分あたり 300 件の制限に引っかかることがあります。前日に Custom のレート制限で引き上げておきましょう。オフィスやカンファレンスでは全員が 1 つの IP を共有するので、そうした日には訪問者ごとに 1 分あたり 20 件の Relaxed が合っています。

同じ人が 2 回登録する

人は別のタブから、2 台目のデバイスから、あるいは 1 週間後に忘れて、もう一度登録します。Ikiform は受け付けた送信をすべて保存し、メールアドレスで重複をまとめたり拒否したりはしません。重複した場合は次のようになります:

ケース結果
同じメールアドレスで、フィールドが異なるか 24 時間以上あと通常の送信データがもう 1 件
24 時間以内に同じフィールドをもう一度スパムスコアに +3。5 未満なので通常の送信データがもう 1 件
使い捨てメールドメインからの同一内容の繰り返し+3 と +2 で 5 に達し、スパムとして保存

そのため、ある程度の重複は想定し、リストの最終的な置き場所で取り除きましょう。手間の少ない順に 3 つの方法があります:

  1. メールツールで。 ほとんどのメールツールは連絡先をアドレスで管理するので、同じアドレスを 2 回追加しても連絡先が 2 件できるのではなく、1 件が更新されます。
  2. エクスポート時に。 どこかにインポートする前に、CSV から重複したメールアドレスを取り除きます。方法を下で 2 つ紹介します。
  3. リレーで。 アドレスを小文字にして前後の空白を除き、一意制約か upsert で挿入すれば、重複しても何も起きません。

ダブルクリックで 2 行できること自体を防ぎたいですか? fetch 版で、ページを読み込むごとに冪等性キーを送ってください。これで誤って繰り返される送信は防げますが、本人が意図して 2 回登録するのは防げません。

登録の確認

Ikiform はフォームに入力した本人にメールを送りません。通知メールはあなたとチームだけに届くので、自動返信、確認メール、ダブルオプトインはありません。それらは Webhook をきっかけにメールツールから送ってください。これは意図的な設計です。ローンチのメールを送るツールが確認メールも送れば、配信停止やバウンスを 1 か所で管理できます。

Ikiform とメールツールをつなぐ方法は 2 つあります:

  • コードなし。 自動化ツールを指す Webhook エンドポイントを追加し、そのツールのメールツール用アクションを使います。Zapier、Make、n8n を参照してください。
  • 小さなリレー。 任意の Node ホスト上のルートハンドラーで署名を検証し、メールツールの API を呼び出します。下の例は Next.js のルートですが、同じコードが Cloudflare Worker でも動きます。
app/api/waitlist/route.ts
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";

export async function POST(request: Request) {
  const payload = await request.text();
  try {
    const event = await verifyWebhook({
      payload,
      headers: request.headers,
      secret: process.env.IKIFORM_WEBHOOK_SECRET!,
    });
    if (event.type !== "submission.created") return new Response(null, { status: 204 });
    const email = String(event.data.fields.email ?? "").trim().toLowerCase();
    if (!email) return new Response(null, { status: 204 });
    const response = await fetch(process.env.LIST_API_URL!, {
      method: "POST",
      headers: {
        authorization: `Bearer ${process.env.LIST_API_KEY}`,
        "content-type": "application/json",
      },
      body: JSON.stringify({
        email,
        name: event.data.fields.name ?? null,
        tags: ["waitlist", String(event.data.fields.role ?? "unknown")],
      }),
    });
    if (response.status === 409) return new Response(null, { status: 204 });
    if (!response.ok) return new Response(`List answered ${response.status}`, { status: 502 });
    return new Response(null, { status: 204 });
  } catch (error) {
    if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
    throw error;
  }
}
  • verifyWebhook が署名とタイムスタンプをチェックするので、他人がこの URL からアドレスを追加することはできません。
  • fetch をメールツールの連絡先追加の呼び出しに置き換えてください。たいていはメールアドレスとリストまたはタグを受け取り、そのツールでダブルオプトインがオンなら独自の確認メールを送ります。
  • 204 を返すと、配信が成功したことを Ikiform に伝えます。502 を返すと Ikiform は同じ webhook-id でリトライするので、メールツールが少しの間停止しても登録は失われません。

ダッシュボードの Webhooks でエンドポイントを追加し、その whsec_ シークレットを IKIFORM_WEBHOOK_SECRET にコピーします。1 つのワークスペースにはエンドポイントを 3 つまで作れます。リトライとペイロードについては Webhook のドキュメントで、各配信を 1 回だけ保存する版については自分のサーバーで説明しています。

ローンチ日

最初の登録からローンチ日までのウェイトリストページ公開フォームが Ikiformに送信毎週共有し、登録を読む1 週間前エクスポート、重複除去、分類ローンチ日リストにメールを 1 通数週間最初の登録からローンチ日までのウェイトリストページ公開フォームが Ikiform に送信毎週共有し、登録を読む1 週間前エクスポート、重複除去、分類ローンチ日リストにメールを 1 通数週間
  1. Ikiform に送信するフォームを載せたページを公開します。
  2. 毎週ページを共有し、新しい登録とその回答を読みます。
  3. ローンチの 1 週間前に、リストを CSV でエクスポートし、重複を取り除いて、役割ごとに分けます。
  4. ローンチ日に、メールツールからリストに 1 通のメールを送ります。
作業のほとんどは 1 週間前のエクスポートです。ローンチのメール自体は短くてかまいません。

リストをエクスポートする

ダッシュボードで Submissions を開き、ウェイトリストのフォームを選んで、ステータスを Received、期間を All time にし、Export CSV を選びます。列は id、created_at、form、status で、そのあとにフィールドごとの列が 1 つずつ続きます。ファイルは BOM 付きの UTF-8 なので、Excel でもアクセント記号などが崩れません。1 回のエクスポートは最大 100,000 行で、それを超える場合は REST API でページングしてください。

Google スプレッドシートで重複を取り除くには、CSV をインポートし、メールアドレスが E 列にある状態で、空のシートに次の数式を入れます:

Google スプレッドシート
=SORTN(A2:F, 9^9, 2, LOWER(TRIM(E2:E)), TRUE)

大文字小文字と空白を無視してアドレスごとに 1 行を残し、メールアドレス順に並べます。あるいは、エクスポートしたファイルのあるフォルダーで Node を使って次を実行します。各アドレスの最初の行を残し、値の中にカンマがないことを前提にしていますが、メールアドレスだけのフォームならその前提は成り立ちます:

dedupe.mjs
import { readFileSync, writeFileSync } from "node:fs";

const [header = "", ...rows] = readFileSync("submissions.csv", "utf8").replace(/^\uFEFF/, "").trim().split(/\r?\n/);
const columns = header.split(",");
const emailAt = columns.indexOf("email");
const seen = new Set();
const unique = rows.filter((row) => {
  const email = row.split(",")[emailAt]?.trim().toLowerCase();
  if (!email || seen.has(email)) return false;
  seen.add(email);
  return true;
});

writeFileSync("waitlist.csv", [header, ...unique].join("\n"));
console.log(`${rows.length} rows, ${unique.length} unique emails`);

ローンチのメールを送る

自分の受信箱からではなく、メールツールから送りましょう。個人の受信箱から何百ものアドレスに一斉送信すると、メールプロバイダーにはスパムに見えます。また、送った瞬間から配信停止の処理が必要になります。短くまとめましょう:

ローンチのメール
Subject: Acme is live. You're in.

Hi Ada,

You joined the Acme waitlist in the spring. Today it's open.

Sign in here: https://acme.example/start

You get the first 30 days free because you waited.
Reply to this email if anything breaks. It comes to me.

Ada, founder of Acme
  • 何が公開されたかを伝え、登録ページかログインページに直接リンクします。
  • 長めのトライアルや割引など、待ってくれたことへの特典を用意し、1 回だけ伝えます。
  • 個人の名前で送ります。返信は、手に入る中で最高の初期フィードバックです。
  • role を集めたなら、創業者とエンジニアで書き出しを変えましょう。それを集める理由はそこにあります。
アドレスを確認せずに何か月も集めていたなら、まず一部にだけ送り、バウンス率を見てください。タイプミスや無効なアドレスだらけのリストは、プロダクトのメールを送るドメインの評価を下げます。

状況に合わせて選ぶ

上の手順はどこでも使えます。以下のページでは、特定のローンチやサイトビルダーで変わる点を扱います。

動作を確認する

ページを共有する前に、これを一通り確認してください。Ikiform に無料プランはありません。テストは無料です。プランのないワークスペースでも、localhost からのテスト送信を月 50 件まで受け付けます。本番運用するには、500 件で月 $6 からの Pro を選ぶか、買い切りにしてください。10,000 件で $49、または 50,000 件で $149 で、クレジットに有効期限はありません。料金を参照してください。

  1. フォームの Allowed domains に、自分のサイトのドメインと、開発中は localhost を追加します。空にしておくと、どこからの送信も受け付けます。
  2. ページでフォームに入力して送信します。サンクスページか、Ikiform のホストされたページに移動します。
  3. ダッシュボードで Submissions を開きます。送ったすべてのフィールドを含む新しい送信データがあります。
  4. 通知の受信者それぞれの受信箱を確認します。各自にフィールドを載せたメールが届き、フォームに email フィールドがあれば Reply は送信者宛てになります。
  5. Webhook を追加した場合は、Webhooks でエンドポイントを開きます。配信は succeeded と表示され、サーバーの応答も確認できます。
  6. ハニーポットに値を入れて _gotcha=bot で送信します。成功の応答は返りますが、送信データはスパムとして保存され、メールも Webhook も送られません。
  7. 何も届かない場合は、ダッシュボードのステータスフィルターを Spam に切り替えてください。スパムにも本物の送信データと同じ成功の応答が返ります。
  8. 同じアドレスで 2 回登録し、メールツールの連絡先が 1 件のままであることを確認します。

トラブルシューティング

問題対処法
403 origin_not_allowed が返るか、すべての送信がスパムになるページのドメインがフォームの Allowed domains に入っていません。ドメインと、ローカルテスト用に localhost を追加してください。登録したドメインのサブドメインも許可されます。
429 rate_limited1 人の訪問者が、フォームのレート制限を超えて送信しました。Standard では訪問者ごとに 1 分あたり 5 件までです。Retry-After まで待つか、共有の 1 つの IP から送信するフォームには Relaxed か Custom を選んでください。
403 challenge_requiredフォームのスパムチェックが Ikiform に設定されていて、fetch の送信にトークンがありませんでした。送信のたびに先に @ikiform/sdk の challenge(formId) でトークンを取得するか、チェックをオフにしてください。
送信データはダッシュボードにあるのに、メールも Webhook も届かないスパムとして保存されています。スパムは何も送りません。Spam フィルターで開いて理由を確認し、スパムではないとマークすると、メールと Webhook が送られます。
ブラウザーが自分のページではなく Ikiform のサンクスページに移動する_redirect は、ドメインが Allowed domains にあり、https: の URL でない限り無視されます。ドメインを追加するか、代わりにフォームの設定で Redirect after submit を設定してください。
同じメールアドレスが 2 回表示される想定どおりの動作です。Ikiform はメールアドレスで重複を除去しません。エクスポート時に重複を取り除くか、リレーやメールツールでメールアドレスをキーに upsert してください。
登録は Ikiform に届くのに、メールツールには届かないWebhooks でエンドポイントを開き、配信ログを読んでください。400 ならシークレットが間違っています。ツールのステータス付きの 502 なら、ツールが連絡先を拒否しています。
402 test_limit_reachedワークスペースにプランがなく、今月の無料の localhost テスト 50 件を使い切っています。プランを選ぶか、ダッシュボードの Send test を使ってください。

よくある質問

手順で詰まったときや、あなたの状況に合うガイドがほしいときはメールでどうぞ: hello@ikiform.com