Doodle
Doodle API integration for group scheduling polls, 1:1 booking pages, and meeting coordination
You are an expert in integrating the Doodle API for group scheduling polls and meeting coordination. ## Key Points - Enable the "if need be" option on polls to capture soft availability; this significantly increases the chance of finding a time that works for large groups. - Set a deadline on polls to prevent them from staying open indefinitely; automate a reminder or auto-close workflow as the deadline approaches. - Use the hidden poll setting for sensitive scheduling (e.g., interviews) so participants cannot see each other's responses. - Distribute the `participantUrl` (not the `adminUrl`) to participants; the admin URL grants edit and close permissions. - For recurring group meetings, create polls programmatically with a template of time options based on historical preferences. - Doodle's free tier has API rate limits and restricted access; premium plans are required for full API functionality including webhooks and booking pages. - Poll options are positional — when analyzing preferences, match by index rather than by option ID, since the preferences array aligns with the options array order. - Closing a poll is irreversible; once finalized, the poll cannot be reopened. Always confirm with the organizer before programmatically closing. - The API does not natively send calendar invites upon finalization; your integration must generate and send ICS files or create calendar events separately. - Participant names are free-text and not deduplicated; the same person can submit multiple responses under different names unless you enforce email-based identity through poll settings. ## Quick Example ```bash # Obtain an API key from Doodle's developer portal export DOODLE_API_KEY="your-api-key" ```
skilldb get scheduling-services-skills/DoodleFull skill: 296 linesDoodle — Scheduling Integration
You are an expert in integrating the Doodle API for group scheduling polls and meeting coordination.
Core Philosophy
Overview
Doodle is a scheduling platform best known for group polling — finding a time that works for multiple participants. Beyond polls, Doodle offers 1:1 Booking Pages for direct appointment scheduling. The REST API enables programmatic creation and management of polls, retrieval of participant responses, and automation of meeting finalization. Doodle is strongest when you need consensus-based scheduling among groups where not everyone shares the same calendar system.
Setup & Configuration
API Key
# Obtain an API key from Doodle's developer portal
export DOODLE_API_KEY="your-api-key"
Client Setup
import axios, { AxiosInstance } from "axios";
function createDoodleClient(apiKey: string): AxiosInstance {
return axios.create({
baseURL: "https://doodle.com/api/v2.0",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
}
const doodle = createDoodleClient(process.env.DOODLE_API_KEY!);
Core Patterns
Creating a Group Poll
interface PollOption {
start: string; // ISO 8601 datetime
end: string;
}
interface CreatePollParams {
title: string;
description?: string;
options: PollOption[];
settings?: {
deadline?: string;
multiDay?: boolean;
ifNeedBe?: boolean; // allow "if need be" responses
hidden?: boolean; // hide participant responses from each other
askAddress?: boolean;
askEmail?: boolean;
askPhone?: boolean;
};
}
interface Poll {
id: string;
title: string;
state: "OPEN" | "CLOSED" | "CANCELED";
adminUrl: string;
participantUrl: string;
options: PollOption[];
participants: Participant[];
finalOption?: PollOption;
}
async function createPoll(params: CreatePollParams): Promise<Poll> {
const { data } = await doodle.post("/polls", {
title: params.title,
description: params.description,
options: params.options.map((opt) => ({
start: opt.start,
end: opt.end,
})),
settings: params.settings || {},
});
return data;
}
Adding Participants and Responses
interface Participant {
id: string;
name: string;
preferences: ParticipantPreference[];
}
interface ParticipantPreference {
optionId: string;
preference: "yes" | "no" | "ifNeedBe";
}
async function addParticipant(
pollId: string,
name: string,
preferences: ParticipantPreference[]
): Promise<Participant> {
const { data } = await doodle.post(`/polls/${pollId}/participants`, {
name,
preferences,
});
return data;
}
Retrieving Poll Results
interface PollResults {
poll: Poll;
bestOptions: {
option: PollOption;
yesCount: number;
ifNeedBeCount: number;
noCount: number;
}[];
}
async function getPoll(pollId: string): Promise<Poll> {
const { data } = await doodle.get(`/polls/${pollId}`);
return data;
}
function analyzePollResults(poll: Poll): PollResults {
const optionScores = poll.options.map((option, index) => {
let yesCount = 0;
let ifNeedBeCount = 0;
let noCount = 0;
for (const participant of poll.participants) {
const pref = participant.preferences[index];
if (pref?.preference === "yes") yesCount++;
else if (pref?.preference === "ifNeedBe") ifNeedBeCount++;
else noCount++;
}
return { option, yesCount, ifNeedBeCount, noCount };
});
// Sort by most "yes" votes, then by fewest "no" votes
optionScores.sort((a, b) => {
if (b.yesCount !== a.yesCount) return b.yesCount - a.yesCount;
return a.noCount - b.noCount;
});
return { poll, bestOptions: optionScores };
}
Finalizing a Poll
async function finalizePoll(
pollId: string,
chosenOptionIndex: number
): Promise<Poll> {
const { data } = await doodle.post(`/polls/${pollId}/close`, {
selectedOptionIndex: chosenOptionIndex,
});
return data;
}
// Auto-finalize: pick the option with the most "yes" votes
async function autoFinalizePoll(pollId: string): Promise<Poll> {
const poll = await getPoll(pollId);
const results = analyzePollResults(poll);
if (results.bestOptions.length === 0) {
throw new Error("No options available to finalize");
}
const bestOptionIndex = poll.options.indexOf(
results.bestOptions[0].option
);
return finalizePoll(pollId, bestOptionIndex);
}
1:1 Booking Pages
interface BookingPage {
id: string;
name: string;
slug: string;
url: string;
duration: number; // minutes
location: string;
}
async function listBookingPages(): Promise<BookingPage[]> {
const { data } = await doodle.get("/booking-pages");
return data;
}
async function getBookingPageAppointments(
bookingPageId: string,
from?: string,
to?: string
): Promise<any[]> {
const { data } = await doodle.get(
`/booking-pages/${bookingPageId}/appointments`,
{ params: { from, to } }
);
return data;
}
Webhook Handling
import express, { Request, Response } from "express";
interface DoodleWebhookEvent {
type:
| "poll.participant_added"
| "poll.closed"
| "poll.deleted"
| "booking.created"
| "booking.canceled";
data: {
pollId?: string;
participantId?: string;
bookingId?: string;
};
}
function setupDoodleWebhooks(app: express.Application): void {
app.post("/webhooks/doodle", (req: Request, res: Response) => {
const event: DoodleWebhookEvent = req.body;
switch (event.type) {
case "poll.participant_added":
// Check if all expected participants have responded
checkPollCompletion(event.data.pollId!);
break;
case "poll.closed":
// Sync finalized time to your calendar system
syncFinalizedMeeting(event.data.pollId!);
break;
case "booking.created":
handleNewBooking(event.data.bookingId!);
break;
case "booking.canceled":
handleBookingCancellation(event.data.bookingId!);
break;
}
res.status(200).send("OK");
});
}
Best Practices
- Enable the "if need be" option on polls to capture soft availability; this significantly increases the chance of finding a time that works for large groups.
- Set a deadline on polls to prevent them from staying open indefinitely; automate a reminder or auto-close workflow as the deadline approaches.
- Use the hidden poll setting for sensitive scheduling (e.g., interviews) so participants cannot see each other's responses.
- Distribute the
participantUrl(not theadminUrl) to participants; the admin URL grants edit and close permissions. - For recurring group meetings, create polls programmatically with a template of time options based on historical preferences.
Common Pitfalls
- Doodle's free tier has API rate limits and restricted access; premium plans are required for full API functionality including webhooks and booking pages.
- Poll options are positional — when analyzing preferences, match by index rather than by option ID, since the preferences array aligns with the options array order.
- Closing a poll is irreversible; once finalized, the poll cannot be reopened. Always confirm with the organizer before programmatically closing.
- The API does not natively send calendar invites upon finalization; your integration must generate and send ICS files or create calendar events separately.
- Participant names are free-text and not deduplicated; the same person can submit multiple responses under different names unless you enforce email-based identity through poll settings.
Anti-Patterns
Using the service without understanding its pricing model. Cloud services bill differently — per request, per GB, per seat. Deploying without modeling expected costs leads to surprise invoices.
Hardcoding configuration instead of using environment variables. API keys, endpoints, and feature flags change between environments. Hardcoded values break deployments and leak secrets.
Ignoring the service's rate limits and quotas. Every external API has throughput limits. Failing to implement backoff, queuing, or caching results in dropped requests under load.
Treating the service as always available. External services go down. Without circuit breakers, fallbacks, or graceful degradation, a third-party outage becomes your outage.
Coupling your architecture to a single provider's API. Building directly against provider-specific interfaces makes migration painful. Wrap external services in thin adapter layers.
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"
Microsoft Bookings
Microsoft Bookings integration via Microsoft Graph API for enterprise appointment scheduling and calendar management
Nylas
"Nylas: unified calendar/email/contacts API, scheduling, calendar CRUD, email send/read, OAuth providers, Node SDK"