Más allá de los formularios del creador8 min de lectura
Formularios de Framer: cuándo añadir un backend de formularios y cómo
Los formularios de Framer ya envían a email, Sheets o un webhook; añade Ikiform como webhook para tener envíos guardados y filtrados, avisos al equipo y más destinos.
Los formularios nativos de Framer envían cada envío a email, Google Sheets o un webhook, con protección antispam integrada, así que para una bandeja o una hoja puede que no necesites nada más. Añade Ikiform como destino webhook cuando quieras guardar cada envío con un estado de spam, validarlo contra tus campos, enviarlo por email al equipo y reenviarlo a varios webhooks firmados. Como Framer envía desde sus servidores, o relajas las comprobaciones de origen del formulario o pones en medio un pequeño relay que verifique la firma de Framer.
Qué cambia en Framer
Framer es el único creador de esta guía sin un límite del que escapar. Sus formularios pueden enviar a email, Google Sheets o cualquier webhook, y Framer tiene protección antispam integrada que marca o bloquea los envíos sospechosos. Sus webhooks están firmados: cada petición lleva una cabecera Framer-Signature, un HMAC-SHA256 del cuerpo y del id del envío, y una Framer-Webhook-Submission-Id. Framer reintenta hasta cinco veces cuando tu endpoint no responde 2xx, y no sigue redirecciones.
Así que sé honesto sobre por qué añadirías un backend de formularios. Esto es lo que cubre cada lado:
| Quieres | Solo Framer | Con Ikiform como webhook |
|---|---|---|
| Envíos en una bandeja o una hoja | Sí | Sí, más el panel |
| Un registro consultable con el spam marcado, y exportación CSV | Tu bandeja o tu hoja es el registro | Sí |
| Comprobaciones en el servidor de tipos, longitudes y opciones | No | Sí, con una definición de campos |
| Avisos a varias personas, con Responder dirigido a quien envió | Destino email | Hasta 10 destinatarios, con reply-to |
| Varias herramientas alimentadas desde un formulario, cada una con reintentos | Un webhook por cada destino que añadas | Hasta 3 endpoints firmados, con reintentos y un registro de entregas |
| El mismo backend en sitios de Framer, de Webflow y con código | No | Sí |
Si la primera fila es todo lo que necesitas, quédate con los destinos de Framer. El resto de esta página es para cuando no lo es.
El problema: Framer envía desde sus servidores
Un destino webhook significa que los servidores de Framer envían el post, no el navegador del visitante. Cambian tres cosas en el lado de Ikiform:
- Sin origen. Los envíos desde servidor no llevan cabecera
Origin, así que con Allowed domains configurado, cada envío se trata como si viniera del sitio equivocado. - Direcciones IP compartidas. Ikiform cuenta los límites de frecuencia por dirección IP, y todos los envíos vienen de unas pocas direcciones de Framer en lugar de las de tus visitantes.
- Sin honeypot. Framer envía los inputs que creaste y nada más, y su formulario no puede ejecutar la verificación humana de Ikiform.
Puedes aceptarlo, o solucionarlo con un relay. Abajo están los dos caminos.
- Directo: un visitante envía el formulario de Framer, los servidores de Framer lo mandan como JSON sin cabecera Origin, e Ikiform lo acepta con Allowed domains vacío y un límite de frecuencia Relaxed.
- Relay: los servidores de Framer firman cada envío con tu secreto y lo mandan a un Worker que despliegas tú.
- El Worker recalcula la firma de Framer. Un envío sin firma válida recibe un 401 y no pasa de ahí.
- Un envío válido se reenvía a Ikiform con tu clave secreta y el id de envío de Framer como clave de idempotencia, así que un reintento de Framer nunca guarda una segunda copia.
Opción 1: Ikiform como webhook
- En Framer, dale a cada input un nombre en minúsculas:
name,work_email,company,team_size. Framer usa los nombres como claves JSON. - Selecciona el formulario y, junto a Send To, elige Add… > Webhook. Pega tu endpoint. Deja el secreto vacío; Ikiform no comprueba
Framer-Signature. - En los ajustes del formulario de Ikiform, deja Allowed domains vacío, deja Spam check desactivado y pon Rate limit en Relaxed.
- Publica. Framer solo envía webhooks desde el sitio publicado.
https://ikiform.com/f/frm_xxxxxxxxxxxxxxxxIkiform responde 201 en cuanto el envío está guardado, lo que Framer cuenta como entregado. Un 422 validation_failed falla igual en los cinco reintentos, así que arregla los nombres de los inputs en lugar de esperar. La puntuación de spam sigue aplicándose a cada envío: enlaces, frases de spam, direcciones desechables y repeticiones.
Opción 2: un relay firmado
Despliega un Cloudflare Worker que compruebe la firma de Framer y reenvíe los envíos válidos con tu clave secreta. Así el formulario de Ikiform puede listar tu dominio de Framer en Allowed domains con Reject, y se rechaza todo lo que no sea un navegador en tu sitio o tu Worker.
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 });
},
};Despliégalo
npx wrangler secret put FRAMER_SECRET
npx wrangler secret put IKIFORM_SECRET_KEY
npx wrangler deployFRAMER_SECRETes el secreto que configuras en el destino webhook de Framer. Framer exige al menos 32 caracteres y no volverá a mostrarlo, así que genéralo, guárdalo y pégalo en los dos sitios.IKIFORM_SECRET_KEYes una API key secreta del panel de Ikiform. Nunca la pongas en Framer.- La firma es
sha256=más el HMAC en hex del cuerpo sin procesar seguido del id del envío. Lee el cuerpo una vez como bytes y usa esos mismos bytes para la comprobación y para el reenvío. Idempotency-Keyhace que un reintento de Framer repita la primera respuesta de Ikiform en lugar de guardar un duplicado. Las claves se guardan durante 24 horas.- El Worker transmite el estado de Ikiform. Framer reintenta cualquier respuesta que no sea
2xx, hasta cinco veces, así que un429o un5xxtienen otra oportunidad, mientras que un422falla igual cada vez hasta que arreglas los nombres de los inputs.
Después apunta el webhook de Framer a la URL del Worker en lugar de al endpoint, con el secreto rellenado.
IKIFORM_SECRET_KEY en variables de entorno solo de servidor.Opción 3: prescindir del formulario de Framer
Un code component renderiza un formulario real y envía desde el navegador del visitante, así que las comprobaciones de origen, el honeypot y los límites de frecuencia por visitante funcionan como en cualquier sitio. Pierdes la edición visual de formularios de Framer para ese formulario. La guía de Framer de la documentación tiene el componente.
Comprueba que funciona en Framer
- Publica el sitio de Framer y envía el formulario ahí, no en la vista previa del editor.
- Abre Submissions en el panel. El nuevo envío está ahí con todos los campos que enviaste.
- Comprueba que los nombres de campo coinciden exactamente con los nombres de tus inputs, sin claves añadidas por Framer.
- Para el relay: envía al Worker una petición con un
Framer-Signatureinventado y confirma que responde401. - Para el relay: en los detalles del envío, el origen está vacío y el estado es recibido, no spam.
- Revisa la bandeja de entrada de cada destinatario de las notificaciones. Cada uno recibe un email con los campos, y Reply va a quien envió el formulario cuando este tiene un campo
email. - Si añadiste un webhook, abre el endpoint en Webhooks. La entrega muestra
succeededcon la respuesta de tu servidor.
Errores comunes
| Problema | Qué hacer |
|---|---|
Todos los envíos del webhook están en spam con origin_mismatch | Allowed domains está configurado mientras Framer envía directamente. Vacíalo, o pon el relay en medio. |
El Worker responde 401 a los envíos reales | El secreto es distinto entre Framer y el Worker, o algo leyó el cuerpo como texto y lo cambió. Vuelve a pegar el secreto y calcula el hash de los bytes sin procesar. |
429 rate_limited durante un lanzamiento | Todos los envíos comparten las direcciones de Framer o de Cloudflare. Elige Relaxed, o un límite Custom. |
422 validation_failed | El envío no coincide con la definición de campos del formulario. error.fields indica cada campo y el motivo. Haz que el name del input coincida con el id del campo. |
403 challenge_required | La verificación antispam del formulario está en Ikiform y un envío con fetch no llevaba token. Consigue uno con challenge(formId) de @ikiform/sdk antes de cada envío, o desactiva la verificación. |
Preguntas
No siempre. Framer envía los envíos a email, Google Sheets o un webhook por sí solo. Añade Ikiform cuando quieras un registro guardado y filtrado, validación en el servidor, avisos al equipo con reply-to o varios webhooks con reintentos desde un formulario.
No. Ikiform autentica los envíos por origen o por tu API key secreta, no por la cabecera de Framer. Para comprobar Framer-Signature, pon entre Framer e Ikiform un pequeño relay que la verifique y reenvíe con tu clave secreta.
Casi siempre porque Allowed domains está configurado. Los envíos del webhook de Framer vienen de sus servidores sin cabecera Origin, así que no coinciden con ningún dominio. Vacía la lista, o usa un relay que envíe con tu clave secreta.
Pueden hacerlo al enviar directamente, si una entrega se reintenta después de que Ikiform la guardara. El relay envía el id de envío de Framer como clave de idempotencia, así que un reintento repite la primera respuesta.
Todavía no. Un envío que contiene un archivo se rechaza con 422 files_not_supported. Pide en su lugar un enlace al archivo, como la URL de una carpeta compartida.
¿Te atascaste en un paso o quieres una guía para tu caso? Escribe a hello@ikiform.com.