# How to Integrate Lovable with Google Meet

- Tool: Lovable
- Difficulty: Intermediate
- Time required: 50 minutes
- Last updated: March 2026

## TL;DR

To integrate Lovable with Google Meet, create a Supabase Edge Function that uses the Google Calendar API to create calendar events with conferenceData, which automatically generates a Meet link. Authenticate using a Google service account, store the private key in Cloud → Secrets, and call the Edge Function from your Lovable frontend to create meetings and return joinable Meet URLs.

## Generate Google Meet links in Lovable using the Calendar API

Google Meet works differently from Zoom or Webex — there is no standalone 'create a meeting' endpoint. Instead, Meet links are a feature of Google Calendar events. When you create a calendar event via the API and include a conferenceData request, Google automatically provisions a Meet room and attaches its URI to the event. This design means Meet integration is fundamentally a Calendar API integration, using Google Calendar v3 with the conferenceDataVersion=1 query parameter.

For Lovable applications, this pattern works cleanly through an Edge Function: your function signs a JWT using a service account private key, exchanges it for a short-lived Google access token, then calls the Calendar API to insert an event. The response includes the event's conferenceData.entryPoints array, from which you extract the VIDEO type entry to get the Meet link URI. This URI is permanent for the duration of the event and can be shared with participants immediately.

