Formulário de feedback para um site ou app6 min de leitura
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.
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
ratingdo Ikiform vai de 1 a no máximo 10, então não comporta um 0. O NPS usa um camponumbercommin: 0,max: 10einteger: 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.
- Uma nota de 0 a 6 pergunta o que decepcionou a pessoa, e conta como detratora.
- Um 7 ou 8 pergunta o que faria virar um 10, e conta só para o total.
- Um 9 ou 10 pergunta o que a pessoa mais valoriza, e conta como promotora.
- O NPS é a porcentagem de promotores menos a porcentagem de detratores, de menos 100 a 100.
Os campos
Prévia
Roda o validador do Ikiform no seu navegador. Nada é enviado.
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:
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.
<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>/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:
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
- Abra
/nps?score=3&r=test_respondent_0001&wave=2026-q4. O 3 aparece selecionado e a pergunta diz "O que mais decepcionou você?" - Clique em 9. A pergunta muda para "O que você mais valoriza nele?"
- Abra o mesmo link com
curl. Nada novo aparece em Submissions, porque nada foi enviado. - Clique em Enviar. A resposta mostra
scorecomo o número 9. - 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 do guia principal, usando
score <= 6. - O template de formulário 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.
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
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.
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.
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.
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.
Travou em algum passo ou quer um guia para o seu caso? Envie um e-mail para hello@ikiform.com.