# Lasting Language — Automated Patient Scheduling System

## Spec v1.0 — April 2, 2026

---

## Overview

Amanda Smith (owner, Lasting Language Therapy Services) needs to schedule speech therapy patients quickly after evaluations. The system accepts scheduling instructions via two channels, confirms before booking, books appointments on the correct GHL calendar, checks for PT schedule conflicts, and notifies the patient and Amanda.

---

## Trigger Paths

### Path A: Telegram (Dakota)
Amanda texts the Dakota bot on Telegram with a scheduling request.

**Example messages:**
- "Schedule Jay Smith Mon/Tue AM biweekly"
- "Book Ruthann Swinsky for 8 adult sessions Wednesday mornings weekly"
- "Myo patient Cora Pollard, Thursdays PM, every other week, 10 sessions"

### Path B: GHL Note + Tag
Amanda navigates to a contact's record in GHL, writes a scheduling note, and adds the tag `schedule`.

**Example note:** "Available Monday and Tuesday for AM appointments, needs to be booked biweekly"

**Key difference:** Path B already knows the patient (the note is on their record). Path A requires a name lookup.

---

## Conversation Flow

### Path A: Telegram Flow

```
STEP 1: Amanda sends message to Dakota
         ↓
STEP 2: Parse message with OpenAI
         Extract: patient name, days, preference, frequency, sessions, session type
         ↓
STEP 3: Search GHL for patient by name
         ↓
         ├── 0 matches → Reply: "I couldn't find a contact named [name]. 
         │                       Can you double-check the name or spelling?"
         │                       → WAIT for Amanda's reply → back to STEP 3
         │
         ├── 1 match → Continue to STEP 4
         │
         └── 2+ matches → Reply: "I found [N] contacts matching '[name]':
                           1. Jay Smith (jayridgelysmith@icloud.com, 404-245-0046)
                           2. Janet Smith (jsmith@gmail.com, 770-555-1234)
                           Which one?"
                           → WAIT for Amanda's reply (number or name) → Continue to STEP 4
         ↓
STEP 4: Check Google Drive PT Scheduling folder for patient's Synapse PDF
         ├── Found → Parse PT schedule, will cross-reference when scheduling
         └── Not found → Tag contact "no-pt" in GHL, proceed without PT data
         ↓
STEP 5: Fetch available slots from correct GHL calendar
         Calendar selection:
           - session_type "adult" → "Adult 45 minute treatment calendar" (blAjNdl84YudbyBaiSvu)
           - session_type "myo" → "Myo Appointments" (RBSNb6Vn1pqiZ6Kmjf3z)
         ↓
STEP 6: Run scheduling algorithm (see Scheduling Logic below)
         ↓
STEP 7: Send confirmation to Amanda via Telegram:
         "Here's the proposed schedule for Jay Smith:
         
         8 adult sessions (45 min), Mon/Tue AM, biweekly
         Calendar: Adult 45 minute treatment
         PT schedule: 18 appointments on file — 6/8 sessions matched to PT days
         
         1. Mon Apr 13 @ 10:00 AM (PT at 11:00 AM)
         2. Tue Apr 27 @ 10:00 AM
         3. Mon May 11 @ 10:00 AM (PT at 11:00 AM)
         4. Tue May 25 @ 10:00 AM
         5. Mon Jun 08 @ 10:00 AM (PT at 12:00 PM)
         6. Tue Jun 22 @ 10:00 AM
         7. Mon Jul 06 @ 10:00 AM
         8. Tue Jul 20 @ 10:00 AM
         
         Confirm to book? Reply YES, or tell me what to change."
         ↓
STEP 8: Wait for Amanda's response
         ├── "Yes" / "Confirm" / "Book it" → STEP 9
         ├── Correction → Re-run with updated params → back to STEP 7
         │   Examples: "Actually make it Wed/Thu instead"
         │             "Change to PM"
         │             "Only 6 sessions"
         └── "Cancel" / "Nevermind" → Reply "Cancelled. No appointments booked." → END
         ↓
STEP 9: Book all appointments via GHL API
         ↓
STEP 10: Send notifications (see Notifications below)
         ↓
STEP 11: Reply to Amanda on Telegram:
          "Done! Booked 8 sessions for Jay Smith (Apr 13 - Jul 20).
          Patient confirmation email and text sent.
          6/8 sessions matched to PT days."
          → END
```

### Path B: GHL Note + Tag Flow

