Guias

Guias16 min de leitura

Envie os envios de formulários HTML para o seu e-mail

Receba todo envio de formulário na caixa de entrada sem servidor de e-mail: por que mailto: e mail() do PHP falham, e uma configuração que filtra spam e permite responder.

Preet SutharFundador, Ikiform

Um formulário HTML não consegue enviar e-mail sozinho, porque o navegador não tem como falar com um servidor de e-mail. Aponte o action do formulário para um backend de formulários, que recebe o envio, filtra spam, armazena e manda um e-mail para você e sua equipe. Dê ao campo de e-mail o nome email e o Reply na sua caixa de entrada responde direto para quem preencheu o formulário.

A maioria dos sites precisa de uma coisa de um formulário de contato: a mensagem numa caixa de entrada que alguém lê. Isso acaba sendo mais difícil do que parece. O HTML tem o elemento <form>, mas nenhum jeito de enviar e-mail, e os atalhos antigos ou devolvem o trabalho para o visitante ou caem no spam. Este guia explica por quê, compara as opções com honestidade e configura um formulário que manda e-mail para você em qualquer site e qualquer stack.

Por que um formulário não consegue enviar e-mail sozinho

Quando um visitante clica em Send, o navegador faz uma requisição HTTP para a URL no action do formulário. É tudo o que um formulário consegue fazer. Enviar e-mail exige uma conversa SMTP com um servidor de e-mail, a partir de uma máquina em que o servidor de destino confia. O navegador não consegue abrir essa conexão e, mesmo que conseguisse, um e-mail saindo do notebook de um visitante qualquer seria rejeitado. Então sempre existe algo num servidor entre o formulário e a sua caixa de entrada: seu próprio código, um script no seu host ou um serviço.

Com um backend de formulários, esse algo é uma URL. O formulário envia para https://ikiform.com/f/<id> e o Ikiform faz o resto:

Como um envio de formulário vira um e-mail na sua caixa de entradaseuformulárioqualquer siteou stackIkiformvalida,pontua spam,armazenaum e-mailpordestinatárioreply-to é oremetentesua caixade entradaReplyresponde aovisitantearmazenadocomo spamsem e-mail,sem webhookenvionão é spamsegundospontuação de spam atingidaComo um envio de formulário vira um e-mail na sua caixa de entradaseu formulárioqualquer site ou stackIkiformvalida, pontua spam,armazenaum e-mail pordestinatárioreply-to é o remetentesua caixa deentradaReply responde aovisitantearmazenadocomo spamsem e-mail,sem webhookenvionão é spamsegundospontuação de spam atingida
  1. O visitante envia o formulário, e o navegador ou o seu servidor o envia ao seu endpoint do Ikiform.
  2. O Ikiform confere os campos com a definição do formulário, dá uma pontuação de spam ao envio e o armazena.
  3. Um envio que atinge a pontuação de spam é armazenado como spam. Ele recebe a mesma resposta de sucesso, mas não dispara e-mail nem webhook.
  4. Todos os outros envios mandam um e-mail para cada destinatário, com o endereço do visitante como reply-to.
  5. O e-mail chega na sua caixa de entrada segundos depois. Clicar em Reply responde ao visitante.
O visitante nunca vê um endereço de e-mail e nunca abre um app de e-mail. A única coisa na sua página é um formulário.

Seis formas de levar um formulário até a sua caixa de entrada

Cada uma delas funciona para alguém. A tabela mostra o que cada uma custa para você quando visitantes de verdade, e bots de verdade, começam a usar o formulário.

AbordagemBom emContras
action="mailto:you@example.com"Sem servidor, sem conta, um atributo.Nada é enviado. O navegador abre o app de e-mail do visitante com um rascunho, ou não faz nada se não houver app de e-mail configurado. Seu endereço fica na página para os scrapers. Veja o guia do Gmail.
Um script PHP com mail()Roda em quase qualquer hospedagem PHP compartilhada, sem terceiros.Depende da configuração de e-mail do host, e e-mails que dizem vir do seu domínio muitas vezes falham no SPF ou no DMARC e caem no spam. Sem filtro de spam, sem armazenamento, e nada de PHP em hosts estáticos. Veja sem PHP.
Um SDK de e-mail no navegador, como o EmailJSSem servidor. Modelos de e-mail que você desenha no painel deles.As chaves e os ids dos modelos ficam no JavaScript da sua página, então qualquer um pode enviar seus modelos com o próprio texto. Veja a alternativa ao EmailJS.
Sua própria rota com uma API de e-mail, como Resend, Postmark, Amazon SES ou Nodemailer via SMTPControle total: seu assunto, seu HTML, uma confirmação para o visitante.Você constrói validação, filtro de spam, limites de taxa, armazenamento e novas tentativas, verifica um domínio de envio e precisa de um servidor mesmo num site estático. Veja o guia do Next.js.
Google Apps Script como web appRoda numa conta Google que você já tem.As cotas de script limitam para quantos destinatários ele consegue mandar e-mail por dia, a URL não tem proteção contra spam e a manutenção do script fica com você.
Um backend de formulários, como o Ikiform, o Formspree ou o Web3FormsUma URL no action. Filtro de spam, armazenamento e e-mail sem servidor.O layout e o assunto do e-mail são os do serviço. Com o Ikiform, nada nunca é enviado por e-mail ao visitante.

Se você precisa de um e-mail totalmente com a sua marca, ou de uma confirmação para o visitante, escreva sua própria rota com uma API de e-mail. Se você quer a mensagem na sua caixa de entrada e um jeito de responder, com o spam do lado de fora, um backend de formulários é o que exige menos código. O resto deste guia usa o Ikiform.

O formulário

Três campos cobrem a maioria dos formulários de contato: quem é a pessoa, para onde responder e o que ela quer. Escolha sua stack abaixo; a escolha vale para todos os exemplos de código destes guias.

Este formulário funciona sem nenhum JavaScript: o navegador o envia, o Ikiform manda um e-mail para você e o visitante cai na página definida em _redirect. Cada campo tem um espaço vazio para erro logo abaixo, usado pelo script a seguir.

contact.html
<form id="contact" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="POST">
  <label for="name">Name</label>
  <input id="name" name="name" autocomplete="name" required maxlength="120" />
  <p data-error="name"></p>

  <label for="email">Your email</label>
  <input id="email" name="email" type="email" autocomplete="email" required />
  <p data-error="email"></p>

  <label for="message">Message</label>
  <textarea id="message" name="message" rows="6" required minlength="10"></textarea>
  <p data-error="message"></p>

  <input type="hidden" name="page" value="/contact" />
  <input type="hidden" name="_redirect" value="https://example.com/contact/sent" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Send message</button>
  <p id="contact-status" role="status"></p>
</form>

O script mantém o visitante na página. Ele envia com fetch, escreve cada erro do servidor abaixo do campo correspondente e troca o formulário por uma frase que repete o endereço para o qual você vai responder, para que um erro de digitação fique fácil de notar.

contact.js
const form = document.getElementById("contact");
const status = document.getElementById("contact-status");
const button = form.querySelector("button");

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  for (const slot of form.querySelectorAll("[data-error]")) slot.textContent = "";
  button.disabled = true;
  status.textContent = "Sending";

  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) {
      const email = form.elements.namedItem("email").value;
      const done = document.createElement("p");
      done.setAttribute("role", "status");
      done.textContent = `Sent. We'll reply to ${email}.`;
      form.replaceWith(done);
      return;
    }
    for (const [field, messages] of Object.entries(result.error.fields ?? {})) {
      const slot = form.querySelector(`[data-error="${field}"]`);
      if (slot) slot.textContent = messages[0];
    }
    status.textContent = result.error.fields ? "Check the fields above." : result.error.message;
  } catch {
    status.textContent = "Couldn't send. Check your connection and try again.";
  }
  button.disabled = false;
});
  • Troque frm_xxxxxxxxxxxxxxxx pelo id do seu formulário, que está no painel.
  • name="email" transforma esse campo no endereço de reply-to do e-mail de notificação. Mantenha o nome, mesmo que o rótulo diga "Seu e-mail".
  • page é um campo oculto comum. Ele aparece no e-mail, então você sabe de qual página veio a mensagem.
  • _gotcha é o honeypot. As pessoas nunca o veem; um bot que o preenche é armazenado como spam e não manda e-mail para ninguém.
  • _redirect só funciona com uma URL https: em um dos Allowed domains do formulário. Deixe-o de fora e defina Redirect after submit nas configurações do formulário, ou o Ikiform mostra a própria página de agradecimento.

Os guias de frameworks vão além: Next.js, Astro, SvelteKit, Nuxt, React, Vue e HTML puro. Criadores de sites como o Webflow e o Carrd têm páginas próprias, e o modelo de formulário de contato adiciona um campo de assunto.

Defina os campos no painel também, e o Ikiform recusa um envio que não corresponde com 422 validation_failed e uma mensagem por campo, que o código acima mostra ao lado de cada input. Experimente aqui; isto roda o mesmo validador no seu navegador:

Prévia

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

Cole esta definição na aba Fields do formulário. Sem uma definição, o Ikiform aceita quaisquer nomes de campo e manda por e-mail o que chegar.

Como é o e-mail

Todo envio aceito dispara um e-mail simples e legível. Esta é a versão em texto de um deles:

E-mail de notificação
From: Ikiform <notifications@ikiform.com>
To: you@yourcompany.com
Reply-To: ada@example.com
Subject: New submission: Contact

Contact got a new submission on Oct 25, 2026, 9:14 AM UTC.

name: Ada Lovelace
email: ada@example.com
message: Do you ship to Canada? I'd like to order 40 units before December.
page: /contact

View it in Ikiform:
https://ikiform.com/dashboard/submissions?form=frm_xxxxxxxxxxxxxxxx&submission=sub_...
  • Ele vem de notifications@ikiform.com. Você não configura registros DNS nem domínio de envio, porque o e-mail sai do domínio do Ikiform.
  • O assunto é sempre New submission: <form name>. _subject não muda o assunto dos e-mails de notificação. Ele é repassado aos webhooks e à API como meta.subject. Para diferenciar os formulários, dê a cada um um nome claro no painel, como "Contato, página de preços".
  • Os campos aparecem na ordem em que foram enviados, identificados pelo name. Campos que começam com _ ficam de fora.
  • Um formulário ou uma resposta muito longa é encurtada, e o e-mail diz quantos campos a mais estão no painel. A página de limites tem os números.
  • Campos que parecem senhas ou números de cartão ficam de fora, e o e-mail de um envio que os contém fica retido para revisão. Não colete credenciais com um backend de formulários.
  • Membros do workspace recebem um botão View submission. Pessoas de fora do workspace recebem os campos e um link para cancelar a inscrição.

Quem recebe o e-mail

Os destinatários são definidos por formulário no painel, nunca pelo próprio formulário. Um campo que indicasse um destinatário deixaria qualquer um usar seu formulário para mandar e-mail para qualquer pessoa, então o Ikiform não tem esse campo.

QuemComo é adicionadoObservações
O padrãoNada a fazerO membro escolhido como Notification email do workspace, ou todos os donos do workspace quando nenhum é escolhido
Membros escolhidosMarque-os nas configurações de Notifications do formulárioSó membros com endereço de e-mail verificado
Pessoas de fora do workspaceAdd someone outside the workspace, depois Send linkSó recebem e-mails depois de confirmar. Até 5 por formulário, e cada um pode cancelar a inscrição

Cada formulário manda e-mail para no máximo 10 endereços. Cada destinatário recebe a própria cópia em vez de um CC compartilhado, então ninguém vê os endereços dos outros. Se a caixa de um destinatário devolve os e-mails, o Ikiform pausa os envios para esse endereço por um tempo e o painel mostra Emails bounced ao lado dele. Vários destinatários explica o roteamento por assunto, que os destinatários sozinhos não resolvem.

Responda à pessoa, não ao Ikiform

O Ikiform define o endereço de reply-to primeiro a partir de um campo _replyto, depois a partir de um campo chamado email, email_address, your_email, reply_to, contact_email. O valor precisa ser um único endereço de e-mail válido, ou é ignorado. Com um reply-to definido, clicar em Reply no Gmail, no Outlook ou no Apple Mail abre uma resposta para o visitante, e sua resposta sai do seu próprio endereço.

Sem ele, uma resposta vai para notifications@ikiform.com, que ninguém lê. Essa é a surpresa mais comum, e geralmente é um campo chamado user_email ou Email Address com espaço. Renomeie-o ou adicione _replyto. O guia de reply-to cobre formulários com dois campos de e-mail e criadores de sites que renomeiam os campos por você.

Quando chega muita coisa de uma vez

Cada formulário envia até 50 e-mails de notificação por hora, ou 10 enquanto o workspace tem menos de 7 dias. Passando disso, o resto dos envios da hora é armazenado normalmente e chega num único e-mail de resumo quando a hora termina. O limite protege sua caixa de entrada, e a reputação dos e-mails do Ikiform com o seu provedor, quando um formulário é divulgado em algum lugar grande ou um bot passa pelos filtros.

O que acontece com os e-mails de notificação numa hora movimentadaum formulário, uma hora cheia em UTCenvios 1 a 50um e-mail cada,segundos depoisenvio 51 emdiantearmazenado,guardado para oresumofim da horaum resumo porformuláriolimite atingidopróxima horaO que acontece com os e-mails de notificação numa hora movimentadaum formulário, uma hora cheia em UTCenvios 1 a 50um e-mail cada, segundos depoisenvio 51 em diantearmazenado, guardado para oresumofim da horaum resumo por formuláriolimite atingidopróxima hora
  1. Os 50 primeiros envios para um formulário numa hora cheia disparam um e-mail cada, como de costume.
  2. Os envios seguintes na mesma hora são armazenados e aparecem no painel na hora, sem um e-mail para cada um.
  3. Quando a hora termina, cada destinatário recebe um resumo que conta esses envios e leva ao painel.
