ガイド

ガイド3 分で読めます

自分のサイトで作るイベントの参加登録と出欠フォーム

自分のイベントページで出欠を受け付けます。フォーム、同伴者の上限、正確な人数集計、満員時の締め切り、受付リストまで、チケットアプリなしで。

Preet SutharIkiform 創業者

イベントページに、フォームバックエンドに送信する出欠フォームを置きます。参加・不参加の回答、サーバー側で上限をかけた同伴者数、計画に必要な情報を集めます。人数は、CSV エクスポートか短いスクリプトで、メールアドレスごとの最新の回答から数え、満員になったらフォームを一時停止します。Ikiform はすべての出欠を保存して転送しますが、チケット発行、決済、自動の定員制限はないので、それらが必要ならチケットプラットフォームを使ってください。

ほとんどのイベントにチケットプラットフォームは要りません。ミートアップ、ワークショップ、ローンチパーティー、読書会、無料のカンファレンスに必要なのは 3 つだけです。何がいつあるかを伝えるページ、誰が来るかを集めるフォーム、そして前日に信頼できる人数です。このガイドではそれを自分のサイトに作り、フォームではできない部分もはっきり説明します。

出欠の回答の流れ

フォームはイベントページに置きます。各回答は Ikiform に送信され、Ikiform がフィールドのルールに照らして確認し、スパムを除外し、保存してあなたに知らせます。そこから、計画に使うものへ届きます。ダッシュボード、スプレッドシート、人数を数えるスクリプトなどです。

出欠の回答がイベントページから人数集計に届くまでイベントページ出欠フォームIkiform同伴者の上限を確認、スパムを除外ダッシュボード全回答、CSVエクスポート主催者へのメールReply でゲストに返信署名付きWebhooksubmission.createdシートかスクリプトメールごとの最新の回答RSVP集計出欠の回答がイベントページから人数集計に届くまでイベントページ出欠フォームIkiform同伴者の上限を確認、スパムを除外ダッシュボード全回答、CSV エクスポート主催者へのメールReply でゲストに返信署名付き Webhooksubmission.createdシートかスクリプトメールごとの最新の回答RSVP集計
  1. ゲストがイベントページで出欠フォームに入力すると、Ikiform のエンドポイントに送信されます。
  2. Ikiform は同伴者の上限を含むフィールドを確認し、保存する前に送信のスパムスコアを付けます。
  3. すべての回答がダッシュボードに表示され、絞り込みや CSV でのエクスポートができます。
  4. 主催者には回答ごとにメールが届き、Reply でそのままゲストに返信できます。
  5. Ikiform が署名付きの submission.created Webhook を送ります。
  6. スプレッドシートや短いスクリプトが、各人の最新の回答を使って出欠を人数に集計します。
Ikiform は回答を保存します。人数はその回答に対して自分で計算する合計です。チケットアプリならこの手順を代わりにやってくれます。

Ikiform がイベントでやらないこと

選ぶ前にはっきりさせておきましょう。フォームバックエンドは回答を集めるものです。チケットシステムではありません:

  • 定員の上限はありません。 一定数の回答やゲストでフォームを締め切る設定はありません。人数を見ながら、自分でフォームを一時停止します。
  • チケットも決済もありません。 QR コード、受付アプリ、有料の区分、返金はありません。
  • ゲストへのメールはありません。 Ikiform はフォームを入力した本人にはメールを送りません。通知メールが届くのはあなたとチームだけなので、自動返信、確認メール、ダブルオプトインはありません。それらは Webhook をきっかけに、自分のメールツールから送ってください。
  • キャンセル待ちはありません。 フォームを一時停止すると、新しい送信は拒否されます。キャンセル待ちが必要なら、代わりに 2 つ目のフォームを置くことになります。
  • 重複の除去はありません。 Ikiform は同じメールアドレスからの重複送信を統合も拒否もしません。24 時間以内のまったく同じ再送信は、スパムスコアを上げるだけです。重複はリストを管理する場所か、エクスポート時に取り除いてください。

どれにも、以下で実用的な対処法を示します。いくつかが致命的なら、チケットプラットフォームのほうが適しています。

出欠を受け付ける方法

