Skip to content

How to Add Appointment Booking to a Lovable App

Add real appointment booking to a Lovable app in about 20 minutes: a copy-paste Lovable prompt, a secure edge function, and the Vennio scheduling API doing the calendar work.

AI app builders like Lovable generate frontends quickly, but scheduling is a category of feature they get subtly wrong when asked to build it from scratch: availability calculation, conflict detection, timezone conversion, and calendar OAuth are edge-case-riddled by nature. The reliable pattern is to pair the generated app with a scheduling API that owns that logic. This guide walks through adding real appointment booking — live availability from a connected calendar, confirmed bookings, confirmation emails — to a Lovable app in about 20 minutes, using Lovable’s Cloud backend and the Vennio scheduling API.

The architecture is three pieces: a Vennio API key stored as a Lovable secret, an edge function that proxies two API calls, and a booking UI built against that proxy. The guide includes the exact prompt to paste into Lovable and the code Lovable should produce, so the output can be verified rather than trusted.

What You’ll Build

A booking flow inside the Lovable app: the visitor picks a date, sees available time slots pulled live from a connected Google or Microsoft calendar (with conflicts already excluded), fills in their name and email, and books. Vennio creates the calendar event, emails both sides a confirmation, and fires a booking.created webhook for any downstream automation.

What you don’t build: availability logic, conflict checking, timezone maths, calendar OAuth, or email sending. That is the case for using a scheduling API rather than asking an AI app builder to generate scheduling logic directly.

Prerequisites

You need a Lovable project (any app — this drops into an existing one) and a free Vennio account. Setup on the Vennio side takes five minutes:

  1. Sign up at vennio.app and connect a Google or Microsoft 365 calendar.
  2. Set an availability schedule (working hours) in the dashboard.
  3. Create an API key and copy it — it will be pasted into Lovable as a secret, never into a prompt.
  4. Copy the Business ID from the “Your Integration Credentials” panel on the Get Started page — this is the principal_id used in API calls, and it identifies whose calendar gets booked.

The free tier covers 1,000 bookings a month on one calendar — enough to build, test, and run a small production product.

Step 1: Store the API Key as a Secret

In the Lovable project, add a secret named VENNIO_API_KEY with the key as the value. Lovable’s Cloud backend keeps secrets server-side, available to edge functions but never shipped to the browser.

The single most common security mistake in vibe-coded apps is an API key pasted into frontend code, where anyone can read it from the bundle and book (or cancel) against the calendar. The rule this guide enforces throughout: the key lives in a secret, and only an edge function touches it.

Step 2: The Prompt

Paste this into Lovable:

Add an appointment booking feature to this app using the Vennio scheduling
API (https://api.vennio.app). Requirements:

1. Create an edge function called "vennio" that proxies two operations,
   using the VENNIO_API_KEY secret as a Bearer token. Never expose the
   key to the client.
   - "availability": GET https://api.vennio.app/v1/availability/slots
     for a given date range, a 30-minute duration, and the visitor's
     IANA timezone. Use principal ID <YOUR_PRINCIPAL_ID>.
   - "book": POST https://api.vennio.app/v1/bookings with JSON body:
     principal_id, customer_name, customer_email, start_time, end_time
     (ISO 8601 UTC), and optional notes.

2. Build a booking page with: a 7-day date strip, available slots for
   the selected day shown in the visitor's local timezone, and a
   name/email form. On submit, call "book", then show a confirmation
   screen with the booked time. Handle the case where a slot is taken
   between selection and booking by refreshing availability and asking
   the visitor to pick again.

3. Show a friendly empty state when a day has no slots. Show errors
   from the API as readable messages, not raw JSON.

Replace <YOUR_PRINCIPAL_ID> with the actual principal ID. The specificity is deliberate: naming the endpoints, the fields, and the failure case is what separates a first-try success from several rounds of corrective prompting.

Step 3: Verify What Lovable Built

Lovable will generate the edge function and UI. Reading the generated code takes thirty seconds and catches the failure modes that otherwise surface as production bugs. The edge function should look structurally like this:

// Edge function: vennio
const BASE = 'https://api.vennio.app';

export default async function handler(req: Request) {
  const key = Deno.env.get('VENNIO_API_KEY'); // secret, server-side only
  const { action, payload } = await req.json();

  if (action === 'availability') {
    const params = new URLSearchParams({
      principal_id: payload.principal_id,
      duration_minutes: '30',
      from: payload.from,          // ISO 8601, e.g. 2026-08-03T00:00:00Z
      to: payload.to,
      timezone: payload.timezone,  // e.g. Europe/London
    });
    const res = await fetch(`${BASE}/v1/availability/slots?${params}`, {
      headers: { Authorization: `Bearer ${key}` },
    });
    return Response.json(await res.json(), { status: res.status });
  }

  if (action === 'book') {
    const res = await fetch(`${BASE}/v1/bookings`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${key}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(payload), // principal_id, customer_name,
                                     // customer_email, start_time, end_time
    });
    return Response.json(await res.json(), { status: res.status });
  }

  return new Response('Unknown action', { status: 400 });
}

Three checks: the key is read from the environment (not hardcoded), all Vennio calls happen inside the function (a search of the frontend code for api.vennio.app should return nothing), and times are handled as ISO 8601 UTC on the wire with conversion to the visitor’s timezone only at display time.

Step 4: Test the Full Booking Loop

Book a real slot against the connected calendar. The full loop, in order: the slot list matches the calendar’s actual gaps, a confirmation screen appears after booking, the event lands on the connected calendar, and confirmation emails arrive at both addresses. Then test the double-booking case — book the same slot from two browser tabs. The second attempt should fail cleanly and refresh the slot list, because Vennio checks conflicts at booking time.

If availability comes back empty, it is almost always one of: calendar not connected, no schedule set, or a query window in the past. The dashboard shows the first two at a glance.

If booking does not need to be embedded in the app’s own UI, skip all of the above: generate a Venn Link — a hosted, shareable booking page — and point a button at it. It is the right call for “book a demo” links, MVPs, and anywhere the booking experience does not need to be the product’s own. The custom-UI pattern above is for when scheduling is part of the product, not just a page it links to.

Where This Goes Next

Once the basic flow works, the same edge function pattern extends to the rest of the API: event types for different meeting lengths, paid bookings via Stripe, webhooks that write bookings into the app’s own database, and — for agent-shaped products — the MCP server, which gives an AI agent the same availability and booking operations wired up above.

Full API reference at docs.vennio.app. If you build something with this, I’d like to see it — get in touch.