Skip to main content

Add an in-app NPS or CSAT survey

Ask a standard NPS or CSAT question inside your product, ask for the reason behind a score, and count one answer per person per cycle or interaction.

Build this with your agent. Copy a prompt that hands this guide to Claude Code, Codex, Cursor, or any coding agent.

Ask a standard NPS or CSAT question inside your product and let Fillo compute the score. You end with a survey that asks for the reason behind a score and counts one answer per person for each cycle or interaction.

Choose NPS or CSAT

NPSCSAT
QuestionHow likely are you to recommend us?How satisfied were you with this interaction?
MeasuresLoyalty to the product overallSatisfaction with one moment
Fieldlinear_scale from 0 to 10rating or linear_scale from 1 to 5
insightsMetric"nps""csat"
Score in Fillo% of 9–10 answers minus % of 0–6 answers% of 4–5 answers
When to askOn a schedule, such as each quarterRight after the interaction
One answer perPerson and survey cyclePerson and interaction ID

Fillo scores a field only when its scale matches. A 1 to 5 rating marked as NPS gets no NPS score.

Add an NPS survey

ts
import { defineForm } from "@usefillo/react";

export const nps = defineForm({
  id: "quarterly-nps",
  title: "How are we doing?",
  pages: [{
    id: "score",
    blocks: [
      { id: "nps_cycle", kind: "hidden", label: "Survey cycle" },
      {
        id: "recommend",
        kind: "linear_scale",
        label: "How likely are you to recommend us?",
        min: 0,
        max: 10,
        minLabel: "Not likely",
        maxLabel: "Very likely",
        insightsMetric: "nps",
        required: true,
      },
      {
        id: "why",
        kind: "long_text",
        label: "What's the main reason for your score?",
        visibleIf: [{ fieldId: "recommend", op: "answered" }],
      },
    ],
  }],
  settings: {
    submitLabel: "Send feedback",
    successTitle: "Thanks for the feedback",
    responseLimit: { by: "identify", scopeField: "nps_cycle", onRepeat: "keep" },
  },
});

Render it for a signed-in user and pass the current cycle:

tsx
<FilloForm
  form={nps}
  client={client}
  respondent={respondent} // { id, email, hash }, hashed on your server
  initialData={{ nps_cycle: "2026-q4" }}
/>

Add a CSAT survey

ts
import { defineForm } from "@usefillo/react";

export const csat = defineForm({
  id: "support-csat",
  title: "Rate your support experience",
  pages: [{
    id: "feedback",
    blocks: [
      { id: "ticket_id", kind: "hidden", label: "Ticket", paramName: "ticket" },
      {
        id: "satisfaction",
        kind: "rating",
        label: "How satisfied are you with the help you received?",
        max: 5,
        insightsMetric: "csat",
        required: true,
      },
      {
        id: "improve",
        kind: "long_text",
        label: "What could we improve?",
        visibleIf: [{ fieldId: "satisfaction", op: "lt", value: 4 }],
      },
    ],
  }],
  settings: {
    submitLabel: "Send rating",
    successTitle: "Feedback received",
    responseLimit: { by: "identify", scopeField: "ticket_id", onRepeat: "keep" },
  },
});

Name the thing being rated in the label, such as the support case, the purchase, or the onboarding step. Pass the ID with initialData={{ ticket_id }} in your app, or as ?ticket= on an emailed link.

Limit repeat answers

by: "identify" counts a signed-in person, verified by the hash your server computes. scopeField allows one answer per person for each value. One customer can rate several tickets, but each ticket once.

An identify limit rejects submissions it can't tie to a person. Use it only where the form knows who is answering. See respondents and identity for the hash.

Why the hidden ID isn't proof

A visitor can change a URL parameter or a hidden value. The hidden ID scopes and routes the answer. The signed respondent is what ties it to a real account.

Act on the scores

Insights shows the score and the distribution for each marked field. Read low scores with their written reason before anyone follows up.

For support teams, Slack sends a response link by default. You can add up to three answer fields to the message, such as the score and the reason.

This page for agents: /guides/build-an-nps-survey.md · index at /llms.txt

Updated

Was this page helpful?