Skip to main content
RapidDev - Software Development Agency
v0-integrationsNext.js API Route

How to Integrate Doodle with V0

To integrate Doodle with a V0 by Vercel app, create a Next.js API route that calls the Doodle REST API v2 using OAuth2 Bearer token authentication. Generate your scheduling interface with V0, then connect it to API routes that create polls, fetch poll results, and display participant availability. Store your Doodle API credentials in Vercel environment variables to keep them secure.

What you'll learn

  • How to generate a meeting scheduling UI with availability calendars using V0
  • How to authenticate with the Doodle API using OAuth2 client credentials
  • How to create Doodle scheduling polls via a Next.js API route
  • How to fetch poll results and participant availability from the Doodle API
  • How to embed the Doodle poll UI or display poll results in your V0 app
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate15 min read60 minutesProductivityMarch 2026RapidDev Engineering Team
TL;DR

To integrate Doodle with a V0 by Vercel app, create a Next.js API route that calls the Doodle REST API v2 using OAuth2 Bearer token authentication. Generate your scheduling interface with V0, then connect it to API routes that create polls, fetch poll results, and display participant availability. Store your Doodle API credentials in Vercel environment variables to keep them secure.

Add Meeting Scheduling Polls to Your V0 App with the Doodle API

Doodle is the go-to tool for finding meeting times when multiple people need to align on availability. With millions of polls created every month, Doodle has built-in familiarity — participants know how to use it without instructions. Integrating Doodle into your V0 app lets you embed scheduling capabilities directly in your product without building a full calendar availability system from scratch.

The integration works in two directions: creating polls (for app users who want to schedule a meeting with their clients or team) and displaying poll results (showing which times have the most availability). Common use cases include embedding a 'Schedule a Demo' flow where leads can propose meeting times, a team coordinator tool that creates polls for recurring sync meetings, and client portals where project kickoffs are scheduled without email back-and-forth.

Doodle's API v2 provides endpoints for creating polls with proposed time slots, updating poll options, fetching participant responses, and marking the winning time slot as the final meeting time. Access requires registering as a Doodle partner to get API credentials — this is a formal application process, though Doodle does provide sandbox access for development.

Integration method

Next.js API Route

Doodle integrates with V0 apps through Next.js API routes that use Doodle's REST API v2 with OAuth2 authentication. The API route creates scheduling polls, retrieves poll responses, and returns participant availability data to V0-generated scheduling interface components. Doodle API access requires an OAuth2 application registered with Doodle's partner program.

Prerequisites

  • A Doodle account and API credentials — apply for developer access at doodle.com/api or contact Doodle's partner program (standard Doodle accounts cannot access the API directly)
  • Doodle OAuth2 client ID and client secret from your registered application
  • Understanding that Doodle's API is gated behind a partner program — sandbox access is available during development
  • A V0 project exported to GitHub and deployed on Vercel
  • Basic understanding of Next.js API routes and OAuth2 flows

Step-by-step guide

1

Generate the Scheduling UI with V0

Before working with the Doodle API, use V0 to generate the scheduling interface. Scheduling UIs have specific interaction patterns — time slot selection grids, participant availability matrices, and confirmation flows — that V0 handles well when described clearly. For a poll creation flow, you need: a step to define the meeting title and duration, a step to select proposed time slots (typically a calendar with clickable time blocks), and a step to add participant email addresses. For a poll results view, you need: a grid showing time slots as columns and participant names as rows, with color-coded cells (green = available, red = unavailable, yellow = if need be), and a highlighted best time slot. Ask V0 to generate the time slot selection UI as a responsive calendar grid. Describe the interaction: clicking a date expands time slot options for that day, clicking a time slot toggles it as proposed. Show selected slots in a summary list on the right side. For mobile, collapse the calendar to a vertical list of date + time options. For the availability matrix (showing poll results), a CSS grid or table with color-coded cells works well. Ask V0 to generate this with a 'Most available' badge on the time slot with the highest participant count. The participant names column should be sticky on horizontal scroll for wide tables with many time slots.

V0 Prompt

Create a Doodle poll creation wizard with 3 steps: Step 1: meeting title input and duration selector (30/45/60/90 min buttons). Step 2: a 2-week calendar where clicking a date shows AM/PM/Evening time slots as checkboxes to add as options. Show selected time count. Step 3: participant email input with Add button showing tags of added emails. Bottom nav: Back/Next buttons. Final step shows a preview of the poll link and a 'Create Poll' button.

