Formulaire de feedback pour un site ou une application

Formulaire de feedback pour un site ou une application7 min de lecture

Mener une enquête NPS sur votre propre site, du lien dans l'e-mail au score

Menez une enquête NPS avec un score de 0 à 10 dans l'e-mail, une question de suivi sur votre propre page qui change selon le score, et un script qui calcule le NPS.

Preet SutharFondateur, Ikiform

Mettez onze liens, de 0 à 10, dans l'e-mail d'enquête, chacun pointant vers une page de votre site avec le score et un token de répondant opaque dans l'URL. La page présélectionne le score, pose une question de suivi qui en dépend et n'enregistre rien tant que la personne n'a pas appuyé sur Envoyer. Stockez le score dans un champ number de 0 à 10, puis calculez le NPS comme la part de 9 et de 10 moins la part de 0 à 6.

Ce qui change avec le NPS

Le NPS pose une seule question sur une échelle fixe : quelle est la probabilité que vous nous recommandiez, de 0 à 10. Trois détails le distinguent de la note de 1 à 5 du guide principal :

  • L'échelle commence à 0. Le champ rating d'Ikiform va de 1 à 10 au plus, il ne peut donc pas contenir de 0. Le NPS utilise un champ number avec min: 0, max: 10 et integer: true, stocké comme un nombre.
  • Il est généralement envoyé par e-mail. Les clients e-mail n'envoient pas les formulaires et n'exécutent pas les scripts de façon fiable : l'e-mail contient donc des liens, et le formulaire se trouve sur votre site. Ikiform ne propose pas de pages de formulaire hébergées.
  • Les liens des e-mails sont cliqués par des machines. Beaucoup de scanners de sécurité de messagerie ouvrent chaque lien d'un message pour le vérifier. Si ouvrir un lien enregistrait un score, vous collecteriez les scores des scanners. La page n'enregistre que lorsqu'une personne appuie sur Envoyer.
Comment chaque tranche de scores NPS reçoit sa propre question de suivi et compte dans le résultat0 à 6Qu'est-ce qui vous ale plus déçu ?détracteursoustrait7 ou 8Qu'est-ce qui enferait un 10 ?passifcompté dans le totalseulement9 ou 10Qu'appréciez-vousle plus ?promoteurajouténotela question de suivicompte commeComment chaque tranche de scores NPS reçoit sa propre question de suivi et compte dans le résultat0 à 6Qu'est-cequivous ale plusdéçu ?détracteursoustrait7 ou 8Qu'est-cequi enferaitun 10 ?passifcomptédans letotalseulement9 ou 10Qu'appréciez-vousleplus ?promoteurajouténotela questionde suivicompte comme
  1. Un score de 0 à 6 demande ce qui a déçu, et compte comme détracteur.
  2. Un 7 ou un 8 demande ce qui en ferait un 10, et ne compte que dans le total.
  3. Un 9 ou un 10 demande ce qui est le plus apprécié, et compte comme promoteur.
  4. Le NPS est le pourcentage de promoteurs moins le pourcentage de détracteurs, de moins 100 à 100.
La question change avec le score parce que la réponse utile change. La raison d'un détracteur est un problème à corriger ; celle d'un promoteur, c'est du texte pour votre page d'accueil.

Les champs

Aperçu

Exécute le validateur d'Ikiform dans votre navigateur. Rien n'est envoyé.

Essayez un score de 11 ou de 7.5 : Ikiform refuse les deux. Un wave manquant est refusé aussi, pour que chaque réponse appartienne à une vague d'enquête.
ChampPourquoi
scorenumber, de 0 à 10, nombres entiers uniquement. Stocké comme un nombre
reasonLa question de suivi. Facultative, car un score seul compte quand même
respondentUn token opaque que vous générez par personne et envoyez. Renvoie au client dans votre propre base de données
waveQuelle vague, comme 2026-q4. Permet de comparer les trimestres sans filtre de date

Ne mettez ni l'e-mail ni l'id de compte du client dans le lien. Les URL finissent dans l'historique du navigateur, les logs de proxy et les e-mails transférés. Un token aléatoire de 16 caractères ou plus vous dit qui a répondu, et ne dit rien à personne d'autre.

