Guias

Guias19 min de leitura

Como criar uma lista de espera para um novo SaaS

Monte uma lista de espera pré-lançamento na sua própria landing page: o formulário, proteção contra spam, duplicados, e-mails de confirmação e uma exportação limpa no dia do lançamento.

Preet SutharFundador, Ikiform

Coloque um formulário HTML simples na sua landing page que envia o e-mail do visitante a um backend de formulários, para você não precisar rodar um servidor antes do lançamento. Encaminhe cada inscrição por webhook para a ferramenta de e-mail com que você vai lançar, e deixe essa ferramenta enviar qualquer confirmação. No dia do lançamento, exporte a lista como CSV, remova os duplicados por e-mail e envie um único e-mail de lançamento.

Uma lista de espera é o teste mais barato de uma ideia de SaaS. Se desconhecidos deixam o e-mail por algo que ainda não existe, você tem um motivo para construir, e uma lista de pessoas para avisar quando sair. Este guia monta uma na sua própria landing page, sem banco de dados nem servidor, e a deixa pronta para o dia do lançamento.

Como funciona uma lista de espera

Toda lista de espera tem as mesmas três partes: um formulário na sua página, um lugar que armazena cada inscrição e a ferramenta pela qual você vai mandar e-mail para a lista. Aqui, o formulário envia ao Ikiform. O Ikiform verifica se o envio é spam, armazena, avisa você por e-mail e envia um webhook assinado. Um pequeno relay ou uma ferramenta de automação entrega o endereço à sua ferramenta de e-mail.

Como uma inscrição na lista de espera vai da sua landing page até a sua ferramenta de e-maillanding pageseu formulário,qualquer siteou criadorIkiformverifica spam,armazena ainscriçãopainelpesquisa,filtro, exportarCSVe-mail denotificaçãopara você, nãopara o inscritowebhookassinadosubmission.createdferramentade e-mailconfirma eenvia nolançamentoenviorelayComo uma inscrição na lista de espera vai da sua landing page até a sua ferramenta de e-maillandingpageseu formulário,qualquer siteou criadorIkiformverifica spam,armazena ainscriçãopainelpesquisa, filtro,exportar CSVe-mail denotificaçãopara você, não parao inscritowebhookassinadosubmission.createdferramenta dee-mailconfirma e envia nolançamentoenviorelay
  1. Um visitante digita o e-mail na sua landing page, e o formulário envia ao seu endpoint do Ikiform.
  2. O Ikiform confere o honeypot, a pontuação de spam e o limite de taxa, e então armazena a inscrição.
  3. A inscrição aparece no painel, onde você pode pesquisá-la e exportar a lista como CSV.
  4. O Ikiform envia e-mail para você e sua equipe. Ele nunca envia e-mail para quem se inscreveu.
  5. O Ikiform envia um webhook submission.created assinado.
  6. Um relay ou uma ferramenta de automação adiciona o endereço à sua ferramenta de e-mail, que envia qualquer confirmação e, depois, o e-mail de lançamento.
A ferramenta de e-mail é opcional no primeiro dia. Você pode coletar por semanas e importar um CSV depois; o webhook mantém os dois em sincronia desde o início.

Quatro formas de rodar uma lista de espera

Não existe uma única ferramenta certa. A tabela compara as quatro configurações mais comuns pelo que fazem bem e pelo que custam depois.

AbordagemBom emContras
SaaS de lista de espera, como LaunchList ou WaitlisterLinks de indicação, rankings e posição na fila, prontos para usar. Uma página hospedada se você ainda não tem site.Sua lista fica em outro produto. O widget tem a cara deles, a menos que você pague ou mude o estilo. O preço geralmente cresce com as inscrições.
Formulário incorporado de um criador de formulários, como Typeform ou TallyFluxos caprichados de uma pergunta, sem nenhum código.Um iframe que não combina com a sua página e carrega scripts próprios. Marca, limites de respostas e webhooks dependem do plano.
Seu próprio banco de dados e rota de APIControle total sobre os dados e qualquer lógica, como códigos de indicação.Você constrói proteção contra spam, limites de taxa, notificações e exportação, e precisa de um servidor mesmo que a landing page seja estática.
Backend de formulários, como o IkiformSeu próprio HTML em qualquer site. Filtro de spam, armazenamento, alertas por e-mail, webhooks assinados e exportação CSV sem servidor.Sem ranking de indicações e sem e-mails para quem se inscreve. Isso vem da sua ferramenta de e-mail.

