Fetch Calendly Events
Fetch scheduled events from Calendly with invitee details
Source Code
import fs from "fs";
import path from "path";
const [outputPath, minStartTime, maxStartTime, status = "active"] = process.argv.slice(2);
if (!outputPath) {
console.error("Error: outputPath is required");
console.error("Usage: node script.js session/calendly-events.json [minStartTime] [maxStartTime] [status]");
process.exit(1);
}
console.log("Fetching Calendly scheduled events...");
try {
// First get the current user to get their URI
const userRes = await fetch("https://api.calendly.com/users/me", {
headers: { Authorization: "Bearer PLACEHOLDER_TOKEN" },
});
const userData = await userRes.json();
if (userData.resource?.uri === undefined) {
throw new Error(`Failed to get user info: ${JSON.stringify(userData)}`);
}
const userUri = userData.resource.uri;
const userName = userData.resource.name;
const calendlyTimezone = userData.resource.timezone;
console.log(` User: ${userName}`);
console.log(` Calendly timezone: ${calendlyTimezone}`);
// Build query params
const params = new URLSearchParams({
user: userUri,
count: "100",
sort: "start_time:asc",
});
if (minStartTime && minStartTime !== "undefined" && minStartTime !== "null") {
params.set("min_start_time", minStartTime);
} else {
// Default to now if no min time specified
params.set("min_start_time", new Date().toISOString());
}
if (maxStartTime && maxStartTime !== "undefined" && maxStartTime !== "null") {
params.set("max_start_time", maxStartTime);
}
if (status && status !== "all") {
params.set("status", status);
}
// Fetch events with pagination
const events = [];
let nextPageToken = null;
do {
if (nextPageToken) {
params.set("page_token", nextPageToken);
}
const eventsRes = await fetch(
`https://api.calendly.com/scheduled_events?${params}`,
{ headers: { Authorization: "Bearer PLACEHOLDER_TOKEN" } }
);
const eventsData = await eventsRes.json();
if (eventsData.collection === undefined) {
throw new Error(`Failed to fetch events: ${JSON.stringify(eventsData)}`);
}
events.push(...eventsData.collection);
nextPageToken = eventsData.pagination?.next_page_token;
} while (nextPageToken);
console.log(` Found ${events.length} scheduled events`);
// Fetch invitees for each event (in parallel batches to avoid rate limits)
console.log(` Fetching invitee details...`);
const fetchInvitees = async (eventUuid) => {
const res = await fetch(
`https://api.calendly.com/scheduled_events/${eventUuid}/invitees?count=10`,
{ headers: { Authorization: "Bearer PLACEHOLDER_TOKEN" } }
);
const data = await res.json();
return data.collection || [];
};
// Process in batches of 5 to avoid rate limits
const batchSize = 5;
const eventInvitees = new Map();
for (let i = 0; i < events.length; i += batchSize) {
const batch = events.slice(i, i + batchSize);
const results = await Promise.all(
batch.map(async (event) => {
const uuid = event.uri.split("/").pop();
const invitees = await fetchInvitees(uuid);
return { uuid, invitees };
})
);
results.forEach(({ uuid, invitees }) => eventInvitees.set(uuid, invitees));
}
// Process events with invitee details
const processedEvents = events.map((event) => {
const startTime = new Date(event.start_time);
const endTime = new Date(event.end_time);
const durationMinutes = Math.round((endTime - startTime) / (1000 * 60));
const eventUuid = event.uri.split("/").pop();
// Get invitees for this event
const invitees = eventInvitees.get(eventUuid) || [];
const processedInvitees = invitees.map((inv) => ({
name: inv.name,
email: inv.email,
status: inv.status,
timezone: inv.timezone,
questionsAndAnswers: (inv.questions_and_answers || []).map((qa) => ({
question: qa.question,
answer: qa.answer,
})),
}));
// Extract location info
let locationInfo = null;
if (event.location) {
locationInfo = {
type: event.location.type,
location: event.location.location || null,
joinUrl: event.location.join_url || null,
};
}
return {
uuid: eventUuid,
uri: event.uri,
name: event.name,
status: event.status,
startTime: event.start_time,
endTime: event.end_time,
durationMinutes,
location: locationInfo,
invitees: processedInvitees,
inviteesCount: processedInvitees.length,
createdAt: event.created_at,
cancellation: event.cancellation || null,
};
});
// Group by date (using UTC date from ISO string)
const eventsByDate = {};
for (const event of processedEvents) {
const dateKey = event.startTime.split("T")[0];
if (!eventsByDate[dateKey]) {
eventsByDate[dateKey] = [];
}
eventsByDate[dateKey].push(event);
}
// Build output
const output = {
query: {
minStartTime: minStartTime || new Date().toISOString(),
maxStartTime: maxStartTime || null,
status,
},
user: {
name: userName,
calendlyTimezone,
},
summary: {
totalEvents: processedEvents.length,
datesWithEvents: Object.keys(eventsByDate).length,
},
events: processedEvents,
eventsByDate,
};
// Write output
const dir = path.dirname(outputPath);
if (dir && dir !== ".") fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(outputPath, JSON.stringify(output, null, 2));
console.log(`\n✓ Found ${processedEvents.length} events`);
console.log(` Output: ${outputPath}`);
// Show preview with invitee names
if (processedEvents.length > 0) {
console.log(`\n Preview:`);
processedEvents.slice(0, 5).forEach((e) => {
const time = e.startTime.split("T")[1]?.slice(0, 5) || "";
const date = e.startTime.split("T")[0];
const inviteeNames = e.invitees.map((i) => i.name).join(", ") || "No invitees";
console.log(` ${date} ${time} UTC - ${e.name} - ${inviteeNames}`);
});
if (processedEvents.length > 5) {
console.log(` ... and ${processedEvents.length - 5} more`);
}
}
console.log(
JSON.stringify({
success: true,
outputPath,
eventCount: processedEvents.length,
})
);
} catch (error) {
console.error("Failed:", error.message);
throw error;
}