How to Build Scheduled AI Agents with Claude Code
Claude Code now supports scheduled tasks natively. Learn how to set up automated, self-healing AI agents that run on a schedule without supervision.
What Scheduled AI Agents Are (and Why They Matter)
Most AI assistants are reactive. You ask, they answer. But a growing category of agents flips that model — they run on a schedule, perform tasks autonomously, and don’t wait for a human to kick them off.
A scheduled AI agent is essentially a background worker with reasoning capabilities. Instead of running a fixed script that checks logs or sends a report, it can reason about what it finds, make decisions, and take corrective action — all without supervision.
Claude Code, Anthropic’s CLI-based coding and automation agent, is particularly well-suited to this pattern. It can read files, run shell commands, call APIs, write code, and work through multi-step tasks. And because it’s invokable from the command line in a non-interactive mode, you can schedule it to run on any cadence you need — hourly, daily, on a trigger, or continuously in a loop.
This guide walks through how to build scheduled AI agents with Claude Code: how to structure them, how to schedule them reliably, and how to design them so they can catch and recover from their own failures.
Understanding Claude Code’s Non-Interactive Mode
Before you can schedule an agent, you need to understand how Claude Code handles unattended execution.
By default, Claude Code runs as an interactive terminal session — you type prompts, it responds, you iterate. But it also supports a non-interactive mode designed specifically for automation.
The -p Flag
The most important flag for scheduled agents is -p (short for --print). It tells Claude Code to run a task, print the output, and exit — no interaction required.
claude -p "Check the latest deployment logs in ./logs/deploy.log and summarize any errors you find."
This runs the agent, executes whatever tools it needs (reading files, running bash commands), and returns the result to stdout. You can pipe it, redirect it, or capture it in a variable like any other command.
Output Formats
For scheduled agents, you usually want structured output. Claude Code supports three output formats via --output-format:
text— Plain text response (default)json— A JSON object with the response and metadatastream-json— Streaming JSON events, useful for real-time processing
For most scheduled workflows, json is the right choice:
claude -p "Analyze ./data/metrics.csv and flag any anomalies." --output-format json > /var/log/agent-output.json
The JSON output includes the response text, tool calls made, and error details — which makes it much easier to parse downstream and feed into monitoring systems.
Controlling Agent Behavior
A few other flags matter for scheduled agents:
--max-turns N— Limits how many agentic steps the agent can take. Critical for preventing runaway execution on complex tasks.--allowedTools— Restricts which tools the agent can use. If your agent only needs to read files, don’t give it write access.--disallowedTools— Explicitly block specific tools.--model— Specify which Claude model to use. Claude Haiku is significantly cheaper for high-frequency monitoring tasks.--system-prompt— Add a system-level instruction that shapes the agent’s behavior for a specific run.
For a scheduled agent, being explicit about permissions is not optional:
claude -p "Review new pull requests and comment on code quality issues." \
--allowedTools "Bash,Read,Write" \
--max-turns 20 \
--output-format json
The Hooks System
Claude Code includes a hooks system that lets you run shell commands at specific lifecycle points during an agent run. You configure hooks in ~/.claude/settings.json or a project-level .claude/settings.json.
Available hook types:
PreToolUse— Runs before Claude uses a specific toolPostToolUse— Runs after Claude uses a specific toolNotification— Triggers on agent notificationsStop— Runs when the agent finishesSubagentStop— Runs when a subagent finishes
For scheduled agents, the Stop hook is particularly useful for sending notifications, updating dashboards, or triggering downstream workflows after the agent completes.
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "/scripts/notify-agent-complete.sh $CLAUDE_EXIT_CODE"
}
]
}
]
}
}
Setting Up Your Environment
Prerequisites
You’ll need:
- Node.js 18+ — Claude Code runs on Node
- Claude Code installed —
npm install -g @anthropic-ai/claude-code - An Anthropic API key — Stored as
ANTHROPIC_API_KEYin your environment - A Unix-like system — Linux or macOS for cron-based scheduling (Windows users can use Task Scheduler or WSL)
Installation
npm install -g @anthropic-ai/claude-code
Verify the installation:
claude --version
API Key Storage
For scheduled agents, the API key needs to be accessible at runtime without manual input. Store it securely:
# For a system-level service, add to /etc/environment or a secure env file
ANTHROPIC_API_KEY="your-key-here"
For production systems, use a dedicated secrets manager:
- AWS Secrets Manager — Integrates with EC2, Lambda, and ECS
- HashiCorp Vault — Works across cloud providers
- GitHub Actions Secrets — If running in CI/CD pipelines
- 1Password Secrets Automation — Good for team setups
Never hardcode the key in a script or commit it to version control.
Using CLAUDE.md for Persistent Agent Instructions
One of Claude Code’s most useful features for scheduled agents is the CLAUDE.md file. Place one in your project root (or at ~/.claude/CLAUDE.md for global agent instructions), and Claude reads it at the start of every session.
This is where you define the persistent context your agent needs to operate:
# Monitoring Agent Instructions
## Project Context
This is a Node.js API server. Logs are stored in ./logs/.
The database is PostgreSQL running on localhost:5432.
The API serves traffic on ports 3000 (staging) and 4000 (production).
## Agent Responsibilities
- You are a monitoring agent. Your job is to observe and report, not to make changes.
- When you find issues, write them to ./alerts/[timestamp].json
- Never modify files in ./src/ or ./config/
- If you find a critical issue, also append a summary to ./alerts/critical.log
## What "Critical" Means
- Error rate above 5% in the last hour
- Average response time above 2000ms
- Database connection failures
- Any 5xx errors from /api/payments endpoint
## Escalation
For critical alerts, run /scripts/notify-oncall.sh with the alert details.
For warnings, append to /alerts/warnings.log only.
This file acts as standing orders for the agent. Update it once, and every scheduled run picks up the changes automatically.
Building Your First Scheduled Agent
Here’s a complete walkthrough of building a log monitoring agent that runs every hour.
Step 1: Define the Agent’s Scope
Before writing any configuration, be specific about:
- What data the agent needs access to
- What decisions it makes
- What actions it takes
- How it reports results
For this example: the agent reads application logs, identifies errors from the past hour, and writes an alerts file if anything needs attention.
Step 2: Write the Shell Wrapper Script
Create a script that invokes Claude Code:
#!/bin/bash
# /opt/agents/log-monitor.sh
# Load environment variables
source /etc/environment
# Set working directory — always use absolute paths in scheduled scripts
cd /var/www/myapp || { echo "Cannot navigate to project directory"; exit 1; }
# Generate a timestamp for this run
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
LOG_FILE="/var/log/agent-runs/monitor-${TIMESTAMP}.log"
echo "Agent run started: ${TIMESTAMP}" >> "$LOG_FILE"
# Run the agent
claude -p "
You are a log monitoring agent running an automated check.
Current time: ${TIMESTAMP}
Your task:
1. Read ./logs/app.log and ./logs/error.log
2. Find any lines with level ERROR or FATAL from the last 60 minutes
3. Categorize each issue by severity: critical, warning, or informational
4. If there are critical or warning issues, write a JSON file to ./alerts/${TIMESTAMP}.json with:
- timestamp
- severity
- affected_component
- error_message
- suggested_action
5. If everything looks healthy, write 'HEALTHY: ${TIMESTAMP}' to ./status/latest.txt
If you cannot read a log file, note that in your output and continue with what you can access.
" \
--allowedTools "Bash,Read,Write" \
--max-turns 15 \
--output-format json \
>> "$LOG_FILE" 2>&1
EXIT_CODE=$?
echo "Agent run completed with exit code: ${EXIT_CODE}" >> "$LOG_FILE"
# Alert if the agent itself failed
if [ $EXIT_CODE -ne 0 ]; then
/scripts/notify-team.sh "Agent log-monitor failed (exit code $EXIT_CODE). Check $LOG_FILE"
fi
A few things to note here:
- Always use absolute paths. The working directory is unpredictable in scheduled contexts.
- Capture the exit code. Claude Code returns non-zero on failure.
- Include the current timestamp in the prompt. The agent doesn’t have real-time clock awareness unless you provide it.
- Log everything. You’ll need those logs when debugging a 3am failure.
Step 3: Test Before Scheduling
Run the script manually first:
chmod +x /opt/agents/log-monitor.sh
/opt/agents/log-monitor.sh
Check the log output. Watch for:
- Tool permission errors — Adjust
--allowedToolsif the agent can’t access what it needs - Path issues — If the agent reports it can’t find files, check your working directory setup
- Prompt ambiguity — If the agent does something unexpected, your instructions need to be more specific
Iterate on your CLAUDE.md and the prompt until the behavior is exactly what you want.
Step 4: Add the Cron Job
Once the script runs correctly, schedule it:
crontab -e
Add your schedule:
# Run log monitor every hour
0 * * * * /opt/agents/log-monitor.sh
# Run daily summary every morning at 7am
0 7 * * * /opt/agents/daily-summary.sh
# Run security scan every Sunday at 2am
0 2 * * 0 /opt/agents/security-scan.sh
Quick cron syntax reference:
┌───────────── minute (0–59)
│ ┌───────────── hour (0–23)
│ │ ┌───────────── day of month (1–31)
│ │ │ ┌───────────── month (1–12)
│ │ │ │ ┌───────────── day of week (0–6, Sunday=0)
│ │ │ │ │
* * * * * command
Use crontab.guru to validate schedule expressions before deploying them.
Cloud-Based Scheduling Options
Cron works for agents running on a single server, but it has real limits: the server must be running, there’s no built-in retry logic, and failures don’t surface easily.
For more robust setups, several cloud scheduling options integrate cleanly with Claude Code.
GitHub Actions Scheduled Workflows
GitHub Actions supports cron-style scheduling and is a natural fit for code-related agents:
# .github/workflows/daily-code-review.yml
name: Daily Code Quality Review
on:
schedule:
- cron: '0 9 * * 1-5' # Weekdays at 9am UTC
workflow_dispatch: # Allow manual trigger
jobs:
review:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
- name: Run review agent
env:
ANTHROPIC_API_KEY: $
run: |
claude -p "
Review all commits merged to main in the last 24 hours.
For each commit, check:
1. Are there obvious bugs introduced?
2. Are there security concerns (hardcoded secrets, SQL injection risks)?
3. Does the code follow the patterns established in this codebase?
Write your findings to ./review-output/review-$(date +%Y-%m-%d).md
" \
--allowedTools "Bash,Read,Write" \
--max-turns 30 \
--output-format json
- name: Upload review
uses: actions/upload-artifact@v4
with:
name: daily-review
path: review-output/
GitHub Actions also handles secrets, retry logic, and run history for you — which removes a significant chunk of the infrastructure you’d otherwise build yourself.
AWS EventBridge + EC2 or ECS
For agents that need consistent compute resources, AWS EventBridge can trigger a shell script on EC2 via Systems Manager, or start an ECS task on a schedule:
# Lambda handler that triggers agent via SSM
import boto3
def handler(event, context):
ssm = boto3.client('ssm')
response = ssm.send_command(
InstanceIds=['i-1234567890abcdef0'],
DocumentName='AWS-RunShellScript',
Parameters={
'commands': [
'source /etc/environment && /opt/agents/monitoring-agent.sh'
]
}
)
return {'commandId': response['Command']['CommandId']}
Google Cloud Scheduler + Cloud Run Jobs
Google Cloud Scheduler can trigger Cloud Run jobs that run Claude Code in a container. Package your agent setup in a Dockerfile:
FROM node:20-slim
RUN npm install -g @anthropic-ai/claude-code
COPY agents/ /agents/
COPY CLAUDE.md /CLAUDE.md
RUN chmod +x /agents/*.sh
CMD ["/agents/run.sh"]
Build the container, push it to Artifact Registry, and configure Cloud Scheduler to trigger the Cloud Run job on your schedule. Cloud Run handles scaling, retry logic, and execution logs.
Designing Self-Healing Agents
A scheduled agent that runs a task is useful. An agent that can detect failures, diagnose them, and recover — or escalate when it can’t — is what makes autonomous operation actually viable.
Self-healing means the agent can:
- Detect when something is broken
- Understand what went wrong
- Take corrective action within defined bounds
- Verify the fix worked
- Escalate to a human when it can’t fix it
Pattern 1: Check-Then-Act
Structure prompts so the agent assesses the current state before doing anything:
claude -p "
BEFORE TAKING ANY ACTION: assess the current state.
1. Run 'systemctl status migration-service' and note the output
2. Read /var/log/migration/latest.log and check for errors
3. Run /scripts/db-check.sh to verify database connectivity
Based on what you find:
- If everything is healthy: write 'OK: $(date -u)' to /tmp/migration-status.txt
- If the service is stopped: restart it with 'systemctl restart migration-service', wait 10 seconds, check status again, and write the result to /tmp/migration-status.txt
- If you see database connection errors: write a report to /alerts/db-alert-$(date +%s).json — do NOT attempt automated database fixes
- If anything else looks wrong: write details to /alerts/agent-error.log and stop
Do not take any action on the database under any circumstances.
"
The explicit branching matters. The agent needs to know what to do in each scenario, including the “do nothing and escalate” scenario.
Pattern 2: Write-Then-Verify
After taking action, have the agent confirm the action worked:
claude -p "
Your task is to clean up old log files to free disk space.
Steps:
1. Check current disk usage: run 'df -h /var/log'
2. List files in /var/log/app/ older than 30 days
3. Delete those files (but never delete files modified in the last 24 hours, regardless of creation date)
4. Run 'df -h /var/log' again to verify space was freed
5. Write a summary to /var/log/cleanup-$(date +%Y%m%d).txt showing:
- Files deleted and sizes
- Disk space freed
- New disk usage percentage
If disk usage is still above 85% after cleanup, write 'ESCALATE: disk still critical' to /alerts/disk-critical.txt
"
The verification step means the agent confirms its own work. If it deleted files but disk usage didn’t improve, it knows something is still wrong rather than falsely reporting success.
Pattern 3: Idempotent Operations
Design tasks so the agent can safely re-run them without causing problems. Your agent will run again even if the previous run failed halfway through — that’s the nature of scheduled execution.
Idempotency guidelines:
- Check before creating: “Create X if it doesn’t already exist”
- Use timestamps or checksums to avoid processing the same data twice
- Track completion state in a file: write “DONE” when complete, check for it at the start of the next run
- Archive before deleting — never permanently destroy data in an automated context
Example with state tracking:
claude -p "
FIRST: check if /var/state/weekly-report.lock exists.
- If it contains today's date ($(date +%Y-%m-%d)), the report has already been generated. Exit immediately.
- Otherwise, proceed.
Generate the weekly usage report:
[... report generation instructions ...]
When the report is complete, write today's date ($(date +%Y-%m-%d)) to /var/state/weekly-report.lock
"
Pattern 4: Tiered Escalation
The agent can’t and shouldn’t try to fix everything. Define clear boundaries in your CLAUDE.md:
# Escalation Policy
## What You Can Fix Automatically
- Restart stopped application services (not the database)
- Clear temp files when /tmp is above 80% capacity
- Re-trigger failed CI/CD builds
- Rotate log files when they exceed 500MB
## What You Should Alert On — Don't Fix
- Database connection failures or errors
- SSL certificate expiry within 14 days
- Memory usage above 90% for more than 10 minutes
- Any unexpected changes to files in /etc/ or /config/
- More than 3 service restarts in a single hour
## Alert Methods
- Write critical alerts to /alerts/critical-[timestamp].json
- For critical alerts, also run /scripts/notify-oncall.sh with the details
- For warnings, append to /alerts/warnings.log
## Hard Limits
Never modify: /etc/, database files, SSL certificates, user data directories
These guardrails persist across every scheduled run without you repeating them in every prompt.
Real-World Use Cases
Automated Code Review Agent
Runs on a schedule to review recently merged changes:
claude -p "
Check for pull requests merged to main in the last 2 hours.
For each merged PR:
1. Read the diff
2. Check for:
- Obvious logic errors or edge cases not handled
- Missing error handling
- Hardcoded credentials or API keys
- Patterns that conflict with ./docs/coding-standards.md
3. Write your review to ./pr-reviews/pr-[number]-$(date +%Y%m%d).md
Keep feedback specific and constructive. Skip purely stylistic preferences.
If you find potential security issues, mark them clearly as SECURITY REVIEW NEEDED.
" \
--allowedTools "Bash,Read,Write" \
--max-turns 25
Dependency Vulnerability Monitor
Runs nightly to surface vulnerable packages before they become incidents:
claude -p "
Run a dependency audit on this Node.js project.
1. Run 'npm audit --json' and parse the output
2. Run 'npm outdated' and note major version gaps
3. For each critical or high severity vulnerability:
- Is a fixed version available?
- Is it a direct or transitive dependency?
4. Generate a report at ./reports/deps-$(date +%Y%m%d).md with:
- Summary count of vulnerabilities by severity
- Critical issues with exact upgrade commands
- Non-critical issues for next sprint planning
- Packages with major version updates available
Do not automatically update any packages.
" \
--allowedTools "Bash,Read,Write" \
--max-turns 20
Database Health Check
Runs every 15 minutes:
claude -p "
Check PostgreSQL health. Current time: $(date -u +%Y-%m-%dT%H:%M:%SZ)
1. Run: psql -U readonly_user -c 'SELECT count(*), state FROM pg_stat_activity GROUP BY state;'
2. Check for queries running longer than 5 minutes
3. Check table bloat by running /scripts/check-bloat.sql
4. If this is a primary server, check replication lag
Thresholds:
- Active connections > 80% of max_connections → write to /alerts/db-connections-warning.json
- Any query running > 10 minutes → write to /alerts/db-long-query.json with query details
- Replication lag > 30 seconds → write to /alerts/db-replication-lag.json
Always write a health summary to /var/log/db-health/$(date +%Y%m%d%H%M).log
" \
--allowedTools "Bash,Read,Write" \
--max-turns 12
Nightly Data Quality Agent
Checks data pipelines for inconsistencies before the business day starts:
claude -p "
Run data quality checks on yesterday's pipeline output.
1. Read /data/pipeline-outputs/$(date -d 'yesterday' +%Y%m%d)/
2. For each output file:
- Verify row counts are within expected range (documented in /config/pipeline-expectations.json)
- Check for null values in required fields
- Check for duplicate records on primary key columns
- Check for values outside expected ranges
3. Write a data quality report to /reports/dq-$(date +%Y%m%d).json
4. If any critical check fails (row count off by more than 10%, required nulls found), write to /alerts/pipeline-failure-$(date +%s).json
Reference the data dictionary at /docs/data-dictionary.md for field definitions and expected ranges.
" \
--allowedTools "Bash,Read,Write" \
--max-turns 30
Monitoring Your Scheduled Agents
Running scheduled agents without visibility into what they’re doing creates a different problem: you’ve replaced a manual process with an invisible one. You need to know when agents fail, when they find issues, and when they behave unexpectedly.
Structured Logging
Use --output-format json consistently and pipe output to a centralized location:
claude -p "..." --output-format json | tee /var/log/agents/run-$(date +%s).json
The JSON output includes:
- The response text
- Each tool call made (name, input, output)
- Total tokens used
- Any errors encountered
If you’re using a log aggregation system like the ELK stack, Datadog, or Grafana Loki, ship these files there. Structured logs are significantly easier to query and alert on than plain text.
Run History Tracking
Keep a lightweight run history for trend analysis:
#!/bin/bash
AGENT_NAME="log-monitor"
RUN_ID=$(uuidgen)
START_TIME=$(date -u +%s)
RESULT=$(claude -p "..." --output-format json 2>&1)
EXIT_CODE=$?
END_TIME=$(date -u +%s)
DURATION=$((END_TIME - START_TIME))
# Append to run history
echo "{\"run_id\":\"$RUN_ID\",\"agent\":\"$AGENT_NAME\",\"start\":$START_TIME,\"duration\":$DURATION,\"exit_code\":$EXIT_CODE}" \
>> /var/log/agents/run-history.jsonl
A JSONL (newline-delimited JSON) file like this can be loaded directly into most analytics tools for reviewing agent performance over time.
Heartbeat Monitoring
For critical agents, use a dead-man’s switch service. Healthchecks.io, BetterUptime, and similar services expect a ping after each successful run. If they don’t receive it within the expected window, they alert you.
# At the end of a successful agent run
curl -s "https://hc-ping.com/your-check-uuid" > /dev/null
This is one of the most reliable ways to catch silent failures — when the agent runs but exits with an error, or when cron itself fails to fire.
Preventing Overlapping Runs
If an agent run takes longer than its schedule interval, the next run starts before the first one finishes. Use file-based locking to prevent this:
LOCKFILE="/var/lock/log-monitor.lock"
if [ -f "$LOCKFILE" ]; then
echo "Another instance is still running. Exiting."
exit 0
fi
touch "$LOCKFILE"
trap "rm -f $LOCKFILE" EXIT
# Agent code here
The trap ensures the lock file is removed even if the script exits unexpectedly.
Where MindStudio Fits
Building scheduled agents with Claude Code is powerful, but it requires managing the surrounding infrastructure yourself: cron jobs, shell scripts, log aggregation, secrets management, retry logic, and overlapping run prevention. That’s a significant amount of work before you get to the actual agent logic.
If your team doesn’t want to maintain that infrastructure — or if your scheduled agents need to connect to business tools like Slack, HubSpot, Airtable, or Google Sheets — MindStudio handles the scheduling and integration layer natively.
MindStudio supports autonomous background agents that run on a configurable schedule. You build the agent logic in a visual editor, connect it to 1,000+ pre-built integrations (no custom API calls to write), and set the schedule — no shell scripts, no cron, no server to maintain.
For developers who are already using Claude Code and want to extend their agents’ capabilities, MindStudio also offers an Agent Skills Plugin (@mindstudio-ai/agent) — an npm SDK that lets Claude Code agents call typed capabilities like agent.sendEmail(), agent.searchGoogle(), or agent.runWorkflow() directly from their automation scripts. It handles rate limiting, retries, and auth so the Claude Code agent can focus on reasoning rather than API plumbing.
The practical comparison:
| Claude Code + Cron | MindStudio | |
|---|---|---|
| Schedule setup | Edit crontab or configure cloud scheduler | Set in UI |
| Business tool integrations | Write custom API calls | 1,000+ pre-built connectors |
| Secrets management | Handle yourself | Managed by platform |
| Retry logic | Write it yourself | Built-in |
| Run history and monitoring | Set up your own logging stack | Built-in |
| Model flexibility | Claude models | 200+ models available |
For teams that want full control over their execution environment and infrastructure, Claude Code with cron or GitHub Actions is the right approach. For teams that want to build and deploy scheduled agents without owning the infrastructure, MindStudio removes the majority of that overhead.
You can try MindStudio free at mindstudio.ai.
Common Mistakes and How to Avoid Them
Using Relative Paths
The single most common failure mode for scheduled scripts. The working directory when cron runs is not your home directory, and it’s not your project root — it’s typically /. Always use absolute paths, or set the working directory explicitly at the start of every script:
cd /var/www/myapp || { echo "Cannot navigate to project directory"; exit 1; }
The || { exit 1; } part is important. If cd fails, stop immediately — don’t continue running in the wrong directory.
Writing Ambiguous Prompts
When you’re at the terminal, you can clarify what you meant. When the agent runs at 3am without you, it can’t ask. Write prompts that are specific enough to behave correctly in edge cases.
Ambiguous:
Check the logs and report any problems.
Specific:
Read /var/log/app/app.log. Report any lines with log level ERROR or FATAL
from the last 60 minutes (current time: {TIMESTAMP}).
Write findings to /reports/hourly-check-{TIMESTAMP}.json.
If the log file doesn't exist or is empty, write "NO_LOGS_FOUND" to the same path.
The difference matters at 3am when the log file is missing and the agent needs to decide what to do.
No Max Turns Limit
Without --max-turns, an agent can take an arbitrarily large number of steps on a complex or ambiguous task. Set a reasonable limit based on how complex the task actually is. If the agent needs more turns than you expected, that’s usually a sign the prompt needs clarification.
Running as Root
Don’t run scheduled agents as the root user unless you have no other option. Create a dedicated service account with the minimum filesystem permissions the agent needs. Even if the agent has instructions not to modify certain files, those instructions are not a substitute for filesystem-level permissions.
# Create a dedicated user for agent processes
useradd -r -s /sbin/nologin claude-agent
# Run agent as that user
sudo -u claude-agent /opt/agents/log-monitor.sh
Silent Failures
An agent that fails without alerting you is worse than no agent at all. Check the exit code after every agent invocation and alert on non-zero results:
claude -p "..." --output-format json
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
/scripts/send-alert.sh "Agent ${AGENT_NAME} failed (exit code: ${EXIT_CODE}). Check /var/log/agent-runs/"
fi
Not Testing With Actual Scheduled Timing
An agent that works when you run it manually might behave differently when cron runs it. The environment variables available, the PATH, and the working directory can all differ. Always test your complete script by simulating the cron environment:
# Test with a minimal environment similar to what cron uses
env -i PATH=/usr/bin:/bin HOME=/root /opt/agents/log-monitor.sh
Frequently Asked Questions
Does Claude Code support scheduling natively?
Claude Code itself is a CLI tool — it doesn’t have a built-in scheduler. The scheduling layer comes from external tooling: cron for server-based scheduling, GitHub Actions for code-related workflows, or cloud schedulers like AWS EventBridge or Google Cloud Scheduler. That’s not really a limitation; it means you can use whatever scheduling infrastructure you already have, and Claude Code agents fit naturally into existing automation pipelines. What Claude Code does support natively is the non-interactive execution mode (-p flag) that makes scheduling practical in the first place.
How much does it cost to run scheduled Claude Code agents?
Costs depend on three factors: execution frequency, task complexity, and model choice. Claude Haiku is dramatically cheaper than Claude Sonnet or Opus for high-frequency tasks where maximum reasoning capability isn’t needed — things like log checking, health monitoring, or simple data validation. Use --max-turns to cap execution and prevent runaway cost on unexpected edge cases. For a rough estimate, run the task manually a few times, note the token usage from the JSON output, and multiply by your schedule frequency.
Can scheduled Claude Code agents access the internet?
Claude Code agents can run bash commands, which means they can use curl, wget, or any installed CLI tool to make HTTP requests. They don’t have a browser-based web access capability built in, but for most scheduled automation tasks (calling APIs, checking endpoints, querying services) bash-level HTTP access is sufficient. If you need agents that can browse and parse arbitrary web pages, you can install and invoke browser automation tools like Playwright from within the agent’s bash environment.
How do I handle failures in a scheduled agent?
Build failure handling at two levels. First, in your shell wrapper: check the exit code after the claude invocation, log the full output, and send an alert if it’s non-zero. Second, within the prompt itself: give the agent explicit instructions for what to do when it encounters an error, including when to stop trying and write an escalation alert rather than retrying indefinitely. The combination of OS-level exit code checking and agent-level error instructions gives you much better coverage than either alone.
Is it safe to give a scheduled agent filesystem write access?
It can be, with the right layers of control. Don’t rely solely on agent instructions — also enforce permissions at the OS level by running the agent as a restricted service account with write access only to specific directories. Set --allowedTools to explicitly restrict which tools the agent can use. Include clear write restrictions in your CLAUDE.md file. And test thoroughly in a staging environment before running anything in production. The combination of OS permissions and agent-level instructions is more robust than either one alone.
Can I run multiple Claude Code agents in parallel?
Yes. Each claude invocation is independent and stateless by default. You can run multiple agents concurrently without them interfering with each other, as long as they’re not writing to the same files without coordination. For agents that share state (writing to the same log file, the same database table, or the same output directory), use file locking or a database as the coordination layer to prevent race conditions.
How do I pass dynamic context to a scheduled agent?
Inject it directly into the prompt string in your shell script. Since you’re building the prompt with shell string interpolation, you can include any value the shell can produce:
LAST_RUN=$(cat /var/state/last-run.txt 2>/dev/null || echo "never")
ERROR_COUNT=$(wc -l < /var/log/app/errors.log 2>/dev/null || echo "0")
CURRENT_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)
claude -p "
Current context:
- Last successful run: ${LAST_RUN}
- Current error count in logs: ${ERROR_COUNT}
- Current time: ${CURRENT_TIME}
Your task: [...]
"
This pattern lets you feed the agent any runtime state it needs without modifying your CLAUDE.md or building a separate context-passing system.
Key Takeaways
Building reliable scheduled AI agents with Claude Code comes down to a handful of core principles:
- Use
-pfor automation — Non-interactive mode is the foundation. Without it, scheduling isn’t possible. - CLAUDE.md carries your standing orders — Persistent context, constraints, and escalation policies live there. Every scheduled run inherits them automatically.
- Write prompts with explicit branching — Tell the agent what to do in each scenario, including when to escalate and when to do nothing.
- Design for re-runs — Agents will run again after failures. Idempotent task design prevents those re-runs from causing new problems.
- Instrument everything — Structured logs, exit code checking, and heartbeat monitoring are how you know your agents are working.
- Layer your guardrails — OS-level permissions plus agent-level instructions is more reliable than either one alone.
The combination of Claude Code’s reasoning capabilities and standard scheduling infrastructure gives you autonomous agents that can handle real operations work — without requiring a specialized orchestration platform or significant infrastructure investment.
If you’d rather skip the infrastructure setup entirely, MindStudio provides scheduled background agents with built-in integrations, monitoring, and retry logic — free to start.