Le message d'enquête

Construisez la rangée de liens avec les balises de fusion de votre outil d'e-mailing, ou générez-la dans le code qui envoie l'e-mail. Cette fonction renvoie le HTML pour un répondant :

nps-email.js
function npsRow(respondent, wave) {
  const cells = Array.from({ length: 11 }, (_, score) => {
    const link = new URL("https://example.com/nps");
    link.searchParams.set("score", String(score));
    link.searchParams.set("r", respondent);
    link.searchParams.set("wave", wave);
    return `<td><a href="${link}" style="display:block;padding:8px 10px">${score}</a></td>`;
  });
  return `<table role="presentation"><tr>${cells.join("")}</tr></table>
<p>0 is not at all likely, 10 is extremely likely.</p>`;
}

Limitez l'e-mail à la question et à la rangée de chiffres. Une longue introduction repousse l'échelle sous la ligne de flottaison sur mobile, et la plupart des réponses viennent de personnes qui cliquent sur un chiffre sans faire défiler.

La page d'atterrissage

Placez ceci à l'adresse /nps de votre site. La page dessine l'échelle de 0 à 10, présélectionne le score depuis le lien, règle la question de suivi pour ce score et envoie avec fetch seulement quand la personne appuie sur Envoyer. Si elle change le score sur la page, la question change avec.

nps.html
<form id="nps" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <fieldset>
    <legend>How likely are you to recommend us to a friend or colleague?</legend>
    <div id="nps-scale"></div>
  </fieldset>
  <label for="reason" id="reason-label">What's the main reason for your score?</label>
  <textarea id="reason" name="reason" rows="4" maxlength="4000"></textarea>
  <input type="hidden" name="respondent" />
  <input type="hidden" name="wave" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Send</button>
  <p id="nps-status" role="status"></p>
</form>

<script>
  const QUESTIONS = [
    [6, "What disappointed you most?"],
    [8, "What would make it a 10 for you?"],
    [10, "What do you value most about it?"],
  ];
  const params = new URLSearchParams(location.search);
  const form = document.getElementById("nps");
  const label = document.getElementById("reason-label");
  const scale = document.getElementById("nps-scale");

  for (let score = 0; score <= 10; score += 1) {
    scale.insertAdjacentHTML(
      "beforeend",
      `<label><input type="radio" name="score" value="${score}" required /> ${score}</label>`,
    );
  }

  function askFor(score) {
    label.textContent = QUESTIONS.find(([top]) => score <= top)[1];
  }

  const preset = Number(params.get("score"));
  if (params.has("score") && Number.isInteger(preset) && preset >= 0 && preset <= 10) {
    form.elements.score.value = String(preset);
    askFor(preset);
  }
  form.elements.respondent.value = params.get("r") ?? "";
  form.elements.wave.value = params.get("wave") ?? "";
  scale.addEventListener("change", (event) => askFor(Number(event.target.value)));

  form.addEventListener("submit", async (event) => {
    event.preventDefault();
    const status = document.getElementById("nps-status");
    status.textContent = "Sending";
    const response = await fetch(form.action, {
      method: "POST",
      headers: { Accept: "application/json" },
      body: new FormData(form),
    }).catch(() => null);
    const result = await response?.json().catch(() => null);
    if (result?.ok) {
      form.replaceChildren(Object.assign(document.createElement("p"), { textContent: "Thanks for answering." }));
      return;
    }
    status.textContent = result?.error?.fields?.score?.[0] ?? result?.error?.message ?? "Couldn't send it. Try again.";
  });
</script>
La page fonctionne aussi sans lien : quelqu'un qui ouvre /nps directement voit l'échelle sans rien de sélectionné. Sa réponse est refusée faute de wave, sauf si vous définissez une valeur par défaut, ce qui garde les visites égarées hors de vos chiffres.

Calculer le score

Lisez toutes les réponses d'une vague avec le SDK, gardez la dernière réponse de chaque répondant et appliquez la formule. Lancez-le avec une clé secrète dans IKIFORM_SECRET_KEY :

nps.mjs
import { createClient } from "@ikiform/sdk";

const WAVE = process.argv[2] ?? "2026-q4";
const ikiform = createClient({ apiKey: process.env.IKIFORM_SECRET_KEY });
const latest = new Map();

for await (const submission of ikiform.submissions.iterate({ formId: "frm_xxxxxxxxxxxxxxxx", status: "received" })) {
  const { score, respondent, wave } = submission.fields;
  if (wave !== WAVE) continue;
  const key = respondent || submission.id;
  const seen = latest.get(key);
  if (!seen || seen.at < submission.created_at) latest.set(key, { score, at: submission.created_at });
}

const scores = [...latest.values()].map((answer) => answer.score);
const promoters = scores.filter((value) => value >= 9).length;
const detractors = scores.filter((value) => value <= 6).length;
const nps = Math.round(((promoters - detractors) / scores.length) * 100);

console.log(`${WAVE}: ${scores.length} people, ${promoters} promoters, ${detractors} detractors, NPS ${nps}`);

Le script garde une réponse par répondant, car il arrive qu'on clique sur un second chiffre après avoir changé d'avis. Ikiform ne fusionne ni ne rejette les soumissions répétées par e-mail. Une répétition identique dans les 24 heures ne fait qu'augmenter le score de spam. Dédupliquez là où vit la liste, ou à l'export.

Vérifiez que tout fonctionne

  1. Ouvrez /nps?score=3&r=test_respondent_0001&wave=2026-q4. Le 3 est sélectionné et la question est "What disappointed you most?"
  2. Cliquez sur 9. La question devient "What do you value most about it?"
  3. Ouvrez le même lien avec curl. Rien de nouveau n'apparaît sous Submissions, car rien n'a été envoyé.
  4. Appuyez sur Envoyer. La réponse affiche score avec le nombre 9.
  5. Lancez node nps.mjs 2026-q4. Il indique une personne et un promoteur.

Pièges

  • N'interrogez pas trop souvent le même client. Choisissez un intervalle, par exemple une fois par trimestre, et gardez la liste des répondants de votre côté.
  • Les petits échantillons varient énormément. Avec 20 réponses, une seule personne fait bouger le NPS de 5 points. Indiquez le nombre de personnes à côté du score.
  • Les notes basses méritent une réponse. Envoyez les détracteurs dans le chat de votre équipe avec un relais de webhook comme l'alerte sur les notes basses du guide principal, avec score <= 6.
  • Le modèle de formulaire d'enquête pose le score dans une enquête plus longue. Cela convient à une étude ponctuelle ; une question NPS seule obtient plus de réponses.
  • Ikiform n'envoie pas d'e-mail à la personne qui remplit le formulaire. Les e-mails de notification vont uniquement à vous et à votre équipe : il n'y a donc ni réponse automatique, ni e-mail de confirmation, ni double opt-in. Envoyez-les depuis votre outil d'e-mailing, déclenché par un webhook.

Dépannage

ProblèmeQue faire
422 avec Must be at most 10, ou une erreur de nombre entierLe score vient d'ailleurs que de l'échelle, par exemple d'une URL modifiée à la main. La page n'accepte que 0 à 10 depuis le lien ; vérifiez les liens de votre modèle d'e-mail.
422 en nommant waveLe lien ne portait pas wave, ou sa valeur ne ressemble pas à 2026-q4. Corrigez le modèle d'e-mail, ou définissez une valeur par défaut dans le script de la page.
403 origin_not_allowed, ou tous les envois finissent en spamLe domaine de la page n'est pas dans les Allowed domains du formulaire. Ajoutez-le, plus localhost pour les tests en local. Les sous-domaines d'un domaine listé sont autorisés.
429 rate_limitedUn visiteur a envoyé plus que ce que permet la limite de débit du formulaire. Standard autorise 5 envois par minute et par visiteur. Attendez la durée de Retry-After, ou choisissez Relaxed ou Custom pour les formulaires qui envoient depuis une seule IP partagée.

Questions

Une étape vous bloque, ou vous voulez un guide pour votre cas ? Écrivez à hello@ikiform.com.