Cron Jobs (Gateway Scheduler)

Cron vs Heartbeat? Schau dir Cron vs Heartbeat an, um zu entscheiden, wann du was verwendest.

Cron ist der eingebaute Scheduler des Gateway. Er speichert Jobs persistent, weckt den Agent zur richtigen Zeit und kann optional die Ausgabe zurück in einen Chat liefern.

Wenn du “das jeden Morgen ausführen” oder “den Agent in 20 Minuten anstupsen” willst, ist Cron der richtige Mechanismus.

TL;DR

  • Cron läuft im Gateway (nicht im Modell).
  • Jobs werden unter ~/.openclaw/cron/ gespeichert, sodass Neustarts keine Zeitpläne verlieren.
  • Zwei Ausführungsstile:
    • Main Session: System-Event in die Warteschlange stellen, dann beim nächsten Heartbeat ausführen.
    • Isolated: Dedizierte Agent-Ausführung in cron:<jobId>, optional mit Ausgabe-Zustellung.
  • Wakeups sind erstklassig: Ein Job kann “jetzt aufwecken” vs. “nächster Heartbeat” anfordern.

Schnellstart (praktisch)

Erstelle eine einmalige Erinnerung, prüfe ob sie existiert und führe sie sofort aus:

openclaw cron add \
  --name "Reminder" \
  --at "2026-02-01T16:00:00Z" \
  --session main \
  --system-event "Reminder: check the cron docs draft" \
  --wake now \
  --delete-after-run

openclaw cron list
openclaw cron run <job-id> --force
openclaw cron runs --id <job-id>

Plane einen wiederkehrenden isolierten Job mit Zustellung:

openclaw cron add \
  --name "Morning brief" \
  --cron "0 7 * * *" \
  --tz "America/Los_Angeles" \
  --session isolated \
  --message "Summarize overnight updates." \
  --deliver \
  --channel slack \
  --to "channel:C1234567890"

Tool-Call-Äquivalente (Gateway Cron Tool)

Die kanonischen JSON-Strukturen und Beispiele findest du unter JSON Schema für Tool Calls.

Wo Cron Jobs gespeichert werden

Cron Jobs werden standardmäßig auf dem Gateway-Host unter ~/.openclaw/cron/jobs.json persistent gespeichert. Das Gateway lädt die Datei in den Speicher und schreibt sie bei Änderungen zurück. Manuelle Bearbeitungen sind daher nur sicher, wenn das Gateway gestoppt ist. Verwende besser openclaw cron add/edit oder die Cron Tool Call API für Änderungen.

Einsteigerfreundliche Übersicht

Denk an einen Cron Job als: wann ausführen + was tun.

  1. Wähle einen Zeitplan

    • Einmalige Erinnerung → schedule.kind = "at" (CLI: --at)
    • Wiederkehrender Job → schedule.kind = "every" oder schedule.kind = "cron"
    • Wenn dein ISO-Zeitstempel keine Zeitzone angibt, wird er als UTC behandelt.
  2. Wähle wo er läuft

    • sessionTarget: "main" → läuft beim nächsten Heartbeat mit Main-Context.
    • sessionTarget: "isolated" → läuft als dedizierte Agent-Ausführung in cron:<jobId>.
  3. Wähle die Payload

    • Main Session → payload.kind = "systemEvent"
    • Isolated Session → payload.kind = "agentTurn"

Optional: deleteAfterRun: true entfernt erfolgreiche einmalige Jobs aus dem Speicher.

Konzepte

Jobs

Ein Cron Job ist ein gespeicherter Datensatz mit:

  • einem Schedule (wann er laufen soll),
  • einer Payload (was er tun soll),
  • optionaler Delivery (wohin die Ausgabe gesendet werden soll).
  • optionalem Agent Binding (agentId): Job unter einem bestimmten Agent ausführen; falls fehlend oder unbekannt, fällt das Gateway auf den Standard-Agent zurück.

