Webhooks

Set up real-time event notifications

Webhooks

A webhook is the delivery of data to an endpoint when an event occurs. In our case that event is a survey response being submitted. Webhooks are intended for advanced users with programming knowledge.

How to connect

To set up webhooks, go to the Integrations tab → Webhooks and click Connect.

Download the markdown version of the "Webhooks" section for use in ChatGPT / other LLMs:

Webhook configuration UI

Step 1: Navigate to integrations

To set up webhooks, go to the Integrations tab → Webhooks and click Connect.

Step 2: Webhook settings form

A screen will open where you need to enter the webhook name, callback URL and request type.

Step 3: Testing the webhook

You can test the webhook using https://webhook.site

Step 4: Viewing logs

Or by viewing the logs in the SurveyNinja interface.

Webhook settings

The webhook settings form has the following parameters:

Parameter Description
Name A custom name for identifying the webhook in the list
URL Your endpoint URL. SurveyNinja sends a POST request to it for every new survey response
Headers Custom HTTP headers in "key: value" format. Sent with every request and duplicated in the headers payload body field for easy logging
Enabled Activity toggle. A disabled webhook does not send requests but retains all settings

Secret token via header

Add an Authorization: Bearer your-secret header — your server can then verify that the request genuinely came from SurveyNinja.

When webhooks fire

In SurveyNinja, webhooks fire when a survey response is submitted. This is the main event you can track.

Event: Survey response

The webhook fires every time a respondent submits an answer to your survey, giving you real-time notifications of new responses.

Automatic delivery

The webhook is sent automatically as soon as a survey response is received

Payload format

SurveyNinja sends a POST request with a JSON body. The complete request body structure is described below.

Root payload fields

Field Type Description
headers object Headers configured in the webhook. Duplicated in the body for easy logging and debugging
extra_params object / array Hidden variables and UTM tags passed during the survey. Empty array [] if no variables
promo_code string / null Promo code used during the survey, or null
log_id int Log record ID for this webhook delivery
score_all int Maximum possible score for the entire survey. 0 if scoring is not configured
score_earned int Total score earned by the respondent
score_percent float / null Result percentage: (score_earned / score_all) × 100, rounded to 1 decimal place. null if score_all = 0
scores object Score summary: fields user, max and by_question — a map { "uuid": int | null }
{question_uuid} object Answer to the question with this UUID. Each question in the survey has a separate top-level key

Question object fields

Each question in the payload is an object keyed by the question UUID. All question types share the following common fields:

Field Type Description
title string Question text (widget title)
type string Widget type: choiceSingle, choiceMultiple, rating, matrix and others.
question_uuid / uuid string Question UUID (duplicated inside the object)
results object / array Respondent's answer. Format depends on widget type — see section below
score_count int / null Score earned for this question. null if scoring doesn't apply to this type
score_max int / null Maximum score for this question. null if scoring doesn't apply
is_correct_question bool / null Overall answer correctness: true — correct, false — incorrect, null — not applicable
other string Text of the 'Other' option, if selected (for choice widgets). Empty string if not selected
inline_group_id string / null Question group UUID, if the question belongs to a group
inline_group_title string / null Question group name
is_inline_group_child bool true if the question is inside a group

Format of results by widget type

The results field has a different format depending on the question type.

choiceSingle, choiceMultiple, yesno, dropdown

Array of selected options. Each element contains the option text, score and correctness flag.

{ "results": [ { "result": "Option A", "score": 10, "is_correct": true } ] }

choiceMedia

Array of selected media options. The result field contains the URL of the selected option image.

{ "results": [ { "result": "https://app.surveyninja.io/.../image.png", "score": 3, "is_correct": false } ] }

ranking

Array of options in the order set by the user (first element = ranked 1st). Scoring and correctness don't apply to this type.

{ "results": [ { "result": "First place", "score": null, "is_correct": null }, { "result": "Second place", "score": null, "is_correct": null }, { "result": "Third place", "score": null, "is_correct": null } ] }

rating

Object (not array). Field is_star: true — stars/hearts mode; score_num: false — non-numeric display mode.

{ "results": { "result": "4", "is_star": true, "score": 0, "score_num": false, "is_correct": false } }

scale

Object with the selected scale value, score and correctness.

{ "results": { "result": "6", "score": 0, "is_correct": true } }

slider

Object with value only. Scoring doesn't apply to sliders.

{ "results": { "result": "45" } }

input, email, phone

Object with the entered value. Scoring doesn't apply. For multiline input, line breaks are preserved.

{ "results": { "result": "User's answer text" } }

datetime

Object with a date/time value. The value format depends on the widget mode.

// Date + time { "results": { "result": "12.03.2026 21:12" } } // Date only { "results": { "result": "11.03.2026" } } // Time only { "results": { "result": "18:14" } }

file

Array of uploaded files. Each element contains a download URL and file extension.

{ "results": [ { "result": "https://app.surveyninja.io/.../document.pdf", "file_ext": "pdf" } ] }

matrix

The most complex type. Contains two representations: results — human-readable view: nested object { "row": { "column": { result, score, is_correct } } }. Value result: "1" — cell is checked, result: "" — empty. answers — machine view by UUID: { "row_uuid": { "col_uuid": true | false } }