方法得意なことトレードオフ
Eventbrite や Luma などのチケットプラットフォームチケット、決済、定員制限、キャンセル待ち、確認メール、受付がすべて揃っているイベントページと参加者リストは相手のプラットフォームにあり、相手のブランドで表示される。有料チケットには手数料がかかるので、料金ページを確認すること
Google Forms無料ですぐ作れる。回答の受け付けを止めるスイッチがある見た目は Google で、自分のイベントらしくない。ページも別になり、サーバー側の同伴者上限もない
カレンダーの招待やグループチャット友人 20 人程度なら十分信頼できる人数も、食事の要望も、当日のリストもない
自分のフォームと、Ikiform などのフォームバックエンド自分のページ、自分のデザイン、自由なフィールド。同伴者の上限はサーバーで確認され、スパムは除外され、主催者にメールが届き、CSV と Webhook も使える人数の集計、登録の締め切り、確認メールの送信は、自分で行うか、メールツールへの Webhook で行う

有料イベントならチケットプラットフォームを使ってください。ページとリストを自分の手元に置いておきたい無料イベントなら、このガイドの残りがその設定方法です。Eventbrite のページ で、両者をさらに詳しく比較しています。

計画を立てられるフィールド

行動につながることだけを尋ね、それ以外は尋ねないでください。ここにある質問はどれも、注文、予約、手配の内容を変えるものです。試してみてください。プレビューはブラウザーで Ikiform のバリデーターを実行するので、4 人目の同伴者で返る 422 を確認できます。

プレビュー

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

参加できますか?

ご本人を除きます。最大 3 人。

この定義をフォームの Fields タブに貼り付けると、どのページからの送信にも Ikiform がこのルールを適用します。イベント出欠テンプレートには、コードを生成できる短い版があります。
フィールド理由注意点
attending不参加も情報です。その人を追いかけずに済みますyes と no を持つ radio なので、集計は絞り込みで済む
guests料理や椅子を手配する人数min: 0、max: 3、integer: true の number。Ikiform はサーバーで 5 や 1.5 を拒否する
dietaryケータリングの数select ならきれいに合計できる。自由記述ではできない
access_needs段差のない動線、座席、静かな部屋任意。当日までにすべて読んでおく
email変更のお知らせと、変更された回答の照合すべての通知メールの reply-to にもなる
イベントに必要なことだけを尋ねてください。食事やアクセシビリティの回答は健康情報になりえます。尋ねる理由をフォームに書き、エクスポートは非公開の場所に保管し、イベントが終わったら送信データを削除してください。

出欠フォーム

スタックを選んでください。どの版も参加と不参加で異なるお礼を表示し、不参加なら同伴者の質問を隠し、来ない人の guests を 0 として送るので、合計がずれません。

フォームをイベントページに貼り付けます。通常の方法で送信して確認ページにリダイレクトするので、JavaScript なしでも動きます。短いスクリプトが、不参加と答えたときに同伴者の質問を隠し、同伴者数を 0 に戻します。

event.html
<form id="rsvp" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label for="name">Name</label>
  <input id="name" name="name" autocomplete="name" required maxlength="120">
  <label for="email">Email</label>
  <input id="email" name="email" type="email" autocomplete="email" required>
  <fieldset>
    <legend>Can you make it?</legend>
    <label><input type="radio" name="attending" value="yes" required> Yes</label>
    <label><input type="radio" name="attending" value="no"> No</label>
  </fieldset>
  <div id="if-going">
    <label for="guests">Guests you're bringing</label>
    <input id="guests" name="guests" type="number" min="0" max="3" step="1" value="0">
    <label for="dietary">Dietary needs</label>
    <select id="dietary" name="dietary">
      <option value="none">None</option>
      <option value="vegetarian">Vegetarian</option>
      <option value="vegan">Vegan</option>
      <option value="gluten_free">Gluten free</option>
      <option value="other">Other, tell us below</option>
    </select>
    <label for="access_needs">Access needs or anything else</label>
    <textarea id="access_needs" name="access_needs" rows="3" maxlength="1000"></textarea>
  </div>
  <input type="hidden" name="_redirect" value="https://example.com/meetup/registered">
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden>
  <button type="submit">Send RSVP</button>
</form>
<script>
  const rsvp = document.getElementById("rsvp");
  const extra = document.getElementById("if-going");
  rsvp.addEventListener("change", () => {
    const no = rsvp.elements.attending.value === "no";
    extra.hidden = no;
    if (no) rsvp.elements.guests.value = "0";
  });