Paste this in V0 chat

Pro tip: Ask V0 to generate the availability matrix as a component that accepts a `slots` array and `participants` array as props — this makes it easy to populate with real Doodle API data later.

Expected result: A multi-step scheduling wizard UI with time slot selection calendar, participant management, and a poll results availability matrix, all using placeholder data.

2

Authenticate with the Doodle API and Create the API Route

Create a Next.js API route at `app/api/doodle/route.ts` that handles Doodle API authentication and proxies poll operations. Doodle uses OAuth2 client credentials flow for server-to-server authentication — exchange your client ID and secret for an access token, then use that token for API calls. Doodle's token endpoint is `https://api.doodle.com/oauth/token` with parameters: `grant_type=client_credentials`, `client_id={your_id}`, `client_secret={your_secret}`. The token response includes `access_token` (valid for a limited time, typically 1 hour) and `token_type: Bearer`. Doodle's REST API v2 base URL is `https://api.doodle.com/api/v2/`. Key endpoints: - `POST /polls` — create a new poll with title, options (time slots), and invitee emails - `GET /polls/{pollId}` — fetch poll details including all options and participant responses - `PUT /polls/{pollId}/participants/{participantId}` — update participant availability - `POST /polls/{pollId}/book` — mark a time slot as the confirmed booking The poll creation payload requires a `type` (standard, book, or signup), `title`, `options` (array of proposed time slots as ISO datetime strings), and `initiator` (name/email of poll creator). Time slot options are structured as `{ start: ISO_datetime, end: ISO_datetime }` objects. Cache the access token with expiry tracking to avoid refreshing it on every API call. Use a module-level variable similar to the Zoho CRM pattern — store the token and its expiry timestamp, check if it's still valid before making a new token request. Note: if you don't yet have Doodle API credentials and are in development, implement mock responses that match the Doodle API response format. This lets you build and test the UI integration before API access is provisioned.

app/api/doodle/route.ts
1// app/api/doodle/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3
4const DOODLE_BASE = 'https://api.doodle.com/api/v2';
5const DOODLE_TOKEN_URL = 'https://api.doodle.com/oauth/token';
6const CLIENT_ID = process.env.DOODLE_CLIENT_ID!;
7const CLIENT_SECRET = process.env.DOODLE_CLIENT_SECRET!;
8
9let tokenCache: { token: string; expiresAt: number } | null = null;
10
11async function getToken(): Promise<string> {
12 if (tokenCache && Date.now() < tokenCache.expiresAt - 60_000) {
13 return tokenCache.token;
14 }
15 const res = await fetch(DOODLE_TOKEN_URL, {
16 method: 'POST',
17 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
18 body: new URLSearchParams({
19 grant_type: 'client_credentials',
20 client_id: CLIENT_ID,
21 client_secret: CLIENT_SECRET,
22 }),
23 });
24 if (!res.ok) throw new Error(`Doodle token error: ${res.status}`);
25 const data = await res.json();
26 tokenCache = {
27 token: data.access_token,
28 expiresAt: Date.now() + (data.expires_in || 3600) * 1000,
29 };
30 return data.access_token;
31}
32
33async function doodleFetch(method: string, path: string, body?: object) {
34 const token = await getToken();
35 const res = await fetch(`${DOODLE_BASE}${path}`, {
36 method,
37 headers: {
38 Authorization: `Bearer ${token}`,
39 'Content-Type': 'application/json',
40 Accept: 'application/json',
41 },
42 body: body ? JSON.stringify(body) : undefined,
43 });
44 if (!res.ok) {
45 const error = await res.text();
46 throw new Error(`Doodle API error ${res.status}: ${error}`);
47 }
48 return res.json();
49}
50
51export async function POST(request: NextRequest) {
52 const { action, ...data } = await request.json();
53
54 try {
55 if (action === 'create_poll') {
56 const poll = await doodleFetch('POST', '/polls', {
57 title: data.title,
58 type: 'standard',
59 options: data.options, // [{ start: ISO, end: ISO }]
60 initiator: data.initiator, // { name, email }
61 invitees: data.invitees?.map((email: string) => ({ email })) || [],
62 });
63 return NextResponse.json(poll);
64 }
65 return NextResponse.json({ error: 'Unknown action' }, { status: 400 });
66 } catch (err: any) {
67 return NextResponse.json({ error: err.message }, { status: 500 });
68 }
69}
70
71export async function GET(request: NextRequest) {
72 const { searchParams } = new URL(request.url);
73 const pollId = searchParams.get('pollId');
74
75 if (!pollId) return NextResponse.json({ error: 'pollId required' }, { status: 400 });
76
77 try {
78 const poll = await doodleFetch('GET', `/polls/${pollId}`);
79 return NextResponse.json(poll);
80 } catch (err: any) {
81 return NextResponse.json({ error: err.message }, { status: 500 });
82 }
83}

