Webhooks for Surveys
In this article
- About Webhooks for Surveys.
- Setting up Webhooks for Surveys
- Testing Webhooks.
- Locating your Webhooks URL.
- When are Webhooks triggered?
- Technical documentation for Surveys Webhooks.
Suggested next article: Publishing and Sending Surveys.
About Webhooks for Surveys
The goal of Webhooks for Surveys is to push response data directly to your downstream applications — (e.g. business intelligence tools or products like Zapier that can trigger automations).
Webhooks provides a convenient way to send customer feedback directly to your team within your organization's existing tech stack.
-
Webhooks vs API GET requests
Surveys' Webhooks will send submitted survey responses to the URL of your choice, allowing you to ping other platforms’ APIs and automate your workflows. Webhooks can be advantageous over API GET requests because they trigger in real time — so there’s no need for ongoing API polling.
Setting up Webhooks for Surveys
Prior to setting up Delighted Surveys' Webhooks, decide where you want to send the data — whether to an API endpoint from another software application, a link connecting to your data warehouse, or some other URL. (See Locating your Webhooks URL.)
To set up Webhooks for Surveys:
- 1
- Click 'Integrations"
- 2
- Click the "Webhooks" card
-
A published survey is needed before you can create your Webhooks If you haven't created and published your target survey, you can do so quickly by clicking the empty space at the bottom of the Webhooks page under Connections.
- 3
- Click the "Edit" link → seen next to any Published survey
- 4
- Click "Add a rule"
- 5
- Set the conditions that will trigger your Webhook → (ex. "How likely are you to buy from us again?" for the "Question" with "4" and "5" as our "With response" settings)
-
Question Pick "Any Question" (and trigger for an entire survey) or choose a specific question from the list → (ex. below, we selected a text entry question, "What features are missing that you would like to see?") With response If you selected a specific question, pick a response as the trigger → (ex. below, "Is not empty," was picked, meaning that a Webhook will be triggered every time a text response is submitted) - 6
- Paste in the URL that will receive the Delighted Webhook payload → and click "Save" rule. (See Locating your Webhooks URL if you need help)
- 7
- Switch the toggle to "Enabled" if necessary → and start using Webhooks!
-
Look for the little Webhooks icon on the the Surveys index card after it has been enabled.
Testing Webhooks
To test a webhook in Delighted Surveys:
- 1
-
From your Webhooks connections page → click "Test"
-
Look and see your Webhooks test data
Check out your Webhook in the platform you have selected to receive the test. You will see sample test data, and not actual data from your survey.
Locating your Webhooks URL
Most web-based products have a Webhooks section that provides a URL that can be used to catch Webhook payloads from third-party services — like Delighted Surveys. Typically, these are super easy to find.
A good example is Zapier. Let’s say you want to set up a Zap to send you a text message every time a new response is received. In Zapier, 1) Catch the Webhook and 2) Configure the other action steps needed to automate the sending of an SMS, email, or other type of message to your team.
When you go into Zapier to set-up that first step (catching the webhook), Zapier will display your Webhooks URL. Click "Copy" and "Paste" the URL into Surveys Webhooks, as seen in Setting up Webhooks for Surveys step 6.
When are Webhooks triggered?
A response webhook is triggered by Surveys when a response is:
Received | Triggered after an answer in a survey is submitted |
Updated | Triggered after each additional answer in the survey is submitted |
Webhooks are triggered 10 minutes after a response is received. This time buffer allows for much of the survey to be completed before we send the webhook payload.
All updates are also delayed by 10 minutes (eg. if someone updates their response after the initial submission, we'll wait 10 minutes before sending a new webhook).
Technical documentation for Surveys Webhooks
1. Webhook structure
Webhooks are always sent as an HTTP POST request to the webhook URLs you’ve configured via your Webhooks integration. The details of the activity that triggered the webhook are sent as the body of the HTTP request in JSON format. Every webhook will include an event_type property that describes what kind of activity triggered the event.
2. Event types
We currently support sending webhook notifications for the following event types:
Type | Description |
survey_response.created |
This occurs the moment a person provides their first response to a survey question. |
survey_response.updated |
This occurs any time an additional response is given to questions after the first survey question, or if a survey response is changed. |
3. Example request body for a survey_response object
{ "event_type": "survey_response.updated", "event_id": "ab85fd23a00e4e210f021800107ab77d6e358f32616b066b1f7ce4b10c0d7b20", "event_data": { "id": "6456", "created_at": "2023-08-14T22:50:07Z", "updated_at": "2023-08-14T22:50:24Z", "survey": { "id": "50", "name": "New survey 2023-08-14", "questions": [ { "id": "72", "type": "free_text", "text": "How can we improve your experience?" }, { "id": "73", "type": "numeric_scale", "text": "How satisfied are you with the time it took to resolve your issue?", "scale_from": 1, "scale_to": 10 }, { "id": "74", "type": "graphic_scale", "text": "How helpful was your agent?", "graphic_type": "stars-5" }, { "id": "75", "type": "multiple_choice", "text": "How did you hear about us?", "options": [ { "id": "116", "text": "Search engine" }, { "id": "117", "text": "Instagram" }, { "id": "118", "text": "YouTube" }, { "id": "119", "text": "Phone" }, { "id": "120", "text": "Email" }, { "id": "121", "text": "Text message" }, { "id": "122", "text": "Word of mouth" }, { "id": "123", "text": "TV or radio" }, { "id": "124", "text": "Our website" }, { "id": "125", "text": "Third party review" }, { "id": "126", "text": "Print ad" } ] }, { "id": "76", "type": "graphic_scale", "text": "How helpful was your agent?", "graphic_type": "thumbs" } ] }, "contact": { "id": "6420", "email": "test@example.com" }, "answers": [ { "id": "16541", "question_id": "72", "value": { "free_text": "Here's what a text response to your question might look like." } }, { "id": "16542", "question_id": "73", "value": { "numeric_scale": 6 } }, { "id": "16543", "question_id": "74", "value": { "graphic_scale": 4 } }, { "id": "16544", "question_id": "75", "value": { "multiple_choice": [ { "option_id": "117", "text": "Instagram" }, { "option_id": "120", "text": "Email" }, { "option_id": "123", "text": "TV or radio" }, { "option_id": "126", "text": "Print ad" } ] } }, { "id": "16545", "question_id": "76", "value": { "graphic_scale": "Thumbs up" } } ] } }
4. Webhook request body parameters
Parameter | Type | Description |
---|---|---|
event_type | string | This describes what kind of event triggered this Webhook. It is composed of an object type and an action, separated by a period: survey_response.created. |
event_id | string | The event_id can be used to deduplicate notifications that may be delivered more than once when it’s hard for us to be sure that earlier attempts succeeded. You can ensure that you don’t unnecessarily process requests multiple times by logging each event_id as you process it and checking each event_id is new to ensure you haven’t already processed it. |
event_data.id | string | A unique identifier for the event data. |
event_data.created_at | string | The timestamp when data was created (in ISO 8601). |
event_data.updated_at | string | The timestamp when data was last updated (in ISO 8601). |
event_data.survey.id | string | A unique identifier for the survey. |
event_data.survey.name | string | The name of the survey. |
event_data.survey.questions.id | string | A unique identifier for each question. |
event_data.survey.questions.type | string | The response format for each question (eg. multiple_choice) |
event_data.survey.questions.text | string | The text for the specific question. |
event_data.survey.questions.scale_from (optional) | integer | The lowest value in a scale question. |
event_data.survey.questions.scale_to (optional) | integer | The highest value in a scale question. |
event_data.survey.questions.graphic_type (optional) | string | The type of graphic scale used in the question. |
event_data.survey.questions.options.id (optional) | string | A unique identifier for each option provided in a multiple choice type question. |
event_data.survey.questions.options.text (optional) | string | The text for each provided option in a multiple choice type question. |
event_data.survey.questions.options.is_none (optional) | boolean | An optional boolean that will be set to true when the option is the “none of the above” option. |
event_data.survey.questions.options.is_other (optional) | boolean | An optional boolean that will be set to true when the option is the “other” option. |
event_data.answers.value.multiple_choice.option.other_text (optional) | string | An optional string that will have the text a user enters for “Other”. |
event_data.contact.id | string | A unique identifier for the contact. |
event_data.contact.email (optional) | string | The email of the contact. (optional) |
event_data.answers.id | string | A unique identifier for each given answer. |
event_data.answers.question_id | string | The unique identifier for the question to which the answer corresponds. |
event_data.answers.value.free_text (optional) | string | The respondent's free text entry. |
event_data.answers.value.numeric_scale (optional) | integer | The value selected by the respondent in a numeric scale type question. |
event_data.answers.value.graphic_scale (optional) | integer | The value selected by the respondent in a graphic scale type question. |
event_data.answers.value.multiple_choice.option_id (optional) | string | The options chosen by the respondent for a multiple choice type question. |
event_data.answers.value.multiple_choice.option_text (optional) | string | The text of the options chosen by the respondent for a multiple choice type question. |
5. Example request headers
Name | Example Value |
---|---|
Content-Type | application/json |
User-Agent | Delighted Webhook Notifier |
X-Delighted-API-Version | v1 |
X-Delighted-Webhook-Signature | sha256=5ccf874cdc518138f08f0e5d0ba6f8b4fd1d7a78372d0b858571204b99c0b094 |
6. Responding to Webhooks
You should respond with an HTTP status code in the 200 range to indicate that you successfully received the webhook. Any response with a status code outside the 200-299 range, including a redirect, will be treated as a failure, and we will try to deliver the webhook again multiple times over several days.
7. Signature verification
Every webhook includes a unique X-Delighted-Webhook-Signature header that must be used to verify that the request was indeed sent by us. The value of this header comprises two parts: the signature algorithm and the signature itself, divided by an equals sign.
To validate the signature, you must collect the exact HTTP request body as originally transmitted (before decoding the JSON), use your unique webhook signature key, and generate an HMAC digest over the request body, using the signature algorithm specified in the header.
Here’s an example in Ruby:
def webhook_signature_valid?(request) unique_webhook_signature_key = ENV['WEBHOOK_SIGNATURE_KEY'] signature_header = request.headers['X-Delighted-Webhook-Signature'] algorithm, received_signature = signature_header.split('=', 2) if algorithm != 'sha256' raise Exception, "Invalid algorithm" end expected_signature = OpenSSL::HMAC.hexdigest( OpenSSL::Digest.new(algorithm), unique_webhook_signature_key, request.body.read ) return SecurityUtils.secure_compare(received_signature, expected_signature) end
8. Data security
We recommend that your Webhooks use HTTPS URLs so that the customer information delivered to you via Webhooks is encrypted while traveling over the internet.