Skip to main content
This guide shows production patterns for scheduling inside agent using mid-call Actions.

Before you start

1) Connect your scheduling tool

Go to Integrations and connect your scheduler so it is available in both:
Integrations screen with Calendly and Cal.com connected

Key building blocks (how scheduling works)

Variables (capture the caller’s date/time)

Variables extract immediately after the caller’s latest reply and before outcome evaluation, so your routing can validate the date/time right away. If you loop back to the same node, variables re-extract and overwrite prior values (perfect for “try again” date collection). See Loops.
Variables panel with a "preferred_datetime" variable configured

Actions (run scheduling mid-call)

In our agent, Actions run mid-call from a Speak node. When Actions exist, the node can auto-proceed without waiting for another caller reply, and rule-based outcomes are recommended because outcomes fire based on internal values/results.
Speak node Actions panel with a scheduling action selected

Scheduling timezone handling (important)

Scheduling related Actions accept an optional timezone. If not set, Thoughtly falls back to:
  1. timezone input (action config) -> 2) agent timezone (advanced settings) -> 3) America/New_York.
See Calendly timezone handling for details.

Flow overview

  1. Ask for preferred date (Speak -> Prompt)
  2. Check availability mid-call (Speak node -> Get Available Times Action)
  3. Present returned slots and collect a choice (Speak -> Variable + loop to Step 2 if needed)
  4. Book the selected slot (Speak node with Scheduling Action)
  5. Confirm (Message / Prompt)
  6. Fallback (offer alternatives or Transfer)

Step-by-step (Agent Builder)

Step 1 - “Collect preferred date” Speak node

Create a Speak node that asks something like:
  • “What day works best for you?”
  • “If you have a timezone preference, tell me as well.”
Create Variables on this node:
  • preferred_date (Text)
  • preferred_timezone (Text, optional)
Extraction instruction suggestion (copy/paste style):
Goal: extract the appointment date the caller states.
Output: YYYY-MM-DD.
If absent or unclear: return empty.
Do not invent values.
Speak node to collect date
Route directly to Step 2 from this node. Keep loops in Step 3, where slot selection happens.

Step 2 - Check availability node (mid-call Action)

Create a new Speak node to run availability lookup:
  • Message: “Let me check what times are open.”
  • Add a scheduler Get Available Times action (Calendly or Cal.com)
  • Map input values from Step 1:
    • Requested date: preferred_date
    • Timezone: preferred_timezone (optional)
Speak node to check timeslots via Action

Step 3 - Collect preferred slot from returned availability

Create another Speak node that presents the returned slots and asks the caller to pick one:
  • “I have 10:00 AM, 2:30 PM, or 4:00 PM. Which works best?”
  • Extract a variable such as selected_time from the caller’s reply
  • Add outcomes on this node:
    • If selected_time is valid -> continue to Step 4 (Booking)
    • If no slot is selected, caller wants another time, or caller provides a different date -> loop back to Step 2 (Check availability) and run lookup again
Speak node to provide available timeslots
Prompt suggestion (copy/paste):
timeslots: response

Your task is to provide a few (no more than 3) available time slots from the list, based on the person's request. If the timeslots is empty just say: "I don't have any openings for this day, should I check another one?"

Only provide and talk about information that is available in the timeslots list. If it is empty, do not come up with information; simply let the customer know that you don't have an opening for that date.

Step 4 - Booking Speak node (Actions)

Create a new Speak node:
  • Message: “One moment while I book that for you.”
  • Add an Action for your scheduler (Calendly or Cal.com) and map:
    • Date/time input: selected_time
    • Timezone: preferred_timezone (or rely on the Calendly fallback order)
Authoring tip: disable interruptions for mid-call Actions so the caller does not interrupt during booking.
Calendly "Schedule appointment" action mapping fields

Step 5 - Confirm vs error (rule-based Outcomes after booking)

In the same booking node, add rule-based outcomes that check Action outputs such as:
  • booking_status == "confirmed" -> confirmation node
  • Else -> error-handling node (try different time / get availability / transfer)
Speak node to book and do error handling

Step 6 - Confirmation Speak node

Use Message mode if you want an exact script:
  • “You’re all set for [date/time]. You’ll receive a confirmation shortly.”
