skip to Main Content

I know there are many requests about this issue; but none of them seems to answer my question. To automate the generation of Google Calendar appointments, including the automatic generation of an according Google Meet Link, I’ve done the following:

  1. Created Google Cloud Project
  2. Enabled Google Calendar API for concerned account
  3. Created New Google Service Account
  4. Grabbed the e-mail address of that service account, and added it under the Google Calendar’s Settings, to allow for the privileges Make changes / Manage access for that e-mail.
  5. downloaded the most recent version of the google api client (https://packagist.org/packages/google/apiclient).
  6. Went to Google Service Accounts > Keys > Generated new key file as JSON, let’s say access.json.
  7. Implement code for generation of calendar API appointment, including recent patches (https://developers.google.com/calendar/api/releases?hl=de#september_07_2020), within PHP:

        putenv('GOOGLE_APPLICATION_CREDENTIALS=my/key/data/directoy/access.json');
        $client = new Client();
        $client->useApplicationDefaultCredentials();
        $client->addScope(Google_Service_Calendar::CALENDAR);
        $client->setSubject("{address_explained_below}");
        
        // Set up the Calendar API service
        $service = new Google_Service_Calendar($client);
        
        // Create a new event on Google Calendar
        $event = new Google_Service_Calendar_Event(
            [
                'summary'        => 'Meeting',
                'start'          => [
                    'dateTime' => '2023-04-07T10:00:00-07:00',
                    // Replace with your desired start time
                    'timeZone' => 'America/Los_Angeles',
                    // Replace with your desired timezone
                ],
                'end'            => [
                    'dateTime' => '2023-04-07T11:00:00-07:00',
                    // Replace with your desired end time
                    'timeZone' => 'America/Los_Angeles',
                    // Replace with your desired timezone
                ]
            ]
        );
        
        // Set the event's conference data to use Google Meet
        $conferenceRequest = new Google_Service_Calendar_CreateConferenceRequest();
    
        $conferenceRequest->setRequestId(uniqid());
        
        $solution_key = new Google_Service_Calendar_ConferenceSolutionKey();
        $solution_key->setType("hangoutsMeet");
        $conferenceRequest->setConferenceSolutionKey($solution_key);
        
        $conference = new Google_Service_Calendar_ConferenceData();
        
        $conference->setCreateRequest($conferenceRequest);
        
        $event->setConferenceData($conference);
        
        // Insert the event into the user's calendar
        $calendarId = 'myCalendarsID';
        $event = $service->events->insert(
            $calendarId,
            $event,
            [ 'conferenceDataVersion' => 1 ]
        );
        
        var_dump($event);
        
        // Retrieve the generated Google Meet link from the event's conference data
        $meetLink = $event->getHangoutLink();

I’ve really tried to follow the advices of all of the other posts regarding this issue in this forum; but nothing did the trick, and I am getting now a 400 Bad Request error, no matter the type I set (hangoutsMeet or whatever else), saying Invalid conference type value.. What am I missing? I’ve come across this; is this maybe library-specific, and it’s rather better to implement raw HTTP REST API calls, without using the library?

Update:

After using the Test Feature provided by Google to fire this request here:

GET https://www.googleapis.com/calendar/v3/calendars/{calendarId}

I can indeed confirm that hangoutsMeet is within the allowedConferenceSolutionTypes.

{
 "kind": "calendar#calendar",
 "etag": "secret",
 "id": "secret",
 "summary": "secret",
 "timeZone": "secret",
 "conferenceProperties": {
  "allowedConferenceSolutionTypes": [
   "hangoutsMeet"
  ]
 }
}

So I suppose, according to @Lorena Gomez (Thanks!) comment, that I have to "grant domain-wide delegation to my service account and impersonate the user with rights to that calendar", even though I’m not really clear on what that actually means / does.

Regarding the domain-wide delegation, I’ve now done that for the registered service account, as specified here. This seems to have worked, although I cannot see any difference in the service account panel within the "IAM and Management" Screen under the concerned service account. Does this delegation may take some time to have an effect?

I’ve been digging further and apparently this line of code here:

$client->setSubject($user_to_impersonate);

serves to impersonate the user with rights to that calendar. But what should be the value of $user_to_impersonate here? I’ve provided the e-mail address of the service account for which I granted domain-wide delegation for the https://www.googleapis.com/auth/calendar scope, in which case I still get:

Google_Service_Exception: { 
  "error": { 
    "errors": [ 
      { 
        "domain": "global", 
        "reason": "invalid", 
        "message": "Invalid conference type value." 
      } 
    ], 
    "code": 400, 
    "message": "Invalid conference type value." 
  } 
}

Then I’ve tried it with the e-mail address of the google account that actually owns the calendar, which resulted in:

Google_Service_Exception: { 
  "error": "unauthorized_client", 
  "error_description": "Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested." 
} 

Error code: 401 

I’ve then tried it with the user ID of the service account which was granted domain-wide delegation, which is when I get:

Caught Google_Service_Exception: { 
  "error": "invalid_request", 
  "error_description": "Invalid principal" 
} 

Error code: 400 

So I don’t understand, what am I still missing? I’ve updated my code above to the state as it is now (The string address_explained_below is replaced with the three above-mentioned attempts).

I’ve now made a last attempt where I’ve created a new service account, enabled the domain-wide delegation for it, generated the access keys, and uploaded them to the server. When then running the code above with the e-mail address of that newly created service account, I got a 404 Not found error. I then went to the Google Calendar Settings of the concerned calendar, and added the e-mail address of the service account under Share with specific people or groups (I’ve translated that part; it’s the part where you can grant access to your calendar for others). After doing so and re-running the script above, I now again get the 400 Error Invalid conference type value.

The only thing that I can think of is that the putenv call sets the value of the variable (checked with getenv) to the absolute path to the .json file holding the credentials, from the server’s root. Is that the intention? How are the credentials read/accessed? Just asking because the .json file is in a 0500 directory and the .json file itself also is 0500, but this should be okay? And well, I’m stuck.

One thought I had: Does the Domain which you verify within your Google Workspace-Domain actually have to be the domain from which you send the API request / run the code above? Because that’s currently not the case…

2

Answers


  1. Chosen as BEST ANSWER

    As I've been struggling significantly with this and there are countless posts regarding issues with this setup / confusions about steps of the setup in this forum; here's a step-by-step guide, explaining how to properly setup the programmatic generation of Google Calendar Events for your desired Google Calendar, + generate related Google Meet Links.

    What you will mainly need:

    The steps are:

    1. Create a Google Workspace Account for the domain of your business. Use your Gmail Address of the Google Calendar you're trying to integrate, for simplicity.
    2. In case you get confused: You will be asked to be provide the domain of your business, and then to create a Google Workspace user, which appears to be an e-mail address with the domain name of your business. This does NOT represent an active e-mail address (yet), not even in g-mail. It's simply a username to login into your Google Workspace account.
    3. Complete the procedure to verify your domain (normally simply adding a TXT entry to your domain's DNS records and you're good to go).
    4. The step saying Create new User and Setup Gmail appear to be mandatory for the setup of new a Google Workspace Account, due to persistent popup telling you to do so, but it is actually not necessary at all. You should especially watch out in setting up your email via gmail, as this would ask you to remove all of your MX records from your DNS records, and insert one from Google. What this will do is activate the username of your Google Workspace mentioned in step 2. as a gmail account. BUT, as you have to remove all MX records of your previous DNS settings for your business domain to do so, this will cause that all of your current business e-mails, registered by your host, will stop working. Which is why it is no problem to skip this part, if you already have an e-mail provider setup for your business domain.
    5. At this stage, everything's done with the Google Workspace Account.
    6. Create a Google Cloud Platform Account. I would again use your Gmail Address of the Google Calendar you're trying to integrate, for simplicity.
    7. Within it, go to Menu > APIs and Services, then click on the + Activate APIs and Services Button, search for Google Calendar API, click on it, and enable it.
    8. Also under Menu > APIs and Services, click on Credentials.
    9. Click on + Create new Credentials > Service Account to create a new service account. Consider this as the HTTP client "bot" which will be used to authenticate requests from your server to your Google Calendar API to create your events + meet links.
    10. Once created, click on it, go to Keys > Add New Key > JSON, and store the obtained JSON at a secure place (e.g. path/to/your/access_data.json).
    11. Next, you have to grant domain-wide delegation to the service account you've created for everything to work. To do so, go back to the Details of the service account you've just created, click on Advanced Settings where you see Domain-Wide Delegation, and click on Open Google Admin Workspace Console. This will ask you to login into a Google Workspace Account; use the one you've created with 1. Note that you will need the Google Workspace Account's Username to login, not the email you've used when creating the account. That's normal.
    12. Within the Google Workspace Account, go to Security > Access and Data Controls > API Controls. Note that this menu may not show up, depending on how you created your Google Workspace Account; hence it is important to open the Google Workspace Admin Console via your Google Cloud Platform as explained in the previous step. This will make the API Controls link visible, if the menu wasn't present until now within your Google Workspace Account. Within there, follow the instructions written here.
    13. Next, go to your Google Calendar where you want to generate events for programmatically, and go to the Settings of that Calendar. Scroll down until you see sth like Grant Access To persons or groups, and add the username of your Google Workspace in there, for the action Make changes and manage accesses. Wait until its saved. If you don't do this, your API requests will result in a 404 (Not found) error.
    14. Scroll further down to Integrate Calendar, and write down the Calendar ID, which will be needed in the code in the next step. This may be an e-mail address, yes, don't be confused.
    15. Once that's done, to finally code your requests to your Google Calendar API, download the Google API Client library which suits your system. In my case, this was PHP, and a sample script which finally creates Google Calendar Events via the API + generates an according Google Meet link is:
    <?php
    declare(strict_types=1);
    
    namespace MyGoogleAPIStuff;
    
    use GoogleClient;
    use Google_Service_Calendar;
    use Google_Service_Calendar_Event;
    
    final class Calendar
    {
        
        /**
         * Generate New Google Calendar Event + related Google Meet Link
         *
         * @return string Google Meet Link of the scheduled event
         */
        
        public static function create_calendar_meeting_and_google_meet_link(): string
        {
            
            // Set up credentials for the Calendar API
            putenv(
                assignment: 'GOOGLE_APPLICATION_CREDENTIALS=path/to/your/access_data.json'
            );
            
            // Initiate API Client
            $client = new Client();
            $client->useApplicationDefaultCredentials();
            
            // Define Scope for which authorization is needed to execute event creation in Google Calendar API
            $client->addScope(
                scope_or_scopes: Google_Service_Calendar::CALENDAR
            );
            
            // Impersonate Google Workspace User to submit request to Google Calendar API, otherwise won't work
            $client->setSubject(
                subject: "Here, you have to enter your Google Workspace Account Username"
            );
            
            // Set up the Calendar API service
            $service = new Google_Service_Calendar(
                clientOrConfig: $client
            );
            
            // Create a new event on Google Calendar
            $event = new Google_Service_Calendar_Event(
                [
                    'summary'        => 'Meeting',
                    'start'          => [
                        'dateTime' => '2023-04-10T10:00:00-07:00',
                        'timeZone' => 'America/Los_Angeles',
                    ],
                    'end'            => [
                        'dateTime' => '2023-04-10T11:00:00-07:00',
                        'timeZone' => 'America/Los_Angeles',
                    ],
                    // Payload section needed to create a unique Google Meet link for the appointment
                    'conferenceData' => [
                        'createRequest' => [
                            'requestId'             => uniqid(),
                            'conferenceSolutionKey' => [
                                'type' => 'hangoutsMeet'
                            ]
                        ]
                    ],
                ]
            );
            
            // Insert the event into the targeted Google Calendar
            $event = $service->events->insert(
                calendarId: "Here, you have to enter your Google Calendar ID",
                postBody:   $event,
                optParams:  [ 'conferenceDataVersion' => 1 ] // To allow for Google Meet Link creation
            );
            
            // Retrieve the generated Google Meet link from the event's conference data
            return $event->getHangoutLink();
            
        }
        
    }
    

    What I've struggled with is that, if you do not provide the username of your Google Workspace Account to the line:

    $client->setSubject(
    subject: "Here, you have to enter your Google Workspace Account Username"
    );
    

    Even if you provide for example the e-mail address of your GCP account, or your GCP service account, you will get a 400 (Bad Request) Error back from the API, saying Invalid conference type value. This can be confusing, as it may be the case (which was the case for me) that a checkup of the concerence types which are allowed for your calendar, via this, still tells you that:

    ...
    "allowedConferenceSolutionTypes": [
       "hangoutsMeet"
      ]
    ...
    

    Meaning that the creation of Google Meet Links is allowed for the concerned calendar. Hence the error you get from the API, saying Invalid conference type value is inaccurate; the actual problem is that you're not impersonating the Google Workspace Account User which you used to implement the domain-wide delegation to the service account you're using to send the request. Really struggled with this one.

    Anyways, after you do all of the above, it should all be working. And again, this implementation will not execute for free. Many websites tell you that the use of the Google Calendar API is free of charge, even chatGPT etc. But to do what's explained in my step-by-step guide, you need an active Google Workspace subscription; I've verified that with a quick support session with Google. Just so you know.

    Happy scheduling!


  2. Try this to your code to set the conff data as follows to generate a Google Calendar appointment with an automatic Google Meet link using the Google API client for PHP

    <?php
    
    // Include the Google API client library
    require __DIR__ . '/vendor/autoload.php';
    
    // Set the path to your Google Cloud service account key file
    putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/service_account_key.json');
    
    // Create a new Google API client instance
    $client = new Google_Client();
    $client->useApplicationDefaultCredentials();
    $client->addScope(Google_Service_Calendar::CALENDAR);
    
    // Set up the Google Calendar API service
    $service = new Google_Service_Calendar($client);
    
    // Set the start and end times of the appointment
    $startDateTime = new DateTime('2023-04-07T10:00:00-07:00', new DateTimeZone('America/Los_Angeles'));
    $endDateTime = new DateTime('2023-04-07T11:00:00-07:00', new DateTimeZone('America/Los_Angeles'));
    
    // Create a new Google Calendar event with the specified start and end times
    $event = new Google_Service_Calendar_Event([
        'summary' => 'Meeting',
        'start' => [
            'dateTime' => $startDateTime->format(DateTime::RFC3339),
            'timeZone' => $startDateTime->getTimezone()->getName(),
        ],
        'end' => [
            'dateTime' => $endDateTime->format(DateTime::RFC3339),
            'timeZone' => $endDateTime->getTimezone()->getName(),
        ],
    ]);
    
    // Set the event's conference data to use Google Meet
    $conferenceRequest = new Google_Service_Calendar_CreateConferenceRequest();
    $conferenceRequest->setRequestId(uniqid());
    $conferenceSolutionKey = new Google_Service_Calendar_ConferenceSolutionKey();
    $conferenceSolutionKey->setType('hangoutsMeet');
    $conferenceRequest->setConferenceSolutionKey($conferenceSolutionKey);
    $conferenceData = new Google_Service_Calendar_ConferenceData();
    $conferenceData->setCreateRequest($conferenceRequest);
    $event->setConferenceData($conferenceData);
    
    // Insert the event into the user's calendar
    $calendarId = 'primary'; // Change this to the calendar ID you want to use
    $event = $service->events->insert($calendarId, $event, ['conferenceDataVersion' => 1]);
    
    // Retrieve the generated Google Meet link from the event's conference data
    $meetLink = $event->getHangoutLink();
    
    // Output the generated Google Meet link
    echo 'Google Meet link: ' . $meetLink;
    

    Note:

    1. plz change /path/to/service_account_key.json
    2. Also Enter $calendarId value.

    You have to install the Dipendency .. by composer require google/apiclient

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search