Schedule and notify for synthetics and Slack
schedule persists time-based jobs (cron, interval, one-shot) whose v1 action is synthetic HTTP probes with assertions and run history in SQLite. notify posts human-readable updates to Slack via chat.postMessage. Used separately, each solves a different problem; used together, they form the project’s pattern for “probe in the background, page humans when something breaks (or recovers)” without bundling a third-party APM. For Human-in-the-loop (HITL) review queues, add hitl_enqueue_label_studio and webhooks, then use notify to signal reviewers in Slack.
Canonical references: schedule-synthetic-checks.md · mcp-tools.md § schedule · notify-tool.md · mcp-tools.md § notify · hitl-label-studio.md. Site hubs: Schedule synthetic checks, Slack notify, HITL — Label Studio. Skills: clawql-schedule-workflows, clawql-notify-workflows.
What schedule and notify are
| Tool | Role |
|---|---|
schedule | operation: create | list | get | delete | trigger. Persists frequency + action.kind: "synthetic" + synthetic_test (HTTP request, limits, assertions). A background worker (default poll CLAWQL_SCHEDULE_POLL_MS, 5000 ms) runs due jobs automatically (#76). |
notify | Thin execute wrapper for Slack chat_postMessage — channel, text, optional thread_ts, Block Kit blocks as JSON string, etc. Fails fast if Slack auth is missing; maps Slack ok: false to a structured tool error (#77). |
Both are default off — opt in (feature tier diagram in configuration): CLAWQL_ENABLE_SCHEDULE=1, CLAWQL_ENABLE_NOTIFY=1, plus their respective prerequisites (Slack in the merged spec for notify).
Using schedule alone
- Create jobs with
operation: "create",schedule.frequency, andaction(see Schedule for interval / cron / one-shot JSON). - Validate with
operation: "trigger",job_id,dry_run: true— runs assertion logic without persisting a live run row (same as clawql-schedule-workflows). - Inspect with
list/getandinclude_runsto read pass/fail, latency,http_status,error_text,response_excerpt. - Safety:
CLAWQL_SCHEDULE_URL_ALLOWLIST_PREFIXES, localhost/private blocking unless allowlisted, caps underCLAWQL_SCHEDULE_SYNTHETIC_*(schedule-synthetic-checks.md § Safety).
You can operate entirely inside SQLite + MCP (or Grafana dashboards elsewhere) with no Slack — useful in air-gapped or chat-less environments.
Using notify alone
Use notify whenever a human channel should see milestones that are not tied to the schedule worker: deploy finished, batch import complete, “needs approval” pings, thread updates on a long-running investigation (Slack notify, notify-tool.md).
Patterns from clawql-notify-workflows:
- One Slack thread per workflow run (
thread_ts) so replies stay grouped. textcarries status, owner, next action, and links (Onyx, Paperless, GitHub, internal wiki).- Pair with
audit/memory_ingestfor durable traceability.
notify does not require schedule — any agent step can call it after execute, knowledge_search_onyx, or vault writes.
Built-in schedule to Slack notifications
When CLAWQL_ENABLE_NOTIFY=1 and Slack auth is configured, the schedule worker can call the same internal path as notify after a synthetic run:
| Env | Effect |
|---|---|
CLAWQL_SCHEDULE_NOTIFY_CHANNEL | Target channel (ID like C… or #name). If unset, no automatic Slack posts. |
CLAWQL_SCHEDULE_NOTIFY_ON_FAILURE=1 | Post on fail runs (assertion or HTTP failure). |
CLAWQL_SCHEDULE_NOTIFY_ON_RECOVERY=1 | Post only when the latest run is pass and the previous was fail (recovery signal). |
The message body is a compact summary (job name, job_id, http_status, latency_ms, error_text). Failures in Slack delivery are swallowed so the schedule loop never breaks (src/clawql-schedule.ts maybeSendScheduleNotification).
This is best-effort automation — not a replacement for paging policies — but it is the simplest end-to-end “synthetic → human” path with no extra cron scripts.
Agent-orchestrated schedule plus notify
Agents often combine tools explicitly:
schedulecreate— register the recurring probe.scheduletriggerdry_run: true— prove URL + assertions before leaving it enabled.notify— richer message than the built-in template (e.g. @oncall, runbook links, Block Kit).memory_ingest— snapshot job id, channel, and operator decisions.
The built-in env bridge (previous section) covers machine-generated failure/recovery lines; agent-authored notify adds context and routing (threads, mentions, formatted blocks) the worker does not know about.
Human-in-the-loop with Label Studio and Slack
HITL here means humans approve or correct model outputs (or extracted fields) before downstream systems trust them.
hitl_enqueue_label_studio(CLAWQL_ENABLE_HITL_LABEL_STUDIO=1) — push review tasks into Label Studio withclawql_hitlmetadata (confidence, correlation id). See HITL — Label Studio and hitl-label-studio.md.notify— post to#ml-review(or a DM) with project link, task count, and deadline so reviewers know work is waiting (human notification).- Label Studio webhook →
POST /hitl/label-studio/webhookonclawql-mcp-http— persists outcomes viamemory_ingest(vault) orauditwhen the vault is unavailable. notifyagain (agent or small automation) — “Review complete” with correlation id and summary, optionally in the same thread as step 2.
Slack is the attention layer; Label Studio is the work queue; vault / audit is the record.
flowchart LR
subgraph time [Time]
SCH[schedule worker]
end
subgraph human [Humans]
SL[Slack]
LS[Label Studio UI]
end
subgraph mcp [ClawQL MCP]
N[notify]
HITL[hitl_enqueue_label_studio]
WH[webhook to vault or audit]
end
SCH -->|CLAWQL_SCHEDULE_NOTIFY_*| N
HITL --> LS
N --> SL
LS --> WH
WH -.->|optional follow-up| N
Full example env job flow and thread
1 — Environment (schedule + notify + optional worker Slack bridge)
CLAWQL_ENABLE_SCHEDULE=1
CLAWQL_ENABLE_NOTIFY=1
# Slack token vars per notify-tool.md / .env.example
CLAWQL_SCHEDULE_URL_ALLOWLIST_PREFIXES=https://api.example.com,https://status.example.com
CLAWQL_SCHEDULE_NOTIFY_CHANNEL=C0123456789
CLAWQL_SCHEDULE_NOTIFY_ON_FAILURE=1
CLAWQL_SCHEDULE_NOTIFY_ON_RECOVERY=1
2 — Create synthetic job (schedule tool — same shape as Schedule § interval):
{
"operation": "create",
"schedule": {
"frequency": {
"type": "interval",
"seconds": 300
}
},
"action": {
"kind": "synthetic",
"synthetic_test": {
"name": "api-health",
"request": {
"method": "GET",
"url": "https://api.example.com/health",
"headers": { "accept": "application/json" },
"body": null
},
"limits": {
"timeout_ms": 10000,
"max_response_bytes": 1048576,
"max_redirects": 3
},
"assert": {
"status_in": [200],
"latency_ms_max": 2000,
"body_contains": "\"ok\":true"
}
}
}
}
3 — Dry-run before trusting it
{
"operation": "trigger",
"job_id": "<id from create response>",
"dry_run": true
}
4 — Rich human message in Slack (separate from the worker’s compact failure line):
{
"channel": "C0123456789",
"thread_ts": "1713898000.000100",
"text": "*Synthetic `api-health` registered* — job_id `<uuid>`\n• Allowlist: `https://api.example.com`\n• On failure/recovery this channel also receives worker summaries (env bridge).\n• Next: HITL batch queued in Label Studio project `42`."
}
Replace thread_ts with a real parent message timestamp from an earlier notify post.
Safety and operations
- Redact secrets in
notifytext and in stored scheduleerror_text/ excerpts (schedule-synthetic-checks.md). - Webhook auth: production
CLAWQL_HITL_WEBHOOK_TOKENrequirements for Label Studio — see hitl-label-studio.md § 4.3. - Scope: v1
scheduleactions aresyntheticonly; other kinds (e.g.sandbox_exec) stay separate and need their own security review (mcp-tools.md § schedule).
Related guides and references
- Audit tool & observability — breadcrumbs alongside scheduled and HITL events.
- Using search and execute — APIs your synthetics may guard.
- Onyx enterprise search — optional evidence before posting conclusions to Slack.
- Issues #76, #77, #228.
