Skip to main content
Spacebot’s cron system runs scheduled tasks on timers. Each cron job gets a fresh short-lived channel, runs the job’s prompt through the LLM, and delivers the result to a messaging target.

Configuration

Define cron jobs in your agent.toml:
agent.toml
[[cron]]
id = "daily-standup"
prompt = """Summarize what I worked on today based on my memories and tasks. 
Include completed tasks, new memories saved, and any blockers."""
cron_expr = "0 17 * * 1-5"  # 5 PM weekdays
delivery_target = "discord:123456789"
enabled = true

[[cron]]
id = "morning-briefing"
prompt = "Review my calendar and top-priority tasks. Send a brief summary."
interval_secs = 86400  # Daily at consistent wall-clock time
active_hours = [8, 18]  # Only fire between 8 AM and 6 PM
delivery_target = "telegram:user_456"
timeout_secs = 120

Scheduling Modes

Spacebot supports two scheduling modes:
Use standard 5-field cron expressions:
cron_expr = "0 9 * * 1-5"  # 9 AM weekdays
cron_expr
string
5-field cron syntax: minute hour day-of-month month day-of-week
Evaluated in the timezone specified by cron_timezone in [defaults]:
[defaults]
cron_timezone = "America/New_York"  # IANA timezone
If cron_timezone is not set, wall-clock jobs use the system’s local time, which is often UTC in Docker/containerized environments. Set an explicit timezone to avoid jobs skipping their active window.
If both cron_expr and interval_secs are present, cron_expr takes precedence.

Active Hours

Restrict when a job can fire using active_hours:
active_hours = [9, 17]  # Only fire between 9 AM and 5 PM
active_hours
tuple
[start_hour, end_hour] in 24-hour format (0-23)
Examples:
active_hours = [9, 17]  # 9 AM - 5 PM
Active hours respect the cron_timezone setting.

Delivery Targets

Results are sent via the messaging system:
delivery_target = "adapter:target"
delivery_target
string
required
Format: adapter:target_idExamples:
  • discord:123456789 (Discord channel ID)
  • telegram:user_456 (Telegram user ID)
  • webhook:https://example.com/hook
The agent must have the corresponding messaging adapter configured. See Messaging for adapter setup.

Run-Once Jobs

Schedule one-time execution:
[[cron]]
id = "onboarding-welcome"
prompt = "Send a welcome message with getting-started tips"
interval_secs = 60  # Fire 1 minute after startup
delivery_target = "discord:123456789"
run_once = true
run_once
boolean
If true, the job disables itself after first execution (default: false)
Run-once jobs transition to enabled = false in the database after firing. You can re-enable them via the API.

Execution Model

When a cron job fires:
1

Create Channel

Spacebot creates a fresh short-lived channel with ID cron:{job_id}.
2

Send Prompt

The job’s prompt becomes a synthetic message sent to the channel.
3

LLM Processing

The channel processes the message with full branching and worker capabilities.
4

Collect Response

Text responses are collected (up to the timeout).
5

Deliver

The result is sent to the delivery target via the messaging system.
6

Cleanup

The channel terminates. Its history is discarded (cron jobs are stateless).
src/cron/scheduler.rs
let timeout = Duration::from_secs(job.timeout_secs.unwrap_or(120));

let channel = Channel::new(
    channel_id.clone(),
    context.deps.clone(),
    response_tx,
    event_rx,
    context.screenshot_dir.clone(),
    context.logs_dir.clone(),
);

// Collect responses until timeout or channel closes
timeout_secs
integer
Maximum wall-clock seconds to wait for the job to complete (default: 120)

Circuit Breaker

Cron jobs have automatic failure protection:
1

Failure Tracking

Each job tracks consecutive_failures.
2

Threshold

After 3 consecutive failures, the circuit breaker trips.
3

Auto-Disable

The job is automatically disabled and persisted to the database.
src/cron/scheduler.rs
const MAX_CONSECUTIVE_FAILURES: u32 = 3;

if consecutive_failures >= MAX_CONSECUTIVE_FAILURES {
    tracing::warn!(
        cron_id = %job_id,
        "circuit breaker tripped, disabling"
    );
    job.enabled = false;
    store.update_enabled(&job_id, false).await?;
}
Re-enable via the API:
curl -X POST http://localhost:3000/api/agents/spacebot/cron/daily-standup/enable
This resets the failure counter.

Dynamic Management

Manage cron jobs at runtime via LLM tools or HTTP API:
Channels have access to the cron tool:
{
  "action": "create",
  "id": "weekly-review",
  "prompt": "Summarize this week's progress",
  "cron_expr": "0 9 * * 1",
  "delivery_target": "discord:123456789"
}

Execution Logs

Cron executions are logged to the cron_executions table:
CREATE TABLE cron_executions (
    id TEXT PRIMARY KEY,
    cron_id TEXT NOT NULL,
    started_at TEXT NOT NULL,
    completed_at TEXT,
    success INTEGER NOT NULL,
    summary TEXT
);
Query via the API:
curl http://localhost:3000/api/agents/spacebot/cron/daily-standup/executions?limit=10

Best Practices

Keep Prompts Focused

Cron jobs run in fresh channels with no history. Be explicit about what you want. “Summarize today’s tasks” not “what did I do?”

Set Active Hours

Avoid firing jobs during off-hours. Use active_hours to respect work/life boundaries.

Use Timeouts

Default is 120s. If your job needs more time (e.g., running a full test suite), increase timeout_secs.

Monitor Failures

Check the executions table periodically. If a job is failing repeatedly, the circuit breaker will disable it.
Cron jobs are stateless. The channel created for each execution has no memory of previous runs. If you need continuity, store results as memories or tasks.

Examples

[[cron]]
id = "task-summary"
prompt = "List all tasks in 'in_progress' status and their subtask completion. Include any blockers."
cron_expr = "0 9 * * 1-5"
delivery_target = "discord:123456789"
active_hours = [9, 18]
[[cron]]
id = "weekly-review"
prompt = "Recall memories from the past 7 days. Summarize key decisions, learnings, and events."
cron_expr = "0 17 * * 5"  # Friday 5 PM
delivery_target = "telegram:user_456"
timeout_secs = 180
[[cron]]
id = "health-check"
prompt = "Check if the API is responding. If not, investigate logs and report status."
interval_secs = 3600
delivery_target = "webhook:https://alerts.example.com/spacebot"
[[cron]]
id = "morning-briefing"
prompt = "Good morning! Here are your high-priority tasks, recent memories, and calendar for today."
cron_expr = "0 8 * * 1-5"
active_hours = [8, 9]  # Only fire during the 8 AM hour
delivery_target = "discord:123456789"

Build docs developers (and LLMs) love