# Rode uma pesquisa NPS no seu próprio site, do link no e-mail até a nota

> Rode uma pesquisa NPS com a nota de 0 a 10 no e-mail, uma pergunta de acompanhamento na sua própria página que muda com a nota e um script que calcula o NPS.

Publicado em 2026-10-25 · https://ikiform.com/pt-br/guides/feedback-form/nps-survey

Coloque onze links, de 0 a 10, no e-mail da pesquisa, cada um apontando para uma página no seu site com a nota e um token opaco do respondente na URL. A página pré-seleciona a nota, faz uma pergunta de acompanhamento que depende dela e não registra nada até a pessoa clicar em Enviar. Armazene a nota num campo number de 0 a 10 e calcule o NPS como a parcela de 9 e 10 menos a parcela de 0 a 6.

## O que muda no NPS

O NPS faz uma pergunta com uma escala fixa: qual a probabilidade de você nos recomendar, de 0 a 10. Três detalhes o diferenciam da nota de 1 a 5 do guia principal:

- **A escala começa em 0.** O campo `rating` do Ikiform vai de 1 a no máximo 10, então não comporta um 0. O NPS usa um campo `number` com `min: 0`, `max: 10` e `integer: true`, armazenado como número.
- **Normalmente é enviado por e-mail.** Clientes de e-mail não enviam formulários nem rodam scripts de forma confiável, então o e-mail leva links, e o formulário fica no seu site. O Ikiform não tem páginas de formulário hospedadas.
- **Links em e-mails são clicados por máquinas.** Muitos scanners de segurança de e-mail abrem cada link de uma mensagem para verificá-lo. Se abrir um link registrasse uma nota, você coletaria notas de scanners. A página só registra quando uma pessoa clica em Enviar.

Diagrama: Como cada faixa de notas do NPS ganha sua própria pergunta de acompanhamento e conta para o resultado

1. Uma nota de 0 a 6 pergunta o que decepcionou a pessoa, e conta como detratora.
2. Um 7 ou 8 pergunta o que faria virar um 10, e conta só para o total.
3. Um 9 ou 10 pergunta o que a pessoa mais valoriza, e conta como promotora.
4. O NPS é a porcentagem de promotores menos a porcentagem de detratores, de menos 100 a 100.

A pergunta muda com a nota porque a resposta útil muda. O motivo de um detrator é um problema para corrigir; o de um promotor é texto para a sua homepage.

## Os campos

Formulário: NPS survey

- **Qual a probabilidade de você nos recomendar a um amigo ou colega?** (`score`, number, obrigatório)
- **Qual o principal motivo da sua nota?** (`reason`, textarea)
- **Respondente** (`respondent`, text)
- **Rodada da pesquisa** (`wave`, text, obrigatório)

Tente uma nota 11 ou 7.5: o Ikiform recusa as duas. Um `wave` ausente também é recusado, então toda resposta pertence a uma rodada de pesquisa.

| Campo | Por quê |
| --- | --- |
| `score` | `number`, de 0 a 10, só números inteiros. Armazenado como número |
| `reason` | A pergunta de acompanhamento. Opcional, porque uma nota sozinha ainda conta |
| `respondent` | Um token opaco que você gera por pessoa e envia. Leva de volta ao cliente no seu próprio banco de dados |
| `wave` | Qual rodada, como `2026-q4`. Permite comparar trimestres sem filtros de data |

Não coloque o e-mail nem o id da conta do cliente no link. URLs vão parar no histórico do navegador, em logs de proxy e em e-mails encaminhados. Um token aleatório de 16 caracteres ou mais diz quem respondeu, e nada a mais ninguém.

## O e-mail da pesquisa

Monte a linha de links com as merge tags da sua ferramenta de e-mail, ou gere-a no código que envia o e-mail. Esta função devolve o HTML para um respondente:

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

Limite o e-mail à pergunta e à linha de números. Uma introdução longa empurra a escala para baixo da dobra nos celulares, e a maioria das respostas vem de quem clica num número sem rolar.

## A landing page

Coloque isto em `/nps` no seu site. A página desenha a escala de 0 a 10, pré-seleciona a nota do link, define a pergunta de acompanhamento para essa nota e envia com `fetch` só quando a pessoa clica em Enviar. Se ela mudar a nota na página, a pergunta muda junto.

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

> A página também funciona sem link: quem abre `/nps` diretamente vê a escala sem nada selecionado. A resposta é recusada por falta de `wave`, a menos que você defina um padrão, o que mantém visitas perdidas fora dos seus números.

## Calcule a nota

