Webhooks
Overview
Webhooks (AKA callbacks) is a powerful tool to create a custom integration between Userpilot and your own app or third-party systems.
When activated, Userpilot will be sending real-time notifications to the defined end-point whenever certain events happen. These notifications will happen without any delay to allow you to make time-sensitive data-driven decisions.
Common Use Cases
- Fire workflows in third-party platforms such as Zapier or Workato
- Sending an email campaign when a user submits a certain NPS score
- Sending content engagement events to your analytical tools for analysis in real-time
- Notify your back-end system when an event occurs in Userpilot
- Send real-time updates to CRM platforms like Hubspot
- Send real-time notifications to Slack channels after users take an action in Userpilot
- Trigger an email when a user clicks on a button in one of the flows.
If you signed up after May 30th 2023, this feature is only available for Growth and Enterprise plans. If you wish to know more about upgrading your plan please reach out to support@userpilot.co
Events
Webhooks can be created for any of these events:
- Flows: Completed, Started, Dismissed
- Checklists: Completed, Started, Dismissed
- NPS: Score, Dismissed, Feedback
- Forms: Submitted
- Feature Tags: Click, Hover, Text Input
- Button Tracked Events: Tracked through the button action settings in a flow
How to create a Webhook
- Click on Create Webhook to initiate the creation pop-up.
- Give your Webhook a descriptive name and specify the endpoint where your events will be sent.
- Click Create.
- Select the Events that you would like to send in your Webhook from the Events list
- You can also include up to 10 user/company properties and a secret token to secure your requests.
Note: We strongly recommend including the secret token to ensure requests are coming from Userpilot and nobody else. The complexity of this secret will vary based on your security protocol requirements.
6. Toggle the Event so that the events are sent live
7. Click on Save and toggle the Status column to enable the Webhook.
Important note: Make sure your event is toggled before saving or the events won't be sent
Testing Webhook Events
You can send a test event before enabling the webhook live. Once you have configured all the settings mentioned in the steps above, you can click on the "Send Test" button.
Verifying Webhook Events from Userpilot
To ensure the security and integrity of the events we send to your webhook, we include a signature in the X-Signature
header. By verifying this signature, you can confirm that the event was sent by Userpilot and has not been tampered with during transmission.
Structure of the X-Signature
Header
The X-Signature
header consists of two components (t={{TIMESTAMP}},v1={{SIGNATURE}}
):
- t: Timestamp of the request.
- v1: Signature derived from the request payload and your secret key.
How to Verify the Signature
To verify the signature (v1
value) of an incoming webhook event:
- Parse the Request Body: Extract and stringify the JSON payload of the incoming webhook request.
- Retrieve the Timestamp: Extract the timestamp value (
t
) from theX-Signature
header. As a fallback, the timestamp is also provided within the request body. Construct the Signature Base String: Concatenate the stringified request body and the timestamp with a period (.) in between:
signature_base_string = stringified_request_body + "." + timestamp
- Compute the HMAC SHA-256 Signature: Using your secret key, compute the HMAC SHA-256 hash of the signature base string.
- Compare Signatures: The computed hash should match the
v1
value in theX-Signature
header. If they are identical, the webhook event is verified.
Example in NodeJS:
const express = require('express'); const bodyParser = require('body-parser'); const crypto = require('crypto'); const app = express(); const PORT = 3000; const SECRET = 'your_secret_key_here'; // Replace with your secret key app.use(bodyParser.json()); app.post('/webhook-endpoint', (req, res) => { const receivedBody = JSON.stringify(req.body); // Extract t and v1 from the X-Signature header const signatureHeader = req.get('X-Signature') || ''; const receivedTimestamp = (signatureHeader.match(/t=(\d+)/) || [])[1]; const receivedSignature = (signatureHeader.match(/v1=([\w]+)/) || [])[1]; // Construct the signature base string const signatureBaseString = `${receivedTimestamp}.${receivedBody}`; // Compute the HMAC SHA-256 signature const computedSignature = crypto .createHmac('sha256', SECRET) .update(signatureBaseString) .digest('hex'); // Compare the signatures if (computedSignature === receivedSignature) { console.log('Webhook verified!'); res.status(200).send('Verified'); } else { console.log('Verification failed!'); res.status(400).send('Verification failed'); } }); app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });
Webhook Payload Schema
Webhooks are sent to your specified destination endpoint as HTTP POST requests with a JSON-formatted body. You can find the payload information with a sample request below.
The Event Response
This part of the payload includes information about the triggered event.
Generic Metadata
This part of the payload includes default information sent with every payload - e.g. user id, app token, etc.
Additional User & Company Properties
This part of the payload includes user and company objects containing properties you've opted to send in your Webhook
Note: We strongly recommend sending data only to third party systems you trust as Webhook information may contain personally identifiable information (PII)
Flows
{ "type": "flows.seen" || "flows.completed" || "flows.dismissed", "timestamp": 1679578625, "app_token": "NX-bd46697c", "data": { "type": "flow", "action": "seen" || "completed" || "dismissed", "id": 1, "hostname": "demo.userpilot.io", "pathname": "/dashboard", "user": { "user_id": "1", "name": "John Doe", "email": "john@userpilot.co" //... rest of chosen user properties }, "company": { "id": "1", "name": "Userpilot" //... rest of chosen company properties } } }
Checklists
{ "type": "checklist.seen" || "checklist.completed" || "checklist.dismissed", "timestamp": 1679578625, "app_token": "NX-bd46697c", "data": { "type": "checklist", "action": "seen" || "completed" || "dismissed", "id": 1, "hostname": "demo.userpilot.io", "pathname": "/dashboard", "user": { "user_id": "1", "name": "John Doe", "email": "john@userpilot.co" //... rest of chosen user properties }, "company": { "id": "1", "name": "Userpilot" //... rest of chosen company properties } } }
Surveys
{ "timestamp": 1679578625, "app_token": "NX-bd46697c", "type": "surveys.seen" || "surveys.completed" || "surveys.dismissed", "data": { "id": 1, "action": "seen" || "completed" || "dismissed", "title": "UI/UX Survey", "type": "survey", "hostname": "run.userpilot.io/", "pathname": "users/", "user": { "user_id": "1", "name": "John Doe", "email": "john@userpilot.co" //... rest of chosen user properties }, "company": { "id": "1", "name": "Userpilot" //... rest of chosen company properties } "answers": [ { "id": 1, "question": "Rate your overall product experience so far", "type": "likert_scale", "answer": 5, }, { "id": 2, "question": "Which feature would you like to see the most?" "type": "multiple_choice", "answer": "Session Replay" }, { "id": 3, "question": "Anything we could improve?", "type": "open_text", "answer": "Sort table entries by column" } ... ] } }
NPS
{ "type": "nps.feedback", "app_token": "NX-bd46697c", "timestamp": 1679578619, "data": { "action": "feedback", "survey_question": "How likely it is that you would recommend us to your friends?", "score": 10, "follow_up_question": "Why did you choose this score? ", "feedback": "Great Product", "hostname": "demo.userpilot.io", "pathname": "/dashboard", "user": { "user_id": "1", "name": "John Doe", "email": "john@userpilot.co" //... rest of chosen user properties }, "company": { "id": "1", "name": "Userpilot" //... rest of chosen company properties } } } NPS ASK LATER --------- { "type": "nps.ask_later", "app_token": "NX-bd46697c", "timestamp": 1679578619, "data": { "action": "ask_later", "hostname": "demo.userpilot.io", "pathname": "/dashboard", "user": { "user_id": "1", "name": "John Doe", "email": "john@userpilot.co" //... rest of chosen user properties }, "company": { "id": "1", "name": "Userpilot" //... rest of chosen company properties } } }
Feature Tags
{ "type": "feature_tag.clicked" || "feature_tag.hover" || "feature_tag.text_input", "timestamp": 1679578625, "app_token": "NX-bd46697c", "data": { "type": "feature_tag", "action": "clicked" || "hover" || "text_input", "id": 1, "hostname": "demo.userpilot.io", "pathname": "/dashboard", "user": { "user_id": "1", "name": "John Doe", "email": "john@userpilot.co" //... rest of chosen user properties }, "company": { "id": "1", "name": "Userpilot" //... rest of chosen company properties } } }
Forms
{ "type": "forms.submitted", "timestamp": 1679578625, "app_token": "NX-bd46697c", "data": { "type": "forms", "action": "submitted", "id": 1, "hostname": "demo.userpilot.io", "pathname": "/dashboard", "user": { "user_id": "1", "name": "John Doe", "email": "john@userpilot.co" //... rest of chosen user properties }, "company": { "id": "1", "name": "Userpilot" //... rest of chosen company properties } } }
Button Tracked Events
{ "type": "button_tracked_event.occurred", "timestamp": 1679578625, "app_token": "NX-bd46697c", "data": { "type": "button_tracked_event", "action": "occurred", "id": 1, "hostname": "demo.userpilot.io", "pathname": "/dashboard", "user": { "user_id": "1", "name": "John Doe", "email": "john@userpilot.co" //... rest of chosen user properties }, "company": { "id": "1", "name": "Userpilot" //... rest of chosen company properties } } }
If you have any questions please reach out to us at support@userpilot.co