# 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.

Publié le 2026-10-25 · https://ikiform.com/fr/guides/website-builder-forms/framer-forms

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.

Schéma : Un formulaire Framer qui envoie à Ikiform directement, et via un relais signé

1. 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.
2. Relais : les serveurs de Framer signent chaque envoi avec votre secret et l'envoient à un Worker que vous déployez.
3. Le Worker recalcule la signature de Framer. Un envoi sans signature valide reçoit un 401 et ne va pas plus loin.
4. 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.

Le relais tient en une douzaine de lignes de vraie logique. En échange, le formulaire peut garder des domaines autorisés stricts, car seul votre Worker détient la clé secrète.

## Option 1 : Ikiform comme webhook

1. Dans Framer, donnez à chaque input un nom en minuscules : `name`, `work_email`, `company`, `team_size`. Framer utilise les noms comme clés JSON.
2. 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`.
3. Dans les paramètres du formulaire Ikiform, laissez **Allowed domains** vide, laissez **Spam check** désactivé, et réglez **Rate limit** sur **Relaxed**.
4. Publiez. Framer n'envoie de webhooks que depuis le site publié.

`URL du webhook`:

```text
https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx
```

Ikiform 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é.

`src/index.ts`:

```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 });
  },
};
```

### Le déployer

`Terminal`:

```bash
npx wrangler secret put FRAMER_SECRET
npx wrangler secret put IKIFORM_SECRET_KEY
npx wrangler deploy
```

- `FRAMER_SECRET` est 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_KEY` est 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-Key` fait 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 un `429` ou un `5xx` a une autre chance, alors qu'un `422` é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é.

> Chaque envoi vient désormais de votre Worker. Les versions côté serveur envoient avec une clé secrète, donc Ikiform ignore **Allowed domains** et sa vérification humaine. Chaque envoi vient alors de l'IP de votre serveur, et la limite Standard de 5 par minute et par visiteur compte tous vos visiteurs ensemble. Choisissez la [limite de débit](https://ikiform.com/fr/docs/rate-limits.md) Relaxed ou Custom pour un formulaire que vous envoyez via un serveur, et gardez `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](https://ikiform.com/fr/docs/guides/framer.md#code-component) contient le composant.

## Vérifier que tout fonctionne sur Framer

1. Publiez le site Framer et envoyez le formulaire depuis le site, pas depuis l'aperçu de l'éditeur.
2. Ouvrez **Submissions** dans le tableau de bord. La nouvelle soumission y est, avec tous les champs envoyés.
3. Vérifiez que les noms de champs correspondent exactement aux noms de vos inputs, sans clés ajoutées par Framer.
4. Pour le relais : envoyez au Worker une requête avec un `Framer-Signature` inventé et vérifiez qu'il répond `401`.
5. Pour le relais : dans les détails de la soumission, l'origine est vide et le statut est reçu, pas spam.
6. 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`.
7. Si vous avez ajouté un webhook, ouvrez l'endpoint dans **Webhooks**. La livraison affiche `succeeded` avec 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

### Ai-je besoin d'un backend de formulaires pour les formulaires Framer ?

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.

### Ikiform vérifie-t-il la signature du webhook de Framer ?

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.

### Pourquoi mes soumissions Framer sont-elles marquées comme spam ?

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.

### Les nouvelles tentatives de Framer créeront-elles des soumissions en double ?

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.

### Le formulaire peut-il accepter des envois de fichiers ?

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é.

## Autres guides

- [Dépasser les formulaires intégrés](https://ikiform.com/fr/guides/website-builder-forms.md): Quand les formulaires de Webflow, Netlify, Carrd ou Framer atteignent un plafond ou manquent d'une fonctionnalité, gardez le formulaire que vous avez conçu et faites-le pointer vers un backend de formulaires.
- [Limite de soumissions Webflow](https://ikiform.com/fr/guides/website-builder-forms/webflow-submission-limit.md): L'offre gratuite Starter de Webflow cesse de collecter après 50 soumissions de formulaire par site et les sites exportés n'en reçoivent aucune, alors faites plutôt pointer le Form Block vers Ikiform.
- [Formulaires Carrd](https://ikiform.com/fr/guides/website-builder-forms/carrd-forms.md): Le formulaire de contact de Carrd ne fait que vous envoyer un e-mail et les formulaires personnalisés nécessitent Pro Plus, alors utilisez Send to URL sur Plus ou une intégration HTML sur Standard pour stocker chaque soumission.
- [Limite de Netlify Forms](https://ikiform.com/fr/guides/website-builder-forms/netlify-forms-limit.md): Les anciennes offres Netlify comptent les soumissions de formulaire par site et vous font passer automatiquement au niveau supérieur, et les offres à crédits peuvent mettre le site en pause, alors déplacez le formulaire vers un backend.