Escolha um SaaS de lista de espera se a mecânica de indicações é o plano inteiro. Escolha um backend de formulários se você quer que o formulário tenha a cara do resto do seu site, que a lista seja portátil e que os e-mails saiam da ferramenta que você vai usar depois do lançamento de qualquer jeito. O resto deste guia usa o backend de formulários.

O formulário

Para começar, peça um e-mail e nada mais. Cada campo a mais custa inscrições, e você pode fazer perguntas depois, por e-mail, a quem já disse sim. Escolha sua stack abaixo; a escolha vale para todos os exemplos de código destes guias.

Cole o formulário onde a inscrição deve aparecer. Ele envia do jeito tradicional e redireciona, então funciona sem nenhum JavaScript.

index.html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label for="email">Email</label>
  <input id="email" name="email" type="email" placeholder="you@company.com" autocomplete="email" required />
  <input type="hidden" name="ref" value="homepage" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Join the waitlist</button>
</form>

Para manter os visitantes na página, adicione este script depois do formulário. Ele envia com fetch, troca o formulário pela frase de agradecimento e mostra os erros de campo do servidor.

waitlist.js
const form = document.querySelector('form[action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx"]');
const status = document.createElement("p");
status.setAttribute("role", "alert");
form.append(status);

const labels = {
  email: "Email",
};

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  const button = form.querySelector("button");
  button.disabled = true;
  status.textContent = "";
  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.replaceWith("You're on the list. We'll email you at launch.");
      return;
    }
    const fields = Object.entries(result.error.fields ?? {});
    status.textContent = fields.length
      ? fields.map(([id, messages]) => `${labels[id] ?? id}: ${messages[0]}`).join(" ")
      : result.error.message;
  } catch {
    status.textContent = "Couldn't reach the server. Try again.";
  }
  button.disabled = false;
});

Quer ir além com HTML? Leia Como adicionar um formulário de lista de espera em HTML puro a qualquer landing page.

  • O endpoint é a URL do seu formulário. Troque frm_xxxxxxxxxxxxxxxx pelo id que está no painel.
  • name="email" é o id do campo. Mantenha como email: o Ikiform o usa como endereço de reply-to dos e-mails de notificação e o confere contra domínios de e-mail descartáveis.
  • O input oculto _gotcha é o honeypot. As pessoas nunca o veem. Bots que preenchem todos os inputs se marcam como spam, e as versões no servidor o repassam sem mexer.
  • O input oculto ref registra de onde veio a inscrição. Mude o valor dele por página ou campanha.
  • Com uma definição de campos na aba Fields, um input que ela não lista é recusado com 422 e um erro unknown_field, então liste ref além de email, como abaixo.
  • Um envio de HTML simples redireciona o navegador depois do sucesso. Defina Redirect after submit nas configurações do formulário com a sua própria página de agradecimento, ou o Ikiform mostra a página hospedada dele.
Campos
{
  "version": 1,
  "title": "Waitlist",
  "fields": [
    { "id": "email", "type": "email", "label": "Email", "required": true, "placeholder": "you@company.com" },
    { "id": "ref", "type": "text", "label": "Source", "validation": { "max_length": 40 } }
  ]
}
As versões no servidor enviam com uma chave secreta, então o Ikiform pula Allowed domains e a verificação humana. Todo envio passa a vir do IP do seu servidor, e o limite Standard de 5 por minuto por visitante conta todos os seus visitantes juntos. Escolha o limite de taxa Relaxed ou Custom para um formulário que você envia por um servidor, e mantenha IKIFORM_SECRET_KEY em variáveis de ambiente só do servidor.

As versões no servidor repassam o header Origin do navegador, porque o Ikiform identifica os testes gratuitos de localhost por ele e o fetch do próprio servidor não envia nenhum.