{ "results": { "Pasta": { "Bad": { "result": "1", "score": 2, "is_correct": false }, "OK": { "result": "", "score": null, "is_correct": false }, "Good": { "result": "", "score": null, "is_correct": true } }, "Potatoes": { "Bad": { "result": "1", "score": 2, "is_correct": true }, "OK": { "result": "", "score": null, "is_correct": true }, "Good": { "result": "", "score": null, "is_correct": false } } }, "answers": { "row-uuid-1": { "col-uuid-1": true, "col-uuid-2": false, "col-uuid-3": false }, "row-uuid-2": { "col-uuid-1": true, "col-uuid-2": false, "col-uuid-3": false } }, "is_bool": true }

Full payload example

A real example of a webhook request body with scoring, multiple question types and custom headers. UUIDs are shortened for readability.

{ "headers": { "Authorization": "Bearer my-secret-token", "X-Source": "surveyninja" }, "extra_params": { "utm_source": "email", "utm_campaign": "spring2024" }, "promo_code": null, "log_id": 1916713, "score_all": 52, "score_earned": 29, "score_percent": 55.8, "scores": { "user": 29, "max": 52, "by_question": { "uuid-choice-1": 0, "uuid-choice-2": 3, "uuid-rating-1": 0, "uuid-scale-1": 0, "uuid-matrix-1": 6 } }, "uuid-choice-1": { "title": "Select one option", "type": "choiceSingle", "question_uuid": "uuid-choice-1", "uuid": "uuid-choice-1", "results": [ { "result": "Option B", "score": 0, "is_correct": false } ], "score_count": 0, "score_max": 4, "is_correct_question": false, "other": "", "inline_group_id": null, "inline_group_title": null, "is_inline_group_child": false }, "uuid-choice-2": { "title": "Check all that apply", "type": "choiceMultiple", "question_uuid": "uuid-choice-2", "uuid": "uuid-choice-2", "results": [ { "result": "Option A", "score": 3, "is_correct": true } ], "score_count": 3, "score_max": 3, "is_correct_question": true, "other": "", "inline_group_id": null, "inline_group_title": null, "is_inline_group_child": false }, "uuid-rating-1": { "title": "Rate the service", "type": "rating", "question_uuid": "uuid-rating-1", "uuid": "uuid-rating-1", "results": { "result": "4", "is_star": true, "score": 0, "score_num": false, "is_correct": false }, "score_count": 0, "score_max": 2, "is_correct_question": false, "inline_group_id": null, "inline_group_title": null, "is_inline_group_child": false }, "uuid-scale-1": { "title": "On a scale of 1 to 10", "type": "scale", "question_uuid": "uuid-scale-1", "uuid": "uuid-scale-1", "results": { "result": "6", "score": 0, "is_correct": true }, "score_count": 0, "score_max": 3, "is_correct_question": true, "inline_group_id": null, "inline_group_title": null, "is_inline_group_child": false }, "uuid-input-1": { "title": "Leave a comment", "type": "input", "question_uuid": "uuid-input-1", "uuid": "uuid-input-1", "results": { "result": "Everything is great!" }, "score_count": null, "score_max": null, "is_correct_question": null, "inline_group_id": null, "inline_group_title": null, "is_inline_group_child": false }, "uuid-file-1": { "title": "Attach a file", "type": "file", "question_uuid": "uuid-file-1", "uuid": "uuid-file-1", "results": [ { "result": "https://app.surveyninja.io/.../document.pdf", "file_ext": "pdf" } ], "score_count": null, "score_max": null, "is_correct_question": null, "inline_group_id": null, "inline_group_title": null, "is_inline_group_child": false }, "uuid-matrix-1": { "title": "Rate the dishes", "type": "matrix", "question_uuid": "uuid-matrix-1", "uuid": "uuid-matrix-1", "results": { "Pasta": { "Bad": { "result": "1", "score": 2, "is_correct": false }, "Good": { "result": "", "score": null, "is_correct": true } }, "Potatoes": { "Bad": { "result": "1", "score": 2, "is_correct": true }, "Good": { "result": "", "score": null, "is_correct": false } } }, "answers": { "row-uuid-1": { "col-uuid-1": true, "col-uuid-2": false }, "row-uuid-2": { "col-uuid-1": true, "col-uuid-2": false } }, "is_bool": true, "score_count": 6, "score_max": 6, "is_correct_question": false, "inline_group_id": null, "inline_group_title": null, "is_inline_group_child": false } }

Backward compatibility

Scoring fields (score_max, is_correct_question, is_correct inside results) are added on top of the base format. Existing fields are not removed or renamed.

Testing webhooks

You can use the following tools to test webhooks:

webhook.site

Recommended service for testing webhooks

https://webhook.site

Go to the site, get a unique URL and use it to test your webhooks.

Viewing logs

You can verify webhook operation via logs

The SurveyNinja interface provides webhook delivery logs to track their operation.

Security

For webhook security, it is recommended to use HTTPS and verify request authenticity.

Security recommendations

  • Use HTTPS for the callback URL
  • Verify request signatures to confirm their authenticity
  • Restrict endpoint access to SurveyNinja IP addresses only
  • Implement timeouts for webhook processing