Why move the survey to WhatsApp
Satisfaction surveys by email all share the same problem: they arrive late, ask for a click to open a form and compete with dozens of other emails. The people who answer tend to be the very happy or the very annoyed, and the middle of the curve disappears.
On WhatsApp, the question lands in the same conversation where the customer got the order or booked the appointment, and the score is one tap on a list. With the WhatsApp API, the send goes out on its own from the right event, and the score lands in your database with no spreadsheet in between.
NPS, CSAT and the right format for each
| NPS | CSAT | |
|---|---|---|
| What it asks | How likely they are to recommend the company | How satisfied they were with that interaction |
| Scale | 0 to 10 | 1 to 5, or bad, ok and great |
| When to send | Periodically, or after relationship milestones | Right after support, delivery or a visit |
| WhatsApp format | List with all 11 scores | List or poll with a few options |
The list returns the exact option ID to your system, which makes reading it trivial. The poll, sent through messages/send/poll with question and options, looks lighter, but the vote arrives in the poll_update event with the vote data protected, and matching the vote to the option takes more work. For NPS, stick with the list. The formats are detailed in WhatsApp buttons and lists.
Building the 0 to 10 list
There is a practical detail: each list section accepts up to 10 rows, and NPS has 11 scores. The fix is to split them into two sections, which also helps the customer find their score faster:
import { DApi } from 'd-api-sdk'
const dapi = new DApi({ apiKey: process.env.DAPI_API_KEY! })
const score = (n: number, surveyId: string) => ({ rowId: `nps:${surveyId}:${n}`, title: String(n) })
export function sendNps(phone: string, surveyId: string) {
return dapi.interactive.sendList({
sessionId: 'store-relationship',
to: phone, // e.g. 14155550123
description: 'From 0 to 10, how likely are you to recommend Example Store to a friend?',
buttonText: 'Rate us',
footerText: 'Takes 5 seconds',
sections: [
{ title: '0 to 5', rows: [0, 1, 2, 3, 4, 5].map((n) => score(n, surveyId)) },
{ title: '6 to 10', rows: [6, 7, 8, 9, 10].map((n) => score(n, surveyId)) },
],
})
}The rowId carries the survey ID and the score. That way the answer does not depend on guessing which send it belongs to, even if the customer replies days later.
Collecting answers through the webhook
The score arrives as a messages.received event with type set to list_response. Recommended handling:
- Filter only
list_responseevents whose ID starts with the survey prefix. - Split the survey ID and the score, and check that the survey belongs to the number that replied.
- Store the first answer. If the customer changes their mind, decide whether the last or the first counts.
- If the score is 0 to 6, start the detractor flow. For 9 to 10, ask whether they would leave a public review.
- Free-text messages after the score are the comment. Link them to the same survey for a short window.
Answer the webhook with a 2xx status quickly and process in a queue. Event format and retry rules are in WhatsApp webhooks.
Response rate: what matters most
- Timing. CSAT one to two hours after support or delivery. Relationship NPS on a weekday, outside peak hours.
- One question. Score first. The why comes later, as an open question, only for those who answered.
- Same number as the relationship. A survey from a number the customer already knows gets more answers than one from a new number.
- Interval per customer. Track when each person got their last survey and respect a minimum interval.
- No pushy reminders. One reminder at most, and only for relationship NPS.
Detractor follow-up
The value of NPS on WhatsApp is not the number on the dashboard, it is how fast you can act. When a low score comes in, the flow should do three things: thank the customer and ask why, open a ticket with the score and the conversation history, and alert the account owner. A person reaching out the same day changes how someone who just complained sees you.
Surveys usually close other flows: delivery confirmation and the visit that follows an appointment reminder. To build the survey without code, with triggers and conditions, see the automation flows. The 3-day free trial, no credit card, is enough to run a pilot round with real customers.
