How to Create a Meeting on Zoom using Zoom API and PHP

Recently I was working on a project where I needed to interact with the Zoom API. In the client’s application, we implemented a lot of stuff using Zoom API like Accounts, Billing, Meetings, Groups, Reports, Rooms, etc. Though we did many things with the Zoom API, the main task was creating meetings. If you are the one who’s looking for the same then you are at the right place. In this tutorial, I will discuss how to create a meeting using Zoom API and PHP.

As we all know, Zoom is a platform used for teleconferencing, telecommuting, distance education, etc. It is popular for online conferences, meetings, and webinars.

Those who are looking to create meetings through Zoom API need to build the OAuth flow. OAuth provides a high level of security to make interactions with third-party services. In this post, I’ll build the OAuth process to interact with the Zoom API.

Create an OAuth App on Zoom

Once you have your Zoom account, you have to create an OAuth app on Zoom using the below steps.

  • Register your app on Zoom APP Marketplace.
  • Upon registering an app, you will get your generated credentials. Here you need to pass Redirect URL for OAuth and Whitelist URL.
  • In the next step, enter the basic information about your app.
  • You can optionally enable some additional features such as Event Subscriptions and Chat Subscriptions for your app.
  • Under the ‘Scopes’ tab, you need to add scopes regarding your app. For example, you can add a scope for Zoom meetings.

If you are on localhost then use the ngrok and generate the local URL. In my case, ngrok URLs for OAuth redirection and a Whitelist URL are shown below.

App Credentials

If you are facing any issues with creating an OAuth app please refer to Zoom’s official documentation on Create an OAuth App.

Basic Setup and Configuration

I didn’t find any PHP package which can be used to interact with the Zoom API. So I decided to build the solution from scratch. After doing some back and forth I managed it through the Guzzle library and Zoom REST API. The Guzzle package is useful to handle HTTP requests and responses.

Install the Guzzle library using the command:

composer require guzzlehttp/guzzle

The interaction with Zoom REST API requires to have an access token. We are going to generate and store it in the database. The access token is valid for a short period of time. In our code, we will regenerate the access token in the background so that the user doesn’t need to do the authorization process again.

Let’s create a zoom_oauth table using the below SQL.

CREATE TABLE `zoom_oauth` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `provider` varchar(255) NOT NULL,
 `provider_value` text NOT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

As we will require to fetch token values from the database, it needs to write a code for it. Create a file class-db.php and add the code below to it.

class-db.php

<?php
class DB {
    private $dbHost     = "DB_HOST";
    private $dbUsername = "DB_USERNAME";
    private $dbPassword = "DB_PASSWORD";
    private $dbName     = "DB_NAME";
 
    public function __construct(){
        if(!isset($this->db)){
            // Connect to the database
            $conn = new mysqli($this->dbHost, $this->dbUsername, $this->dbPassword, $this->dbName);
            if($conn->connect_error){
                die("Failed to connect with MySQL: " . $conn->connect_error);
            }else{
                $this->db = $conn;
            }
        }
    }
 
    public function is_table_empty() {
        $result = $this->db->query("SELECT id FROM zoom_oauth WHERE provider = 'zoom'");
        if($result->num_rows) {
            return false;
        }
 
        return true;
    }
 
    public function get_access_token() {
        $sql = $this->db->query("SELECT provider_value FROM zoom_oauth WHERE provider = 'zoom'");
        $result = $sql->fetch_assoc();
        return json_decode($result['provider_value']);
    }
 
    public function get_refresh_token() {
        $result = $this->get_access_token();
        return $result->refresh_token;
    }
 
    public function update_access_token($token) {
        if($this->is_table_empty()) {
            $this->db->query("INSERT INTO zoom_oauth(provider, provider_value) VALUES('zoom', '$token')");
        } else {
            $this->db->query("UPDATE zoom_oauth SET provider_value = '$token' WHERE provider = 'zoom'");
        }
    }
}

Make sure to replace the placeholders with your actual database credentials. Next, let’s generate an access token following the OAuth standard.

Generate an Access Token

The user can create an access token for their account using the App credentials and OAuth flow. Create a config.php file that will contain your app credentials, and redirect URL. Also include the DB class and Guzzle package as follows.

config.php

<?php
require_once 'vendor/autoload.php';
require_once "class-db.php";
 
define('CLIENT_ID', 'YOUR_CLIENT_ID');
define('CLIENT_SECRET', 'YOUR_CLIENT_SECRET');
define('REDIRECT_URI', 'REDIRECT_URL_FOR_OAUTH');

Replace the placeholders with your app credentials. Set the same redirect URL you added in the Zoom OAuth app. In my case, the redirect URL is https://f2448150.ngrok.io/zoom/callback.php. It means in the callback.php file, we have to write the code which calls a Zoom API, fetches an access token, and stores it in the database.

callback.php

<?php
require_once 'config.php';
 
try {
    $client = new GuzzleHttp\Client(['base_uri' => 'https://zoom.us']);
 
    $response = $client->request('POST', '/oauth/token', [
        "headers" => [
            "Authorization" => "Basic ". base64_encode(CLIENT_ID.':'.CLIENT_SECRET)
        ],
        'form_params' => [
            "grant_type" => "authorization_code",
            "code" => $_GET['code'],
            "redirect_uri" => REDIRECT_URI
        ],
    ]);
 
    $token = json_decode($response->getBody()->getContents(), true);
 
    $db = new DB();
    $db->update_access_token(json_encode($token));
    echo "Access token inserted successfully.";
} catch(Exception $e) {
    echo $e->getMessage();
}

Now, let’s generate an authorization URL where a user can click and complete the authorization. I am going to create this URL in the index.php file.

index.php

<?php
require_once 'config.php';
 
$url = "https://zoom.us/oauth/authorize?response_type=code&client_id=".CLIENT_ID."&redirect_uri=".REDIRECT_URI;
?>
 
<a href="<?php echo $url; ?>">Login with Zoom</a>

Run the above file on the browser, click on the ‘Login with Zoom’ link and complete the authorization. On successful authentication, you should see a success message and the access token would store in your zoom_oauth table. If it works, we can go ahead and create a meeting with the Zoom API.

Create a Meeting on Zoom using Zoom API

Zoom is providing an endpoint for creating a meeting through their REST API. You may read about it in their documentation. It requires sending a POST request to the given endpoint along with the required parameters.

In addition to the parameters, you must send an access token in the Authorization header. As I said earlier, the access token has a short life and we are going to regenerate it in the background. This can be handled using the refresh token which we already got during authentication.

I have created a create-meeting.php file for sending a POST request which will create a Zoom meeting. I also handled the condition of token expiry and regenerated it if expired.

create-meeting.php

<?php
require_once 'config.php';
 
function create_meeting() {
    $client = new GuzzleHttp\Client(['base_uri' => 'https://api.zoom.us']);
 
    $db = new DB();
    $arr_token = $db->get_access_token();
    $accessToken = $arr_token->access_token;
 
    try {
        $response = $client->request('POST', '/v2/users/me/meetings', [
            "headers" => [
                "Authorization" => "Bearer $accessToken"
            ],
            'json' => [
                "topic" => "Let's learn Laravel",
                "type" => 2,
                "start_time" => "2023-05-05T20:30:00",
                "duration" => "30", // 30 mins
                "password" => "123456"
            ],
        ]);
 
        $data = json_decode($response->getBody());
        echo "Join URL: ". $data->join_url;
        echo "<br>";
        echo "Meeting Password: ". $data->password;
 
    } catch(Exception $e) {
        if( 401 == $e->getCode() ) {
            $refresh_token = $db->get_refresh_token();
 
            $client = new GuzzleHttp\Client(['base_uri' => 'https://zoom.us']);
            $response = $client->request('POST', '/oauth/token', [
                "headers" => [
                    "Authorization" => "Basic ". base64_encode(CLIENT_ID.':'.CLIENT_SECRET)
                ],
                'form_params' => [
                    "grant_type" => "refresh_token",
                    "refresh_token" => $refresh_token
                ],
            ]);
            $db->update_access_token($response->getBody());
 
            create_meeting();
        } else {
            echo $e->getMessage();
        }
    }
}
 
create_meeting();

If you noticed the code, I have passed “2023-05-05T20:30:00” as a ‘start_time’. It means the meeting time will be 5 May 2023, 08:30 PM. The user should use the same format as yyyy-MM-ddTHH:mm:ss. For the ‘type’ key I passed the value ‘2’ which is for a Scheduled meeting. I also set a meeting password to ‘123456’.

Go ahead and run this code and you should see your meeting is created on your Zoom account.

Update Zoom Meeting

For some reason, if you want to update the meeting’s information, send a PATCH request along with the details that need to be updated. Refer to the below code to update your Zoom meeting. For more information, you may read the documentation.

<?php
require_once 'config.php';
  
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.zoom.us']);
  
$db = new DB();
$arr_token = $db->get_access_token();
$accessToken = $arr_token->access_token;
  
