Documentation

9. Setting Tiered Free Hours & Usage Caps via Rate Scripts | QReserve

Automate Monthly Allowances, Free Tiers, and Overage Billing Across Any Facility

The Scenario

High-demand resources are easily monopolized when usage isn't managed fairly. Whether you are allocating monthly meeting room allowances for incubator tenants, capped research hours on shared lab instruments, monthly machine allotments for makerspace tiers, or free studio time included in media memberships, setting balanced caps keeps access equitable. This setup enforces monthly usage allowances per group or organization, automatically transitioning users to standard hourly rates or prorating overages once their free tier is exhausted.

🏢 Coworking Spaces & Incubators (Primary Example)

  • Example: Allocate a shared pool of monthly free meeting room hours to tenant companies (e.g., 20 hrs/month for Company A, 10 hrs/month for Company B). Once the cap is hit, additional hours are automatically billed at standard hourly rates.

🔬 Research Labs & Core Facilities

  • Example: Grant specific research groups or departments an monthly allotment of subsidized instrument time (e.g., electron microscopes or DNA sequencers) before shifting to full cost-recovery billable rates.

🛠️ Makerspaces & Machine Shops

  • Example: Include 5 free hours of laser cutter or CNC mill time with premium membership tiers each month, charging standard pay-per-hour machine rates for any time used past the monthly allowance.

🎙️ Media & Production Studios

  • Example: Provide resident creators or production partners with monthly studio credits (e.g., 10 free podcast booth hours per month), prorating bookings that cross over the credit boundary.

QReserve Subscription: Standard
Implementation Difficulty: Advanced

Implementation Steps

  1. Go to Administration → Users → User Groups and create a group for each tenant organization. Use a consistent naming convention, such as "Tenant: Company ABC."
  2. To establish a shared group limit (e.g., 20 hours per month total for all members), add the hour count to the User Group name (e.g., "Company A: 20", Company B: 10).
  3. To enforce these shared limits, use a rate script. Go to Administration → Resources → Scripts → Rate → Create Rate Script. Copy the script template below and save it, ensuring you also provide a name for the script.
1base_rate = util.dicts.get(d=script, path='rate.rate')
2rate_unit = util.dicts.get(d=script, path='rate.rate_unit')
3rate_basis = util.dicts.get(d=script, path='rate.rate_basis')
4
5user_id = reservation['reserved_for']['user_id']
6user_groups = api.users.usergroups(user_id=user_id)
7
8limit = -1
9matched_group_name = ''
10
11for grp in user_groups:
12    g_name = grp['name']
13    if util.strings.match(s=g_name, test='*:*'):
14        parts = util.strings.split(s=g_name, separator=':')
15        if len(parts) == 2:
16            limit_str = util.strings.strip(s=parts[1])
17            limit_val = util.strings.to_decimal(s=limit_str)
18            if limit_val > limit:
19                limit = limit_val
20                matched_group_name = g_name
21
22final_rate = base_rate
23rate_desc = 'Standard rate'
24
25if limit >= 0.0:
26    month_start = datetime.start_of(dt=reservation['start'], unit='month')
27    month_end = datetime.end_of(dt=reservation['start'], unit='month')
28
29    consumed_raw = api.reservations.hours_consumed(
30        usergroup_name=matched_group_name,
31        start=month_start,
32        end=month_end,
33        include_cancelled=False,
34    )
35
36    total_consumed = 0.0
37    if isinstance(consumed_raw, 'dict'):
38        for k, v in consumed_raw:
39            total_consumed = total_consumed + v
40    else:
41        if consumed_raw != None:
42            total_consumed = consumed_raw
43
44    res_duration = reservation['duration'] / 3600
45
46    prior_hours = total_consumed - res_duration
47    if prior_hours < 0.0:
48        prior_hours = 0.0
49
50    if total_consumed <= limit:
51        final_rate = 0.0
52        remaining_hours = limit - total_consumed
53        rate_desc = 'Free reservation (' + str(remaining_hours) + ' free hrs remaining this month)'
54    else:
55        if prior_hours >= limit:
56            final_rate = base_rate
57            rate_desc = 'Standard rate (Monthly free hrs limit of reached)'
58        else:
59            free_hours = limit - prior_hours
60            paid_hours = res_duration - free_hours
61            if res_duration > 0.0:
62                ratio = paid_hours / res_duration
63                final_rate = base_rate * ratio
64            else:
65                final_rate = base_rate
66            rate_desc = 'Prorated rate (' + str(paid_hours) + ' hrs billed)'
67
68return {
69    'rate': final_rate,
70    'rate_unit': rate_unit,
71    'rate_basis': rate_basis,
72    'rate_description': rate_desc
73}


  1. Next, create your rate schedule. Navigate to your resource, create the rate, and attach the rate script you just created. For example, if you set a standard rate of $50/hour, the script will automatically discount the rate based on the free hours available to the user's group. Once their allocated free hours are used up, users will be charged the standard rate.
    Note: This script keeps the user informed on how many free hours they have remaining for the current month.
  2. Finally, verify that your setup is working properly by adding test or bot users to your user groups and placing reservations on their behalf. Confirm that reservations within their allocated cap are priced at $0, while any usage exceeding the cap correctly charges the standard rate.

📖 Learn more about rate scripting

📖 Learn more about Bot and Test Users

📖 Learn more about User Groups

🎬 [ ▶ Watch: Setting Tiered Free Hours and Usage Caps with Rate Scripts]

In this video guide, we walk through configuring tenant company meeting room hour limits using user groups and rate scripts. However, the exact same logic applies to managing subsidized instrument quotas in research labs, monthly machine allotments for makerspace members, or free monthly studio time for media production partners.