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.
Use Cases
Section titled “Use Cases”- 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:
- Every Monday at 9:00 AM, send me a weekly-plan reminder on the web chat
- Every day at 5:00 PM, remind me to do a 5-minute journal entry about what went well today
- 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:

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_1775913246174The 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"Listing and managing schedules
Section titled “Listing and managing schedules”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:

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 updateButler: Deleted "Daily Weekday Status Update" (sched_xxx)
You: change the Morning Briefing to 9am instead of 8amButler: Updated "Weekday Morning Briefing" cron to "0 9 * * 1-5"
You: pause the Friday celebration for this weekButler: Paused "Friday Celebration Prompt" — next fire is 2026-04-24Under the hood these call schedule.delete, schedule.update, and schedule.pause respectively — all capability-gated under schedule.manage.
Cron Expressions
Section titled “Cron Expressions”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:
| Cron | Meaning |
|---|---|
0 7 * * * | Every day at 7:00 AM |
0 9 * * 1-5 | Weekdays at 9:00 AM |
*/15 * * * * | Every 15 minutes |
0 0 1 * * | First day of every month |
0 18 * * 5 | Every Friday at 6:00 PM |
Listing and Managing
Section titled “Listing and Managing”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-reminderButler: Deleted "plants-reminder".Under the hood the agent calls schedule.list and schedule.delete — capability-gated tools in the schedule.manage namespace.
Configuration
Section titled “Configuration”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 tasksDelivery Channels
Section titled “Delivery Channels”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 messagewebchat— notification in the web UIwebhook— POST the output to your webhook URL
See Channels for channel setup.
Durability
Section titled “Durability”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.