</script>
サーバー側の版はシークレットキーで送信するので、Ikiform は Allowed domains と人間確認をスキップします。その場合、すべての送信がサーバーの IP から来るため、訪問者ごとに 1 分あたり 5 件という Standard の制限が全訪問者をまとめて数えてしまいます。サーバー経由で送信するフォームには Relaxed か Custom のレート制限を選び、IKIFORM_SECRET_KEY はサーバー専用の環境変数に置いてください。サーバー版は、ブラウザーの Origin ヘッダーがあればそれも引き継ぎます。サーバー自身の fetch はこれを送らず、Ikiform は無料の localhost テスト送信をこのヘッダーで判定するからです。

ドキュメントのフレームワーク別ガイドでさらに詳しく説明しています: Next.js、Astro、SvelteKit、Nuxt、React、Vue、プレーンな HTML。サイトビルダーでは、HTML 版を埋め込みブロックに貼り付けます。

最新の回答から人数を数える

人の気は変わります。月曜に同伴者 2 人で参加と答えた人が、木曜に不参加と答えることもあります。送信データは 2 件で、正しいのは 2 件目です。そこでルールは、メールアドレスごとに最新の回答を 1 つだけ使い、本人の 1 人に同伴者数を足す、というものです。

人数集計に各人の最新の回答を使う理由Ada: 参加同伴者 2 人Ben: 参加同伴者 0 人Ada: 不参加気が変わった1 人Ben のみ最新が優先月曜水曜木曜人数人数集計に各人の最新の回答を使う理由Ada:参加同伴者2人Ben:参加同伴者0人Ada:不参加気が変わった1人Benのみ最新が優先月曜水曜木曜人数
  1. 月曜: Ada が同伴者 2 人で参加と回答。すべての送信データを足すと 3 人になります。
  2. 水曜: Ben が同伴者なしで参加と回答。
  3. 木曜: Ada がもう一度フォームを送り、不参加と回答。Ikiform はこれを彼女の 2 件目の送信データとして保存します。
  4. メールアドレスごとに最新の送信データだけを使うと Ada は不参加なので、人数は Ben の 1 人です。
すべての行を足すと Ada の 3 人組を数えてしまいます。メールアドレスごとに最新の行を数えれば正しくなります。

スプレッドシートで

  1. Submissions で出欠フォームを選び、ステータスを Received、期間を All time にして、Export CSV を選びます。
  2. Google Sheets にインポートし、created_at で新しい順に並べ替えます。
  3. データを選択し、email 列で Data > Data cleanup > Remove duplicates を使います。Sheets は最初に見つけた行を残すので、残るのは最新の行です。
  4. attending が G 列、guests が H 列の場合の数え方です。列の文字はエクスポートに合わせて調整してください:
Google Sheets
=COUNTIF(G2:G, "yes") + SUMIF(G2:G, "yes", H2:H)

エクスポートは先頭に id、created_at、form、status があり、その後にフィールドごとに 1 列が続きます。1 ファイルに最大 100,000 行まで入ります。

スクリプトで

いつでも再計算できる数字がほしいなら、REST API で出欠を読み込みます。一覧は新しい順に返るので、メールアドレスごとに最初に見つかった送信データが最新です。Node とシークレット API キーで実行してください:

headcount.ts
import { createClient } from "@ikiform/sdk";

const ikiform = createClient({ apiKey: process.env.IKIFORM_SECRET_KEY! });
const latest = new Map<string, Record<string, unknown>>();

for await (const submission of ikiform.submissions.iterate({
  formId: "frm_xxxxxxxxxxxxxxxx",
  status: "received",
  limit: 100,
})) {
  const email = String(submission.fields.email ?? "").trim().toLowerCase();
  if (email && !latest.has(email)) latest.set(email, submission.fields);
}

let people = 0;
const meals = new Map<string, number>();
for (const fields of latest.values()) {
  if (fields.attending !== "yes") continue;
  people += 1 + Number(fields.guests ?? 0);
  const meal = String(fields.dietary ?? "none");
  meals.set(meal, (meals.get(meal) ?? 0) + 1);
}

console.log(`${latest.size} people replied. ${people} are coming, guests included.`);
console.table(Object.fromEntries(meals));

料理の表は人数ではなく回答の数を数えます。1 つのフォームでは、どの同伴者が何を食べるかまではわからないからです。結婚式のように同伴者の料理が重要なら、1 人ずつ尋ねてください。方法は結婚式の出欠ページ にあります。

満員になったら登録を締め切る