```
STEP 1: GHL workflow fires on tag "schedule" added to contact
         Webhook sends: contactId, firstName, lastName, email, phone, most recent note body
         ↓
STEP 2: Parse note with OpenAI (same prompt as Path A, but no patient name needed)
         Extract: days, preference, frequency, sessions, session type
         ↓
STEP 3: Check Google Drive PT Scheduling folder for patient's Synapse PDF
         (same as Path A STEP 4)
         ↓
STEP 4: Fetch GHL calendar slots + run scheduling algorithm
         (same as Path A STEPS 5-6)
         ↓
STEP 5: Send confirmation to Amanda via SMS:
         "Scheduling [name]: 8 adult sessions Mon/Tue AM biweekly.
         First: Apr 13 @ 10 AM. Last: Jul 20 @ 10 AM.
         6/8 matched to PT days.
         Reply YES to book or NO to cancel."
         ↓
STEP 6: Wait for Amanda's SMS reply
         ├── "Yes" → Book + notify → SMS Amanda "Done! 8 sessions booked for [name]."
         └── "No" → SMS Amanda "Cancelled." → Remove "schedule" tag → END
         ↓
STEP 7: Book + send notifications (same as Path A STEPS 9-10)
         ↓
STEP 8: Remove "schedule" tag from contact → END
```

---

## Scheduling Logic

### Input
- **Available days**: e.g., ["monday", "tuesday"] — from Amanda's note/message
- **Time preference**: "am" (8:00-12:00) or "pm" (12:00-5:00) or "any"
- **Frequency**: "weekly" (7 days), "biweekly" (14 days), "2x_week" (3 days), "3x_week" (2 days)
- **Sessions**: number of appointments to book (default 8)
- **Session type**: "adult" (45 min) or "myo" (30 min)
- **PT schedule**: parsed from Synapse PDF if available

### Slot Selection Priority (in order)
1. **PT day + allowed day + in preference + closest to PT time** — Patient is already at the building for PT. Book speech therapy adjacent to their PT appointment.
2. **PT day + in preference** — Still a PT day, still in the right time window, but not on Amanda's specified days.
3. **Allowed day + in preference** — No PT that day, but matches Amanda's instructions.
4. **Any available slot** — Fallback if nothing else works.

