Guide

Calendar Automation with PowerShell

According to Harvard Business Review, executives spend an average of 23 hours per week in meetings, up from 10 hours in the 1960s. Much of that time goes to scheduling logistics. This guide shows how to automate meeting invites, check participant availability, book conference rooms, and send calendar-driven emails from PowerShell using Nylas CLI. Works across all major email providers.

Written by Qasim Muhammad Staff SRE

Reviewed by Nick Barraclough

VerifiedCLI 3.1.1 · Gmail, Outlook · last tested April 11, 2026

Calendar automation methods compared

PowerShell calendar automation can be done through Microsoft Graph SDK, Google Calendar API, or the Nylas CLI. Microsoft Graph requires Azure AD app registration and Calendars.ReadWrite permission, which takes 15-20 minutes to configure. Google Calendar requires a GCP project with OAuth consent screen setup. The CLI connects to 5 providers through a single nylas auth config command, eliminating per-provider auth logic.

TaskMicrosoft Graph SDKGoogle Calendar APINylas CLI
Check availabilityPOST /me/calendar/getSchedule + Azure AD appfreebusy.query + OAuth consent screennylas calendar availability check
Create eventNew-MgUserEvent (6+ params)events.insert (JSON body)nylas calendar events create
Book a roomfindMeetingTimes + room list APIresources.calendars.list (Workspace admin)Same availability check on room email
Auth setupAzure AD app registration + admin consentGCP project + OAuth consent screennylas auth config (one command)
ProvidersMicrosoft onlyGoogle onlyGoogle, Outlook, Exchange, Yahoo, iCloud

Microsoft Graph requires the Calendars.ReadWrite permission and an Azure AD app. Google Calendar requires a GCP project with the Calendar API enabled. Both require token refresh logic in your scripts. The CLI handles all of this behind nylas auth config.

Before you start

Calendar automation requires the Nylas CLI installed and authenticated with a grant that has calendar permissions. Installation takes under 2 minutes using irm https://cli.nylas.com/install.ps1 | iex on Windows or brew install nylas/nylas-cli/nylas on macOS. If you haven't set up the CLI yet, follow the install guide first.

After authentication, verify that the CLI can read calendar data from your provider. The command below fetches one event and parses the JSON output into a PowerShell object. If it returns an event, calendar access is working.

nylas calendar events list --json --limit 1 | ConvertFrom-Json

Check participant availability

The Nylas CLI availability check queries free/busy data across all participants in a single API call, regardless of whether participants use Google Calendar, Outlook, or Exchange. According to a 2023 Reclaim.ai study, the average worker spends 5.2 hours per week on scheduling logistics. Automating availability checks from PowerShell eliminates the back-and-forth emails that drive that number.

The script below accepts a list of participant email addresses, a target date, and a meeting duration. It calls nylas calendar availability check to find time slots where every participant is free during business hours (9 AM to 5 PM), then recommends the first slot that fits.

# check-availability.ps1 -- Find open slots for a group meeting
param(
    [string[]]$Participants = @("alice@company.com", "bob@company.com"),
    [string]$Date = (Get-Date).AddDays(1).ToString("yyyy-MM-dd"),
    [int]$DurationMinutes = 30
)

$startTime = "$($Date)T09:00:00"
$endTime = "$($Date)T17:00:00"

$availability = nylas calendar availability check `
    --start $startTime `
    --end $endTime `
    --participants ($Participants -join ',') `
    --json | ConvertFrom-Json

$freeSlots = $availability.time_slots |
    Where-Object { $_.status -eq "free" }

if ($freeSlots.Count -eq 0) {
    Write-Host "No common availability on $Date" -ForegroundColor Red
    exit 1
}

Write-Host "Available slots on $Date:" -ForegroundColor Green
$freeSlots | ForEach-Object {
    $start = [DateTime]::Parse($_.start_time)
    $end = [DateTime]::Parse($_.end_time)
    Write-Host "  $($start.ToString('h:mm tt')) - $($end.ToString('h:mm tt'))"
}

