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

Publié le 2026-10-25 · https://ikiform.com/fr/guides/feedback-form/nps-survey

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.

Schéma : Comment chaque tranche de scores NPS reçoit sa propre question de suivi et compte dans le résultat

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

Formulaire : NPS survey

- **Quelle est la probabilité que vous nous recommandiez à un ami ou à un collègue ?** (`score`, number, obligatoire)
- **Quelle est la principale raison de votre note ?** (`reason`, textarea)
- **Répondant** (`respondent`, text)
- **Vague d'enquête** (`wave`, text, obligatoire)

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.

| Champ | Pourquoi |
| --- | --- |
| `score` | `number`, de 0 à 10, nombres entiers uniquement. Stocké comme un nombre |
| `reason` | La question de suivi. Facultative, car un score seul compte quand même |
| `respondent` | Un token opaque que vous générez par personne et envoyez. Renvoie au client dans votre propre base de données |
| `wave` | Quelle 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`:

```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`:

```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`:

```js
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](https://ikiform.com/fr/guides/feedback-form.md#routing) du guide principal, avec `score <= 6`.
- Le [modèle de formulaire](https://ikiform.com/forms/survey) 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](https://ikiform.com/fr/docs/webhooks.md).

## Dépannage

| Problème | Que faire |
| --- | --- |
| `422` avec `Must be at most 10`, ou une erreur de nombre entier | Le 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 `wave` | Le 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 spam | Le 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_limited` | Un 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

### Comment mettre une question NPS dans un e-mail ?

Mettez onze liens, de 0 à 10, dans l'e-mail, chacun ouvrant une page de votre site avec le score dans l'URL. La page le présélectionne, pose une question de suivi et enregistre la réponse quand la personne appuie sur Envoyer.

### Puis-je utiliser le champ rating d'Ikiform pour le NPS ?

Non. Un rating va de 1 à 10 au plus, et le NPS a besoin du 0. Utilisez un champ number avec min 0, max 10 et integer true.

### Comment calcule-t-on le NPS ?

Prenez le pourcentage de réponses à 9 ou 10, soustrayez le pourcentage de réponses de 0 à 6, et ignorez les 7 et 8 sauf dans le total. Le résultat va de moins 100 à 100.

### Pourquoi ne pas enregistrer le score dès que le lien est cliqué ?

Les scanners de sécurité de messagerie ouvrent les liens des e-mails entrants pour les vérifier. Enregistrer au clic compterait ces visites comme des réponses. N'enregistrer qu'à l'envoi garde des chiffres honnêtes.

## Autres guides

- [Formulaire de feedback pour un site ou une application](https://ikiform.com/fr/guides/feedback-form.md): Placez un formulaire de feedback sur n'importe quelle page ou dans votre application : une note, un commentaire, la page d'origine, le filtrage du spam et des alertes quand une note est basse.
- [Enquête de résiliation](https://ikiform.com/fr/guides/feedback-form/cancellation-survey.md): Demandez pourquoi vos clients résilient dans le parcours d'annulation de votre SaaS, gardez Passer à un clic, enregistrez la raison dans Stripe et Ikiform, sans jamais retarder la résiliation.
- [Feedback des bêta-testeurs](https://ikiform.com/fr/guides/feedback-form/beta-tester-feedback.md): Un formulaire de feedback bêta avec un lien personnel par testeur et par build, deux échelles de notation et un résumé hebdomadaire qui montre si chaque build s'est amélioré.
- [Widget de feedback React](https://ikiform.com/fr/guides/feedback-form/react-feedback-widget.md): Un bouton de feedback flottant pour une application React qui ouvre un petit panneau, envoie l'écran, la version et le compte avec chaque réponse, et ne perd jamais un brouillon.
- [Formulaire de signalement de bug](https://ikiform.com/fr/guides/feedback-form/bug-report.md): Un formulaire de signalement de bug qui demande les étapes et la gravité, et laisse la page ajouter elle-même l'URL, le navigateur, le viewport et les erreurs récentes.
- [Formulaire de demande de fonctionnalité](https://ikiform.com/fr/guides/feedback-form/feature-request.md): Un formulaire de demande de fonctionnalité qui demande le problème plutôt que la solution, range les demandes par thème, compte les personnes et non les lignes, et les prévient quand c'est livré.