Pro tip: If you don't have Doodle API credentials yet, build the UI with mock data and implement a 'demo mode' that uses hardcoded poll data. When real API credentials arrive, simply swap in the real API calls without changing the UI components.

Expected result: The API route creates polls via POST and fetches poll details via GET, with OAuth2 token caching to avoid redundant token requests.

3

Configure Vercel Environment Variables

Navigate to your Vercel project → Settings → Environment Variables. Add the credentials for your Doodle API application. Add `DOODLE_CLIENT_ID` — the Client ID from your registered Doodle API application. Doodle provides this when you register as a partner or receive sandbox access through their developer program. Add `DOODLE_CLIENT_SECRET` — the Client Secret for your application. This is used with the Client ID to obtain OAuth2 access tokens. Store this as a server-only variable (no `NEXT_PUBLIC_` prefix) — it must never be exposed in the browser. To get Doodle API credentials, visit Doodle's developer documentation at doodle.com/api or contact Doodle's partner team. Doodle's API is not open to general public sign-up — you need to register as a technology partner. For development, Doodle typically provides sandbox credentials with limited functionality for testing your integration before going live. For Vercel Preview environments (used for pull request previews), use the same sandbox credentials. Only use production API credentials in the Production environment to avoid polluting your live account with test polls. After adding the environment variables, trigger a redeployment and test the token acquisition by checking your server logs — a successful token response means the credentials are correct. If you're building this integration before receiving Doodle API access, add placeholder values and implement a `DOODLE_DEMO_MODE=true` environment variable. When demo mode is on, your API route returns hardcoded mock data instead of calling the Doodle API.

Pro tip: Doodle API credentials are different from your regular Doodle login. Your API client ID/secret are application credentials managed in Doodle's partner portal, not your personal Doodle account password.

Expected result: DOODLE_CLIENT_ID and DOODLE_CLIENT_SECRET set in Vercel environment variables. Token acquisition test passes in server logs on next deployment.

4

Connect the Scheduling Wizard to Create Real Polls

Wire the V0-generated scheduling wizard to your Doodle API route. When the user completes the final step of the wizard and clicks 'Create Poll', POST the collected data to `/api/doodle` with `action: 'create_poll'`. The key data transformation is converting selected time slots from your UI's format to Doodle's ISO datetime format. Your UI likely stores selected slots as date strings and time ranges ('2026-04-15 afternoon'). Convert these to specific ISO start and end times: `{ start: '2026-04-15T14:00:00', end: '2026-04-15T16:00:00' }`. After successful poll creation, Doodle returns the created poll object including a `participationUrl` — the link for participants to respond. Display this URL prominently as a copyable link. Also show a QR code of this URL using a library like `qrcode.react` so it can be shared in presentations or printed. For displaying poll results, poll regularly using the `/api/doodle?pollId={id}` endpoint or use a 'refresh' button. Doodle's poll response object includes `participants` (array of respondents with their availability choices) and `options` (the time slots). Map this to your availability matrix component — create a 2D structure where rows are participants and columns are time slots. To determine the 'best time', count votes per option and highlight the option with the most 'yes' responses. Handle ties by showing all tied options as equally highlighted. RapidDev's team can help implement more sophisticated scheduling logic like 'required attendees vs optional' if your use case requires it. For the embedded Doodle approach (alternative to the custom UI): Doodle provides an embed URL format where you can iframe a Doodle poll directly into your page. The embed URL is `https://doodle.com/poll/{pollId}` in an iframe — this is simpler but gives you less control over the visual design.