Confirmation node with verbatim message enabled

Pattern B (pre-call availability): Prefetch times in Automations, book faster mid-call

This pattern reduces mid-call complexity by fetching available times before the live conversation starts, then using those times as metadata during the call. It is ideal when you want:
  • Faster conversations
  • Fewer mid-call API round trips
  • Simpler agent logic
  • More deterministic scheduling flows

How it works

1) Pre-call Automation

Use a Get Available Times action inside an Automation, then store the returned times in metadata before you trigger the call.
  • Add a scheduling availability step (Calendly / Cal.com)
  • Store output as metadata (for example: available_times)
  • Start or route to your call only after this step succeeds
available_times = {{ steps.calendly_get_available_times.slots }}
Pre-call automation flow that prefetches available times (Get Times Node)
Pre-call automation flow that prefetches available times (Call Phone Number Node)

2) Agent conversation

During the live call, the agent reads options from available_times, presents a short list, and captures the caller’s selected slot.
  • Present only times that already exist in metadata
  • Capture selection in a variable such as selected_time
  • Confirm the chosen slot out loud before booking
Agent conversation that presents prefetched slots and captures the selected time

3) Booking Action

In the scheduling Action, set Reference Node to None and map the selected time directly from metadata/variables instead of calling availability again mid-call.
  • Reference Node: None
  • Booking time input: selected_time (or equivalent mapped value)
  • Keep booking outcomes rule-based (confirmed vs fallback path)
Booking action configuration with reference node disabled and selected slot mapped directly

Why this reduces friction

Because availability is already known:
  • The agent does not need to fetch availability mid-call
  • There is no back-and-forth waiting on additional API checks
  • The conversation feels faster and more natural
  • The in-call routing logic is much simpler

Important limitation

This approach works best for near-term scheduling windows (typically within a week). If callers often request far-future dates, Pattern A (real-time availability lookup) is still recommended for accuracy.

When to use Pattern B

Use pre-call availability when:
  • Speed and conversational simplicity are top priorities
  • You want fewer live integrations running during calls
  • Booking windows are short-term and predictable
  • You prefer deterministic slot presentation over open-ended date parsing
This gives you two production-ready scheduling approaches:
  • Pattern A: Flexible, real-time booking with full date parsing
  • Pattern B: Faster, lower-friction booking using prefetched availability
Both can coexist in production depending on your use case.

Tips and tricks (prompting + reliability)

Use a strict date-only extractor for validation loops

This keeps your validation clean when the caller is vague or revises their date.
Print ONLY one line with the ISO date (YYYY-MM-DD). No labels, no prose, no JSON.
You are given ONLY the latest caller message and TODAY’S DATE (CURRENT_DATE) in the America/New_York timezone. Extract exactly one date the caller intends for scheduling and output ONLY that date in YYYY-MM-DD. No other text.

Rules
1) Prefer the most specific date mentioned. If multiple are given, choose the earliest that matches the caller’s intent modifiers (e.g., “late”, “end of”).
2) If the mentioned date would be in the past relative to CURRENT_DATE, roll it forward to the next logical occurrence (e.g., same month/day next year, or the next instance of that weekday).
3) If the caller is vague (e.g., “some time next week”, “next month”, “this week”, “what do you have available”), ALWAYS produce a date by applying the mapping below.
4) Assume ISO weeks start Monday; “weekend” = Saturday–Sunday. If a fallback lands on a weekend and no weekend was requested, use the next business day (Mon–Fri).
5) Output must be a valid calendar date within the next 365 days. If your first choice falls outside, choose the nearest valid alternative that respects the intent.

Relative-Date Mapping (examples; always relative to CURRENT_DATE)
- “today” → CURRENT_DATE
- “tomorrow” → CURRENT_DATE + 1 day
- “day after tomorrow” → CURRENT_DATE + 2 days
- Bare weekday (“Friday”) → next occurrence of that weekday after CURRENT_DATE
- “this <weekday>” → that weekday in the current week; if already passed, use the same weekday next week
- “this week” / “sometime this week” → the soonest remaining day this week after CURRENT_DATE
- “next week” / “sometime next week” → Monday of next week
- “weekend” / “this weekend” → upcoming Saturday
- “next weekend” → Saturday of next week
- “this month” → the earliest remaining day this month after CURRENT_DATE
- “next month” / “sometime next month” → the 1st business day of next month
- “early <month>” → 5th of that month; “mid <month>” → 15th; “late <month>” → 25th
- “end of <month>” → last calendar day of that month
- “in N days/weeks/months” → add N with standard calendar arithmetic (weeks = 7 days; months add by month, clamping to month end if needed)
- “a couple of weeks” → 14 days
- Ordinal day without month (“the 15th”) → the next 15th on the calendar (this month if still upcoming, else next month)
- Month/day without year → this year if still upcoming; otherwise next year
- No date intent / open-ended (“what do you have available”, “ASAP”, “whenever”) → next business day after CURRENT_DATE

Use a full datetime extractor right before booking

This captures the final intent, including confirmation of a proposed slot.
The final scheduled datetime the caller intends, returned as YYYY-MM-DDTHH:MM:SS (24h).
From FULL_CONVERSATION, extract exactly one datetime the caller intends for scheduling. Output ONLY one string in the format YYYY-MM-DDTHH:MM:SS and nothing else.


Conversation logic
1) Consider only the caller’s latest clear intent. If the caller revises the time/date later, the latest revision wins.
2) If the agent proposes a slot and the caller affirms (e.g., “yes”, “works”, “sounds good”), use that proposed slot.
3) Ignore tentative or rejected options that are later superseded.

Date resolution (vague → concrete)
- “today” → CURRENT_DATE
- “tomorrow” → +1 day
- Bare weekday (“Friday”) → next occurrence after CURRENT_DATE
- “this <weekday>” → that weekday in the current ISO week (Mon–Sun); if already past, use next week
- “this week” / “sometime this week” → soonest remaining business day this week after CURRENT_DATE
- “next week” → Monday next week
- “weekend” / “this weekend” → upcoming Saturday
- “next weekend” → Saturday next week
- “this month” → earliest remaining day this month
- “next month” → 1st business day of next month
- Ordinal day without month (“the 15th”) → next 15th (this month if upcoming, else next month)
- Month/day without year → this year if upcoming, else next year
- “in N days/weeks/months” → add N with calendar arithmetic (weeks=7 days; months clamp to month end)
- “early/mid/late <month>” → 05/15/25 of that month
- If fallback date lands on weekend and caller didn’t ask for weekend, use next Monday.

Time resolution (vague → concrete, 24h)
- Exact times → parse as given (handle “am/pm”).
- “noon”/“midday” → 12:00:00
- “midnight” → 00:00:00 (on the chosen date)
- “morning” → 10:00:00
- “early morning” → 08:00:00
- “afternoon” → 15:00:00
- “evening” → 18:00:00
- “late evening” / “tonight” → 20:00:00
- “EOD” / “end of day” → 17:00:00
- “lunchtime” → 13:00:00
- “quarter past X” → X:15:00; “half past X” → X:30:00; “quarter to X” → (X-1):45:00
- If no time is given, default to 10:00:00 (business-friendly).
- If the chosen time falls outside business days and caller did not indicate off-hours/weekend, keep the date but set time to 10:00:00 next business day.

Edge cases
- If caller is completely open-ended (“what do you have available”, “whenever”), choose next business day at 10:00:00.

Output
Return exactly one line in this format: YYYY-MM-DDTHH:MM:SS
No labels, no prose, no JSON, no quotes.

Offer only real availability

Use this prompt when you’re reading from an availability response to avoid hallucinated slots.
timeslots: {{response}} 

Your taks is to provide a few (no more than 3) available time slots from the list, based on the person's request. If the timeslots is empty just say: "I don't have any openings for this day, should I check another one?"

Only provide and talk about information that is available in the timeslots list, if it is empty then do not come up with information, simply let customer know that you don't have an opening for a date.

See also

  • Actions - mid-call integrations and execution order
  • Variables - extraction and overwrite behavior
  • Outcomes - rule-based vs prompt-based routing
  • Automations - prefetch and data passing
  • Calendly - action details and timezone handling
  • Cal.com - action details