How to Automate Interview Scheduling with the Hiring Agent: A Config-Driven Guide

To automate interview scheduling with the hiring agent, configure the interview_scheduler provider in providers.json, set your timing constraints in config.py, and implement the calendar provider using the INTERVIEW_SCHEDULE_CONFIG parameters to generate slots and create events.

The interviewstreet/hiring-agent repository is a lightweight AI-driven framework that automates end-to-end hiring workflows for software engineers. Beyond parsing resumes and evaluating candidates, the system supports fully automated interview scheduling through a provider-based architecture that externalizes calendar integration logic into configuration files and modular Python components.

Configure the Interview Scheduler Provider

The Hiring Agent uses a provider-mapping pattern to delegate scheduling operations to external calendar services. According to providers.json (lines 1–5), the interview_scheduler key determines which backend handles calendar operations:

{
  "resume_parser": "openai",
  "interview_scheduler": "google_calendar",
  "candidate_evaluator": "ml_model"
}

Setting "interview_scheduler": "google_calendar" instructs the system to route all scheduling requests through the Google Calendar provider implementation. This decouples the scheduling logic from the core workflow, allowing you to swap providers (e.g., to Outlook or Calendly) by changing this single value and implementing the corresponding adapter.

Define Scheduling Parameters in config.py

All interview timing rules are centralized in config.py (lines 9–16) within the INTERVIEW_SCHEDULE_CONFIG dictionary. This configuration drives the slot-selection algorithm:

INTERVIEW_SCHEDULE_CONFIG = {
    "default_duration_minutes": 45,
    "buffer_minutes": 10,
    "working_hours": {
        "start": 9,
        "end": 17
    }
}

Key parameters include:

  • default_duration_minutes: The standard length of interview slots (45 minutes).
  • buffer_minutes: Minimum gap between consecutive interviews (10 minutes).
  • working_hours: The time window (9 AM to 5 PM) during which interviews can be scheduled.

The scheduling engine imports this configuration at runtime to validate candidate availability against interviewer calendars, ensuring generated slots respect business hours and required buffers.

Implement the Calendar Integration

While the repository defines the interface via providers.json, you must implement the provider class that interacts with the Google Calendar API. The implementation should consume INTERVIEW_SCHEDULE_CONFIG to enforce scheduling constraints. Below is a complete reference implementation that connects the configuration to the Google Calendar API:

import json
from pathlib import Path
from datetime import datetime, timedelta, time
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from config import INTERVIEW_SCHEDULE_CONFIG

# Load provider mapping

with open(Path(__file__).parent / "providers.json") as f:
    PROVIDERS = json.load(f)

class GoogleCalendarScheduler:
    SCOPES = ['https://www.googleapis.com/auth/calendar']
    
    def __init__(self):
        self.service = self._authenticate()
        self.config = INTERVIEW_SCHEDULE_CONFIG
        
    def _authenticate(self):
        """Initialize Google Calendar API service using OAuth2."""
        creds = None
        if Path('token.json').exists():
            creds = Credentials.from_authorized_user_file('token.json', self.SCOPES)
        if not creds or not creds.valid:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', self.SCOPES)
            creds = flow.run_local_server(port=0)
            Path('token.json').write_text(creds.to_json())
        return build('calendar', 'v3', credentials=creds)
    
    def find_available_slot(self, interviewer_busy, candidate_busy, days_ahead=5):
        """Find next available slot using config parameters."""
        duration = timedelta(minutes=self.config["default_duration_minutes"])
        buffer_time = timedelta(minutes=self.config["buffer_minutes"])
        work_start = time(self.config["working_hours"]["start"])
        work_end = time(self.config["working_hours"]["end"])
        
        now = datetime.utcnow()
        
        for day_offset in range(days_ahead):
            date = now.date() + timedelta(days=day_offset)
            start_dt = datetime.combine(date, work_start)
            end_dt = datetime.combine(date, work_end)
            
            current = start_dt
            while current + duration <= end_dt:
                slot_end = current + duration
                # Check against busy periods

                is_available = True
                for busy in interviewer_busy + candidate_busy:
                    if (current < busy['end'] and slot_end > busy['start']):
                        is_available = False
                        break
                
                if is_available:
                    return current, slot_end
                
                current += timedelta(minutes=30)  # 30-min increments

        
        raise ValueError("No available slots found within constraints")
    
    def create_interview_event(self, start_time, end_time, candidate_email, interviewer_id='primary'):
        """Create calendar event with conference data."""
        event = {
            'summary': 'Technical Interview - Hiring Agent',
            'description': 'Automated interview scheduled by hiring-agent',
            'start': {
                'dateTime': start_time.isoformat() + 'Z',
                'timeZone': 'UTC',
            },
            'end': {
                'dateTime': end_time.isoformat() + 'Z',
                'timeZone': 'UTC',
            },
            'attendees': [
                {'email': candidate_email},
            ],
            'conferenceData': {
                'createRequest': {
                    'requestId': f"interview-{start_time.timestamp()}",
                    'conferenceSolutionKey': {'type': 'hangoutsMeet'}
                }
            },
            'reminders': {
                'useDefault': False,
                'overrides': [
                    {'method': 'email', 'minutes': 24 * 60},
                    {'method': 'popup', 'minutes': 10},
                ],
            },
        }
        
        event = self.service.events().insert(
            calendarId=interviewer_id,
            body=event,
            conferenceDataVersion=1,
            sendUpdates='all'
        ).execute()
        
        return event.get('htmlLink')

