Skip to main content

Calendar Functions

Four edge functions handle calendar OAuth, availability checking, and event creation. All support dual-mode operation -- they work with both client-owned calendars (calendar_connections) and user-owned calendars (user_calendar_connections).

google-calendar-callback

Handles the OAuth callback after a user authorizes Google Calendar access. Exchanges the authorization code for tokens and stores them.

Flow

State Parameter Parsing

const state = url.searchParams.get('state');
const parts = state.split(':');

if (parts[0] === 'user') {
// Internal mode
const userId = parts[1]; // user:{userId}:{provider}
const provider = parts[2];
// Upsert into user_calendar_connections
} else {
// Client mode
const clientId = parts[0]; // {clientId}:{provider}
const provider = parts[1];
// Upsert into calendar_connections
}

Side Effects

  • Upserts calendar_connections or user_calendar_connections with tokens
  • Sets is_active = true
  • Stores connected_email from the token response

outlook-calendar-callback

Same as google-calendar-callback but for Microsoft Outlook/Office 365.

Differences from Google

  • Token endpoint: https://login.microsoftonline.com/common/oauth2/v2.0/token
  • Scopes: Calendars.ReadWrite, offline_access
  • Calendar ID discovery via Microsoft Graph API
  • Stores provider = 'outlook'

Side Effects

  • Same dual-mode upsert as Google callback
  • Redirects to the same destinations based on state format

get-calendar-availability

Checks calendar busy times for a specific date to calculate available booking slots. Called by the booking page when a user selects a date.

Input

interface AvailabilityRequest {
bookingLinkId: string; // UUID of the booking link
date: string; // ISO date string (YYYY-MM-DD)
}

Flow

Slot Calculation Logic

  1. Load the owner's availability settings (e.g., Monday 9:00-17:00)
  2. Generate all possible slots for the requested date at the booking link's duration_minutes interval
  3. Query the calendar provider's FreeBusy API for busy times on that date
  4. Subtract busy times from available slots
  5. Return remaining open slots

Output

interface AvailabilityResponse {
slots: Array<{
start: string; // ISO datetime
end: string; // ISO datetime
}>;
}

Token Refresh

If the stored access token is expired, the function automatically refreshes it:

if (new Date(connection.token_expires_at) < new Date()) {
const newTokens = await refreshOAuthToken(connection);
await supabaseAdmin
.from(tableName)
.update({
access_token: newTokens.access_token,
token_expires_at: new Date(Date.now() + newTokens.expires_in * 1000).toISOString(),
})
.eq('id', connection.id);
}

create-calendar-event

Creates an event in the owner's calendar (Google or Outlook) when a meeting is booked.

Input

interface CreateEventRequest {
bookingLinkId: string;
slot: { start: string; end: string };
bookerName: string;
bookerEmail: string;
bookerPhone?: string;
// For internal mode:
salesMeetingId?: string;
prospectId?: string;
// For client mode:
meetingId?: string;
contactId?: string;
}

Flow

Side Effects

  • Creates event in Google Calendar or Outlook
  • Updates meetings or sales_meetings with calendar_event_id and meeting_link
  • Triggers send-booking-notification for confirmation email
  • Google events include a Google Meet link automatically
  • Outlook events include a Teams link if the organization supports it