Vá além dos formulários do criador

Vá além dos formulários do criador8 min de leitura

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.

Preet SutharFundador, Ikiform

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ê querSó o FramerCom o Ikiform como webhook
Envios numa caixa de entrada ou numa planilhaSimSim, mais o painel
Um registro pesquisável com spam marcado, e exportação CSVSua caixa de entrada ou planilha é o registroSim
Verificações no servidor de tipos, tamanhos e opçõesNãoSim, com uma definição de campos
Alertas para várias pessoas, com a resposta indo para quem enviouDestino de e-mailAté 10 destinatários, com reply-to
Várias ferramentas alimentadas por um formulário, cada uma com novas tentativasUm webhook por destino que você adicionaAté 3 endpoints assinados, com novas tentativas e log de entregas
O mesmo backend no Framer, no Webflow e em sites feitos com códigoNãoSim

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.

Um formulário do Framer enviando ao Ikiform direto e por um relay assinadoWebhook diretoRelay assinadoFormuláriodo FramerServidoresdo FramerJSON, semorigemIkiformdomíniosvazios,limiteRelaxedFormuláriodo FramerServidoresdo Framerassina cadaenvioseuWorkerconfereFramer-SignatureIkiformchavesecreta,chave deidempotência401,descartadoenviarwebhookenviarwebhookencaminhaassinatura inválidaUm formulário do Framer enviando ao Ikiform direto e por um relay assinadoWebhook diretoRelay assinadoFormulário doFramerServidores doFramerJSON, sem origemIkiformdomínios vazios,limite RelaxedFormulário doFramerServidores doFramerassina cada envioseu WorkerconfereFramer-SignatureIkiformchave secreta, chavede idempotência401,descartadoenviarwebhookenviarwebhookencaminhaassinatura inválida
  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
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
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
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 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 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

ProblemaO que fazer
Todo envio de webhook cai em spam com origin_mismatchAllowed domains está configurado enquanto o Framer envia direto. Limpe, ou coloque o relay no meio.
O Worker responde 401 para envios reaisO 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çamentoTodos os envios compartilham os endereços do Framer ou da Cloudflare. Escolha Relaxed, ou um limite Custom.
422 validation_failedO 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_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.

Perguntas

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