Describe the problem:
Building hair salon booking system with Retell AI voice agent + n8n + Google Calendar. AI Agent is blocking entire event duration instead of only the active time window, making collision detection completely wrong.
Business Logic:
Some hair services have two time components:
- ACTIVE time = stylist physically working on client (blocks calendar for other appointments)
- PASSIVE time = client waiting while product processes (stylist is FREE to start another client)
Example: Hair coloring = 120 minutes total - 45 minutes active (stylist applying color)
- 75 minutes passive (client sits while color develops)
- Event in calendar: 14:00-16:00 (full duration)
- Stylist is ONLY busy: 14:00-14:45
- From 14:45-16:00: stylist can start new appointments
The Problem (CRITICAL):
SCENARIO:
- Existing appointment in Google Calendar:
- Service: Balayage (180 min: 60 active + 120 passive)
- Time: 13:00-16:00 (event start/end)
- ACTIVE_TIME stored in description: 13:00-14:00
- Customer calls asking for appointment at 14:00 for haircut (60 min, all active)
- EXPECTED behavior:
- Check: Does 14:00-15:00 overlap with 13:00-14:00? NO
- Return: available = TRUE
- ACTUAL behavior:
- AI Agent checks: Does 14:00-15:00 overlap with 13:00-16:00? YES
- Return: available = FALSE
- WRONG - it’s blocking the passive time 14:00-16:00 when stylist is actually free
Workflow Structure (detailed):
┌──────────────┐
│ Webhook │ ← Receives from Retell AI
│ │ Input: { time, service, worker }
└──────┬───────┘
│
▼
┌──────────────┐
│ Switch │ ← Routes by worker name
│ mode: Rules │ Outputs: "Alex", "Marta", "Carolina"
└──┬───┬───┬───┘
│ │ │
▼ ▼ ▼
Alex Marta Carolina (3 parallel branches, identical structure)
EACH BRANCH (Alex example):
┌─────────────────┐
│ AI Agent │ ← Receives webhook data
│ │ Has prompt with active/passive logic
└────────┬────────┘
│
├─→ [Tool: Google Calendar Get Many]
│ └─→ Connected to Alex's calendar
│ Operation: getAll event
│
└─→ [Tool: OpenAI Chat Model]
└─→ GPT model for reasoning
│
▼
┌─────────────────┐
│ Respond to │ ← Should return:
│ Webhook │ { available: bool, worker: string, time: string }
└─────────────────┘
How Data Flows:
- Webhook receives:
{
"body": {
"args": {
"time": "2026-03-18T14:00:00Z",
"service": "Balayage / sombre / ombre",
"worker": "Alex"
}
}
}
- Switch routes to Alex branch
- AI Agent has this data available:
{{$json.body.args.time}}= requested time{{$json.body.args.service}}= service name{{$json.body.args.worker}}= worker name
- AI Agent uses Google Calendar tool to fetch events from Alex’s calendar
- Google Calendar returns events like:
{
"summary": "Balayage - Jan Kowalski - 555123456",
"start": { "dateTime": "2026-03-18T13:00:00+01:00" },
"end": { "dateTime": "2026-03-18T16:00:00+01:00" },
"description": "Imię: Jan Kowalski\nTelefon: 555123456\nUsługa: Balayage / sombre / ombre\nStart wizyty: 2026-03-18T13:00:00Z\nACTIVE_TIME: 2026-03-18T13:00:00Z - 2026-03-18T14:00:00Z"
}
- AI Agent is supposed to:
- Extract ACTIVE_TIME from description: 13:00-14:00
- Calculate active time for requested service (Balayage = 60 min active)
- Check if 14:00-15:00 (new) overlaps with 13:00-14:00 (existing)
- Return available = TRUE (no overlap)
- WHAT ACTUALLY HAPPENS:
- AI Agent sees event 13:00-16:00 (start/end fields)
- Ignores or doesn’t correctly parse ACTIVE_TIME from description
- Checks if 14:00-15:00 overlaps with 13:00-16:00
- Returns available = FALSE (wrong!)
AI Agent Prompt (exact):
Role: Check appointment availability for hair salon.
Input data:
- Current time: {{$now}}
- Requested time: {{$json.body.args.time}}
- Service: {{$json.body.args.service}}
Service durations (CRITICAL - MUST USE THESE):
- Strzyżenie damskie krótkie: 40 min (40 active, 0 passive)
- Strzyżenie damskie długie: 60 min (60 active, 0 passive)
- Strzyżenie męskie klasyczne: 20 min (20 active, 0 passive)
- Koloryzacja jeden kolor: 120 min (45 active, 75 passive)
- Balayage / sombre / ombre: 180 min (60 active, 120 passive)
- Dekoloryzacja + farbowanie: 240 min (90 active, 150 passive)
Rules (AI MUST FOLLOW):
1. ACTIVE time = stylist working, blocks calendar
2. PASSIVE time = client waiting, does NOT block calendar
3. Check Google Calendar for existing appointments
4. For EACH existing appointment:
a. Look in the "description" field for line starting with "ACTIVE_TIME:"
b. If found: extract the start and end times from that line
c. If NOT found: use entire event duration as active time (fallback)
5. For the requested appointment:
a. Calculate active end time = requested time + active minutes from service list
6. Check collision:
a. Does requested active time overlap with existing active time?
b. Overlap formula: (requestedStart < existingActiveEnd) AND (requestedActiveEnd > existingActiveStart)
7. If NO overlap with ANY existing active time: return available = true
8. If overlap detected: return available = false
CRITICAL: Do NOT check against event.start/event.end - ONLY check against ACTIVE_TIME extracted from description!
Return format:
{
"available": true/false,
"worker": "Alex",
"time": "2026-03-18T14:00:00Z"
}
Google Calendar Tool Configuration:
- Resource: Event
- Operation: Get Many
- Calendar: [Alex’s calendar ID]
- Return All: false
- Limit: 50
- After: (should fetch events from requested date)
- Before: (should fetch events up to 24h after requested date)
What I’ve Tried:
- Current approach: AI Agent with detailed prompt
- Result: AI ignores ACTIVE_TIME, blocks entire event duration
- Prompt variations:
- “Extract ACTIVE_TIME from description field”
- “Only check active time, not total event time”
- “Parse description for ACTIVE_TIME: [start] - [end]”
- Result: No improvement, still blocks entire duration
- Explicit step-by-step instructions in prompt
- Numbered steps for extraction and comparison
- Result: Still doesn’t work
Why This is Critical:
This is the ONLY blocking issue preventing launch. The rest works:
book_appointment creates events correctly (with ACTIVE_TIME in description)
cancel_appointment works
reschedule_appointment works
check_availability is completely broken due to this active/passive time issue
Without this working, system blocks customers from booking valid time slots, making it unusable.
Expected vs Actual Examples:
Test Case 1:- Existing: Balayage 13:00-16:00 (ACTIVE: 13:00-14:00)
- Check: 14:00 Haircut (60 min active)
- Expected: TRUE (14:00-15:00 doesn’t overlap 13:00-14:00)
- Actual: FALSE