Ikiform は一定数でフォームを止めないので、満員になる前の通知と締め切りの方法の 2 つを用意します。締め切るのはフォームとページの 2 か所です:

  1. フォームの設定で Pause form を選びます。それ以降の送信は 403 form_paused で拒否されるので、開きっぱなしのタブやボットが追加することはできません。
  2. イベントページのフォームを、Registration is closed. Email us to join the waitlist. のような一文に置き換えます。訪問者にエラーを見せてはいけません。
  3. 再開するときは Resume form を選び、フォームを元に戻します。

通知には、参加の回答があるたびに再集計し、定員の 90% を超えたら Slack などのチャットの Webhook に投稿する Webhook ハンドラーを使えます。これは Next.js のルートハンドラーですが、同じロジックはどのサーバーでも動きます:

app/api/rsvp-alert/route.ts
import { createClient } from "@ikiform/sdk";
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";

const CAPACITY = 80;
const ikiform = createClient({ apiKey: process.env.IKIFORM_SECRET_KEY! });

async function peopleComing(formId: string) {
  const seen = new Set<string>();
  let people = 0;
  for await (const submission of ikiform.submissions.iterate({ formId, status: "received", limit: 100 })) {
    const email = String(submission.fields.email ?? "").trim().toLowerCase();
    if (!email || seen.has(email)) continue;
    seen.add(email);
    if (submission.fields.attending === "yes") people += 1 + Number(submission.fields.guests ?? 0);
  }
  return people;
}

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" || event.data.fields.attending !== "yes") {
      return new Response(null, { status: 204 });
    }
    const people = await peopleComing(event.data.form.id);
    if (people >= CAPACITY * 0.9) {
      await fetch(process.env.ALERT_WEBHOOK_URL!, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          text: `${people} of ${CAPACITY} places taken. Pause the RSVP form in Ikiform when you're full.`,
        }),
      });
    }
    return new Response(null, { status: 204 });
  } catch (error) {
    if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
    throw error;
  }
}
  • 毎回 API で再集計するので、変更された回答も反映されます。キーごとに 1 分あたり 100 回の API リクエストができ、1 回の集計で数千件の出欠に対応できます。
  • 通知は人に一時停止を促すだけです。一時停止はダッシュボードでの操作なので、知らないうちに締め切られることはありません。
  • Webhooks でエンドポイントを追加し、そのシークレットを IKIFORM_WEBHOOK_SECRET にコピーします。1 つのワークスペースに作れるエンドポイントは 3 つです。リトライについては Webhook のドキュメントを参照してください。
受付開始から締め切りまで、自動のものは何もなし受付中ページ上のフォーム90% で通知Webhookハンドラーが再集計Pauseform403form_pausedページに締め切りを表示メールでキャンセル待ち出欠自分で判断受付開始から締め切りまで、自動のものは何もなし受付中ページ上のフォーム90% で通知Webhook ハンドラーが再集計Pause form403 form_pausedページに締め切りを表示メールでキャンセル待ち出欠自分で判断
  1. 受付中は、すべての出欠が Ikiform に送信されます。
  2. Webhook ハンドラーが参加の回答のたびに最新の回答を再集計し、定員の 90% で通知します。
  3. 満員かどうかは自分で判断し、Pause form を選びます。新しい送信は 403 form_paused で拒否されます。
  4. ページのフォームを、締め切りのメッセージとキャンセル待ちへの登録方法に置き換えます。
締め切りを決めるのは人です。チケットアプリの厳密な上限より遅いですが、直前の予定変更で、入れてあげたはずの人を締め出すことはありません。

確認メールなしで参加を確定させる

Ikiform はゲストにメールを送らないので、必要な情報はサンクスページで伝えます。日付、時刻、住所、カレンダーファイルです。ページと同じ場所に置いた静的な .ics ファイルは、どのカレンダーアプリでも使えます。確認ページから Add to calendar としてリンクしてください:

meetup.ics
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Meetup//RSVP//EN
BEGIN:VEVENT
UID:meetup-2027-03-14@example.com
DTSTAMP:20261025T090000Z
DTSTART:20270314T183000Z
DTEND:20270314T210000Z
SUMMARY:Example meetup
LOCATION:The Old Library, 12 High Street, London
DESCRIPTION:Doors at 6:30 pm. Reply to the organiser if your plans change.
END:VEVENT
END:VCALENDAR

Z で終わる時刻は UTC なので、カレンダーアプリは各ゲストのタイムゾーンで表示します。本物の確認メールを送りたい場合は、各回答を Webhook でメールツールに直接、または Zapier、Make、n8n 経由で送り、そこから送信してください。同じリストに前日のリマインダーも送れます。

回答の変更とキャンセル