$response = $client->request('PATCH', '/v2/meetings/{meeting_id}', [
    "headers" => [
        "Authorization" => "Bearer $accessToken"
    ],
    'json' => [
        "topic" => "Let's Learn WordPress",
        "type" => 2,
        "start_time" => "2023-05-20T10:30:00",
        "duration" => "45", // 45 mins
        "password" => "123456"
    ],
]);
 
if (204 == $response->getStatusCode()) {
    echo "Meeting is updated successfully.";
}

Once the meeting is updated, Zoom returns 204 as the HTTP status code.

List Zoom Meetings

We have written a code for creating and updating Zoom meetings. After this, you may want to list all meetings in your application.

Zoom provides an API through which you can fetch all Zoom meetings. Create a file list-meeting.php and use the code below which will print all meetings.

list-meeting.php

<?php
require_once 'config.php';

function list_meetings($next_page_token = '') {
    $db = new DB();
    $arr_token = $db->get_access_token();
    $accessToken = $arr_token->access_token;

    $client = new GuzzleHttp\Client(['base_uri' => 'https://api.zoom.us']);
  
    $arr_request = [
        "headers" => [
            "Authorization" => "Bearer $accessToken"
        ]
    ];
 
    if (!empty($next_page_token)) {
        $arr_request['query'] = ["next_page_token" => $next_page_token];
    }
 
    $response = $client->request('GET', '/v2/users/me/meetings', $arr_request);
     
    $data = json_decode($response->getBody());
 
    if ( !empty($data) ) {
        foreach ( $data->meetings as $d ) {
            $topic = $d->topic;
            $join_url = $d->join_url;
            echo "<h3>Topic: $topic</h3>";
            echo "Join URL: $join_url";
        }
 
        if ( !empty($data->next_page_token) ) {
            list_meetings($data->next_page_token);
        }
    }
}
 
list_meetings();

In the above code, I am printing a topic and URL of meetings. You may also display other information. Print the variable $data to get a list of available details.

While fetching meetings Zoom return 30 records in a single call. Using the value of next_page_token you can fetch the next set of records as shown in the code.

Get Past Meeting Participants

Once the meeting is over, you can get a list of participants using the Zoom API. It is necessary to have a paid account to call this API. If you try to call this API with a free account you would get an error.

<?php
require_once 'config.php';
  
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.zoom.us']);
  
$db = new DB();
$arr_token = $db->get_access_token();
$accessToken = $arr_token->access_token;
  
$response = $client->request('GET', '/v2/past_meetings/MEETING_ID/participants', [
    "headers" => [
        "Authorization" => "Bearer $accessToken"
    ]
]);
 
$data = json_decode($response->getBody());
if ( !empty($data) ) {
    foreach ( $data->participants as $p ) {
        $name = $p->name;
        $email = $p->user_email;
        echo "Name: $name";
        echo "Email: $email";
    }
}

Replace the placeholder MEETING_ID with the actual meeting id. In the response, you will get the names and emails of the participants.

Delete a Meeting

You can delete a meeting by sending a DELETE request to the API endpoint. To this endpoint, you should pass your meeting id as shown below.

<?php
require_once 'config.php';
 
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.zoom.us']);
 
$db = new DB();
$arr_token = $db->get_access_token();
$accessToken = $arr_token->access_token;
 
$response = $client->request('DELETE', '/v2/meetings/{meeting_id}', [
    "headers" => [
        "Authorization" => "Bearer $accessToken"
    ]
]);

if (204 == $response->getStatusCode()) {
    echo "Meeting is deleted.";
}

I hope you got to know how to create a meeting using Zoom API and PHP. We also covered a few more topics like how to update, list and delete meetings. I would like to hear your thoughts and suggestions in the comment section below.

Related Articles

If you liked this article, then please subscribe to our YouTube Channel for video tutorials.

