Dépasser les formulaires intégrés8 min de lecture
Formulaires Framer : quand ajouter un backend de formulaires, et comment
Les formulaires Framer envoient déjà vers l'e-mail, Sheets ou un webhook ; ajoutez Ikiform comme webhook pour des soumissions stockées et filtrées, des alertes d'équipe et davantage de destinations.
Les formulaires natifs de Framer envoient chaque soumission par e-mail, vers Google Sheets ou vers un webhook, avec une protection anti-spam intégrée, donc pour une seule boîte de réception ou une seule feuille vous n'avez peut-être besoin de rien d'autre. Ajoutez Ikiform comme destination webhook quand vous voulez que chaque soumission soit stockée avec un statut de spam, validée selon vos champs, envoyée par e-mail à l'équipe et transmise à plusieurs webhooks signés. Comme Framer envoie depuis ses serveurs, assouplissez les vérifications d'origine du formulaire ou placez entre les deux un petit relais qui vérifie la signature de Framer.
Ce qui change sur Framer
Framer est le seul créateur de sites de ce guide sans limite à contourner. Ses formulaires peuvent envoyer par e-mail, vers Google Sheets ou vers n'importe quel webhook, et Framer a une protection anti-spam intégrée qui signale ou bloque les soumissions suspectes. Ses webhooks sont signés : chaque requête porte un en-tête Framer-Signature, un HMAC-SHA256 du corps et de l'identifiant de soumission, et un Framer-Webhook-Submission-Id. Framer réessaie jusqu'à cinq fois quand votre endpoint ne répond pas 2xx, et ne suit pas les redirections.
Soyez donc honnête sur la raison d'ajouter un backend de formulaires. Voici ce que couvre chaque côté :
| Vous voulez | Framer seul | Avec Ikiform comme webhook |
|---|---|---|
| Soumissions dans une seule boîte ou une seule feuille | Oui | Oui, plus le tableau de bord |
| Un historique consultable avec le spam signalé, et un export CSV | Votre boîte de réception ou votre feuille fait office d'historique | Oui |
| Vérifications côté serveur des types, longueurs et options | Non | Oui, avec une définition de champs |
| Alertes à plusieurs personnes, avec Répondre qui va à l'auteur de la soumission | Destination e-mail | Jusqu'à 10 destinataires, avec adresse de réponse |
| Plusieurs outils alimentés par un seul formulaire, chacun avec nouvelles tentatives | Un webhook par destination ajoutée | Jusqu'à 3 endpoints signés, avec nouvelles tentatives et journal de livraison |
| Le même backend pour les sites Framer, Webflow et codés | Non | Oui |
Si la première ligne suffit à vos besoins, restez sur les destinations de Framer. La suite de cette page est pour les cas où elle ne suffit pas.
Le piège : Framer envoie depuis ses serveurs
Une destination webhook signifie que ce sont les serveurs de Framer qui envoient, pas le navigateur du visiteur. Trois choses changent côté Ikiform :
- Pas d'origine. Les envois serveur ne portent pas d'en-tête
Origin, donc avec Allowed domains défini, chaque envoi est traité comme venant du mauvais site. - Adresses IP partagées. Ikiform compte les limites de débit par adresse IP, et chaque envoi vient de quelques adresses Framer au lieu de celles de vos visiteurs.
- Pas de honeypot. Framer envoie les inputs que vous avez construits et rien d'autre, et son formulaire ne peut pas exécuter la vérification humaine d'Ikiform.
Vous pouvez l'accepter, ou le corriger avec un relais. Les deux chemins sont décrits ci-dessous.
- Direct : un visiteur envoie le formulaire Framer, les serveurs de Framer le transmettent en JSON sans en-tête Origin, et Ikiform l'accepte avec Allowed domains vide et une limite de débit Relaxed.
- Relais : les serveurs de Framer signent chaque envoi avec votre secret et l'envoient à un Worker que vous déployez.
- Le Worker recalcule la signature de Framer. Un envoi sans signature valide reçoit un 401 et ne va pas plus loin.
- Un envoi valide est transmis à Ikiform avec votre clé secrète et l'identifiant de soumission de Framer comme clé d'idempotence, donc une nouvelle tentative de Framer ne stocke jamais de seconde copie.
Option 1 : Ikiform comme webhook
- Dans Framer, donnez à chaque input un nom en minuscules :
name,work_email,company,team_size. Framer utilise les noms comme clés JSON. - Sélectionnez le formulaire et, à côté de Send To, choisissez Add… > Webhook. Collez votre endpoint. Laissez le secret vide ; Ikiform ne vérifie pas
Framer-Signature. - Dans les paramètres du formulaire Ikiform, laissez Allowed domains vide, laissez Spam check désactivé, et réglez Rate limit sur Relaxed.
- Publiez. Framer n'envoie de webhooks que depuis le site publié.
https://ikiform.com/f/frm_xxxxxxxxxxxxxxxxIkiform répond 201 une fois la soumission stockée, ce que Framer compte comme livré. Un 422 validation_failed échoue de la même façon aux cinq nouvelles tentatives, donc corrigez les noms des inputs plutôt que d'attendre. Le score de spam s'applique quand même à chaque envoi : liens, expressions de spam, adresses jetables et répétitions.
Option 2 : un relais signé
Déployez un Cloudflare Worker qui vérifie la signature de Framer et transmet les envois valides avec votre clé secrète. Le formulaire Ikiform peut alors lister votre domaine Framer sous Allowed domains avec Reject, pour que tout ce qui n'est ni un navigateur sur votre site ni votre Worker soit refusé.
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 });
},
};Le déployer
npx wrangler secret put FRAMER_SECRET
npx wrangler secret put IKIFORM_SECRET_KEY
npx wrangler deployFRAMER_SECRETest le secret que vous définissez sur la destination webhook de Framer. Framer exige au moins 32 caractères et ne le réaffichera pas, donc générez-le, conservez-le et collez-le aux deux endroits.IKIFORM_SECRET_KEYest une clé API secrète du tableau de bord Ikiform. Ne la placez jamais dans Framer.- La signature est
sha256=suivi du HMAC hexadécimal du corps brut suivi de l'identifiant de soumission. Lisez le corps une seule fois en octets et utilisez ces mêmes octets pour la vérification et la transmission. Idempotency-Keyfait qu'une nouvelle tentative de Framer rejoue la première réponse d'Ikiform au lieu de stocker un doublon. Les clés sont conservées 24 heures.- Le Worker transmet le statut d'Ikiform tel quel. Framer réessaie toute réponse autre que
2xx, jusqu'à cinq fois, donc un429ou un5xxa une autre chance, alors qu'un422échoue de la même façon à chaque fois jusqu'à ce que vous corrigiez les noms des inputs.
Faites ensuite pointer le webhook de Framer vers l'URL du Worker au lieu de l'endpoint, avec le secret renseigné.
IKIFORM_SECRET_KEY dans des variables d'environnement réservées au serveur.Option 3 : se passer du formulaire Framer
Un code component affiche un vrai formulaire et envoie depuis le navigateur du visiteur, donc les vérifications d'origine, le honeypot et les limites de débit par visiteur fonctionnent comme sur n'importe quel site. Vous perdez l'édition visuelle de Framer pour ce formulaire. Le guide Framer de la documentation contient le composant.
Vérifier que tout fonctionne sur Framer
- Publiez le site Framer et envoyez le formulaire depuis le site, pas depuis l'aperçu de l'éditeur.
- Ouvrez Submissions dans le tableau de bord. La nouvelle soumission y est, avec tous les champs envoyés.
- Vérifiez que les noms de champs correspondent exactement aux noms de vos inputs, sans clés ajoutées par Framer.
- Pour le relais : envoyez au Worker une requête avec un
Framer-Signatureinventé et vérifiez qu'il répond401. - Pour le relais : dans les détails de la soumission, l'origine est vide et le statut est reçu, pas spam.
- Vérifiez la boîte de réception de chaque destinataire des notifications. Chacun reçoit un e-mail avec les champs, et Reply répond à l'expéditeur quand le formulaire a un champ
email. - Si vous avez ajouté un webhook, ouvrez l'endpoint dans Webhooks. La livraison affiche
succeededavec la réponse de votre serveur.
Pièges
| Problème | Que faire |
|---|---|
Chaque envoi de webhook est classé en spam avec origin_mismatch | Allowed domains est défini alors que Framer envoie directement. Videz-le, ou placez le relais entre les deux. |
Le Worker répond 401 aux vrais envois | Le secret diffère entre Framer et le Worker, ou quelque chose a lu le corps comme texte et l'a modifié. Collez à nouveau le secret et hachez les octets bruts. |
429 rate_limited pendant un lancement | Tous les envois partagent les adresses de Framer ou de Cloudflare. Choisissez Relaxed, ou une limite Custom. |
422 validation_failed | L'envoi ne correspond pas à la définition des champs du formulaire. error.fields nomme chaque champ et la raison. Faites correspondre le name de l'input à l'id du champ. |
403 challenge_required | Le spam check du formulaire est réglé sur Ikiform et un envoi fetch n'avait pas de jeton. Obtenez-en un avec challenge(formId) de @ikiform/sdk avant chaque envoi, ou désactivez la vérification. |
Questions
Pas toujours. Framer envoie lui-même les soumissions par e-mail, vers Google Sheets ou vers un webhook. Ajoutez Ikiform quand vous voulez un historique stocké et filtré, une validation côté serveur, des alertes d'équipe avec adresse de réponse, ou plusieurs webhooks avec nouvelles tentatives depuis un seul formulaire.
Non. Ikiform authentifie les envois par origine ou par votre clé API secrète, pas par l'en-tête de Framer. Pour vérifier Framer-Signature, placez entre Framer et Ikiform un petit relais qui la vérifie et transmet avec votre clé secrète.
Le plus souvent parce que Allowed domains est défini. Les envois webhook de Framer viennent de ses serveurs sans en-tête Origin, donc ils ne correspondent à aucun domaine. Videz la liste, ou utilisez un relais qui envoie avec votre clé secrète.
Elles le peuvent en envoi direct, si une livraison est retentée après qu'Ikiform l'a stockée. Le relais envoie l'identifiant de soumission de Framer comme clé d'idempotence, donc une nouvelle tentative rejoue plutôt la première réponse.
Pas encore. Un envoi contenant un fichier est refusé avec 422 files_not_supported. Demandez plutôt un lien vers le fichier, comme une URL de drive partagé.
Une étape vous bloque, ou vous voulez un guide pour votre cas ? Écrivez à hello@ikiform.com.