Os guias de frameworks na documentação vão mais a fundo: Next.js, Astro, SvelteKit, Nuxt, React, Vue e HTML puro. O SDK encapsula a requisição do navegador como submit(), e o modelo de formulário de lista de espera gera a versão com quatro campos.

Campos que valem a pena pedir

O e-mail é o único campo de que você precisa. Se quiser organizar a lista antes do lançamento, adicione no máximo mais dois. Experimente a versão com quatro campos abaixo: ela roda o validador do Ikiform no seu navegador e mostra o que seria armazenado, ou o 422 que você receberia de volta.

Prévia

Roda o validador do Ikiform no seu navegador. Nada é enviado.

Os campos do modelo de lista de espera mais o ref, que a sua página preenche com um input oculto em vez de uma caixa visível. Cole na aba Fields do formulário e o Ikiform aplica tudo no servidor.
CampoPor que perguntarLembre-se
emailA lista em siObrigatório. Use type="email" para os celulares mostrarem o teclado certo
nameUm e-mail de lançamento personalizadoOpcional. O primeiro nome basta
roleConvidar fundadores primeiro, ou escrever um e-mail diferente para engenheirosUm select mantém as respostas limpas para filtrar
sourceDescobrir qual canal funciona antes de gastar com eleTexto livre é sincero, mas bagunçado. Um input oculto ref é mais limpo, veja abaixo
refContar as inscrições por página ou campanha sem perguntarUm input oculto. text opcional com max_length 40

Para acompanhar de onde vêm as inscrições sem perguntar, adicione um input oculto por página ou por campanha. Nomes que começam com _ são instruções para o Ikiform e nunca são armazenados, então use um nome simples como ref:

Inputs ocultos
<input type="hidden" name="ref" value="homepage" />
<input type="hidden" name="_redirect" value="https://example.com/thanks" />

Cada envio também registra a origem e o referrer da página, então você vê de qual página veio uma inscrição mesmo sem ref. O input _redirect manda o navegador para a sua própria página de agradecimento; ele só funciona com uma URL https: em um dos Allowed domains do formulário.

Se você vai enviar algo além da novidade do lançamento, como uma newsletter, adicione um checkbox de consentimento obrigatório com a promessa exata como rótulo. O Ikiform armazena true com o horário do envio, o que registra quando a pessoa concordou. O modelo de inscrição em newsletter tem um.

Mantenha os bots fora da lista

Um formulário público com campo de e-mail atrai bots em poucos dias. Inscrições falsas inflam seus números e, pior, seu e-mail de lançamento cai em spam traps que prejudicam a reputação do remetente. O Ikiform empilha quatro defesas; as duas primeiras já vêm ligadas, sem configuração.

DefesaO que ela barraConfiguração
HoneypotBots simples que preenchem todos os inputsO input oculto _gotcha no formulário acima
Pontuação de spamEndereços descartáveis (+2), repetições idênticas (+3), links e frases de spam conhecidas. Com 5, uma inscrição é armazenada como spamNenhuma
Limites de taxaScripts que enviam em loopLigado por padrão: Standard, 5 por minuto por visitante, e 300 por minuto para o formulário inteiro
Verificação humanaBots que rodam um navegador de verdadeDefina Spam check como Ikiform. Ele roda o Cloudflare Turnstile sem chaves para gerenciar

Spam é armazenado com uma resposta de sucesso normal, então o bot não aprende nada. Ele não dispara e-mail nem webhook e não conta no seu uso. Confira o filtro Spam no painel uma vez por semana no primeiro mês; se uma inscrição real cair lá, marque-a como não spam e o webhook dela é enviado na hora.

Adicione também o seu domínio em Allowed domains. Envios de outros sites passam a ser marcados como spam, ou recusados com 403 origin_not_allowed se você escolher Reject. Com a verificação humana ligada, o formulário HTML não precisa de mudança. A versão com fetch precisa pedir um token antes com challenge() de @ikiform/sdk; a documentação de spam mostra as quatro linhas.