O limite é contado por formulário, então uma rajada num formulário não atrasa outro. Os webhooks não têm limite e continuam disparando para cada envio.

O assunto do resumo é a contagem e o nome do formulário, e ele não lista os campos nem define reply-to, então responda essas mensagens pelo painel. Se você passa do limite com frequência, o e-mail não é o canal certo para esse formulário: envie para o Slack ou para uma caixa compartilhada por meio de um webhook.

Mantenha o spam fora da sua caixa de entrada

O e-mail deixa o spam caro: cada envio de bot é uma notificação que alguém abre. O Ikiform armazena spam com uma resposta de sucesso normal, então o bot não aprende nada, e não manda e-mail por ele. Quatro coisas decidem o que conta como spam:

DefesaO que ela pegaConfiguração
HoneypotBots que preenchem todos os inputsO input oculto _gotcha
Pontuação de spamLinks no campo de nome, três ou mais links, frases de spam conhecidas, endereços descartáveis e repetições idênticas. Com 5 o envio é spamNenhuma
Domínios permitidosEnvios de outros sitesAdicione seu domínio nas configurações do formulário
Verificação humanaBots que rodam um navegador de verdadeDefina Spam check como Ikiform

Confira o filtro Spam no painel de vez em quando. Se uma mensagem real cair lá, marque-a como não spam e o e-mail dela é enviado na hora. A documentação de spam explica cada sinal, inclusive quando ligar a verificação humana.

O que o e-mail não faz

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. Se o seu site promete "recebemos sua mensagem", mostre isso na página depois do envio, onde o código acima já mostra.

  • Uma caixa de entrada compartilhada da equipe. Adicione o endereço da equipe como destinatário ou encaminhe pelo seu provedor de e-mail.
  • Um canal de chat. Envie cada envio para o Slack, o Discord ou o Microsoft Teams por webhook. O chat combina com formulários que recebem mais do que algumas mensagens por dia.
  • Uma planilha ou CRM. O Google Sheets, ou qualquer ferramenta via Zapier ou n8n.
  • Anexos. Upload de arquivos ainda não é suportado. Um envio com arquivo recebe 422 files_not_supported. Peça um link para um arquivo compartilhado.

Desligando o e-mail

Alguns formulários ficam melhores sem e-mail, como uma inscrição de newsletter movimentada. Desligue Email new submissions nas configurações de Notifications do formulário. Os envios continuam sendo armazenados, e os webhooks continuam disparando.

O limite começa mais baixo em workspaces novos: 10 e-mails por formulário por hora nos primeiros 7 dias. Se você está testando com um script, espere receber um resumo mais cedo do que receberia depois.

Escolha o seu caso

A configuração acima é a mesma em todo lugar. Estas páginas tratam de uma caixa de entrada específica, de uma stack específica ou da ferramenta que você está deixando para trás.

Confira se funciona

Siga estes passos antes de colocar o formulário no ar. 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. Nas configurações de Notifications do formulário, confira se Email new submissions está ligado e se as pessoas certas estão marcadas.
  3. Preencha o formulário na sua página e envie. Você cai na sua página de agradecimento, ou na hospedada pelo Ikiform.
  4. Abra Submissions no painel. O novo envio está lá com todos os campos que você enviou.
  5. Abra o e-mail de notificação. O assunto é New submission: mais o nome do formulário, e todos os campos que você enviou estão listados.
  6. Clique em Reply. A linha To mostra o endereço que você digitou no formulário, não o do Ikiform.
  7. 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.
  8. Não chegou nada? Mude o filtro de status do painel para Spam. Spam recebe a mesma resposta de sucesso de um envio real.

Solução de problemas

ProblemaO que fazer
O envio está no painel, mas nenhum e-mail chegouOlhe primeiro na pasta de spam ou de promoções e adicione notifications@ikiform.com aos seus contatos. Depois confira as configurações de Notifications do formulário: o e-mail pode estar desligado, um endereço de fora pode ainda mostrar Waiting for confirmation, ou pode aparecer Emails bounced.
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.
Reply vai para notifications@ikiform.comNenhum reply-to foi encontrado. Dê ao campo de e-mail um destes nomes: email, email_address, your_email, reply_to, contact_email, ou adicione um campo _replyto com o endereço do visitante.
Os e-mails pararam e chegou um resumo no lugarO formulário passou de 50 e-mails numa hora (10 num workspace novo). Está tudo no painel. Procure uma rajada de bots em Spam, ou use um webhook para formulários movimentados.
_subject não muda o assunto_subject não muda o assunto dos e-mails de notificação. Ele é repassado aos webhooks e à API como meta.subject.
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.
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.
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.