# HR interview coordinator
URL: https://metorial.com/docs/build/samples/interview-coordinator-bot

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

---

<Callout type="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.
</Callout>

## Introduction [#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.

<Callout type="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
</Callout>

## Prerequisites [#prerequisites]

Before starting, make sure you have:

* **Metorial Account**: [Sign up](https://platform.metorial.com) and get your API key
* **Google Workspace Account**: With Calendar and Gmail access
* **Anthropic API Key**: Get one from [Anthropic Console](https://console.anthropic.com)
* **Development Environment**: Node.js 18+ or Python 3.9+

## Architecture overview [#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 [#step-1-configure-google-calendar-provider]

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

<Steps>
  <Step title="Navigate to provider catalog">
    Go to [platform.metorial.com](https://platform.metorial.com) and click **Providers**
  </Step>

  <Step title="Find Google Calendar">
    Search for "Google Calendar" in the catalog
  </Step>

  <Step title="Create an integration">
    Open the provider, click **Use Provider** → **Integration**, then note the provider deployment or integration ID shown in the dashboard.
  </Step>
</Steps>

<Callout type="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.
</Callout>

## Step 1b: Configure Gmail provider [#step-1b-configure-gmail-provider]

Next, configure the Gmail provider for email communications:

<Steps>
  <Step title="Search for Gmail">
    In the same catalog, search for "Gmail"
  </Step>

  <Step title="Create an integration">
    Open Gmail, click **Use Provider** → **Integration**, and note your Gmail provider deployment or integration ID.
  </Step>
</Steps>

<Callout type="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.
</Callout>

## Step 2: Set up OAuth authentication [#step-2-set-up-oauth-authentication]

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

<Steps>
  <Step title="Install dependencies">
    <CodeBlockTabs defaultValue="TypeScript" groupId="language" mode="shell">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="TypeScript">
          TypeScript
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Python">
          Python
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="TypeScript">
        <Shell>
          <Command>
            npm install metorial @metorial/anthropic @anthropic-ai/sdk
          </Command>
        </Shell>
      </CodeBlockTab>

      <CodeBlockTab value="Python">
        <Shell>
          <Command>
            pip install metorial anthropic
          </Command>
        </Shell>
      </CodeBlockTab>
    </CodeBlockTabs>
  </Step>

  <Step title="Create OAuth setup script">
    ```typescript TypeScript
      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();
    ```

    ```python Python
    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)
    ```
  </Step>

  <Step title="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.
  </Step>

  <Step title="Save auth config IDs">
    The script outputs your auth config IDs (starting with `pac_`). Save these for use in Step 3.
  </Step>
</Steps>

<Callout type="note">
  **OAuth Scopes**: Calendar needs `calendar.events` and `calendar.readonly`. Gmail needs `gmail.send` and `gmail.readonly`. These are configured automatically by Metorial.
</Callout>

## Step 3: Build the interview coordinator [#step-3-build-the-interview-coordinator]

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

<CodeBlockTabs defaultValue="TypeScript" mode="code">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="TypeScript">
      TypeScript
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="Python">
      Python
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="TypeScript">
    ```typescript
    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: "john.novak@sample.com",
      role: "Senior Software Engineer",
      interviewers: [
        "user@yourCompany.com",  // 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",
    });
    ```
  </CodeBlockTab>

  <CodeBlockTab value="Python">
    ```python
    import asyncio
    from metorial import Metorial
    from anthropic import AsyncAnthropic
    from typing import List, Literal

    # Initialize clients
    metorial = Metorial(api_key="YOUR_METORIAL_API_KEY")
    anthropic = AsyncAnthropic(api_key="YOUR_ANTHROPIC_API_KEY")

    # Server deployment credentials
    CALENDAR_DEPLOYMENT_ID = "YOUR_CALENDAR_DEPLOYMENT_ID"
    CALENDAR_AUTH_CONFIG_ID = "YOUR_CALENDAR_AUTH_CONFIG_ID"
    GMAIL_DEPLOYMENT_ID = "YOUR_GMAIL_DEPLOYMENT_ID"
    GMAIL_AUTH_CONFIG_ID = "YOUR_GMAIL_AUTH_CONFIG_ID"

    async def schedule_interview(
        candidate_name: str,
        candidate_email: str,
        role: str,
        interviewers: List[str],
        duration_minutes: int,
        preferred_dates: List[str],
        interview_type: Literal["technical", "behavioral", "panel"]
    ):
        print(f"Scheduling {interview_type} interview for {candidate_name}...")

        async with metorial.provider_session(
            provider="anthropic",
            providers=[
                {
                    "provider_deployment_id": CALENDAR_DEPLOYMENT_ID,
                    "provider_auth_config_id": CALENDAR_AUTH_CONFIG_ID
                },
                {
                    "provider_deployment_id": GMAIL_DEPLOYMENT_ID,
                    "provider_auth_config_id": GMAIL_AUTH_CONFIG_ID
                }
            ],
        ) as session:
            messages = [
                {
                    "role": "user",
                    "content": f"""Schedule interview and send confirmations:

    Candidate: {candidate_name} ({candidate_email})
    Role: {role}
    Interviewers: {", ".join(interviewers)}
    Duration: {duration_minutes} minutes
    Preferred dates: {", ".join(preferred_dates)}
    Type: {interview_type}

    Steps to complete:
    1. Check calendar availability for all interviewers on preferred dates
    2. Create calendar event with:
       - Title: "{role} Interview - {candidate_name}"
       - Add all interviewers as attendees
    3. Send confirmation email to candidate ({candidate_email}) with:
       - Subject: "Interview Scheduled - {role}"
       - Professional email including: interview date/time, what to prepare, who they'll meet
    4. Send prep email to interviewers ({", ".join(interviewers)}) with:
       - Subject: "Interview Prep - {candidate_name}"
       - 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."""
                }
            ]

            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":
                tool_use_blocks = [
                    block for block in response.content if block.type == "tool_use"
                ]

                print(f"AI using {len(tool_use_blocks)} tool(s)...")

                tool_results = await session.call_tools(tool_use_blocks)

                messages.append({"role": "assistant", "content": response.content})
                messages.append(tool_results)

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

            # Extract final response
            final_text = "\n".join(
                block.text for block in response.content if block.type == "text"
            )

            print(f"Interview coordination complete: {final_text}")

    # Example usage
    asyncio.run(schedule_interview(
        candidate_name="John Novak",
        candidate_email="john.novak@sample.com",
        role="Senior Software Engineer",
        interviewers=[
            "user@yourCompany.com",  # Hiring manager
        ],
        duration_minutes=45,
        preferred_dates=[
            "2026-02-7T14:00:00Z",  # First preference
            "2026-02-7T5:00:00Z",  # Second preference
            "2026-02-7T15:00:00Z",  # Third preference
        ],
        interview_type="technical"
    ))
    ```
  </CodeBlockTab>
</CodeBlockTabs>

<Callout type="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
</Callout>

<Callout type="warning">
  **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.
</Callout>

## Email templates [#email-templates]

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

<Accordion title="Candidate confirmation email">
  **Subject**: Interview Scheduled - Senior Software Engineer

  **Body**:

  ```
  Hi Sarah,

  Great news! We've scheduled your interview for the Senior Software Engineer position.

  Interview Details:
  - Date: Tuesday, February 10, 2026
  - Time: 2:00 PM - 3:00 PM EST
  - Duration: 60 minutes
  - Format: Technical Interview

  You'll be meeting with:
  - Alex Chen, Hiring Manager
  - Jordan Smith, Technical Lead

  What to Prepare:
  - Please have your development environment ready for a live coding session
  - We'll be discussing system design and your recent project experience
  - Feel free to ask questions about our team and tech stack

  If you need to reschedule, please let us know as soon as possible.

  Looking forward to speaking with you!

  Best regards,
  Hiring Team
  ```
</Accordion>

<Accordion title="Interviewer prep email">
  **Subject**: Interview Prep - Sarah Johnson

  **Body**:

  ```
  Hi Team,

  You have an upcoming interview scheduled:

  Candidate: Sarah Johnson
  Position: Senior Software Engineer
  Interview Type: Technical
  Date: Tuesday, February 10, 2026 at 2:00 PM EST
  Duration: 60 minutes

  Interview Format:
  - First 10 minutes: Introductions and company overview
  - Next 35 minutes: Technical assessment (live coding + system design)
  - Final 15 minutes: Candidate questions

  Evaluation Focus:
  - Problem-solving approach and code quality
  - System design thinking
  - Communication and collaboration style
  - Cultural fit and team dynamics

  Please review the candidate's background before the interview and come prepared with your assessment criteria.

  If you need to reschedule, please notify the team immediately.

  Thanks,
  Recruiting Team
  ```
</Accordion>

## Step 4: Test the coordinator [#step-4-test-the-coordinator]

Let's test the coordinator with a realistic scenario:

<CodeBlockTabs defaultValue="TypeScript" mode="code">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="TypeScript">
      TypeScript
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="Python">
      Python
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="TypeScript">
    ```typescript
    // Test scenario: Schedule technical interview
    await scheduleInterview({
      candidateName: "Alex Rivera",
      candidateEmail: "alex.rivera@example.com",
      role: "Staff Frontend Engineer",
      interviewers: [
        "engineering.director@company.com",
        "senior.engineer@company.com",
        "product.manager@company.com",
      ],
      durationMinutes: 90,  // Panel interview
      preferredDates: [
        "2026-02-15T13:00:00Z",
        "2026-02-16T14:00:00Z",
        "2026-02-17T10:00:00Z",
      ],
      interviewType: "panel",
    });
    ```
  </CodeBlockTab>

  <CodeBlockTab value="Python">
    ```python
    # Test scenario: Schedule technical interview
    asyncio.run(schedule_interview(
        candidate_name="Alex Rivera",
        candidate_email="alex.rivera@example.com",
        role="Staff Frontend Engineer",
        interviewers=[
            "engineering.director@company.com",
            "senior.engineer@company.com",
            "product.manager@company.com",
        ],
        duration_minutes=90,  # Panel interview
        preferred_dates=[
            "2026-02-15T13:00:00Z",
            "2026-02-16T14:00:00Z",
            "2026-02-17T10:00:00Z",
        ],
        interview_type="panel"
    ))
    ```
  </CodeBlockTab>
</CodeBlockTabs>

**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

<Callout type="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.
</Callout>

## Troubleshooting [#troubleshooting]

<Accordion title="Calendar event not created">
  **Possible causes**:

  * OAuth session expired or invalid
  * Calendar API not enabled in Google Workspace
  * Insufficient permissions (need `calendar.events` scope)
  * No availability found in provided date range

  **Solutions**:

  * Re-run OAuth setup to refresh credentials
  * Verify Calendar API is enabled at [Google Cloud Console](https://console.cloud.google.com)
  * Check OAuth session has correct scopes in Metorial dashboard
  * Expand preferred date range or reduce number of required attendees
</Accordion>

<Accordion title="Emails not being sent">
  **Possible causes**:

  * OAuth session expired for Gmail
  * Gmail API not enabled
  * Insufficient permissions (need `gmail.send` scope)
  * Daily sending limit reached (500 emails/day for standard accounts)

  **Solutions**:

  * Re-run OAuth setup for Gmail specifically
  * Enable Gmail API in Google Cloud Console
  * Verify OAuth session includes `gmail.send` scope
  * Check your Gmail sending quota at Google Workspace Admin
</Accordion>

<Accordion title="Emails going to spam folder">
  **Possible causes**:

  * Sending from unverified domain
  * Email content triggers spam filters
  * High volume of emails in short period

  **Solutions**:

  * Set up SPF and DKIM records for your sending domain
  * Use professional, well-formatted email templates
  * Add delays between bulk sends
  * Test email content with spam checking tools
  * Whitelist your sending address with recipients
</Accordion>

<Accordion title="AI creates event without checking availability">
  **Issue**: Calendar event created despite conflicts

  **Solution**: This is likely a prompt issue. Ensure your prompt includes:

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

  The anti-hallucination instruction forces the AI to actually check calendars before proceeding.
</Accordion>

<Accordion title="Wrong people added to calendar event">
  **Issue**: Extra or missing attendees

  **Solution**: Be explicit in your prompt about who should attend:

  ```
  Add ALL these interviewers as attendees: {interviewers.join(", ")}
  Do not add anyone else as an attendee.
  ```

  Also verify the email addresses are correct in your request object.
</Accordion>

<Accordion title="Cannot check multiple interviewers' calendars">
  **Issue**: Need to verify availability across multiple interviewers' calendars

  **Current Limitation**: A single Google Calendar provider instance can only access one calendar (the authenticated user's calendar).

  **Solution**: To check availability across multiple interviewers:

  1. Deploy a separate Calendar provider instance for each interviewer
  2. Set up individual OAuth sessions for each interviewer's calendar
  3. Pass all calendar deployments to `providers` array in your session
  4. Update your prompt to check all calendar instances before scheduling

  Example with multiple calendars:

  ```typescript
  providers: [
    { providerDeploymentId: CALENDAR_1_ID, providerAuthConfigId: AUTH_CONFIG_1_ID },
    { providerDeploymentId: CALENDAR_2_ID, providerAuthConfigId: AUTH_CONFIG_2_ID },
    { providerDeploymentId: GMAIL_DEPLOYMENT_ID, providerAuthConfigId: GMAIL_AUTH_CONFIG_ID }
  ]
  ```

  For this tutorial, we use a single calendar and add interviewers as attendees without checking their availability.
</Accordion>

## Advanced customization [#advanced-customization]

<Cards columns="2">
  <Card title="Email personalization" icon="envelope">
    Customize email templates based on role, interview type, or company culture:

    ```typescript
    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.
  </Card>

  <Card title="Multi-stage interviews" icon="sitemap">
    Coordinate complex interview pipelines:

    ```typescript
    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.
  </Card>

  <Card title="Feedback collection" icon="clipboard">
    Request and aggregate interviewer feedback:

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

    Automate post-interview feedback workflow.
  </Card>

  <Card title="Calendar preferences" icon="calendar">
    Respect interviewer working hours and preferences:

    ```typescript
    const preferences = {
      "eng.dir@co.com": {
        hours: "9-17",
        timezone: "America/New_York"
      }
    };
    ```

    Include preferences in AI prompt for smarter scheduling.
  </Card>

  <Card title="Multi-language support" icon="language">
    Send emails in candidate's preferred language:

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

    AI automatically generates emails in requested language.
  </Card>

  <Card title="Multi-calendar support" icon="calendar-days">
    Check availability across multiple interviewers' calendars:

    ```typescript
    // 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.
  </Card>
</Cards>

## Production considerations [#production-considerations]

Before deploying to production:

### Error handling [#error-handling]

Add comprehensive error handling for common failures:

```typescript
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 [#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 [#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 [#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 [#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

## Related sample projects [#related-sample-projects]

<Cards columns="2">
  <Card title="Code review bot" icon="code-branch" href="/docs/build/samples/code-review-bot">
    Build an AI code reviewer that posts comments on pull requests
  </Card>

  <Card title="Slack standup bot" icon="users" href="/docs/build/samples/slack-standup-bot">
    Create a bot that collects and summarizes team standup updates
  </Card>
</Cards>