Web Surveys

Marketing Cloud Personalization Surveys uses SurveyJS to build and render surveys on your website. Surveys are deployed within web campaigns, leveraging Personalization’s targeting rules and testing capabilities.

Use survey data to create user segments, enhance personalization experiences, A/B test to validate business impact, and analyze feedback. You can clone or build a survey using the Surveys editor in the Personalization UI, or use the SurveyJS JSON editor for more advanced customization. For more information about Personalization Surveys, see Surveys. For detailed documentation on using the JSON survey editor, see the official SurveyJS documentation.

Before You Begin 

  • Surveys are available to all Premium edition customers. If your account was created before April 7, 2021, request a Gears refresh from Support to turn on this feature. Your account administrator can then review the Gears list to confirm whether the Surveys Gear is enabled.
  • Create at least one survey in the Surveys editor within the Personalization UI.

With these steps complete, you’re ready to use Surveys in your web campaign templates.

To learn how to create a survey, see Create a Survey.

Third-Party Library 

SurveyJS

  • Version: 1.0.95
  • Sizes:
    • JS (compressed) - 149 KB
    • CSS (compressed) - 4 KB

This version of SurveyJS is a variant of the jQuery version, updated to use the Cash library included in the Personalization module of the Salesforce Interactions SDK. However, features such as SurveyJS custom widgets are currently not supported.

API 

Methods 

This code injects the SurveyJS library, renders the survey, and handles responses upon completion.

1renderSurvey(survey: Survey, renderTarget: Element): Promise<void>

The Survey interface defines the structure of the survey object, containing its ID, name, and the SurveyJS JSON configuration.

1interface Survey {
2  id: string;
3  name: string;
4  config: SurveyConfig; // references SurveyJS's JSON object
5}

To integrate custom resources, such as themes, use injectSurveyResourcesIntoPage().

1injectSurveyResourcesIntoPage(): Promise<void>

Implementation 

  1. Invoking renderSurvey automatically injects the SurveyJS library from the Personalization CDN using injectSurveyResourcesIntoPage on page load.

    This automatic injection doesn’t occur for client-side page changes, such as those within a Single-Page Application (SPA).

    Note

  2. The survey renders after the SurveyJS library instance loads.

  3. Upon completion or submission of each survey page, the system parses the survey configuration and sends an event with each user’s responses stored as survey attributes.

Store Responses on the User Profile 

Survey attributes are automatically created when you design a survey. You can view them in Personalization under Settings > Attributes > Survey Attributes. These attributes populate an individual user’s profile with their survey responses only after Personalization receives a survey event in the correct format. For information about sending a survey event, see Send a Survey Event.

Upon receiving a survey event, Personalization stores the responses within corresponding survey attributes. Each event must include the surveyId, surveyStartTime, and all survey attributes representing the user’s responses.

Depending on the type of question, survey responses are stored in these formats:

  • Matrix questions: survey:<surveyId>:<questionId>:<rowId>
  • Other question types: survey:<surveyId>:<questionId>

Personalization generates <surveyId> and <questionId> to identify surveys and questions respectively. <rowId> is a unigue ID for a row within a Matrix question and is omitted for other question types.

By default, Personalization limits the number of survey attributes to 10.

Send a Survey Event 

The Surveys Gear automatically handles survey submissions and sends survey events in the required format. As a developer, you don’t need to include any code that sends a survey event within your web campaign template.

These examples show how to manually track a survey event in each SDK namespace, if needed.

Avoid tracking survey events outside the Surveys gear.

Important

SalesforceInteractions Namespace
1SalesforceInteractions.sendEvent({
2    source: {
3        surveyId: "string"
4        surveyStartTime: "string"
5    },
6    user: {
7        attributes: {
8            "survey:<surveyId>:<questionId>:<rowId>": "string"
9        }
10    }
11})
Evergage Namespace
1Evergage.sendEvent({
2    source: {
3        surveyId: "string"
4        surveyStartTime: "string"
5    },
6    user: {
7        attributes: {
8            "survey:<surveyId>:<questionId>:<rowId>": "string"
9        }
10    }
11})

Style Surveys 

You can style your surveys in different ways using methods described in the SurveyJS documentation. Some of these methods include using Custom CSS and creating a Custom Theme.

These examples demonstrate how to apply custom styling with client-side JavaScript using the applyTheme function.

SalesforceInteractions Namespace
1function apply(context, template) {
2  const contentZoneSelector = SalesforceInteractions.mcis.getContentZoneSelector(
3    context.contentZone,
4  );
5
6  return SalesforceInteractions.DisplayUtils.bind(buildBindId(context))
7    .pageElementLoaded(contentZoneSelector)
8    .then((element) => {
9      const $survey = SalesforceInteractions.cashDom(template(context).trim());
10
11      return SalesforceInteractions.mcis.Surveys.injectSurveyResourcesIntoPage().then(() => {
12        Survey.StylesManager.applyTheme("default");
13        return SalesforceInteractions.mcis.Surveys.renderSurvey(context.survey, $survey).then(
14          () => {
15            SalesforceInteractions.cashDom(element).append($survey);
16          },
17        );
18      });
19    });
20}
Evergage Namespace
1function apply(context, template) {
2  const contentZoneSelector = Evergage.getContentZoneSelector(context.contentZone);
3
4  return Evergage.DisplayUtils.bind(buildBindId(context))
5    .pageElementLoaded(contentZoneSelector)
6    .then((element) => {
7      const $survey = Evergage.cashDom(template(context).trim());
8
9      return Evergage.Surveys.injectSurveyResourcesIntoPage().then(() => {
10        Survey.StylesManager.applyTheme("default");
11        return Evergage.Surveys.renderSurvey(context.survey, $survey).then(() => {
12          Evergage.cashDom(element).append($survey);
13        });
14      });
15    });
16}