Test Case 2: - Existing: Balayage 13:00-16:00 (ACTIVE: 13:00-14:00)
- Check: 13:30 Haircut (60 min active)
- Expected: FALSE (13:30-14:30 overlaps 13:00-14:00)
- Actual: FALSE
(correct but for wrong reason - checking entire event)
Test Case 3: - Existing: None (empty calendar)
- Check: 14:00 Haircut
- Expected: TRUE
- Actual: TRUE

Questions:
- Can AI Agent actually handle this logic? Or is this too complex for LLM reasoning and I need to switch to deterministic Code node?
- Is there a way to force AI Agent to correctly parse and use ACTIVE_TIME from description field instead of event.start/event.end?
- Should I restructure the workflow completely? (e.g., Code node to extract ACTIVE_TIME → pass to AI Agent)
- Is this a known limitation of AI Agent + Google Calendar tool combination?
What is the error message (if any)?
No error message - workflow executes successfully but returns incorrect availability status (available: false when should be true due to active/passive time logic not working).
Please share your workflow:
Cannot share full workflow JSON due to sensitive Calendar IDs, but the structure is:
Webhook → Switch (mode: Rules, routes by worker: Alex/Marta/Carolina) → AI Agent (connected tools: Google Calendar Get Many + OpenAI Chat Model) → Respond to Webhook
Each worker branch (Alex/Marta/Carolina) is identical except for the Google Calendar ID used in the Calendar tool.
Key configuration:
- Switch node: Routes based on
{{$json.body.args.worker}} - AI Agent: Contains the prompt shown above
- Google Calendar tool: Get Many operation, limit 50, fetches from requested date range
- OpenAI Chat Model: GPT-4
Share the output returned by the last node:
Current output (INCORRECT):
{
"available": false,
"worker": "Alex",
"time": "2026-03-18T14:00:00Z"
}
Expected output (CORRECT):
{
"available": true,
"worker": "Alex",
"time": "2026-03-18T14:00:00Z"
}
This happens when checking 14:00 availability against an existing Balayage appointment at 13:00-16:00 (with ACTIVE_TIME: 13:00-14:00 in description). The 14:00 slot should be available since it doesn’t overlap with the 13:00-14:00 active window.
Information on your n8n setup:
- n8n version: Cloud (latest)
- Database: n8n Cloud default
- n8n EXECUTIONS_PROCESS setting: n8n Cloud default
- Running n8n via: n8n Cloud
- Operating system: N/A (cloud-hosted)
Any help massively appreciated! Been stuck for 2 weeks on this single issue.