V0 Prompt

Connect the scheduling wizard's final step to POST to /api/doodle with action 'create_poll'. Include the meeting title, duration, time slots formatted as ISO datetimes, and participant emails. On success, show the poll URL as a large copyable link with a copy button. Display a QR code of the URL using the qrcode.react library. Add a 'View Results' section below that fetches poll results from /api/doodle?pollId=X every 30 seconds and displays them as a table: rows=participants, columns=time slots, cells=green checkmark or red X.

Paste this in V0 chat

lib/doodle-helpers.ts
1// Helper: convert UI time selection to Doodle ISO format
2export function slotsToISOOptions(
3 selections: { date: string; hour: number; duration: number }[]
4): { start: string; end: string }[] {
5 return selections.map(({ date, hour, duration }) => {
6 const start = new Date(`${date}T${hour.toString().padStart(2, '0')}:00:00`);
7 const end = new Date(start.getTime() + duration * 60 * 1000);
8 return {
9 start: start.toISOString(),
10 end: end.toISOString(),
11 };
12 });
13}
14
15// Helper: compute best time from poll response
16export function getBestSlot(poll: {
17 options: { id: string; start: string; end: string }[];
18 participants: { preferences: { optionId: string; availability: string }[] }[];
19}): string | null {
20 const voteCounts: Record<string, number> = {};
21 poll.options.forEach(opt => { voteCounts[opt.id] = 0; });
22 poll.participants.forEach(p => {
23 p.preferences.forEach(pref => {
24 if (pref.availability === 'yes') voteCounts[pref.optionId] = (voteCounts[pref.optionId] || 0) + 1;
25 });
26 });
27 const maxVotes = Math.max(...Object.values(voteCounts));
28 if (maxVotes === 0) return null;
29 return Object.entries(voteCounts).find(([, v]) => v === maxVotes)?.[0] || null;
30}

Pro tip: Doodle polls have a time-limited participation window. When displaying the poll creation confirmation, show the poll's expiry date so participants know they need to respond before it expires.

Expected result: The scheduling wizard creates real Doodle polls on submission. The confirmation screen shows a working poll URL. The results view updates automatically every 30 seconds with participant responses.

Common use cases

Sales Demo Scheduling Flow

Build a 'Schedule a Demo' page where prospects select their available times from a calendar, triggering a Doodle poll that syncs with the sales team's availability. The sales team responds in Doodle, and the app shows the confirmed meeting time to the prospect.

V0 Prompt

Create a demo scheduling page with a 2-week calendar where users can click time slots to mark availability (morning, afternoon, evening segments). Show a summary of selected times with a 'Send Availability' button. On submit, display a confirmation message: 'Thanks! We'll confirm your demo within 24 hours.' Include a list of what the demo covers (bullet points).

Copy this prompt to try it in V0

Team Meeting Coordinator

Build an internal tool that creates Doodle polls for recurring team meetings, displays current poll results with color-coded availability heatmaps, and announces the confirmed meeting time to the team's Slack channel when finalized.

V0 Prompt

Create a meeting scheduler tool with: a form to set meeting title, description, and duration (30/60/90 mins), a calendar date picker to add proposed time options (multiple dates/times), a list of participants to invite by email, and a 'Create Doodle Poll' button. After creation, show the poll URL and an embed of the current responses as a table with participant names as rows and time slots as columns.

Copy this prompt to try it in V0

Client Project Kickoff Scheduler

Embed a scheduling component in a client portal where new clients can immediately book their project kickoff call. The component creates a Doodle poll with the agency's available slots and sends the client a direct link to respond.

V0 Prompt

Create a project kickoff scheduling card that shows 'Schedule your kickoff call' with a description, a set of available time options as selectable pills (e.g., 'Tue Apr 15, 2:00 PM', 'Wed Apr 16, 10:00 AM'), a name and email input, and a 'Submit Availability' button. Show a 'Poll sent!' confirmation with a copy-link button for the Doodle poll URL.

Copy this prompt to try it in V0

Troubleshooting

Token request returns 401 or 'invalid_client'

Cause: The DOODLE_CLIENT_ID or DOODLE_CLIENT_SECRET is incorrect, or the application hasn't been approved by Doodle's partner program.

