Building an AI Recruiting Coordinator That Schedules Across 6 Time Zones
A recruiting platform serves companies hiring globally. A candidate in San Francisco interviews with a hiring manager in London and a team lead in Singapore. The recruiting coordinator — now an AI agent — needs to find a time that works across all three calendars, account for DST transitions, handle stale availability, and book without double-booking.
This is the hardest scheduling problem most companies face. It’s also a clean illustration of why scheduling needs infrastructure, not glue code.
The architecture
The system has four layers:
┌─────────────┐ ┌─────────────────────┐ ┌──────────────────┐ ┌───────────────────┐
│ ATS │────▶│ Scheduling Agent │────▶│ Temporal Cortex │────▶│ Calendar Providers │
│ (Lever, │ │ (LangGraph / │ │ (MCP Server) │ │ (Google, Outlook, │
│ Greenhouse)│ │ OpenAI Agents) │ │ │ │ CalDAV) │
└─────────────┘ └─────────────────────┘ └──────────────────┘ └───────────────────┘
The ATS (applicant tracking system) triggers the workflow: “Schedule a 60-minute technical interview for candidate Sarah Chen with interviewers James Wright (London) and Wei Lin (Singapore).” The Scheduling Agent orchestrates the tool calls. Temporal Cortex handles the calendar math, provider abstraction, and booking safety. The calendar providers are the source of truth for each person’s schedule.
The agent doesn’t need to know which calendar provider each interviewer uses. It doesn’t need to handle DST math. It doesn’t need to implement locking. It calls five tools in sequence, and the infrastructure handles the rest.
Step 1: Resolve the participants
The agent starts by resolving each participant from the ATS data to their calendar identity.
// Tool: search_contacts
{ "query": "[email protected]" }
// Response
{
"contacts": [
{
"email": "[email protected]",
"name": "James Wright",
"timezone": "Europe/London",
"calendar_ids": ["outlook/cal-jw-001"]
}
]
}
// Tool: search_contacts
{ "query": "[email protected]" }
// Response
{
"contacts": [
{
"email": "[email protected]",
"name": "Wei Lin",
"timezone": "Asia/Singapore",
"calendar_ids": ["google/primary"]
}
]
}
The agent now knows three things about each interviewer: their email, their timezone, and which calendar to check. James uses Outlook. Wei uses Google Calendar. The agent doesn’t care — both are accessible through the same tool interface.
For the candidate, the ATS provides the email and the agent resolves their timezone:
// Tool: resolve_contact
{ "identifier": "[email protected]" }
// Response
{
"email": "[email protected]",
"name": "Sarah Chen",
"timezone": "America/Los_Angeles"
}
Three participants. Three timezones. Two calendar providers. The agent has everything it needs.
Step 2: Establish temporal context
Before doing any datetime work, the agent grounds itself in the current time.
// Tool: get_temporal_context
{ }
// Response
{
"utc": "2026-03-16T15:00:00+00:00",
"local": "2026-03-16T11:00:00-04:00",
"timezone": "America/New_York",
"day_of_week": "Monday",
"dst_active": true,
"next_dst_transition": "2026-11-01T06:00:00+00:00",
"next_dst_direction": "fall-back"
}
It’s Monday, March 16, 2026. US DST is active (spring-forward happened March 8). The next transition is fall-back in November — far enough away that this week’s scheduling won’t cross a boundary. But there’s a subtlety: the UK springs forward on March 29, two weeks later than the US. If the interview window extends into late March, the UTC offset between New York and London changes from 5 hours to 4 hours on March 29. The agent needs to be aware of this.
Step 3: Find overlapping availability
This is the hard part. The agent needs a 60-minute window that’s free on all three calendars, during reasonable working hours in all three timezones.
First, define the constraints. “Reasonable working hours” for a global interview typically means:
- San Francisco (UTC-7): 8am-6pm → 15:00-01:00 UTC
- London (UTC+0, or UTC+1 after March 29): 9am-6pm → 09:00-18:00 UTC
- Singapore (UTC+8): 9am-6pm → 01:00-10:00 UTC
The overlap of all three working-hour windows is 09:00-10:00 UTC — one hour. That’s 1:00-2:00 AM in San Francisco, which is unusable. For a real scheduling scenario, the company needs to decide which timezone compromises. Typically, the candidate gets priority, so the constraint relaxes: San Francisco keeps standard hours, London and Singapore flex.
A practical overlap: 16:00-18:00 UTC. That’s 9:00-11:00 AM in San Francisco, 4:00-6:00 PM in London, and midnight-2:00 AM in Singapore. Singapore is still bad. A more realistic constraint for US-Europe-Asia interviews is splitting into two sessions or asking the Singapore interviewer to flex to early morning.
Let’s say the company policy is that Singapore interviewers are available 8:00 AM-8:00 PM SGT, and the agent is looking for slots this week. The agent queries availability across all three calendars:
// Tool: find_free_slots
{
"calendar_ids": ["outlook/cal-jw-001", "google/primary"],
"start": "2026-03-16T16:00:00+00:00",
"end": "2026-03-20T18:00:00+00:00",
"min_duration_minutes": 60,
"timezone": "America/Los_Angeles"
}
// Response
{
"slots": [
{
"start": "2026-03-17T17:00:00+00:00",
"end": "2026-03-17T18:30:00+00:00",
"duration_minutes": 90
},
{
"start": "2026-03-18T16:00:00+00:00",
"end": "2026-03-18T18:00:00+00:00",
"duration_minutes": 120
},
{
"start": "2026-03-19T16:30:00+00:00",
"end": "2026-03-19T18:00:00+00:00",
"duration_minutes": 90
}
],
"count": 3
}
The infrastructure merged availability across James’s Outlook calendar and Wei’s Google Calendar, expanding recurring events through the Truth Engine, and returned windows where both are free. The agent now has three candidate slots.
Translating the first slot for each participant:
| Participant | Time (local) | Date |
|---|---|---|
| Sarah Chen (SF) | 9:00 AM PDT | Tue Mar 17 |
| James Wright (London) | 5:00 PM GMT | Tue Mar 17 |
| Wei Lin (Singapore) | 1:00 AM SGT | Wed Mar 18 |
Singapore at 1:00 AM is outside the flex window. The agent needs to try the second slot:
| Participant | Time (local) | Date |
|---|---|---|
| Sarah Chen (SF) | 9:00 AM PDT | Wed Mar 18 |
| James Wright (London) | 4:00 PM GMT | Wed Mar 18 |
| Wei Lin (Singapore) | 12:00 AM SGT | Thu Mar 19 |
Still midnight in Singapore. The agent recognizes the constraint is too tight for a synchronous 3-way call and suggests the ATS split the interview into two sessions: a US-Europe session and an Asia session with a separate time.
This decision is the agent’s job — it understands the business logic of “reasonable hours.” The infrastructure’s job was computing the accurate availability and timezone conversions that made the decision possible.
Step 4: Book the slot
Assume the ATS confirms: split into two sessions. The US-Europe session books on Wednesday at 4:00 PM GMT / 9:00 AM PDT.
// Tool: book_slot
{
"calendar_id": "outlook/cal-jw-001",
"start": "2026-03-18T16:00:00+00:00",
"end": "2026-03-18T17:00:00+00:00",
"summary": "Technical Interview — Sarah Chen",
"attendees": ["[email protected]", "[email protected]"],
"description": "60-minute technical interview for Senior Engineer position."
}
// Response
{
"success": true,
"event_id": "evt-2026-0318-jw",
"booking_id": "bk-a1b2c3",
"summary": "Technical Interview — Sarah Chen",
"start": "2026-03-18T16:00:00+00:00",
"end": "2026-03-18T17:00:00+00:00"
}
Behind the scenes, book_slot ran Two-Phase Commit: it acquired a lock on the 4:00-5:00 PM UTC window, verified no other agent had booked it since the availability check, created the event in Outlook, recorded it in the shadow calendar, and released the lock. If another recruiting agent had been booking the same interviewer at the same time, one would succeed and the other would get a conflict error.
Step 5: Handle the candidate without calendar access
The agent has James’s calendar credentials (connected via OAuth). But Sarah is an external candidate — the agent can’t read or write to her calendar. This is where compose_proposal bridges the gap.
// Tool: compose_proposal
{
"recipient": "[email protected]",
"slots": [
{
"start": "2026-03-18T16:00:00+00:00",
"end": "2026-03-18T17:00:00+00:00"
}
],
"subject": "Technical Interview — Acme Corp, Senior Engineer",
"message": "Hi Sarah, your technical interview with James Wright is confirmed for Wednesday, March 18 at 9:00 AM PDT (12:00 PM EDT). A calendar invite has been sent. Please let us know if you need to reschedule.",
"timezone": "America/Los_Angeles"
}
The proposal generates a booking link and email with the time rendered in Sarah’s timezone. If she has a scheduling agent, the proposal can route through A2A for automatic calendar confirmation. If she doesn’t, she gets a clean email with an “Add to Calendar” button.
Edge cases the infrastructure handles
DST transition mid-week
If this interview had been scheduled for the week of March 29, the UK would spring forward on Sunday. A meeting at “4:00 PM London time” on March 27 (Friday) is UTC+0, but the same meeting on March 30 (Monday) is UTC+1. The 5-hour gap between New York and London would shrink to 4 hours. Without DST-aware computation, the agent might book the Monday slot using Friday’s UTC offset, putting the London interviewer at the wrong time.
find_free_slots handles this automatically. Each day’s availability is computed with the correct UTC offset for that date.
Stale availability
Between the find_free_slots call and the book_slot call, James might accept another meeting. The availability response is already stale. The Two-Phase Commit’s verify step catches this: if the slot is no longer free when the lock is acquired, the booking aborts with a conflict error. The agent can then re-query availability and try the next slot.
Multi-panel interviews
A full-day onsite might involve four interviewers across six one-hour sessions. The agent runs find_free_slots once with all four interviewer calendars, then books six sessions sequentially. Each book_slot call sees the previous bookings via the shadow calendar, preventing the same interviewer from being double-booked across sessions — even if the calendar API hasn’t propagated the earlier events yet.
Recurring conflicts
An interviewer has a weekly standup that appears as an RRULE, not as individual events. find_free_slots expands all RRULEs through the Truth Engine before computing availability. The standup correctly blocks its time slot on every applicable day in the search range, even if the calendar API only returns the RRULE string.
The broader pattern
Recruiting is one vertical. The same architecture applies to sales (scheduling demos across time zones), healthcare (coordinating multi-specialist consultations), customer success (booking quarterly reviews with global accounts), and education (scheduling parent-teacher conferences).
In each case, the agent handles the domain logic — understanding what kind of meeting to schedule, who should attend, and what constraints apply. The scheduling infrastructure handles the calendar math — timezone conversion, availability merging, RRULE expansion, and conflict-free booking.
npx @temporal-cortex/cortex-mcp
Five tool calls took the agent from “schedule an interview” to a booked, DST-aware, conflict-free calendar event. The agent didn’t implement timezone math, availability merging, or Two-Phase Commit. It called the infrastructure, and the infrastructure got it right.