Track Survey Statistics 

When using the sample survey template in this section in a web campaign, an impression statistic is automatically tracked after the initial page of the survey renders. To track additional statistics, see Campaign Stats Tracking.

Handlebars code

1{{!--
2    Template: Sample Survey Template
3
4    Requirements:
5    1) Surveys are available to all Premium edition customers. If your account was provisioned prior to 4/7/2021, you
6        will need to request a gears refresh for your account from support in order for the Survey gear to show up in
7        your gears list and for the feature functionality to be fully enabled.
8    2) Create at least one Survey in the Survey editor, which can be found in the Personalization UI.
9--}}
10
11<div id="evg-surveys" data-evg-campaign-id="{{campaign}}" data-evg-experience-id="{{experience}}"
12    data-evg-user-group="{{userGroup}}" aria-label="Survey{{#if survey}} - {{survey.name}}{{/if}}">
13</div>

CSS

1/* Default Theme */

Client-side JavaScript

SalesforceInteractions Namespace
1(function () {
2  /**
3    * @function buildBindId
4    * @param {Object} context
5    * @description Create unique bind ID based on the campaign and experience IDs.
6    */
7  function buildBindId(context) {
8    return `${context.campaign}:${context.experience}`;
9  }
10
11  function apply(context, template) {
12    const contentZoneSelector = SalesforceInteractions.mcis.getContentZoneSelector(
13      context.contentZone,
14    );
15
16    return SalesforceInteractions.DisplayUtils.bind(buildBindId(context))
17      .pageElementLoaded(contentZoneSelector)
18      .then((element) => {
19        const $survey = SalesforceInteractions.cashDom(template(context).trim());
20
21        return SalesforceInteractions.mcis.Surveys.renderSurvey(context.survey, $survey).then(
22          () => {
23            SalesforceInteractions.cashDom(element).append($survey);
24          },
25        );
26      });
27  }
28
29  function reset(context, template) {
30    SalesforceInteractions.DisplayUtils.unbind(buildBindId(context));
31    SalesforceInteractions.cashDom(
32      `[data-evg-campaign-id="${context.campaign}"][data-evg-experience-id="${context.experience}"]`,
33    ).remove();
34  }
35
36  function control(context) {
37    const contentZoneSelector = SalesforceInteractions.mcis.getContentZoneSelector(
38      context.contentZone,
39    );
40    return SalesforceInteractions.DisplayUtils.bind(buildBindId(context))
41      .pageElementLoaded(contentZoneSelector)
42      .then((element) => {
43        SalesforceInteractions.cashDom(element).attr({
44          "data-evg-campaign-id": context.campaign,
45          "data-evg-experience-id": context.experience,
46          "data-evg-user-group": context.userGroup,
47        });
48      });
49  }
50
51  registerTemplate({
52    apply: apply,
53    reset: reset,
54    control: control,
55  });
56})();
Evergage Namespace
1(function () {
2  /**
3    * @function buildBindId
4    * @param {Object} context
5    * @description Create unique bind ID based on the campaign and experience IDs.
6    */
7  function buildBindId(context) {
8    return `${context.campaign}:${context.experience}`;
9  }
10
11  function apply(context, template) {
12    const contentZoneSelector = Evergage.getContentZoneSelector(context.contentZone);
13
14    return Evergage.DisplayUtils.bind(buildBindId(context))
15      .pageElementLoaded(contentZoneSelector)
16      .then((element) => {
17        const $survey = Evergage.cashDom(template(context).trim());
18
19        return Evergage.Surveys.renderSurvey(context.survey, $survey).then(() => {
20          Evergage.cashDom(element).append($survey);
21        });
22      });
23  }
24
25  function reset(context, template) {
26    Evergage.DisplayUtils.unbind(buildBindId(context));
27    Evergage.cashDom(
28      `[data-evg-campaign-id="${context.campaign}"][data-evg-experience-id="${context.experience}"]`,
29    ).remove();
30  }
31
32  function control(context) {
33    const contentZoneSelector = Evergage.getContentZoneSelector(context.contentZone);
34    return Evergage.DisplayUtils.bind(buildBindId(context))
35      .pageElementLoaded(contentZoneSelector)
36      .then((element) => {
37        Evergage.cashDom(element).attr({
38          "data-evg-campaign-id": context.campaign,
39          "data-evg-experience-id": context.experience,
40          "data-evg-user-group": context.userGroup,
41        });
42      });
43  }
44
45  registerTemplate({
46    apply: apply,
47    reset: reset,
48    control: control,
49  });
50})();

Server-side TypeScript

1import { SurveyReference, SurveyReferenceLookup } from "surveys";
2
3export class SurveyTemplate implements CampaignTemplateComponent {
4  @title("Survey Selector")
5  @lookupOptions(() => new SurveyReferenceLookup())
6  surveyReference: SurveyReference;
7
8  run(context: CampaignComponentContext) {
9    const survey = context.services.surveys.getSurvey(this.surveyReference.id);
10    return {
11      survey: survey,
12    };
13  }
14}

Sample Template 

Download the sample survey template for your SDK namespace:

See Also