Back to list
groeimetai

scheduled-jobs

by groeimetai

🤖 AI-powered ServiceNow development with 400+ MCP tools. Works with Claude, GPT, Gemini, Ollama & 75+ providers. Deploy widgets, manage incidents, automate workflows - all through natural language. Open-source Build Agent alternative.

42🍴 9📅 Jan 23, 2026

SKILL.md


name: scheduled-jobs description: This skill should be used when the user asks to "create scheduled job", "scheduled script", "cron job", "automation schedule", "recurring task", "batch processing", "nightly job", or any ServiceNow Scheduled Job development. license: Apache-2.0 compatibility: Designed for Snow-Code and ServiceNow development metadata: author: groeimetai version: "1.0.0" category: servicenow tools:

  • snow_schedule_job
  • snow_find_artifact
  • snow_edit_artifact
  • snow_execute_script_with_output

Scheduled Jobs for ServiceNow

Scheduled Jobs automate recurring tasks, batch processing, and maintenance operations.

Job Types

TypeTablePurpose
Scheduled Script Executionsysauto_scriptRun custom scripts
Report Schedulersysauto_reportGenerate and email reports
Table Cleanersys_auto_flushDelete old records
LDAP Refreshldap_server_configSync LDAP data
Discoverydiscovery_scheduleNetwork discovery

Scheduled Script Execution (ES5)

Basic Scheduled Job

// Table: sysauto_script
// Name: Close Stale Incidents
// Run: Daily at 2:00 AM

// Script (ES5 ONLY!):
(function executeScheduledJob() {
    var LOG_PREFIX = '[CloseStaleIncidents] ';
    var closedCount = 0;

    // Find incidents inactive for 30 days
    var staleDate = new GlideDateTime();
    staleDate.addDaysLocalTime(-30);

    var gr = new GlideRecord('incident');
    gr.addQuery('state', 'IN', '1,2,3');  // New, In Progress, On Hold
    gr.addQuery('sys_updated_on', '<', staleDate);
    gr.addQuery('active', true);
    gr.query();

    gs.info(LOG_PREFIX + 'Found ' + gr.getRowCount() + ' stale incidents');

    while (gr.next()) {
        gr.state = 7;  // Closed
        gr.close_code = 'Closed/Resolved by Caller';
        gr.close_notes = 'Auto-closed due to 30 days of inactivity';
        gr.update();
        closedCount++;
    }

    gs.info(LOG_PREFIX + 'Closed ' + closedCount + ' stale incidents');
})();

Scheduled Job with Error Handling (ES5)

// Name: Sync User Data
// Run: Every 6 hours

(function executeScheduledJob() {
    var LOG_PREFIX = '[SyncUserData] ';
    var stats = {
        processed: 0,
        updated: 0,
        errors: 0
    };

    try {
        // Get users needing sync
        var gr = new GlideRecord('sys_user');
        gr.addQuery('u_needs_sync', true);
        gr.addQuery('active', true);
        gr.setLimit(1000);  // Process in batches
        gr.query();

        while (gr.next()) {
            stats.processed++;
            try {
                var updated = syncUserFromSource(gr);
                if (updated) {
                    stats.updated++;
                }
            } catch (e) {
                stats.errors++;
                gs.error(LOG_PREFIX + 'Error syncing user ' + gr.user_name + ': ' + e.message);
            }
        }

        gs.info(LOG_PREFIX + 'Sync complete: ' + JSON.stringify(stats));

        // Send summary email if errors
        if (stats.errors > 0) {
            sendErrorSummary(stats);
        }

    } catch (e) {
        gs.error(LOG_PREFIX + 'Job failed: ' + e.message);
        notifyAdmins('User sync job failed: ' + e.message);
    }

    function syncUserFromSource(userGr) {
        // Sync logic here
        userGr.u_needs_sync = false;
        userGr.u_last_sync = new GlideDateTime();
        return userGr.update();
    }

    function sendErrorSummary(stats) {
        gs.eventQueue('user.sync.errors', null, JSON.stringify(stats), '');
    }

    function notifyAdmins(message) {
        gs.eventQueue('system.job.failure', null, message, '');
    }
})();

Batch Processing Job (ES5)

// Name: Process Large Dataset
// Run: Weekly on Sunday at 1:00 AM

(function executeScheduledJob() {
    var LOG_PREFIX = '[BatchProcessor] ';
    var BATCH_SIZE = 500;
    var MAX_RUNTIME = 3600000;  // 1 hour in ms
    var startTime = new Date().getTime();

    var processed = 0;
    var hasMore = true;

    while (hasMore && !isTimeExceeded()) {
        hasMore = processBatch();
    }

    if (hasMore) {
        gs.warn(LOG_PREFIX + 'Job stopped due to time limit. Processed: ' + processed);
        // Re-queue for next run
        queueContinuation();
    } else {
        gs.info(LOG_PREFIX + 'Job complete. Total processed: ' + processed);
    }

    function processBatch() {
        var gr = new GlideRecord('u_large_table');
        gr.addQuery('u_processed', false);
        gr.setLimit(BATCH_SIZE);
        gr.query();

        if (!gr.hasNext()) {
            return false;
        }

        while (gr.next()) {
            processRecord(gr);
            processed++;
        }

        return true;
    }

    function processRecord(gr) {
        // Processing logic
        gr.u_processed = true;
        gr.u_processed_date = new GlideDateTime();
        gr.update();
    }

    function isTimeExceeded() {
        var elapsed = new Date().getTime() - startTime;
        return elapsed > MAX_RUNTIME;
    }

    function queueContinuation() {
        // Queue another run
        var job = new GlideRecord('sysauto_script');
        if (job.get('name', 'Process Large Dataset - Continuation')) {
            job.next_action = new GlideDateTime();
            job.update();
        }
    }
})();

