イベント登録

イベント登録2 分で読めます

Eventbrite を使わない、無料コミュニティイベントの参加登録

無料のコミュニティイベントの参加登録を自分のサイトで運営します。セッションの選択、自分のツールからの確認メール、部屋ごとの人数、受付リストまで。

Preet SutharIkiform 創業者

無料イベントなら、自分のサイトの参加登録フォームで Eventbrite の申し込みを置き換えられます。フォームは Ikiform に送信し、Ikiform が各登録を保存してチームにメールで通知します。自分のメールプロバイダーから確認メールを送る Webhook を追加し、メールアドレスごとの最新の登録からセッションごとの席数を数えます。有料チケット、厳密な定員管理、受付でのスキャンには Eventbrite や他のチケットプラットフォームを使い続けてください。

チケットプラットフォームなしで変わること

Eventbrite は 5 つの仕事を同時にこなしているので、これから自分でやる仕事を見落としがちです。無料のコミュニティデイ、ミートアップのシリーズ、ワークショップなら、ほとんどは小さな仕事です。正直に分けると次のようになります:

Eventbrite から自分の環境に移る仕事と、それぞれの担い手イベントページ相手のページ、相手のブランド自分のページ参加登録相手のチェックアウト自分のフォーム、Ikiformが保存確認メール自動で送信メールツールへの Webhook定員厳密な上限、キャンセル待ち自分で数えて一時停止受付アプリ、QR スキャン印刷したリスト仕事Eventbrite では自分のサイトではEventbrite から自分の環境に移る仕事と、それぞれの担い手イベントページ相手のページ、相手のブランド自分のページ参加登録相手のチェックアウト自分のフォーム、Ikiformが保存確認メール自動で送信メールツールへのWebhook定員厳密な上限、キャンセル待ち自分で数えて一時停止受付アプリ、QRスキャン印刷したリスト仕事Eventbriteでは自分のサイトでは
  1. イベントページは Eventbrite のページから、自分のデザインの自分のサイトに移ります。
  2. 参加登録は Eventbrite のチェックアウトから、Ikiform に送信する自分のフォームに移ります。
  3. 確認メールは自動では送られなくなります。Webhook が自分のメールプロバイダーから送ります。
  4. 定員は厳密な上限ではなくなります。セッションごとに登録数を数え、満員になったらフォームを一時停止します。
  5. 受付は QR スキャン付きのアプリではなくなります。CSV エクスポートから印刷したリストを使います。
最初の 3 つは自分のサイトのほうが優れています。残りの 2 つは劣り、大規模なイベントや有料イベントではそれがチケットプラットフォームに残る理由になります。

Eventbrite の料金は変わるので、コストを比較する前に公式の料金ページを確認してください。無料イベントの場合、離れる理由はたいてい手数料ではありません。参加者リストが他社のプラットフォームにあること、ページが相手のデザインになること、登録者が独自のアカウントやメール設定を持つ別の会社に情報を渡すことです。有料イベントなら、そのまま使い続けてください。Ikiform に決済機能はありません。

セッションを選べる参加登録フォーム

コミュニティデイでは並行セッションがよくあり、部屋の広さもまちまちです。参加予定のセッションを尋ねておけば、当日までに人気のワークショップを大きな部屋に移せます。この定義では、2 つが同時に行われるので、4 つのうち最大 3 つまで選べるようにしています:

プレビュー

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

参加するセッション
昼食
sessions は配列として保存される multi_select です。HTML ではチェックボックスの name を sessions[] にすると、1 つだけ選んだ場合もリストとして保存されます。
  • name のラベルを名札用にしているので、本人が呼ばれたい名前を集められます。
  • lunch は No lunch の選択肢がある radio なので、食べる人の分だけ注文できます。
  • updates は初期状態でチェックの入っていないチェックボックスで、独自の created_at を持つ送信データに true か false として保存されます。ニュースレターに追加するのは true の人だけにしてください。
  • 各スタックのマークアップはメインガイドの出欠フォーム を使い、フィールドをここのものに差し替えてください。
参加登録フォームで今後のお知らせも案内する場合は、そのチェックボックスを初期状態でオフにし、何を送るのかを具体的に書いてください。チェックボックスは同意の記録になります。下の確認メールはマーケティングではないので、同意は不要です。

確認メールは自分のメールツールから送る

Ikiform はフォームを入力した本人にはメールを送りません。通知メールが届くのはあなたとチームだけなので、自動返信、確認メール、ダブルオプトインはありません。それらは Webhook をきっかけに、自分のメールツールから送ってください。イベントでは確認メールを必ず送りましょう。参加者は上司に転送したり、当日の朝に検索したりするからです。このルートハンドラーは Ikiform の署名付き Webhook を受け取り、Resend の API でメールを送ります。送信用のエンドポイントがあるプロバイダーなら、どれでも同じように使えます。

app/api/registered/route.ts
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";

const TITLES: Record<string, string> = {
  keynote: "Opening talk, 10:00",
  workshop_a: "Workshop A, 11:30",
  workshop_b: "Workshop B, 11:30",
  panel: "Closing panel, 15:00",
};

