Sync Applicant (ATS) to Employee Onboarding (HRIS)

This guide provides a step-by-step procedure for integrating an ATS with an HRIS to automate the transfer of applicant data as soon as an applicant is marked as hired to the employee onboarding system.

This is a common use case for ATS tools that integrate with HRIS platforms. For the purposes of this guide, we are using connectors available on our Unified APIs:

Trigger: an applicant gets updated ⇒ the employee gets updated

See the full example on GitHub.

Flow diagram

Highlevel flow

Detailed flow

Untitled

Scenario

  1. Apideck monitors the ATS system for changes
  2. Apideck detects an update for a candidate
  3. Apideck broadcasts a webhook event
    ats.applicant.updated
  4. Client application receives webhook event
    ats.applicant.updated
  5. Client requests “applicant details” via Unify API
  6. Client transforms the applicant details into an employee model
  7. Client executes a “update employee” request via Unify API

Screenshots

Teamtailer - Updated applicant

https://app.teamtailor.com/companies/YTf4HVdoBks/candidates/segment/all/candidate/31066324

Untitled

Client - handling the incoming applicant trigger and starting the update employee process based on a webhook.

Untitled

Humaans - Updated employee

https://app.humaans.io/

Untitled

Client POC Application

  1. Create a new folder
  2. Place the files below in the folder
  3. Execute the command
    npm install
    , this will install all the dependencies
  4. Configure the correct API key & other settings in the
    .env
    file
  5. Execute the command
    npm run start
    , to start the client application, which will run on: 127.0.0.1:7777
  6. Simulate the Webhook event using this CURL command or via Postman
curl --location --request POST '127.0.0.1:7777/start' \
--header 'Content-Type: application/json' \
--data-raw '{
  "payload": {
    "event_type": "ats.applicant.updated",
    "unified_api": "ats",
    "service_id": "teamtailor",
    "consumer_id": "test-consumer",
    "event_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
    "entity_id": "31066324",
    "entity_url": "https://unify.apideck.com/ats/applicants/31066324",
    "entity_type": "applicant",
    "occurred_at": "2023-09-22T00:00:00.000Z"
  }
}'

Files

package.json file

{
  "name": "app-ats-hris",
  "version": "1.0.0",
  "description": "Sample webhook listener for ATS to HRIS app",
  "scripts": {
    "start": "node app.mjs"
  },
  "dependencies": {
    "@apideck/node": "^2.9.0",
    "concurrently": "^8.2.1",
    "dotenv": "^16.3.1",
    "express": "^4.18.1",
    "ngrok": "^5.0.0-beta.2"
  }
}

.env file

APP_ID="<YOUR_APP_ID>"
API_KEY="<YOUR_API_KEY>"
CONSUMER_ID="test-consumer"
SERVICE_ID_ATS="teamtailor"
SERVICE_ID_HRIS="humaans-io"

app.msj file

import dotenv from 'dotenv'
import express from 'express'
import { Apideck } from '@apideck/node'

dotenv.config()

// Initiate Unify SDK
const apideckSdk = new Apideck({
  apiKey: process.env.API_KEY,
  appId: process.env.APP_ID,
  consumerId: process.env.CONSUMER_ID
})

console.log('APP_ID: ' + process.env.APP_ID)
console.log('CONSUMER_ID: ' + process.env.CONSUMER_ID)
console.log('SERVICE_ID_ATS: ' + process.env.SERVICE_ID_ATS)
console.log('SERVICE_ID_HRIS: ' + process.env.SERVICE_ID_HRIS)

// Set listener
const app = express()
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
const port = process.env.port || 7777

//
const currentDate = new Date()
const year = currentDate.getFullYear()
const month = String(currentDate.getMonth() + 1).padStart(2, '0') // Months are 0-indexed
const day = String(currentDate.getDate()).padStart(2, '0')

const formattedDate = `${year}-${month}-${day}`

app.all('/*', async function (req, res) {
  // Initiate variables
  let employee = null

  // Build the response
  const msg = {
    message: 'Thank you for the message'
  }

  // Process the request
  const webhook_id = req?.body?.payload?.entity_id || 31066324

  // Lookup the ATS candidate
  if (webhook_id) {
    try {
      const { data } = await apideckSdk.ats.applicantsOne({
        id: webhook_id,
        serviceId: process.env.SERVICE_ID_ATS
      })
      console.log('ATS - applicantsOne called successfully', data)
      // Set the employee object
      employee = {
        first_name: data.first_name,
        last_name: data.last_name,
        title: 'Product Manager',
        employment_start_date: formattedDate,
        employee_number: data?.id,
        employment_status: 'active',
        employment_role: {
          type: 'employee',
          sub_type: 'full_time'
        },
        gender: 'female',

        phone_numbers: data?.phone_numbers,
        emails: data?.emails,
        addresses: [
          {
            type: 'primary',
            city: 'Antwerp',
            country: 'BE'
          }
        ]
      }
      console.log('employee data:', employee)

      // Buildup the response
      msg.ats_candidate = {
        id: data.id,
        first_name: data?.first_name || '-',
        last_name: data?.last_name || '-'
      }
    } catch (error) {
      console.error('ATS error:', error)
    }
  }

  if (employee) {
    try {
      const { data } = await apideckSdk.hris.employeesUpdate({
        employee: employee,
        serviceId: process.env.SERVICE_ID_HRIS
      })
      console.log('HRIS - employeesUpdate called successfully', data)
      msg.hris_employee = {
        id: data?.id,
        url: `https://app.humaans.io/?profile=${data?.id}`
      }
    } catch (error) {
      console.error('HRIS error:', error)
    }
  }

  // console.log("-------------- New Request --------------");
  // console.log("Headers:",req.headers);
  // console.log("Body:", req.body);
  res.json(msg)
})

app.listen(port, function () {
  console.log(`Unify app listening at ${port}`)
})

Troubleshooting

Humaans has a unique employee validation based on the email address, so make sure that the employee “Amelia Earhart” does not exist in Humaans.

  1. Goto Humaans > People https://app.humaans.io/

  2. Goto “Amelia Earhart” > “Full Profile”

    Untitled

Untitled

  1. Offboard “Amelia Earhart”
    1. Click “Offboarding” from the sidebar
    2. Click “Confirm and Schedule offboarding
    3. Next click “Delete this profile”

Untitled

Untitled

Untitled

Untitled