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
Controller
public ActionResult TriggerGoal(string goalId)
{
// stop tracking this API call
Tracker.Current?.CurrentPage?.Cancel();
trackingRepository.TriggerGoal(goalId);
}
TrackingRepository
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;
TriggerGoal(goalId);
}
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);
return;
}
Log.Info($"Goal Triggered: Contact: {Tracker.Current.Contact.ContactId} : {goalId}", this);
var page = fromApi ? GetPageForApi() : Tracker.Current.CurrentPage;
page.RegisterGoal(goalDefinition);
}