# Waitlist signup form for a product launch

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

```json
{
  "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 [webhook](/blog/quiz-response-webhooks) —
`POST /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](/mcp)
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](/blog/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.