### Rules
- One session per day maximum
- Sessions must not overlap with PT appointments (45-60 min PT session blocked)
- Frequency spacing enforced via time windows (e.g., biweekly = one session per 14-day window)
- If a window has no available slots, skip to next window (don't count as a used session)
- Search range: calculated from frequency × sessions + 4 weeks buffer

### Calendar Mapping
| Session Type | Calendar Name | Calendar ID | Duration |
|-------------|---------------|-------------|----------|
| adult | Adult 45 minute treatment calendar | blAjNdl84YudbyBaiSvu | 45 min |
| myo | Myo Appointments | RBSNb6Vn1pqiZ6Kmjf3z | 30 min |

---

## PT Cross-Reference

### PDF Lookup
- PDFs are stored in Google Drive folder: PT Scheduling (ID: 1hn7QTvEJ9pTk87LenI4PAU9PSqxTE8FY)
- Filename convention: "Copy of [AbCd] WebPT - Upcoming Appointments.pdf" where AbCd = first 2 letters of first name + first 2 letters of last name
- System searches by this pattern to match patient to PDF

### PT Matching Logic
- Synapse NeuroRehab (PT) is Suite A14, Lasting Language is Suite B-16 — same building
- When PT schedule exists, the system PREFERS booking on the same day as PT, in an adjacent time slot
- Example: PT at 11:00 AM → book speech therapy at 9:30 AM or 12:00 PM
- This is a convenience optimization, not a hard requirement — if PT days don't have open speech slots, book on other available days

### No PT PDF Found
- Tag the contact "no-pt" in GHL
- Proceed with scheduling based purely on Amanda's instructions and GHL calendar availability

---

## Notifications

### After booking is confirmed, send ALL THREE:

**1. Patient Email (HTML)**
- Subject: "Your [Adult Speech Therapy (45 min) / Myofunctional Therapy (30 min)] Schedule — Lasting Language"
- Body: greeting, schedule table (date + time), office address, directions (back of B building, fence line, Toyota dealership landmark, lower parking lot for mobility issues), reschedule instructions, phone number
- Sent via: GHL Conversations API (type: Email)

**2. Patient SMS**
- Content: "Hi [first name], your [type] sessions are booked at Lasting Language. [N] sessions starting [first date]. Confirmation email with your full schedule is on the way. Call (470) 851-4988 with questions."
- Sent via: GHL Conversations API (type: SMS)

**3. Amanda Confirmation**
- Channel: Telegram (Path A) or SMS (Path B)
- Content: "Done! Booked [N] [type] sessions for [name] ([first date] - [last date]). [X/N matched to PT days / No PT schedule on file]. Patient email and text sent."

### Test Mode
During testing, ALL emails and SMS go to:
- Email: 1brycefolsom@gmail.com
- Phone: 404-213-1213
- Amanda confirmation: also 404-213-1213

Production cutover requires changing these to real contact info and Amanda's number.

---

## Error Handling

| Scenario | Response |
|----------|----------|
| OpenAI parsing fails | Fall back to defaults: weekly, AM, 8 sessions, adult |
| GHL calendar returns no slots | Reply to Amanda: "No available slots on [calendar] in the next [X] weeks. Amanda may need to open more availability." |
| Fewer sessions found than requested | Reply to Amanda: "Could only find [N] of [requested] sessions. [Reason — e.g., limited AM availability on Mondays]. Want to book these [N], or adjust the days/time?" |
| GHL booking API fails for a slot | Book remaining slots, report which failed: "Booked 7/8. Failed: Apr 13 @ 10 AM (conflict). Want me to find a replacement?" |
| Contact not found (Telegram) | "I couldn't find [name] in GHL. Check the spelling or tell me their email/phone." |
| Multiple contacts (Telegram) | List matches with name + email + phone, ask Amanda to pick |
| PT PDF parse fails | Proceed without PT data, note in Amanda's confirmation: "Couldn't read PT schedule — booked without cross-reference." |
| GHL rate limit (429) | Retry with 5-second backoff, up to 3 attempts per chunk |
| Telegram message isn't a scheduling request | Dakota responds normally (non-scheduling conversation). Only trigger scheduling when OpenAI classifies the message as a scheduling request. |

---

## Edge Cases

1. **Amanda sends a correction after seeing the proposed schedule**: System re-runs with updated params. Old proposal is discarded. No bookings made until confirmation.

2. **Amanda confirms but some slots get taken between proposal and booking**: Book what's available, report failures, offer to find replacements.

3. **Patient already has upcoming appointments on the calendar**: System doesn't check for existing appointments — it only looks at free slots. If Amanda double-books, GHL will either reject (if calendar has conflict protection) or allow it. Future enhancement: check for existing appointments first.

4. **Amanda sends scheduling request for a patient who already has sessions booked**: Same as above — system doesn't know about existing bookings. It books new ones. Amanda is responsible for knowing patient status.

5. **Two scheduling requests for the same patient in quick succession**: Each runs independently. Could result in double-booking if Amanda confirms both. Mitigation: Amanda sees the confirmation and would notice.

6. **Amanda's Telegram message is ambiguous**: "Can you schedule Smith?" — OpenAI extracts "Smith" as the name, GHL lookup probably returns multiple matches, disambiguation flow handles it.

7. **Note on GHL contact has no scheduling information**: "Patient called about insurance" + "schedule" tag. OpenAI tries to parse, falls back to defaults. Amanda sees the confirmation with default params and either corrects or cancels.

8. **Session type not specified**: Default to "adult" since majority of caseload is adult/Parkinson's.

9. **Amanda wants to schedule on a day the calendar has no regular hours**: System simply won't find slots on that day. It'll book on the next best available day and Amanda can see in the confirmation.

10. **Patient has no email or phone in GHL**: Email/SMS notification will fail. Amanda's confirmation will still go through. Failed notifications reported in Amanda's confirmation message.

---

## Technical Architecture

### Components
| Component | Purpose | Status |
|-----------|---------|--------|
| n8n workflow "LL Scheduling Automation" | Core orchestration — slot fetching, scheduling algorithm, booking, notifications | Deployed, needs conversation flow update |
| Telegram bot (Dakota) | Amanda's interface for scheduling requests | Needs new bot key |
| GHL workflow (tag trigger) | Fires webhook when "schedule" tag is added | Needs to be created in GHL UI |
| OpenAI API (GPT-4o-mini) | Parse natural language notes into structured scheduling params | Working |
| GHL API | Calendar slots, booking, contacts, email/SMS | Working |
| Google Drive API | PT PDF search and download | Needs OAuth setup in n8n |
| Python tools (local) | PDF parsing, scheduling algorithm reference implementation | Working |

### API Credentials
| Key | Location | Purpose |
|-----|----------|---------|
| LL_GHL_API_KEY | .env + n8n workflow | Lasting Language GHL sub-account |
| LL_GHL_LOCATION_ID | .env + n8n workflow | Lasting Language GHL location |
| OPENAI_API_KEY | .env + n8n workflow | Note/message parsing |
| Dakota Telegram Bot Key | TBD | Telegram bot communication |
| Google Drive OAuth | n8n credentials UI | PT PDF access |

### Webhook URLs
| URL | Purpose |
|-----|---------|
| https://northos.app.n8n.cloud/webhook/ll-schedule | GHL tag trigger (Path B) |
| https://northos.app.n8n.cloud/webhook/ll-schedule-telegram | Telegram bot webhook (Path A) — TBD |

---

## What's Needed from Bryce

1. **New Dakota Telegram bot key** — for the scheduling bot
2. **GHL workflow creation** — tag "schedule" added → POST to webhook with contact + note data
3. **Google Drive OAuth in n8n** — for PT PDF lookup (can defer, system works without it)
4. **Test run** — add note + "schedule" tag to Bryce Test contact to validate Path B
5. **Test run** — send Telegram message to Dakota to validate Path A
6. **Production cutover** — change test email/phone to real values once validated

---

## Out of Scope (Future Enhancements)

- Check for existing patient appointments before scheduling (avoid double-booking)
- Automatically detect when a new PDF is uploaded to Drive and re-run affected schedules
- Appointment reminders / follow-up sequences
- Rescheduling flow (patient cancels → system finds replacement slot)
- Multi-clinician scheduling (currently assumes Amanda is the only provider)
- SimplePractice / billing system integration
