# How we coordinate interview loops

The interview-scheduling agent we run on Kortix — daily, it finds candidates ready to schedule in Greenhouse, checks interviewer availability on Google Calendar, and drafts a slot proposal to the candidate and the calendar invites for a coordinator to confirm.

Canonical page: https://kortix.com/use-cases/interview-scheduler

Once a candidate clears a screen, scheduling the next round becomes its own small
project: read the interview plan to see who's on the panel, find a time when every
one of those interviewers is actually free, propose it to the candidate, and get
invites onto everyone's calendar before the slot goes stale. None of it is hard,
but it's tedious across several interviewers and several candidates at once, and
every day it sits undone is a day the loop stalls.

We run an interview-scheduling agent on Kortix that does the coordination every
day. For every candidate marked ready to schedule in Greenhouse, it reads the
interview plan, checks the panel's availability on Google Calendar, and drafts a
slot proposal to the candidate plus the calendar invites for each interviewer. It
never sends anything and never touches a hiring decision — a coordinator reviews
every proposal and invite before it goes out. This is how we keep our own interview
loops moving.

- **Team:** Kortix
- **Runs on:** Daily cron
- **Connected systems:** Greenhouse · Google Calendar · Email
- **Mode:** Proposes and drafts only · coordinator confirms and sends

## The problem

Scheduling an interview loop means combining two things that live in different
systems: the interview plan — who's on the panel, how many rounds, how long each
one runs — sits in Greenhouse, while whether those people are actually free sits in
their calendars. Checking both by hand, for every candidate who clears a screen,
is the kind of coordination work that's easy to fall behind on the moment more than
one requisition is open at once.

The usual workarounds don't hold up. A shared scheduling link only works if every
interviewer keeps it current, and it says nothing about who the panel actually is
for this candidate. A coordinator juggling several calendar tabs and a Greenhouse
tab in parallel can do it, but it doesn't scale past a handful of loops, and a
slipped invite reads as disorganized to a candidate who's interviewing elsewhere
too.

## What we built

On Kortix, a daily cron triggers an agent. It spawns a fresh session (a cloud
sandbox) that reads every candidate marked ready to schedule in Greenhouse, pulls
that candidate's interview plan to see who's on the panel, checks each panelist's
Google Calendar for open slots, and drafts a slot proposal for the candidate along
with a calendar invite per interviewer. Every draft lands with the coordinator; the
agent sends nothing and decides nothing about the candidate.

## How it works

### Run on a daily cron

A **cron trigger** fires the agent once a day. Each firing spawns a fresh
**session** in its own sandbox, so the set of candidates ready to schedule and the
panel's real availability are both recomputed from the current state — nothing
carries over from the day before.

### Give the agent the scheduling conventions

How we read an interview plan, how many slot options to propose, how far out to
look for availability, and what a good candidate-facing proposal reads like all
live as **skills** and **memory** that travel with the agent. When we change how
loops are structured, we update the file and the next day's proposals follow it.

### Connect Greenhouse, Google Calendar, and email

Through scoped **connectors**, brokered server-side so no raw token reaches the
model, the agent can:

- **Read Greenhouse** — which candidates are marked ready to schedule, and each
one's interview plan: the panel, the round, and the duration.
- **Read Google Calendar** — the availability of every interviewer on the plan, so
a proposed time is one every panelist can actually make.
- **Draft to email** — a slot proposal to the candidate and a calendar invite per
interviewer, held for the coordinator.

### Set the guardrails

The agent's job stops at the draft. It never emails a candidate directly, never
sends a calendar invite, and never makes or implies a hiring decision — no
rejecting a candidate, no advancing a stage, no offer. Credentials are encrypted
in the Secrets Manager and injected at runtime, scoped to the agents you grant them to or
written to logs.

### Hand the batch to the coordinator

With that in place, each morning brings a batch of ready-to-schedule candidates
already matched against real interviewer availability: a proposed slot for the
candidate and a drafted invite for each panelist. The coordinator reviews, confirms
the time, and sends — or adjusts first. Nothing reaches a candidate or an
interviewer's calendar without them.

> **The pattern**
> A daily **cron** spawns a fresh session with **connectors** into Greenhouse for
> the interview plan and Google Calendar for panel availability. The scheduling
> conventions live as **skills** and **memory**. The agent drafts the candidate
> proposal and the invites and stops — the coordinator confirms and sends.

## Guardrails

The agent touches a candidate's next step in the hiring process, so the boundary
between "proposed" and "confirmed" is the control that matters:

- **Isolation.** Every run happens in its own isolated sandbox. Only the drafted
  proposal and invites are written back out.
- **Scoped secrets.** The Greenhouse and Google Calendar credentials are encrypted
  in the Secrets Manager and injected into the sandbox at runtime, scoped to the agents you grant them to.
- **Human approval gate.** The agent never sends the candidate proposal and never
  sends a calendar invite. Every draft is held for the coordinator to review,
  adjust, and send.
- **No hiring decisions, ever.** The agent cannot reject a candidate, advance a
  stage, or send an offer. It schedules the next conversation; it never decides
  whether that conversation happens.
- **Everything is code.** The scheduling conventions and per-system permissions are
  files in the repo, versioned and changed through a reviewed **change request**
  rather than a dashboard setting.

## The outcome

- **Every day:** Ready-to-schedule candidates matched to real panel availability
- **Zero:** Invites or candidate emails sent without coordinator review
- **3 systems:** Greenhouse, Google Calendar, and email in one agent

Scheduling an interview loop now starts with a proposal and a set of invites
already drafted against the panel's real calendars, instead of a coordinator
opening five tabs to find one time that works. The coordinator's day starts with a
review instead of a hunt, and every send and every hiring call still stays with a
person.
