# Customer satisfaction survey template (with NPS)

Short answer: this is a complete QuizGen JSON definition you can POST as-is
— a CSAT survey built around a real Net Promoter Score question, a
satisfaction rating, a driver question, and open feedback, with a branching
follow-up so detractors and promoters each get asked the right thing. No
form builder, no field-by-field rebuilding.

If you just want a quick post-purchase "how did we do?" without the NPS
math, [the customer feedback form template](/blog/customer-feedback-form-template)
is the shorter version — one satisfaction scale, one driver, one open
question. This one is for when you specifically want to track Net Promoter
Score over time, alongside a fuller satisfaction read.

## The template

```json
{
  "schema_version": 1,
  "kind": "survey",
  "title": "Customer satisfaction survey",
  "description": "Two minutes. Your answers shape what we fix next.",
  "settings": {
    "collect_respondent": "email_optional",
    "autosave": true,
    "completion": { "message": "Thanks — we read every one of these." }
  },
  "sections": [
    {
      "key": "main",
      "questions": [
        { "id": "nps", "type": "scale", "required": true,
          "prompt": "How likely are you to recommend us to a friend or colleague?",
          "min": 0, "max": 10,
          "min_label": "Not at all likely", "max_label": "Extremely likely" },
        { "id": "satisfaction", "type": "rating", "required": true,
          "prompt": "Overall, how satisfied are you with us?", "max": 5 },
        { "id": "score_driver", "type": "choice",
          "prompt": "What mainly drove your score?",
          "options": [
            { "value": "product_quality", "label": "Product quality" },
            { "value": "customer_support", "label": "Customer support" },
            { "value": "price_value", "label": "Price / value" },
            { "value": "ease_of_use", "label": "Ease of use" },
            { "value": "other", "label": "Something else" }
          ] },
        { "id": "detractor_recovery", "type": "long_text",
          "prompt": "Sorry to hear that. What's the one thing we could do to earn a higher score from you?",
          "rows": 4,
          "show_if": { "question": "nps", "op": "lte", "value": 6 } },
        { "id": "promoter_referral", "type": "long_text",
          "prompt": "Glad to hear it! What would you tell a colleague who was considering us?",
          "rows": 4,
          "show_if": { "question": "nps", "op": "gte", "value": 9 } },
        { "id": "open_feedback", "type": "long_text",
          "prompt": "Anything else you'd like us to know?", "rows": 4 }
      ]
    }
  ]
}
```

## Why NPS fits QuizGen exactly

The `nps` question above isn't a workaround — it's a native `scale`
question with `min: 0, max: 10`. The wording is the canonical NPS phrasing,
and `min_label`/`max_label` anchor the ends exactly the way the
methodology expects. That's the whole question; everything else in the
template exists to give that single number context.

What each piece is doing:

- `nps` — the canonical 0–10 recommend question, required so every response
  has a score to bucket.
- `satisfaction` as a `rating` (5 stars) rather than another 0–10 scale —
  it reads differently on the page and gives you a second, complementary
  number without the two questions blurring together.
- `score_driver` — a `choice` you can aggregate ("support drove 40% of low
  scores this month") instead of parsing free text for a reason.
- `detractor_recovery` and `promoter_referral` — both use `show_if` against
  the same `nps` question, but with opposite operators (`lte 6` vs `gte 9`),
  so passives (7–8) see neither and go straight to `open_feedback`. Each
  respondent only ever sees one of the two branches, so it doesn't add to
  the questions anyone has to answer — a detractor gets a recovery prompt,
  a promoter gets asked for a referral-worthy line, and nobody sees an
  irrelevant question.
- `open_feedback` — a catch-all that isn't conditional, so everyone gets a
  chance to say something you didn't ask about directly.

## How NPS scoring actually works

QuizGen doesn't compute NPS for you automatically — it's honest to be
explicit about that. What it does is store the raw 0–10 answer on every
response (`answers.nps`), which is all the math needs:

- **Promoters**: score 9–10
- **Passives**: score 7–8
- **Detractors**: score 0–6
- **NPS = % promoters − % detractors** (a number from -100 to 100, not a
  percentage itself)

Pull the numbers with `GET /quizzes/:id/responses`, bucket each
`answers.nps` value into promoter/passive/detractor, and do the
subtraction — a few lines in a spreadsheet or a script. If you want it
computed the moment each response lands instead of pulled on demand, a
[webhook](/blog/quiz-response-webhooks) posts each completed response to
your endpoint and you run the same bucketing there.

If you want QuizGen to tag each response with its bucket automatically —
useful for filtering responses without recomputing every time — you can
attach an `outcomes` block that maps the `nps` answer value to a bucket
key:

```json
"outcomes": {
  "method": "points",
  "points": {
    "nps": { "0": 0, "1": 0, "2": 0, "3": 0, "4": 0, "5": 0, "6": 0,
              "7": 1, "8": 1, "9": 2, "10": 2 }
  },
  "buckets": [
    { "key": "detractor", "max": 0, "title": "Detractor", "body": "Score 0–6." },
    { "key": "passive", "min": 1, "max": 1, "title": "Passive", "body": "Score 7–8." },
    { "key": "promoter", "min": 2, "title": "Promoter", "body": "Score 9–10." }
  ]
}
```

Be clear with yourself about what this buys you: each response now carries
an `outcome` field (`"detractor"` / `"passive"` / `"promoter"`) you can
filter or sort on directly, which is a convenience. It does **not** compute
the NPS score itself — that subtraction is still something you (or your
webhook handler) do across all responses. Treat `outcomes` here as
per-response tagging, not an NPS calculator.

## Let an AI assistant fill it in

Connect [QuizGen's MCP server](/blog/claude-mcp-servers-for-quizzes-and-forms)
and say something like:

> Read https://quizgen.dev/blog/customer-satisfaction-survey-template.md and
> create me this CSAT + NPS survey for [MY BUSINESS]. Change the
> `score_driver` options to match what we actually sell. Give me the live
> link.

The assistant adapts the options and prompts, POSTs the definition, and
hands back a live URL — and can pull the NPS numbers back for you later in
the same chat.

## When something else is the better pick

If tracking NPS trend lines with automatic dashboards, industry benchmarks,
and segment comparisons is the whole job, a dedicated NPS tool (Delighted,
Wootric, or similar) will save you the spreadsheet math — that's what
they're built for. QuizGen is the better fit when you want the survey to
live alongside your other forms, want an AI assistant or your own code to
create and read it, or don't want a subscription just to ask one number.

## FAQ

**Can I ask NPS without the rest of the CSAT questions?** Yes — delete
everything except the `nps` question. A single-question NPS survey is
still a valid definition.

**What if someone skips the NPS question?** It's marked `required: true`
here, so they can't submit without answering it — remove `required` if you'd
rather let people skip it (their response just won't count toward NPS).

**Can I send this by email instead of just sharing a link?** Yes, on paid
plans — `POST /quizzes/:id/send` emails it to a list, and
`GET /quizzes/:id/sends` shows who's completed it.

**Does the `rating` question have to be 5 stars?** No — `max` on `rating`
accepts 2–10; 5 is just the common default for a satisfaction score.
