The source tracking APIs (/trackers) defined by TAS are for a candidate source tracking system based on fractional attribution.
With fractional attribution, an end result (such as someone applying for a job) can be mapped back to a whole chain of user interactions such as social sharing, emailing links, etc. This provides more detailed sourcing information than just tracking the last click that led the candidate to apply for the job.
There are two types of trackers:
Trackers are organized into trees. The path from any single leaf to the trunk of the tree represents the chain of sourcing events that led to a candidate outcome (like applying for a job).
Any candidate-facing web site (like a careers site, microsite, etc.) that is tracker aware should maintain session trackers when candidates use the site. This means:
For example, when a candidate first follows a link like this to a page in a job board/careers site:
https://acme.supersite.com/marketing.html?tracker=1234
..then the job board should create a session tracker and attach it to the tracker 1234. The resulting tracker tree will be like this:
1234
+-- 1287 (session tracker)
After this, the job board should attach the session tracker to all links that it emits, even for internal navigation around the site itself. For example, the candidate above might search for jobs at the site, and then click through to a specific job using a link like this:
https://acme.supersite.com/job=23&tracker=1287
Now the candidate might share the job via email using the browser's native share button. They email the job to a friend. The friend clicks through to the job. Because the friend represents a new unique visit, a new session tracker is created by the job board, linking back to tracker 1287. Now the tracker tree looks like this:
1234
+-- 1287 (session tracker)
+-- 1344 (session tracker)
If the friend now goes on to apply for the job, then the tracker will be passed into the applicant tracking system (ATS), giving it a deep insight into how the candidate came to hear about this job.
The following example (in Java) shows one approach to maintaining session trackers.
This algorithm:
// get details of tracker (if any) passed in as a url parameter
Integer trackerParam = httpRequest.getParms("tracker");
// get details of existing session tracker (if any) and its parent (if any)
HttpSession session = httpRequest.getSession(true);
Integer sessionTracker = session.getAttribute("sessionTracker");
Integer parentTracker = session.getAttribute("parentTracker");
// we test if session tracker missing or mismatched with an incoming tracker param - if so, potentially need to create a new one.
// there are two ways to do this, based on whether the app emits the tracker parameter on all its internal links (which might still
// end up getting shared - so it should) or not.
//
// conditional when the app does emit tracker on all links
if (sessionTracker == null || ! Objects.equals(parentTracker, trackerParam)) {
// conditional when the app does not emit tracker on all links
// if (sessionTracker == null || (trackerParam != null && (parentTracker == null || parentTracker != trackerParam)) {
// prepare a request body for use with either create tracker API call
String request =
{
"kind": "sessionCreation",
"ip": httpRequest.getIP(),
"Accept-Language": httpRequest..,
"userAgent": httpRequest..
}
if (trackerParam == null) {
// create a session tracker with no parent
POST /trackers/root
Request: <- request
Location: -> trackerID
session.setAttribute("sessionTracker", trackerID);
session.removeAttribute("parentTracker");
} else {
// create a session tracker chained to the incoming tracker
POST /trackers/byID/9022/trackers
Request: <- request
Location: -> trackerID
session.setAttribute("sessionTracker", trackerID);
session.setAttribute("parentTracker", parentTracker);
}
}
// from here on, we will add (or replace) the sessionTracker parm on any trackerized links we emit, e.g. "?tracker=9023"
// one tiny problem is that the current URL (i.e. this entry page) has the original (if any) tracker param on it.
// That creates a problem in that if this page contained a non-TAS social sharing plugin (one that just referenced the current page rather
// than having tracker param passed into it), this new session tracker would not be seen by it. The solution is
// to try and force navigation to any new page as quickly as possible - the new page's links will contain the proper tracker param.
Attaching the tracker parameter to all URLs that your site emits can affect your web site analytics. You may want to instruct analytics code to ignore the tracker parameter - for Google, see https://support.google.com/analytics/answer/1010249?hl=en.
Attaching the tracker parameter to all URLs that your site emits can affect your web site's SEO. You may want to instruct search engines to ignore the tracker parameter - for Google, use Webmaster tools.
The legacy ATS maintains candidateSource and device values in its session for incoming candidates.
A candidate arrives at:
acme/legacyats.com/jobs/Creative-Director-GR4022?candidateSource=facebook
The legacy ATS knows it will be handing off the apply process to some other microservices.
This means the apply process is out of the legacy ATS's control. But eventually the legacy ATS will regain control, when the apply app calls POST /candidates to push the candidate and job application in.
Therefore the legacy ATS needs to pack its legacy detail into a tracker, and pass that tracker in to the apply app, relying on the apply app eventually passing that tracker back in again.
Like any good candidate-facing app, the legacy ATS maintains a session tracker, as per the logic above.
In this case the ATS also embeds the legacy sourcing data in the userArea field:
Not shown, but legacy ATS passes this tracker ID in as a URL parameter to the apply app in its job details page.
ATS renders job details page. In it, a deep link like this leads to simpleapply:
acme.simpleapply.com/jobs/16670?tracker=16044
Simpleapply is tracker-aware and so creates a session tracker for the candidate (16045) and chains it to the existing tracker.
Once the candidate has completed the application, simpleapply passes that tracker (16045) back into the ATS via the POST /candidates API call.
The legacy ATS uses the following logic to extract its legacy source details from the incoming tracker:
The ATS searches up the tracker chain until it reaches a tracker with a "legacyATS" field in the userArea if found use that for source details else the ATS attempts to manufacture its own source details from the immediately incoming tracker. It can only do a very limited job. 1) the ATS searches up the tracker chain until it reaches any session tracker device = derive from user-agent as per existing ATS behaviour 2) The legacy ATS starts again and searches up the tracker chain until it reaches either: a session tracker with a "sessionCreation.referer" value that matches one of the rows in its own table of media a campaign tracker with a "campaign.referrer" value that matches one of the rows in its own table of media a campaign tracker with a "campaign.referrer" value that matches one of the rows in its own table of media if found candidateSource = the media
A sample chain is shown here though it doesn't yet show the data discussed above.
GET /trackers/16045/chain
Response:
A root campaign tracker is created:
POST /trackers/root
Response:
Location: 9022
A root tracker is obtained by the mailout app:
POST /trackers/root
Response:
Location: 9022
The outgoing email has a link like this:
<a href="/jobs/1004532?tracker=9022>Creative director</a>
The careers site is tracker-aware so creates a new session tracker.
The careers site may force navigation to a new page, so that any sharing plugins which rely on current URL have access to the up to date session tracker (see comments above).
One way is to hide the sharing plugins when the job details page is being generated for a new session, and instead show a button "Enable sharing" that just navigates to the current page - after navigation the page will not be being generated for a new session, so the sharing plugins will show as they should.
The friend clicks through to the target page at /jobs/1004532?tracker=9023.
The same logic as above fires, and the careersite app creates a new session tracker (9024), and attaches it to all links it emits.
This job is at /jobs/26?tracker=9024
they use sharing via email to send the link to themselves
they click through to the target page at /jobs/26?tracker=9024
the same logic as above fires, and another new session tracker (9025) is created and used for all links.
refly creates a new campaign tracker that will hold its own specific details, in this case that referral has been made by Fred.
POST /trackers
Response:
Location: 9041
refly now facilitates getting that link in front of candidate(s):
<a href="/jobs/1004532?tracker=9041>
As previously, a new session tracker (9046) is created by the careers site and chained to the existing tracker.
POST /trackers/byID/9041/trackers
Request
.. create session tracker
Response:
Location: 9046
The tracker is passed into POST /candidates with the job application.
The ATS producing POST /candidates now notifies the agent insight app that some change has happened to an application (i.e. creation in this case):
POST /applications/66022/deltaPings
agent insight gets the application's tracker
GET /applications/byID/66022/tracker
Response:
Location: 9046
agent insight now asks for every installed app that had some part in sourcing this application - i.e. that created a tracker upstream from 9046
GET /trackers/byID/9046/chain
Response:
agent insight now uses the list of sourcing apps to alert each of the tracker owners that an application they have sourced has changed. Theoretically a sourcing app may appear multiple times in the chain:
POST /applications/bySourcingApp/acme/supersoft/refly/66022/deltaPings
{
"operation": "insert",
"trackers": [ 9041 ]
}
refly asks for specific information about the application. Agent insight internally re-creates the chain of trackers and verifies that refly is allowed to ask for this application before responding
GET /applications/bySourcingApp/acme/supersoft/refly/66022/sourcerDetails
Response:
{
"sourcerBucket": "interview",
"trackers": [ 9041 ]
}
refly now has all the information it needs about the application that it had a part in sourcing