Skip to content

Scheduling

AI Butler’s scheduler runs saved prompts on a cron schedule. Results are delivered to whatever channel you pick: Telegram, Slack, the web chat, or any other active channel.

Unlike most Butler features, scheduling is driven through the agent, not a dedicated CLI. You just tell the agent what you want scheduled, and it calls the built-in schedule.create tool under the hood.

  • Daily briefing — weather, calendar, overnight email, news delivered to Telegram at 7am
  • Weekly report — summarize the week’s work from memory every Friday
  • Recurring reminder — “water the plants every 3 days”
  • Batch job — nightly backup verification or log rotation
  • Monitoring — poll a website every hour, alert if down

Creating scheduled tasks — one message, multiple schedules

Section titled “Creating scheduled tasks — one message, multiple schedules”

The fastest way to understand the scheduler is to watch the agent set up three recurring routines from a single natural-language message:

You: Set up three recurring tasks for me:

  1. Every Monday at 9:00 AM, send me a weekly-plan reminder on the web chat
  2. Every day at 5:00 PM, remind me to do a 5-minute journal entry about what went well today
  3. Every Friday at 4:00 PM, ask me to pick one thing to celebrate from the week

Create all three now, confirm each one’s cron expression and ID, and tell me when each will first fire.

The agent calls schedule.create three times, converts each natural-language spec to a cron expression, gets back the persistent schedule IDs, and returns a clean summary with a cron breakdown explaining each field:

AI Butler creating 3 recurring schedules in one message — table showing Weekly Plan Reminder (cron 0 9 * * 1 → Monday only), Daily Journal Reminder (cron 0 17 * * * → every day 5pm), Friday Celebration Prompt (cron 0 16 * * 5 → Friday only), with schedule IDs and first-fire times, plus a cron breakdown explaining minute/hour/day-of-month/day-of-week fields for each expression

Three schedules created, three cron expressions generated, three stable IDs returned, and the agent proactively noted that the journal reminder fires soonest (today at 5 PM) so you know what to expect first. No UI forms, no picker widgets, no manual cron editing.

Under the hood each call is:

schedule.create(
name="weekly-plan-reminder",
cron="0 9 * * 1",
task="Time to plan your week — what are your top 3 priorities?",
channel="webchat",
account_id="user"
)
→ sched_1775913246174

The natural-language → cron translation has a small rule-based path for common phrases (“every weekday at 8am”, “every 15 minutes”, “every first Monday of the month”), with an optional LLM fallback for tricky expressions the rules don’t cover. Everything is persisted in SQLite and survives restarts.

You can also be explicit if you want to bypass the natural-language path:

You: schedule.create name="weekly-report" cron="0 18 * * 5"
task="summarize this week's activity from memory"
channel="slack" account_id="C1234567890"

Same pattern — just ask:

You: Show me all my currently scheduled tasks. For each one, tell me the schedule name, the cron expression in a human-readable form, the target channel, and what the task does.

The agent calls schedule.list, returns a structured summary, and spots an overlap in the existing schedules that a human might miss:

AI Butler listing existing schedules: 1. Daily Weekday Status Update (every Mon-Fri 8:30 AM, webchat, brief morning status), 2. Weekday Morning Briefing (every Mon-Fri 8:00 AM, webchat, 3-point briefing with project reminder + high-impact suggestion + encouraging line), plus a closing note: 'Both fire on weekdays, but the Morning Briefing hits at 8:00 AM and the Status Update at 8:30 AM — so you get the richer briefing first, followed by the task/reminder digest 30 minutes later. You may want to consolidate these into one if there's overlap.'

Notice the agent’s closing observation: “Both fire on weekdays, but the Morning Briefing hits at 8:00 AM and the Status Update at 8:30 AM — you may want to consolidate these if there’s overlap.” It didn’t just list the schedules — it noticed a duplication and suggested cleanup. This is the kind of meta-observation that’s only possible when memory and scheduling share one store.

To pause, delete, or modify a schedule, just ask:

You: delete the 8:30 AM status update
Butler: Deleted "Daily Weekday Status Update" (sched_xxx)
You: change the Morning Briefing to 9am instead of 8am
Butler: Updated "Weekday Morning Briefing" cron to "0 9 * * 1-5"
You: pause the Friday celebration for this week
Butler: Paused "Friday Celebration Prompt" — next fire is 2026-04-24

Under the hood these call schedule.delete, schedule.update, and schedule.pause respectively — all capability-gated under schedule.manage.

Standard 5-field cron in the server’s timezone:

┌─ minute (0-59)
│ ┌─ hour (0-23)
│ │ ┌─ day of month (1-31)
│ │ │ ┌─ month (1-12)
│ │ │ │ ┌─ day of week (0-6, Sunday=0)
│ │ │ │ │
* * * * *

Examples:

CronMeaning
0 7 * * *Every day at 7:00 AM
0 9 * * 1-5Weekdays at 9:00 AM
*/15 * * * *Every 15 minutes
0 0 1 * *First day of every month
0 18 * * 5Every Friday at 6:00 PM

Again — through the agent:

You: list my schedules
Butler: You have 3 active schedules:
• morning-briefing — every day at 7:00 (→ telegram)
• weekly-report — every Friday at 18:00 (→ slack)
• plants-reminder — every 3 days at 10:00 (→ webchat)
You: delete plants-reminder
Butler: Deleted "plants-reminder".

Under the hood the agent calls schedule.list and schedule.delete — capability-gated tools in the schedule.manage namespace.

Scheduling is enabled by default. You can toggle it and tune its runtime in config.yaml:

configurations:
schedule:
enabled: true
options:
schedule:
tick_interval: 30s # how often the scheduler checks for due tasks
max_concurrent: 3 # max parallel scheduled tasks

The channel parameter on any schedule must match an active channel name — the task’s output is delivered there when it runs:

  • telegram / slack / discord / whatsapp — direct message
  • webchat — notification in the web UI
  • webhook — POST the output to your webhook URL

See Channels for channel setup.

Scheduled tasks are persisted in SQLite with their next-run timestamps. When AI Butler boots, the scheduler picks up where it left off. Missed fires during downtime are logged.