Nylas
"Nylas: unified calendar/email/contacts API, scheduling, calendar CRUD, email send/read, OAuth providers, Node SDK"
Nylas provides a single API abstraction over dozens of email and calendar providers -- Gmail, Outlook, Exchange, Yahoo, IMAP, and more. Instead of writing provider-specific integrations, you write against the Nylas API once and get cross-provider support for calendar events, email, and contacts. The platform handles OAuth token management, provider quirks, and real-time sync. Choose Nylas when your product needs to read or write to your users' existing calendars and inboxes without building separate integrations for each provider. ## Key Points 1. **Store the grant ID, not raw OAuth tokens** -- Nylas manages token refresh internally; your app only needs the stable grant ID to make API calls. 2. **Use calendar IDs explicitly** -- many accounts have multiple calendars; always let the user choose which calendar to read from and write to, defaulting to "primary". 3. **Enable webhook challenge verification** -- the GET endpoint must return the challenge parameter as plain text for Nylas to activate the subscription. 4. **Handle provider differences gracefully** -- not all features work identically across Gmail, Outlook, and Exchange; check the `provider` field and adapt UI affordances accordingly. 5. **Paginate with cursors** -- list endpoints return a `nextCursor`; always follow it to retrieve complete result sets. 6. **Use `expandRecurring: true` for event queries** -- without this flag, recurring events return as a single master record instead of individual occurrences. 1. **Storing raw provider tokens alongside Nylas tokens** -- this creates a maintenance burden and risks token conflicts; let Nylas handle all provider auth. 2. **Ignoring grant status changes** -- grants can become invalid when users revoke access; listen for `grant.expired` webhooks and prompt re-authentication. 3. **Fetching all events without time bounds** -- unbounded event queries return years of data; always pass `start` and `end` timestamps. 4. **Creating one webhook per grant** -- use a single webhook endpoint for all grants; Nylas includes the grant ID in each webhook payload. 5. **Hardcoding provider-specific scopes** -- scopes vary by provider; use Nylas-recommended scope sets and let the platform translate them. 6. **Treating email and calendar as always available** -- some grants may only have calendar or email permissions; check the grant's scope before calling endpoints that require specific access.
skilldb get scheduling-services-skills/NylasFull skill: 349 linesNylas Unified Communication API
Core Philosophy
Nylas provides a single API abstraction over dozens of email and calendar providers -- Gmail, Outlook, Exchange, Yahoo, IMAP, and more. Instead of writing provider-specific integrations, you write against the Nylas API once and get cross-provider support for calendar events, email, and contacts. The platform handles OAuth token management, provider quirks, and real-time sync. Choose Nylas when your product needs to read or write to your users' existing calendars and inboxes without building separate integrations for each provider.
Setup
SDK Installation and Configuration
// npm install nylas
import Nylas from "nylas";
const nylas = new Nylas({
apiKey: process.env.NYLAS_API_KEY!,
apiUri: process.env.NYLAS_API_URI || "https://api.us.nylas.com",
});
// Each connected account is identified by a grant ID
// obtained after the user completes the OAuth flow
const grantId = process.env.NYLAS_GRANT_ID!;
OAuth Flow for Connecting User Accounts
import express from "express";
const app = express();
// Step 1: Redirect user to Nylas hosted auth
app.get("/auth/connect", (req, res) => {
const authUrl = nylas.auth.urlForOAuth2({
clientId: process.env.NYLAS_CLIENT_ID!,
redirectUri: `${process.env.APP_URL}/auth/callback`,
loginHint: req.query.email as string | undefined,
scope: ["https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/gmail.modify"],
});
res.redirect(authUrl);
});
// Step 2: Exchange the authorization code for a grant
app.get("/auth/callback", async (req, res) => {
const { code } = req.query;
const tokenResponse = await nylas.auth.exchangeCodeForToken({
clientId: process.env.NYLAS_CLIENT_ID!,
redirectUri: `${process.env.APP_URL}/auth/callback`,
code: code as string,
});
// Store grantId and email for this user
const grantId = tokenResponse.grantId;
const email = tokenResponse.email;
// Save to your database
await saveUserGrant(req.session.userId, grantId, email);
res.redirect("/dashboard");
});
Key Techniques
Calendar Event CRUD
interface NylasEventParams {
title: string;
startTime: number; // Unix timestamp
endTime: number;
description?: string;
location?: string;
participants?: Array<{ name?: string; email: string }>;
conferencing?: { provider: "Google Meet" | "Zoom Meeting" | "Microsoft Teams" };
}
async function createCalendarEvent(
grantId: string,
calendarId: string,
params: NylasEventParams
) {
const event = await nylas.events.create({
identifier: grantId,
queryParams: { calendarId },
requestBody: {
title: params.title,
when: {
startTime: params.startTime,
endTime: params.endTime,
},
description: params.description,
location: params.location,
participants: params.participants ?? [],
conferencing: params.conferencing
? { provider: params.conferencing.provider, autocreate: {} }
: undefined,
},
});
return event.data;
}
async function listCalendarEvents(
grantId: string,
calendarId: string,
startTime: number,
endTime: number
) {
const events = await nylas.events.list({
identifier: grantId,
queryParams: {
calendarId,
start: startTime.toString(),
end: endTime.toString(),
expandRecurring: true,
},
});
return events.data;
}
async function updateCalendarEvent(
grantId: string,
calendarId: string,
eventId: string,
updates: Partial<NylasEventParams>
) {
const event = await nylas.events.update({
identifier: grantId,
eventId,
queryParams: { calendarId },
requestBody: {
title: updates.title,
description: updates.description,
location: updates.location,
when: updates.startTime && updates.endTime
? { startTime: updates.startTime, endTime: updates.endTime }
: undefined,
},
});
return event.data;
}
async function deleteCalendarEvent(
grantId: string,
calendarId: string,
eventId: string
) {
await nylas.events.destroy({
identifier: grantId,
eventId,
queryParams: { calendarId },
});
}
Email Operations
// Send an email
async function sendEmail(
grantId: string,
to: Array<{ name?: string; email: string }>,
subject: string,
body: string,
replyToMessageId?: string
) {
const message = await nylas.messages.send({
identifier: grantId,
requestBody: {
to,
subject,
body,
replyToMessageId,
},
});
return message.data;
}
// List recent emails
async function listRecentEmails(grantId: string, limit: number = 20) {
const messages = await nylas.messages.list({
identifier: grantId,
queryParams: {
limit,
in: ["INBOX"],
},
});
return messages.data;
}
// Read a single message with full body
async function readMessage(grantId: string, messageId: string) {
const message = await nylas.messages.find({
identifier: grantId,
messageId,
});
return message.data;
}
// Search emails
async function searchEmails(grantId: string, query: string) {
const messages = await nylas.messages.list({
identifier: grantId,
queryParams: {
searchQueryNative: query,
limit: 50,
},
});
return messages.data;
}
Scheduling with Nylas
// Create a scheduling configuration for a user
async function createSchedulingConfig(grantId: string) {
const config = await nylas.scheduling.configurations.create({
identifier: grantId,
requestBody: {
participants: [
{
email: "host@example.com",
availability: {
calendarIds: ["primary"],
},
booking: {
calendarId: "primary",
},
},
],
availability: {
durationMinutes: 30,
availabilityRules: {
availabilityMethod: "collective",
buffer: { before: 10, after: 5 },
roundTo: 15,
},
},
eventBooking: {
title: "Meeting with {{invitee}}",
description: "Booked via scheduling API",
conferencingProvider: "Google Meet",
},
},
});
return config.data;
}
// Get available time slots
async function getAvailableSlots(configurationId: string, startTime: number, endTime: number) {
const availability = await nylas.scheduling.availability.get({
queryParams: {
configurationId,
startTime: startTime.toString(),
endTime: endTime.toString(),
},
});
return availability.data.timeSlots;
}
// Book a time slot
async function bookSlot(configurationId: string, startTime: number, endTime: number, invitee: { name: string; email: string }) {
const booking = await nylas.scheduling.bookings.create({
requestBody: {
configurationId,
startTime,
endTime,
guest: {
name: invitee.name,
email: invitee.email,
},
},
});
return booking.data;
}
Webhook Handling
import crypto from "crypto";
import express from "express";
function verifyNylasWebhook(rawBody: string, signature: string, secret: string): boolean {
const hmac = crypto.createHmac("sha256", secret);
hmac.update(rawBody);
const digest = hmac.digest("hex");
return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature));
}
const app = express();
app.post("/webhooks/nylas", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-nylas-signature"] as string;
if (!verifyNylasWebhook(req.body.toString(), signature, process.env.NYLAS_WEBHOOK_SECRET!)) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(req.body.toString());
for (const delta of event.data.deltas) {
switch (delta.type) {
case "event.created":
console.log(`Calendar event created: ${delta.object_data.id}`);
break;
case "event.updated":
console.log(`Calendar event updated: ${delta.object_data.id}`);
break;
case "message.created":
console.log(`New email from: ${delta.object_data.from?.[0]?.email}`);
break;
}
}
res.status(200).json({ success: true });
});
// Nylas sends a challenge parameter on subscription creation
app.get("/webhooks/nylas", (req, res) => {
const challenge = req.query.challenge as string;
res.status(200).send(challenge);
});
Best Practices
- Store the grant ID, not raw OAuth tokens -- Nylas manages token refresh internally; your app only needs the stable grant ID to make API calls.
- Use calendar IDs explicitly -- many accounts have multiple calendars; always let the user choose which calendar to read from and write to, defaulting to "primary".
- Enable webhook challenge verification -- the GET endpoint must return the challenge parameter as plain text for Nylas to activate the subscription.
- Handle provider differences gracefully -- not all features work identically across Gmail, Outlook, and Exchange; check the
providerfield and adapt UI affordances accordingly. - Paginate with cursors -- list endpoints return a
nextCursor; always follow it to retrieve complete result sets. - Use
expandRecurring: truefor event queries -- without this flag, recurring events return as a single master record instead of individual occurrences.
Anti-Patterns
- Storing raw provider tokens alongside Nylas tokens -- this creates a maintenance burden and risks token conflicts; let Nylas handle all provider auth.
- Ignoring grant status changes -- grants can become invalid when users revoke access; listen for
grant.expiredwebhooks and prompt re-authentication. - Fetching all events without time bounds -- unbounded event queries return years of data; always pass
startandendtimestamps. - Creating one webhook per grant -- use a single webhook endpoint for all grants; Nylas includes the grant ID in each webhook payload.
- Hardcoding provider-specific scopes -- scopes vary by provider; use Nylas-recommended scope sets and let the platform translate them.
- Treating email and calendar as always available -- some grants may only have calendar or email permissions; check the grant's scope before calling endpoints that require specific access.
Install this skill directly: skilldb add scheduling-services-skills
Related Skills
Acuity Scheduling
Acuity Scheduling API integration for appointment booking, availability management, and calendar sync
Cal.com
"Cal.com: open-source scheduling, booking API, event types, availability, webhooks, embeds, self-hosted"
Calendly
"Calendly API: scheduling links, event types, invitees, webhooks, organization management, OAuth, REST API"
Cronofy
"Cronofy: calendar API, availability, scheduling, real-time sync, conferencing, UI elements, enterprise calendar integration"
Doodle
Doodle API integration for group scheduling polls, 1:1 booking pages, and meeting coordination
Microsoft Bookings
Microsoft Bookings integration via Microsoft Graph API for enterprise appointment scheduling and calendar management