Create your own calendar tool Timothy Liu Published: December 21, 2018 Since we released our new Meeting Scheduler UI Tool (blog post), many developers have wondered what’s going on under the hood. We’ll be walking through the best way to implement your own Meeting Scheduler using the Kloudless Calendar API.Authenticate your users’ calendar accountsAs a developer, you want to provide the optimal user experience, so the best way is to support all of the calendars your users need. Kloudless’ Calendar API supports the following cloud services:Outlook CalendarGoogle CalendarCalDAViCloud Calendar (via CalDAV)Exchange Calendar (for on-premises)Kloudless uses OAuth 2.0 for users to grant access to information about their calendars and events. The easiest way for a developer to connect to their users is to use the authenticator.js JS library. Here’s a code snippet that shows how easy it is to embed the authenticator.js (angular.js, react.js, and vue.js bindings coming soon):In your HTML: <script type="text/javascript" src="https://static-cdn.kloudless.com/p/platform/sdk/kloudless.authenticator.js"></script>1<script type="text/javascript" src="https://static-cdn.kloudless.com/p/platform/sdk/kloudless.authenticator.js"></script>In your JS: let e = document.getElementById("auth-button"); // You can also use jQuery: e = $('#auth-button'); let config = { 'client_id': 'oeD8Kzi8oN2uHvBALivVA_3zlo2OhL5Ja6YtfBrtKLA', 'scope': 'all.calendar' }; let callback = function (result) { if (result.error) { console.error('An error occurred:', result.error); return; } console.log( 'Yay! I now have a newly authenticated', result.account.service, 'account with ID', result.account.id, 'and Bearer Token', result.access_token); }; // To configure the authenticator pop-up to launch when the button is clicked: let auth = Kloudless.authenticator(e, config, callback); // To remove the click handler from the button: Kloudless.stop(e); // To launch programmatically: auth.launch();123456789101112131415161718192021222324let e = document.getElementById("auth-button");// You can also use jQuery:e = $('#auth-button');let config = { 'client_id': 'oeD8Kzi8oN2uHvBALivVA_3zlo2OhL5Ja6YtfBrtKLA', 'scope': 'all.calendar'};let callback = function (result) { if (result.error) { console.error('An error occurred:', result.error); return; } console.log( 'Yay! I now have a newly authenticated', result.account.service, 'account with ID', result.account.id, 'and Bearer Token', result.access_token);};// To configure the authenticator pop-up to launch when the button is clicked:let auth = Kloudless.authenticator(e, config, callback);// To remove the click handler from the button:Kloudless.stop(e);// To launch programmatically:auth.launch();The key pieces of information are the Account ID and Bearer Token, which let you make API requests to the following endpoints:Retrieve all of the user’s calendars: // returned from the authenticator const accountId = '12345'; const bearerToken = 'abcdef'; const promise = axios({ method: 'GET', url: 'https://api.kloudless.com/v1/accounts/' + accountId + '/cal/calendars', headers: { Authorization: 'Bearer ' + bearerToken } }); promise.then((response) => { console.log(response); }).catch((error) => { console.log(error) });123456789101112131415// returned from the authenticatorconst accountId = '12345';const bearerToken = 'abcdef';const promise = axios({ method: 'GET', url: 'https://api.kloudless.com/v1/accounts/' + accountId + '/cal/calendars', headers: { Authorization: 'Bearer ' + bearerToken }});promise.then((response) => { console.log(response);}).catch((error) => { console.log(error)});Calculating availability based on time constraintsIn Kloudless’ Meeting Scheduler, we prompt the user for some basic information about the meeting they are trying to create: Event DurationEvent DescriptionEvent LocationEvent NamePreferred Date and Time RangeIn this section, we presume that the user is also the owner of the event. The most important pieces of information are the invite owner’s desired times for when the meeting should occur and how long the meeting should be. Developers can then use Kloudless’ Calendar Availability endpoint to dynamically calculate when a meeting should take place given the events on the owner’s calendar.A basic example of querying the endpoint can be found below. For a more detailed explanation, check out the product release blog post.Calculating Availability: // The data below is returned from the Authenticator const accountId = '12345'; const bearerToken = 'abcdef'; // Data below returned from prompting the user for the time range // of the event. const duration = 'PT1H'; // ISO 8601 format const freeTimeStarts = '2018-12-20T08:00:00-08:00'; const freeTimeEnds = '2018-12-20T17:00:00-08:00'; // Either use "primary" or a calendar ID returned from // the previous request for all the user's calendars const calendarId = 'primary'; const bodyData = { calendars: [calendarId], meeting_duration: duration, time_windows: [{ start: freeTimeStarts, end: freeTimeEnds }] }; const promise = axios({ method: 'POST', url: 'https://api.kloudless.com/v1/accounts/' + accountId + '/cal/availability', headers: { Authorization: 'Bearer ' + bearerToken }, bodyData }); promise.then((response) => { console.log(response); }).catch((error) => { console.log(error) });1234567891011121314151617181920212223242526272829303132// The data below is returned from the Authenticatorconst accountId = '12345';const bearerToken = 'abcdef';// Data below returned from prompting the user for the time range// of the event.const duration = 'PT1H'; // ISO 8601 formatconst freeTimeStarts = '2018-12-20T08:00:00-08:00';const freeTimeEnds = '2018-12-20T17:00:00-08:00';// Either use "primary" or a calendar ID returned from// the previous request for all the user's calendarsconst calendarId = 'primary';const bodyData = { calendars: [calendarId], meeting_duration: duration, time_windows: [{ start: freeTimeStarts, end: freeTimeEnds }]};const promise = axios({ method: 'POST', url: 'https://api.kloudless.com/v1/accounts/' + accountId + '/cal/availability', headers: { Authorization: 'Bearer ' + bearerToken }, bodyData});promise.then((response) => { console.log(response);}).catch((error) => { console.log(error)});Creating a calendar event and adding invitees:The last and final step is the easiest in the process since we simply need to create a calendar event and invite the attendees. However, we require additional information on the attendee list and agreed-upon meeting time. In the initial release of Kloudless’ Meeting Scheduler, we provide a public link that the owner can use to invite individual attendees to choose when an event should be held. The attendee can also provide their email address to receive a calendar invite to the event. In the next version of the Kloudless Meeting Scheduler, we plan to support multiple attendees and calendars when creating an event.Creating an event and inviting an attendee // These are the account details of the event's owner, // originally returned from the authenticator. // Be sure not to include them in the client-side when the owner // is not using the browser! No one other than the owner, such as // meeting attendees, should be privy to the owner's bearer token. const accountId = '12345'; const bearerToken = 'abcdef'; const calendarId = 'primary'; // returned from prompting the owner of the event for the // event information. const eventName = 'Interview'; const eventLocation = '1234 Dummy St.'; const eventDescription = 'Meet the team!'; // returned from prompting the attendee for their details // and preferred meeting time slot. // In the Kloudless Meeting Scheduler, the public link serves // to gather this information. const eventStartTime = '2018-12-20T10:00:00-08:00'; const eventEndTime = '2018-12-20T11:00:00-08:00'; const attendeeEmail = 'attendee@event.com'; const attendeeName = 'Mario Speedwagon'; const bodyData = { name: eventName, location: eventLocation, description: eventDescription, start: eventStartTime, end: eventEndTime, attendees: [{ name: attendeeName, email: attendeeEmail }] }; const promise = axios({ method: 'POST', url: 'https://api.kloudless.com/v1/accounts/' + accountId + '/cal/calendars/' + calendarId + '/events', headers: { Authorization: 'Bearer ' + bearerToken }, bodyData }); promise.then((response) => { console.log(response); }).catch((error) => { console.log(error) });123456789101112131415161718192021222324252627282930313233343536373839404142434445// These are the account details of the event's owner,// originally returned from the authenticator.// Be sure not to include them in the client-side when the owner// is not using the browser! No one other than the owner, such as// meeting attendees, should be privy to the owner's bearer token.const accountId = '12345';const bearerToken = 'abcdef';const calendarId = 'primary';// returned from prompting the owner of the event for the// event information.const eventName = 'Interview';const eventLocation = '1234 Dummy St.';const eventDescription = 'Meet the team!';// returned from prompting the attendee for their details// and preferred meeting time slot.// In the Kloudless Meeting Scheduler, the public link serves// to gather this information.const eventStartTime = '2018-12-20T10:00:00-08:00';const eventEndTime = '2018-12-20T11:00:00-08:00';const attendeeEmail = 'attendee@event.com';const attendeeName = 'Mario Speedwagon';const bodyData = { name: eventName, location: eventLocation, description: eventDescription, start: eventStartTime, end: eventEndTime, attendees: [{ name: attendeeName, email: attendeeEmail }]};const promise = axios({ method: 'POST', url: 'https://api.kloudless.com/v1/accounts/' + accountId + '/cal/calendars/' + calendarId + '/events', headers: { Authorization: 'Bearer ' + bearerToken }, bodyData});promise.then((response) => { console.log(response);}).catch((error) => { console.log(error)});You’ve scheduled a meeting!And that’s how you can implement the core functionality of a system to schedule meetings among users! Check out our implementation on GitHub for an out-of-the-box solution that can be embedded in your app. If you have any questions or comments, please feel free to reach out to us at hello@kloudless.com!