Webhooks logo

Stream feedback scores into your analytics pipeline

Each NPS response is posted to an ingestion endpoint that normalises the score and streams the record into your warehouse.

When this happens

New submission on your feedback or NPS form

Do this

POST the response JSON to an ingestion endpoint that normalises the score and streams it into your data warehouse

One-directional. A submission triggers the action — nothing is written back into your form.

Feedback that lives only in a form is feedback nobody can join to anything else. A product team with its own analytics stack wants the score sitting next to the account, the plan and the release, which means each response has to reach the warehouse in a shape the other tables already use.

This flow posts every response to an ingestion endpoint. The handler pulls the score out, tags the record with a source and a received-at timestamp, and streams it on. Nothing waits in a spreadsheet for someone to export it on a Friday.

Setting it up

  1. 1

    Publish your NPS form and leave the 0–10 question as the only rating on it, so the handler never has to guess which answer is the score.

  2. 2

    Stand up an ingestion route, /webhooks/nps, that accepts a JSON body and writes to your streaming buffer rather than straight to the warehouse.

  3. 3

    Enable the outgoing webhook in the form's settings, paste the ingestion URL, and save the form.

  4. 4

    Send a test response and log the body so you can see how the 0–10 answer arrives; a string like "9" is common and has to be cast.

  5. 5

    Normalise in the handler: cast the score to an integer, then derive promoter, passive or detractor from it rather than storing a label the form sent.

  6. 6

    Tag every record with a source of nps_form, the form's id and a received-at timestamp, so the rows join cleanly to the rest of the warehouse.

  7. 7

    Keep the free-text answers in their own columns and leave them untouched — a comment trimmed or truncated at ingestion cannot be recovered later.

  8. 8

    Return 2xx once the buffer accepts the record, and alert on a rise in non-2xx responses, since a broken ingestion route looks exactly like a quiet week for feedback.

What maps where

Using the Net Promoter Score Survey as the starting point. These are its real fields — swap in your own and the mapping works the same way.

Form fieldWebhooks
On a scale of 0–10, how likely are you to recommend us to a friend or colleague?score column, cast to an integer and bucketed
What's the main reason for your score?verbatim comment column, stored unchanged
What could we do to improve your experience?improvement text column on the same row
Which of our products or services did you use?product dimension the score is joined on
Email Addressaccount key, hashed if the warehouse holds no addresses

Variations worth knowing

Send detractors down a second path

Once the record is buffered, branch on the bucketed score: anything in the detractor range is also posted to your alerting service so an account manager hears about it the same day. The warehouse copy is unchanged, which keeps the trend line honest even when the alert path fails.

Keep the raw body alongside the parsed row

Write the untouched JSON to object storage keyed by received-at, then parse. When you change how the score is bucketed six months from now, you can rebuild the table from the raw records instead of living with two definitions inside the same column.

If something isn't arriving

Scores land in the warehouse as text and nothing will average them.

The 0–10 answer is arriving as a string and being inserted into a text column. Cast it in the handler, reject anything outside 0 to 10 with a 4xx, and change the column type — a warehouse will happily keep an empty string next to 9 and never complain.

The comment columns are empty on most rows.

Those questions are optional on the form, so a blank is a real answer rather than a delivery problem. Store an explicit null instead of an empty string and track the completion rate separately, so a quiet month reads as low completion rather than as missing data.

Frequently asked questions

Should I write straight to the warehouse from the handler?

Rarely. Most warehouses dislike single-row inserts, and a slow load makes the recorded delivery status depend on your query engine. Write to a buffer or a queue, return 2xx, and let a batch job load the rows on its own schedule.

Can I keep responses anonymous in the warehouse?

Yes, if you drop or hash Email Address at ingestion. The handler is the right place for it, because once a raw address is in the warehouse it is in every downstream copy too. A hash keeps the join to an account without storing the address itself.

How do I join a score to the account that gave it?

Match on the address given in Email Address, and accept that some responses will not resolve, since people answer from personal addresses. Do the join once at ingestion and store the account id on the row, rather than re-running the same fuzzy match in every downstream query.

Related automations

Build the form first

The automation needs somewhere to fire from. Publish a form, connect it once, and every submission from then on runs this flow.

Start free