Leia todas as respostas de uma rodada com o SDK, fique com a resposta mais recente de cada respondente e aplique a fórmula. Rode com uma chave secreta em `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}`);
```

O script mantém uma resposta por respondente porque às vezes as pessoas clicam num segundo número depois de mudar de ideia. O Ikiform não junta nem rejeita envios repetidos por e-mail. Uma repetição idêntica em até 24 horas só soma à pontuação de spam. Remova os duplicados onde a lista fica, ou ao exportar.

## Confira se funciona

1. Abra `/nps?score=3&r=test_respondent_0001&wave=2026-q4`. O 3 aparece selecionado e a pergunta diz "O que mais decepcionou você?"
2. Clique em 9. A pergunta muda para "O que você mais valoriza nele?"
3. Abra o mesmo link com `curl`. Nada novo aparece em **Submissions**, porque nada foi enviado.
4. Clique em Enviar. A resposta mostra `score` como o número 9.
5. Rode `node nps.mjs 2026-q4`. Ele informa uma pessoa e um promotor.

## Pegadinhas

- Não faça a pesquisa com o mesmo cliente com frequência demais. Escolha um intervalo, como uma vez por trimestre, e mantenha a lista de respondentes do seu lado.
- Amostras pequenas oscilam muito. Com 20 respostas, uma pessoa move o NPS em 5 pontos. Informe o número de pessoas ao lado da pontuação.
- Notas baixas merecem resposta. Mande os detratores para o chat da equipe com um relay de webhook como o [alerta de nota baixa](https://ikiform.com/pt-br/guides/feedback-form.md#routing) do guia principal, usando `score <= 6`.
- O [template de formulário](https://ikiform.com/forms/survey) de pesquisa pergunta a nota dentro de uma pesquisa mais longa. Isso serve para um estudo pontual; uma pergunta de NPS isolada recebe mais respostas.
- O Ikiform não envia e-mail para quem preenche o formulário. Os e-mails de notificação vão só para você e sua equipe, então não há resposta automática, e-mail de confirmação nem double opt-in. Envie esses e-mails pela sua ferramenta de e-mail, disparados por um [webhook](https://ikiform.com/pt-br/docs/webhooks.md).

## Solução de problemas

| Problema | O que fazer |
| --- | --- |
| `422` com `Must be at most 10`, ou um erro de número inteiro | A nota veio de outro lugar que não a escala, como uma URL editada à mão. A página só aceita de 0 a 10 vindo do link; confira os links do seu template de e-mail. |
| `422` citando `wave` | O link não levou `wave`, ou o valor não segue o formato `2026-q4`. Corrija o template de e-mail ou defina um padrão no script da página. |
| `403 origin_not_allowed`, ou todo envio cai no spam | O domínio da página não está em **Allowed domains** do formulário. Adicione-o, mais `localhost` para testes locais. Subdomínios de um domínio listado são permitidos. |
| `429 rate_limited` | Um visitante enviou mais do que o limite de taxa do formulário permite. O Standard permite 5 por minuto por visitante. Espere o `Retry-After`, ou escolha Relaxed ou Custom para formulários que enviam a partir de um único IP compartilhado. |

## Perguntas

### Como colocar uma pergunta de NPS num e-mail?

Coloque onze links, de 0 a 10, no e-mail, cada um abrindo uma página do seu site com a nota na URL. A página pré-seleciona a nota, faz uma pergunta de acompanhamento e registra a resposta quando a pessoa clica em Enviar.

### Posso usar o campo de avaliação do Ikiform para NPS?

Não. Uma avaliação vai de 1 até no máximo 10, e o NPS precisa do 0. Use um campo numérico com min 0, max 10 e integer true.

### Como o NPS é calculado?

Pegue a porcentagem de respostas 9 ou 10, subtraia a porcentagem de respostas de 0 a 6 e ignore 7 e 8, exceto no total. O resultado vai de menos 100 a 100.

### Por que não registrar a nota assim que o link é clicado?

Scanners de segurança de e-mail abrem os links das mensagens recebidas para verificá-los. Registrar no clique contaria essas visitas como respostas. Registrar só no Enviar mantém os números honestos.

## Mais guias

- [Formulário de feedback para um site ou app](https://ikiform.com/pt-br/guides/feedback-form.md): Coloque um formulário de feedback em qualquer página ou dentro do seu app: uma nota, um comentário, a página de origem, filtro de spam e alertas para notas baixas.
- [Pesquisa de cancelamento](https://ikiform.com/pt-br/guides/feedback-form/cancellation-survey.md): Pergunte por que os clientes cancelam dentro do fluxo de cancelamento do seu SaaS, deixe o Pular a um clique, salve o motivo no Stripe e no Ikiform e nunca atrase o cancelamento.
- [Feedback de beta testers](https://ikiform.com/pt-br/guides/feedback-form/beta-tester-feedback.md): Um formulário de feedback de beta com um link pessoal por tester e build, duas escalas de nota e um resumo semanal que mostra se cada build melhorou.
- [Widget de feedback em React](https://ikiform.com/pt-br/guides/feedback-form/react-feedback-widget.md): Um botão de feedback flutuante para um app React que abre um pequeno painel, envia a tela, a versão do app e a conta com cada resposta e nunca perde um rascunho.
- [Formulário de relato de bug](https://ikiform.com/pt-br/guides/feedback-form/bug-report.md): Um formulário de relato de bug que pede às pessoas os passos e a gravidade, e deixa a página adicionar sozinha a URL, o navegador, o viewport e os erros recentes.
- [Formulário de pedido de funcionalidade](https://ikiform.com/pt-br/guides/feedback-form/feature-request.md): Um formulário de pedido de funcionalidade que pergunta pelo problema em vez da solução, armazena pedidos por tema, conta pessoas, não linhas, e avisa quando sair.