# First slot that fits the requested duration
$bestSlot = $freeSlots | Where-Object {
    $s = [DateTime]::Parse($_.start_time); $e = [DateTime]::Parse($_.end_time)
    ($e - $s).TotalMinutes -ge $DurationMinutes
} | Select-Object -First 1

if ($bestSlot) {
    Write-Host "Recommended: $([DateTime]::Parse($bestSlot.start_time).ToString('h:mm tt'))" -ForegroundColor Cyan
}

Create meeting invites with participants

The nylas calendar events create command creates a calendar event and sends invitations through the participant's native provider — Google Calendar, Outlook, or Exchange. Recipients see standard calendar invitations in their inbox, not raw email. According to Microsoft's documentation, Graph API event creation requires 6 separate parameters including attendee objects with nested emailAddress structures. The CLI reduces this to flat flags like --participants and --title.

The script below checks availability first, then creates the event if the time slot is open. This two-step pattern prevents double-booking and gives the script a chance to warn the user about conflicts before sending invitations.

# create-meeting.ps1 -- Book a meeting with availability check
param(
    [string]$Title = "Team Sync",
    [string[]]$Participants = @("alice@company.com", "bob@company.com"),
    [string]$Date,
    [string]$StartTime = "10:00",
    [int]$Duration = 30,
    [string]$Description = ""
)

if (-not $Date) { $Date = (Get-Date).AddDays(1).ToString("yyyy-MM-dd") }

$startDt = "$($Date)T$($StartTime):00"
$endDt = [DateTime]::Parse($startDt).AddMinutes($Duration).ToString("yyyy-MM-ddTHH:mm:ss")

# Check availability first
$avail = nylas calendar availability check `
    --start $startDt --end $endDt `
    --participants ($Participants -join ',') `
    --json | ConvertFrom-Json

$conflicts = $avail.time_slots | Where-Object { $_.status -ne "free" }
if ($conflicts) {
    Write-Host "WARNING: Not all participants are free" -ForegroundColor Yellow
}

# Create the event
$event = nylas calendar events create `
    --title $Title `
    --start $startDt --end $endDt `
    --participants ($Participants -join ',') `
    --description $Description `
    --json | ConvertFrom-Json

Write-Host "Meeting created:" -ForegroundColor Green
Write-Host "  Title: $($event.title)"
Write-Host "  Time:  $startDt to $endDt"
Write-Host "  With:  $($Participants -join ', ')"

Conference room booking

Conference rooms in Google Workspace and Microsoft 365 are represented as calendar resources with their own email addresses (e.g., room-a@company.com). The Nylas CLI treats room calendars as regular participants, so the same nylas calendar availability check command works for rooms. A 2024 Robin survey found that 30% of conference rooms sit empty during booked slots because manual booking is unreliable — automated booking reduces ghost reservations.

The script below iterates through a list of room email addresses, checks each one for availability, and books the first free room. It adds both the room and human attendees as participants so the room calendar shows as reserved and attendees receive the meeting invitation.

# book-room.ps1 -- Find a free room and book it
param(
    [string[]]$Rooms = @("room-a@company.com", "room-b@company.com", "room-c@company.com"),
    [string]$Date = (Get-Date).AddDays(1).ToString("yyyy-MM-dd"),
    [string]$StartTime = "14:00",
    [int]$DurationMinutes = 60,
    [string]$Title = "Project Review",
    [string[]]$Attendees = @("alice@company.com", "bob@company.com")
)

$startDt = "$($Date)T$($StartTime):00"
$endDt = [DateTime]::Parse($startDt).AddMinutes($DurationMinutes).ToString("yyyy-MM-ddTHH:mm:ss")

$availableRoom = $null

