Introduction

StateSet Schedules transform your agents from reactive responders to proactive assistants. By automating time-based actions, your agents can anticipate needs, follow up on conversations, perform routine maintenance, and ensure nothing falls through the cracks. This guide shows you how to build sophisticated scheduling systems that work 24/7.

What are Agent Schedules?

Agent Schedules are intelligent automation rules that trigger actions based on time. Unlike simple cron jobs, StateSet Schedules are context-aware, can adapt to business logic, and integrate seamlessly with your agent’s capabilities.

Key Features

Flexible Timing

Cron expressions, intervals, or business calendar rules

Smart Execution

Conditional logic, timezone awareness, and holiday handling

Reliable Delivery

Guaranteed execution with retry logic and monitoring

Use Cases

  • Follow up 24 hours after purchase
  • Check in weekly with VIP customers
  • Send satisfaction surveys after support tickets close
  • Remind about expiring subscriptions

Getting Started

Prerequisites

  1. StateSet account with scheduling enabled
  2. At least one configured agent
  3. Understanding of cron expressions (optional)

Installation

npm install stateset-node @stateset/scheduler

Creating Schedules

Basic Schedule Example

import { StateSetClient } from 'stateset-node';

const client = new StateSetClient({
  apiKey: process.env.STATESET_API_KEY
});

async function createDailyCheckIn() {
  const schedule = await client.schedules.create({
    agent_id: 'agent_123',
    name: 'daily_customer_check_in',
    description: 'Check in with customers who have open issues',
    schedule: {
      type: 'cron',
      expression: '0 9 * * 1-5', // 9 AM weekdays
      timezone: 'America/New_York'
    },
    action: {
      type: 'function',
      function: 'check_open_issues',
      parameters: {
        priority: 'high',
        age_days: 2
      }
    },
    enabled: true,
    metadata: {
      category: 'customer_success',
      owner: 'support_team'
    }
  });
  
  console.log('Schedule created:', schedule.id);
  return schedule;
}

Advanced Schedule with Conditions

async function createSmartFollowUp() {
  const schedule = await client.schedules.create({
    agent_id: 'agent_123',
    name: 'smart_purchase_follow_up',
    description: 'Intelligent post-purchase follow-up based on order value and customer type',
    
    // Complex scheduling rules
    schedule: {
      type: 'event_based',
      trigger: 'order.completed',
      delay: {
        unit: 'hours',
        value: 24,
        business_hours_only: true
      },
      conditions: [
        {
          field: 'order.total',
          operator: 'greater_than',
          value: 100
        },
        {
          field: 'customer.lifetime_value',
          operator: 'greater_than',
          value: 500
        }
      ]
    },
    
    // Dynamic action based on context
    action: {
      type: 'workflow',
      steps: [
        {
          type: 'evaluate',
          condition: 'order.total > 1000',
          true_action: {
            type: 'send_message',
            template: 'vip_thank_you',
            channel: 'email'
          },
          false_action: {
            type: 'send_message',
            template: 'standard_thank_you',
            channel: 'email'
          }
        },
        {
          type: 'wait',
          duration: '7d'
        },
        {
          type: 'send_message',
          template: 'product_review_request',
          channel: 'sms'
        }
      ]
    },
    
    // Error handling
    error_handling: {
      retry_attempts: 3,
      retry_delay: '5m',
      on_failure: 'create_ticket'
    }
  });
  
  return schedule;
}

Schedule Types

1. Cron-Based Schedules

// Common cron patterns
const cronPatterns = {
  // Every hour
  hourly: '0 * * * *',
  
  // Every day at 9 AM
  daily_morning: '0 9 * * *',
  
  // Every Monday at 10 AM
  weekly_monday: '0 10 * * 1',
  
  // First day of month at midnight
  monthly_start: '0 0 1 * *',
  
  // Every 15 minutes during business hours
  business_quarter_hour: '*/15 9-17 * * 1-5',
  
  // Last Friday of month at 5 PM
  monthly_last_friday: '0 17 * * 5L'
};

// Create schedule with cron
const cronSchedule = await client.schedules.create({
  agent_id: 'agent_123',
  name: 'hourly_metrics_check',
  schedule: {
    type: 'cron',
    expression: cronPatterns.hourly,
    timezone: 'UTC'
  },
  action: {
    type: 'function',
    function: 'collect_hourly_metrics'
  }
});

2. Interval-Based Schedules

