# Como adicionar uma lista de espera a um site no Framer

> Duas formas de coletar inscrições na lista de espera no Framer: o formulário do Framer com webhook para o Ikiform, ou um code component que mantém os limites por visitante e as verificações de spam.

Publicado em 2026-10-25 · https://ikiform.com/pt-br/guides/waitlist/framer

Os formulários do Framer não conseguem enviar para uma URL externa, mas conseguem mandar cada envio para um webhook, então o caminho sem código é o formulário do Framer com o seu endpoint do Ikiform como webhook. Para um lançamento público, use um pequeno code component: o navegador envia diretamente, então o limite de taxa por visitante, os domínios permitidos e o honeypot do Ikiform continuam funcionando.

O Framer é onde muitas páginas de pré-lançamento são construídas, porque a página em si sai numa tarde. O formulário da lista de espera é onde a coisa fica menos óbvia. O bloco de formulário do Framer não consegue mudar o `action`, então você escolhe entre dois caminhos que se comportam de forma diferente quando o tráfego real chega.

## O que muda no Framer

- **Sem action personalizado.** Um formulário do Framer envia para os destinos do Framer: o armazenamento dele, e-mail ou um **Webhook**. Um webhook é o único jeito de chegar a um backend externo sem código.
- **Os webhooks vêm dos servidores do Framer, não do visitante.** O Ikiform vê os endereços IP do Framer e nenhum header `Origin`. Isso desliga três proteções: domínios permitidos, o limite de taxa por visitante e a verificação humana.
- **Novas tentativas.** O Framer trata qualquer coisa diferente de um `2xx` como falha e tenta de novo até cinco vezes. Ele não segue redirecionamentos. O Ikiform responde a um envio JSON com `201`, então uma inscrição armazenada é um sucesso na primeira vez.
- **Code components são React de verdade.** Eles rodam no navegador do visitante no site publicado, então um formulário dentro de um deles envia como qualquer outra página web.

Diagrama: Duas formas de uma inscrição numa lista de espera do Framer chegar ao Ikiform

1. No caminho do webhook, o visitante envia o formulário do Framer e os servidores do Framer encaminham os campos ao Ikiform como JSON.
2. O Ikiform vê os servidores do Framer como remetente, então não consegue diferenciar os visitantes nem conferir em que site o formulário está.
3. Com o code component, o navegador do visitante envia direto ao Ikiform.
4. O Ikiform vê o IP de cada visitante e o seu domínio, então todas as proteções se aplicam.

Os dois caminhos armazenam a mesma inscrição. A diferença é quem o Ikiform acha que a enviou.

## Escolha um caminho

|   | Formulário do Framer com webhook | Code component |
| --- | --- | --- |
| Configuração | Dois campos na barra lateral direita | Um arquivo de código, colado uma vez |
| Estilo | Os estilos e estados de formulário do Framer | Estilos inline que você edita, ou suas próprias classes |
| Limite de taxa por visitante | Não. Todos os envios compartilham os IPs do Framer | Sim, 5 por minuto cada |
| Domínios permitidos | Deixe vazio | Defina com seus domínios do Framer e personalizados |
| Honeypot | Não | Sim |
| Verificação humana | Desligada | Desligada, porque envia com `fetch` |
| Rastreamento de origem | Nenhuma | `?ref=` da URL, ou um padrão por instância |

O caminho do webhook serve para uma página que você divulga para algumas centenas de pessoas. Se você está prestes a divulgar a página num lugar grande, use o code component: uma rajada de inscrições de bots pelos servidores do Framer só pode ser barrada pela pontuação de spam.

## Caminho 1: o formulário do Framer com webhook