Jobs werden durch eine stabile jobId identifiziert (verwendet von CLI/Gateway APIs). In Agent Tool Calls ist jobId kanonisch; das veraltete id wird aus Kompatibilitätsgründen akzeptiert. Jobs können optional nach einer erfolgreichen einmaligen Ausführung automatisch gelöscht werden via deleteAfterRun: true.

Schedules

Cron unterstützt drei Schedule-Arten:

  • at: Einmaliger Zeitstempel (ms seit Epoch). Gateway akzeptiert ISO 8601 und konvertiert zu UTC.
  • every: Festes Intervall (ms).
  • cron: 5-Feld-Cron-Ausdruck mit optionaler IANA-Zeitzone.

Cron-Ausdrücke verwenden croner. Wenn eine Zeitzone weggelassen wird, wird die lokale Zeitzone des Gateway-Hosts verwendet.

Main vs. Isolated Execution

Main Session Jobs (System Events)

Main Jobs stellen ein System-Event in die Warteschlange und wecken optional den Heartbeat Runner. Sie müssen payload.kind = "systemEvent" verwenden.

  • wakeMode: "next-heartbeat" (Standard): Event wartet auf den nächsten geplanten Heartbeat.
  • wakeMode: "now": Event löst einen sofortigen Heartbeat-Lauf aus.

Das ist die beste Wahl, wenn du den normalen Heartbeat Prompt + Main-Session-Context willst. Siehe Heartbeat.

Isolated Jobs (dedizierte Cron Sessions)

Isolated Jobs führen eine dedizierte Agent-Ausführung in Session cron:<jobId> aus.

Wichtige Verhaltensweisen:

  • Prompt wird mit [cron:<jobId> <job name>] für Nachvollziehbarkeit präfixiert.
  • Jede Ausführung startet eine frische Session ID (keine Übernahme vorheriger Konversationen).
  • Eine Zusammenfassung wird in die Main Session gepostet (Präfix Cron, konfigurierbar).
  • wakeMode: "now" löst einen sofortigen Heartbeat nach dem Posten der Zusammenfassung aus.
  • Wenn payload.deliver: true, wird die Ausgabe an einen Channel zugestellt; andernfalls bleibt sie intern.

Verwende Isolated Jobs für laute, häufige oder “Hintergrund-Aufgaben”, die deine Main-Chat-Historie nicht zuspammen sollen.

Payload Shapes (was läuft)

Zwei Payload-Arten werden unterstützt:

  • systemEvent: Nur Main-Session, geroutet durch den Heartbeat Prompt.
  • agentTurn: Nur Isolated-Session, führt eine dedizierte Agent-Ausführung aus.

Häufige agentTurn Felder:

  • message: Erforderlicher Text-Prompt.
  • model / thinking: Optionale Overrides (siehe unten).
  • timeoutSeconds: Optionaler Timeout-Override.
  • deliver: true um Ausgabe an ein Channel-Ziel zu senden.
  • channel: last oder ein spezifischer Channel.
  • to: Channel-spezifisches Ziel (Telefon/Chat/Channel-ID).
  • bestEffortDeliver: Verhindert Fehlschlagen des Jobs, wenn Zustellung fehlschlägt.

Isolation-Optionen (nur für session=isolated):

  • postToMainPrefix (CLI: --post-prefix): Präfix für das System-Event in Main.
  • postToMainMode: summary (Standard) oder full.
  • postToMainMaxChars: Max. Zeichen wenn postToMainMode=full (Standard 8000).

Model und Thinking Overrides

Isolated Jobs (agentTurn) können das Modell und Thinking-Level überschreiben:

  • model: Provider/Modell-String (z.B. anthropic/claude-sonnet-4-20250514) oder Alias (z.B. opus)
  • thinking: Thinking-Level (off, minimal, low, medium, high, xhigh; nur GPT-5.2 + Codex-Modelle)

Hinweis: Du kannst model auch bei Main-Session-Jobs setzen, aber das ändert das gemeinsame Main-Session-Modell. Wir empfehlen Modell-Overrides nur für Isolated Jobs, um unerwartete Context-Wechsel zu vermeiden.

Auflösungspriorität:

  1. Job Payload Override (höchste)
  2. Hook-spezifische Defaults (z.B. hooks.gmail.model)
  3. Agent Config Default

