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

Navigate to the Integrations page under Configure, then to the Webhooks tab. From here:
  1. Click on Create Webhook to initiate the creation pop-up.
  2. Give your Webhook a descriptive name and specify the endpoint where your events will be sent.
  3. Click Create.
  4. Select the Events that you would like to send in your Webhook from the Events list
  5. 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:

  1. Parse the Request Body: Extract and stringify the JSON payload of the incoming webhook request.
  2. Retrieve the Timestamp: Extract the timestamp value (t ) from the X-Signature header. As a fallback, the timestamp is also provided within the request body.
  3. 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

  4. Compute the HMAC SHA-256 Signature: Using your secret key, compute the HMAC SHA-256 hash of the signature base string.
  5. Compare Signatures: The computed hash should match the v1 value in the X-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

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.