1. Adicione à página um formulário com um input **Email**. Nas configurações do input, defina o nome dele como `email`. O Framer envia o nome de cada input como chave JSON, e o Ikiform usa `email` para o reply-to e a verificação de domínios descartáveis.
2. Selecione o formulário. Ao lado de **Send To**, escolha **Add…** › **Webhook** e cole o seu endpoint, `https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx`. O signing secret é opcional; o Ikiform não confere a assinatura do Framer.
3. No Ikiform, deixe **Allowed domains** vazio e a verificação de spam desligada. Com qualquer um dos dois ligado, todo envio do Framer é sinalizado ou recusado, porque não traz origem e não consegue rodar uma verificação de navegador.
4. Defina o [limite de taxa](https://ikiform.com/pt-br/docs/rate-limits.md) como Relaxed, ou Custom no dia do lançamento. O Standard conta todos os envios do Framer como um único visitante, e o formulário inteiro tem limite de 300 por minuto.
5. Estilize o estado de sucesso do formulário no Framer. Ele aparece quando o Ikiform responde `201`.

> Um envio que o Ikiform recusa, como um sem e-mail com uma definição de campos ativa, falha do mesmo jeito nas cinco tentativas do Framer. Confira a entrega no Framer se as inscrições pararem de chegar.

## Caminho 2: um code component

No painel **Assets**, abra **Code**, crie um arquivo de código chamado `WaitlistSignup` e cole isto. Arraste-o para a página e defina **Form ID** no painel de propriedades.

`WaitlistSignup.tsx`:

```tsx
import { addPropertyControls, ControlType } from "framer";
import { type CSSProperties, type FormEvent, useRef, useState } from "react";

const box: CSSProperties = {
  font: "inherit",
  padding: "12px 14px",
  borderRadius: 10,
  border: "1px solid rgba(0, 0, 0, 0.15)",
};

interface Props {
  formId: string;
  buttonLabel: string;
  joinedText: string;
  source: string;
}

export default function WaitlistSignup({ formId, buttonLabel, joinedText, source }: Props) {
  const [status, setStatus] = useState<"idle" | "sending" | "joined">("idle");
  const [error, setError] = useState("");
  const key = useRef("");

  async function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    const body = new FormData(event.currentTarget);
    const ref = new URLSearchParams(window.location.search).get("ref");
    body.set("ref", (ref || source).slice(0, 40));
    key.current ||= crypto.randomUUID();
    setStatus("sending");
    setError("");
    try {
      const response = await fetch(`https://ikiform.com/f/${formId}`, {
        method: "POST",
        headers: { Accept: "application/json", "Idempotency-Key": key.current },
        body,
      });
      const result = await response.json();
      if (result.ok) return setStatus("joined");
      setError(result.error.fields?.email?.[0] ?? result.error.message);
    } catch {
      setError("Couldn't reach the server. Try again.");
    }
    setStatus("idle");
  }

  if (status === "joined") return <p role="status" style={{ margin: 0 }}>{joinedText}</p>;

  return (
    <form onSubmit={onSubmit} style={{ display: "flex", flexWrap: "wrap", gap: 8, width: "100%" }}>
      <input
        name="email"
        type="email"
        placeholder="you@company.com"
        aria-label="Email"
        autoComplete="email"
        required
        style={{ ...box, flex: "1 1 220px" }}
      />
      <input name="_gotcha" type="text" tabIndex={-1} autoComplete="off" hidden />
      <button type="submit" disabled={status === "sending"} style={{ ...box, cursor: "pointer" }}>
        {status === "sending" ? "Joining" : buttonLabel}
      </button>
      {error ? <p role="alert" style={{ flexBasis: "100%", margin: 0 }}>{error}</p> : null}
    </form>
  );
}

addPropertyControls(WaitlistSignup, {
  formId: { type: ControlType.String, title: "Form ID", defaultValue: "frm_xxxxxxxxxxxxxxxx" },
  buttonLabel: { type: ControlType.String, title: "Button", defaultValue: "Join the waitlist" },
  joinedText: { type: ControlType.String, title: "Joined text", defaultValue: "You're on the list." },
  source: { type: ControlType.String, title: "Source", defaultValue: "framer" },
});
```

- O painel de propriedades expõe o texto do botão, o texto de inscrito e uma **Source** padrão. Coloque uma instância no hero com `hero` e outra no rodapé com `footer`, e você vai ver qual converte.
- Um `?ref=` na URL da página vence o padrão, então os links de campanha se marcam sozinhos.
- A chave de idempotência é criada no primeiro envio e reutilizada nas novas tentativas da mesma visita, então um toque duplo no celular armazena uma única inscrição.
- `window` só é lido dentro do handler de envio. O Framer renderiza os componentes no servidor primeiro, e mexer em `window` durante a renderização quebraria isso.

### Configurações do Ikiform para o componente

Adicione estes campos na aba **Fields** do formulário, para o e-mail ser obrigatório e o `ref` ser permitido:

`Campos`:

```json
{
  "version": 1,
  "title": "Waitlist",
  "fields": [
    { "id": "email", "type": "email", "label": "Email", "required": true },
    { "id": "ref", "type": "text", "label": "Source", "validation": { "max_length": 40 } }
  ]
}
```

- Em **Allowed domains**, adicione `your-project.framer.app` e o seu domínio personalizado. Nunca adicione `framer.app` sozinho; todo site do Framer passaria a corresponder.
- Mantenha a verificação de spam desligada. O componente envia com `fetch` e não busca um token, então um formulário com a verificação ligada responde `403 challenge_required`.
- Mantenha os limites de taxa Standard. Agora cada visitante é contado separadamente.

## Confira se funciona

1. Publique o site. Webhooks e code components só enviam a partir do site publicado, não do preview do editor.
2. Envie o formulário no seu domínio `framer.app` e de novo no seu domínio personalizado.
3. Abra **Submissions** no painel. O novo envio está lá com todos os campos que você enviou.
4. Para o componente, abra a página com `?ref=test` e confira se a inscrição mostra `ref` como `test`.
5. 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`.
6. Não chegou nada? Mude o filtro de status do painel para **Spam**. Spam recebe a mesma resposta de sucesso de um envio real.

