Get Started
Mobile Integrations
Ingesting Data into Marketing Cloud Personalization
Marketing Cloud Personalization Trends
Web Surveys
Sending Campaign Statistics to Third Parties
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.
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.
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.
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>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
The survey renders after the SurveyJS library instance loads.
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.
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:
survey:<surveyId>:<questionId>:<rowId>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.
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
1SalesforceInteractions.sendEvent({
2 source: {
3 surveyId: "string"
4 surveyStartTime: "string"
5 },
6 user: {
7 attributes: {
8 "survey:<surveyId>:<questionId>:<rowId>": "string"
9 }
10 }
11})1Evergage.sendEvent({
2 source: {
3 surveyId: "string"
4 surveyStartTime: "string"
5 },
6 user: {
7 attributes: {
8 "survey:<surveyId>:<questionId>:<rowId>": "string"
9 }
10 }
11})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.
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}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}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
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})();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}Download the sample survey template for your SDK namespace: