Auto-reminder and auto-close after 10 business days for pending approvals and On Hold tickets

Deepika Gangra1
Tera Expert

Hi All,

I have a requirement to implement a scheduled job in ServiceNow for both Incidents and Request Items (RITMs) with the following logic:

Requirement:

  1. For records in:
    • Pending Approval
    • On Hold
  2. The system should:
    • Send reminders during the 10 business days
    • If no action is taken within 10 business days, then:
      • Auto-close the ticket

Business Rules:

  • Business days = Sunday to Thursday
  • Exclude weekends (Friday & Saturday)
  • Exclude holidays (defined in schedule)
  • Applies to:
    • Incident
    • sc_req_item (RITM)

Current Approach:

  • I initially implemented a loop-based logic to calculate business days by excluding weekends
  • However, this does not handle holidays
  • I am now considering using GlideSchedule to:
    • Calculate business duration
    • Handle holidays properly

Challenges:

  • How to accurately calculate 10 business days including holidays
  • How to design reminder logic within those 10 days
  • Ensuring performance efficiency in a scheduled job (avoiding heavy loops per record)
  • Applying the same logic consistently across Incident and RITM

    Scheduled job:
    // ================= DAY CHECK =================
    var gdtToday = new GlideDateTime();
    gdtToday.setStartOfDay();

    var day = parseInt(gdtToday.getDayOfWeekLocalTime(), 10);

    // Run only Sunday–Thursday
    if (day != 5 && day != 6) {

        // ================= CALCULATE 10 BUSINESS DAYS =================
        var count = 0;
        var pastDate = new GlideDateTime(gdtToday);

        while (count < 10) {

            pastDate.addDaysLocalTime(-1);

            var d = parseInt(pastDate.getDayOfWeekLocalTime(), 10);

            // Skip Friday (5) and Saturday (6)
            if (d != 5 && d != 6) {
                count++;
            }
        }

        // VERY IMPORTANT → include full day
        pastDate.setDisplayValue(pastDate.getLocalDate() + " 23:59:59");

        gs.info("Auto-close running. Cutoff date: " + pastDate.getDisplayValue());

        // ================= INCIDENT =================
        var appr = new GlideRecord('incident');
        appr.addQuery('active', true);
        appr.addQuery('state', 3);
        appr.addQuery('sys_updated_on', '<=', pastDate);
        appr.addQuery('hold_reason', '6');
        appr.query();

        var incidentCount = 0;

        while (appr.next()) {

            gs.eventQueue('autocancel.incident', appr, "", "");

            appr.state = '8';
            appr.sys_updated_by = 'system';
            appr.comments = "This ticket has been automatically closed due to no activity over the past 10 business days (Sun–Thu). On Hold Reason: " + appr.getDisplayValue('hold_reason');

            appr.update();
            incidentCount++;
        }

        // ================= RITM =================
        var gr_SRonhold = new GlideRecord('sc_req_item');
        gr_SRonhold.addQuery('active', true);
        gr_SRonhold.addQuery('state', 17);
        gr_SRonhold.addQuery('sys_updated_on', '<=', pastDate);
        gr_SRonhold.addQuery('u_on_hold_reason', '2');
        gr_SRonhold.query();

        var ritmCount = 0;

        while (gr_SRonhold.next()) {

            var ritmSysId = gr_SRonhold.sys_id;

            // Reject approvals
            var notapproved = new GlideRecord('sysapproval_approver');
            notapproved.addQuery('source_table', 'sc_req_item');
            notapproved.addQuery('sysapproval', ritmSysId);
            notapproved.addQuery('state', 'requested');
            notapproved.query();

            while (notapproved.next()) {
                notapproved.state = 'rejected';
                notapproved.comments = "Auto-closed after 10 business days. On Hold Reason: " + gr_SRonhold.getDisplayValue('u_on_hold_reason');
                notapproved.update();
            }

            // Close RITM
            gr_SRonhold.state = '4';
            gr_SRonhold.comments = "This ticket has been automatically closed due to no activity over the past 10 business days. On Hold Reason: " + gr_SRonhold.getDisplayValue('u_on_hold_reason');
            gr_SRonhold.update();

            // Update Request
            var grReq = new GlideRecord('sc_request');
            grReq.addQuery('sys_id', gr_SRonhold.request);
            grReq.query();

            while (grReq.next()) {
                grReq.state = '4';
                grReq.update();
            }

            // Update Catalog Tasks
            var grtask = new GlideRecord('sc_task');
            grtask.addQuery('request_item', ritmSysId);
            grtask.query();

            while (grtask.next()) {
                grtask.state = '4';
                grtask.update();
            }

            gs.eventQueue('autocancel.requestItem', gr_SRonhold, "", "");
            ritmCount++;
        }

        // ================= LOG =================
        gs.info("Auto-close completed. Incidents: " + incidentCount + ", RITMs: " + ritmCount);

    } else {

        gs.info("Auto-close job skipped (Weekend)");

    }
2 REPLIES 2

Its_Azar
Mega Sage

Hi there @Deepika Gangra1 

 

Right now your logic only excludes Fri/Sat, so holidays will still break the calculation.

I’d recommend:

  • Create a business schedule (Sun–Thu + holidays)
  • Use DurationCalculator to check if 10 business days elapsed
  • Run reminders separately (ex: day 3, 7, 9)
  • Batch process records to avoid huge loops in scheduled jobs

Also, small optimization:
Instead of querying Requests and Tasks inside every RITM loop, consider using bulk updates or encoded queries to reduce DB hits.

☑️ If this helped, please mark it as Helpful or Accept Solution so others can find the answer too.

Kind Regards,
Azar
Serivenow Rising Star
Developer @ KPMG.

Hi @Its_Azar ,

Thanks for your suggestion, it’s really helpful.

In my case, the requirement is slightly different — I need to send reminders continuously for all 10 business days (Sun–Thu, excluding weekends and holidays) for records in Pending Approval and On Hold state.
If there is still no action after the 10th business day, the ticket should be auto-closed.

So instead of sending reminders only on specific days (like Day 3, 7, 9), the expectation is to trigger reminders daily during the 10 business day window.

Could you please help with a sample script or best approach using DurationCalculator or GlideSchedule to handle this efficiently (especially avoiding loops and handling holidays correctly)?

Also, any recommendations on avoiding duplicate reminders (since this will run as a scheduled job) would be really helpful.

Thanks in advance!