Daftar tunggu untuk SaaS baru

Daftar tunggu untuk SaaS baru7 menit baca

Cara membuat formulir pendaftaran beta yang menyaring pelamar

Formulir lamaran beta dengan pertanyaan penyaringan yang ditegakkan server, dan skrip yang menilai pelamar serta memilih setiap kohort dari daftar.

Preet SutharPendiri, Ikiform

Ajukan beberapa pertanyaan penyaringan di samping email, dan biarkan form backend menegakkannya agar setiap lamaran tiba lengkap dan bisa dibandingkan. Lalu pilih setiap kohort dari daftar dengan skrip penilaian singkat, undang sekelompok kecil, dan biarkan yang lain menunggu putaran berikutnya.

Daftar tunggu mengirim email ke semua orang di hari peluncuran. Beta justru sebaliknya: Anda memasukkan dua puluh orang, belajar dari mereka, lalu memasukkan dua puluh lagi. Itu mengubah formulirnya. Anda butuh jawaban yang bisa diurutkan, bukan hanya alamat.

Apa yang berubah untuk beta

  • Ini adalah seleksi. Anda mengundang sedikit orang setiap kali, jadi Anda butuh cara untuk membandingkan peringkat pelamar satu sama lain. Pilihan tertutup, seperti peran dan ukuran tim, mudah diurutkan. Teks bebas tidak.
  • Lamaran yang tidak lengkap membuang satu slot. Jika seseorang melewatkan pertanyaan yang memberi tahu Anda apakah mereka cocok, Anda tidak bisa memilihnya dan juga tidak bisa mencoretnya. Jadikan pertanyaan penyaringan wajib, dan biarkan server menolak kiriman yang melewatkannya.
  • Undangan dikirim bertahap. Daftar Anda perlu mengingat siapa yang sudah diundang, agar kohort berikutnya melewati mereka. Ikiform tidak punya tag, jadi status itu disimpan di skrip atau sheet Anda.
  • Orang yang tidak terpilih tetap penting. Mereka adalah kohort berikutnya. Beri tahu mereka di kalimat terima kasih bahwa kelompok diundang secara bertahap, agar inbox yang sepi tidak terbaca sebagai penolakan.

Pertanyaan penyaringan

Enam pertanyaan adalah batas atasnya. Setiap pertanyaan harus mengubah siapa yang Anda undang; jika Anda tidak bisa menjelaskan bagaimana sebuah jawaban menaikkan atau menurunkan peringkat seseorang, hapus saja. Coba formulir di bawah. Formulir ini menjalankan definisi field yang sama dengan yang ditegakkan Ikiform di server, jadi Anda bisa melihat 422 yang didapat lamaran yang tidak lengkap.

Pratinjau

Menjalankan validator Ikiform di browser Anda. Tidak ada yang dikirim.

Ukuran tim

Satu atau dua kalimat tentang pekerjaan yang akan Anda serahkan padanya di hari pertama.

Tempel definisi ini di tab Fields formulir. Jawaban use_case butuh minimal 40 karakter, yang menyaring lamaran satu kata.
FieldYang ditentukanAlasan memilih tipe ini
emailSiapa yang diundangWajib. Email kantor memberi petunjuk tentang perusahaannya tanpa perlu bertanya
roleApakah mereka merasakan masalah yang Anda selesaikanselect dengan kelompok buatan Anda sendiri, agar Anda bisa memberi bobot
team_sizeApakah versi pertama Anda cocok dengan setup merekaradio: satu jawaban, selalu bisa dibandingkan
use_caseApakah mereka akan memakai beta bulan initextarea dengan panjang minimum. Sinyal paling jujur di formulir
current_toolSiapa yang Anda gantikan, dan dengan apa mereka akan membandingkan AndaTeks bebas opsional
interviewSiapa yang bisa Anda ajak bicaracheckbox. Jika tidak ada berarti false, jadi tidak pernah menghalangi lamaran
Dengan definisi field terpasang, Ikiform menolak field apa pun yang tidak dikenalnya dengan 422 dan error unknown_field. Jika Anda menambahkan input tersembunyi seperti ref, tambahkan juga ke definisinya.

Formulirnya

Setiap versi di bawah menampilkan pesan server di bawah field yang bersangkutan, karena formulir penyaringan bisa gagal karena lebih banyak alasan daripada sekadar email yang salah. Setiap versi mempertahankan apa yang sudah diketik orang saat kiriman ditolak, jadi tidak ada yang perlu mengetik ulang satu paragraf.

required dan minlength bawaan sudah menangkap kebanyakan kekurangan sebelum dikirim. Formulir biasa berfungsi tanpa skrip dan mendarat di halaman terima kasih Anda; skripnya membuat orang tetap di halaman dan menampilkan setiap error field.