foreach ($room in $Rooms) {
    Write-Host "Checking $room ..." -NoNewline
    $avail = nylas calendar availability check `
        --start $startDt --end $endDt `
        --participants $room `
        --json | ConvertFrom-Json

    $free = $avail.time_slots | Where-Object { $_.status -eq "free" }
    if ($free) {
        Write-Host " FREE" -ForegroundColor Green
        $availableRoom = $room
        break
    } else {
        Write-Host " BUSY" -ForegroundColor Red
    }
}

if (-not $availableRoom) {
    Write-Host "No rooms available at $StartTime on $Date" -ForegroundColor Red
    exit 1
}

$allParticipants = @($availableRoom) + $Attendees

$event = nylas calendar events create `
    --title "$Title ($($availableRoom -replace '@.*', ''))" `
    --start $startDt --end $endDt `
    --participants ($allParticipants -join ',') `
    --description "Room: $availableRoom" `
    --json | ConvertFrom-Json

Write-Host "Booked $availableRoom for '$Title'" -ForegroundColor Green

Send meeting prep emails

Meeting prep emails pull recent email history from each participant and deliver a summary to your inbox before the meeting starts. According to a 2022 Otter.ai survey, 35% of workers say they attend meetings without knowing the context. This script solves that by combining nylas calendar events list to find upcoming meetings with nylas email search to retrieve the 3 most recent messages from each participant.

The script checks for events starting within a configurable window (default 30 minutes), builds a summary with participant names, meeting time, and recent email subjects, then sends the prep email using nylas email send. Set the $env:MY_EMAIL environment variable to your email address before running.

# meeting-prep.ps1 -- Prep email before upcoming meetings
param([int]$MinutesBefore = 30)

$events = nylas calendar events list --json | ConvertFrom-Json
$now = Get-Date
$prepWindow = $now.AddMinutes($MinutesBefore)

$upcoming = $events | Where-Object {
    $start = [DateTime]::Parse($_.when.start_time)
    $start -gt $now -and $start -le $prepWindow
}

foreach ($event in $upcoming) {
    $participants = $event.participants | ForEach-Object { $_.email }
    if ($participants.Count -eq 0) { continue }

    $recentEmails = foreach ($p in $participants) {
        $emails = nylas email search "from:$p" --json --limit 3 | ConvertFrom-Json
        $emails | ForEach-Object { "  - [$($_.date)] $($_.subject)" }
    }

    $body = @"
Meeting Prep: $($event.title)
Time: $($event.when.start_time)
Participants: $($participants -join ', ')

Recent emails from participants:
$($recentEmails -join "`n")
"@

    nylas email send --to $env:MY_EMAIL `
        --subject "Prep: $($event.title) in $MinutesBefore min" `
        --body $body --yes
    Write-Host "Sent prep for: $($event.title)" -ForegroundColor Green
}

Daily schedule email

A daily schedule email delivers a formatted summary of the day's meetings — times, participants, and titles — to your inbox every morning. The average knowledge worker has 25.6 meetings per week according to a 2024 Reclaim.ai report, making a quick morning overview valuable. This script uses nylas calendar events list --json to fetch today's events and nylas email send to deliver the summary.

The script formats each event with start time, end time, title, and participant list. If the calendar is empty for the day, it sends a short "No meetings today" message instead. Schedule this with Task Scheduler at 7:30 AM to have the summary waiting when you open your inbox.

# daily-schedule.ps1 -- Morning email with today's calendar
$today = Get-Date -Format "yyyy-MM-dd"
$events = nylas calendar events list --json | ConvertFrom-Json

if ($events.Count -eq 0) {
    $body = "No meetings today. Focus day!"
} else {
    $schedule = $events | ForEach-Object {
        $start = [DateTime]::Parse($_.when.start_time).ToString('h:mm tt')
        $end = [DateTime]::Parse($_.when.end_time).ToString('h:mm tt')
        $who = ($_.participants | ForEach-Object { $_.email }) -join ', '
        "$start - $end: $($_.title)`n  With: $who"
    }
    $body = "Schedule for $today ($($events.Count) meetings)`n`n$($schedule -join "`n`n")"
}

nylas email send --to $env:MY_EMAIL `
    --subject "Schedule: $today ($($events.Count) meetings)" `
    --body $body --yes

Automate recurring meeting follow-ups

Recurring meeting follow-ups send a templated action-item email to all participants after a standup or daily sync ends. According to Atlassian's research, teams lose an average of 31 hours per month in unproductive meetings — follow-up emails with action items help recapture value from that time. This script matches recently ended events whose titles contain "standup", "daily sync", or "scrum".

The script looks for events that ended within the last 2 hours and sends each participant list a follow-up email with blank action-item checkboxes. Run it on a 15-minute schedule through Task Scheduler, and it catches standups regardless of whether they end on time or run over.

# standup-followup.ps1 -- Follow-up email after standup meetings
$events = nylas calendar events list --json | ConvertFrom-Json
$now = Get-Date

$recentStandups = $events | Where-Object {
    $_.title -match 'standup|daily sync|scrum' -and
    [DateTime]::Parse($_.when.end_time) -lt $now -and
    [DateTime]::Parse($_.when.end_time) -gt $now.AddHours(-2)
}

foreach ($standup in $recentStandups) {
    $participants = ($standup.participants | ForEach-Object { $_.email }) -join ','
    $body = @"
Follow-up: $($standup.title) - $(Get-Date -Format 'MMM d')

Action items:
- [ ]
- [ ]

Reply with updates or additional items.
"@
    nylas email send --to $participants `
        --subject "Follow-up: $($standup.title) - $(Get-Date -Format 'MMM d')" `
        --body $body --yes
}

Schedule with Task Scheduler

Windows Task Scheduler runs PowerShell scripts on a cron-like schedule without requiring a third-party tool. The schtasks /create command accepts trigger types like /sc weekly for specific days or /sc minute /mo 15 for 15-minute intervals. Task Scheduler has been built into every Windows version since Windows Vista (2007), so no installation is needed.

The two tasks below schedule the daily summary email at 7:30 AM on weekdays and the meeting prep script every 15 minutes. Both use pwsh.exe -NoProfile to skip loading the PowerShell profile, which reduces startup time by 200-500 milliseconds per invocation.

# Morning schedule at 7:30 AM weekdays
schtasks /create /tn "DailySchedule" `
    /tr "pwsh.exe -NoProfile -File C:\Scripts\daily-schedule.ps1" `
    /sc weekly /d MON,TUE,WED,THU,FRI /st 07:30 /ru "%USERNAME%"

# Meeting prep every 15 minutes
schtasks /create /tn "MeetingPrep" `
    /tr "pwsh.exe -NoProfile -File C:\Scripts\meeting-prep.ps1" `
    /sc minute /mo 15 /ru "%USERNAME%"

Troubleshooting

Calendar automation scripts can fail due to PowerShell version differences, timezone handling, or provider-specific email aliases. The 3 issues below cover the most common failures reported when running Nylas CLI calendar commands in PowerShell on Windows 10 and Windows 11 environments.

ConvertFrom-Json : Invalid JSON primitive

PowerShell 5.1 has a 2 MB limit on ConvertFrom-Json input. If your calendar has many events, add --limit 50 to cap the response size. PowerShell 7+ removes this limit.

Times are wrong by several hours

Calendar events returned by the CLI use UTC. Convert to local time with [DateTime]::Parse($event.when.start_time).ToLocalTime(). If you create events, pass times in your local timezone with an offset: 2026-05-10T09:00:00-04:00.

Availability check returns empty slots

Verify the participant email matches their calendar account — corporate aliases sometimes differ from the calendar address. Test with your own email first: nylas calendar availability check --participants you@company.com --start "..." --end "..." --json.

Frequently asked questions

These 3 questions cover the most common scenarios for PowerShell calendar automation: room booking, sending invitations programmatically, and triggering emails from calendar events. Each answer includes the exact CLI command needed to accomplish the task.

How do I check meeting room availability from PowerShell?

Use nylas calendar availability check --participants room-a@company.com --start "..." --end "..." --json. Room calendars in Google Workspace and Microsoft 365 are treated as regular participants. Parse the result and filter for free slots.

Can I send meeting invites from a PowerShell script?

Yes. Use nylas calendar events create --title "Meeting" --start "..." --end "..." --participants "alice@company.com,bob@company.com". The CLI sends invites through the provider automatically.

How do I schedule emails based on calendar events?

List upcoming events with nylas calendar events list --json, find events starting within your window, and send emails with nylas email send. Run via Task Scheduler every 15 minutes.


Next steps