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
Scenario
Apideck monitors the ATS system for changes
Apideck detects an update for a candidate
Apideck broadcasts a webhook event ats.applicant.updated
Client application receives webhook event ats.applicant.updated
Client requests “applicant details ” via Unify API
Client transforms the applicant details into an employee model
Client executes a “update employee” request via Unify API
Screenshots
Teamtailer - Updated applicant
https://app.teamtailor.com/companies/YTf4HVdoBks/candidates/segment/all/candidate/31066324
Client - handling the incoming applicant trigger and starting the update employee process based on a webhook.
Humaans - Updated employee
https://app.humaans.io/
Client POC Application
Create a new folder
Place the files below in the folder
Execute the command npm install , this will install all the dependencies
Configure the correct API key & other settings in the .env file
Execute the command npm run start , to start the client application, which will run on: 127.0.0.1:7777
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.
Goto Humaans > People https://app.humaans.io/
Goto “Amelia Earhart” > “Full Profile”
Offboard “Amelia Earhart”
Click “Offboarding” from the sidebar
Click “Confirm and Schedule offboarding
Next click “Delete this profile”