Skip to content

Scheduler

Cron-style scheduled events that publish to the message bus. No external cron daemon needed.

workspace/crons/daily-weather.yaml created
→ SchedulerPlugin creates timer
→ [8am] Timer fires → publishes to cron.daily-weather
→ RouterPlugin processes via handleCron()
→ Agent fetches weather, composes response
→ Publishes to message.outbound.signal.{sender}
→ SignalPlugin sends via HTTP
workspace/crons/daily-weather.yaml
id: daily-weather
schedule: "0 8 * * *"
timezone: "America/New_York"
topic: "cron.daily-weather"
payload:
content: "Tell the user today's weather for their location. Keep it brief."
sender: "cron"
channel: "signal"
enabled: true
lastFired: "2026-04-02T12:00:00.000Z"
FieldRequiredDescription
idYesUnique identifier (kebab-case). Used as filename.
typeNocron or once. Auto-detected from schedule format if omitted.
scheduleYesCron expression (0 8 * * *) = recurring. ISO datetime (2026-04-01T15:00:00) = one-shot.
timezoneNoIANA timezone. Defaults to system TZ.
topicYesBus topic to publish on fire (e.g., cron.daily-weather).
payload.contentYesMessage the agent receives when this fires.
payload.senderNoSender identifier. Default: cron.
payload.channelNoReply channel (signal, cli). Default: cli.
enabledNoWhether this schedule is active. Default: true.
lastFiredAutoISO timestamp of last fire. Updated by scheduler.

Use POST /publish to send command.schedule messages from any external caller:

Terminal window
curl -X POST http://localhost:3000/publish \
-H "X-API-Key: $WORKSTACEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"topic": "command.schedule",
"payload": {
"action": "add",
"id": "daily-weather",
"schedule": "0 8 * * *",
"timezone": "America/New_York",
"topic": "cron.daily-weather",
"payload": {
"content": "Tell the user today'\''s weather",
"sender": "cron",
"channel": "signal"
}
}
}'

The scheduler listens on command.schedule for runtime management.

{
"topic": "command.schedule",
"payload": {
"action": "add",
"id": "daily-weather",
"schedule": "0 8 * * *",
"timezone": "America/New_York",
"topic": "cron.daily-weather",
"payload": {
"content": "Tell the user today's weather",
"sender": "cron",
"channel": "signal"
}
}
}
{ "topic": "command.schedule", "payload": { "action": "remove", "id": "daily-weather" } }
{ "topic": "command.schedule", "payload": { "action": "list" } }

Response published to schedule.list.

{ "topic": "command.schedule", "payload": { "action": "pause", "id": "daily-weather" } }
{ "topic": "command.schedule", "payload": { "action": "resume", "id": "daily-weather" } }

If a schedule was missed (container was down), it fires immediately on restart. Only fires once — if missed by more than 24 hours, it’s skipped.

  • Default: system timezone (Intl.DateTimeFormat().resolvedOptions().timeZone)
  • Override: timezone field in YAML
  • Env var: TZ (standard, respected by most runtimes)

Use an ISO datetime in the schedule field. The type is auto-detected. The schedule fires once and the YAML file is deleted.

id: take-break-reminder
schedule: "2026-04-01T15:00:00"
topic: "cron.take-break-reminder"
payload:
content: "Remind the user to take a break"
sender: "cron"
channel: "signal"