What is waitlist infrastructure?
The difference between a landing page with a form and production-grade waitlist infrastructure, and why it matters when your launch hits the front page.
The typical waitlist setup
Most developers wire up a waitlist the same way: an email input, a form POST to a serverless function, an INSERT INTO signups call, and a confirmation email if there's time. It works fine until launch day.
Front page of Hacker News. Five thousand visitors in two hours. Now you have:
- Duplicate signups from double-clicks and network retries
- Position numbers that mean nothing because they were assigned in a race
- No idea how many of those signups actually received a welcome email
- No way to query "who joined from Product Hunt vs. the newsletter"
What infrastructure actually handles
Position assignment inside a transaction. A position number only means something if it's assigned atomically. If two requests hit your server simultaneously, both users should not get position 47. One gets 47, one gets 48. No gap, no collision.
Idempotency on retries. Networks are unreliable. Browsers retry. Forms get submitted twice. If the same email submits again, the second request should return the same position as the first, not create a duplicate row and send a second welcome email.
Source attribution baked in. The signup call accepts utm_source and referrer. Both are stored and rolled up in stats. When you want to know which channel converted best, the data is already there.
Custom metadata per signup. Pass any additional context at signup time (plan tier, A/B cohort, campaign) as key/value pairs. Max 10 keys, stored permanently on the signup row.
Referral tracking as data, not decoration. Each signup gets a code. Pass someone else's back and their count goes up in the same transaction as the insert. Who referred whom is infrastructure; what a referral is worth is a product decision, so nothing is rewarded on your behalf and no one's position moves.
A response shape your UI can use immediately. { id, position, total, referral_code }: everything you need to render "you're #47 of 1,203" and a share link is in the signup response. No polling, no second request.
What the API actually looks like
const response = await fetch('https://api.enlist.dev/v1/waitlists/WAITLIST_ID/signups', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.ENLIST_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'user@example.com',
utm_source: 'producthunt',
}),
});
const { id, position, total } = await response.json();
// position: 47, total: 1203
// Re-submitting the same email returns the same position. No duplicate email sent.One endpoint. One response. The welcome email goes out automatically and delivery state is tracked (delivered, opened, clicked) and queryable per signup.
The line between a form and infrastructure
A form collects data. Infrastructure enforces invariants, tracks state, and handles failure transparently.
The distinction only matters at scale. Until it does, you won't know you needed it. By the time you notice the race conditions and duplicate positions, you've already shipped bad data to real users.
Enlist is that infrastructure, exposed as a REST API. Your form stays yours. You own the design, the domain, and the copy. Enlist owns the queue, the email, and the tracking.