---
title: "AI Agent for Photography Studios: Session Booking, Package Selection, and Gallery Delivery"
description: "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."
canonical: https://callsphere.ai/blog/ai-agent-photography-studios-session-booking-package-selection-gallery
category: "Learn Agentic AI"
tags: ["Photography Business", "Session Booking", "Package Selection", "Gallery Delivery", "Python"]
author: "CallSphere Team"
published: 2026-03-17T00:00:00.000Z
updated: 2026-05-06T01:02:44.476Z
---

# 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.

```mermaid
flowchart LR
    INPUT(["User intent"])
    PARSE["Parse plus
classify"]
    PLAN["Plan and tool
selection"]
    AGENT["Agent loop
LLM plus tools"]
    GUARD{"Guardrails
and policy"}
    EXEC["Execute and
verify result"]
    OBS[("Trace and metrics")]
    OUT(["Outcome plus
next action"])
    INPUT --> PARSE --> PLAN --> AGENT --> GUARD
    GUARD -->|Pass| EXEC --> OUT
    GUARD -->|Fail| AGENT
    AGENT --> OBS
    style AGENT fill:#4f46e5,stroke:#4338ca,color:#fff
    style GUARD fill:#f59e0b,stroke:#d97706,color:#1f2937
    style OBS fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
    style OUT fill:#059669,stroke:#047857,color:#fff
```

```python
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.

```python
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.

```python
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  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

---

Source: https://callsphere.ai/blog/ai-agent-photography-studios-session-booking-package-selection-gallery
