Heartbeats¶
Heartbeats are periodic autonomous check-ins. Configure a heartbeat on a channel and the bot runs a prompt on a schedule — monitoring, summarizing, alerting, or triggering workflows without human intervention.
Quick Start¶
- Open a channel → Settings → Heartbeat tab
- Toggle Enabled
- Set an interval (e.g., 60 minutes)
- Enter a prompt (e.g., "Check system health and report any anomalies")
- Save
The bot now runs that prompt every 60 minutes and posts the result to the channel.
How It Works¶
The heartbeat worker polls every 30 seconds for due heartbeats. When one fires:
- The prompt is resolved (workspace file → template → inline → global fallback)
- Metadata is injected — current time, channel activity since last run, previous results
- The bot runs the prompt with all its normal tools, skills, and carapaces
- The result is dispatched based on dispatch mode (auto-post or LLM decides)
- The next run is scheduled using clock-aligned intervals
Clock-Aligned Scheduling¶
Heartbeats snap to even multiples of the interval from midnight UTC. A 30-minute heartbeat fires at :00 and :30 of every hour — not based on when it was created. This makes timing predictable across restarts.
Prompt Resolution¶
Heartbeat prompts are resolved in priority order — first match wins:
| Priority | Source | When to use |
|---|---|---|
| 1 | Workspace file (workspace_file_path) |
Complex prompts that evolve with the project |
| 2 | Template (prompt_template_id) |
Reusable prompts shared across channels |
| 3 | Inline prompt (prompt field) |
Simple, one-off prompts |
| 4 | Global fallback (HEARTBEAT_DEFAULT_PROMPT env var) |
Fleet-wide default |
Workspace file prompts¶
Point the heartbeat at a .md file in the channel's workspace:
The file is read fresh on every run, so the bot can update its own heartbeat instructions as the project evolves. This is the recommended approach for complex monitoring setups.
Template prompts¶
Select a prompt template in the Heartbeat settings tab. Templates are managed in Admin > Templates and can be reused across multiple channel heartbeats.
Dispatch Modes¶
Always (default)¶
The heartbeat result is automatically posted to the channel every time.
Good for: status updates, monitoring dashboards, regular reports.
Optional¶
The result is not auto-posted. Instead, the LLM gets a post_heartbeat_to_channel tool and decides whether the result is worth sharing.
Good for: analysis heartbeats where most runs find "nothing to report." The bot only posts when something interesting happens, keeping the channel clean.
Silent (no dispatch)¶
Set dispatch_results: false to save results to the database without posting anywhere. Useful for background monitoring where you only check results when investigating issues.
Trigger Response¶
Set trigger_response: true to create a follow-up task after the heartbeat completes. The bot can then react asynchronously — useful for chained automations where the heartbeat detects something and a second agent action handles it.
Quiet Hours¶
Suppress or slow heartbeats during off-hours.
Global quiet hours¶
# .env
HEARTBEAT_QUIET_HOURS=23:00-07:00 # Wraps midnight
HEARTBEAT_QUIET_INTERVAL_MINUTES=0 # 0 = disabled during quiet
TIMEZONE=America/New_York # Timezone for the window
Setting HEARTBEAT_QUIET_INTERVAL_MINUTES=0 disables heartbeats entirely during quiet hours. A positive value (e.g., 240) slows them to that cadence instead.
Per-heartbeat quiet hours¶
Override the global window on individual heartbeats:
Per-heartbeat settings take precedence over the global configuration.
Repetition Detection¶
If a heartbeat produces nearly identical output 3+ times in a row, the system detects it and intervenes.
How it works¶
The worker compares the last several results using text similarity (SequenceMatcher). If 3 consecutive runs exceed the similarity threshold, the next run gets a forceful preamble:
REPETITION ALERT — Your last several heartbeat outputs are nearly identical. You MUST produce something substantially different, or say "No updates."
The previous results are included so the LLM understands what it's been repeating.
Configuration¶
# .env
HEARTBEAT_REPETITION_DETECTION=true # Global on/off (default: true)
HEARTBEAT_REPETITION_THRESHOLD=0.8 # Similarity ratio 0–1 (default: 0.8)
Per-heartbeat override:
Workflow Triggers¶
Instead of running a prompt, a heartbeat can trigger a workflow on schedule.
Set workflow_id in the heartbeat config to switch from prompt mode to workflow-trigger mode:
When configured: - The heartbeat triggers the workflow instead of running an inline LLM call - The workflow handles all steps, tools, and dispatch - Duplicate runs are prevented — if the workflow is already running, the trigger is skipped
See the Workflows guide for workflow configuration.
Metadata Injection¶
Every heartbeat run automatically injects context metadata into the system preamble:
- Current time and timezone
- Channel name and heartbeat interval
- Run number (how many times this heartbeat has fired)
- Time since last run and last run timestamp
- Channel activity — message counts since last heartbeat, time of last user message
- Previous result — truncated conclusion from the last run (configurable via
previous_result_max_chars) - Recent run digest — summary of last 3 runs with tool call info
This gives the bot situational awareness without consuming prompt space. The metadata is injected as a system preamble, not part of the user message — it doesn't affect RAG retrieval.
Configuration Reference¶
Heartbeat Settings (per-channel, via UI or API)¶
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false | Master on/off switch |
interval_minutes |
int | 60 | How often to fire (minimum 1) |
prompt |
string | — | Inline prompt text |
workspace_file_path |
string | — | Workspace file to read as prompt |
prompt_template_id |
uuid | — | Template reference |
dispatch_mode |
string | "always" | "always" or "optional" |
dispatch_results |
bool | true | Post result to channel |
trigger_response |
bool | false | Create follow-up task after completion |
model |
string | — | Override LLM model |
model_provider_id |
string | — | Override LLM provider |
quiet_start |
string | — | Quiet window start (HH:MM) |
quiet_end |
string | — | Quiet window end (HH:MM) |
timezone |
string | — | Timezone for quiet hours |
max_run_seconds |
int | — | Execution timeout (overrides global) |
previous_result_max_chars |
int | 500 | How much of last result to inject |
repetition_detection |
bool | — | Override global repetition detection |
workflow_id |
string | — | Trigger this workflow instead of prompt |
workflow_session_mode |
string | — | "shared" or "isolated" |
Environment Variables (global)¶
# Quiet hours window (wraps midnight)
HEARTBEAT_QUIET_HOURS=23:00-07:00
# Behavior during quiet hours: 0 = disabled, >0 = slower cadence (minutes)
HEARTBEAT_QUIET_INTERVAL_MINUTES=0
# Fallback prompt when nothing else is configured
HEARTBEAT_DEFAULT_PROMPT=
# Max chars of previous result injected as context
HEARTBEAT_PREVIOUS_CONCLUSION_CHARS=500
# Repetition detection
HEARTBEAT_REPETITION_DETECTION=true
HEARTBEAT_REPETITION_THRESHOLD=0.8
# Global task timeout
TASK_MAX_RUN_SECONDS=300
# Timezone for global quiet hours
TIMEZONE=UTC
API Reference¶
All endpoints require Authorization: Bearer $API_KEY.
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/admin/channels/{id}/heartbeat |
Get config + recent run history |
| PUT | /api/v1/admin/channels/{id}/heartbeat |
Update heartbeat settings |
| POST | /api/v1/admin/channels/{id}/heartbeat/toggle |
Toggle enabled on/off |
| POST | /api/v1/admin/channels/{id}/heartbeat/fire |
Fire immediately (non-blocking) |
| POST | /api/v1/admin/channels/{id}/heartbeat/infer |
AI-generate a prompt from channel context |
Prompt inference¶
The infer endpoint uses AI to generate a tailored heartbeat prompt based on the channel's bot, workspace schema, and active integrations. Useful for bootstrapping a heartbeat on a new channel.
Examples¶
Monitoring Channel¶
Post system health updates every hour, quiet at night:
enabled: true
interval_minutes: 60
dispatch_mode: always
quiet_start: 23:00
quiet_end: 06:00
timezone: America/New_York
prompt: |
Check system health:
1. Disk usage (flag >80%)
2. Memory usage
3. Service status (docker ps)
Report anomalies clearly. If everything is normal, say "All systems nominal."
Smart Analysis (Optional Dispatch)¶
Run analysis every 2 hours, only post if something is found:
enabled: true
interval_minutes: 120
dispatch_mode: optional
prompt: |
Review recent channel activity and workspace files.
If there are stale tasks, unresolved questions, or items that need attention,
use post_heartbeat_to_channel to alert the team.
If everything looks fine, do nothing.
Daily Workflow Trigger¶
Trigger a multi-step workflow once per day:
enabled: true
interval_minutes: 1440
workflow_id: daily-report
workflow_session_mode: isolated
dispatch_results: true
Business Hours Only¶
Run during work hours with a per-heartbeat quiet window: