Resources API

The Resources API allows clients to browse bookable resources and check their availability.

List Resources

Retrieve a list of all active, bookable resources.

Request:

GET /api/v1/resources/

Query Parameters:

Name Type Description
venue integer Filter by venue ID
venue_slug string Filter by venue slug
activity integer Filter by activity type ID
is_active boolean Filter by active status
is_bookable boolean Filter by bookable status

Response:

[
    {
        "id": 1,
        "name": "Court 1",
        "description": "Indoor tennis court with clay surface",
        "venue": 1,
        "venue_name": "Sports Center Munich",
        "venue_slug": "sports-center-munich",
        "capacity": 4,
        "base_price": "25.00",
        "currency": "EUR",
        "price_unit": "hour",
        "booking_interval_minutes": 30,
        "min_booking_duration_minutes": 60,
        "max_booking_duration_minutes": 180,
        "prevent_unbookable_gaps": true,
        "is_active": true,
        "is_bookable": true,
        "attributes": {}
    }
]

Get Resource Details

Retrieve detailed information about a specific resource including venue details and availability schedules.

Request:

GET /api/v1/resources/{id}/

Response:

{
    "id": 1,
    "name": "Court 1",
    "description": "Indoor tennis court with clay surface",
    "venue": {
        "id": 1,
        "name": "Sports Center Munich",
        "slug": "sports-center-munich",
        "address": "Sportstrasse 1, 80000 Munich"
    },
    "activity_types": ["Tennis", "Badminton"],
    "capacity": 4,
    "base_price": "25.00",
    "currency": "EUR",
    "price_unit": "hour",
    "dynamic_pricing_enabled": false,
    "min_price": "20.00",
    "max_price": "40.00",
    "current_demand_level": 0.5,
    "attributes": {},
    "booking_interval_minutes": 30,
    "min_booking_duration_minutes": 60,
    "max_booking_duration_minutes": 180,
    "prevent_unbookable_gaps": true,
    "min_advance_booking_minutes": 60,
    "max_advance_booking_days": 30,
    "is_active": true,
    "is_bookable": true,
    "availability_schedules": [
        {
            "id": 1,
            "schedule_type": "recurring",
            "day_of_week": 1,
            "day_of_week_display": "Monday",
            "specific_date": null,
            "start_date": null,
            "end_date": null,
            "priority": 0,
            "is_active": true,
            "time_slots": [
                {
                    "id": 1,
                    "start_time": "08:00:00",
                    "end_time": "22:00:00",
                    "price_per_unit": "25.00",
                    "pricing_unit": "hour",
                    "slot_duration_minutes": 60,
                    "capacity_override": null,
                    "label": "",
                    "is_bookable": true
                }
            ]
        }
    ],
    "created_at": "2025-01-01T10:00:00Z",
    "updated_at": "2025-01-01T10:00:00Z"
}

Get Resource Availability

Retrieve availability windows, existing bookings, and booking constraints for a resource. The frontend uses this data to calculate valid booking start times and durations.

Request:

GET /api/v1/resources/{id}/availability/

Query Parameters (required):

Name Type Description
start_date string Start date in YYYY-MM-DD format
end_date string End date in YYYY-MM-DD format (max 31 days from start)

Example:

curl -X GET "http://127.0.0.1:8000/api/v1/resources/1/availability/?start_date=2025-01-15&end_date=2025-01-16"

Response:

{
    "resource_id": 1,
    "resource_name": "Court 1",
    "start_date": "2025-01-15",
    "end_date": "2025-01-16",
    "timezone": "Europe/Berlin",
    "booking_interval_minutes": 30,
    "min_duration_minutes": 60,
    "max_duration_minutes": 180,
    "prevent_unbookable_gaps": true,
    "min_advance_booking_minutes": 60,
    "max_advance_booking_days": 30,
    "windows": [
        {
            "date": "2025-01-15",
            "start_time": "08:00:00",
            "end_time": "22:00:00",
            "price_per_hour": "25.00",
            "currency": "EUR",
            "label": "",
            "booking_interval_minutes": 30,
            "min_duration_minutes": 60,
            "max_duration_minutes": 180,
            "prevent_unbookable_gaps": true
        }
    ],
    "booked_slots": [
        {
            "start_datetime": "2025-01-15T10:00:00+01:00",
            "end_datetime": "2025-01-15T11:30:00+01:00",
            "booking_id": "550e8400-e29b-41d4-a716-446655440000"
        }
    ],
    "unavailable_periods": []
}

Response Fields

Constraints (resource-level):

Field Type Description
booking_interval_minutes integer Booking start times must align to this interval (e.g., 30 = :00, :30)
min_duration_minutes integer Minimum booking duration
max_duration_minutes integer Maximum booking duration (null = no limit)
prevent_unbookable_gaps boolean If true, bookings that would leave unbookable gaps are rejected
min_advance_booking_minutes integer Minimum minutes in advance to book
max_advance_booking_days integer Maximum days in advance to book

Windows - Time periods when the resource is available:

Field Type Description
date string The date (YYYY-MM-DD)
start_time string Window start time (HH:MM:SS)
end_time string Window end time (HH:MM:SS)
price_per_hour string Price per hour for this window
currency string Currency code (e.g., "EUR")
label string Optional label (e.g., "Peak Hours", "Happy Hour")

Booked Slots - Existing bookings that block availability:

Field Type Description
start_datetime string Booking start (ISO 8601)
end_datetime string Booking end (ISO 8601)
booking_id string Reference to the booking

Unavailable Periods - Maintenance, holidays, etc.:

Field Type Description
start_datetime string Period start (ISO 8601)
end_datetime string Period end (ISO 8601)
reason string Reason code (maintenance, holiday, etc.)

Frontend Calculation Logic

The frontend should calculate valid booking times as follows:

  1. Generate valid start times within each window:
  2. Start at window.start_time
  3. Increment by booking_interval_minutes (e.g., 08:00, 08:30, 09:00...)
  4. Stop when start_time + min_duration_minutes > window.end_time

  5. For each start time, calculate valid end times:

  6. Minimum: start_time + min_duration_minutes
  7. Maximum: start_time + max_duration_minutes (or window.end_time if no max)
  8. End times should also align to booking_interval_minutes

  9. Remove blocked times:

  10. Remove any slot that overlaps with booked_slots
  11. Remove any slot that overlaps with unavailable_periods

  12. If prevent_unbookable_gaps is true:

  13. Check that the booking doesn't leave a gap smaller than min_duration_minutes between:
    • Start of window and booking start
    • Booking end and next booking/end of window
    • Previous booking end and booking start

Gap Prevention Example

Given: - Window: 08:00 - 12:00 - min_duration_minutes: 60 - Existing booking: 10:00 - 11:30

If a user tries to book 08:00 - 09:30: - Gap between 09:30 and 10:00 = 30 minutes - 30 < 60 (min_duration) = INVALID (would leave unbookable gap)

If a user tries to book 08:00 - 10:00: - No gap (ends exactly where next booking starts) = VALID

Error Responses:

  • 400 Bad Request - Missing or invalid date parameters
  • 404 Not Found - Resource not found or not available