# Usage example

if PROVIDERS.get("interview_scheduler") == "google_calendar":
    scheduler = GoogleCalendarScheduler()
    # Query free/busy for interviewer and candidate...

    # start, end = scheduler.find_available_slot(interviewer_busy, candidate_busy)

    # event_link = scheduler.create_interview_event(start, end, "candidate@example.com")

This implementation enforces the 45-minute duration and 10-minute buffer specified in config.py, iterating only within the configured 9-to-5 working hours.

Integrate with Resume Parsing

The scheduling workflow begins with candidate data extraction. The llm_utils.py module provides the get_completion() function (lines 1–9) that queries OpenAI models to parse resumes and extract availability windows:

import openai

def get_completion(prompt, model="gpt-3.5-turbo"):
    response = openai.ChatCompletion.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        temperature=0.7,
    )
    return response.choices[0].message.content.strip()

Typical scheduling workflow:

  1. Parse the candidate's resume using get_completion() to extract availability constraints and timezone.
  2. Query the interviewer's Google Calendar for busy periods using the GoogleCalendarScheduler authentication flow.
  3. Call find_available_slot() to identify the next valid window based on INTERVIEW_SCHEDULE_CONFIG.
  4. Execute create_interview_event() to generate the calendar invite and video conference link.

Generate Interview Communications

Use the Jinja2 template system defined in prompt.py to generate consistent interview invitations. The prompts/templates/ directory contains templates for system messages and evaluation criteria. Create a scheduling-specific template (e.g., interview_invitation.jinja) and load it via the Environment instance:

from prompt import load_template

template = load_template('interview_invitation.jinja')
invitation_text = template.render(
    candidate_name="Jane Doe",
    interview_date="2024-01-15",
    interview_time="14:00 UTC",
    duration=INTERVIEW_SCHEDULE_CONFIG["default_duration_minutes"]
)

This ensures all automated communications reflect the correct duration and timing parameters defined in your centralized configuration.

Summary

  • Provider configuration: Map interview_scheduler to google_calendar in providers.json (lines 1–5) to activate automated scheduling.
  • Timing constraints: Centralize interview duration, buffer time, and working hours in INTERVIEW_SCHEDULE_CONFIG within config.py (lines 9–16).
  • Calendar implementation: Implement the provider class to consume these configurations when querying free/busy data and creating events via the Google Calendar API.
  • Pipeline integration: Connect llm_utils.py for resume parsing, use prompt.py templates for communications, and validate all slots against the configured business rules before event creation.

Frequently Asked Questions

How do I change the default interview duration?

Modify the default_duration_minutes value in config.py (line 11). The scheduling algorithm automatically uses this value when calculating slot boundaries and buffer periods. If you set it to 60, the system will reserve 60-minute blocks plus the configured buffer_minutes between events.

Can I use a different calendar provider instead of Google Calendar?

Yes. Update the interview_scheduler value in providers.json (line 3) to your preferred provider key (e.g., "microsoft_graph" or "calendly"). You must then implement a corresponding provider class that follows the same interface—accepting INTERVIEW_SCHEDULE_CONFIG parameters and exposing find_available_slot() and create_interview_event() methods.

Where does the Hiring Agent extract candidate availability from?

The system uses the get_completion() function in llm_utils.py (lines 3–9) to parse resume text via OpenAI's API. You should craft prompts that instruct the model to extract explicit availability windows (e.g., "Tuesday and Thursday afternoons") or timezone information, which the scheduler then uses to filter potential slots retrieved from the interviewer's calendar.

What happens if no valid slots are found within the working hours?

The find_available_slot() implementation raises a ValueError when no availability exists within the days_ahead search window and configured working_hours. In production, you should wrap this call in error handling that notifies the recruiter to manually coordinate a time or expand the configured working_hours range in config.py.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →