Integrate Sitecore Search with Workday jobs

Integrating two specific platforms can sometimes feel like hula hooping at the Olympics. But it can also give you insights into solutions that can be applied to other integrations as well. Authentication hurdles and multilingual requirements are a common scenario and for both I have some insights and learnings to share.

Why combine Sitecore Search and Workday?

Workday is a popular platform used by companies to manage their workforce. Companies can manage open positions in Workday and integrate them via API into the website. Instead of directly adding the API to the frontend application to display jobs, we chose to index them with Sitecore Search. The benefits being that jobs can be part of any search-based component, are filterable and can leverage personalization and boost features of Sitecore Search.

💡
Sitecore Search can not only crawl websites, but also APIs and any other publicly available resource.

API authentication

Workday offers powerful REST APIs, but they are protected with OAuth. Unfortunately, Sitecore Search does not offer this type of authentication for its crawlers.

Another API option that Workday offers is called Report-as-a-Service (RaaS). The data available for this API can be configured by Workday and it supports basic authentication.

Adding support for this is as easy as adding an authorization header to the trigger for your crawler:

function extract() {
    return [{
        "url": "[YOUR_RAAS_API]",
        "headers": {
            "Authorization": [
                "Basic NVSVX0lOAVD5MDhfVU5JQ19JbnRlcm5hbF9VU19JbmJvdW5kX0ZSQTpwOTNxZmI3NlBASEpEQG1yd25rQHQ="
            ]
        }
    }];
}

Support multiple languages

It is likely that your website supports multiple languages and so do the job openings. Therefore you might also need to index them by language. The Workday RaaS APIs handle languages not through the URL of the API, but with different logins. This means, the URL will stay the same, but you will have a different authorization token per language.

Add a trigger for every language

The authorization header can be set on a trigger level. To have different headers, you will need to add multiple triggers. In our example, we support both en-us and de-ch

⚠️
There seems to be an issue in Sitecore Search that multilingual indexing does not work properly when multiple triggers share an identical URL. As a workaround, we add an #EN or #DE to the API endpoint.

To correctly index an entry for multiple languages, you need to define a locale extractor. Consider reading Dealing with multiple languages in Sitecore Search with XM Cloud if you want to learn more about locale extractors.

Dealing with multiple languages in Sitecore Search with XM Cloud
Unless you have been replaced by AI, there is a chance that you will be the one to add multiple languages to your Sitecore website. When you want to integrate this with Sitecore Search, there are a few things you need to know. Let’s try to see how we can

A simple trick to pass a language from the trigger to the locale extractor is an additional header. Here is an example how the final source of a trigger for the German language could look like.

function extract() {
    return [{
        "url": "[YOUR_RAAS_API]#DE",
        "headers": {
            "Authorization": [
                "Basic NVSVX0lOAVD5MDhfVU5JQ19JbnRlcm5hbF9VU19JbmJvdW5kX0ZSQTpwOTNxZmI3NlBASEpEQG1yd25rQHQ="
            ],
            "culture": ["de_ch"]
        }
    }];
}

Define a locale extractor

The locale extractor for this case is very simple. Use the Header extractor type and configure the header name from the previous paragraph.

Crawl the API

To get data out of the API and into Sitecore Search is the purpose of a document extractor. Be sure to check the Localized option and write your JavaScript code. Here is a simplified example of how this could look like.

function extract(request, response) {
  var $ = response.body;

  var jobs = $('wd\\:Report_Data').find('wd\\:Report_Entry');
  var out = [];

  for (var i = 0; i < jobs.length; i++) {
    var job = jobs[i];

    var id =
      $(job).find('wd\\:workdayID').text() ||
      $(job).find('wd\\:jobRequisitionId').text() ||
      $(job).find('wd\\:Job_Requisition').children('wd\\:ID').first().text();

    out.push({
      id: id,
      type: 'Job',
      type_id: 'job',
      page_type: 'Job Workday',
      page_type_id: 'job-workday',
      name: $(job).find('wd\\:title').text(),
      job_name: $(job).find('wd\\:title').text(),
      job_field: $(job).find('wd\\:jobFamilyGroup').attr('wd:Descriptor'),
      job_exp_level: $(job).find('wd\\:CFINT0908ExperienceLevel').text(),
      job_description: $(job).find('wd\\:workerSubType ').attr('wd:Descriptor'),
      job_offer: $(job).find('wd\\:timeType').attr('wd:Descriptor'),
      job_number: $(job).find('wd\\:jobRequisitionId').text(),
      job_posting_status: $(job).find('wd\\:Status').text(),
      url: $(job).find('wd\\:url').text()?.toLowerCase(),
      tile_type: 'job',
    });
  }

  return out;
}

Group jobs in a widget

A way to make the indexed jobs available to the Sitecore Search API is to combine them in a widget. If you haven't build a Sitecore Search powered component before, you might want to read Building Sitecore Search powered Components.

Building Sitecore Search powered Components
When we think of a search solution, our brains might immediately jump to the use case of displaying search results for a user query. There are however many more use cases. This blog post shows how you can configure Sitecore Search to provide a component with data to display upcoming

In the crawler code, we assign every job document a type_id with the value job. We can use this to define a widget rule that blacklists everything that is not a job. Additionally, we can also filter out expired or deleted postings.

Build your component

The final step is to build the actual component that exposes the jobs to the website. In our case, we built a component where you can search for jobs and filter them by various facets. Here is what we ended up with.

Summary

Integrating Sitecore Search with Workday has been an interesting journey. It's fascinating to see how combining these platforms can create such a seamless job search experience. The challenges we overcame taught me valuable lessons about API integrations and multilingual support. I'm excited to apply these insights to future projects and hope they prove useful to you too.