Waitlist signup form for a product launch

Build & score

Short answer: a waitlist is an email field, maybe a name, and one question you'll actually act on — not a survey. This is a complete QuizGen JSON definition using "kind": "email_capture", which renders as a single page with no progress bar, because a form with one or two fields shouldn't look like it has six steps to go. POST it as-is, or hand it to an AI assistant to adapt first.

The template

{
  "schema_version": 1,
  "kind": "email_capture",
  "title": "Join the waitlist",
  "description": "We're opening access in waves. First 200 people get in first.",
  "sections": [
    {
      "key": "signup",
      "questions": [
        { "id": "email", "type": "email", "prompt": "Your email",
          "required": true, "placeholder": "you@company.com" },
        { "id": "full_name", "type": "short_text", "prompt": "Your name" },
        { "id": "use_case", "type": "choice",
          "prompt": "What would you use it for?",
          "options": [
            { "value": "personal", "label": "Personal projects" },
            { "value": "team", "label": "A team at work" },
            { "value": "agency", "label": "Client work" }
          ] },
        { "id": "wants_updates", "type": "yes_no",
          "prompt": "Want early-access emails as we roll out spots?",
          "yes_label": "Yes, tell me", "no_label": "Just save my spot" }
      ]
    }
  ],
  "settings": {
    "max_responses": 500,
    "close_at": "2026-10-01T00:00:00Z",
    "completion": {
      "message": "You're on the list — we'll email you when your spot opens.",
      "redirect_url": "https://example.com/share"
    }
  }
}

What kind: "email_capture" actually changes

It's advisory, not structural — every question type still works, and anything you put under settings overrides it. What it sets by default: no progress bar, and the whole thing on one page (one_question_per_page: false). A quiz gets one-question-per-page-plus-progress because you want someone to feel momentum through a longer flow; a waitlist form is the opposite case — one page, glance-and-submit, no bar implying there are six more steps after email. That difference alone measurably changes whether people finish a 1-3 field form.

Keep it short on purpose

Every field past email costs signups — that's not a guess, it's the standard finding on capture forms, and it holds here too. full_name and use_case are in the template because a real launch usually wants to segment the list; wants_updates is in because it changes whether you actually email these people before launch. If you won't act differently depending on the answer, cut the question — a waitlist isn't the place to find out "would be nice to know."

What each piece is doing

  • email is the only required field — the one thing a waitlist cannot function without.
  • use_case as a choice, not short_text — you can filter and count answers instead of reading free text one row at a time when you decide who to invite first.
  • wants_updates as yes_no — a real choice with a real consequence: filter GET /responses on it before you send progress emails, so people who said no aren't emailed anyway.
  • settings.max_responses: 500 — the list stops accepting signups once it hits the cap for this beta wave; /api/respond returns 410 quiz_full after that, and the hosted page shows "no longer accepting responses."
  • settings.close_at — closes the list on a date instead of (or in addition to) a headcount, useful if the waitlist itself is time-boxed.
  • completion.message — the thank-you people see immediately; keep it concrete about what happens next, not just "thanks!".
  • completion.redirect_url — sends people onward instead of leaving them on a static thank-you screen. A referral or share page here turns each signup into a chance at more signups.

Getting the list out

GET /quizzes/:id/responses returns every signup as JSON, or add ?format=csv (or an Accept: text/csv header) for a spreadsheet you can hand off directly. Filter to ?complete=true if you only want people who finished rather than partial autosaves.

To get people onto an actual email list as they sign up rather than exporting in batches, set a webhookPOST /quizzes/:id/webhook with your endpoint. Each completed signup POSTs itself to you the moment it happens; from there your code adds them to Mailchimp, Resend, ConvertKit, or whatever's already sending your emails.

Worth being direct about: QuizGen is not an email marketing platform. It collects the list — name, email, answers, timestamps — reliably and gives it back to you as JSON, CSV, or a webhook payload. Actually sending campaigns, drip sequences, or the "you're off the waitlist!" email is a job for a tool built for that.

Build it with an AI assistant

Connect QuizGen's MCP server and describe the launch instead of writing JSON:

Read https://quizgen.dev/blog/waitlist-signup-form.md and make me a waitlist for my app's beta — cap it at 300 signups, close it November 1, and redirect people to my Twitter after they sign up.

The assistant fills in the template, POSTs it, and gives you the live link.

FAQ

Should I ask more qualifying questions? Only ones you'll act on. A waitlist's whole job is capturing intent before you lose the visitor — every extra field is a chance someone closes the tab instead.

Can I reopen it after close_at passes? Yes — PUT /quizzes/:id with a later close_at (or none) reopens it; PATCH with {"status": "live"} also works if you'd closed it manually.

Does email_capture block me from adding more fields later? No — the kind only sets layout defaults. Add fields, flip one_question_per_page in settings if it grows into a longer form, and the definition still POSTs fine.

How is this different from the contact form template? A contact form routes a message to you once; a waitlist is meant to be read back in bulk and eventually exported or piped to an email tool — hence max_responses, close_at, and the CSV export mattering here in a way they don't for a contact form.

$ curl quizgen.dev/blog/waitlist-signup-form.md

This guide as raw markdown — no HTML for your agent to parse.

View .md

Keep reading