AI Agent for Fitness Studios: Class Booking, Membership Inquiries, and Trial Signups
Build an AI agent that handles class bookings, answers membership questions, manages trial signups, and drives retention for fitness studios — from yoga studios to CrossFit boxes.
Fitness Studios Live and Die by Their Front Desk
A fitness studio's revenue depends on two things: getting new members in the door and keeping existing members coming back. Both start at the front desk — answering calls about class schedules, explaining membership options, signing up trial visitors, and rebooking members who are about to lapse. An AI agent handles all of these conversations simultaneously, never puts a caller on hold, and can nudge lapsed members back to class with a well-timed follow-up.
Studio Data Model
Fitness studios revolve around classes, instructors, memberships, and attendance. We model these relationships to give the agent the context it needs.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
flowchart LR
CALLER(["Member or Lead"])
subgraph TEL["Telephony"]
SIP["Twilio SIP and PSTN"]
end
subgraph BRAIN["Fitness Studio AI Agent"]
STT["Streaming STT<br/>Deepgram or Whisper"]
NLU{"Intent and<br/>Entity Extraction"}
TOOLS["Tool Calls"]
TTS["Streaming TTS<br/>ElevenLabs or Rime"]
end
subgraph DATA["Live Data Plane"]
CRM[("CRM and Notes")]
CAL[("Calendar and<br/>Schedule")]
KB[("Knowledge Base<br/>and Policies")]
end
subgraph OUT["Outcomes"]
O1(["Class booked"])
O2(["Trial signup captured"])
O3(["Coach handoff"])
end
CALLER --> SIP --> STT --> NLU
NLU -->|Lookup| TOOLS
TOOLS <--> CRM
TOOLS <--> CAL
TOOLS <--> KB
NLU --> TTS --> SIP --> CALLER
NLU -->|Resolved| O1
NLU -->|Schedule| O2
NLU -->|Escalate| O3
style CALLER fill:#f1f5f9,stroke:#64748b,color:#0f172a
style NLU fill:#4f46e5,stroke:#4338ca,color:#fff
style O1 fill:#059669,stroke:#047857,color:#fff
style O2 fill:#0ea5e9,stroke:#0369a1,color:#fff
style O3 fill:#f59e0b,stroke:#d97706,color:#1f2937
from dataclasses import dataclass, field
from datetime import datetime, date, time, timedelta
from enum import Enum
from typing import Optional
class MembershipTier(Enum):
TRIAL = "trial"
BASIC = "basic" # 4 classes/month
UNLIMITED = "unlimited" # unlimited classes
PREMIUM = "premium" # unlimited + perks
class ClassStatus(Enum):
OPEN = "open"
FULL = "full"
WAITLISTED = "waitlisted"
CANCELLED = "cancelled"
@dataclass
class FitnessClass:
id: str
name: str
instructor: str
day_of_week: str
start_time: time
duration_minutes: int
capacity: int
enrolled: int = 0
difficulty: str = "all levels"
description: str = ""
@property
def spots_left(self) -> int:
return max(0, self.capacity - self.enrolled)
@property
def status(self) -> ClassStatus:
if self.enrolled >= self.capacity:
return ClassStatus.FULL
return ClassStatus.OPEN
@dataclass
class Membership:
tier: MembershipTier
monthly_price: float
classes_per_month: Optional[int] # None = unlimited
perks: list[str] = field(default_factory=list)
contract_months: int = 0 # 0 = month-to-month
@dataclass
class Member:
id: str
name: str
phone: str
email: str
membership: MembershipTier
classes_this_month: int = 0
join_date: Optional[date] = None
last_class_date: Optional[date] = None
Class Schedule and Booking Engine
WEEKLY_SCHEDULE: list[FitnessClass] = [
FitnessClass("c1", "Morning Vinyasa", "Lisa", "monday", time(6, 30), 60, 20, 18),
FitnessClass("c2", "HIIT Burn", "Marcus", "monday", time(17, 30), 45, 25, 25),
FitnessClass("c3", "Beginner Yoga", "Lisa", "tuesday", time(9, 0), 60, 15, 8),
FitnessClass("c4", "Spin & Core", "Jade", "wednesday", time(6, 0), 45, 20, 14),
FitnessClass("c5", "Power Sculpt", "Marcus", "thursday", time(18, 0), 50, 25, 22),
FitnessClass("c6", "Restorative Yoga", "Lisa", "friday", time(10, 0), 75, 12, 6),
FitnessClass("c7", "Weekend Warrior HIIT", "Marcus", "saturday", time(8, 0), 45, 30, 28),
]
MEMBERSHIP_TIERS = {
MembershipTier.TRIAL: Membership(
MembershipTier.TRIAL, 0, 2,
perks=["2 free classes", "Locker rental included"],
),
MembershipTier.BASIC: Membership(
MembershipTier.BASIC, 59, 4,
perks=["4 classes/month", "10% retail discount"],
),
MembershipTier.UNLIMITED: Membership(
MembershipTier.UNLIMITED, 99, None,
perks=["Unlimited classes", "Free mat rental", "15% retail discount"],
),
MembershipTier.PREMIUM: Membership(
MembershipTier.PREMIUM, 149, None,
perks=["Unlimited classes", "1 guest pass/month",
"Free retail item/quarter", "Priority booking"],
contract_months=6,
),
}
waitlist: dict[str, list[str]] = {} # class_id -> list of member names
class BookingEngine:
def book_class(self, member: Member, class_id: str) -> dict:
fitness_class = next(
(c for c in WEEKLY_SCHEDULE if c.id == class_id), None
)
if not fitness_class:
return {"success": False, "message": "Class not found."}
# Check membership class limit
tier = MEMBERSHIP_TIERS[member.membership]
if tier.classes_per_month and member.classes_this_month >= tier.classes_per_month:
return {
"success": False,
"message": (
f"You have used all {tier.classes_per_month} classes "
f"this month. Upgrade to Unlimited for more."
),
}
if fitness_class.status == ClassStatus.FULL:
waitlist.setdefault(class_id, []).append(member.name)
position = len(waitlist[class_id])
return {
"success": False,
"message": f"Class is full. Added to waitlist (position {position}).",
}
fitness_class.enrolled += 1
member.classes_this_month += 1
return {
"success": True,
"message": (
f"Booked: {fitness_class.name} with {fitness_class.instructor} "
f"on {fitness_class.day_of_week.title()} at "
f"{fitness_class.start_time.strftime('%I:%M %p')}. "
f"Spots remaining: {fitness_class.spots_left}."
),
}
Trial Signup and Conversion
Trial conversion is where fitness studios make or break their growth. The agent should make signing up frictionless and highlight what the prospect will experience.
trial_signups: list[dict] = []
def create_trial_signup(
name: str, phone: str, email: str, interests: str
) -> dict:
signup = {
"name": name,
"phone": phone,
"email": email,
"interests": interests,
"signed_up_at": datetime.now().isoformat(),
"classes_remaining": 2,
"converted": False,
}
trial_signups.append(signup)
return {
"message": (
f"Welcome, {name}! Your free trial includes 2 classes. "
f"Based on your interest in {interests}, I recommend starting with "
f"our Beginner Yoga on Tuesday at 9 AM or Morning Vinyasa on Monday "
f"at 6:30 AM. Shall I book one for you?"
),
"recommended_classes": ["c3", "c1"],
}
Agent Tools and Assembly
from agents import Agent, Runner, function_tool
booking_engine = BookingEngine()
MEMBERS_DB = {
"maria-garcia": Member("m1", "Maria Garcia", "555-0201", "[email protected]", MembershipTier.UNLIMITED, 3, date(2025, 9, 1), date(2026, 3, 10)),
}
@function_tool
def get_class_schedule(day: str = "", class_type: str = "") -> str:
"""Get the class schedule, optionally filtered by day or type."""
classes = WEEKLY_SCHEDULE
if day:
classes = [c for c in classes if c.day_of_week == day.lower()]
if class_type:
classes = [c for c in classes if class_type.lower() in c.name.lower()]
if not classes:
return "No classes found matching your criteria."
lines = []
for c in classes:
lines.append(
f"{c.name} ({c.difficulty}) - {c.day_of_week.title()} "
f"{c.start_time.strftime('%I:%M %p')} with {c.instructor} "
f"[{c.spots_left}/{c.capacity} spots]"
)
return "\n".join(lines)
@function_tool
def book_class(member_name: str, class_id: str) -> str:
"""Book a class for a member."""
key = member_name.lower().replace(" ", "-")
member = MEMBERS_DB.get(key)
if not member:
return f"Member '{member_name}' not found."
result = booking_engine.book_class(member, class_id)
return result["message"]
@function_tool
def get_membership_info(tier: str = "") -> str:
"""Get information about membership tiers and pricing."""
if tier:
t = MembershipTier(tier.lower())
m = MEMBERSHIP_TIERS.get(t)
if not m:
return "Tier not found."
perks = ", ".join(m.perks)
return f"{t.value.title()}: ${m.monthly_price}/month - {perks}"
lines = []
for t, m in MEMBERSHIP_TIERS.items():
if t == MembershipTier.TRIAL:
continue
perks = ", ".join(m.perks)
contract = f" ({m.contract_months}-month commitment)" if m.contract_months else " (month-to-month)"
lines.append(f"{t.value.title()}: ${m.monthly_price}/mo{contract} - {perks}")
return "\n".join(lines)
@function_tool
def signup_trial(name: str, phone: str, email: str, interests: str) -> str:
"""Sign up a new visitor for a free trial."""
result = create_trial_signup(name, phone, email, interests)
return result["message"]
studio_agent = Agent(
name="FitLife Studio Assistant",
instructions="""You are an enthusiastic, encouraging assistant for FitLife Studio.
1. For class schedule questions, use get_class_schedule. Highlight classes
with available spots.
2. To book a class, use book_class. If the class is full, mention the
waitlist and suggest alternatives.
3. When someone asks about membership, use get_membership_info.
Recommend Unlimited for people who want to come 3+ times per week.
4. For new visitors, use signup_trial to register them. Recommend
classes based on their stated interests and fitness level.
5. Be energetic and supportive. Use the member's first name.
6. If a member has not visited in 2+ weeks, gently encourage them
to get back to class.""",
tools=[get_class_schedule, book_class, get_membership_info, signup_trial],
)
FAQ
How does the agent handle class cancellations and no-shows?
Add a cancel_booking tool that marks the member's enrollment as cancelled and decrements the class enrollment count. When a spot opens, check the waitlist for that class and automatically notify the first person in line. For no-shows, implement a policy (e.g., three no-shows results in a booking restriction) and have the agent enforce it during the booking flow.
Can the agent run promotions or discounts?
Yes. Add a promotions table with start dates, end dates, and discount rules. The get_membership_info tool checks for active promotions and includes them in the response. For example, "This week only: first month of Unlimited is $79 instead of $99."
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
How do I track trial-to-member conversion rates?
Log every trial signup with a timestamp, then track whether the trial member converts to a paid membership within 14 days. The agent can proactively follow up after the first trial class to ask about their experience and present membership options. Conversion analytics come from querying the signup log against the membership database.
#FitnessStudio #ClassBooking #MembershipAI #TrialConversion #Python #AgenticAI #LearnAI #AIEngineering
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.