Delivery (Channel + Target)

Isolated Jobs können Ausgabe an einen Channel zustellen. Die Job Payload kann angeben:

  • channel: whatsapp / telegram / discord / slack / mattermost (Plugin) / signal / imessage / last
  • to: Channel-spezifisches Empfängerziel

Wenn channel oder to weggelassen wird, kann Cron auf die “Last Route” der Main Session zurückfallen (der letzte Ort, an dem der Agent geantwortet hat).

Delivery-Hinweise:

  • Wenn to gesetzt ist, stellt Cron automatisch die finale Ausgabe des Agent zu, auch wenn deliver weggelassen wird.
  • Verwende deliver: true, wenn du Last-Route-Zustellung ohne explizites to willst.
  • Verwende deliver: false, um Ausgabe intern zu halten, auch wenn ein to vorhanden ist.

Target-Format-Erinnerungen:

  • Slack/Discord/Mattermost (Plugin) Targets sollten explizite Präfixe verwenden (z.B. channel:<id>, user:<id>), um Mehrdeutigkeit zu vermeiden.
  • Telegram Topics sollten die :topic:-Form verwenden (siehe unten).

Telegram Delivery Targets (Topics / Forum Threads)

Telegram unterstützt Forum-Topics via message_thread_id. Für Cron-Zustellung kannst du das Topic/Thread in das to-Feld kodieren:

  • -1001234567890 (nur Chat-ID)
  • -1001234567890:topic:123 (bevorzugt: expliziter Topic-Marker)
  • -1001234567890:123 (Kurzform: numerisches Suffix)

Präfixierte Targets wie telegram:... / telegram:group:... werden ebenfalls akzeptiert:

  • telegram:group:-1001234567890:topic:123

JSON Schema für Tool Calls

Verwende diese Strukturen beim direkten Aufrufen von Gateway cron.* Tools (Agent Tool Calls oder RPC). CLI-Flags akzeptieren menschenlesbare Dauern wie 20m, aber Tool Calls verwenden Epoch-Millisekunden für atMs und everyMs (ISO-Zeitstempel werden für at-Zeiten akzeptiert).

cron.add params

Einmalig, Main Session Job (System Event):

{
  "name": "Reminder",
  "schedule": { "kind": "at", "atMs": 1738262400000 },
  "sessionTarget": "main",
  "wakeMode": "now",
  "payload": { "kind": "systemEvent", "text": "Reminder text" },
  "deleteAfterRun": true
}

Wiederkehrend, Isolated Job mit Zustellung:

{
  "name": "Morning brief",
  "schedule": { "kind": "cron", "expr": "0 7 * * *", "tz": "America/Los_Angeles" },
  "sessionTarget": "isolated",
  "wakeMode": "next-heartbeat",
  "payload": {
    "kind": "agentTurn",
    "message": "Summarize overnight updates.",
    "deliver": true,
    "channel": "slack",
    "to": "channel:C1234567890",
    "bestEffortDeliver": true
  },
  "isolation": { "postToMainPrefix": "Cron", "postToMainMode": "summary" }
}

Hinweise:

  • schedule.kind: at (atMs), every (everyMs), oder cron (expr, optional tz).
  • atMs und everyMs sind Epoch-Millisekunden.
  • sessionTarget muss "main" oder "isolated" sein und muss zu payload.kind passen.
  • Optionale Felder: agentId, description, enabled, deleteAfterRun, isolation.
  • wakeMode ist standardmäßig "next-heartbeat", wenn weggelassen.

cron.update params

{
  "jobId": "job-123",
  "patch": {
    "enabled": false,
    "schedule": { "kind": "every", "everyMs": 3600000 }
  }
}

Hinweise:

  • jobId ist kanonisch; id wird aus Kompatibilitätsgründen akzeptiert.
  • Verwende agentId: null im Patch, um ein Agent Binding zu löschen.

cron.run und cron.remove params

{ "jobId": "job-123", "mode": "force" }
{ "jobId": "job-123" }

