Formulário de inscrição em newsletter

Formulário de inscrição em newsletter6 min de leitura

Envie as inscrições do seu próprio formulário de newsletter para o beehiiv

Conecte seu próprio formulário de inscrição ao beehiiv: um relay cria cada subscription com double opt-in ligado, a posição como UTM source e a página como referrer.

Preet SutharFundador, Ikiform

Envie seu próprio formulário ao Ikiform e aponte o webhook para um relay que chama o endpoint de criar subscription do beehiiv. Envie double_opt_override: "on" para o beehiiv enviar a confirmação por e-mail, passe a posição do formulário como utm_source e a página como referring_site, e mantenha reactivate_existing como false para que uma inscrição nunca desfaça um descadastro sem ninguém perceber. Uma chamada de API por inscrição, com a chave guardada no relay.

O que muda com o beehiiv

A API do beehiiv é organizada em torno de publicações. Uma subscription é um endereço de e-mail inscrito numa publicação, e você a cria com um POST em /v2/publications/{publicationId}/subscriptions. Tudo sobre a inscrição vai nessa única requisição: se deve confirmar, se deve enviar o e-mail de boas-vindas, de onde o leitor veio e os campos personalizados. Não há etapa separada de tag ou formulário como no Kit.

O beehiiv também reporta a aquisição em termos de UTM. Os registros de subscription dele trazem utm_source, utm_medium, utm_campaign e referring_site. Mapeie os campos do seu formulário para esses, e cada assinante no beehiiv mostra qual posição e qual artigo o trouxeram:

Como o relay mapeia um envio do Ikiform para uma subscription do beehiivEvento do IkiformAssinatura no beehiivfields.emaile-mailfields.sourceutm_sourcemeta.referrerreferring_sitefields.first_namecustom_fields: First Namedouble_opt_override: "on"reactivate_existing: falseComo o relay mapeia um envio do Ikiform para uma subscription do beehiivEvento do IkiformAssinatura no beehiivfields.emaile-mailfields.sourceutm_sourcemeta.referrerreferring_sitefields.first_namecustom_fields:First Namedouble_opt_override:"on"reactivate_existing:false
  1. fields.email vira email.
  2. fields.source vira utm_source.
  3. meta.referrer vira referring_site.
  4. fields.first_name vira custom_fields: First Name.
  5. double_opt_override: "on" é adicionado pelo relay.
  6. reactivate_existing: false é adicionado pelo relay.
Os quatro primeiros vêm do evento do Ikiform. Os dois últimos são escolhas fixas que o relay faz para toda inscrição.

Configure o beehiiv

  1. No beehiiv, abra Settings, depois API em Workspace Settings, e escolha Create New API Key. Copie na hora; o beehiiv não vai mostrá-la de novo.
  2. Num plano pago, restrinja a chave à única publicação que o formulário alimenta. Uma chave restrita a uma publicação recebe 404 para qualquer outra publicação, o que limita o estrago se ela vazar.
  3. Encontre o id da publicação, o id com prefixo que a API do beehiiv usa no caminho. O endpoint da API que lista publicações o retorna.
  4. Crie um campo personalizado chamado First Name se quiser guardar primeiros nomes. O beehiiv descarta campos personalizados que ainda não existem.
  5. Configure os e-mails de double opt-in e de boas-vindas nas configurações da publicação. O relay os ativa a cada requisição; o conteúdo deles fica no beehiiv.
A página de preços do beehiiv lista acesso à API em todos os planos, com limites de taxa da API menores nos planos mais baratos. A Send API, para enviar e-mail pela API, fica restrita aos planos mais altos, mas este relay só cria assinaturas e não precisa dela.

O relay

Esta é uma Netlify Function, que combina com um site que já está na Netlify: roda no seu próprio domínio, ao lado do site estático, sem um serviço separado. Instale @netlify/functions para os tipos e defina as três variáveis nas configurações de ambiente do site.

netlify/functions/beehiiv.mts
import type { Config } from "@netlify/functions";
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";

export default async (request: Request) => {
  const payload = await request.text();
  let event;
  try {
    event = await verifyWebhook({
      payload,
      headers: request.headers,
      secret: Netlify.env.get("IKIFORM_WEBHOOK_SECRET")!,
    });
  } catch (error) {
    if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
    throw error;
  }
  if (event.type !== "submission.created") return new Response(null, { status: 204 });

  const { email, first_name, consent, source } = event.data.fields;
  if (consent !== true || typeof email !== "string") return new Response("Skipped", { status: 200 });

  const publication = Netlify.env.get("BEEHIIV_PUBLICATION_ID");
  const answer = await fetch(`https://api.beehiiv.com/v2/publications/${publication}/subscriptions`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${Netlify.env.get("BEEHIIV_API_KEY")}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      email,
      double_opt_override: "on",
      send_welcome_email: true,
      reactivate_existing: false,
      utm_source: typeof source === "string" ? source : "website",
      utm_medium: "signup_form",
      referring_site: event.data.meta.referrer ?? undefined,
      custom_fields: typeof first_name === "string" && first_name ? [{ name: "First Name", value: first_name }] : [],
    }),
  });

  if (answer.status === 429) {
    const reset = Number(answer.headers.get("ratelimit-reset"));
    const wait = reset ? Math.max(1, reset - Math.floor(Date.now() / 1000)) : 60;
    return new Response("beehiiv rate limit", { status: 503, headers: { "retry-after": String(wait) } });
  }
  if (!answer.ok) return new Response(`beehiiv answered ${answer.status}: ${await answer.text()}`, { status: 502 });
  return new Response(null, { status: 204 });
};

