Enlist

Quickstart

Waitlist infrastructure for speed. You own the form. We own email, delivery tracking, and analytics.

1. Get an API key

Create an account, then create your first key under API keys. Keys look like en_live_… and are server-side secrets.

The plaintext is shown once, at creation, and never again — only a hash is stored. Copy it straight into your secret manager; if you lose it, revoke the key and create another.

2. Create a waitlist

Once, in the dashboard or over the API. Reuse its id for every signup — you do not create a waitlist per person.

curl -X POST https://api.enlist.dev/v1/waitlists \
  -H "Authorization: Bearer $ENLIST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"my-launch"}'

3. First signup

The API is plain REST, so this is the whole integration. No dependency required.

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();
// Response is immediate. Email goes out with position in it. Dashboard tracks delivery.
// Re-submitting the same email returns the same position, no duplicate email.

Wiring it into a form

A Next.js route handler on your backend. Your API key stays server-side. The browser gets the response — you render what you want, we track what lands.

app/api/waitlist/route.ts
export async function POST(request: Request) {
  const { email } = await request.json();
  const utmSource = new URL(request.url).searchParams.get('utm_source') ?? undefined;

  const response = await fetch(
    `https://api.enlist.dev/v1/waitlists/${process.env.WAITLIST_ID}/signups`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.ENLIST_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ email, utm_source: utmSource }),
    },
  );

  const { position, total } = await response.json();
  return Response.json({ position, total });
}

Customize signup emails

Open settings to edit the signup email subject and body. Use variables: {{waitlist_name}}, {{position}}, {{total}}, {{email}}. Plain text with auto-wrapped HTML.

Prefer a typed client?

TypeScript SDK

npm install @enlistdev/sdksee the docs. Drop-in replacement for REST, so the integration you build now stays the same.

Keep the key server-side

Call this from your server, not from browser JavaScript — a key shipped to the browser can be read by anyone. If you must call it from the client, proxy through a route on your own backend, as in the example above.

What happens on signup

  • The signup gets the next position on that waitlist. Positions never shift.
  • A signup email goes out — "You're on the list — you're #47 of 1203." Delivery and open state show up in the dashboard.
  • Re-submitting the same email is a no-op: the original position comes back and no second email is sent.
  • utm_source is captured and rolled up in the stats endpoint.
  • A referral_code comes back in the response. Put it behind ?ref= for a share link, and pass whatever arrives in ?ref back as referral_code on the next signup — see Referrals.

Using an AI coding agent?

Skip all of this. Add the MCP server and prompt your agent: "Add a waitlist to this project." Your agent wires everything in one message.