回答の変え方を伝えておきましょう。たとえば Plans changed? Send the form again with the same email. です。集計はすでに最新の回答を使っているので、他に必要なことはありません。通知で変更がその都度わかるので、イベント直前の週には役立ちます。

同じ Wi-Fi を使う 2 人は IP アドレスを共有し、レート制限は IP ごとに数えます。デフォルトの Standard(訪問者ごとに 1 分あたり 5 件)は、イベントページには十分です。重複を防ぐために 1 日 1 件の Custom 制限を設定してはいけません。同じネットワークにいる家族の 2 人目が締め出されますし、重複は最新の回答のルールですでに処理できます。

当日

出欠をエクスポートし、上のように重複を取り除き、attending = yes で絞り込んで名前順に並べ、印刷します。チェック用の列と、到着した同伴者用の列を追加しておきます。会場の電波が悪くても、印刷したリストなら使えます。アクセシビリティの要望の列は受付チームの控えに、食事の列はケータリング業者の控えに残してください。

スパムがリストに入ることはありません。ハニーポット、スパムスコア、レート制限が集計前に除外し、スパムではメールも Webhook も送られません。印刷する前に一度 Spam フィルターを確認し、変わったメッセージの本物の回答が入っていないか見てください。使い捨てアドレスはスパムスコアに 2 を加えます。

状況に合わせて選ぶ

結婚式、無料のコミュニティイベント、ボランティアのシフト表では、それぞれ数え方が違います。何が変わるかは以下のページで説明しています。

動作を確認する

イベントを告知する 1 週間前に、一連の流れをテストしてください。Ikiform に無料プランはありません。テストは無料で、プランのないワークスペースでも localhost からのテスト送信を月 50 件まで受け付けます。本番運用するには、月 $6 で 500 件からの Pro を選ぶか、1 回払いで 10,000 件 $49 または 50,000 件 $149 のパックを購入します。クレジットに有効期限はありません。料金を参照してください。

  1. フォームの Allowed domains に自分のサイトのドメインを追加し、開発中は localhost も追加します。空のままにすると、どこからの送信も受け付けます。
  2. 別々のメールアドレスで、同伴者 2 人の参加と、不参加を送ります。両方が届き、不参加の guests は 0 になっています。
  3. 同伴者 3 人、次に 4 人で送ります。4 人目は Must be at most 3 で拒否されます。
  4. 通知の各受信者の受信トレイを確認します。それぞれにフィールド入りのメールが届き、フォームに email フィールドがあれば Reply で送信者に返信できます。
  5. 人数集計のスクリプトかスプレッドシートの数式を実行します。送った内容と一致するはずです。
  6. フォームを一時停止して、もう一度送信します。403 form_paused が返ります。再開してください。
  7. ハニーポットを埋めて _gotcha=bot で送信します。成功の応答は返りますが、送信データはスパムとして保存され、メールも Webhook も送られません。
  8. 何も届かない場合は、ダッシュボードのステータスフィルターを Spam に切り替えてください。スパムにも本物の送信と同じ成功の応答が返ります。

トラブルシューティング

問題対処法
人数が多すぎるすべての送信データを足しています。メールアドレスごとに最新のものだけを残し、不参加で guests が 0 として送られているか確認してください。
本番のページで 403 form_paused が返るダッシュボードでフォームが一時停止されています。再開するか、ページのフォームを締め切りのメッセージに置き換えてください。
guests についての 422 validation_failed同伴者がフィールドの max より多いか、小数です。サンプルのように、Ikiform のメッセージをフィールドの横に表示してください。
403 origin_not_allowed が返る、またはすべての送信がスパムになるページのドメインがフォームの Allowed domains にありません。ドメインと、ローカルテスト用の localhost を追加してください。登録したドメインのサブドメインは許可されます。
429 rate_limited1 人の訪問者がフォームのレート制限を超えて送信しました。Standard では訪問者ごとに 1 分あたり 5 件までです。Retry-After まで待つか、共有の IP 1 つから送信するフォームには Relaxed か Custom を選んでください。
送信データはダッシュボードにあるのに、メールも Webhook も届かないスパムとして保存されています。スパムでは何も送られません。Spam フィルターで開いて理由を確認し、スパムではないとマークすると、メールと Webhook が送られます。
ブラウザーが自分のページではなく Ikiform のサンクスページに移動する_redirect は、ドメインが Allowed domains にあり、https: の URL でないと無視されます。ドメインを追加するか、代わりにフォーム設定で Redirect after submit を設定してください。

よくある質問

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