How-to Build an AI-Powered Candidate Assessment & Interview Scheduler
May 7, 2025
Let's build an application that scores a candidate's resume using an AI LLM and then schedule an interview if they pass a certain threshold, all with Unified's APIs.
You can see a video of this article at https://youtu.be/0hfH39vh7Qk
Overview of the system
The system we are creating today will be able to perform the following tasks:
- Fetch information of candidates via ATS
- Score candidates based on information they submitted and job requirements using AI
- Automatically retrieve scheduling links and send them to chosen candidates
Requirements
We will need the following things setup to get started:
- Node JS Installed on your machine (version 19 or higher)
- Unified.to account with active connections for:
- Greenhouse (ATS)
- OpenAI (GenAI)
- Calendly
Let's get going!
Step 1: Setting up Project
Create your project and initialize Node in the workspace by running the following command:
mkdir ai-job-automation
cd ai-job-automation
npm init -y
npm install express dotenv node-fetch
We will be using node's built-in fetch
to communicate with Unified.to's API, but you can also use Unified.to's Typescript SDK.
Step 2: Setting up your development Environment
In order to setup your development environment, you must get the following connection IDs from Unified.
- Unified API Key (Settings -> API Keys)
- OpenAI Connection ID (In the connections page)
- Calendly Connection ID (In the connections page)
- Greenhouse Connection ID (In the connections page)
Once you obtain these connection IDs, place them in your .env
file as follows.
UNIFIED_API_KEY=your_unified_api_key
CONNECTION_GPT=your_gpt_connection_id
CONNECTION_Calendly=your_calendly_connection_id
CONNECTION_GREENHOUSE=your_greenhouse_connection_id
PORT=3000
Step 3: Setup your Main Application
For this example we will be using a single file system, with various supporting functions. Create your main application file (app.js)
.
Import your dependencies
import 'dotenv/config';
import express from 'express';
import fetch from 'node-fetch';
const app = express();
app.use(express.json());
// Load environment variables
const {
UNIFIED_API_KEY,
CONNECTION_GPT,
CONNECTION_Calendly,
CONNECTION_GREENHOUSE,
PORT
} = process.env;
Step 4: Create AI Scoring function
We will now be creating the scoreCandidate
function, which will score and return each candidate, using Unified's GenAI Integration.
// Function to score candidate profiles using Unified GenAI (GPT-4o)
async function scoreCandidate(candidate, jobDescription) {
const candidateProfile = `
Name: ${candidate.name} Title: ${candidate.title} Company: ${candidate.company_name} Experiences: ${JSON.stringify(candidate.experiences)} Education: ${JSON.stringify(candidate.education)} `;
const prompt = `
Job Description:
${jobDescription} Candidate Profile:
${candidateProfile} Provide a match score (0-100). The passing threshold is 75. Be lenient, especially if they have relevant experience. Pass at least 30% of candidates. Provide a brief explanation in the following format:
Score: <number>
Explanation: <brief explanation>
`;
const response = await fetch(`https://api.unified.to/genai/${CONNECTION_GPT}/prompt`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${UNIFIED_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
messages: [{ role: "USER", content: prompt }],
max_tokens: 512,
temperature: 0.2
})
});
if (!response.ok) {
const errorDetails = await response.text();
throw new Error(`Error scoring candidate: ${response.statusText} - ${errorDetails}`);
}
const result = await response.json();
const content = result.choices[0]?.message?.content;
const scoreMatch = content.match(/Score:\s*(\d+)/i);
const explanationMatch = content.match(/Explanation:\s*(.+)/i);
const score = scoreMatch ? parseInt(scoreMatch[1], 10) : null;
const explanation = explanationMatch ? explanationMatch[1].trim() : "No explanation provided.";
return { score, explanation };
}
Step 5: Retrieve Calendly scheduling links
Candidates who pass the initial screening through the AI will automatically have a scheduling link retrieved through Unified's Calendly integration.
// Function to retrieve Calendly scheduling link via Unified integration
async function retrieveSchedulingLink(linkId) {
const response = await fetch(`https://api.unified.to/calendar/${CONNECTION_Calendly}/link/${linkId}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${UNIFIED_API_KEY}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
const errorDetails = await response.text();
throw new Error(`Error retrieving scheduling link: ${response.statusText} - ${errorDetails}`);
}
const linkData = await response.json();
return linkData.url;
}
Step 6: Main Candidate processing function (Webhook handler)
This function receives candidate information through Unified's webhook and processes candidates using the scoreCandidate
& retrieveSchedulingLink
functions we created above. Basically, each time that
app.post('/webhook', async (req, res) => {
const event = req.body;
const candidates = event.data;
for (const candidate of candidates) {
const candidateName = candidate.name;
const candidateEmail = candidate.emails[0]?.email;
try {
// you can also get the Job description from the ATS as well
const jobDescription = "Looking for someone competent in Mathematics and Computing to work as an IT intern in a bank";
const aiResult = await scoreCandidate(candidate, jobDescription);
console.log(`Candidate ${candidateName} scored ${aiResult.score}: ${aiResult.explanation}`);
if (aiResult.score >= 75) {
const schedulingLink = await retrieveSchedulingLink('your_link_id_here');
console.log(`Scheduling link retrieved: ${schedulingLink}`);
// workflow with your preferred email partner to send the email
// Example: await sendEmail(candidateEmail, candidateName, schedulingLink);
} else {
console.log(`Candidate ${candidateName} did not meet the threshold.`);
}
} catch (error) {
console.error(`Error processing candidate ${candidateName}:`, error.message);
}
}
res.status(200).send('Webhook received successfully');
});
Step 7: Automatically create webhook subscription (No UI needed!)
Your webhook subscription will now be automatically created when your server starts.
let WEBHOOK_ID;
app.listen(PORT, async () => {
console.log(`Webhook server running on port ${PORT}`);
const webhookUrl = 'https://your-public-webhook-url.com/webhook'; // Replace with your actual public URL
try {
const webhookSubscription = await fetch(`https://api.unified.to/unified/webhook`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${UNIFIED_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
hook_url: webhookUrl,
event: 'created',
object_type: 'ats_candidate',
connection_id: CONNECTION_GREENHOUSE
})
});
const webhookData = await webhookSubscription.json();
console.log('Webhook subscription created successfully:', webhookData);
WEBHOOK_ID = webhookData.id;
} catch (error) {
console.error('Failed to create webhook subscription:', error.message);
}
});
// unsubscribe when exiting
process.on('SIGINT', async () => {
await fetch(`https://api.unified.to/unified/webhook/${WEBHOOK_ID}`, {
method: 'DELETE'
});
});
That is it. You are ready to rip this example up and build your own AI-powered candidate assessment and interviewing solution.