Schedule Configuration

Run Frequencies

FrequencyCronExample
Every 5 minutes0 */5 * * * ?Health checks
Hourly0 0 * * * ?Data sync
Daily at midnight0 0 0 * * ?Cleanup
Weekly Sunday0 0 0 ? * SUNReports
Monthly 1st0 0 0 1 * ?Billing
CustomVariousSpecific needs

Create Scheduled Job (ES5)

// Create scheduled job programmatically (ES5 ONLY!)
var job = new GlideRecord('sysauto_script');
job.initialize();
job.setValue('name', 'Nightly Cleanup');
job.setValue('active', true);

// Schedule: Daily at 2:00 AM
job.setValue('run_type', 'daily');
job.setValue('run_time', '02:00:00');
// Or use explicit schedule
job.setValue('run_dayofweek', 'daily');

// Script
job.setValue('script',
    '(function executeScheduledJob() {\n' +
    '    var gr = new GlideRecord("sys_audit_delete");\n' +
    '    gr.addQuery("sys_created_on", "<", gs.daysAgo(90));\n' +
    '    gr.deleteMultiple();\n' +
    '    gs.info("Cleanup complete");\n' +
    '})();'
);

// Run as system
job.setValue('run_as', '');  // Empty = System

job.insert();

Conditional Execution

// Job that checks conditions before running (ES5 ONLY!)
(function executeScheduledJob() {
    var LOG_PREFIX = '[ConditionalJob] ';

    // Check if job should run
    if (!shouldRun()) {
        gs.info(LOG_PREFIX + 'Skipping execution - conditions not met');
        return;
    }

    // Execute main logic
    executeMainTask();

    function shouldRun() {
        // Check business hours
        var now = new GlideDateTime();
        var hour = parseInt(now.getLocalTime().getByFormat('HH'), 10);

        // Only run outside business hours (before 6am or after 8pm)
        if (hour >= 6 && hour < 20) {
            return false;
        }

        // Check for active change freeze
        var freeze = new GlideRecord('change_request');
        freeze.addQuery('type', 'freeze');
        freeze.addQuery('state', 'implement');
        freeze.query();

        if (freeze.hasNext()) {
            gs.info(LOG_PREFIX + 'Change freeze active');
            return false;
        }

        return true;
    }

    function executeMainTask() {
        // Main job logic here
        gs.info(LOG_PREFIX + 'Executing main task');
    }
})();

Job Monitoring

Check Job Status (ES5)

// Query scheduled job history (ES5 ONLY!)
var history = new GlideRecord('sys_trigger');
history.addQuery('name', 'CONTAINS', 'Nightly Cleanup');
history.orderByDesc('sys_created_on');
history.setLimit(10);
history.query();

while (history.next()) {
    gs.info('Job: ' + history.getValue('name') +
            ' | State: ' + history.getValue('state') +
            ' | Next: ' + history.getValue('next_action'));
}

Job with Metrics (ES5)

// Job that records performance metrics (ES5 ONLY!)
(function executeScheduledJob() {
    var LOG_PREFIX = '[MetricsJob] ';
    var startTime = new Date().getTime();
    var metrics = {
        startTime: new GlideDateTime().getDisplayValue(),
        recordsProcessed: 0,
        errors: 0
    };

    try {
        // Main processing
        var gr = new GlideRecord('incident');
        gr.addQuery('active', true);
        gr.query();

        while (gr.next()) {
            processRecord(gr);
            metrics.recordsProcessed++;
        }

    } catch (e) {
        metrics.errors++;
        gs.error(LOG_PREFIX + 'Error: ' + e.message);
    } finally {
        // Record metrics
        metrics.endTime = new GlideDateTime().getDisplayValue();
        metrics.duration = (new Date().getTime() - startTime) / 1000;
        recordMetrics(metrics);
    }

    function processRecord(gr) {
        // Processing logic
    }

    function recordMetrics(metrics) {
        var metricsRecord = new GlideRecord('u_job_metrics');
        metricsRecord.initialize();
        metricsRecord.setValue('u_job_name', 'MetricsJob');
        metricsRecord.setValue('u_start_time', metrics.startTime);
        metricsRecord.setValue('u_end_time', metrics.endTime);
        metricsRecord.setValue('u_duration', metrics.duration);
        metricsRecord.setValue('u_records_processed', metrics.recordsProcessed);
        metricsRecord.setValue('u_errors', metrics.errors);
        metricsRecord.insert();

        gs.info(LOG_PREFIX + 'Metrics: ' + JSON.stringify(metrics));
    }
})();

MCP Tool Integration

Available Tools

ToolPurpose
snow_schedule_jobCreate scheduled job
snow_find_artifactFind existing jobs
snow_execute_script_with_outputTest job script
snow_get_logsCheck job execution logs

Example Workflow

// 1. Create scheduled job
await snow_schedule_job({
    name: 'Daily Report Generator',
    run_type: 'daily',
    run_time: '06:00:00',
    script: '/* report generation script */',
    active: true
});

// 2. Test the script
await snow_execute_script_with_output({
    script: '/* test job script */'
});

// 3. Check logs
await snow_get_logs({
    filter: 'message CONTAINS "Daily Report"',
    limit: 50
});

Best Practices

  1. Logging - Comprehensive logging for debugging
  2. Error Handling - Try-catch with notifications
  3. Batching - Process large datasets in batches
  4. Time Limits - Check runtime to prevent timeouts
  5. Off-Peak - Schedule during low-usage periods
  6. Idempotent - Safe to run multiple times
  7. Monitoring - Record metrics and status
  8. ES5 Only - No modern JavaScript syntax

Score

Total Score

75/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon