Only show these results:

Using virtual calendars

Virtual calendars work like any other calendar in Nylas, and they make it easy to include customized scheduling in your Nylas application. They allow you to provide scheduling to your end users without requiring them to connect a third-party service provider like Google, Microsoft Exchange, or Office 365.

ℹ️ Virtual Calendars is available for Core and Plus plans.

There are two major scheduling use cases for virtual calendars:

  • Enabling scheduling for end users who have sensitive information in their personal calendars that they don't want to expose in order to complete their task.
  • Enabling scheduling for people or resources that don't have an account on a third-party service provider (for example, a meeting room).

A diagram showing how virtual calendars integrate with a Nylas application.

How virtual calendars work

Virtual calendars are associated with virtual accounts — grants that represent a person or resource. You can use virtual accounts as part of any Nylas application, and you can create them using Custom authentication.

You create a virtual account by passing your Nylas application's client ID. Each application can have multiple virtual accounts, but each virtual account can have only ten virtual calendars.

After you create a virtual account, you can create a virtual calendar. The virtual calendar's email field becomes its unique ID. Nylas uses the email field to manage the calendar's account.

⚠️ The account listed in a virtual calendar's Email field cannot be used as a regular email account. Nylas strongly recommends against using an existing email address for this field.

The following JSON snippet shows the schema for a virtual calendar.

{
"client_id": "3",
"provider": "nylas",
"scopes": "calendar",
"email": "virtual_account_unique_id",
"name": "Virtual Calendar",
"settings": {}
}

After it's created, the virtual calendar appears in the Nylas Dashboard as a connected account. A virtual calendar's provider will always be listed as Nylas, as in the image below.

The Nylas Dashboard showing a list of calendars. The virtual calendar has no Account Type, and its Provider is listed as Nylas.

Before you begin

Before you can create a virtual calendar, you need the following prerequisites:

  • A Nylas account.
  • A v3 Nylas application.
  • Your Nylas application's client ID and API key.

Authenticate a virtual account

Before you can create a virtual calendar, you need a virtual account to represent its "owner". Virtual accounts use Custom authentication. You can use the same auth process each time you need to create a new grant for a virtual calendar.

You can authenticate a virtual account using either the Nylas APIs or the Nylas SDKs.

First, create a virtual calendar connector as in the following examples.

curl --location 'https://api.us.nylas.com/v3/connectors' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <NYLAS_API_KEY>' \
--data '{
"provider": "virtual-calendar",
"name": "nylas"
}'
import 'dotenv/config'
import Nylas from 'nylas'

const NylasConfig = {
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI,
}

const nylas = new Nylas(NylasConfig)

async function createConnector() {
try {
const connector = await nylas.connectors.create({
requestBody: {
name: 'nylas',
provider: 'virtual-calendar',
}
})

console.log('Connector created:', connector)
} catch (error) {
console.error('Error creating provider:', error)
}
}

createConnector()
from dotenv import load_dotenv
load_dotenv()
import os
import sys
from nylas import Client

nylas = Client(
os.environ.get('NYLAS_API_KEY'),
os.environ.get('NYLAS_API_URI')
)

connector = nylas.connectors.create(
request_body={
"name": 'nylas',
"provider": "virtual-calendar"
}
)

print(connector)
require 'nylas'

nylas = Nylas::Client.new(api_key: '<NYLAS_API_KEY>')

request_body = {
name: 'Nylas',
provider: 'virtual-calendar'
}

begin
nylas.connectors.create(request_body: request_body)
rescue Exception => exception
puts exception
end
import com.nylas.NylasClient;
import com.nylas.models.*;

public class connector {
public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
CreateConnectorRequest request = new CreateConnectorRequest.VirtualCalendar();
Response<Connector> connectorResponse = nylas.connectors().create(request);

System.out.println(connectorResponse);
}
}
import com.nylas.NylasClient
import com.nylas.models.CreateConnectorRequest

fun main(args: Array<String>) {
val nylas: NylasClient = NylasClient(apiKey = "<NYLAS_API_KEY>")
val request = CreateConnectorRequest.VirtualCalendar()
val connector = nylas.connectors().create(request)

print(connector.data)
}

Next, create a grant for the virtual account.

curl --location 'https://api.us.nylas.com/v3/connect/custom' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <NYLAS_API_KEY>' \
--data '{
"provider": "virtual-calendar",
"settings": { "email": "devrel-virtual-calendar" },
"scope": [ "calendar" ]
}'
import 'dotenv/config'
import Nylas from 'nylas'

const NylasConfig = {
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI,
}

const nylas = new Nylas(NylasConfig)

async function createVirtualCalendarGrant() {
try {
const response = await nylas.auth.grants.create({
requestBody: {
provider: 'virtual-calendar',
settings: {
email: 'devrel-virtual-calendar',
},
scope: ['calendar']
}
})

console.log('Grant created:', response)
} catch (error) {
console.error('Error creating grant:', error)
}
}

createVirtualCalendarGrant()
from dotenv import load_dotenv
load_dotenv()
import os
import sys
from nylas import Client

