I'm working on a scenario where I have a modal on a page, and if/when the user dismisses it, I want to make it such that the modal doesn't come back up for the user. The ideal scenario is to have this occur cross-device for the user, so if they're logged in and have dismissed the modal, it remains that way regardless of what device they're on.

I'm still newer in the ways of the xDB Force, but my understanding is I could set up an event or goal on the modal's datasource, and on the click of the close icon, trigger a web API call that would record the event/goal to the current user. (I'm saying current since they could be anonymous and later sign up/in and the records should be merged.)

Is this the proper procedure, and if so has someone implemented such a scenario or can point to a good resource?

  • A couple questions: 1) Do you need to report on the closing of the modal? 2) How are you logging in? 3) Are you adding arbitrary modals to arbitrary pages or is this a one-off instance? Nov 16 '18 at 2:13
  • 1) No. 2) Could be anonymous users, or using Sitecore security 3) It's a rendering that could be added to any pages, or to multiple pages...I'm trying to target the modal content versus the page for that reason. Nov 16 '18 at 11:50

I use the API route all the time to track something someone is doing on the client site. Without a page load, Sitecore does not know what is happening or what to do. So in my JS I call my goal repo to trigger goals for opening of modals, starting of videos, etc... You just call the API and send in you goal ID.

You do have to take note that when you call an API and trigger a goal, you are assigning that goal to the API call itself, since it is a page that Sitecore understands exists. In my code below, if this is an API, GetPageForApi(), then I return the previous page. Which is the actual page before I started firing off API calls. You also want to trigger Tracker.Current?.CurrentPage?.Cancel(); in your controller so that Sitecore does not track the API call as a page view. And it ensures the real page is always the previous page.

Last this is that you want to make sure you send the API call as a real call with full credentials and cookie payload. This is because you want Sitecore to read the Analytics cookie to know the current user's contact ID. With fetch that would be credentials: 'include'

  fetch('/apis/v1/registration/xdb', {
    method: 'POST',
    body: formData,
    credentials: 'include',

Sitecore 9


public ActionResult TriggerGoal(string goalId)
    // stop tracking this API call



public bool TriggerGoal(string goalId)
    ID.TryParse(goalId, out var goal);

    if (!ID.IsNullOrEmpty(goal))
        eventRepository.TriggerGoalOnce(goal, true);
        return true;

    return false;

Event Repository

I include this code because you need to use DI to new up a new IDefinitionManager<IGoalDefinition>.

private readonly IDefinitionManager<IGoalDefinition> goalDefinitionManager;

public EventRepository(IDefinitionManager<IGoalDefinition> goalDefinitionManager)
    this.goalDefinitionManager = goalDefinitionManager;

In this example I want the ability to only trigger my goal once. So no matter how many times they do this, lets not keep generating events.

public void TriggerGoalOnce(ID goalId, bool fromApi = false)
    // if the user has trigger this goal before, skip it.
    if (IfGoalAlreadyExists(goalId)) return;


public bool IfGoalAlreadyExists(ID goalId)
    // get the goals in the current session
    var goalsTriggered = Tracker.Current.Session.Interaction.Pages.SelectMany(x => x.PageEvents).Where(x => x.IsGoal).ToList();

    // get the goals from the KeyBehaviorCache (past sessions)
    var behaviorCache = Tracker.Current.Session.Contact.KeyBehaviorCache.Goals.Any(x => x.Id == goalId.ToGuid());
    var pages = goalsTriggered.Any(x => x.PageEventDefinitionId == goalId.ToGuid());

    return behaviorCache || pages;

I also care if this is an API call. If it is, then assign the event to the previous page which is the actual page.

private IPageContext GetPageForApi()
    if (Tracker.Current?.Session?.Interaction?.PreviousPage == null)
        return Tracker.Current.CurrentPage;

    return Tracker.Current.Session.Interaction.PreviousPage;

public void TriggerGoal(ID goalId, bool fromApi = false)
    var goalDefinition = this.goalDefinitionManager.Get(goalId.Guid, CultureInfo.InvariantCulture);
    if (goalDefinition == null)
        Log.Warn($"Cannot find goal: {goalId}", this);

    Log.Info($"Goal Triggered: Contact: {Tracker.Current.Contact.ContactId} : {goalId}", this);

    var page = fromApi ? GetPageForApi() : Tracker.Current.CurrentPage;


  • This is good stuff, Chris, thanks. One question on the page bit, does it have to be a page? Since in the end everything is an item, could I assign the goal to the content of the modal instead (the datasource), so if someone puts the same modal popup on multiple pages, and they triggered a close on one page, it doesn't come back? It would seem the answer is yes but I want to confirm with you. Cheers! Nov 16 '18 at 11:53
  • Yes, you can assign the event to the modal item. See my question and the answer here on how to add an item to the tracker. You can assign the goal to the new "page" after you create it. sitecore.stackexchange.com/questions/11943/…. You just need to remember that once you assign the goal to your modal item, that item will show up as a page view in your analytics.
    – Chris Auer
    Nov 16 '18 at 13:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.