beta.html
<form id="beta-form" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label for="email">Work email</label>
  <input id="email" name="email" type="email" autocomplete="email" required>

  <label for="role">Your role</label>
  <select id="role" name="role" required>
    <option value="">Choose one</option>
    <option value="founder">Founder</option>
    <option value="engineer">Engineer</option>
    <option value="product">Product manager</option>
    <option value="designer">Designer</option>
    <option value="other">Something else</option>
  </select>

  <fieldset>
    <legend>Team size</legend>
    <label><input type="radio" name="team_size" value="1" required> Only me</label>
    <label><input type="radio" name="team_size" value="2-10"> 2 to 10</label>
    <label><input type="radio" name="team_size" value="11-50"> 11 to 50</label>
    <label><input type="radio" name="team_size" value="51+"> More than 50</label>
  </fieldset>

  <label for="use_case">What would you use it for first?</label>
  <textarea id="use_case" name="use_case" rows="4" minlength="40" maxlength="1000" required></textarea>

  <label for="current_tool">What do you use today?</label>
  <input id="current_tool" name="current_tool" maxlength="120">

  <label><input type="checkbox" name="interview" value="true"> I'm open to a 20-minute call</label>
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden>
  <ul id="beta-errors" role="alert"></ul>
  <button>Apply for the beta</button>
</form>
Sebelum </body>
<script>
  const form = document.getElementById("beta-form");
  const errors = document.getElementById("beta-errors");
  const labels = { email: "Work email", role: "Role", team_size: "Team size", use_case: "First use", current_tool: "Current tool" };

  form.addEventListener("submit", async (event) => {
    event.preventDefault();
    const button = form.querySelector("button");
    button.disabled = true;
    errors.replaceChildren();
    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.outerHTML = "<p role=\"status\">Thanks for applying. We invite testers in small groups and will email you either way.</p>";
        return;
      }
      const fields = Object.entries(result.error.fields ?? {});
      const lines = fields.length ? fields.map(([id, messages]) => `${labels[id] ?? id}: ${messages[0]}`) : [result.error.message];
      errors.replaceChildren(...lines.map((line) => Object.assign(document.createElement("li"), { textContent: line })));
    } catch {
      errors.textContent = "Couldn't reach the server. Your answers are still here; try again.";
    }
    button.disabled = false;
  });
</script>
Versi sisi server mengirim dengan secret key, jadi Ikiform melewati Allowed domains dan pemeriksaan manusianya. Setiap kiriman lalu datang dari IP server Anda, dan batas Standard 5 per menit per pengunjung menghitung semua pengunjung Anda sekaligus. Pilih rate limit Relaxed atau Custom untuk formulir yang Anda kirim lewat server, dan simpan IKIFORM_SECRET_KEY di environment variable khusus server.

Pilih kohort

Bagaimana lamaran menjadi kohort betalamaranlengkap,ditegakkanserverskorperan,ukuran,usaha,panggilan25terataskohort 1,diundangputaranberikutnyalewati yangsudahdiundangmasihmenunggudiberi tahubertahapekspordua minggudi bawah batasBagaimana lamaran menjadi kohort betalamaranlengkap, ditegakkanserverskorperan, ukuran, usaha,panggilan25 terataskohort 1, diundangputaranberikutnyalewati yang sudahdiundangmasihmenunggudiberi tahubertahapekspordua minggudi bawah batas
  1. Lamaran tiba lengkap, karena Ikiform menolak kiriman yang melewatkan pertanyaan wajib.
  2. Skrip membacanya lewat API dan menilai masing-masing berdasarkan peran, ukuran tim, panjang use case, dan checkbox panggilan.
  3. 25 teratas menjadi kohort pertama dan mendapat undangan dari tool email Anda.
  4. Dua minggu kemudian, jalankan skrip lagi dengan email yang sudah diundang, agar mereka dilewati, lalu pilih kelompok berikutnya.
  5. Semua yang berada di bawah batas tetap di daftar dan akan Anda kabari saat gilirannya tiba.
Skor adalah urutan awal, bukan keputusan akhir. Baca sendiri use case semua orang yang berada di dekat garis batas.

Skrip membaca setiap lamaran yang diterima dengan REST API, menyimpan yang terbaru per email, menilainya, dan menulis kelompok teratas ke cohort.csv. Berikan email yang sudah Anda undang sebagai argumen agar putaran berikutnya melewatinya. Skrip ini butuh secret key dengan scope submissions:read.

cohort.mjs
import { writeFileSync } from "node:fs";
import { createClient } from "@ikiform/sdk";

const ikiform = createClient({ apiKey: process.env.IKIFORM_SECRET_KEY });
const COHORT_SIZE = 25;
const invited = new Set(process.argv.slice(2).map((email) => email.toLowerCase()));

const ROLE = { founder: 3, engineer: 3, product: 2, designer: 1, other: 0 };
const SIZE = { "2-10": 3, "11-50": 2, "1": 1, "51+": 1 };

const people = new Map();
for await (const submission of ikiform.submissions.iterate({ formId: "frm_xxxxxxxxxxxxxxxx", status: "received", limit: 100 })) {
  const answers = submission.fields;
  const email = String(answers.email ?? "").trim().toLowerCase();
  if (!email || invited.has(email) || people.has(email)) continue;
  const words = String(answers.use_case ?? "").split(/\s+/).length;
  const score =
    (ROLE[answers.role] ?? 0) +
    (SIZE[answers.team_size] ?? 0) +
    Math.min(3, Math.floor(words / 25)) +
    (answers.interview === true ? 2 : 0) +
    (answers.current_tool ? 1 : 0);
  people.set(email, { email, score, role: answers.role, team_size: answers.team_size });
}

const ranked = [...people.values()].sort((a, b) => b.score - a.score);
const cohort = ranked.slice(0, COHORT_SIZE);
const quote = (value) => `"${String(value ?? "").replaceAll('"', '""')}"`;
const rows = cohort.map((person) => [person.email, person.score, person.role, person.team_size].map(quote).join(","));
writeFileSync("cohort.csv", ["email,score,role,team_size", ...rows].join("\n"));
console.log(`${ranked.length} applicants, ${cohort.length} in this cohort, lowest score ${cohort.at(-1)?.score}`);
  • Ubah ROLE dan SIZE sesuai siapa target versi pertama Anda. Tool untuk tim mungkin memberi skor tertinggi untuk 2-10; tool untuk perorangan, 1.
  • Use case mendapat satu poin per 25 kata, hingga 3. Panjang bukan berarti kualitas, tetapi orang yang menulis tiga kalimat biasanya benar-benar datang.
  • Memberi tanda kutip pada setiap nilai mencegah koma di dalam jawaban merusak CSV.
  • Simpan daftar yang sudah diundang di tempat yang mudah Anda temukan, seperti invited.txt yang Anda berikan dengan $(cat invited.txt).

Kirim undangan

Impor cohort.csv ke tool email Anda dan kirim undangan dari sana. Ikiform tidak mengirim email ke orang yang mengisi formulir. Email notifikasi hanya dikirim ke Anda dan tim Anda, jadi tidak ada autoresponder, email konfirmasi, atau double opt-in. Kirim semua itu dari tool email Anda, dipicu oleh webhook. Buat undangan pertama singkat: apa yang mereka dapat, satu hal yang ingin Anda minta mereka coba, dan cara menghubungi Anda. Setiap email notifikasi yang dikirim Ikiform kepada Anda punya Reply yang ditujukan ke pelamar, cocok untuk catatan pribadi singkat kepada orang-orang di dekat garis batas.

Pastikan berfungsi

  1. Di Allowed domains formulir, tambahkan domain situs Anda, ditambah localhost selama pengembangan. Biarkan kosong untuk menerima kiriman dari mana saja.
  2. Tempel definisi field di tab Fields formulir dan simpan.
  3. Kirim lamaran dengan use case satu baris. Formulir menampilkan pesan panjang minimum di bawah field itu, dan tidak ada yang disimpan.
  4. Kirim lamaran yang lengkap. Lamaran muncul di Submissions dengan setiap jawaban, dan interview berisi true atau false.
  5. Periksa inbox setiap penerima notifikasi. Masing-masing mendapat email berisi field-field tersebut, dan Reply ditujukan ke pengirim jika formulir punya field email.
  6. Kirim dengan honeypot terisi, _gotcha=bot. Kiriman tetap mendapat jawaban sukses, tetapi disimpan sebagai spam dan tidak memicu email atau webhook.
  7. Jalankan skrip penilaian dengan secret key dan pastikan cohort.csv mencantumkan lamaran uji Anda beserta skornya.

Jebakan

MasalahYang harus dilakukan
422 validation_failedKiriman tidak cocok dengan definisi field formulir. error.fields menyebutkan setiap field dan alasannya. Samakan name input dengan ID field.
422 dengan Unknown field untuk ref, utm_source, atau input tersembunyi lainDefinisi field tidak mencantumkannya. Tambahkan sebagai field text opsional, atau ganti namanya dengan awalan _ agar Ikiform membuangnya.
Jawaban panjang hilang setelah error di Next.jsReact mereset formulir setelah server action. Kembalikan jawaban dari action dan atur defaultValue di setiap input, seperti contoh Next.js.
Skrip penilaian melempar 401 unauthorized atau 403 forbidden401 berarti key tidak ada atau sudah dicabut; 403 berarti key-nya publishable atau tidak punya scope. Gunakan secret key (ik_sk_) dengan scope submissions:read.
Kiriman ada di dashboard tetapi tidak ada email atau webhook yang datangKiriman itu disimpan sebagai spam. Spam tidak memicu apa pun. Buka di filter Spam untuk melihat alasannya, dan tandai sebagai bukan spam untuk mengirim email dan webhook-nya.

Pertanyaan

Mentok di salah satu langkah, atau ingin panduan untuk situasi Anda? Email hello@ikiform.com.