Divulgações de lançamento chegam em rajadas. Se você divulgar a página num lugar grande, o limite de 300 por minuto do formulário inteiro pode bater antes do limite de qualquer visitante. Aumente-o com um limite de taxa Custom na véspera. Um escritório ou uma conferência inteira compartilha um único IP, então o Relaxed, 20 por minuto por visitante, combina com esses dias.

A mesma pessoa, duas vezes

As pessoas se inscrevem de novo por outra aba, por um segundo dispositivo ou uma semana depois, quando esquecem. O Ikiform armazena todo envio aceito e não mescla nem rejeita repetições pelo e-mail. Veja o que acontece com uma repetição:

CasoResultado
Mesmo e-mail, campos diferentes ou mais de 24 horas depoisUm segundo envio normal
Os mesmos campos de novo em até 24 horas+3 na pontuação de spam, abaixo de 5, então um segundo envio normal
Uma repetição idêntica de um domínio de e-mail descartável+3 e +2, o que chega a 5: armazenado como spam

Então espere alguns duplicados e remova-os onde a lista for parar. Três lugares funcionam, do menor para o maior esforço:

  1. Na ferramenta de e-mail. A maioria das ferramentas de e-mail identifica os contatos pelo endereço, então adicionar o mesmo endereço duas vezes atualiza um contato em vez de criar dois.
  2. Na exportação. Remova os e-mails duplicados do CSV antes de importá-lo em qualquer lugar. Há duas formas logo abaixo.
  3. No seu relay. Passe o endereço para minúsculas e tire os espaços, depois insira-o com uma restrição de unicidade ou um upsert, para que uma repetição não faça nada.

Quer evitar que um clique duplo crie duas linhas? Envie uma chave de idempotência por carregamento de página com a versão fetch. Isso impede repetições acidentais; não impede alguém de se inscrever duas vezes de propósito.

Confirmando as inscrições

O Ikiform não envia e-mail para quem preenche o formulário. Os e-mails de notificação vão só para você e sua equipe, então não há resposta automática, e-mail de confirmação nem double opt-in. Envie esses e-mails pela sua ferramenta de e-mail, disparados por um webhook. Isso é proposital: a ferramenta que envia o seu e-mail de lançamento também deve enviar a confirmação, para que descadastros e devoluções fiquem registrados num lugar só.

Duas formas de conectar o Ikiform a ela:

  • Sem código. Adicione um endpoint de webhook que aponta para uma ferramenta de automação e use a ação dela para a sua ferramenta de e-mail. Veja Zapier, Make ou n8n.
  • Um pequeno relay. Um route handler em qualquer host Node verifica a assinatura e chama a API da sua ferramenta de e-mail. O exemplo abaixo é uma rota do Next.js; o mesmo código roda num Cloudflare Worker.
app/api/waitlist/route.ts
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";

export async function POST(request: Request) {
  const payload = await request.text();
  try {
    const event = await verifyWebhook({
      payload,
      headers: request.headers,
      secret: process.env.IKIFORM_WEBHOOK_SECRET!,
    });
    if (event.type !== "submission.created") return new Response(null, { status: 204 });
    const email = String(event.data.fields.email ?? "").trim().toLowerCase();
    if (!email) return new Response(null, { status: 204 });
    const response = await fetch(process.env.LIST_API_URL!, {
      method: "POST",
      headers: {
        authorization: `Bearer ${process.env.LIST_API_KEY}`,
        "content-type": "application/json",
      },
      body: JSON.stringify({
        email,
        name: event.data.fields.name ?? null,
        tags: ["waitlist", String(event.data.fields.role ?? "unknown")],
      }),
    });
    if (response.status === 409) return new Response(null, { status: 204 });
    if (!response.ok) return new Response(`List answered ${response.status}`, { status: 502 });
    return new Response(null, { status: 204 });
  } catch (error) {
    if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
    throw error;
  }
}
  • verifyWebhook confere a assinatura e o timestamp, então ninguém mais consegue adicionar endereços por esta URL.
  • Troque o fetch pela chamada de adicionar contato da sua ferramenta de e-mail. A maioria recebe um e-mail e uma lista ou tag, e envia o próprio e-mail de confirmação quando o double opt-in está ligado naquela ferramenta.
  • Responder 204 diz ao Ikiform que a entrega deu certo. Um 502 faz o Ikiform tentar de novo, com o mesmo webhook-id, então uma instabilidade curta na sua ferramenta de e-mail não faz você perder inscrições.