52 thoughts on “How to Create a Meeting on Zoom using Zoom API and PHP

  1. We registered oAuth as a User Managed app and tested it with the sample source above.
    After registering the Zoom oAuth Draft account and developing oAuth authentication, the owner account has been successfully registered and the Token has been received, but other users cannot be registered.
    When registering another user, the message “OOPS It may have been deleted or you don’t have permission to view it right now” appears and the registration fails.
    Is it not possible to register other users in the Draft account? Can I register other users only after being officially registered on the Marketplace?
    Or is there something wrong with my oAuth setup options?
    Waiting for expert’s answer.

    1. I wrote this article considering a single user gonna create the meeting. If you wish it for multiple users, you have to modify a code in the class-db.php file. Also, logic should be adjusted in respective files in order to get the specific user from the list.

      1. Now, it has nothing to do with single-user or multi-user.
        When authenticating with the owner account, it is normal, but when authenticating with another account, an authentication error occurs on the Zoom screen before returning the sample source.
        When authentication is requested, the Zoom screen appears, and when another account user logs in, “Oops, It may have been deleted or you don’t have permission to view it right now” appears on the Zoom screen.
        In case of an authentication error, it is terminated without returning to the Callback.
        I’m not sure what permissions are required.
        I wonder if it is because the Zoom oAuth account is a beta account or if there is a problem with the account settings.
        Or is it possible to register another personal account only when it is formally posted on Zoom MarketPlace?
        When registering oAuth, the same error occurs even if you register with a private account.
        Please let me know if there is any information you know.
        thank you

  2. hi, nice post very useful and very clear.
    My question now is:
    If I want to start your php functionalities from a wordpress page, how can I do?

    Thanx in advance
    Dino

  3. is there any option to let user join the zoom meeting in our website and remain in our website. he will join meeting using our own authentication. if he passed our authentication, we will let him join the meeting with name and email we already had in our database.. the users can join only through our website…

  4. get past meeting participants:
    how to get the name , email , leave time , join time and duration of the meeting ? i tried editing the code but it is not working.

  5. Thanks for providing these examples, I have been able to get the PHP scripts to run on a Synology NAS with the OAuth being correctly authorised and the token added to an mysql database. I don’t know a lot about PHP but was wondering if there is an error in the code. The database is checked and only updated if empty, but the code is calls will add if empty and update if a token exists. I removed the if statement from my code and it still works and appears to always correctly update the token on my limited tests.

    1. I got it, here is the code, just replace {meetingId} = the number of meeting

      require_once ‘config.php’;

      $client = new GuzzleHttp\Client([‘base_uri’ => ‘https://api.zoom.us’]);

      $db = new DB();
      $arr_token = $db->get_access_token();
      $accessToken = $arr_token->access_token;

      $response = $client->request(‘GET’, ‘/v2/report/meetings/{mettingId}/participants’, [
      “headers” => [
      “Authorization” => “Bearer $accessToken”
      ]
      ]);

      $data = json_decode($response->getBody());
      //echo $data . ”;
      if (!empty($data)) {
      foreach ($data->participants as $d) {
      $email = $d->user_email;
      $nombre = $d->name;
      $join_time = $d->join_time;
      $leave_time = $d->leave_time;
      $duration = $d->duration;
      echo “Email: $email”;
      echo “join time: $join_time “;
      echo “Leave time: $leave_time”;
      echo “Duration: $duration”;
      }
      }

  6. Hi sir, can you provide any idea on how can I view list of created meeting/s on my PHP website? Many thanks in advance.

      1. Thank you very much for responding quickly sir, I would like to ask if this is applicable on live php website so I could add, edit, delete and update meetings, can I use a basic zoom account for meetings or do I need pro account? for the live website, I would just change the URL with the domain right? Right now I am using Codeigniter, would like to apply this example. Thanks in advance, God bless and stay safe sir.

        1. For this article, I am using a free version of Zoom. It allows managing meetings through APIs.
          In the case of live site, yes you have to use your live domain.

          1. Thanks very much, sorry for the late reply. I have another question, how about creating a webinar? is the procedure the same? will just change the type? thanks!

          2. Got it thanks! how about getting the list of meetings (upcoming meetings) only, where could I add the parameter to filter only upcoming? thanks much!

    1. you need create a helper whit all the functions, something like this.

      class zoomHelper
      {
      public static $accessToken = ‘XXXXXXX’;
      public static $urlzoom = ‘https://api.zoom.us’;

      static public function listMeetings(){
      $url = self::$urlzoom . ‘/v2/users/me/meetings’;
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_HTTPHEADER, array(‘Content-Type: application/json’,
      ‘Authorization: Bearer ‘. self::$accessToken));
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
      curl_setopt($ch, CURLOPT_HEADER, FALSE);
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      $response = curl_exec($ch);
      curl_close($ch);
      return $response;
      }

      static public function createMeeting($params) {
      $url = self::$urlzoom . ‘/v2/users/me/meetings’;
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_HTTPHEADER, array(‘Content-Type: application/json’,
      ‘Authorization: Bearer ‘. self::$accessToken));
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
      curl_setopt($ch, CURLOPT_HEADER, FALSE);
      curl_setopt($ch, CURLOPT_POST, 1);
      curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      $response = curl_exec($ch);
      curl_close($ch);
      return $response;
      }

      static public function updateMeeting($params,$meetingid){
      $url = self::$urlzoom . ‘/v2/meetings/’.$meetingid;
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_HTTPHEADER, array(‘Content-Type: application/json’,
      ‘Authorization: Bearer ‘. self::$accessToken));
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
      curl_setopt($ch, CURLOPT_HEADER, FALSE);
      curl_setopt($ch, CURLOPT_CUSTOMREQUEST, ‘PATCH’);
      curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      $response = curl_exec($ch);
      curl_close($ch);
      return $response;
      }

      static public function deleteMeeting($meetingid){
      $url = self::$urlzoom . ‘/v2/meetings/’.$meetingid;
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_HTTPHEADER, array(‘Content-Type: application/json’,
      ‘Authorization: Bearer ‘. self::$accessToken));
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
      curl_setopt($ch, CURLOPT_HEADER, FALSE);
      curl_setopt($ch, CURLOPT_CUSTOMREQUEST, “DELETE”);
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      $response = curl_exec($ch);
      curl_close($ch);
      return $response;
      }

      static public function meetingParticipants($meetingid){
      $url = self::$urlzoom . ‘/v2/past_meetings/’.$meetingid.’/participants’;
      //$url = self::$urlzoom . ‘/v2/report/meetings/’.$meetingid.’/participants’;
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_HTTPHEADER, array(‘Content-Type: application/json’,
      ‘Authorization: Bearer ‘. self::$accessToken));
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
      curl_setopt($ch, CURLOPT_HEADER, FALSE);
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      $response = curl_exec($ch);
      curl_close($ch);
      return $response;
      }

      then in your controller you can call the helper like this (this one bring all meetings in your account)
      $data = json_decode(zoomHelper::listMeetings());

      this use a jwt token created in https://marketplace.zoom.us/user/dashboard

  7. after a moment later token will be invalid, that’s why in class-db.php files there is a update_access_token method. but it won’t work because in callback.php 22 th line, it check if table is empty or not, but when table is not empty then this code line is not working. you need to update it.

  8. I am looking for some assistance in creating a PHP/MySQL interface to create Zoom meetings. Are you able to assist on contract?

  9. Hi, what if im just give the ability to a user to create a meeting without being login into the zoom account is this possible?

    1. Exactly the question I have been asking.. Can Zoom see the user as my app and allow access without asking for login

  10. Given the situation all over the world due to covid, there is a need for may of us to include zoom into web apps.
    So it will be very kind of you to train us on a full-fledged application to start meeting , join meeting etc in this portal.

    Thanks

  11. What meeting “type”: 2 means ? Does it means that I have to upgrade to payed Zoom account in order to do this code ?

  12. Thanks for this Tutorial. I would love see a tutorial about a meeting registrants with the zoom API.

  13. Hi

    – i want to create users with same host its possible,if yes can create a php example for me . how to do this
    – any help?
    Thanks

  14. I am facing problem with access_token. I am able to create a meeting and stream it too. But When i first time click on login with Zoom it’s create access_token according to callback.php when token table has no row.
    When it’s have a row and am trying to generate another meeting it’s generate error.

    Error be like

    “lient error: `POST https://zoom.us/oauth/token` resulted in a `400 Bad Request` response:
    {“reason”:”Invalid authorization code TErvZKNMeZ_GWbcLEGxR3Wfz_CAUuPTeQ”,”error”:”invalid_request”}”

    What to do ?

    1. The purpose of this tutorial is authorize a user once and then using a refresh_token regenerate its access token in the background.

      You don’t need to authorize the account again. And if you want it, first you need to remove a previous entry and then run authorization.

      1. The refresh works only for a certain amount of time. After a while you get a white page when creating a meeting with create-meeting.php. When it stops working you then have to delete the token record in the mysql table and generate a new one from the link in index.php. Has anyone found a work around for this?

  15. Hello. I am getting this error
    Fatal error: Uncaught Error: Class ‘GuzzleHttp\Client’ not found in

    1. Hi Diantha

      – this error occurs bcoz cofig.php cannot connect with guzzle folder. create vendor folder and paste all extracted guzzle folder

      Thanks.

  16. Hi Sajid,

    – Thanks for your pretty reply.

    – But can you confirm for me? if I want to create the room [ API zoom PHP ] then I will become a pro member of zoom?

  17. Can you help me? I’m trying to trying to rewrite thc “class-db” code, using postgresql!

Leave a Reply

Your email address will not be published. Required fields are marked *