Calendar & meetings
Microsoft Teams and Google Meet OAuth, admin consent, and live interview scheduling.
Calendar integrations let recruiters schedule live interviews with automatic Microsoft Teams or Google Meet links. Connections are per-user (delegated OAuth), not org-wide service accounts. Calendar cards live on Settings → Integrations under Calendar & meetings.
Feature gates
Two platform feature flags control availability:
| Flag slug | Provider |
|---|---|
meetings_microsoft_teams | Microsoft 365 / Teams |
meetings_google_meet | Google Calendar / Meet |
Platform admins grant these per tenant. The API also requires server-side OAuth app configuration:
| Provider | Required env vars |
|---|---|
| Microsoft | MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET, MICROSOFT_REDIRECT_URI |
GOOGLE_CALENDAR_CLIENT_ID, GOOGLE_CALENDAR_CLIENT_SECRET, GOOGLE_CALENDAR_REDIRECT_URI |
calendar.capabilities returns enabled, configured, connected, and tenantConsented flags so the UI shows real connect buttons only when both platform grant and server config are present.
Settings path
| Action | Path |
|---|---|
| Connect calendar | /dashboard/settings/integrations |
| Schedule interview | Candidate profile or position Schedule tab |
| After OAuth | Redirect to ?google=connected or ?ms=admin_consent_granted |
These slugs are hidden from the ATS integrations grid — they only appear as dedicated calendar cards.
Microsoft Teams
OAuth flow
- Recruiter clicks Connect Microsoft on the calendar card.
- Client calls
calendar.startMicrosoftConnect→ receivesauthorizeUrl+ CSRFstate. - Browser redirects to Microsoft login.
- Microsoft redirects to
/api/auth/microsoft/callbackwithcode+state. - API exchanges tokens and stores encrypted row in
user_calendar_integrations. - UI polls
calendar.listIntegrationsor reads?connected=1query param.
Scopes (user self-consent)
InsightHire requests only scopes any user can self-consent to:
User.ReadCalendars.ReadWriteoffline_access
Teams meeting links are created inline on the calendar event (event.isOnlineMeeting=true), which works with Calendars.ReadWrite alone. The admin-consent-required OnlineMeetings.ReadWrite scope is not requested.
Admin consent
Some Entra ID tenants require tenant-wide admin approval before users can consent.
Option A — Copy admin consent URL
calendar.getMicrosoftAdminConsentUrl builds a link targeted at the customer's tenant domain (e.g. prismcare.com) so IT skips the tenant picker.
Option B — Email IT admin
calendar.emailMicrosoftAdminConsent sends a templated email with the consent link and Reply-To set to the requesting recruiter.
After admin consent, organizations.microsoftTenantConsentedAt is set and other recruiters in the org skip the "IT admin needs to approve" panel.
Disconnect
calendar.disconnectMicrosoft removes the user's Microsoft integration row.
Google Meet
OAuth flow
Same pattern as Microsoft:
calendar.startGoogleConnect→ authorize URL- Redirect to Google consent
- Callback at
/api/auth/google/callback - Tokens stored in
user_calendar_integrationswith providerGOOGLE
Scopes
Minimal set for Meet-bearing events on the recruiter's calendar:
openid,emailhttps://www.googleapis.com/auth/calendar.events
No Google Workspace admin involvement required for standard Google accounts.
Disconnect
calendar.disconnectGoogle removes the Google integration row.
Scheduling live interviews
Scheduling is implemented in candidate-interviews router. When a recruiter creates an interview:
Location types
| Type | Behavior |
|---|---|
TEAMS | Create Outlook event via Microsoft Graph with Teams link |
GOOGLE_MEET | Create Google Calendar event with Meet conference |
INSIGHTHIRE_LIVE | Provision Daily.co room (separate insighthire_live_interviews flag) |
PHONE, IN_PERSON, OTHER | Persist row + reminder emails only |
Calendar connected vs fallback
If the organizer has a connected calendar for the chosen provider:
- Event created on their calendar
- Candidate receives standard calendar invite (works in Gmail, Apple Mail, Outlook)
- Reschedule/cancel propagates to attendees
If no calendar connected:
- Interview row persisted with
metadata.inviteDelivery='ICS_EMAIL' .icsemail invites sent viainterview-ics-emails.service.ts
The schedule modal uses calendar.capabilities to show "auto-create meeting" vs "email invites" hints.
Journey scheduling nodes
Live interviews are scheduled from:
- Candidate profile — Schedule interview action
- Position Schedule tab —
/dashboard/positions/[id]?tab=schedule
Scheduling requires at least one of: Teams enabled, Google Meet enabled, or InsightHire Live enabled (ensureMeetingsEnabled).
Per-location gates apply at create time — choosing Teams when only Google is granted returns a precondition error.
Email delivery
- Graph/Google path: provider sends calendar invites
- ICS fallback: InsightHire sends
.icsattachments - Live interview updates/cancels:
live-interview-emails.service.ts - From address for live interview mail:
hello@insighthire.com
Capability checklist
| Step | Microsoft | |
|---|---|---|
| Platform admin grants feature | ✅ meetings_microsoft_teams | ✅ meetings_google_meet |
| API env vars set | ✅ MICROSOFT_* | ✅ GOOGLE_CALENDAR_* |
| Recruiter connects account | ✅ OAuth | ✅ OAuth |
| Admin consent (if required) | ✅ One-time per tenant | ❌ Usually not needed |
| Schedule TEAMS/GOOGLE_MEET interview | ✅ | ✅ |
Troubleshooting
| Issue | Resolution |
|---|---|
| Connect button disabled | Ask platform admin to grant feature + verify API env vars |
| "Admin needs to approve" | Send admin consent URL to IT |
| Teams link missing on event | Organizer may lack Teams license; use ICS fallback or InsightHire Live |
| Google connect fails | Verify redirect URI in Google Cloud Console matches API callback |
| Scheduler not available | Enable at least one of Teams, Google Meet, or InsightHire Live flags |
Related
- Integrations overview
- Getting started — roles — who can access Settings

