With Qualtrics and FullStory working together, you can add FullStory session URLs to survey responses and pass survey responses into FullStory. Additionally, FullStory Ragehooks allow you to detect frustrated sessions and automatically send surveys. This bi-directional flow of data allows you to:

  • Quickly search for and segment users based on positive or negative feedback in order to understand how their  experience affected their survey response
  • Expose the relationship between your Qualtrics response data and your conversion rates and uncover revenue-impacting improvements.
  • Deliver targeted surveys to frustrated customers in a timely and relevant manner.
  • Laser in on sessions where a user left specific feedback, a low NPS score, or any other response data and get a full view of their experience.
  • … and more

Connecting the two products currently requires some manual configuration of your survey and your site intercepts. Note that all examples below are for the JFE survey engine, not the SE engine.


Step 1: Pass FullStory data into surveys

Using Website / App Feedback projects

If you have access to Website / App Feedback projects in Qualtrics, you can use intercepts to automatically pass information into FullStory when a survey is launched. Intercepts serve as the glue between your survey and your website, and they are highly configurable. The steps below will show how to use embedded data to pass FullStory data points into the survey. 

Open your intercept and, in the relevant action set(s), choose Options > Embedded Data.


Create three pieces of embedded data: fsCookie, fsSession, and fsSessionUrl, as shown in the screenshot below:


 The fsCookie field should get its value from a cookie named “fs_uid”. The other two embedded data fields are JavaScript values. The expression for fsSession should be as follows:

window["_fs_namespace"] && window[window["_fs_namespace"]] && window[window["_fs_namespace"]].getCurrentSession ? window[window["_fs_namespace"]].getCurrentSession() : ""


The expression for fsSessionUrl should be as follows:

window["_fs_namespace"] && window[window["_fs_namespace"]] && window[window["_fs_namespace"]].getCurrentSessionURL ? window[window["_fs_namespace"]].getCurrentSessionURL() : ""

Click “Save,” and then publish the intercept.

Without Website / App Feedback projects

If your Qualtrics plan does not include Website / App Feedback projects, but you have built your own mechanism for launching surveys from your website, you can still integrate Qualtrics and FullStory. Note that, because FullStory only runs on your website, integration is only possible if you are somehow distributing surveys from your website and can make calls to FullStory’s client API.

Embedded data fields are passed into the survey as part of the survey URL. In the absence of site intercepts, you can simply construct the appropriate survey URL yourself.

You will need to add one parameter to the survey URL for each embedded data field. The final URL should be of the form:


Here is a more complete example; note that the parameter values have been URL-encoded:


Parameter values:

Step 2: Configure survey recording

The next step is to configure the survey to record activity into the same FullStory session from which the survey was launched. 

Set up incoming embedded data

Open your survey project, and then open the survey flow. At the start of the flow, if you don’t already have an embedded data block, add one. Add fields for fsCookie, fsSession, and fsSessionUrl, without setting a value (their values will thus be taken from the survey URL). 


FullStory session IDs and URLs will now appear in the embedded data section of survey responses. 

The FullStory cookie will also appear, but the cookie has no value by itself. In the next section, we will see how to set up recording of surveys, and how to use the cookie to tie together survey activity with recordings of your website.

Add recording JavaScript

JFE surveys are single-page apps, so recording the survey can be accomplished by adding the FullStory recording snippet to the first question of your survey. On the first question, click the settings gear icon, and then “Add JavaScript...”


In the below snippet, replace “[YOUR RECORDING SNIPPET HERE]” with your FullStory snippet. If you have no other JavaScript logic on the question, you can copy-paste the entire snippet into the pop-up window. If you have existing logic in the addOnload function, add the content of the function to your existing function content:

