# Formulários do Framer: quando adicionar um backend de formulários, e como

> Os formulários do Framer já enviam para e-mail, Sheets ou um webhook; adicione o Ikiform como webhook para envios armazenados e filtrados, alertas à equipe e mais destinos.

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

Os formulários nativos do Framer enviam cada resposta para e-mail, Google Sheets ou um webhook, com proteção contra spam embutida, então para uma caixa de entrada ou uma planilha talvez você não precise de mais nada. Adicione o Ikiform como destino de webhook quando quiser cada envio armazenado com status de spam, validado contra os seus campos, enviado por e-mail à equipe e encaminhado para vários webhooks assinados. Como o Framer envia a partir dos servidores dele, afrouxe as verificações de origem do formulário ou coloque no meio um pequeno relay que verifica a assinatura do Framer.

## O que muda no Framer

O Framer é o único criador deste guia sem um limite do qual escapar. Os formulários dele podem enviar para e-mail, Google Sheets ou qualquer webhook, e o Framer tem proteção contra spam embutida que marca ou bloqueia envios suspeitos. Os webhooks dele são assinados: cada requisição traz um header `Framer-Signature`, um HMAC-SHA256 do corpo e do id do envio, e um `Framer-Webhook-Submission-Id`. O Framer tenta de novo até cinco vezes quando o seu endpoint não responde `2xx`, e não segue redirecionamentos.

Então seja honesto sobre por que você adicionaria um backend de formulários. Veja o que cada lado cobre:

| Você quer | Só o Framer | Com o Ikiform como webhook |
| --- | --- | --- |
| Envios numa caixa de entrada ou numa planilha | Sim | Sim, mais o painel |
| Um registro pesquisável com spam marcado, e exportação CSV | Sua caixa de entrada ou planilha é o registro | Sim |
| Verificações no servidor de tipos, tamanhos e opções | Não | Sim, com uma definição de campos |
| Alertas para várias pessoas, com a resposta indo para quem enviou | Destino de e-mail | Até 10 destinatários, com reply-to |
| Várias ferramentas alimentadas por um formulário, cada uma com novas tentativas | Um webhook por destino que você adiciona | Até 3 endpoints assinados, com novas tentativas e log de entregas |
| O mesmo backend no Framer, no Webflow e em sites feitos com código | Não | Sim |

Se a primeira linha é tudo de que você precisa, fique com os destinos do Framer. O resto desta página é para quando não é.

## O porém: o Framer envia a partir dos servidores dele

Um destino de webhook significa que os servidores do Framer enviam o post, não o navegador do visitante. Três coisas mudam do lado do Ikiform:

- **Sem origem.** Envios de servidor não trazem header `Origin`, então com **Allowed domains** configurado, todo envio é tratado como vindo do site errado.
- **Endereços IP compartilhados.** O Ikiform conta os limites de taxa por endereço IP, e todo envio vem de alguns poucos endereços do Framer em vez dos endereços dos seus visitantes.
- **Sem honeypot.** O Framer envia os inputs que você montou e nada mais, e o formulário dele não consegue rodar a verificação humana do Ikiform.

Você pode aceitar isso, ou resolver com um relay. Os dois caminhos estão abaixo.

Diagrama: Um formulário do Framer enviando ao Ikiform direto e por um relay assinado

1. Direto: um visitante envia o formulário do Framer, os servidores do Framer o mandam como JSON sem header Origin, e o Ikiform aceita com Allowed domains vazio e um limite de taxa Relaxed.
2. Relay: os servidores do Framer assinam cada envio com o seu segredo e o mandam para um Worker que você publica.
3. O Worker recalcula a assinatura do Framer. Um envio sem assinatura válida recebe 401 e não vai adiante.
4. Um envio válido é encaminhado ao Ikiform com a sua chave secreta e com o id de envio do Framer como chave de idempotência, então uma nova tentativa do Framer nunca armazena uma segunda cópia.

O relay tem uma dúzia de linhas de lógica de verdade. Em troca, o formulário pode manter domínios permitidos rígidos, porque só o seu Worker tem a chave secreta.

## Opção 1: o Ikiform como webhook