const intervalSchedule = await client.schedules.create({
  agent_id: 'agent_123',
  name: 'continuous_monitoring',
  schedule: {
    type: 'interval',
    every: {
      unit: 'minutes',
      value: 30
    },
    start_time: '2024-01-01T00:00:00Z',
    end_time: '2024-12-31T23:59:59Z'
  },
  action: {
    type: 'function',
    function: 'monitor_system_health'
  }
});

3. Event-Triggered Schedules

const eventSchedule = await client.schedules.create({
  agent_id: 'agent_123',
  name: 'abandoned_cart_recovery',
  schedule: {
    type: 'event_based',
    trigger: 'cart.abandoned',
    delay: {
      unit: 'hours',
      value: 2
    },
    conditions: [
      {
        field: 'cart.value',
        operator: 'greater_than',
        value: 50
      }
    ]
  },
  action: {
    type: 'workflow',
    workflow_id: 'abandoned_cart_recovery_flow'
  }
});

4. Business Calendar Schedules

const businessSchedule = await client.schedules.create({
  agent_id: 'agent_123',
  name: 'quarterly_business_review',
  schedule: {
    type: 'business_calendar',
    calendar: 'fiscal_2024',
    events: [
      'quarter_end',
      'quarter_start'
    ],
    offset: {
      days: -5,
      business_days_only: true
    }
  },
  action: {
    type: 'function',
    function: 'prepare_quarterly_report'
  },
  notifications: {
    on_start: ['email:manager@company.com'],
    on_complete: ['slack:#reports']
  }
});

Complex Workflows

Multi-Step Customer Journey

async function createCustomerJourneySchedule() {
  const journey = await client.schedules.create({
    agent_id: 'agent_123',
    name: 'new_customer_onboarding',
    description: 'Automated 30-day onboarding journey',
    
    schedule: {
      type: 'journey',
      trigger: 'customer.created',
      steps: [
        {
          delay: '1h',
          action: 'send_welcome_email'
        },
        {
          delay: '1d',
          action: 'check_first_login',
          condition: 'customer.logins == 0',
          true_action: 'send_login_reminder'
        },
        {
          delay: '3d',
          action: 'send_getting_started_guide'
        },
        {
          delay: '7d',
          action: 'schedule_onboarding_call',
          condition: 'customer.plan == "enterprise"'
        },
        {
          delay: '14d',
          action: 'send_feature_highlights'
        },
        {
          delay: '30d',
          action: 'send_feedback_survey'
        }
      ]
    },
    
    tracking: {
      metrics: ['completion_rate', 'engagement_score'],
      report_to: 'customer_success_dashboard'
    }
  });
  
  return journey;
}

Intelligent Escalation System

async function createEscalationSchedule() {
  const escalation = await client.schedules.create({
    agent_id: 'agent_123',
    name: 'ticket_escalation_system',
    
    schedule: {
      type: 'continuous',
      check_interval: '15m'
    },
    
    rules: [
      {
        name: 'urgent_escalation',
        condition: {
          all: [
            { field: 'ticket.priority', operator: 'equals', value: 'urgent' },
            { field: 'ticket.age_minutes', operator: 'greater_than', value: 30 },
            { field: 'ticket.status', operator: 'not_equals', value: 'resolved' }
          ]
        },
        action: {
          type: 'escalate',
          to: 'senior_support',
          notification: 'immediate'
        }
      },
      {
        name: 'standard_escalation',
        condition: {
          all: [
            { field: 'ticket.priority', operator: 'equals', value: 'normal' },
            { field: 'ticket.age_hours', operator: 'greater_than', value: 4 }
          ]
        },
        action: {
          type: 'escalate',
          to: 'support_lead',
          notification: 'email'
        }
      },
      {
        name: 'vip_escalation',
        condition: {
          all: [
            { field: 'customer.tier', operator: 'equals', value: 'vip' },
            { field: 'ticket.age_minutes', operator: 'greater_than', value: 15 }
          ]
        },
        action: {
          type: 'escalate',
          to: 'vip_support',
          notification: 'phone'
        }
      }
    ]
  });
  
  return escalation;
}

Schedule Management

Monitoring and Analytics

class ScheduleMonitor {
  async getScheduleMetrics(scheduleId, timeframe = '7d') {
    const metrics = await client.schedules.getMetrics({
      schedule_id: scheduleId,
      timeframe: timeframe,
      metrics: [
        'execution_count',
        'success_rate',
        'average_duration',
        'failure_reasons'
      ]
    });
    
    return {
      health: this.calculateHealth(metrics),
      performance: {
        executions: metrics.execution_count,
        success_rate: `${metrics.success_rate}%`,
        avg_duration: `${metrics.average_duration}ms`,
        reliability: this.calculateReliability(metrics)
      },
      issues: this.identifyIssues(metrics),
      recommendations: this.generateRecommendations(metrics)
    };
  }
  
  calculateHealth(metrics) {
    if (metrics.success_rate >= 99) return 'excellent';
    if (metrics.success_rate >= 95) return 'good';
    if (metrics.success_rate >= 90) return 'fair';
    return 'poor';
  }
  
  identifyIssues(metrics) {
    const issues = [];
    
    if (metrics.success_rate < 95) {
      issues.push({
        type: 'reliability',
        severity: 'high',
        message: `Success rate ${metrics.success_rate}% is below threshold`
      });
    }
    
    if (metrics.average_duration > 5000) {
      issues.push({
        type: 'performance',
        severity: 'medium',
        message: 'Schedule execution taking longer than expected'
      });
    }
    
    return issues;
  }
}

Bulk Schedule Operations

// Pause all schedules for maintenance
async function pauseAllSchedules(reason) {
  const schedules = await client.schedules.list({
    filter: { enabled: true }
  });
  
  const results = await Promise.all(
    schedules.map(schedule => 
      client.schedules.update({
        id: schedule.id,
        enabled: false,
        pause_reason: reason,
        paused_at: new Date().toISOString()
      })
    )
  );
  
  console.log(`Paused ${results.length} schedules`);
  return results;
}

// Migrate schedules to new timezone
async function migrateScheduleTimezones(fromTz, toTz) {
  const schedules = await client.schedules.list({
    filter: { 
      'schedule.timezone': fromTz,
      'schedule.type': 'cron'
    }
  });
  
  for (const schedule of schedules) {
    const updatedCron = convertCronTimezone(
      schedule.schedule.expression,
      fromTz,
      toTz
    );
    
    await client.schedules.update({
      id: schedule.id,
      schedule: {
        ...schedule.schedule,
        expression: updatedCron,
        timezone: toTz
      }
    });
  }
}

Testing Schedules

Test Framework

class ScheduleTester {
  async testSchedule(scheduleId, options = {}) {
    const testRun = await client.schedules.test({
      schedule_id: scheduleId,
      mode: options.mode || 'dry_run',
      test_data: options.testData || {},
      simulate_time: options.simulateTime || new Date()
    });
    
    return {
      would_execute: testRun.would_execute,
      execution_time: testRun.execution_time,
      action_preview: testRun.action_preview,
      validation_errors: testRun.validation_errors,
      estimated_duration: testRun.estimated_duration
    };
  }
  
  async simulateScheduleRun(scheduleId, days = 7) {
    const simulations = [];
    const schedule = await client.schedules.get(scheduleId);
    
    for (let i = 0; i < days * 24; i++) {
      const simulatedTime = new Date();
      simulatedTime.setHours(simulatedTime.getHours() + i);
      
      const wouldRun = this.evaluateCron(
        schedule.schedule.expression,
        simulatedTime,
        schedule.schedule.timezone
      );
      
      if (wouldRun) {
        simulations.push({
          time: simulatedTime,
          action: schedule.action
        });
      }
    }
    
    return {
      schedule_id: scheduleId,
      simulation_period: `${days} days`,
      expected_runs: simulations.length,
      run_times: simulations
    };
  }
}

// Usage
const tester = new ScheduleTester();
const results = await tester.simulateScheduleRun('schedule_123', 30);
console.log(`Schedule would run ${results.expected_runs} times in 30 days`);

Error Handling & Recovery

Robust Error Handling

const resilientSchedule = await client.schedules.create({
  agent_id: 'agent_123',
  name: 'critical_daily_task',
  
  schedule: {
    type: 'cron',
    expression: '0 2 * * *' // 2 AM daily
  },
  
  action: {
    type: 'function',
    function: 'process_daily_reports'
  },
  
  error_handling: {
    retry_strategy: {
      max_attempts: 5,
      delays: [30, 60, 300, 900, 1800], // seconds
      backoff_type: 'exponential'
    },
    
    failure_actions: [
      {
        after_attempts: 3,
        action: {
          type: 'notify',
          channel: 'email',
          recipients: ['oncall@company.com'],
          template: 'schedule_failure_warning'
        }
      },
      {
        after_attempts: 5,
        action: {
          type: 'escalate',
          create_incident: true,
          severity: 'high',
          assign_to: 'oncall_engineer'
        }
      }
    ],
    
    recovery: {
      type: 'compensating_action',
      action: 'run_backup_process',
      notify_on_recovery: true
    }
  },
  
  monitoring: {
    alert_on_miss: true,
    alert_on_delay: {
      threshold_minutes: 15
    },
    health_check_endpoint: 'https://status.company.com/schedules/daily_task'
  }
});

