Call2Me
All posts
Integrations

Connect a voice AI agent to Google Calendar (real booking, not a demo)

Let your phone agent check availability and book appointments straight into Google Calendar. Here's the function-call + webhook pattern that actually works in production.

CTCall2Me Team
June 1, 20263 min read
Voice AI agent connected to Google Calendar for appointment booking

"Can your AI agent actually book the appointment?" is the question that turns a voice demo into a paying use case. A bot that says "I'll have someone call you back" is a toy. A bot that checks your real Google Calendar, offers open times, and writes the booking — that replaces a receptionist.

Here's the pattern that works in production, end to end.

Build it free first

$5 in credits, no card. Spin up a booking agent and test the whole flow in the browser before you attach a phone number.

Start free on Call2Me →

The architecture (one diagram in words)

The model never touches Google. It calls two tools you expose, and your backend — which holds the Google OAuth token — does the real work:

  1. check_availability(date) → your backend hits Google Calendar's freebusy API, returns open slots.
  2. book_slot(start, name, phone) → your backend re-checks the slot is still free, then creates the event.

The agent's job is conversation. Your backend's job is truth. That separation is the whole trick.

Step 1 — expose the tools to your agent

In Call2Me, an agent's tools are function definitions with an HTTP webhook behind them. Define two:

{
  "name": "check_availability",
  "description": "Return open appointment slots for a given date.",
  "parameters": { "date": "string (YYYY-MM-DD)" },
  "webhook": "https://your-backend.com/tools/availability"
}
{
  "name": "book_slot",
  "description": "Book an appointment. Confirm only after the caller agrees to a specific time.",
  "parameters": { "start": "string (ISO 8601)", "name": "string", "phone": "string" },
  "webhook": "https://your-backend.com/tools/book"
}

When the agent decides to call a tool, Call2Me POSTs to your webhook and speaks the response back to the caller. Same request/response shape you'd use for any function call.

Step 2 — check availability against the real calendar

Your availability handler asks Google what's busy and returns what's open:

from googleapiclient.discovery import build

def availability(date: str) -> dict:
    cal = build("calendar", "v3", credentials=load_google_creds())
    body = {
        "timeMin": f"{date}T09:00:00Z",
        "timeMax": f"{date}T17:00:00Z",
        "items": [{"id": "primary"}],
    }
    busy = cal.freebusy().query(body=body).execute()["calendars"]["primary"]["busy"]
    open_slots = subtract_busy(business_hours(date), busy)   # your slotting logic
    return {"slots": [s.isoformat() for s in open_slots]}

The agent now offers real times: "I've got 2pm, 3:30, or 4 — which works?"

Step 3 — book it (and handle the race)

The booking handler re-checks at write time, so two callers can't grab the same slot:

def book(start: str, name: str, phone: str) -> dict:
    cal = build("calendar", "v3", credentials=load_google_creds())
    if not still_free(cal, start):
        return {"ok": False, "speak": "That time just filled — I have the next slot open instead."}
    cal.events().insert(calendarId="primary", body={
        "summary": f"Appointment — {name}",
        "description": f"Booked by voice agent. Caller: {phone}",
        "start": {"dateTime": start}, "end": {"dateTime": plus_30min(start)},
    }).execute()
    return {"ok": True, "speak": "You're booked. You'll get a confirmation by text."}

The speak field is what the agent says next — you control the recovery line, the model handles the conversation around it.

Why not let the model call Google directly?

Two reasons, both about safety:

  • Credentials stay off the model. The Google OAuth token lives in your backend, never in a prompt or a tool the model can introspect.
  • Validation lives in one place. Business hours, buffer times, "no bookings within 2 hours," double-booking checks — all enforced server-side, so the agent literally cannot book something invalid no matter what the caller says.

What you end up with

A phone number that answers 24/7, checks your actual calendar, offers real times, books the appointment, and confirms — without a human. For a clinic, salon, garage, or restaurant, that's the difference between a missed call and a captured booking.

See the booking loop live

The fastest way to believe it is to watch your own calendar get a new event from a test call. Free to try — $5 in credits, no card.

Build your booking agent →

Want it wired through a no-code workflow instead of your own backend? The same two tools work as Zapier / Make steps — same contract, just a different runner.

Frequently asked

Q.Does the agent talk to Google Calendar directly?

No — and it shouldn't. The agent calls a function (a tool) you expose; your backend holds the Google OAuth token and talks to the Calendar API. This keeps credentials off the model and lets you add validation (business hours, buffers, double-booking checks) in one place.

Q.Can it check availability before booking?

Yes. Expose two tools: check_availability(date) and book_slot(start, name, phone). The agent calls the first to offer real open times, then the second to confirm. That two-step flow is what makes it feel like a real receptionist instead of a form.

Q.What happens if the slot was just taken?

Your book_slot handler does a freebusy check against the calendar at write time and returns a clear error the agent can speak ('that 3pm just filled — I have 3:30 or 4'). The model handles the recovery; you handle the truth source.

ShareX / TwitterLinkedIn

Try Call2Me free

Spin up a voice agent in 5 minutes. No credit card required.

Start free trial

Build your voice agent in 10 minutes

No code. No credit card. Just a phone number.

Get Started FreeTalk to Sales
Try the voice agent liveLive Demo