## Pegadinhas

| Problema | O que fazer |
| --- | --- |
| Toda inscrição via webhook está no spam com `origin_mismatch` | **Allowed domains** está definido, e os envios do servidor do Framer não têm origem. Limpe-o para este formulário ou troque para o code component. |
| O Framer acusa entregas de webhook com falha | O Ikiform recusou o envio: `403 challenge_required` com a verificação de spam ligada, ou `422` porque o nome de um input não é `email`. Corrija a causa; as novas tentativas falham do mesmo jeito. |
| `429 rate_limited` | Um 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. |
| O componente mostra `Unknown field "ref"` | A definição de campos não lista `ref`. Adicione-o como na definição acima. |
| `403 origin_not_allowed`, ou todo envio cai no spam | O 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. |

A configuração completa do Framer, incluindo a versão de formulário de contato, está no [guia do Framer na documentação](https://ikiform.com/pt-br/docs/guides/framer.md). Exportar a lista e enviar o e-mail de lançamento estão no [guia principal](https://ikiform.com/pt-br/guides/waitlist.md#launch-day).

## Perguntas

### Um formulário do Framer consegue enviar para uma URL externa?

Não como action de formulário. Os formulários do Framer enviam para os destinos do próprio Framer, e um deles é um webhook para qualquer URL https. Use isso para o Ikiform, ou renderize seu próprio formulário com um code component.

### Por que as inscrições da minha lista de espera no Framer são marcadas como spam?

No caminho do webhook, o Framer envia a partir dos servidores dele sem header Origin, então um formulário com Allowed domains definido marca todo envio como origin_mismatch. Deixe Allowed domains vazio em formulários via webhook.

### As inscrições via webhook do Framer contam nos limites de formulário do Framer?

O Framer diz que os envios de formulário são ilimitados, então as inscrições via webhook não batem em nenhum limite do Framer. O caminho do code component nem usa os formulários do Framer.

### O Ikiform é gratuito?

Não, não há plano gratuito. Testar é grátis: você pode enviar 50 envios de teste por mês a partir de localhost antes de pagar. Para entrar no ar, o Pro começa em US$ 6/mês para 500 envios, ou você pode pagar uma vez 10.000 envios por US$ 49 ou 50.000 envios por US$ 149, com créditos que nunca expiram.

## Mais guias

- [Lista de espera para um novo SaaS](https://ikiform.com/pt-br/guides/waitlist.md): 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.
- [Lista de espera no Webflow](https://ikiform.com/pt-br/guides/waitlist/webflow.md): Aponte um Form Block do Webflow para o Ikiform, marque as inscrições pela origem com um campo oculto e mantenha o estado de sucesso do Webflow com um script curto para todos os formulários.
- [Lista de espera no Carrd](https://ikiform.com/pt-br/guides/waitlist/carrd.md): Colete inscrições na lista de espera numa página do Carrd: um formulário Send to URL no Pro Plus com rastreamento de UTM, ou um formulário HTML incorporado com honeypot no Pro Standard.
- [Lista de espera em HTML puro](https://ikiform.com/pt-br/guides/waitlist/html.md): Uma página de lista de espera completa em HTML puro, sem build, que funciona com o JavaScript desligado, mostra um agradecimento na própria página quando carrega e tem plano B quando precisa.
- [Lista para lançamento no Product Hunt](https://ikiform.com/pt-br/guides/waitlist/product-hunt.md): Colete uma lista semanas antes do Product Hunt, marque cada inscrição pelo canal e envie o e-mail do dia do lançamento em duas ondas, no ritmo da virada do horário do Pacífico.
- [Inscrição para beta com triagem](https://ikiform.com/pt-br/guides/waitlist/beta-signup.md): Um formulário de candidatura ao beta com perguntas de triagem que o servidor exige, e um script que pontua os candidatos e escolhe cada turma da lista.
