<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Elisa Levet</title>
    <description>The latest articles on DEV Community by Elisa Levet (@elisalevet).</description>
    <link>https://dev.to/elisalevet</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F848160%2Fa7a15df8-8f0a-4c6a-87a3-8e9636cd5f3e.jpg</url>
      <title>DEV Community: Elisa Levet</title>
      <link>https://dev.to/elisalevet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/elisalevet"/>
    <language>en</language>
    <item>
      <title>Using ngrok to receive Zoom Events</title>
      <dc:creator>Elisa Levet</dc:creator>
      <pubDate>Tue, 31 Jan 2023 00:37:39 +0000</pubDate>
      <link>https://dev.to/zoom/using-ngrok-to-receive-zoom-events-235m</link>
      <guid>https://dev.to/zoom/using-ngrok-to-receive-zoom-events-235m</guid>
      <description>&lt;p&gt;Zoom utilizes &lt;strong&gt;webhooks&lt;/strong&gt; as a medium to notify third-party applications about events that occur in a Zoom account.&lt;/p&gt;

&lt;p&gt;So, instead of making repeated calls to pull data from the Zoom API, you can use webhooks to get information on events that happen in a Zoom account. &lt;/p&gt;

&lt;p&gt;This guide covers how to implement Zoom webhooks using &lt;a href="https://ngrok.com/docs/" rel="noopener noreferrer"&gt;&lt;strong&gt;ngrok&lt;/strong&gt;&lt;/a&gt; as our secure URL to receive events from our Zoom account.&lt;/p&gt;

&lt;p&gt;We will use our &lt;a href="https://github.com/zoom/webhook-sample-node.js" rel="noopener noreferrer"&gt;Webhook-sample-node.js&lt;/a&gt; app available on Gitbhub and we will be setting up a sample app in the &lt;a href="https://marketplace.zoom.us/" rel="noopener noreferrer"&gt;Zoom Marketplace&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Setting up our local environment&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Install the sample app by running the following commands in your terminal:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;git&lt;/span&gt; &lt;span class="nx"&gt;clone&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//github.com/zoom/webhook-sample-node.js.git&lt;/span&gt;
&lt;span class="nx"&gt;cd&lt;/span&gt; &lt;span class="nx"&gt;webhook&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;sample&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;
&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a .env file by running the following command in your terminal:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;touch&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And add the following placeholder&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ZOOM_WEBHOOK_SECRET_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Do not run your app quite yet, we need to head to the &lt;a href="https://marketplace.zoom.us/" rel="noopener noreferrer"&gt;Zoom Marketplace&lt;/a&gt; and create an app first!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Create an app in the Marketplace.&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For this tutorial will use a &lt;em&gt;Webhook only app&lt;/em&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to your Zoom account and head to the Zoom Marketplace&lt;/li&gt;
&lt;li&gt;Click the dropdown that says Develop and then click Build App&lt;/li&gt;
&lt;li&gt;Select the Webhook Only app and give your app a name&lt;/li&gt;
&lt;li&gt;Make sure to fill up the required Basic information (Company name, your name, email address)&lt;/li&gt;
&lt;li&gt;On the Feature tab, copy the &lt;strong&gt;Secret Token&lt;/strong&gt; and paste it in your &lt;strong&gt;.env&lt;/strong&gt; file, under the &lt;strong&gt;ZOOM_WEBHOOK_SECRET_TOKEN&lt;/strong&gt; placeholder; this value will be used later to validate our endpoint URL and to start receiving event notifications.&lt;/li&gt;
&lt;li&gt;Click the Event subscription slider and click on Add Event Subscritions, give your subscription a name; and Add events to your app to subscribe, for example: 

&lt;ul&gt;
&lt;li&gt;Meeting has been created&lt;/li&gt;
&lt;li&gt;Meeting has been updated &lt;/li&gt;
&lt;li&gt;Meeting has been deleted.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; for now, we will be skipping the Event Notification Endpoint URL Validation, but we will come back to this later in the guide.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3: Running our sample app and ngrok&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Make sure you’ve saved your project after copying the value of the &lt;strong&gt;Secret Token&lt;/strong&gt; from our newly created Webhook Only sample app, into the .env file.&lt;/p&gt;