Adicione o endpoint em Webhooks no painel e copie o segredo whsec_ dele para IKIFORM_WEBHOOK_SECRET. Um workspace pode ter 3 endpoints. A documentação de webhooks explica as novas tentativas e o payload, e seu próprio servidor tem uma versão que armazena cada entrega uma única vez.

Dia do lançamento

Uma lista de espera da primeira inscrição ao dia do lançamentopágina no arformulárioenvia aoIkiformtoda semanadivulgue, leiaas inscriçõesuma semanaantesexportar,deduplicar,segmentardia dolançamentoum e-mail paraa listasemanasUma lista de espera da primeira inscrição ao dia do lançamentopágina no arformulário envia ao Ikiformtoda semanadivulgue, leia as inscriçõesuma semana antesexportar, deduplicar, segmentardia do lançamentoum e-mail para a listasemanas
  1. A página entra no ar com o formulário enviando ao Ikiform.
  2. Toda semana, divulgue a página e leia as novas inscrições e as respostas.
  3. Uma semana antes do lançamento, exporte a lista como CSV, remova os duplicados e divida por função.
  4. No dia do lançamento, envie um único e-mail para a lista pela sua ferramenta de e-mail.
A maior parte do trabalho é a exportação uma semana antes. O e-mail de lançamento em si é curto.

Exporte a lista

Abra Submissions no painel, escolha o formulário da lista de espera, defina o status como Received e o período como All time, e então escolha Export CSV. As colunas são id, created_at, form e status, e depois uma coluna por campo. O arquivo é UTF-8 com byte order mark, então os acentos sobrevivem ao Excel. Uma exportação tem até 100.000 linhas; passando disso, pagine pela API REST.

Para remover os duplicados no Google Sheets, importe o CSV e use esta fórmula numa aba vazia, com o e-mail na coluna E:

Google Sheets
=SORTN(A2:F, 9^9, 2, LOWER(TRIM(E2:E)), TRUE)

Ela mantém uma linha por endereço, ignorando maiúsculas e espaços, ordenada por e-mail. Ou rode isto com Node na pasta que contém a exportação. Ele mantém a primeira linha de cada endereço e supõe que não há vírgulas dentro dos valores, o que vale para um formulário só com e-mail:

dedupe.mjs
import { readFileSync, writeFileSync } from "node:fs";

const [header = "", ...rows] = readFileSync("submissions.csv", "utf8").replace(/^\uFEFF/, "").trim().split(/\r?\n/);
const columns = header.split(",");
const emailAt = columns.indexOf("email");
const seen = new Set();
const unique = rows.filter((row) => {
  const email = row.split(",")[emailAt]?.trim().toLowerCase();
  if (!email || seen.has(email)) return false;
  seen.add(email);
  return true;
});

writeFileSync("waitlist.csv", [header, ...unique].join("\n"));
console.log(`${rows.length} rows, ${unique.length} unique emails`);

Envie o e-mail de lançamento

Envie pela sua ferramenta de e-mail, não pela sua caixa pessoal. Uma caixa pessoal mandando para centenas de endereços de uma vez parece spam para os provedores de e-mail, e você vai querer o tratamento de descadastros a partir do momento em que enviar. Seja breve:

E-mail de lançamento
Subject: Acme is live. You're in.

Hi Ada,

You joined the Acme waitlist in the spring. Today it's open.

Sign in here: https://acme.example/start

You get the first 30 days free because you waited.
Reply to this email if anything breaks. It comes to me.

Ada, founder of Acme
  • Diga o que está no ar e coloque o link direto para a página de cadastro ou de login.
  • Recompense a espera, com um trial mais longo ou um desconto, e diga isso uma vez só.
  • Envie em nome de uma pessoa. As respostas são o melhor feedback inicial que você vai ter.
  • Mande primeiras linhas diferentes para fundadores e engenheiros se você coletou role. É por isso que vale coletar.