1. No Framer, dê a cada input um nome em minúsculas: `name`, `work_email`, `company`, `team_size`. O Framer usa os nomes como chaves do JSON.
2. Selecione o formulário e, ao lado de **Send To**, escolha **Add…** > **Webhook**. Cole o seu endpoint. Deixe o segredo vazio; o Ikiform não confere `Framer-Signature`.
3. Nas configurações do formulário no Ikiform, deixe **Allowed domains** vazio, deixe o **Spam check** desligado e defina **Rate limit** como **Relaxed**.
4. Publique. O Framer só envia webhooks a partir do site publicado.

`URL do webhook`:

```text
https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx
```

O Ikiform responde `201` assim que o envio é armazenado, o que o Framer conta como entregue. Um `422 validation_failed` falha do mesmo jeito nas cinco tentativas, então corrija os nomes dos inputs em vez de esperar. A pontuação de spam continua rodando em todo envio: links, frases de spam, endereços descartáveis e repetições.

## Opção 2: um relay assinado

Publique um Cloudflare Worker que confere a assinatura do Framer e encaminha os envios válidos com a sua chave secreta. Aí o formulário do Ikiform pode listar o seu domínio do Framer em **Allowed domains** com **Reject**, e tudo que não for um navegador no seu site ou o seu Worker é recusado.

`src/index.ts`:

```ts
interface Env {
  FRAMER_SECRET: string;
  IKIFORM_SECRET_KEY: string;
}

const ENDPOINT = "https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx";
const encoder = new TextEncoder();

async function signatureFor(secret: string, body: ArrayBuffer, submissionId: string) {
  const key = await crypto.subtle.importKey(
    "raw",
    encoder.encode(secret),
    { name: "HMAC", hash: "SHA-256" },
    false,
    ["sign"],
  );
  const id = encoder.encode(submissionId);
  const message = new Uint8Array(body.byteLength + id.byteLength);
  message.set(new Uint8Array(body), 0);
  message.set(id, body.byteLength);
  const digest = new Uint8Array(await crypto.subtle.sign("HMAC", key, message));
  return `sha256=${[...digest].map((byte) => byte.toString(16).padStart(2, "0")).join("")}`;
}

function sameText(a: string, b: string) {
  const left = encoder.encode(a);
  const right = encoder.encode(b);
  return left.byteLength === right.byteLength && crypto.subtle.timingSafeEqual(left, right);
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== "POST") return new Response(null, { status: 405 });
    const signature = request.headers.get("framer-signature") ?? "";
    const submissionId = request.headers.get("framer-webhook-submission-id") ?? "";
    const body = await request.arrayBuffer();
    if (!submissionId || !sameText(signature, await signatureFor(env.FRAMER_SECRET, body, submissionId))) {
      return new Response("Bad signature", { status: 401 });
    }

    const response = await fetch(ENDPOINT, {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
        Authorization: `Bearer ${env.IKIFORM_SECRET_KEY}`,
        "Idempotency-Key": submissionId,
      },
      body,
    });
    if (response.ok) return new Response(null, { status: 204 });
    return new Response(await response.text(), { status: response.status });
  },
};
```

### Publique

`Terminal`:

```bash
npx wrangler secret put FRAMER_SECRET
npx wrangler secret put IKIFORM_SECRET_KEY
npx wrangler deploy
```

- `FRAMER_SECRET` é o segredo que você define no destino de webhook do Framer. O Framer exige pelo menos 32 caracteres e não o mostra de novo, então gere, guarde e cole nos dois lugares.
- `IKIFORM_SECRET_KEY` é uma chave de API secreta do painel do Ikiform. Nunca a coloque no Framer.
- A assinatura é `sha256=` mais o HMAC em hex do corpo bruto seguido do id do envio. Leia o corpo uma vez como bytes e use esses mesmos bytes na verificação e no encaminhamento.
- `Idempotency-Key` faz uma nova tentativa do Framer repetir a primeira resposta do Ikiform em vez de armazenar um duplicado. As chaves ficam guardadas por 24 horas.
- O Worker repassa o status do Ikiform. O Framer tenta de novo qualquer resposta que não seja `2xx`, até cinco vezes, então um `429` ou `5xx` ganha outra chance, enquanto um `422` falha do mesmo jeito toda vez até você corrigir os nomes dos inputs.

Depois aponte o webhook do Framer para a URL do Worker em vez do endpoint, com o segredo preenchido.

