This article to applies to customers on Voice of Customer Growth plan.
Contentsquare Webhooks allow you to be notified when an event occurs such as when a visitor to your website submits a new survey response. You set up a "webhook" (a URL) on your own website or on a partner company's website, share that webhook with Contentsquare, and then receive event payloads when events occur. Webhooks are therefore a way to push data to your systems in real time.
In technical terms:
You set up a webhook, usually on your own server/website, with a unique URL.
Within the Contentsquare interface, you enter the webhook URL on a Survey or a replay Segment.
Contentsquare then sends HTTP POST requests to your webhook URL whenever a new survey response is created, or when a new replay that matches your replay segment is created.
Why use Webhooks?
Webhooks complement the Contentsquare API. While the API is most useful for bulk export (downloading), Webhooks are useful for immediately receiving survey responses, or replays based on replay segments as they are created. You should consider using Webhooks if:
- You want to receive data immediately.
- You want to send data to your systems, perhaps a data warehouse, without having to poll our API.
- A partner offers an Integration with Contentsquare via Webhooks.
How Webhooks work
When an event occurs Contentsquare will send a message to every configured Webhook:
- The message is sent as a HTTP POST request to your webhook.
- The request will be encoded as UTF-8.
- The content type will be
application/json
. - The body of the request will therefore be a JSON object with the following properties:
event
- string. The value of this property is the name of the event. At time of writing this will be survey_response, replay or test_message.version
- integer. This property is reserved for future use, at present it will always be set to the number 1.data
- The value of this property will be a JSON object with the data of the event. The fields on the object are specific to each event (see below).
We send events as soon as possible after they occur; in practice you should receive a message within just a few seconds. In rare circumstances messages can be delayed, but each event includes the date and time they occurred so we recommend using that rather than the date and time your webhook receives a message.
Contentsquare does not guarantee the order of messages received matches the order the events occurred. Some events contain an index
parameter which can be used for ordering.
Webhook requirements
Your webhook must meet the following requirements:
- The webhook must be HTTPS (TLS).
- The server must respond within 10 seconds.
- The server should respond with any HTTP 2XX status code (any HTTP status code between 200 and 299).
- The server should accept any unexpected event type without returning an error.
- The server should handle accidental duplicate messages. Although Contentsquare makes every attempt to only ever send a message to your webhook once, we cannot guarantee it. Your webhook should therefore rely on the unique identifier in the message for deduplication.
Retries
Contentsquare will attempt to retry the sending of a webhook in the event of an error. Contentsquare will retry up to six times, pausing between retries:
Retry attempt | Delay before retrying |
1 | 30 seconds |
2 | 1 minutes |
3 | 2 minutes |
4 | 5 minutes |
5 | 10 minutes |
6 |
20 minutes |
Contentsquare will retry sending until one of the following occurs:
- The webhook responds with a 2XX status code (i.e. a successful response).
- The webhook responds with a 410 status code (upon which Contentsquare will delete the webhook from your survey or replay segment).
- Maximum 6 retries are reached.
Signatures and replay prevention
Contentsquare's webhooks uses two security techniques to ensure you can check that any payloads you receive are really from Contentsquare:
- All payloads sent to your webhooks are signed by HMAC; you are strongly encouraged to verify the signature so you know that the data really came from Contentsquare
- All payloads include a timestamp so you can verify the data was sent from Contentsquare within the last few minutes. This allows you to mitigate replay attacks because you can be sure Contentsquare sent this data to you within the last few minutes.
Checking the payload signature
The signature is sent to your webhook as a custom header named com-Contentsquare-signature. To check the signature is valid you need to:
1. Generate a signature from the payload you received
2. Check if your generated signature matches the signature in the header
You can generate a signature by using HMAC-SHA3-256, the payload (as bytes) and a signing key. To obtain the signing key for your Contentsquare site open the Webhooks Integration settings within the Contentsquare application and click "View Webhook key".
Once you have generated a signature you can compare it to the signature sent in the com-Contentsquare-signature header. If the two values match then the signature was successfully validated, if they don't match then you should discard the payload and not act on it.
Protecting against replay attacks
The HMAC signature allows you to verify that Contentsquare created the webhook payload but an attacker could send the same payload again (and again!) and the signature verification would succeed. To protect against these "replay attacks" we also send a timestamp within the payload, if the date and time specified in the timestamp is too old (we suggest 5+ minutes in the past) then you should discard the payload.
To verify the timestamp you must first verify the HMAC signature, as described above, before checking the timestamp because an attacker could simply send a valid timestamp within a payload. The timestamp is a UNIX timestamp, meaning it is the number of seconds since the Unix epoch. You can see example timestamps in the example payloads below.
Payloads
Contentsquare may add additional fields to these payloads in the future so your webhook should not raise an error when a field is present that you were not expecting. Note that any field can be set to null in addition to the stated type.
Survey Response
Event name: survey_response
id
integer- The unique identifier of the survey response.index
integer - The index of this survey response within the survey.api_id
string - The public id (includes UUID) referring to this survey response.response_url
string - The link to view the response within the Hotjar app.site_id
integer - The unique identifier of the Hotjar Site.survey_id
integer - The unique identifier of the survey.survey_name
string - The name of the survey.survey_url
string - The link to view the survey within the Hotjar app.device
string - The device type used to submit the survey response. It will be "tablet", "mobile", or "desktop".browser
string - The name of the browser type used to submit the survey response.os
string - The name of the operating system used to submit the survey response.country_code
string - The country code from which the survey response was created. See the ISO 3166 country codes for a list of these codes.country_name
string - The country from which the survey response was created.- hotjar_user_id string - The Hotjar User ID for the user who submitted the survey response (a UUID).
created_str
string - The date and time the survey response was created as an ISO 8601 string.created_timestamp
integer - The date and time the survey response was created as a UNIX Timestamp.is_complete
boolean - Flag indicating whether the survey response is completed or not.recording_url
string - The link to the Hotjar Recording that contains that survey response, if it exists.response_origin_url
string - The URL of the site where the user submitted the survey response.window_width
integer - The width of the user's window when submitting the survey response.window_height
integer - The height of the user's window when submitting the survey response.user_attributes
object - The user's attributes as provided by the Hotjar Identify API. Each key and value in the object represent the following:key
string - The name of the user attribute.value
string/integer/float/boolean/datetime - The value of the user attribute.questions
array/list - The questions and answers to the survey. Each element has the following fields:question_id
integer - The unique ID of the question.question_text
string - The text of the question.question_type
string - The type of the question. This will be one ofreaction
,long-text
,short-text
,single-option
,multiple-option
,email
,1-5-rating
,1-7-rating
,nps
orunknown
.answers
array/list - The answers to the question. Each answer has the following fields:answer
string - The answer the user gave.comment
string - The comment the user left (if any).
Please note that questions that the user did not answer are not included in the payload.
sample
string - Always set to the string "data".
Example payloads
Survey Response
{ "event": "survey_response", "data": { "id": 42, "index": 1, "api_id": "response_f8ccb724-8110-48d0-8715-2138be7a9c06", "response_url": "https://insights.hotjar.com/link/goes/here", "site_id": 42, "survey_id": 42, "survey_name": "Test survey", "survey_url": "https://insights.hotjar.com/link/goes/here", "device": "Desktop", "browser": "Chrome", "os": "Windows", "country_code": "MT", "country_name": "Malta", "hotjar_user_id": "90fc1180-90b4-463c-9d1f-3415477f0168", "created_str": "2023-06-07T11:13:05.193076Z", "created_timestamp": 1686136385, "is_complete": false, "recording_url": "https://insights.hotjar.com/r?site=14&recording=12345", "response_origin_url": "https://www.example.com", "window_width": 1280, "window_height": 1024, "user_attributes": { "test_ua_one": "value", "test_ua_two": true }, "questions": [ { "question_id": 1, "questiom_text": "Question 1", "question_type": "short-text", "answers": [ { "answer": "Answer to question 1 goes here", "comment": null } ] }, { "question_id": 2, "questiom_text": "Question 2", "question_type": "long-text", "answers": [ { "answer": "Answer to question 2 goes here\n\n new line", "comment": null } ] }, { "question_id": 3, "questiom_text": "Question 3", "question_type": "email", "answers": [ { "answer": "support@hotjar.com", "comment": null } ] }, { "question_id": 4, "questiom_text": "Question 4", "question_type": "single-option", "answers": [ { "answer": "radio button?", "comment": "comment" } ] }, { "question_id": 5, "questiom_text": "Question 5", "question_type": "multiple-option", "answers": [ { "answer": "this", "comment": "comment" }, { "answer": "that", "comment": "comment" } ] }, { "question_id": 6, "questiom_text": "Question 6", "question_type": "1-5-rating", "answers": [ { "answer": "3", "comment": null } ] }, { "question_id": 7, "questiom_text": "Question 7", "question_type": "1-7-rating", "answers": [ { "answer": "3", "comment": null } ] }, { "question_id": 8, "questiom_text": "Question 8", "question_type": "nps", "answers": [ { "answer": "3", "comment": null } ] }, { "question_id": 9, "questiom_text": "Question 9", "question_type": "reaction", "answers": [ { "answer": "3", "comment": null } ] }, { "question_id": 10, "questiom_text": "Question 10", "question_type": "short-text", "answers": [ { "answer": "", "comment": null } ] } ] }, "version": 1,
"timestamp": 473385600 }