Qualtrics.SurveyEngine.addOnload(function() {
// pull FS cookie from embedded data
const embeddedCookie = '${e://Field/fsCookie}';
if (embeddedCookie) {
const nowInSeconds = Math.floor(Date.now() / 1000);
const maxExpiration = nowInSeconds + 31536000;
const [content, maybeExpiration] = embeddedCookie.split("/");
let expiration = parseInt(maybeExpiration);
if (expiration > maxExpiration) { expiration = maxExpiration; }
let newCookie = 'fs_uid=' + content + '/' + expiration;
newCookie += '; domain=.qualtrics.com; Expires=' + expiration
newCookie += '; path=/; SameSite=Strict; Secure';                                
document.cookie = newCookie;
// FS snippet


This logic extracts the FullStory cookie from the embedded data and sets the cookie on the Qualtrics domain. It then runs the FullStory recording snippet to start recording the survey. The cookie is sent along with recorded data, so its value can be used on FullStory servers to stitch together activity on your website domain with survey activity on the Qualtrics domain.

Step 3: Pass survey results into FullStory

Now that surveys are being recorded, the FullStory client API will be available on the survey. We can use custom variables and/or custom events to send values from survey responses into FullStory. 

The below snippet is an example of submitting the user’s NPS to FullStory when it is the last question in a survey. It is the JavaScript snippet on the NPS question itself, so `this` in the code refers to the NPS question object. This example makes use of both custom variables and custom events, so that you can search for a user based on their score and for sessions during which they submitted the score.

Qualtrics.SurveyEngine.addOnPageSubmit(function(type) {
// This is the last page, so submit a custom event for the survey with the NPS score
if(type == 'next') {
let eventProps = {};
let selected = this.getSelectedChoices();
if (selected.length > 0) {
let nps = parseInt(selected[0]);
eventProps.nps_real = nps;
eventProps.isDetractor_bool = nps <= 6;
eventProps.isPromotor_bool = nps >= 9;
eventProps.isPassive_bool = nps > 6 && nps < 9;
switch(nps) {
case 10:
case 9:
eventProps.npsGroup_str = 'Promoter';
case 8:
case 7:
eventProps.npsGroup_str = 'Passive';
eventProps.npsGroup_str = 'Detractor';
// TODO: you could add answers to other questions that are of interest, for cohorting
FS.event('Qualtrics CSAT Submitted', eventProps, 'qualtrics');
FS.setUserVars(eventProps, 'qualtrics');


Note that because this is a question on a page which has not yet been submitted, you cannot use
piped text to fetch the answer value. Piped text could be used for the answers to questions on previous pages in the survey flow, and it would considerably simplify the above example. 

This example shows how to retrieve the answer to a Net Promoter Score question; refer to the Qualtrics Question API for examples of working with answer values to different types of questions.

Step 4: Intercept displayed custom event (optional)

Qualtrics keeps statistics that allow you to calculate click-through rates for your intercepts, but you may want to do similar or related analyses in FullStory. You can capture the display of an intercept by adding JavaScript to your intercept as in the example below: 


You could then use the FullStory Conversions functionality to view click-through rates for your survey:


Step 5: Manual deployment (optional)

Note: If you are already using a tag manager or CDP to deploy FullStory, you don’t need to do the below steps.

If you are not using a tag manager like GTM or a CDP like Segment or Tealium, you may wish to manually ensure that there are no load timing issues between FullStory and your Qualtrics intercept. The below steps ensure that the intercept loads after recording starts, so that FullStory session URLs are available to the Qualtrics intercept.

Configure your intercept for manual deployment by following the instructions at “Implementing Manually with JavaScript API.” This means that you will need additional JavaScript code in your web application to load the intercept and evaluate whether it should be displayed to the end user.

This additional JavaScript code is provided by the FullStory integration with Qualtrics. To get started, navigate to “Integrations and API Keys” in your FullStory settings. Next to the Qualtrics integration, click the “Add” button.


When installation is complete, the integration configuration window will slide out from the right side. Click “Done.”



Ragehooks for Qualtrics

Using FullStory Ragehooks and Qualtrics Website Feedback or JSON events, you can leverage frustrated session information with Qualtrics. This information can be used to display a prompt for feedback at the moment of frustration or with event-based actions to start any number of tasks within Qualtrics.


Displaying the Website Feedback prompt

A convenient way to obtain feedback is using the Qualtrics Website Feedback prompt that is already deployed on your website.  FullStory Ragehooks can launch the feedback prompt after a configurable number of frustration events have occurred.

Similar to adding the FullStory recording snippet to your site, add the following Ragehook for Qualtrics Website Feedback snippet. The Ragehook snippet has no dependency on the recording snippet, but if you’d like to include it near it or other FullStory integrations, that’ll keep everything organized.

var _fs_qualtrics_frustration_intercept = (function (exports) {
  'use strict';

  // TODO decide when you want to launch the intercept
  var threshold = 0; // send when a threshold has been met or is above
  // number of frustration events for the current page
  var counter = 0;
   * Adds frustration event listeners.
  function addEventListeners() {
    ['fullstory/rageclick'].forEach(function (event) {
      window.addEventListener(event, launchIntercept);
   * Launches the Intercept currently configured on the page.
  function launchIntercept() {
    var feedbackButton = document.querySelector('#QSIFeedbackButton-btn');
    // NOTE only a single rage event will trigger the intercept in a browser session
    if (feedbackButton && counter >= threshold && !sessionStorage.getItem('_fs_xmInterceptShown')) {
      sessionStorage.setItem('_fs_xmInterceptShown', 'true');
  function run() {

  exports.addEventListeners = addEventListeners;
  exports.launchIntercept = launchIntercept;
  exports.run = run;

  return exports;



Depending on when you'd like to show the Website Feedback prompt:

  • Change the threshold variable if you’d like to display the prompt only when the user exceeds some predefined number of frustration events. By default, it will display on the first frustration event.


Sending email surveys

An alternative to Website Feedback is sending an email survey using an event-based action and email task.  Sending email surveys to frustrated customers has three parts:

  1. Create an event-based action and email task in Qualtrics.
  2. Host a bit of code that executes on a server. This uses a Qualtrics API token and should not be used client-side.
  3. Add client-side code to your website that leverages FullStory Ragehooks for Qualtrics JSON Events and sends information to your server-side program.


Create an event-based action and task

Start by generating an API token if you have not done so previously.

Next, visit the Actions page in Qualtrics and click Create an action. Choose event-based from the dropdown. Then click choose event and select the JSON Event tile. Use the Copy URL button to copy the url to the clipboard, it will be used in the subsequent section.

Next, follow instructions to deploy the server-side and client-side code. You’ll return to the action dialog after deployment.


Deploy the server-side code

In order to send JSON events to Qualtrics, an API token must be used. Since this is like a password, it should not be placed in any code that may appear in the browser. A small program can be hosted to create the JSON event while securely storing your API token.

Below is a sample server that uses Express to create such a program; Qualtrics also provides a similar example. Your IT department or developers may already have ways to integrate with Qualtrics, so check with them first before writing your own solution.

  1. Perform the following changes to start the server:
    Update the text <VALUE_FROM_COPY_URL> with the Copy URL text from your clipboard.
  2. Either set an environment variable called apiToken with your Qualtrics API token or change the code const { apiToken } = process.env; to include your token.
  3. Deploy or start the server locally using node qualtrics-proxy.js. You’ll need Node.js to do this.
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const request = require('request-promise');

// a map of event types to action endpoints
// see https://www.qualtrics.com/support/survey-platform/actions-module/json-events/#SettingUp
const eventEndpoints = {
  'fullstory/rageclick': '<YOUR_ACTION_URL>'

// https://www.qualtrics.com/support/survey-platform/actions-module/json-events/#Example
async function createEvent(payload) {
  const { apiToken } = process.env;

  if (!apiToken) {
    throw new Error('Qualtrics apiToken is not defined');

  const { type } = payload;
  const url = eventEndpoints[type];

  if (!url) {
    throw new Error(`Failed to map FullStory frustration event to Qualtrics JSON Event URL`);
  } else {
    console.log(`Sending JSON event to ${url}`);

    return await request.post({
      json: true,
      body: payload,
      headers: {
        'Content-Type': 'application/json',
        'X-API-TOKEN': apiToken

// create a simple Express server to handle POST from browser
const app = express();
app.post('/jsonevent', async (req, res) => {
  console.log(`JSON event received\n${JSON.stringify(req.body, null, 2)}`);

  try {
    const response = await createEvent(req.body);
    console.log(`Qualtrics returned\n${JSON.stringify(response, null, 2)}`);

  } catch (err) {

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`Qualtrics proxy listening on ${port}`);


Adding the Ragehook snippet

Similar to adding the FullStory recording snippet to your site, add the following Ragehook for Qualtrics JSON Events snippet. The Ragehook snippet has no dependency on the recording snippet, but if you’d like to include it near it or other FullStory integrations, that’ll keep everything organized.

var _fs_qualtrics_frustration = (function (exports) {
 'use strict';

 // Server-side sample endpoint (note this is your proxy code and not Qualtrics)
 var proxyEndpoint = '';
 // TODO decide when you want to send frustration events to Qualtrics
 var threshold = 0; // send when a threshold has been met or is above
 // number of frustration events for the current page
 var counter = 0;
  * Adds frustration event listeners.
 function addEventListeners() {
  ['fullstory/rageclick'].forEach(function (event) {
   window.addEventListener(event, postFrustrationEvent);
  * To send a survey, you'll need to provide the following details about the user:
  * - First name
  * - Last name
  * - email address
  * If you use a XM Directory task, prevent multiple contacts from being added to
  * XM Directory, by enabling deduplication on the email address.
 function getUser() {
  return {
   firsName: '<YOUR_USERS_FIRST_NAME>',
   lastName: '<YOUR_USERS_LAST_NAME>',
   email: '<YOUR_USERS_EMAIL>'
  * Sends the frustration event to the Qualtrics proxy sample.
  * @param event the browser's CustomEvent emitted by FullStory
  * @param url to the Qualtrics server-side code
 function postFrustrationEvent(event, url) {
  if (url === void 0) { url = proxyEndpoint; }
  var type = event.type;
  var _a = event.detail,
   eventEndTimeStamp = _a.eventEndTimeStamp,
   eventReplayUrlAtCurrentTime = _a.eventReplayUrlAtCurrentTime,
   eventReplayUrlAtStart = _a.eventReplayUrlAtStart,
   eventStartTimeStamp = _a.eventStartTimeStamp;

  // create the json event payload using info from the ragehook
  var body = {
   type: type,
   eventEndTimeStamp: eventEndTimeStamp,
   eventReplayUrlAtCurrentTime: eventReplayUrlAtCurrentTime,
   eventReplayUrlAtStart: eventReplayUrlAtStart,
   eventStartTimeStamp: eventStartTimeStamp,
  // a user should be present
  var user = getUser();
  if (user) {
   body.firstName = user.firsName;
   body.lastName = user.lastName;
   body.email = user.email;
  else {
   console.warn("Unable to send survey because user information is missing");
  // only a single rage event will be sent for the current browser session
  if (counter >= threshold &&
   sessionStorage.getItem('_fs_xmEventSeen') !== type) {
   var request = new XMLHttpRequest();
   request.open('POST', url);
   request.setRequestHeader('Content-type', 'application/json');
   sessionStorage.setItem('_fs_xmEventSeen', type);
 function run() {

 exports.addEventListeners = addEventListeners;
 exports.getUser = getUser;
 exports.postFrustrationEvent = postFrustrationEvent;
 exports.run = run;

 return exports;



You’ll want to make a few adjustments to the snippet:

  • Update the proxyEndpoint variable so that it matches your server’s endpoint running the server-side code. This will vary depending on how you’ve hosted the server-side program. If you’re using the sample locally, the value is http://localhost:3000/jsonevent.
  • Change the threshold variable if you’d like to send surveys only when the user exceeds some predefined number of frustration events. By default, it will send the JSON event on the first event.
  • Implement the getUser() function based on your website. If you’re just testing, you can manually fill in your personal information.


Capture event data

With the server-side and client-side code deployed, return to the action dialog, which is still Waiting for an event to be fired. (If it does not say this, simply refresh the page.)

From your website that now has the Ragehook snippet, click furiously on some white space or text. This will trigger the Ragehook and will send a JSON event to Qualtrics. The actions dialog will momentarily show the contents of the JSON event.



Click Finish, then select Add Task, and select the XM Directory tile. Select Distribute survey and fill out the corresponding details:




There are a few things to point out in the above settings:

  • See Qualtrics help documentation more details on XM Directory.
  • The survey is immediately sent. If you’d rather delay, use the Schedule a time option.
  • Since these are “frustrated users”, they are being added to a specific contact list called Frustrated Visitors. You can choose to add these to an existing contact list if you’d like.
  • Each time the customer sends a frustration event to Qualtrics, it will be recorded as a transaction to the contact info. If this is the customer’s first event, a new contact will be added to the list.  Visit Qualtics help documentation for more about transactional data.

Click Save and update the title to something descriptive. Finally, toggle the action to On to complete the integration.

Close your browser window and open a new one. Rage clicking will trigger a survey to be delivered. Congratulations, you’ve now set up an automated survey solution for your most frustrated customers.

One final note: the client-side code will send the JSON event only once during a customer’s visit. If the user returns to your site some time later, it will send another event. Qualtrics will temporarily recognize the user’s email address and prevent re-sending a previously sent survey (see "Skipped as Duplicate in Email Distribution Error Messages). To be certain that users do not receive unnecessary surveys as a result of Ragehook integration, update the server side code to include additional behavior to store previous interactions with site visitors. 


Need to get in touch with us?

The FullStory Team awaits your every question.

Contact us