MetorialDocs

HR interview coordinator

Learn how to build an AI agent that schedules interviews, sends professional emails, and coordinates with your team using Google Calendar and Gmail

Note

What You'll Build: An intelligent interview coordinator that automatically schedules interviews, sends confirmation emails to candidates, notifies your interview panel, and handles all follow-up communications.

Introduction

Coordinating interviews involves juggling multiple calendars, sending professional emails, and ensuring everyone has the right information at the right time. This tutorial shows you how to build an AI-powered interview coordinator that handles all of this automatically.

The bot will:

  • Check calendar availability (using the authenticated user's calendar)
  • Create calendar events with multiple attendees
  • Send professional confirmation emails to candidates
  • Notify interview panel members with prep materials
  • Request feedback from interviewers after completion

By the end of this tutorial, you'll have a working interview coordinator that can handle the entire scheduling and communication workflow with natural language commands.

Info

What You'll Learn:

  • Deploying Google Calendar and Gmail providers
  • Setting up OAuth for multiple Google services
  • Creating an AI agent that coordinates between multiple tools
  • Sending professional, AI-generated emails
  • Managing complex multi-step workflows with Claude

Prerequisites

Before starting, make sure you have:

  • Metorial Account: Sign up and get your API key
  • Google Workspace Account: With Calendar and Gmail access
  • Anthropic API Key: Get one from Anthropic Console
  • Development Environment: Node.js 18+ or Python 3.9+

Architecture overview

Here's how the interview coordinator works:

  1. User provides interview details (candidate, role, date preferences, interviewers)
  2. AI checks calendar availability using the Google Calendar provider (checks the authenticated user's calendar)
  3. AI creates calendar event and adds interviewers as attendees
  4. AI sends confirmation email to candidate via Gmail
  5. AI sends prep email to interview panel via Gmail

Tools used: Google Calendar provider (scheduling) + Gmail provider (communications) + AI Model (coordination)

The AI autonomously decides which tools to use and in what order, handling complex multi-step workflows without explicit programming.

Step 1: Configure Google Calendar provider

First, configure the Google Calendar provider from the Metorial catalog:

Navigate to provider catalog

Go to platform.metorial.com and click Providers

Find Google Calendar

Search for "Google Calendar" in the catalog

Create an integration

Open the provider, click Use ProviderIntegration, then note the provider deployment or integration ID shown in the dashboard.

Note

Save your Calendar deployment ID - you'll need it in Step 3. It looks like: pdp_abc123def456

Note on Multiple Interviewers: This deployment accesses one Google Calendar (the authenticated user's calendar). To check availability across multiple interviewers' calendars, each interviewer would need their own Calendar provider deployment and auth config. For simplicity, this tutorial uses a single calendar instance.

Step 1b: Configure Gmail provider

Next, configure the Gmail provider for email communications:

Search for Gmail

In the same catalog, search for "Gmail"

Create an integration

Open Gmail, click Use ProviderIntegration, and note your Gmail provider deployment or integration ID.

Note

You now have two providers deployed. Both will run in the same session, allowing the AI to coordinate between scheduling and email tools seamlessly.

Step 2: Set up OAuth authentication

Both Calendar and Gmail require OAuth authentication to access your Google Workspace.

Install dependencies

npm install metorial @metorial/anthropic @anthropic-ai/sdk

Create OAuth setup script

  import { Metorial } from "metorial";

  const metorial = new Metorial({
    apiKey: "YOUR_METORIAL_API_KEY",
  });

  async function setupOAuth() {
    // Calendar OAuth
    const calendarSetup = await metorial.providers.setupSessions.create({
      providerId: "YOUR_CALENDAR_PROVIDER_ID",
      providerAuthMethodId: "oauth",
    });

    console.log("Authorize Calendar here:", calendarSetup.url);

    // Wait for authorization
    const calendarCompleted = await metorial.waitForSetupSession([calendarSetup]);
    console.log("✓ Calendar authorized!");

    // Gmail OAuth
    const gmailSetup = await metorial.providers.setupSessions.create({
      providerId: "YOUR_GMAIL_PROVIDER_ID",
      providerAuthMethodId: "oauth",
    });

    console.log("\nAuthorize Gmail here:", gmailSetup.url);

    // Wait for authorization
    const gmailCompleted = await metorial.waitForSetupSession([gmailSetup]);
    console.log("✓ Gmail authorized!");

    console.log("\nSave these auth config IDs for your bot:");
    console.log("Calendar:", calendarCompleted.authConfig.id);
    console.log("Gmail:", gmailCompleted.authConfig.id);
  }

  setupOAuth();
from metorial import Metorial

metorial = Metorial(api_key="YOUR_METORIAL_API_KEY")

# Calendar OAuth
calendar_setup = await metorial.providers.setup_sessions.create(
    provider_id="YOUR_CALENDAR_PROVIDER_ID",
    provider_auth_method_id="oauth",
)

print("Authorize Calendar here:", calendar_setup.url)

# Wait for authorization
calendar_completed = await metorial.wait_for_setup_session(calendar_setup)
print("✓ Calendar authorized!")

# Gmail OAuth
gmail_setup = await metorial.providers.setup_sessions.create(
    provider_id="YOUR_GMAIL_PROVIDER_ID",
    provider_auth_method_id="oauth",
)

print("\nAuthorize Gmail here:", gmail_setup.url)

# Wait for authorization
gmail_completed = await metorial.wait_for_setup_session(gmail_setup)
print("✓ Gmail authorized!")

print("\nSave these auth config IDs for your bot:")
print("Calendar:", calendar_completed.auth_config.id)
print("Gmail:", gmail_completed.auth_config.id)

Authorize in browser

Visit both URLs in your browser and complete the Google OAuth flow for Calendar and Gmail access. The script will wait for you to authorize before continuing.

Save auth config IDs

The script outputs your auth config IDs (starting with pac_). Save these for use in Step 3.

Note

OAuth Scopes: Calendar needs calendar.events and calendar.readonly. Gmail needs gmail.send and gmail.readonly. These are configured automatically by Metorial.

Step 3: Build the interview coordinator

Now let's build the main coordinator that orchestrates interview scheduling and communications.

import { Metorial } from "metorial";
import { metorialAnthropic } from "@metorial/anthropic";
import Anthropic from "@anthropic-ai/sdk";

// Initialize clients
const metorial = new Metorial({
  apiKey: "YOUR_METORIAL_API_KEY",
});

const anthropic = new Anthropic({
  apiKey: "YOUR_ANTHROPIC_API_KEY",
});

// Server deployment credentials
const CALENDAR_DEPLOYMENT_ID = "CALENDAR_DEPLOYMENT_ID";
const CALENDAR_AUTH_CONFIG_ID = "CALENDAR_AUTH_CONFIG_ID";
const GMAIL_DEPLOYMENT_ID = "GMAIL_DEPLOYMENT_ID";
const GMAIL_AUTH_CONFIG_ID = "GMAIL_AUTH_CONFIG_ID";

interface InterviewRequest {
  candidateName: string;
  candidateEmail: string;
  role: string;
  interviewers: string[]; // Email addresses
  durationMinutes: number;
  preferredDates: string[]; // ISO format dates
  interviewType: "technical" | "behavioral" | "panel";
}

async function scheduleInterview(request: InterviewRequest) {
  console.log(`Scheduling ${request.interviewType} interview for ${request.candidateName}...`);

  await metorial.withProviderSession(
    metorialAnthropic,
    {
      providers: [
        {
          providerDeploymentId: CALENDAR_DEPLOYMENT_ID,
          providerAuthConfigId: CALENDAR_AUTH_CONFIG_ID,
        },
        {
          providerDeploymentId: GMAIL_DEPLOYMENT_ID,
          providerAuthConfigId: GMAIL_AUTH_CONFIG_ID,
        },
      ],
    },
    async session => {
      const messages: Anthropic.MessageParam[] = [
        {
          role: "user",
          content: `Schedule interview and send confirmations:

Candidate: ${request.candidateName} (${request.candidateEmail})
Role: ${request.role}
Interviewers: ${request.interviewers.join(", ")}
Duration: ${request.durationMinutes} minutes
Preferred dates: ${request.preferredDates.join(", ")}
Type: ${request.interviewType}

Steps to complete:
1. Check calendar availability for all interviewers on preferred dates
2. Create calendar event with:
   - Title: "${request.role} Interview - ${request.candidateName}"
   - Add all interviewers as attendees
3. Send confirmation email to candidate (${request.candidateEmail}) with:
   - Subject: "Interview Scheduled - ${request.role}"
   - Professional email including: interview date/time, what to prepare, who they'll meet
4. Send prep email to interviewers (${request.interviewers.join(", ")}) with:
   - Subject: "Interview Prep - ${request.candidateName}"
   - Candidate background, interview format, evaluation guidelines

CRITICAL: Only use actual calendar data. If no slots available, inform user. DO NOT create events without checking availability first.`,
        },
      ];

      let response = await anthropic.messages.create({
        model: "claude-sonnet-4-5",
        max_tokens: 8192,
        tools: session.tools,
        messages: messages,
      });

      // Agentic loop - let AI use tools autonomously
      while (response.stop_reason === "tool_use") {
        const toolUseBlocks = response.content.filter(
          (block): block is Anthropic.ToolUseBlock => block.type === "tool_use"
        );

        console.log(`AI using ${toolUseBlocks.length} tool(s)...`);

        const toolResults = await session.callTools(toolUseBlocks);

        messages.push({ role: "assistant", content: response.content });
        messages.push(toolResults);

        response = await anthropic.messages.create({
          model: "claude-sonnet-4-5",
          max_tokens: 8192,
          tools: session.tools,
          messages: messages,
        });
      }

      // Extract final response
      const finalText = response.content
        .filter((block): block is Anthropic.TextBlock => block.type === "text")
        .map((block) => block.text)
        .join("\n");

      console.log("Interview coordination complete:", finalText);
    }
  );
}

// Example usage
scheduleInterview({
  candidateName: "John Novak",
  candidateEmail: "[email protected]",
  role: "Senior Software Engineer",
  interviewers: [
    "[email protected]",  // Hiring manager
  ],
  durationMinutes: 45,
  preferredDates: [
    "2026-02-7T14:00:00Z",  // First preference
    "2026-02-7T5:00:00Z",  // Second preference
    "2026-02-7T15:00:00Z",  // Third preference
  ],
  interviewType: "technical",
});

Note

Key Implementation Details:

  • Single Session: Both Calendar and Gmail servers run in one session, allowing the AI to seamlessly switch between scheduling and emailing tools
  • Agentic Workflow: The AI autonomously decides which tools to use and in what order - no explicit programming needed
  • Anti-Hallucination: The prompt explicitly instructs using only actual calendar data to prevent AI from making up availability
  • Professional Communications: AI generates context-appropriate emails for candidates and interviewers

Important

Multiple Interviewer Calendars: The current implementation uses a single Google Calendar provider instance, which only accesses one Google Calendar (the calendar of the authenticated user). To check availability across multiple interviewers' calendars, you would need to:

  • Deploy a separate Calendar provider instance for each interviewer
  • Set up OAuth for each interviewer's calendar
  • Pass multiple calendar provider deployments to the session

For now, this tutorial demonstrates scheduling with a single calendar owner who can view their own availability. The interviewers array is used for adding attendees to the calendar event and sending prep emails, but not for checking their individual calendar availability.

Email templates

The AI automatically generates professional emails tailored to each recipient. Here are examples of what the coordinator sends:

Step 4: Test the coordinator

Let's test the coordinator with a realistic scenario:

// Test scenario: Schedule technical interview
await scheduleInterview({
  candidateName: "Alex Rivera",
  candidateEmail: "[email protected]",
  role: "Staff Frontend Engineer",
  interviewers: [
    "[email protected]",
    "[email protected]",
    "[email protected]",
  ],
  durationMinutes: 90,  // Panel interview
  preferredDates: [
    "2026-02-15T13:00:00Z",
    "2026-02-16T14:00:00Z",
    "2026-02-17T10:00:00Z",
  ],
  interviewType: "panel",
});

Expected Output:

Scheduling panel interview for Alex Rivera...
AI using 3 tool(s)...
AI using 2 tool(s)...
AI using 2 tool(s)...
Interview coordination complete: Successfully scheduled panel interview for Alex Rivera on February 15, 2026 at 1:00 PM EST. Calendar event created. Confirmation email sent to candidate and prep email sent to all 3 interviewers (Engineering Director, Senior Engineer, Product Manager).

The coordinator will:

  1. Check availability on the authenticated user's calendar (not all 3 interviewers - see note below)
  2. Find the first available slot (Feb 15 at 1:00 PM)
  3. Create a 90-minute calendar event with all 3 interviewers as attendees
  4. Send confirmation email to Alex with interview details
  5. Send prep email to all interviewers with candidate info

Note

Calendar Availability Limitation: This example checks only the authenticated user's calendar availability. To check all 3 interviewers' calendars, you would need to deploy a separate Calendar MCP instance for each interviewer with their OAuth credentials. The interviewers array is used for adding attendees to the event and sending emails, not for checking their individual availability.

Troubleshooting

Advanced customization

Email personalization

Customize email templates based on role, interview type, or company culture:

const emailTemplates = {
  technical: "Include coding prep tips",
  behavioral: "Focus on company values",
  panel: "List all interviewers with bios"
};

Pass templates to your prompt for context-aware emails.

Multi-stage interviews

Coordinate complex interview pipelines:

async function scheduleInterviewPipeline(
  candidate: Candidate,
  stages: InterviewStage[]
) {
  for (const stage of stages) {
    await scheduleInterview({
      ...candidate,
      ...stage
    });
  }
}

Schedule phone screen, technical, and panel interviews in sequence.

Feedback collection

Request and aggregate interviewer feedback:

async function collectFeedback(
  interviewId: string,
  interviewers: string[]
) {
  // Send feedback forms after interview
  // AI analyzes responses for hiring decision
}

Automate post-interview feedback workflow.

Calendar preferences

Respect interviewer working hours and preferences:

const preferences = {
  "[email protected]": {
    hours: "9-17",
    timezone: "America/New_York"
  }
};

Include preferences in AI prompt for smarter scheduling.

Multi-language support

Send emails in candidate's preferred language:

const request = {
  ...interviewDetails,
  candidateLanguage: "es" // Spanish
};

AI automatically generates emails in requested language.

Multi-calendar support

Check availability across multiple interviewers' calendars:

// Deploy separate Calendar MCP for each interviewer
const INTERVIEWER_CALENDARS = [
  { providerDeploymentId: "pdp_eng_dir", providerAuthConfigId: "pac_eng_dir" },
  { providerDeploymentId: "pdp_senior_eng", providerAuthConfigId: "pac_senior" }
];

Each interviewer needs their own Calendar MCP instance with OAuth.

Production considerations

Before deploying to production:

Error handling

Add comprehensive error handling for common failures:

try {
  await scheduleInterview(request);
} catch (error) {
  if (error.message.includes("OAuth")) {
    // Refresh OAuth tokens
  } else if (error.message.includes("quota")) {
    // Handle rate limits
  } else {
    // Log and alert on-call team
  }
}

Monitoring and logging

Track key metrics for reliability:

  • Interview scheduling success rate
  • Email delivery rate
  • Calendar availability check duration
  • AI tool call patterns

Use Metorial's built-in monitoring to track these metrics.

Testing strategy

Test edge cases before production:

  • All interviewers unavailable on all dates
  • Invalid email addresses
  • Time zone edge cases (DST transitions)
  • Calendar quota limits
  • Gmail sending limits

Privacy and compliance

Ensure GDPR/CCPA compliance:

  • Get consent before sending emails
  • Don't store candidate data longer than necessary
  • Encrypt sensitive information in transit and at rest

Scalability

For high-volume hiring:

  • Implement request queuing to avoid rate limits
  • Cache calendar availability queries
  • Batch similar operations
  • Use Metorial's webhook support for async processing