function escape(text: string) {
  return text.replace(/[&<>"']/g, (char) => `&#${char.charCodeAt(0)};`);
}

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 fields = event.data.fields;
    const email = String(fields.email ?? "");
    const chosen = (Array.isArray(fields.sessions) ? fields.sessions : []).map((id) => TITLES[String(id)] ?? String(id));
    const response = await fetch("https://api.resend.com/emails", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.RESEND_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": request.headers.get("webhook-id") ?? event.data.submission_id,
      },
      body: JSON.stringify({
        from: "Community Day <hello@community.example>",
        to: email,
        reply_to: "hello@community.example",
        subject: "You're registered for Community Day, 14 March",
        html: [
          `<p>Hi ${escape(String(fields.name ?? ""))},</p>`,
          "<p>You're on the list for Community Day on 14 March at The Old Library. Doors open at 9:30.</p>",
          `<p>Your sessions: ${chosen.map(escape).join(", ")}.</p>`,
          '<p><a href="https://community.example/day.ics">Add it to your calendar</a>. Can\'t come after all? Reply to this email.</p>',
        ].join(""),
      }),
    });
    if (!response.ok) return new Response(`Email API 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 が署名を確認するので、他人があなたのドメインからメールを送らせることはできません。
  • Idempotency-Key には配信の webhook-id を使います。この値は自動のリトライでも、失敗した配信の手動の Retry でも変わらないので、応答が失われた配信で 2 通目のメールが送られることはありません。
  • 名前とセッション名は、HTML に入れる前にエスケープします。フィールドの値は信頼できない入力として扱ってください。
  • 502 を返すと Ikiform がリトライするので、メールプロバイダーの短い障害で確認メールが失われることはありません。スパムでは Webhook が発火しないので、ボットのせいで見知らぬ人にメールを送ることもありません。

ダッシュボードの Webhooks でルートを追加し、そのシークレットを IKIFORM_WEBHOOK_SECRET にコピーします。Ikiform は失敗した配信を約 3 日間リトライし、配信ログに各試行が表示されます。コードを書かずに、Zapier や Make などの自動化ツールで同じメールを送ることもできます。

セッションごとの席数

Ikiform は、部屋が満員になったセッションの登録を止めることも、満員の選択肢を隠すこともできません。代わりに各参加者の最新の登録を使って数え、数字が必要なときにいつでも実行してください:

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

const ikiform = createClient({ apiKey: process.env.IKIFORM_SECRET_KEY! });
const ROOMS: Record<string, number> = { keynote: 200, workshop_a: 30, workshop_b: 30, panel: 200 };
const latest = new Map<string, string[]>();

for await (const submission of ikiform.submissions.iterate({ formId: process.argv[2], status: "received", limit: 100 })) {
  const email = String(submission.fields.email ?? "").trim().toLowerCase();
  const chosen = Array.isArray(submission.fields.sessions) ? submission.fields.sessions.map(String) : [];
  if (email && !latest.has(email)) latest.set(email, chosen);
}

for (const [session, seats] of Object.entries(ROOMS)) {
  const taken = [...latest.values()].filter((chosen) => chosen.includes(session)).length;
  console.log(`${session.padEnd(12)} ${String(taken).padStart(4)} of ${seats}${taken >= seats ? "  FULL" : ""}`);
}

ワークショップが満員になったら、ページで満員と表示し、フォームとフィールド定義からその選択肢を削除して、新しい送信で選べないようにします。イベント全体が満員になったら Pause form を選びます。送信には 403 form_paused が返るので、ページには登録を締め切ったと表示しましょう。

状況対処法
ワークショップの 1 つが満員フォームとフィールド定義からその選択肢を削除し、ページでその旨を伝える
イベント全体が満員Pause form を選び、フォームをキャンセル待ち用のメールアドレスに置き換える
キャンセルが出た本人が確認メールに返信してくる。送信データを削除して、集計上の席を空ける
無断欠席が見込まれる無料イベントでは一定の割合で無断欠席が出がち。自分たちの割合を記録し、次回は少し多めに受け付ける

受付で

  1. 前日の夜に Submissions を CSV でエクスポートし、メールアドレスの重複を最新のものを残して削除します。
  2. 名前順に並べ替え、name、organisation、lunch を残して、受付用に 2 部印刷します。
  3. 同じシートから差し込み印刷で name 列を使って名札を印刷します。
  4. 当日参加者は手書きで記入します。イベント後は、本人が同意した場合にだけ自分のリストに追加してください。

告知する前に確認する

  1. フォームの Allowed domains に自分のサイトのドメインを追加し、開発中は localhost も追加します。空のままにすると、どこからの送信も受け付けます。
  2. 自分のメールアドレスで、セッションを 2 つ選んで登録します。
  3. 自分のドメインから、両方のセッションが記載された確認メールが届きます。
  4. Webhook を追加した場合は、Webhooks でエンドポイントを開きます。配信が succeeded になり、サーバーの応答が表示されます。
  5. 席数の集計を実行します。選んだ 2 つのセッションがそれぞれ 1 席埋まっています。
  6. 何も届かない場合は、ダッシュボードのステータスフィルターを Spam に切り替えてください。スパムにも本物の送信と同じ成功の応答が返ります。

ハマりどころ

問題対処法
選択肢は最大 3 つまでという 4224 つのセッションすべてを選んだ人がいます。これは max_items のルールが機能している状態です。Ikiform のメッセージをチェックボックスの横に表示してください。
1 つだけ選ぶとセッションが 1 つしか保存されないチェックボックスの name を sessions[] にしてください。フィールドを multi_select として定義していれば、値が 1 つでもリストとして保存されます。
登録は Ikiform にあるのに確認メールが届かないエンドポイントのログで配信を開いてください。プロバイダーのステータス付きの 502 は、メールが拒否されたことを意味します。送信ドメインが未認証であることがよくあります。
送信データはダッシュボードにあるのに、メールも Webhook も届かないスパムとして保存されています。スパムでは何も送られません。Spam フィルターで開いて理由を確認し、スパムではないとマークすると、メールと Webhook が送られます。
429 rate_limited1 人の訪問者がフォームのレート制限を超えて送信しました。Standard では訪問者ごとに 1 分あたり 5 件までです。Retry-After まで待つか、共有の IP 1 つから送信するフォームには Relaxed か Custom を選んでください。

よくある質問

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