> Agora todo envio vem do seu Worker. As versões do lado do 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](https://ikiform.com/pt-br/docs/rate-limits.md) 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.

## Opção 3: dispense o formulário do Framer

Um code component renderiza um formulário de verdade e envia a partir do navegador do visitante, então verificações de origem, o honeypot e os limites de taxa por visitante funcionam como em qualquer site. Você perde a edição visual de formulários do Framer para esse formulário. O [guia do Framer na documentação](https://ikiform.com/pt-br/docs/guides/framer.md#code-component) traz o componente.

## Confira se funciona no Framer

1. Publique o site do Framer e envie o formulário lá, não no preview do editor.
2. Abra **Submissions** no painel. O novo envio está lá com todos os campos que você enviou.
3. Confira se os nomes dos campos batem exatamente com os nomes dos inputs, sem chaves adicionadas pelo Framer.
4. Para o relay: envie uma requisição ao Worker com um `Framer-Signature` inventado e confirme que ele responde `401`.
5. Para o relay: nos detalhes do envio, a origem está vazia e o status é recebido, não spam.
6. 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`.
7. Se você adicionou um webhook, abra o endpoint em **Webhooks**. A entrega mostra `succeeded` com a resposta do seu servidor.

## Pegadinhas

| Problema | O que fazer |
| --- | --- |
| Todo envio de webhook cai em spam com `origin_mismatch` | **Allowed domains** está configurado enquanto o Framer envia direto. Limpe, ou coloque o relay no meio. |
| O Worker responde `401` para envios reais | O segredo é diferente entre o Framer e o Worker, ou algo leu o corpo como texto e o alterou. Cole o segredo de novo e calcule o hash dos bytes brutos. |
| `429 rate_limited` durante um lançamento | Todos os envios compartilham os endereços do Framer ou da Cloudflare. Escolha **Relaxed**, ou um limite Custom. |
| `422 validation_failed` | O envio não corresponde à definição de campos do formulário. `error.fields` aponta cada campo e o motivo. Faça o `name` do input bater com o id do campo. |
| `403 challenge_required` | A 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. |

## Perguntas

### Preciso de um backend de formulários para formulários do Framer?

Nem sempre. O Framer envia as respostas para e-mail, Google Sheets ou um webhook sozinho. Adicione o Ikiform quando quiser um registro armazenado e filtrado, validação no servidor, alertas à equipe com reply-to ou vários webhooks com novas tentativas a partir de um formulário.

### O Ikiform verifica a assinatura de webhook do Framer?

Não. O Ikiform autentica envios pela origem ou pela sua chave de API secreta, não pelo header do Framer. Para conferir o Framer-Signature, coloque entre o Framer e o Ikiform um pequeno relay que o verifica e encaminha com a sua chave secreta.

### Por que os envios do meu Framer estão marcados como spam?

Na maioria das vezes porque Allowed domains está configurado. Os envios de webhook do Framer vêm dos servidores dele sem header Origin, então não batem com nenhum domínio. Limpe a lista ou use um relay que envia com a sua chave secreta.

### As novas tentativas do Framer vão criar envios duplicados?

Podem, ao enviar direto, se uma entrega for tentada de novo depois que o Ikiform a armazenou. O relay manda o id de envio do Framer como chave de idempotência, então uma nova tentativa repete a primeira resposta.

### O formulário aceita upload de arquivos?

Ainda não. Um envio que contém um arquivo é recusado com 422 files_not_supported. Peça um link para o arquivo, como a URL de um drive compartilhado.

## Mais guias

- [Vá além dos formulários do criador](https://ikiform.com/pt-br/guides/website-builder-forms.md): Quando os formulários do Webflow, Netlify, Carrd ou Framer batem num limite ou não têm um recurso, mantenha o formulário que você desenhou e aponte para um backend de formulários.
- [Limite de envios do Webflow](https://ikiform.com/pt-br/guides/website-builder-forms/webflow-submission-limit.md): O plano gratuito Starter do Webflow para de coletar após 50 envios de formulário por site e sites exportados não coletam nada; aponte o Form Block para o Ikiform.
- [Formulários do Carrd](https://ikiform.com/pt-br/guides/website-builder-forms/carrd-forms.md): O formulário de contato do Carrd só manda e-mail e formulários personalizados exigem o Pro Plus: use Send to URL no Plus ou um embed HTML no Standard para guardar cada envio.
- [Limite do Netlify Forms](https://ikiform.com/pt-br/guides/website-builder-forms/netlify-forms-limit.md): Planos legados da Netlify medem envios de formulário por site e fazem upgrade automático, e planos por créditos podem pausar o site; leve o formulário para um backend.
