Documentation

10. Tracking Asset Details, Maintenance & Warranty Expirations

Automate Equipment Maintenance, Service Contracts, and Warranty Expiration Alerts

The Scenario

Managing equipment maintenance, warranties, and service contracts through spreadsheets leads to missed inspections, voided warranties, and sudden downtime. Whether you are tracking annual service contracts on lab instruments, routine blade/part maintenance in a machine shop, warranty expirations on meeting room AV assets, or calibration schedules for studio cameras, reactive maintenance is costly. This setup centralizes asset metadata and automates expiration alerts so your facility stays fully operational, safe, and compliant.

🔬 Research Labs & Core Facilities (Primary Example)

  • Example: Track maintenance contract expiry dates, vendor service agreements, and mandatory calibration schedules for sensitive analytical instruments like mass spectrometers or centrifuges.

🏢 Coworking Spaces & Facility Management

  • Example: Monitor warranty expiration dates, HVAC service logs, and maintenance contracts on high-value meeting room assets like smart displays, video conferencing bars, and projector bulbs.

🛠️ Makerspaces & Machine Shops

  • Example: Log blade replacement dates, filter changes, motor inspections, and safety certification schedules for heavy machinery like CNC mills, dust extractors, and laser cutters.

🎙️ Media & Production Studios

  • Example: Manage equipment health, lens calibration, firmware updates, and protection plan warranties across camera bodies, audio consoles, and lighting grids.

Implementation Steps

  1. Create Custom Properties: Go to Administration → Site Settings → Properties, click + Add Property, and create the required date field:
    • Maintenance Contract Expiry Date: Set Type to Date. (Actionable metadata used for automated alerts)
  2. Attach Properties to Resources: Navigate to Resources → [Resource Name] → Profile → Properties and select Add Existing Property to Resource.
  3. Enter Specific Resource Data: Select Maintenance Contract Expiry Date and choose the appropriate expiration date (e.g., 2026-10-31).
  4. Configure Automated Expiration Alerts: Go to Administration → Workflows & Rules → Subscriptions → Active → + Create Subscription to set up a 30-day alert for the maintenance contract expiry date.
  5. Configure Automated Expiration Alerts: Go to Administration → Workflows & Rules → Subscriptions → Active → + Create Subscription.
  6. Set the subscription schedule to run daily (every 1 day) to ensure the system continually checks for upcoming expirations.
  7. Select Run a Script from the subscription task options and paste the script below. Note that the script targets the property named 'Maintenance Contract Expiry Date'; update this value in the script if your custom property uses a different name.

    Note: This script scans the Maintenance Contract Expiry Date field across all resources and triggers email notifications for contracts expiring in 30, 7, or 3 days, helping you track both upcoming and urgent renewals.
1def parse_expiry_date(date_str: str | none = None) -> datetime | none:
2 if date_str != None:
3 parts: list[str] = util.strings.split(s=date_str, separator='-')
4 if len(parts) == 3:
5 year_val: dec = util.strings.to_decimal(s=parts[0])
6 month_val: dec = util.strings.to_decimal(s=parts[1])
7 day_val: dec = util.strings.to_decimal(s=parts[2])
8 parsed_dt: datetime = datetime.from_date_and_time(year=year_val, month=month_val,
9 day=day_val, hour=0, minute=0, second=0)
10 return parsed_dt
11 return None
12
13def calc_days_until(expiry_dt: datetime | none = None, now_dt: datetime | none = None) -> dec | none:
14 if expiry_dt != None:
15 if now_dt != None:
16 exp_start: datetime = datetime.start_of(dt=expiry_dt, unit='day')
17 today_start: datetime = datetime.start_of(dt=now_dt, unit='day')
18 diff_days: dec = datetime.difference(dt1=exp_start, dt2=today_start, as_days=True)
19 return diff_days
20 return None
21
22now: datetime = datetime.now()
23nl: str = util.strings.newline()
24
25tracked_tag: str = 'Lab Machines'
26resource_ids: list[str] = api.resources.for_tag(tag=tracked_tag)
27
28items_3_days: list[str] = []
29items_7_days: list[str] = []
30items_30_days: list[str] = []
31all_items: list[str] = []
32
33for res_id in resource_ids:
34 res_info: dict | none = api.resources.from_id(reservable_id=res_id)
35 res_name: str = 'Unknown Resource'
36
37 if res_info != None:
38 fetched_name: str | none = util.dicts.get(d=res_info, path='name')
39 if fetched_name != None:
40 res_name = fetched_name
41
42 expiry_prop: str | dec | none = api.resources.custom_property(reservable_id=res_id,
43 prop_name='Maintenance Contract Expiry Date')
44
45 if expiry_prop != None:
46 if isinstance(expiry_prop, 'str'):
47 expiry_dt: datetime | none = parse_expiry_date(date_str=expiry_prop)
48 days_remaining: dec | none = calc_days_until(expiry_dt=expiry_dt, now_dt=now)
49
50 if days_remaining != None:
51 if days_remaining == 3:
52 line_3: str = '- ' + res_name + ' (Expires: ' + expiry_prop + ')'
53 items_3_days = items_3_days + [line_3]
54 all_items = all_items + [line_3]
55 else:
56 if days_remaining == 7:
57 line_7: str = '- ' + res_name + ' (Expires: ' + expiry_prop + ')'
58 items_7_days = items_7_days + [line_7]
59 all_items = all_items + [line_7]
60 else:
61 if days_remaining == 30:
62 line_30: str = '- ' + res_name + ' (Expires: ' + expiry_prop + ')'
63 items_30_days = items_30_days + [line_30]
64 all_items = all_items + [line_30]
65
66total_alerts: dec = len(all_items)
67
68if total_alerts == 0:
69 return {'skip': ['SEND_EMAIL']}
70else:
71 # Individual timeframe lists (defaults to 'None' if empty)
72 list_3_day: str = 'None'
73 if len(items_3_days) > 0:
74 list_3_day = util.strings.join(lst=items_3_days, s=nl)
75
76 list_7_days: str = 'None'
77 if len(items_7_days) > 0:
78 list_7_days = util.strings.join(lst=items_7_days, s=nl)
79
80 list_30_days: str = 'None'
81 if len(items_30_days) > 0:
82 list_30_days = util.strings.join(lst=items_30_days, s=nl)
83
84 # Build dynamically grouped sections (omits headers for empty timeframes)
85 sections_list: list[str] = []
86
87 if len(items_3_days) > 0:
88 sec_3: str = '**Urgent - Expiring in 3 Days:**' + nl + list_3_day
89 sections_list = sections_list + [sec_3]
90
91 if len(items_7_days) > 0:
92 sec_7: str = '**Expiring in 7 Days:**' + nl + list_7_days
93 sections_list = sections_list + [sec_7]
94
95 if len(items_30_days) > 0:
96 sec_30: str = '**Expiring in 30 Days:**' + nl + list_30_days
97 sections_list = sections_list + [sec_30]
98
99 double_nl: str = nl + nl
100 formatted_sections: str = util.strings.join(lst=sections_list, s=double_nl)
101 expiring_resources_list: str = util.strings.join(lst=all_items, s=nl)
102
103 return {
104 'vars': {
105 'expiring_sections': formatted_sections,
106 }
107 }
108

8. Set Task ID: Set the Task ID to SCRIPT for the script task.

9. Add Email Notification Task: Add a new subscription task below the script task and select the Send Email task type. Enter the recipient email addresses for anyone who should receive expiry notifications, along with a subject line and body text. Use $expiring_sections in the email body to dynamically pull in the relevant resource details. Here is an example email template you can use:

Hello,

The following resources have maintenance contracts expiring soon:

$expiring_sections

Please review and initiate renewals as needed.

Thanks,

Lab Operations

  1. Configure Email Task Properties: For the Send Email task, configure the following properties:

Input Script Task ID: SCRIPT

Task ID: SEND_EMAIL

11. Test the Subscription: Run a test subscription to verify that everything is configured correctly. Ensure your resources have upcoming expiry dates that fall within the 30-, 7-, or 3-day notification windows. To test, click Test Tasks For Date and adjust the test date as needed so it aligns with an upcoming expiration date window.

📖 Learn more about Subscriptions

📖 Learn more about Custom Properties

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

In this video guide, we walk through centralizing asset details and configuring automated maintenance alerts for facility equipment. The exact same workflow applies to tracking service contract expirations on lab instruments, managing warranty dates for meeting room AV hardware, tracking machine shop maintenance, or scheduling calibration for production studio gear.