Schedule Dependencies

async function createDependentSchedules() {
  // Parent schedule
  const parentSchedule = await client.schedules.create({
    agent_id: 'agent_123',
    name: 'data_collection',
    schedule: { type: 'cron', expression: '0 1 * * *' },
    action: { type: 'function', function: 'collect_daily_data' }
  });
  
  // Child schedule that depends on parent
  const childSchedule = await client.schedules.create({
    agent_id: 'agent_123',
    name: 'data_processing',
    schedule: {
      type: 'dependent',
      depends_on: parentSchedule.id,
      trigger: 'on_success',
      delay: '30m'
    },
    action: { type: 'function', function: 'process_collected_data' }
  });
  
  // Grandchild schedule
  const reportSchedule = await client.schedules.create({
    agent_id: 'agent_123',
    name: 'report_generation',
    schedule: {
      type: 'dependent',
      depends_on: childSchedule.id,
      trigger: 'on_complete',
      conditions: [
        { field: 'output.record_count', operator: 'greater_than', value: 0 }
      ]
    },
    action: { type: 'function', function: 'generate_report' }
  });
  
  return { parentSchedule, childSchedule, reportSchedule };
}

Best Practices

1. Design for Reliability

// Good: Idempotent actions with proper error handling
const reliableSchedule = {
  action: {
    type: 'function',
    function: 'process_orders',
    parameters: {
      mode: 'idempotent',
      check_processed: true,
      batch_size: 100
    }
  },
  error_handling: {
    retry_attempts: 3,
    alert_on_failure: true
  }
};

// Bad: Non-idempotent actions without safeguards
const unreliableSchedule = {
  action: {
    type: 'function',
    function: 'send_emails',
    parameters: {
      send_all: true // Could send duplicates
    }
  }
};

2. Timezone Awareness

// Good: Explicit timezone handling
const timezoneAwareSchedule = {
  schedule: {
    type: 'cron',
    expression: '0 9 * * 1-5',
    timezone: 'America/New_York',
    observe_dst: true
  },
  metadata: {
    business_hours: '9 AM - 5 PM EST/EDT',
    skip_holidays: true
  }
};

// Bad: Ambiguous timing
const ambiguousSchedule = {
  schedule: {
    type: 'cron',
    expression: '0 9 * * *' // Which 9 AM?
  }
};

3. Performance Optimization

class ScheduleOptimizer {
  // Batch similar schedules
  async optimizeSchedules() {
    const schedules = await client.schedules.list();
    
    // Group schedules by execution time
    const grouped = this.groupByExecutionTime(schedules);
    
    // Create batch processors
    for (const [time, group] of Object.entries(grouped)) {
      if (group.length > 5) {
        await this.createBatchSchedule(time, group);
      }
    }
  }
  
  async createBatchSchedule(time, schedules) {
    return client.schedules.create({
      name: `batch_processor_${time}`,
      schedule: { type: 'cron', expression: time },
      action: {
        type: 'batch',
        actions: schedules.map(s => s.action),
        parallel: true,
        max_concurrent: 10
      }
    });
  }
}

Troubleshooting

Common Issues

  1. Schedule Not Executing

    • Verify timezone settings
    • Check schedule is enabled
    • Validate cron expression
    • Review execution logs
  2. Missed Executions

    • Check system time sync
    • Review resource limits
    • Verify agent availability
    • Check for conflicting schedules
  3. Performance Issues

    • Batch similar operations
    • Optimize action execution
    • Use appropriate intervals
    • Monitor resource usage

Debug Tools

// Schedule debugger
async function debugSchedule(scheduleId) {
  const debug = await client.schedules.debug({
    schedule_id: scheduleId,
    include: ['execution_history', 'next_runs', 'conflicts']
  });
  
  console.log('Schedule Debug Info:');
  console.log('- Status:', debug.status);
  console.log('- Last run:', debug.last_execution);
  console.log('- Next 5 runs:', debug.next_runs.slice(0, 5));
  console.log('- Conflicts:', debug.conflicts);
  console.log('- Error rate:', debug.error_rate);
  
  return debug;
}

Next Steps


Pro Tip: Start with simple schedules and gradually add complexity. Always test schedules in a staging environment before deploying to production.

For schedule templates and examples, visit our GitHub repository or contact support@stateset.com.