nylas = Client(
os.environ.get('NYLAS_API_KEY'),
os.environ.get('NYLAS_API_URI')
)

grant_id = nylas.auth.custom_authentication(
request_body={
"provider": "virtual-calendar",
"settings": {
"email": 'devrel-virtual-calendar',
},
"scope": ['calendar']
}
)

print(grant_id)
# frozen_string_literal: true

require 'nylas'
require 'sinatra'

set :show_exceptions, :after_handler

error 404 do
'No authorization code returned from Nylas'
end

error 500 do
'Failed to exchange authorization code for token'
end

nylas = Nylas::Client.new(api_key: '<NYLAS_API_KEY>')

get '/nylas/auth' do
request_body = {
provider: 'virtual-calendar',
settings: {
email: "nylas-virtual-calendar"
}
}

response = nylas.auth.custom_authentication(request_body)
"#{response}"
end
import java.util.*;
import static spark.Spark.*;
import com.nylas.NylasClient;
import com.nylas.models.*;

public class AuthRequest {
public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();

get("/nylas/auth", (request, response) -> {
Map<String, String> settings = new HashMap<>();
settings.put("email", "nylas-virtual-calendar");

List<String> scopes = new ArrayList<>();
scopes.add("openid");

CreateGrantRequest requestBody = new CreateGrantRequest.
Builder
(AuthProvider.VIRTUAL_CALENDAR, settings).
scopes(scopes).state("xyz").
build();

Response<Grant> authResponse = nylas.auth().customAuthentication(requestBody);

return "%s".formatted(authResponse);
});
}
}
import com.nylas.NylasClient
import com.nylas.models.*
import spark.kotlin.Http
import spark.kotlin.ignite

fun main(args: Array<String>) {
val nylas: NylasClient = NylasClient(apiKey = "<NYLAS_API_KEY>")
val http: Http = ignite()

http.get("/nylas/auth") {
val settings = mapOf("email" to "nylas-virtual-calendar")
val scopes = listOf("openapi")

val requestBody = CreateGrantRequest.
Builder(AuthProvider.VIRTUAL_CALENDAR, settings).
scopes(scopes).
state("xyz").
build()

var authResponse : Response<Grant>

nylas.auth().customAuthentication(requestBody).also { authResponse = it }

authResponse
}
}

The email property associated with a virtual account doesn't need to be an email-formatted string. It can be any arbitrary string. Nylas uses the email value to represent the virtual account.

Create a virtual calendar

Now that you've authenticated a virtual account, you can create a virtual calendar. To do so, make a Create Calendar request or use the Nylas SDKs, as in the examples below.

curl --location 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/calendars' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <NYLAS_API_KEY>' \
--data '{
"name": "Nylas DevRel",
"description": "Nylas Developer Relations"
}'
import 'dotenv/config'
import Nylas from 'nylas'

const NylasConfig = {
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI,
}

const nylas = new Nylas(NylasConfig)

async function createVirualCalendar() {
try {
const calendar = await nylas.calendars.create({
identifier: process.env.VIRTUAL_CALENDAR_GRANT_ID,
requestBody: {
name: 'Nylas DevRel',
description: 'Nylas Developer Relations',
}
})

console.log('Virtual Calendar:', calendar)
} catch (error) {
console.error('Error to create virtual calendar:', error)
}
}

createVirualCalendar()
from dotenv import load_dotenv
load_dotenv()

import os
import sys
from nylas import Client

nylas = Client(
os.environ.get('NYLAS_API_KEY'),
os.environ.get('NYLAS_API_URI')
)

grant_id = os.environ.get("VIRTUAL_CALENDAR_GRANT_ID")

calendar = nylas.calendars.create(
grant_id,
request_body={
"name": 'Nylas DevRel',
"description": 'Nylas Developer Relations'
}
)

print(calendar)
require 'nylas' 

nylas = Nylas::Client.new(api_key: '<NYLAS_API_KEY>')

request_body = {
"name": "Nylas DevRel",
"description": "Nylas Developer Relations",
"timezone": "America/Toronto"
}

begin
calendars, _request_ids = nylas.calendars.create(
identifier: '<VIRTUAL_CALENDAR_ID>',
request_body: request_body)

puts calendars
rescue Exception => exception
puts exception
end
import com.nylas.NylasClient;
import com.nylas.models.*;
import java.util.HashMap;

public class CreateVirtualCalendar {
public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();

CreateCalendarRequest requestBody = new CreateCalendarRequest(
"Nylas DevRel",
"Nylas Developer Relations",
"Nylas Headquarters",
"America/Toronto",
new HashMap<String, String>());

try {
Response<Calendar> calendar = nylas.calendars().create(
"<VIRTUAL_CALENDAR_GRANT_ID>",
requestBody);

System.out.println(calendar);
} catch(Exception e) {
System.out.printf(" %s%n", e);
}
}
}
import com.nylas.NylasClient
import com.nylas.models.*

fun main(args: Array<String>) {
val nylas: NylasClient = NylasClient(apiKey = "<NYLAS_API_KEY>")

val requestBody = CreateCalendarRequest(
"Nylas DevRel",
"Nylas Developer Relations",
"Nylas Headquarters",
"America/Toronto",
mapOf<String, String>()
)

val calendar: Response<Calendar> = nylas.calendars().create(
"<VIRTUAL_CALENDAR_GRANT_ID>",
requestBody)

print(calendar.data)
}

Create an event on a virtual calendar

When you have both a virtual account and a virtual calendar, you can start creating events.

⚠️ Virtual calendars do not send email invitations to event participants.

To create an event on a virtual calendar, you can either make a Create Event request or use the Nylas SDKs, as in the examples below.

curl --location 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events?calendar_id=<CALENDAR_ID>&notify_participants=true' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <NYLAS_API_KEY>' \
--data '{
"when": {
"start_time": 1704302073,
"end_time": 1704305673
},
"title": "Build With Nylas"
}'
import 'dotenv/config'
import Nylas from 'nylas'

const NylasConfig = {
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI
}

const nylas = new Nylas(NylasConfig)
const now = Math.floor(Date.now() / 1000)

async function createAnEvent() {
try {
const event = await nylas.events.create({
identifier: process.env.VIRTUAL_CALENDAR_GRANT_ID,
requestBody: {
title: 'Build With Nylas',
when: {
startTime: now,
endTime: now + 3600,
}
},
queryParams: {
calendarId: process.env.VIRTUAL_CALENDAR_ID,
},
})

console.log('Event:', event)
} catch (error) {
console.error('Error creating event:', error)
}
}

createAnEvent()
from dotenv import load_dotenv
load_dotenv()

import os
import sys
from nylas import Client

nylas = Client(
os.environ.get('NYLAS_API_KEY'),
os.environ.get('NYLAS_API_URI')
)

grant_id = os.environ.get("VIRTUAL_CALENDAR_GRANT_ID")
calendar_id = os.environ.get("VIRTUAL_CALENDAR_ID")

events = nylas.events.create(
grant_id,
request_body={
"title": 'Build With Nylas',
"when": {
"start_time": 1609372800,
"end_time": 1609376400
},
},
query_params={
calendar_id
}
)

print(events)
require 'nylas'

nylas = Nylas::Client.new(api_key: '<NYLAS_API_KEY>')

query_params = {
calendar_id: '<VIRTUAL_CALENDAR_ID>'
}

start_time = Time.now.to_i
end_time = start_time + 3600

request_body = {
when: {
start_time: start_time,
end_time: end_time
},
title: "Build With Nylas",
}

events, _request_ids = nylas.events.create(
identifier: '<VIRTUAL_CALENDAR_GRANT_ID>',
query_params: query_params,
request_body: request_body)

if _request_ids != ""
puts events[:id]
puts events[:title]
puts "Event created successfully"
else
puts "There was an error creating the event"
end
import com.nylas.NylasClient;
import com.nylas.models.*;

public class CreateVirtualEvent {
public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();

CreateEventRequest.When.Timespan timespan = new CreateEventRequest.When.Timespan.
Builder
(Math.toIntExact("<START_TIME>"),
Math.toIntExact("<END_TIME>")).
build();

CreateEventRequest request = new CreateEventRequest.Builder(timespan).
title("Build With Nylas").
build();

CreateEventQueryParams queryParams = new CreateEventQueryParams.Builder("<VIRTUAL_CALENDAR_ID>").build();

Response<Event> event = nylas.events().create(
"<VIRTUAL_CALENDAR_GRANT_ID",
request,
queryParams);

System.out.println(event);
}
}
import com.nylas.NylasClient
import com.nylas.models.*

fun main(args: Array<String>) {
val nylas: NylasClient = NylasClient(apiKey = "<NYLAS_API_KEY>")

val eventWhenObj: CreateEventRequest.When = CreateEventRequest.When.Timespan(<START_TIME>, <END_TIME>)

val eventRequest: CreateEventRequest = CreateEventRequest.Builder(eventWhenObj).
title("Nylas DevRel").
description("Nylas Developer Relations").
location("Nylas Headquarters").
build()

val eventQueryParams: CreateEventQueryParams = CreateEventQueryParams("<VIRTUAL_CALENDAR_ID>")

val event: Response<Event> = nylas.events().create(
"<VIRTUAL_CALENDAR_GRANT_ID>",
eventRequest,
eventQueryParams)

print(event.data)
}

Virtual calendar webhooks

Virtual calendars support the following Calendar and Events webhooks:

  • calendar.created
  • event.created
  • event.updated

For more information, see the Calendar webhook schemas and Event webhook schemas.

Keep in mind

  • Virtual calendars support notifications for events.
  • Virtual calendars can interact with Nylas' Account, Calendar, and Events API endpoints only.
  • Each virtual account can have a maximum of ten associated virtual calendars.
  • The first virtual calendar you create for a virtual account is designated as the primary calendar.