export const config: Config = {
  path: "/hooks/beehiiv",
};
  • double_opt_override: "on" envia o e-mail de confirmação seja qual for o padrão da publicação. Use "not_set" para seguir a configuração da publicação e nunca "off" num formulário público.
  • send_welcome_email tem false como padrão na API. Defina como true se quiser que as inscrições via API recebam o e-mail de boas-vindas que as inscrições pelo embed recebem.
  • reactivate_existing: false também é o padrão. Deixe explícito para ninguém trocar depois sem pensar; ele reativa pessoas que cancelaram a assinatura.
  • O beehiiv responde 200 com a assinatura, e o status dela começa como validating enquanto o beehiiv confere o endereço, e depois fica pending até o leitor confirmar.
  • Em caso de 429, o relay lê o RateLimit-Reset do beehiiv, um horário Unix, e o transforma num atraso Retry-After para o Ikiform.

A URL da função é https://your-site.com/hooks/beehiiv. Adicione-a em Webhooks no Ikiform e copie o segredo whsec_ para IKIFORM_WEBHOOK_SECRET. A documentação de webhooks lista as regras de URL, e seu próprio servidor mostra como pular uma entrega que você já processou.

Escolhendo os valores de UTM

Os campos de UTM do beehiiv são texto livre, então decida os valores uma vez e mantenha-os consistentes, ou os registros se dividem em quase duplicados como footer e Footer. O input oculto source do formulário é o utm_source natural. Mantenha utm_medium fixo, para que toda inscrição vinda dos seus formulários possa ser distinguida das páginas hospedadas do beehiiv e das importações:

PosiçãoInput sourceutm_sourceutm_medium
Rodapé do sitefooterfootersignup_form
Fim de um postpostpostsignup_form
Página da newsletternewsletter-pagenewsletter-pagesignup_form

Se você roda uma campanha com link para uma página com o formulário, leia a campanha da URL da página no relay. meta.referrer guarda a URL completa da página, com a query string quando o navegador a envia, então o relay pode copiar um parâmetro utm_campaign dela para a requisição.

Limites de taxa por plano

O beehiiv mede o limite por chave de API em janelas fixas de 60 segundos, e o limite depende do plano. Cada inscrição é uma requisição:

Plano do beehiivRequisições por minuto
Free30
Lite180
Pro500
Enterprise800, mais sob pedido

Trinta por minuto bastam para a maioria dos formulários de rodapé, mas um post de lançamento numa publicação gratuita pode passar disso. O 503 do relay faz o Ikiform esperar e tentar de novo em vez de perder a inscrição. Nomes de planos e limites mudam, então confira a página de rate limiting do beehiiv se o seu não estiver na lista.

Pegadinhas

  • Nomes de campos personalizados são exatos. O beehiiv associa campos personalizados pelo nome e descarta os desconhecidos com um aviso na resposta, não um erro. Um erro de digitação faz os primeiros nomes sumirem sem alarde.
  • referring_site pode vir vazio. meta.referrer é null quando o navegador não enviou um referrer, e o relay deixa o campo de fora.
  • Assinantes existentes. A documentação do beehiiv diz que a chamada cria ou atualiza a assinatura, mas não o que muda para um assinante ativo. Teste com o seu próprio endereço confirmado antes de colocar o formulário em todas as páginas.
  • Chaves valem para o workspace inteiro por padrão. Uma chave sem restrição de publicação alcança todas as publicações do workspace. Restrinja o escopo.
  • Planos pagos ficam no beehiiv. Deixe tier de fora para que as inscrições entrem sem assinatura paga. Upgrades acontecem no checkout do próprio beehiiv.

Confira se funciona

  1. Escolha Send test no endpoint no Ikiform. A entrega mostra 204 e o beehiiv não recebe nenhuma requisição.
  2. Inscreva-se pela sua página com um endereço seu. A entrega mostra 204.
  3. No beehiiv, o assinante aparece como pendente, com a sua posição como UTM source e o artigo como referring site.
  4. O e-mail de double opt-in chega. Confirme, e o e-mail de boas-vindas vem em seguida.
  5. Cancele a assinatura desse endereço no beehiiv e inscreva-se de novo. O assinante continua inativo, porque reactivate_existing é false.

Perguntas

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