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.
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.
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
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", "maria@email.com", 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."
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
CallSphere Team
Expert insights on AI voice agents and customer communication automation.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.