Solution: Verify credentials are copied exactly from the Doodle partner portal with no extra whitespace. Confirm your Doodle developer application status — sandbox access may have different credentials from production. Contact Doodle's developer support if credentials seem correct but authentication still fails.

Poll creation returns 422 Unprocessable Entity

Cause: The poll creation payload is missing required fields or the time slot format is incorrect. Doodle requires options to have valid ISO datetime strings with a future start time.

Solution: Verify the options array contains objects with 'start' and 'end' as valid ISO 8601 datetime strings (e.g., '2026-04-15T14:00:00Z'). Ensure the start time is in the future. The initiator object must include at least a name field.

typescript
1// Valid poll creation payload example:
2const payload = {
3 title: 'Team Standup',
4 type: 'standard',
5 options: [
6 { start: '2026-04-15T14:00:00Z', end: '2026-04-15T15:00:00Z' },
7 { start: '2026-04-16T10:00:00Z', end: '2026-04-16T11:00:00Z' },
8 ],
9 initiator: { name: 'Alice', email: 'alice@example.com' },
10};

Poll results show no participants despite people responding

Cause: The poll ID used to fetch results may be incorrect, or there's a delay between participant response and when the response appears in the API.

Solution: Verify the poll ID returned from poll creation and used in the fetch. Doodle may have a short processing delay before responses appear. Implement a manual 'Refresh Results' button in addition to the polling interval so users can force an update.

Access token expires mid-session causing failed API calls

Cause: The module-level token cache is only preserved within a serverless function instance. A new deployment or cold start resets the cache, forcing a new token request.

Solution: The token refresh logic in the code handles expiry checking — a new token is obtained automatically when the cached one expires or when the cache is empty after a cold start. This is expected behavior. Each new token request adds ~200ms latency, which is acceptable.

Best practices

  • Cache OAuth2 access tokens in a module-level variable with expiry tracking to minimize token refresh overhead on repeated API calls
  • Never expose Doodle client credentials in client-side code — all Doodle API calls must go through server-side Next.js API routes
  • Implement a demo mode with mock poll data for development while waiting for Doodle API access to be provisioned
  • Always validate time slots are in the future before sending to Doodle — past time slots cause validation errors
  • Display poll expiry dates prominently — Doodle polls have a limited participation window and users need to know the deadline
  • Provide a 'Copy link' button for the poll participation URL — email delivery of Doodle invites can be unreliable, direct sharing is more dependable
  • Handle the case where no participants have responded yet — show an appropriate empty state rather than an empty matrix

Alternatives

Frequently asked questions

Is the Doodle API publicly available for developers?

Doodle's API requires registering as a technology partner through their official partner program. It's not a self-serve API that anyone can sign up for — you need to apply and be approved. Doodle does provide sandbox credentials for development purposes during the approval process. If you need scheduling features without going through a partner approval, consider alternatives like Calendly's API (which has a public developer program) or building a lightweight custom scheduling feature using your own database.

Can I embed a Doodle poll directly in my V0 app?

Yes, the simplest embedding approach is using an iframe with the Doodle poll participation URL. Doodle polls are designed to be embeddable and respond well to iframe constraints. However, this means participants see Doodle's own UI rather than your app's custom design. For a fully branded experience, use the API to fetch poll data and render it with your own components as described in this guide.

What's the difference between Doodle's poll types?

Doodle offers three poll types via the API: 'standard' (classic availability poll where participants mark yes/no/if-need-be for each time slot), 'book' (one-on-one booking where the first person to claim a slot locks it), and 'signup' (volunteers sign up for specific slots like event shifts). For meeting scheduling, 'standard' is the most common type. 'book' is useful for appointment scheduling where each slot can only be taken by one person.

How many time slot options can a Doodle poll have?

Doodle supports up to 15 options in a standard poll. For typical meeting scheduling (proposing 5-7 time slots across 2 weeks), this is more than sufficient. If you need to propose more than 15 time slots, create multiple polls for different date ranges or use a different scheduling approach.

Can I send automatic email reminders to poll participants?

Doodle's platform sends automated email notifications to invited participants when you include their email addresses in the poll's invitees list. The notification timing and content are controlled by Doodle's platform, not the API. For custom reminder logic (e.g., nudging non-responders after 24 hours), you'd need to track response status via the API and send reminders through your own email service like Mailgun or SendGrid.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.