AI Agent for Photography Studios: Session Booking, Package Selection, and Gallery Delivery
Build an AI agent for photography studios that guides clients through package selection, schedules sessions with location coordination, and manages gallery delivery — turning inquiries into booked sessions.
Photography Studios Are Sales Businesses First
Professional photographers spend most of their time behind the camera, not behind a desk. But their business depends on converting inquiries into booked sessions, and every unanswered inquiry is revenue lost to a competitor. Photography clients have specific needs — the right package, the right location, the right time of day for lighting — and they want to feel guided through those choices. An AI agent acts as the studio's always-available booking coordinator, walking clients through package options, handling scheduling logistics, and managing gallery delivery after the shoot.
Photography Business Data Model
Photography studios sell packages that bundle session time, edited images, prints, and digital files. The data model captures these product offerings and client relationships.
from dataclasses import dataclass, field
from datetime import datetime, date, time, timedelta
from enum import Enum
from typing import Optional
class SessionType(Enum):
PORTRAIT = "portrait"
FAMILY = "family"
WEDDING = "wedding"
NEWBORN = "newborn"
HEADSHOT = "headshot"
EVENT = "event"
PRODUCT = "product"
class BookingStatus(Enum):
INQUIRY = "inquiry"
QUOTED = "quoted"
DEPOSIT_PAID = "deposit_paid"
CONFIRMED = "confirmed"
COMPLETED = "completed"
GALLERY_DELIVERED = "gallery_delivered"
ARCHIVED = "archived"
@dataclass
class Package:
id: str
name: str
session_type: SessionType
session_duration_hours: float
edited_images: int
includes_prints: bool
digital_download: bool
price: float
description: str
add_ons: list[str] = field(default_factory=list)
@dataclass
class Location:
name: str
address: str
location_type: str # "studio", "outdoor", "client_location"
travel_fee: float = 0.0
best_time: str = "" # e.g., "golden hour (1 hr before sunset)"
notes: str = ""
@dataclass
class PhotoSession:
id: str
client_name: str
client_phone: str
client_email: str
session_type: SessionType
package_id: str
location: Optional[Location] = None
session_date: Optional[datetime] = None
status: BookingStatus = BookingStatus.INQUIRY
deposit_amount: float = 0.0
total_price: float = 0.0
gallery_url: Optional[str] = None
gallery_expiry: Optional[date] = None
notes: str = ""
Package Catalog
Photography packages are the core product. We define them with enough detail for the agent to make personalized recommendations.
PACKAGES = {
"portrait_mini": Package(
"p1", "Mini Portrait Session", SessionType.PORTRAIT,
0.5, 10, False, True, 195,
"Perfect for headshots or quick individual portraits. "
"30-minute studio session with 10 edited digital images.",
add_ons=["Extra edited images ($15 each)", "Print package ($75)"],
),
"portrait_full": Package(
"p2", "Full Portrait Session", SessionType.PORTRAIT,
1.5, 30, True, True, 450,
"Extended portrait session with wardrobe changes. "
"Includes 30 edited images, 5 prints, and digital downloads.",
add_ons=["Canvas print ($125)", "Additional location ($100)"],
),
"family_standard": Package(
"p3", "Family Session", SessionType.FAMILY,
1.0, 25, True, True, 395,
"Outdoor family session for up to 6 people. "
"Includes 25 edited images, a print set, and digital gallery.",
add_ons=["Holiday cards (set of 25, $60)", "Extra people ($25 each)"],
),
"wedding_essential": Package(
"p4", "Wedding Essentials", SessionType.WEDDING,
6.0, 300, False, True, 2800,
"6 hours of coverage with 300+ edited images. "
"Includes engagement session and online gallery.",
add_ons=["Second photographer ($500)", "Album ($450)", "Extra hour ($350)"],
),
"wedding_premium": Package(
"p5", "Wedding Premium", SessionType.WEDDING,
10.0, 600, True, True, 4500,
"Full-day coverage with 600+ edited images. "
"Includes engagement session, album, prints, and online gallery.",
add_ons=["Video highlight reel ($800)", "Bridal session ($300)"],
),
"headshot_pro": Package(
"p6", "Professional Headshot", SessionType.HEADSHOT,
0.5, 5, False, True, 175,
"Studio headshot session for LinkedIn, websites, and business cards. "
"5 retouched images with digital download.",
add_ons=["Additional looks ($50 each)", "Rush delivery ($50)"],
),
}
STUDIO_LOCATIONS = [
Location("Main Studio", "456 Oak Avenue", "studio",
notes="Natural light studio with white and gray backdrops"),
Location("City Park", "Riverside Park, Downtown", "outdoor",
travel_fee=0, best_time="Golden hour (1 hr before sunset)"),
Location("Botanical Garden", "Springfield Botanical Garden", "outdoor",
travel_fee=50, best_time="Morning (9-11 AM) for soft light"),
Location("Client Location", "Your chosen location", "client_location",
travel_fee=75, notes="Travel fee applies for locations over 15 miles"),
]
Package Recommendation Logic
Different clients need different packages. A mother asking about newborn photos has different needs than a CEO wanting a headshot. The agent uses context clues to recommend the right package.
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
def recommend_packages(
session_type: str, party_size: int = 1,
budget_range: str = ""
) -> list[dict]:
try:
stype = SessionType(session_type.lower())
except ValueError:
return [{"error": f"Unknown session type. Available: {[s.value for s in SessionType]}"}]
matching = [
p for p in PACKAGES.values() if p.session_type == stype
]
if budget_range:
low, high = 0, float("inf")
if budget_range == "budget":
high = 300
elif budget_range == "mid":
low, high = 200, 1000
elif budget_range == "premium":
low = 800
matching = [p for p in matching if low <= p.price <= high]
results = []
for pkg in sorted(matching, key=lambda p: p.price):
add_on_text = "; ".join(pkg.add_ons) if pkg.add_ons else "None"
results.append({
"id": pkg.id,
"name": pkg.name,
"price": pkg.price,
"duration": f"{pkg.session_duration_hours} hours",
"images": pkg.edited_images,
"includes_prints": pkg.includes_prints,
"description": pkg.description,
"add_ons": add_on_text,
})
return results
Agent Tools
from agents import Agent, Runner, function_tool
@function_tool
def browse_packages(
session_type: str, budget: str = ""
) -> str:
"""Browse photography packages by session type and optional budget."""
results = recommend_packages(session_type, budget_range=budget)
if not results:
return "No packages found matching your criteria."
if "error" in results[0]:
return results[0]["error"]
lines = []
for r in results:
prints_note = "Prints included" if r["includes_prints"] else "Digital only"
lines.append(
f"\n{r['name']} - ${r['price']}\n"
f" {r['description']}\n"
f" Duration: {r['duration']} | Images: {r['images']} | {prints_note}\n"
f" Add-ons: {r['add_ons']}"
)
return "\n".join(lines)
@function_tool
def get_locations(session_type: str = "") -> str:
"""Get available session locations with details."""
lines = []
for loc in STUDIO_LOCATIONS:
fee = f" (travel fee: ${loc.travel_fee})" if loc.travel_fee else ""
best = f" | Best time: {loc.best_time}" if loc.best_time else ""
lines.append(f"{loc.name} ({loc.location_type}){fee}{best}")
if loc.notes:
lines.append(f" {loc.notes}")
return "\n".join(lines)
@function_tool
def book_session(
client_name: str, client_phone: str, client_email: str,
package_id: str, preferred_date: str,
location_name: str = "Main Studio"
) -> str:
"""Book a photography session with a specific package and date."""
pkg = next((p for p in PACKAGES.values() if p.id == package_id), None)
if not pkg:
return "Package not found."
location = next(
(l for l in STUDIO_LOCATIONS
if location_name.lower() in l.name.lower()), None
)
travel_fee = location.travel_fee if location else 0
total = pkg.price + travel_fee
deposit = total * 0.3
return (
f"Session booked!\n"
f"Client: {client_name}\n"
f"Package: {pkg.name} (${pkg.price})\n"
f"Location: {location.name if location else location_name}"
f"{f' (+${travel_fee} travel)' if travel_fee else ''}\n"
f"Date: {preferred_date}\n"
f"Total: ${total:.0f}\n"
f"Deposit required: ${deposit:.0f} (30%)\n"
f"Deposit link sent to {client_email}.\n\n"
f"Preparation tips will be emailed 3 days before your session."
)
@function_tool
def check_gallery_status(client_name: str) -> str:
"""Check the status of a client's photo gallery after their session."""
# In production this queries the gallery management system
return (
f"Gallery status for {client_name}:\n"
f"Session: Completed\n"
f"Editing: In progress (estimated delivery: 2-3 weeks after session)\n"
f"You will receive an email with your private gallery link once ready.\n"
f"Gallery will be available for download for 90 days."
)
@function_tool
def send_preparation_guide(session_type: str, client_email: str) -> str:
"""Send a session preparation guide to the client."""
guides = {
"portrait": "Wear solid colors, avoid busy patterns. Bring 2-3 outfit options.",
"family": "Coordinate outfits (not matching). Bring snacks for kids. Plan for golden hour.",
"wedding": "Timeline consultation scheduled separately. Bring your shot list.",
"headshot": "Bring a lint roller. Solid professional attire in 2 colors.",
}
guide = guides.get(session_type, "General prep guide sent.")
return f"Preparation guide sent to {client_email}:\n{guide}"
photography_agent = Agent(
name="Studio Booking Coordinator",
instructions="""You are the booking coordinator for Luminous Photography Studio.
1. When a potential client inquires, ask about the type of session
they need (portrait, family, wedding, headshot, etc.) and their budget.
2. Use browse_packages to present options. Recommend the package
that best fits their needs and explain what is included.
3. Share location options with get_locations. For outdoor sessions,
mention the best time of day for lighting.
4. Once they choose a package and date, use book_session to confirm.
Explain the deposit requirement.
5. Use send_preparation_guide to help them prepare for the session.
6. For returning clients checking on their gallery, use
check_gallery_status.
STYLE:
- Be warm and excited about their milestone (wedding, new baby, etc.).
- Help them visualize the experience, not just the price.
- If budget is a concern, start with the most affordable option and
explain how add-ons can enhance it later.""",
tools=[browse_packages, get_locations, book_session, check_gallery_status, send_preparation_guide],
)
result = Runner.run_sync(
photography_agent,
"Hi, I am getting married in October and looking for a photographer.",
)
print(result.final_output)
FAQ
How does the agent handle wedding consultations that require detailed planning?
Wedding bookings are more complex than standard sessions — they involve timelines, venue logistics, and multi-hour coverage. The agent handles the initial package selection and booking. Once the deposit is paid, it schedules a separate planning consultation (either in-person or video call) where the photographer discusses the timeline, shot list, and venue details. The agent collects the initial information; the photographer handles the creative planning.
Can the agent manage print orders after gallery delivery?
Yes. Add a place_print_order tool that accepts the gallery URL, selected image numbers, print sizes, and quantities. The tool calculates pricing from a print price list and generates an order. This turns the gallery delivery email into a revenue opportunity — the agent follows up after gallery viewing to ask if the client would like prints, canvases, or albums.
How do I handle seasonal pricing or mini-session events?
Create a promotions layer that the browse_packages tool checks before returning results. Seasonal mini-sessions (holiday minis, spring portraits) are temporary packages with their own pricing, duration, and availability windows. Add them to the PACKAGES dictionary with a start and end date, and filter them out automatically once the event period ends.
#PhotographyBusiness #SessionBooking #PackageSelection #GalleryDelivery #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.