&lt;p&gt;Now you can run your app with the following command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that your app is running locally on &lt;strong&gt;port 4000&lt;/strong&gt;, we need to expose our local server to the internet to accept post requests, we will be using ngrok for this. (If you are not an ngrok user yet, just sign up for &lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;ngrok for free&lt;/a&gt; and follow the steps to install ngork and connect your account) &lt;/p&gt;

&lt;p&gt;Start your ngrok by running the following command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ngrok&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the ngrok https url displayed in the terminal and paste it in your Event notification url input, followed by the &lt;strong&gt;/webhook&lt;/strong&gt; path, (it should look like this &lt;a href="https://exampleurl.ngrok.io/webhook" rel="noopener noreferrer"&gt;https://exampleurl.ngrok.io/webhook&lt;/a&gt;)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 4: Validate your endpoint URL&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Once you have added your ngrok https url followed by /webhook path, we will have to validate our endpoint. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the &lt;strong&gt;Validate&lt;/strong&gt; button and expect to get a “Validated” message.&lt;/li&gt;
&lt;li&gt;Click on Save and then Continue to make sure that your app is activated on the account and that it’s ready to receive events from Zoom.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhuzyi2g24paq8cuo7oi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhuzyi2g24paq8cuo7oi.jpg" alt="Screenshot from Marketplace validation" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;What happened here is:&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
Zoom requires to &lt;a href="https://marketplace.zoom.us/docs/api-reference/webhook-reference/#validate-your-webhook-endpoint" rel="noopener noreferrer"&gt;manually trigger the webhook validation&lt;/a&gt; when you add a new event or make changes to an existing one. &lt;/p&gt;

&lt;p&gt;Zoom uses a &lt;em&gt;challenge-response check&lt;/em&gt; (CRC) for webhook validation and when you click the &lt;strong&gt;Validate&lt;/strong&gt; button in your app, a &lt;strong&gt;CRC&lt;/strong&gt; occurs and Zoom will make a POST request to your https endpoint with a challenge-request body and your app will need to response with the challenge response and a 200 or 204 HTTP response code, within 3 seconds&lt;/p&gt;

&lt;p&gt;This is how the CRC event sent by Zoom looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payload&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;plainToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;qgg8vlvZRS6UYooatFL8Aw&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;event_ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1654503849680&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;endpoint.url_validation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our sample app, will be listening to the event ‘ednpoint.url_validation’ and once it receives it, it we will create a HMAC SHA-256 hash using the Zoom webhook secret token and the plainToken received in the CRC request body; to lastly respond to Zoom with a Challenge Response and 200 status&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;endpoint.url_validation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hashForValidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ZOOM_WEBHOOK_SECRET_TOKEN&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plainToken&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;plainToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plainToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encryptedToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hashForValidate&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Challenge response body example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;plainToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;qgg8vlvZRS6UYooatFL8Aw&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encryptedToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;23a89b634c017e5364a15599e2bb04bb1558d9c3a121faa5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Step 5: Receiving events in our sample app&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Head to your Zoom account and trigger the respective Webhook, in this case as we chose the Meeting has been created event, you will have to schedule a Zoom meeting to be able to receive the event notification.&lt;/p&gt;

&lt;p&gt;Once you create a meeting, you will see the Webhook headers and payload logged in terminal, something like this:&lt;/p&gt;

&lt;p&gt;Request headers&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;host&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db05-2603-7000-8f03-fe4c-b56e-6f3b-bded-7b6d.ngrok.io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user-agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Zoom Marketplace/1.0a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-length&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;564&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{LEGACY_WEBHOOK_VERIFICATION_TOKEN}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clientid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{clientID}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json; charset=utf-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-forwarded-for&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{X_FORWARDED_FOR}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-forwarded-proto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-zm-request-timestamp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{X_ZM_REQUEST_TIMESTAMP}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-zm-signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{X_ZM_SIGNATURE}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-zm-trackingid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{X_ZM_TRACKINGID}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accept-encoding&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gzip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response body&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;meeting.created&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payload&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;account_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{ACCOUNT_ID}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;operator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{EMAIL}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;operator_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{OPERATOR_ID}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{MEETING_UUID}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{MEETING_ID}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;host_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{HOST_ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;topic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Meeting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;start_time&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2023-01-26T21:00:00Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;duration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timezone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;America/New_York&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;join_url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{JOIN_URL}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{PASSWORD}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;settings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
         &lt;span class="p"&gt;],&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;is_simulive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;event_ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1674764224723&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What happened here is:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After receiving the webhook event, the sample app constructed  a message string with &lt;em&gt;“v0”&lt;/em&gt;, the webhook request header &lt;em&gt;“x-zm-request-timestamp”&lt;/em&gt; value and the webhook request body&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`v0:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-zm-request-timestamp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the app constructed the message, it created a hash message, by setting the webhook’s secret token as the secret/salt and the message as the string to hash and outputed in hex format&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hashForVerify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ZOOM_WEBHOOK_SECRET_TOKEN&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Created a signature using the hashed Message by prepending "v0=" to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`v0=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;hashForVerify&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compared the signature that we created with the value from our request header &lt;em&gt;“x-zm-signature”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the value of &lt;strong&gt;signature&lt;/strong&gt; matches with the value of the &lt;strong&gt;request header “x-zm-signature”&lt;/strong&gt;, we will start receiving events.&lt;/p&gt;

&lt;p&gt;You should now have a running app enabled to receive events in a secure URL.&lt;/p&gt;

&lt;p&gt;It is important to remember that every time that you make changes your App in the Marketplace (adding new events or restarting our ngrok secure tunnel) you will have to validate the endpoint URL again.&lt;/p&gt;

&lt;p&gt;Thank you for follow along and happy coding! 🙂 &lt;/p&gt;

</description>
      <category>crypto</category>
      <category>blockchain</category>
      <category>offers</category>
      <category>web3</category>
    </item>
    <item>
      <title>Setting up a Postgres database to receive Zoom webhooks</title>
      <dc:creator>Elisa Levet</dc:creator>
      <pubDate>Thu, 15 Sep 2022 00:15:59 +0000</pubDate>
      <link>https://dev.to/zoom/setting-up-a-postgres-database-to-receive-zoom-webhooks-1lj1</link>
      <guid>https://dev.to/zoom/setting-up-a-postgres-database-to-receive-zoom-webhooks-1lj1</guid>
      <description>&lt;p&gt;Thank you for joining us again for part two of this series. As a recap, in the last section we talked about &lt;a href="https://dev.to/elisalevet/sending-webhooks-to-a-postgres-database-3j27-temp-slug-4211231/edit"&gt;webhooks&lt;/a&gt;, what are they, why use them and gave some real life examples. &lt;/p&gt;

&lt;p&gt;I have decided to use a webhook-only app for this example because the setup is simple, and event subscriptions are all we'll need. You can also use webhooks with OAuth apps and the Server-to-Server OAuth apps if you need to make API requests. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a webhook-only app in the Zoom App Marketplace:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log in to your Zoom account and head to the &lt;a href="https://marketplace.zoom.us/" rel="noopener noreferrer"&gt;Zoom App Marketplace&lt;/a&gt; to start.&lt;/li&gt;
&lt;li&gt;Click the dropdown that says Develop and then click Build App:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufpt1x3skvhe7vi21ixr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufpt1x3skvhe7vi21ixr.png" alt="Zoom App Marketplace"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make sure to click Create under the tile that says Webhook Only and give your app a name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhfiygw7slpuijkv3q4eg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhfiygw7slpuijkv3q4eg.png" alt="Webhook Only app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill up the required information and once that's done, head to the Feature tab and make sure to enable event subscriptions. Give your event subscription a name and in the event notification endpoint URL, make sure to add your ngrok URL. We need to expose the local server to the internet to accept post requests from Zoom.&lt;/p&gt;

&lt;p&gt;To get an event notification endpoint URL for this guide, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ngrok http 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the ngrok https url displayed in terminal and include /webhook path, it should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://exampleurl.ngrok.io/webhook" rel="noopener noreferrer"&gt;https://exampleurl.ngrok.io/webhook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtawpyff9zgqd6617ly8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtawpyff9zgqd6617ly8.png" alt="Enabling subscriptions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on Add events. For this guide, we will only add "Meeting has been created" and "Meeting has been deleted" and click done.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnw2yylv1hgdzrrv4dllg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnw2yylv1hgdzrrv4dllg.png" alt="Event Types"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, click Continue so you can activate your app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set up the sample app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pre-requisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Node JS&lt;/li&gt;
&lt;li&gt;Ngrok&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;Zoom Account&lt;/li&gt;
&lt;li&gt;Webhook-only app credentials &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that your webhook-only app is created and activated in the Zoom Marketplace, it is time to run the sample app in your local environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup and install&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, clone the git repository found here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/zoom/webhook-to-postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use NPM to install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the .env file in your text editor and enter the following information from the feature section that you just configured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Zoom Secret token from your Webhook only app
ZOOM_WEBHOOK_SECRET_TOKEN=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create our Database&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can skip these steps if you already have Postgres installed in your local environment. If not, follow along to install pg and create a table in your database.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you are using Windows, download a &lt;a href="https://www.postgresql.org/download/windows/" rel="noopener noreferrer"&gt;Windows installer of PostgreSQL&lt;/a&gt;, or you can use this &lt;a href="https://github.com/garethflowers/postgresql-portable" rel="noopener noreferrer"&gt;PostgreSQL Portable Copy&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If you are using a Mac and you have Homebrew installed on your computer, open up the terminal and install PostgreSQL with brew:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install postgresql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the installation is complete, we'll want to get PostgreSQL up and running, which we can do with services start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew services start postgresql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With PostgreSQL now installed, we will connect to the default Postgres database running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;psql postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are now inside psql in the Postgres database. You will see the prompt ends with an # to denote that we are logged in as the superuser, or root (postgres=#).&lt;br&gt;
Commands within psql start with a backslash. To test this, we can check what database, user, and port we have connected to using the \conninfo command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;postgres=# \conninfo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Creating a role in Postgres&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We are going to create a role called "me" and give it a password of "password":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;postgres=# CREATE ROLE me WITH LOGIN PASSWORD 'password';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we want "me" to be able to create a database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;postgres=# ALTER ROLE me CREATEDB;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we will create a database for the "me" user. Exit from the default session with \q for quit and now we will connect Postgres with "me"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;psql -d postgres -U me
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can create a database with the SQL command as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE DATABASE zoomwebhooks;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect to the new database&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\c zoomwebhooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are now connected to database "zoomwebhooks" as a user "me".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating a table in Postgres&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Finally, in the psql command prompt, we will create a table called events with four fields, two VARCHAR types, one BIGINT type, and an auto-incrementing PRIMARY KEY ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE events (
  ID SERIAL PRIMARY KEY,
  name VARCHAR(30),
  accountid VARCHAR(30),
  meetingid BIGINT
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have finished with all our PostgreSQL tasks, and make sure to add the credentials in your .env file. Now it is time to get our app up and running!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# PostgreSQL credentials and information
PORT=
PG_USER=
PG_HOST=
PG_DATABASE=
PG_PASSWORD=
PG_PORT=

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Start the app&lt;/strong&gt;&lt;br&gt;
It is time to run the app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While your app is running on port 3000, go to Zoom &lt;a href="https://zoom.us/" rel="noopener noreferrer"&gt;here&lt;/a&gt; and create a meeting or delete an existing meeting in your account.&lt;/p&gt;

&lt;p&gt;Once you do this, you will see the event printed in your console and a new instance created in your Postgres database.&lt;/p&gt;

&lt;p&gt;You can add as many events as you want, and they all will be stored in your database.&lt;/p&gt;

&lt;p&gt;Thank you for follow along and happy coding 😀!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Sending webhooks to a postgres database</title>
      <dc:creator>Elisa Levet</dc:creator>
      <pubDate>Thu, 15 Sep 2022 00:15:49 +0000</pubDate>
      <link>https://dev.to/zoom/sending-webhooks-to-a-postgres-database-4eoe</link>
      <guid>https://dev.to/zoom/sending-webhooks-to-a-postgres-database-4eoe</guid>
      <description>&lt;h3&gt;
  
  
  &lt;strong&gt;What are webhooks?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Webhooks are a powerful and valuable tool many Zoom developers use to build event triggers, automatically subscribe to data, and receive a high volume of data without needing to make API requests. To utilize them and accept the incoming requests, you'll need to setup a database, like PostgreSQL. &lt;/p&gt;

&lt;p&gt;In this series, we will learn more about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Webhooks: what are they, why use them, and the main difference between webhooks and API requests&lt;/li&gt;
&lt;li&gt;Example of where Zoom's webhooks can be used vs. APIs requests&lt;/li&gt;
&lt;li&gt;How to create an app in our Zoom Marketplace and enable event subscriptions&lt;/li&gt;
&lt;li&gt;Set up our webhook sample app&lt;/li&gt;
&lt;li&gt;Set up a Postgres database to receive and store Zoom events&lt;/li&gt;
&lt;li&gt;Run our webhook sample app and start storing events in your database
Let's get started!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What are webhooks?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Webhooks are automated event notification messages sent from one server to another in the form of HTTP requests. &lt;/p&gt;

&lt;p&gt;Zoom uses webhooks to push the event-related data to your server for events your app has subscribed to. Webhook events (from Zoom) do not count toward API rate limits, making them a valuable tool for getting a large volume of data. Instead of requesting data through an API, your application tells Zoom to send data when the event happens. &lt;/p&gt;

&lt;p&gt;Webhooks are almost always a faster method to automatically receive data rather than making API requests.&lt;/p&gt;

&lt;p&gt;For a further overview of webhooks, our friends at &lt;a href="https://ngrok.com/partners/zoom"&gt;ngrok&lt;/a&gt; have created a great resource explaining &lt;a href="https://webhooks.fyi/docs/webhook-primer"&gt;what webhooks are&lt;/a&gt; with a &lt;a href="https://webhooks.fyi/docs/webhook-directory"&gt;directory of providers.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But what is the difference between API calls and webhooks?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;API calls work on &lt;u&gt;request-based &lt;/u&gt;output mechanisms, and webhooks work on &lt;u&gt;event-based&lt;/u&gt; output mechanisms.&lt;/p&gt;

&lt;p&gt;Think about this real-life example:&lt;/p&gt;

&lt;p&gt;⏰ Let's say that every night before you go to bed, you set up an alarm for 6:00am, and you know that this alarm will go off at 6:00am. Instead of waking up in the middle of the night multiple times to check the time (like an API call), you can just sleep tight knowing that an alarm will wake you up when it's time!&lt;/p&gt;

&lt;p&gt;Now, let's translate this example to a specific Zoom scenario:&lt;/p&gt;

&lt;p&gt;🏢 Let's say you work in a company with around 40 people under the same Zoom account, all who host daily meetings. You've just been tasked to get all the recordings from all the meetings held during the day under your company's Zoom account. &lt;/p&gt;

&lt;p&gt;You could do it one of two ways. The first option is to call the recordings API to check if a meeting has ended and if it has a recording available (which, if you think about it, can be a lot because there might be at least 40 meetings to check daily). That's a lot of requests your app needs to make!&lt;/p&gt;

&lt;p&gt;Instead, a second option would be to use event subscriptions in one of our various app types and wait for a near real-time notification of when the recordings are available. This means you won't have to continuously check if the meetings have ended or if they have recordings available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let's put it all into practice:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the next series, we'll create a &lt;strong&gt;Webhook-only app&lt;/strong&gt; in the &lt;a href="https://marketplace.zoom.us/"&gt;Zoom App Marketplace&lt;/a&gt; and set up a Postgres database to receive the data. &lt;/p&gt;

&lt;p&gt;For this guide, we will use a &lt;a href="https://marketplace.zoom.us/docs/guides/build/webhook-only-app/"&gt;Webhook-only app&lt;/a&gt; as it is purely a subscription to webhooks, but you can also use webhooks with OAuth and Server-to-Server OAuth apps. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>When to use Server-to-Server OAuth app and when to use OAuth app ?</title>
      <dc:creator>Elisa Levet</dc:creator>
      <pubDate>Thu, 01 Sep 2022 17:26:10 +0000</pubDate>
      <link>https://dev.to/zoom/when-to-use-server-to-server-oauth-app-and-when-to-use-oauth-app--579n</link>
      <guid>https://dev.to/zoom/when-to-use-server-to-server-oauth-app-and-when-to-use-oauth-app--579n</guid>
      <description>&lt;p&gt;If you are asking yourself should I use a Server-to-Server OAuth app or a “standard” OAuth app? 🤔 &lt;/p&gt;

&lt;p&gt;I would suggest you to start by asking yourself the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o8e4e9x---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xdn1itq0j0tybakf5s8v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o8e4e9x---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xdn1itq0j0tybakf5s8v.png" alt="Who will use use my app? People or Programs" width="573" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So,  if you are planning on developing an app that is going be used by &lt;strong&gt;&lt;em&gt;people&lt;/em&gt;&lt;/strong&gt; you should definitely be looking into an &lt;strong&gt;&lt;a href="https://marketplace.zoom.us/docs/guides/build/oauth-app/"&gt;OAuth app. &lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And what I mean by this is that if you want your application to make &lt;em&gt;API calls to the Zoom endpoints&lt;/em&gt; on behalf of 3rd party users, you will need an &lt;strong&gt;OAuth app&lt;/strong&gt; that the end user will authorize to grant your application permission to access their data.&lt;/p&gt;

&lt;p&gt;Now, if your application is going to be used by &lt;em&gt;&lt;strong&gt;programs&lt;/strong&gt;&lt;/em&gt; and if it is going to make API calls on behalf of the account and has &lt;strong&gt;NO&lt;/strong&gt;  user interaction, then you are looking into a &lt;strong&gt;&lt;a href="https://marketplace.zoom.us/docs/guides/build/server-to-server-oauth-app/"&gt;Server-to-Server OAuth app&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s put it in simple words now:&lt;/p&gt;

&lt;p&gt;📝 &lt;strong&gt;Server-to-Server OAuth&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your application calls the Zoom APIs on behalf of the account without users interaction&lt;/li&gt;
&lt;li&gt;Internal applications that work with own data rather than a users data&lt;/li&gt;
&lt;li&gt;Use cases: Internal reporting tools, Managing internal users, Managing accounts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👥 &lt;strong&gt;OAuth app&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Applications created for 3rd party users&lt;/li&gt;
&lt;li&gt;Applications authorized and used by people&lt;/li&gt;
&lt;li&gt;Use cases: Scheduling apps, Tele-health apps, Learning Management system apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Learn more about the different app types available in the Zoom Marketplace here: &lt;a href="https://marketplace.zoom.us/"&gt;https://marketplace.zoom.us/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more questions about this topic, join us at the Zoom Developer Forum: &lt;a href="https://devforum.zoom.us/"&gt;https://devforum.zoom.us/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy coding! 😀&lt;/p&gt;

</description>
      <category>programming</category>
      <category>api</category>
      <category>oauth</category>
      <category>zoom</category>
    </item>
    <item>
      <title>JWT app vs Server to Server OAuth app</title>
      <dc:creator>Elisa Levet</dc:creator>
      <pubDate>Thu, 18 Aug 2022 18:15:00 +0000</pubDate>
      <link>https://dev.to/zoom/jwt-app-vs-server-to-server-oauth-app-p08</link>
      <guid>https://dev.to/zoom/jwt-app-vs-server-to-server-oauth-app-p08</guid>
      <description>&lt;p&gt;With the Deprecation of the JWT app from the Marketplace next year, we have been encouraging developers to start migrating from their JWT app to the Server-to-Server OAuth app, which provides &lt;strong&gt;more granular scoping&lt;/strong&gt;  options for internal apps that retrieve data from our endpoints.&lt;/p&gt;

&lt;p&gt;Now, what are the differences between JWT apps and Server-to-Server OAuth apps?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Internal JWT apps, created by account admins, &lt;strong&gt;have wide scope access.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Server-to-server OAuth allows individual users to create apps with &lt;strong&gt;scoped access&lt;/strong&gt; to APIs which reflect the access they already have.&lt;/li&gt;
&lt;li&gt;JWT apps rely on &lt;strong&gt;token generation&lt;/strong&gt; using account credentials (API key and API secret)&lt;/li&gt;
&lt;li&gt;Server-to-Server OAuth apps rely on &lt;strong&gt;requesting an access token&lt;/strong&gt; to the Zoom OAuth endpoint using account credentials (Account ID, Client ID and Client Secret).&lt;/li&gt;
&lt;li&gt;Access tokens generated with Server-to-Server OAuth app are *&lt;em&gt;only valid for one hour *&lt;/em&gt;(a new one must be requested once they expired).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is important to make emphasis on the fact that the only thing that changes with this migration is the way we are requesting an access token and making &lt;strong&gt;the API call will remain the same.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;how can you enable the Sever-to-Server OAuth app&lt;/strong&gt; and how can you make sure that specific permissions are reflected on the app itself?&lt;/p&gt;

&lt;p&gt;Once the app is enabled in the account (refer to this post if needed &lt;a href="https://dev.to/zoom/why-use-server-to-server-oauth-1n53"&gt;https://dev.to/zoom/why-use-server-to-server-oauth-1n53&lt;/a&gt;), it is important to define which permissions you want to grant to the developers that will have access to this type app, because the developer will only see the scopes that they can authorized.&lt;/p&gt;

&lt;p&gt;For example, let’s say that you do not want your developer to have access to the Dashboard, so you have to make sure that in the Roles setting for that developer those features are disabled:&lt;br&gt;
(ADMIN &amp;gt; User Management &amp;gt; Roles &amp;gt; Edit)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zjlUm3qy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/khyri5a9f82hrf56z49j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zjlUm3qy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/khyri5a9f82hrf56z49j.png" alt="Dashboard permissions" width="880" height="757"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What this will ensure is that Dashboard &lt;strong&gt;scopes wont be visible or available&lt;/strong&gt; in the developer’s Server-to-Server app &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z9FX7OLU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwrb77c71hxk48f08nco.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z9FX7OLU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwrb77c71hxk48f08nco.png" alt="Available scopes" width="880" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LhArfs-l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ltj6w6yc2kspwc3x4gm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LhArfs-l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ltj6w6yc2kspwc3x4gm.png" alt="Available scopes" width="880" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are some code snippets that demonstrate the generation of access token using JWT apps and the request of access token using Server-to-Server OAuth app:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Using JWT app credentials to generate an Access token, then use it to make an API call to our Get Users endpoint&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E2XazeQd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ohinoml6n6me5evmh3f2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E2XazeQd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ohinoml6n6me5evmh3f2.png" alt="Generation of access token with JWT credentials" width="692" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Using Server-to-Server OAuth app credentials to request an access token to the Zoom OAuth endpoint, then use it to make an API call to the same Get Users endpoint&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lfxOdKwq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2atolc171j7fxi85r9h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lfxOdKwq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2atolc171j7fxi85r9h.png" alt="Requesting an access token to Zoom OAuth endpoint" width="861" height="805"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is important to note that once your access token expires, you just need to make the same request to the Zoom OAuth endpoint to &lt;strong&gt;generate a new one&lt;/strong&gt; (there is no refresh token involved); but the generation of a new token,** invalidates the previous one **(even if it was not expired).&lt;/p&gt;

&lt;p&gt;Thank you so much for reading me and happy coding!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Why use Server To Server OAuth?</title>
      <dc:creator>Elisa Levet</dc:creator>
      <pubDate>Fri, 15 Jul 2022 16:01:41 +0000</pubDate>
      <link>https://dev.to/zoom/why-use-server-to-server-oauth-1n53</link>
      <guid>https://dev.to/zoom/why-use-server-to-server-oauth-1n53</guid>
      <description>&lt;p&gt;The &lt;strong&gt;JWT&lt;/strong&gt; app type will be deprecated in June 2023 and we recommend and highly encourage that you start migrating from the JWT app to the newly introduced Server-to-Server OAuth App.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;**But, you might be wondering why we are deprecating the JWT app?&lt;/em&gt;**&lt;/p&gt;

&lt;p&gt;The main reason we are introducing server-to-server OAuth over JWT is to provide more granular scoping options for internal apps that retrieve data from our endpoints.&lt;/p&gt;

&lt;p&gt;Internal JWT apps, created by account admins, have wide scope access and rely on token generation from account-level credentials. Server-to-server OAuth allows individual users to create apps with scoped access to APIs which reflect the access they already have.&lt;/p&gt;

&lt;p&gt;Learn more about the Frequent Asked Question here:&lt;br&gt;
&lt;a href="https://marketplace.zoom.us/docs/guides/build/jwt-app/jwt-faq/" rel="noopener noreferrer"&gt;https://marketplace.zoom.us/docs/guides/build/jwt-app/jwt-faq/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That being said, here is a quick and simple guide on how to use or new app with Postman:&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://marketplace.zoom.us/docs/guides/build/server-to-server-oauth-app/" rel="noopener noreferrer"&gt;new app type &lt;/a&gt;facilitates &lt;strong&gt;OAuth-authenticated requests between servers without end-user involvement&lt;/strong&gt;. This grant type enables your private server application to get your account owner access token without user interaction.&lt;/p&gt;

&lt;p&gt;To start using this App, the Administrator for your Zoom account or the Owner of the account must enable the view and edit permissions for Server-to-Server OAuth app by going to User Management &amp;gt; Roles &amp;gt; Role Settings &amp;gt; Advanced features. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxz693jbkbwegsemog0xk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxz693jbkbwegsemog0xk.png" alt="Role settings needed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once those permissions are enabled, you will be able to see the app in your Marketplace Dashboard.&lt;/p&gt;

&lt;p&gt;Once the app is created and you have added the scopes and features (event subscriptions) that you want to include, you can go ahead and activate your app in your account. &lt;/p&gt;

&lt;p&gt;Feel free to follow along on the Steps on How to Create a Server-to-Server OAuth app here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.zoom.us/docs/guides/build/server-to-server-oauth-app/#create-a-server-to-server-oauth-app" rel="noopener noreferrer"&gt;https://marketplace.zoom.us/docs/guides/build/server-to-server-oauth-app/#create-a-server-to-server-oauth-app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, it is time to get started and use Postman with our newly created app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1
&lt;/h3&gt;

&lt;p&gt;Create a new Post request to &lt;a href="https://zoom.us/oauth/token" rel="noopener noreferrer"&gt;https://zoom.us/oauth/token&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2
&lt;/h3&gt;

&lt;p&gt;Add the following Query Params &lt;br&gt;
grant_type=account_credentials &lt;br&gt;
account_id={account_id}&lt;br&gt;&lt;br&gt;
(Grab your account_id from the App credentials Tab in your newly created app)&lt;/p&gt;

&lt;p&gt;So your Post request should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13gy7upnglzzl2zmupez.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13gy7upnglzzl2zmupez.png" alt="Post request"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3
&lt;/h3&gt;

&lt;p&gt;Go to the Authorization Tab and select Basic Auth as the authorization type and user your Client ID as a Username and your Client SECRET as your password:&lt;br&gt;
(Grab those credentials from your newly created app as well)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4j8eaxkmo0oz3yene9zy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4j8eaxkmo0oz3yene9zy.png" alt="Credentials"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once that is all done, you should be able to send the POST request and you will get a response that will look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcw9q0sk5synz680527c8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcw9q0sk5synz680527c8.png" alt="Bearer token to make API calls with"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should be able to use the access_token as a bearer token to make API calls to those endpoints that can be accessed with the scope/s set up in the Server-to-Server application.&lt;/p&gt;

&lt;p&gt;Hope this helps to understand this new app type better and let me know if you have any questions or suggestions!&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;br&gt;
Elisa :)&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
