A cron job that deletes old logs doesn't email you before it runs. It just runs — usually at 3am, usually with rm -rf, and usually on the day your backup script stopped writing. Scheduled automation is the highest-leverage place for a human decision: it's unattended, it repeats, and when it's wrong it's wrong repeatedly.
This post turns any cron job into a gated job: it runs, checks its trigger condition, and asks before the destructive or expensive part executes.
Cron job approval workflow: gate the action, not the job
# Every hour, check disk usage; only ask when it matters
0 * * * * /opt/actionbox/bin/actionbox ask "Nightly backup disk is 94% full — resize volume?" \
--option resize="Resize volume" \
--option ignore="Ignore for now" \
--callback-url https://ops.acme.com/actionbox/disk-callbackThe cron job itself never performs the destructive step — it requests a decision and the callback does the work on approval. No polling, no state file, no partial shell scripts.
Step 1: decide, don't notify
A "disk 94% full" notification is noise. A decision with a default action is work. The difference:
- Notify: "FYI disk is 94% full" → read, ignore, repeat tomorrow
- Decide: "Approve resizing the volume?" → approve/reject in one tap, or let the callback auto-run the default
0 * * * * actionbox ask "Nightly backup disk is 94% full?" \
--option resize="Resize volume" \
--option ignore="Ignore for now" \
--callback-url https://ops.acme.com/actionbox/disk-callbackFor an unattended default, add a generated future RFC3339 value with
--expires and pair it with --on-expire-json using a
single_choice response. The expiration must be generated at runtime rather
than copied as a stale date.
Step 2: rotate with sign-off
Secret rotation is the cron job most teams are afraid to automate — exactly why it needs a gate. Rotate automatically, but stage the production swap behind a decision:
0 3 1 * * actionbox ask "Rotate DATABASE_URL in production?" \
--option rotate="Rotate now" \
--option defer="Defer 7 days" \
--context-json '[{"type":"key_value","items":{"secret":"DATABASE_URL","age_days":"89","rotation_policy":"90d","staging_tested":"true"}}]' \
--callback-url https://ops.acme.com/actionbox/rotate-callbackThe callback is HMAC-signed; your rotation handler verifies it, swaps the secret, and notifies the channel. If the decision times out, nothing rotates — fail-closed by default.
Step 3: backup jobs with a human abort
Backups should run unattended — but give the on-call engineer a veto window:
15 1 * * * actionbox ask "Run nightly pg_dump to S3?" \
--option run="Run backup" \
--option hold="Hold (suspected disk issue)" \
--context-json '[{"type":"key_value","items":{"db":"orders","size_est":"412GB","last_backup":"2026-08-13"}}]' \
--callback-url https://ops.acme.com/actionbox/backup-callbackIf the engineer suspects problems, they tap "Hold" and the backup job skips — instead of paging at 4am because the dump wrote to a failing disk.
Full example: disk-pressure resolver
#!/usr/bin/env bash
# /etc/cron.d/disk-resolver — run hourly
set -euo pipefail
THRESHOLD=90
USAGE=$(df -h / | awk 'NR==2 {gsub(/%/,"",$5); print $5}')
if (( USAGE < THRESHOLD )); then
exit 0 # under threshold: no gate, no noise
fi
/opt/actionbox/bin/actionbox ask \
"Root volume is ${USAGE}% full — resize?" \
--option resize="Resize to 200GB" \
--option ignore="Watch for now" \
--context-json "[{\"type\":\"key_value\",\"items\":{\"volume\":\"/\",\"usage\":\"${USAGE}%\",\"host\":\"$(hostname)\"}}]" \
--callback-url https://ops.acme.com/actionbox/disk-callbackWhy gated cron beats "on-call pages"
- Fail-closed: no answer means no action — the audit trail records the non-decision
- Explicit defaults: unattended jobs resolve to a named default with a reason
- One tool for every job: backups, rotations, cleanups, cache refreshes — same
askpattern, same dashboard, same audit log - The dashboard is the on-call console: every pending decision with its SLA countdown, no separate notification channel to build
Try it
- Install the CLI on any host:
curl -fsSL https://actionbox.cloud/install.sh | sh - Create a Source →
ACTIONBOX_TOKEN - Replace one
echo "disk full"cron notification with anask
Create a free Source · Cron + CLI reference · Secret rotation with sign-off