Se você coletou por meses sem confirmar os endereços, envie primeiro para uma fatia pequena e acompanhe a taxa de devolução. Uma lista cheia de erros de digitação e endereços mortos prejudica o domínio de onde você vai mandar os e-mails do produto.

Escolha o seu caso

Os passos acima funcionam em qualquer lugar. Estas páginas mostram o que muda para um lançamento ou um criador de sites específico.

Confira se funciona

Siga estes passos antes de divulgar a página. O Ikiform não tem plano gratuito. Testar é grátis: um workspace sem plano aceita 50 envios de teste por mês a partir de localhost. Para entrar no ar, escolha o Pro a partir de US$ 6/mês para 500 envios, ou pague uma vez: 10.000 envios por US$ 49 ou 50.000 envios por US$ 149, com créditos que nunca expiram. Veja os preços.

  1. Em Allowed domains do formulário, adicione o domínio do seu site, mais localhost enquanto desenvolve. Deixe vazio para aceitar envios de qualquer lugar.
  2. Preencha o formulário na sua página e envie. Você cai na sua página de agradecimento, ou na hospedada pelo Ikiform.
  3. Abra Submissions no painel. O novo envio está lá com todos os campos que você enviou.
  4. Confira a caixa de entrada de cada destinatário das notificações. Cada um recebe um e-mail com os campos, e Reply vai para quem enviou quando o formulário tem um campo email.
  5. Se você adicionou um webhook, abra o endpoint em Webhooks. A entrega mostra succeeded com a resposta do seu servidor.
  6. Envie com o honeypot preenchido, _gotcha=bot. Ele ainda recebe uma resposta de sucesso, mas o envio é armazenado como spam e não dispara e-mail nem webhook.
  7. Não chegou nada? Mude o filtro de status do painel para Spam. Spam recebe a mesma resposta de sucesso de um envio real.
  8. Inscreva-se duas vezes com o mesmo endereço e confirme que a sua ferramenta de e-mail continua mostrando um único contato.

Solução de problemas

ProblemaO que fazer
403 origin_not_allowed, ou todo envio cai no spamO domínio da página não está em Allowed domains do formulário. Adicione-o, mais localhost para testes locais. Subdomínios de um domínio listado são permitidos.
429 rate_limitedUm visitante enviou mais do que o limite de taxa do formulário permite. O Standard permite 5 por minuto por visitante. Espere o Retry-After, ou escolha Relaxed ou Custom para formulários que enviam a partir de um único IP compartilhado.
403 challenge_requiredA verificação de spam do formulário está como Ikiform e um envio por fetch não tinha token. Obtenha um com challenge(formId) de @ikiform/sdk antes de cada envio, ou desligue a verificação.
O envio está no painel, mas nenhum e-mail ou webhook chegouEle foi armazenado como spam. Spam não envia nada. Abra-o no filtro Spam para ver os motivos e marque-o como não spam para enviar o e-mail e os webhooks dele.
O navegador cai na página de agradecimento do Ikiform em vez da suaO _redirect é ignorado, a menos que o domínio dele esteja em Allowed domains e ele seja uma URL https:. Adicione o domínio, ou defina Redirect after submit nas configurações do formulário.
O mesmo e-mail aparece duas vezesÉ o esperado. O Ikiform não remove duplicados por e-mail. Remova as repetições na exportação, ou faça upsert por e-mail no seu relay ou na sua ferramenta de e-mail.
As inscrições chegam no Ikiform, mas não na ferramenta de e-mailAbra o endpoint em Webhooks e leia o log de entregas. Um 400 significa que o segredo está errado; um 502 com o status da sua ferramenta significa que a ferramenta recusou o contato.
402 test_limit_reachedO workspace não tem plano e já usou os 50 testes gratuitos de localhost deste mês. Escolha um plano ou use Send test no painel.

Perguntas

Travou em algum passo ou quer um guia para o seu caso? Envie um e-mail para hello@ikiform.com.