Which case is for you?
Case A – simple, for everyone: one chart of your answers that updates with every new response.
Case B – for teams that compare groups: a pivot table you can filter by channel, language or any other column.
Every response your survey collects lands as a new row in a Google Sheet the moment it's submitted. Build a chart or a pivot table on top of it once, and your report stays current on its own: no exports, no copying, no waiting for someone to prepare the numbers.
This is the report you get in Case A, a chart that updates with every new response:
And this is the pivot table in Case B, filtered by channel with a slicer:
How this works. Survio sends each completed response to a webhook URL. An automation tool (we use n8n in this guide) receives it and writes one row into your sheet. Survio does the sending, n8n does the writing, and the sheet does the reporting.
New to webhooks in Survio? Start with Webhooks: send responses to your other tools in real time, then come back here.
What you'll build
Both reports are fed by the same setup: every completed response becomes one row on a tab called responses.
At a glance | Case A: one chart | Case B: report by segment |
Result | A chart of how the answers are distributed, updated with every new response | A pivot table you can filter by channel, language or any other column |
Tools | Survio, n8n, Google Sheets | Survio, n8n, Google Sheets (pivot table and slicer) |
Best for | A small business that wants one simple chart that is always current | A team that compares groups, for example online and in-store customers |
Before you start
A Survio survey on a paid plan. Webhooks aren't available on the Free plan, and each plan allows a set number of webhooks per survey (see the webhooks article).
An n8n account, n8n Cloud or self-hosted.
A Google account for Google Sheets.
In this guide we use a short customer satisfaction survey: a star rating, an "Overall, how satisfied are you?" choice question, a "How did you shop with us this time?" choice question, an open comment and an optional e-mail question. Your survey can look different; you map your own questions in Step 5.
Step 1 — Prepare the sheet
Create a new Google Sheet and rename its first tab to
responses.In row 1, type the column headers. We use
Response ID,Submitted,Rating (1-5),Satisfaction,Channel,What could we do better,E-mail,Order ID,LanguageandSource.
Keep Response ID as the first column. It's Survio's unique id for each response. n8n uses it to update the existing row instead of adding a duplicate when a delivery is retried.
Step 2 — Create the webhook in n8n
In n8n, create a new workflow and add a Webhook node as the trigger.
Set HTTP Method to POST.
Switch to the Production URL tab and copy the address.
Click Publish. The production URL only accepts deliveries once the workflow is published.
Use the Production URL, not the Test URL. The Test URL only listens while the editor is waiting for a test event, so Survio's deliveries fail as soon as you close it. Survio retries a failed delivery for about a day, and after 5 days of failures it switches the webhook off.
Step 3 — Connect the survey in Survio
Open your survey and go to Get responses.
In the left sidebar under Automations & Integrations, select Webhooks and click + Add a webhook.
Paste the n8n Production URL into Webhook URL and click Save webhook. The webhook is switched on straight away.
Step 4 — Send a test response and find your question ids
Open your survey through its normal link and submit one complete response. Survio sends only completed responses, so click through to the end.
In n8n, open the Executions tab. The delivery arrives within a few seconds.
Open the execution and click the Webhook node. Expand
body, thendata, thenanswers. Each answer shows itsquestion.id, the questionlabeland theanswer.Write down the id of each question you want as a column. You can also click Pin to keep building against this response without submitting the survey again.
Always pick answers by question id. Answers arrive as a list. If you later add a question to the survey, positions in the list shift and your columns would quietly fill with the wrong answers. Question ids never change.
Looking for the respondent's e-mail? Don't use data.email: it stays empty when the e-mail comes from an e-mail question or a URL parameter. Read it from the answer to your e-mail question, or from data.parameters.
Step 5 — Turn each response into a row
This takes two nodes: Prepare row picks the answers out of the response, and Save response to sheet writes them into your sheet.
Prepare row
Add an Edit Fields (Set) node after the Webhook node and rename it to
Prepare row.Under Fields to Set, add one field for each column of your sheet and keep its type String. The field name must match the column header exactly; the value is the expression below.
Replace the question ids (90403153 and so on) with the ids you wrote down in Step 4, and Europe/Prague with your time zone.
Field name | Value |
Response ID |
|
Submitted |
|
Rating (1-5) |
|
Satisfaction |
|
Channel |
|
What could we do better |
|
| |
Order ID |
|
Language |
|
Source |
|
Save response to sheet
Add a Google Sheets node after Prepare row, rename it to
Save response to sheetand choose the operation Append or Update Row.Connect your Google account, then pick your spreadsheet under Document (from the list, or paste its ID with By ID) and the
responsestab under Sheet.Set Mapping Column Mode to Map Automatically and Column to match on to Response ID.
Why a separate Prepare row step? When you pick a different spreadsheet or tab in the Google Sheets node, n8n clears the column mapping inside that node. With the expressions in Prepare row, nothing is lost: the Google Sheets node only matches field names to column headers.
Click Publish again. Changes you make after publishing go live only when you publish again.
A few details behind those expressions:
Choice questions arrive as
{ ids, labels, value }.labels[0]is the text of the selected option. For a multiple-choice question, uselabels.join(', ')to put all selected options in one cell.valueholds the text typed into an "Other" option.Times arrive in UTC. The Submitted expression converts them to your time zone, and Google Sheets then treats the column as a date and time you can sort and group by.
URL parameters with a hyphen in their name, such as
order-id, need the bracket formparameters['order-id']. Survio doesn't allow underscores in parameter names.The survey title isn't in the data Survio sends. If several surveys write into one sheet, add a Survey column to the sheet and a Survey field with the survey's name to Prepare row.
Submit another response. A new row appears in the sheet within seconds.
Case A — Chart the answers
Add a tab named
report.Type
Satisfactionin A1 andResponsesin B1. In A2 to A6, list the answer options exactly as they appear in your survey.In B2, enter
=COUNTIF(responses!D:D,A2)and copy it down to B6.Select A1:B6 and choose Insert → Chart. Google Sheets suggests a column chart. To change its title, go to Customize → Chart & axis titles.
Each new response adds a row to responses, and the counts and the chart update on their own, like the chart at the top of this article.
Case B — Compare groups with a pivot table
On the
responsestab, select the table and choose Insert → Pivot table, then New sheet.Under Rows, add Satisfaction. Under Values, add Response ID and change Summarize by from SUM to COUNTA.
Choose Data → Add a slicer and pick the Channel column. Select the values you want to see and confirm with OK. The result looks like the pivot table at the top of this article.
You can slice by any column: Channel (your own question), Language (the language version of the survey the respondent answered in), Source (how the response came in, for example a direct link or an e-mail invitation) or Order ID.
A slicer selection isn't kept for the next visit. When the file is opened again, the slicer shows all values. To keep a selection, save it as the default in the slicer's menu.
Get the ready-made workflow
Instead of building Steps 2 and 5 by hand, start from our workflow. Download the file survio-responses-to-google-sheets.json from the attachments at the end of this article, then:
In n8n, create an empty workflow. Open the menu (three dots) next to the workflow name and choose Import, then From file. Pick the file you downloaded.
Open Prepare row and replace the question ids with your own and Europe/Prague with your time zone.
Open Save response to sheet. It shows a red warning sign until it's set up. Connect your Google account and pick your spreadsheet and its
responsestab. n8n then switches Mapping Column Mode to Map Each Column Manually: set it back to Map Automatically and set Column to match on to Response ID.Open Survio Webhook, copy the Production URL into Survio (Step 3) and click Publish.
Test it end to end
Submit a response through the survey link. A row appears on the
responsestab within seconds.Check that Response ID is filled in. That's what prevents duplicates.
Look at the Executions tab in n8n. Every delivery and its data are listed there.
Troubleshooting
Symptom | Cause | Fix |
Nothing arrives in n8n | The Test URL is in Survio, or the workflow isn't published | Copy the Production URL into Survio and click Publish |
Survio won't save the webhook URL from a self-hosted n8n | n8n shows an internal address instead of its public one | Set n8n's |
"Listen for test event" shows nothing although the survey was submitted | Survio sends to the Production URL; listening watches the Test URL | Check the Executions tab instead |
Your latest change has no effect | The workflow is published, but the change isn't (the Publish button shows a dot) | Click Publish again |
The same response appears twice | Operation set to Append Row, or no column to match on | Use Append or Update Row and match on Response ID |
A column stays empty and a new column appears at the end of the sheet | A field name in Prepare row doesn't match the column header exactly | Rename the field in Prepare row to match the header, then delete the extra column |
A cell shows [object Object] | A choice answer mapped without | Use |
Columns fill with the wrong answers after you edit the survey | Answers picked by their position in the list | Pick them by |
The E-mail column stays empty | Mapped from | Read the e-mail from the e-mail question's answer or a URL parameter |
Responses from before the setup are missing | Webhooks only send responses submitted after the webhook was created | Export the older responses once from Survio and paste them in |
Survio switched the webhook off | The webhook URL failed for 5 days in a row | Fix the cause, then switch the webhook back on in Survio |
Good to know
Survio sends only completed responses, one message per response, within seconds.
Nothing is sent retroactively: not responses from before the webhook existed, and not responses from while it was paused.
A failed delivery is retried up to 8 times over roughly 27 hours, and each attempt waits up to 30 seconds for a reply. Matching on Response ID makes retries harmless.
If deliveries fail for 5 days in a row, Survio switches the webhook off and you need to switch it back on.
Pausing a webhook keeps its slot; deleting it is permanent.
Survio doesn't keep a delivery history in the app. The Executions tab in n8n is your log.
Sending responses to webhooks doesn't use up any extra responses from your plan.
Rather have us set it up? On the Webhooks page in Survio, click Get expert setup, tell us what you'd like to happen, and our team will build it, test it and hand you a working webhook.