Service accounts are the right authentication approach for this use case — they operate server-to-server without requiring individual users to grant OAuth permission. The service account must be granted access to the target calendar (or you can have it create events on any user's calendar by enabling Google Workspace domain-wide delegation). This guide uses a simple approach: the service account creates events on its own calendar, and the Meet link is extracted and returned to your app.

## Before you start

- A Lovable project with Lovable Cloud enabled (visible in the Cloud tab of the editor)
- A Google Cloud project at console.cloud.google.com with the Google Calendar API enabled
- A Google Cloud service account created with the service account JSON key file downloaded
- Basic understanding of Lovable Edge Functions and Cloud → Secrets
- The Google account whose calendar the service account will create events on (can be the service account's own calendar)

## Step-by-step guide

### 1. Set up a Google Cloud service account with Calendar API access

Navigate to console.cloud.google.com and select or create a project. In the left sidebar, go to 'APIs & Services' → 'Library', search for 'Google Calendar API', and click Enable. Next, go to 'APIs & Services' → 'Credentials' and click 'Create Credentials' → 'Service Account'. Give the service account a descriptive name like 'lovable-meet-bot' and click Create and Continue. You do not need to assign any Google Cloud IAM roles for Calendar API access — click Done.

Back on the Credentials page, click on your new service account to open it. Go to the 'Keys' tab and click 'Add Key' → 'Create New Key'. Select JSON format and click Create. This downloads a JSON file containing your service account credentials, including the private_key, client_email, and other fields. Keep this file secure — treat it like a password. You will need the private_key (a PEM-formatted RSA private key) and the client_email from this file for the next step.

**Expected result:** A service account JSON key file downloaded with private_key and client_email values ready to extract.

### 2. Store service account credentials in Cloud → Secrets

The service account JSON file contains several fields, but you only need two for the Edge Function: private_key and client_email. In your Lovable editor, open the Cloud tab by clicking the '+' panel selector at the top right, then click Cloud. Scroll to 'Secrets' and add two secrets.

Name the first secret GOOGLE_SERVICE_ACCOUNT_EMAIL and paste the client_email value from your JSON file (it looks like lovable-meet-bot@your-project.iam.gserviceaccount.com). Name the second secret GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY and paste the entire private_key value — this is a multi-line string starting with -----BEGIN RSA PRIVATE KEY----- or -----BEGIN PRIVATE KEY-----. Copy the entire string including the header and footer lines.

A common issue: the private key in the JSON file has literal \n characters representing newlines. When you paste it into Cloud → Secrets, you may need to replace the literal \n sequences with actual newlines (or your Edge Function code will handle this with a .replace(/\\n/g, '\n') call). Lovable's Secrets panel stores the value as-is, so be careful about how the key is formatted. Lovable blocks approximately 1,200 hardcoded keys daily — always use Secrets, never paste keys in chat.

**Expected result:** Two secrets saved: GOOGLE_SERVICE_ACCOUNT_EMAIL and GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY visible in Cloud → Secrets.

### 3. Build the Google Meet link generation Edge Function

The Edge Function needs to: (1) construct a JWT from the service account credentials, (2) exchange the JWT for a Google access token, and (3) call the Calendar API to create an event with conferenceData. Deno has built-in crypto support for JWT signing.

The JWT for Google service accounts uses RS256 signing. The header is { alg: 'RS256', typ: 'JWT' }. The payload includes iss (service account email), scope (https://www.googleapis.com/auth/calendar), aud (https://oauth2.googleapis.com/token), exp (one hour from now), and iat (now). The token endpoint is https://oauth2.googleapis.com/token with grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer.

Once you have the access token, POST to https://www.googleapis.com/calendar/v3/calendars/primary/events?conferenceDataVersion=1 with the event body. The conferenceData field must include createRequest with requestId (unique per request) and conferenceSolutionKey.type set to 'hangoutsMeet'. The response contains conferenceData.entryPoints array — find the entry with entryPointType 'video' and return its uri as the Meet link.

```
// supabase/functions/google-meet/index.ts
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};

async function getGoogleAccessToken(): Promise<string> {
  const email = Deno.env.get('GOOGLE_SERVICE_ACCOUNT_EMAIL')!;
  const privateKeyPem = Deno.env.get('GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY')!.replace(/\\n/g, '\n');

  const now = Math.floor(Date.now() / 1000);
  const payload = {
    iss: email,
    scope: 'https://www.googleapis.com/auth/calendar',
    aud: 'https://oauth2.googleapis.com/token',
    exp: now + 3600,
    iat: now,
  };

  // Encode JWT header and payload
  const header = btoa(JSON.stringify({ alg: 'RS256', typ: 'JWT' })).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
  const body = btoa(JSON.stringify(payload)).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
  const signingInput = `${header}.${body}`;

  // Import private key
  const pemContents = privateKeyPem.replace(/-----BEGIN.*-----/, '').replace(/-----END.*-----/, '').replace(/\s/g, '');
  const binaryKey = Uint8Array.from(atob(pemContents), c => c.charCodeAt(0));
  const cryptoKey = await crypto.subtle.importKey(
    'pkcs8', binaryKey.buffer,
    { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },
    false, ['sign']
  );

  // Sign JWT
  const signature = await crypto.subtle.sign(
    'RSASSA-PKCS1-v1_5',
    cryptoKey,
    new TextEncoder().encode(signingInput)
  );
  const sigBase64 = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
  const jwt = `${signingInput}.${sigBase64}`;

  // Exchange JWT for access token
  const tokenRes = await fetch('https://oauth2.googleapis.com/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: `grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=${jwt}`,
  });

  const tokenData = await tokenRes.json();
  if (!tokenRes.ok) throw new Error(`Token error: ${JSON.stringify(tokenData)}`);
  return tokenData.access_token;
}

serve(async (req) => {
  if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders });

  try {
    const { summary, start_time, end_time, description = '' } = await req.json();
    const accessToken = await getGoogleAccessToken();

    const eventRes = await fetch(
      'https://www.googleapis.com/calendar/v3/calendars/primary/events?conferenceDataVersion=1',
      {
        method: 'POST',
        headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' },
        body: JSON.stringify({
          summary,
          description,
          start: { dateTime: start_time, timeZone: 'UTC' },
          end: { dateTime: end_time, timeZone: 'UTC' },
          conferenceData: {
            createRequest: {
              requestId: crypto.randomUUID(),
              conferenceSolutionKey: { type: 'hangoutsMeet' },
            },
          },
        }),
      }
    );

    const event = await eventRes.json();
    if (!eventRes.ok) throw new Error(`Calendar API error: ${JSON.stringify(event)}`);

    const meetEntry = event.conferenceData?.entryPoints?.find((ep: { entryPointType: string }) => ep.entryPointType === 'video');
    const meetLink = meetEntry?.uri ?? null;

    return new Response(
      JSON.stringify({ meetLink, eventId: event.id, eventLink: event.htmlLink }),
      { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
    );
  } catch (error) {
    return new Response(
      JSON.stringify({ error: error.message }),
      { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
    );
  }
});
```

**Expected result:** A deployed google-meet Edge Function that creates a Calendar event with a Meet link and returns the Meet URI.

### 4. Connect the Meet link generator to your Lovable UI

With the Edge Function deployed, ask Lovable to build the UI component that calls it. The frontend code should call supabase.functions.invoke('google-meet', { body: { summary, start_time, end_time } }) and handle the returned meetLink. The Supabase client automatically adds the authentication header required by Edge Functions.

Display the Meet link with a prominent 'Join Meeting' button and a copy-to-clipboard button. If your app manages bookings or sessions, store the meetLink and eventId in your Supabase bookings table alongside the meeting time and participant information. Make sure your Row Level Security policy allows authenticated users to read their own bookings but not others'. For emailing the link, you can chain the Edge Function response with a Resend call — Lovable can generate this combined flow if you describe it in your prompt.

**Expected result:** A UI form in Lovable that creates a real Google Meet link when submitted and displays it for copying or sharing.

## Best practices

- Cache Google access tokens for up to 55 minutes (tokens last 60 minutes — subtract 5 as a safety buffer) to avoid signing a new JWT on every meet link request
- Always include a unique requestId in conferenceData.createRequest — use crypto.randomUUID() to generate one per call
- Store the Google Calendar eventId in your Supabase database alongside the Meet link so you can update or delete the event later if the meeting is cancelled
- Use the service account's own primary calendar for event creation unless you specifically need events to appear on users' individual calendars, which requires domain-wide delegation
- Set the timeZone field in event start and end objects to match the user's timezone — 'UTC' works as a safe default but is confusing for users who see the event in their calendar
- Rotate service account keys periodically and update Cloud → Secrets immediately — treat these keys with the same care as database passwords

## Use cases

### Instant Meet link generation for a coaching platform

When a client books a session in your scheduling app, automatically generate a Google Meet link and include it in the confirmation email and the client's dashboard. The Edge Function creates the calendar event and returns the Meet URI, which gets stored in your bookings table.

Prompt example:

```
Add a booking confirmation flow that calls my google-meet Edge Function to generate a Meet link when a session is booked. Store the meet_link in the bookings table and display it in the booking confirmation screen. Include a 'Join Meeting' button that opens the Meet link.
```

### Automated team standup link refresher

Create a weekly scheduler that generates fresh Google Meet links for recurring team standups. The Edge Function creates recurring calendar events with unique Meet rooms, and the links are posted to a Slack channel or stored in a Supabase table for team members to access.

Prompt example:

```
Create a scheduler page where I can set a recurring standup time. When I click 'Generate This Week's Link', call my google-meet Edge Function to create a calendar event and return a Meet link. Display the link with a copy button and store it in a standups table.
```

### Customer onboarding call scheduler with Meet links

Build an onboarding flow where new users schedule their kickoff call and immediately receive a Google Meet link. The Edge Function creates the event for the specified time, returns the join URL, and your Lovable app sends a confirmation with the link via Resend.

Prompt example:

```
Build an onboarding call scheduler. Show a time picker, and when the user selects a slot and clicks 'Schedule Call', call my google-meet Edge Function to create a 30-minute meeting at that time. Show the Meet link and send a confirmation email with the link using Resend.
```

## Troubleshooting

### Edge Function returns 'invalid_grant' or 401 when exchanging the JWT for a Google access token

Cause: The service account private key is incorrectly formatted — the literal \n characters in the JSON file were not converted to actual newlines, so the PEM parsing fails.

Solution: In your Edge Function code, add .replace(/\\n/g, '\n') when reading the private key from Deno.env.get('GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY'). Also verify the service account has not been deleted or disabled in Google Cloud Console → IAM & Admin → Service Accounts.

```
const privateKeyPem = Deno.env.get('GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY')!.replace(/\\n/g, '\n');
```

### Calendar API returns 403 with 'Service accounts cannot invite attendees without Domain-Wide Delegation of Authority'

Cause: The service account is trying to create events on a user's calendar (not the service account's own primary calendar) without domain-wide delegation enabled in Google Workspace.

Solution: Either create events on the service account's own primary calendar (use 'primary' as the calendarId), or enable domain-wide delegation in Google Workspace Admin Console and add the Calendar API scope. For most Lovable use cases, using the service account's own calendar and simply sharing the Meet link is the simplest approach.

### Event is created successfully but conferenceData is null or missing the Meet link

Cause: The conferenceDataVersion=1 query parameter was omitted from the Calendar API request URL, or the Google Calendar API was not enabled in Google Cloud Console.

Solution: Verify your API call URL includes ?conferenceDataVersion=1 as a query parameter. Also confirm in Google Cloud Console → APIs & Services → Enabled APIs that the Google Calendar API is listed as enabled for your project.

```
const url = 'https://www.googleapis.com/calendar/v3/calendars/primary/events?conferenceDataVersion=1';
```

### The service account cannot access the Google Calendar API — returns 'API has not been used in project'

Cause: The Google Calendar API is not enabled for the Google Cloud project where the service account was created.

Solution: Go to console.cloud.google.com → APIs & Services → Library. Search for 'Google Calendar API' and click Enable. Wait 1-2 minutes for the enablement to propagate, then retry the Edge Function call.

## Frequently asked questions

### Can I create Google Meet links without a Google Workspace account?

You can use a standard Google account to create a service account and call the Calendar API, but service account access to create calendar events with Meet links works most reliably with Google Workspace. Personal Google accounts have some limitations around service account calendar access — if you encounter issues, using your service account's own calendar (calendarId: 'primary') is the most reliable approach.

### Do Google Meet links expire?

Google Meet links are tied to calendar events. As long as the calendar event exists, the Meet link remains valid. If you delete the event, the Meet room may eventually become inaccessible. Meet links from the same event can be reused across multiple sessions — the room does not automatically close when a call ends.

### Can I add participants to the calendar event so they receive an invite?

Yes — add an attendees array to the event creation body with objects containing email addresses. However, service accounts can only send invites to attendees within the same Google Workspace domain unless domain-wide delegation is configured. For external attendees, share the Meet link directly rather than relying on calendar invites.

### Why does my Edge Function work locally but return errors in production?

The most common cause is the private key formatting issue — the \n sequences in the JSON file need to be actual newlines in the PEM string. Check Cloud → Logs for the specific error. Also verify that the GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY secret in production contains the full key including the BEGIN and END header lines.

---

Source: https://www.rapidevelopers.com/lovable-integration/google-meet
© RapidDev — https://www.rapidevelopers.com/lovable-integration/google-meet