Storage & History

  • Job Store: ~/.openclaw/cron/jobs.json (Gateway-verwaltetes JSON).
  • Run History: ~/.openclaw/cron/runs/<jobId>.jsonl (JSONL, automatisch bereinigt).
  • Store-Pfad überschreiben: cron.store in der Config.

Konfiguration

{
  cron: {
    enabled: true, // Standard true
    store: "~/.openclaw/cron/jobs.json",
    maxConcurrentRuns: 1, // Standard 1
  },
}

Cron komplett deaktivieren:

  • cron.enabled: false (Config)
  • OPENCLAW_SKIP_CRON=1 (Umgebungsvariable)

CLI Schnellstart

Einmalige Erinnerung (UTC ISO, automatisches Löschen nach Erfolg):

openclaw cron add \
  --name "Send reminder" \
  --at "2026-01-12T18:00:00Z" \
  --session main \
  --system-event "Reminder: submit expense report." \
  --wake now \
  --delete-after-run

Einmalige Erinnerung (Main Session, sofort aufwecken):

openclaw cron add \
  --name "Calendar check" \
  --at "20m" \
  --session main \
  --system-event "Next heartbeat: check calendar." \
  --wake now

Wiederkehrender Isolated Job (Zustellung an WhatsApp):

openclaw cron add \
  --name "Morning status" \
  --cron "0 7 * * *" \
  --tz "America/Los_Angeles" \
  --session isolated \
  --message "Summarize inbox + calendar for today." \
  --deliver \
  --channel whatsapp \
  --to "+15551234567"

Wiederkehrender Isolated Job (Zustellung an ein Telegram Topic):

openclaw cron add \
  --name "Nightly summary (topic)" \
  --cron "0 22 * * *" \
  --tz "America/Los_Angeles" \
  --session isolated \
  --message "Summarize today; send to the nightly topic." \
  --deliver \
  --channel telegram \
  --to "-1001234567890:topic:123"

Isolated Job mit Model und Thinking Override:

openclaw cron add \
  --name "Deep analysis" \
  --cron "0 6 * * 1" \
  --tz "America/Los_Angeles" \
  --session isolated \
  --message "Weekly deep analysis of project progress." \
  --model "opus" \
  --thinking high \
  --deliver \
  --channel whatsapp \
  --to "+15551234567"

Agent-Auswahl (Multi-Agent-Setups):

# Job an Agent "ops" binden (fällt auf Default zurück, wenn dieser Agent fehlt)
openclaw cron add --name "Ops sweep" --cron "0 6 * * *" --session isolated --message "Check ops queue" --agent ops

# Agent bei einem bestehenden Job wechseln oder löschen
openclaw cron edit <jobId> --agent ops
openclaw cron edit <jobId> --clear-agent

Manueller Lauf (Debug):

openclaw cron run <jobId> --force

Bestehenden Job bearbeiten (Felder patchen):

openclaw cron edit <jobId> \
  --message "Updated prompt" \
  --model "opus" \
  --thinking low

Run History:

openclaw cron runs --id <jobId> --limit 50

Sofortiges System-Event ohne Job zu erstellen:

openclaw system event --mode now --text "Next heartbeat: check battery."

Gateway API Surface

  • cron.list, cron.status, cron.add, cron.update, cron.remove
  • cron.run (force oder due), cron.runs Für sofortige System-Events ohne Job verwende openclaw system event.

Troubleshooting

”Nichts läuft”

  • Prüfe ob Cron aktiviert ist: cron.enabled und OPENCLAW_SKIP_CRON.
  • Prüfe ob das Gateway kontinuierlich läuft (Cron läuft im Gateway-Prozess).
  • Für cron Schedules: Bestätige die Zeitzone (--tz) vs. die Host-Zeitzone.

Telegram liefert an den falschen Ort

  • Für Forum-Topics verwende -100…:topic:<id>, damit es explizit und eindeutig ist.
  • Wenn du telegram:... Präfixe in Logs oder gespeicherten “Last Route” Targets siehst, ist das normal; Cron-Zustellung akzeptiert sie und parst Topic-IDs trotzdem korrekt.