Building a Scheduled AI Agent with Claude Managed Agents
How to run a recurring AI agent on a cron schedule with Claude Managed Agents — no client-side scheduler, no server to babysit. Anthropic runs the agent loop and hosts the sandbox; you define the agent and the schedule.
Plenty of useful AI work is recurring: a Monday-morning metrics report, a nightly changelog summary, a weekly compliance sweep. You could wire up a cron job that calls the Claude API, but the moment the task needs to run commands, read files, or browse documentation, you are suddenly hosting and securing an execution sandbox. Claude Managed Agents removes that burden — Anthropic runs the agent loop and the sandbox, and a scheduled deployment fires the sessions on a cron schedule. Here is how to build one end to end.
How Managed Agents is structured
Four concepts do the work. An Agent is a persisted, versioned configuration — model, system prompt, and tools. An Environment is a template for provisioning the container the agent's tools run in. A Session is a single stateful run that references an agent and an environment. The Container is the sandboxed workspace where bash, file operations, and code actually execute. The agent loop itself runs on Anthropic's orchestration layer and acts on the container through tool calls.
The one rule: Agent once, Session every run
This is the mistake I see most often. The model, system prompt, and tools live on the agent object — never on the session. You create the agent one time, store its ID, and reference it on every run. Calling the agent-create endpoint at the top of each run accumulates orphaned agents, pays creation latency for nothing, and defeats the versioning model. Treat agent creation as setup, and session or deployment creation as runtime.
Step 1: Create the environment and agent (once)
Run this as a one-time setup step and store the two IDs it prints. The agent's built-in toolset — agent_toolset_20260401 — gives it bash, file read/write/edit, glob, grep, web fetch, and web search inside the container. The system prompt tells it where to leave its output.
// setup.js — run ONCE, then store the IDs
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic();
const environment = await client.beta.environments.create({
name: 'reporting-env',
config: { type: 'cloud', networking: { type: 'unrestricted' } },
});
const agent = await client.beta.agents.create({
name: 'Weekly Reporter',
model: 'claude-opus-4-8',
system: 'Generate a concise weekly metrics report and write it to /mnt/session/outputs/report.md.',
tools: [{ type: 'agent_toolset_20260401' }],
});
console.log(environment.id, agent.id); // env_... agent_...The SDK sets the required managed-agents beta header automatically on every client.beta call, so you do not manage it by hand. For anything a human maintains, the recommended pattern is to define the agent and environment as version-controlled YAML and apply them with the ant CLI — the CLI owns this control plane, and your app code owns the data plane below.
Step 2: Schedule it as a deployment
A deployment bundles the agent, the environment, the kickoff message, and a cron schedule. Each firing creates a session on its own. The schedule takes a standard cron expression and an IANA timezone, and it matches wall-clock time — so this example runs at 09:00 India time every Monday regardless of daylight-saving shifts elsewhere. Check upcoming_runs_at to confirm the schedule parses the way you expect.
const deployment = await client.beta.deployments.create({
name: 'Weekly report',
agent: agent.id,
environment_id: environment.id,
initial_events: [
{ type: 'user.message', content: [{ type: 'text', text: 'Generate the weekly report.' }] },
],
schedule: {
type: 'cron',
expression: '0 9 * * 1', // 09:00 every Monday
timezone: 'Asia/Kolkata',
},
});
console.log(deployment.schedule.upcoming_runs_at);Step 3: Test with a manual run
Do not wait until Monday to find out the pipeline works. A manual run creates a session immediately — and it works even while the deployment is paused — so it doubles as your smoke test. Then list the deployment runs to audit every trigger, scheduled or manual; each run carries either the created session ID or an error type explaining why a session was not created.
// Fire a session now to test — works even while paused
await client.beta.deployments.run(deployment.id);
// Audit every scheduled and manual trigger
for await (const run of client.beta.deploymentRuns.list({
deployment_id: deployment.id,
})) {
console.log(run.created_at, run.session_id ?? run.error?.type);
}Step 4: Retrieve what the agent produced
Anything the agent writes to /mnt/session/outputs/ is captured automatically. Once a run's session is idle, list the files scoped to that session ID and download them. Note the one header quirk: the session-scoped file list needs the managed-agents beta passed explicitly alongside the Files API header the SDK already adds.
// Download what the agent wrote to /mnt/session/outputs/
for await (const file of client.beta.files.list({
scope_id: sessionId,
betas: ['managed-agents-2026-04-01'],
})) {
const resp = await client.beta.files.download(file.id);
const bytes = Buffer.from(await resp.arrayBuffer());
// write bytes to disk, upload to storage, email it, etc.
}Pause, resume, and versioning
Deployments are reversible where it matters. Pause suppresses scheduled triggers while still allowing manual runs; unpause resumes from the next occurrence without backfilling missed ones; archive is the terminal state. And because the agent is versioned, you can improve its prompt or add a tool without touching running sessions — new sessions pick up the latest version, or you pin a specific one for reproducibility.
When to reach for this versus a cron job
If your recurring task is a single Claude call — summarise this text, classify these rows — a scheduled Route Handler is simpler and cheaper. Managed Agents earns its place when the task is genuinely agentic: it needs to run commands, read and write files, browse documentation across several tool calls, and produce artifacts. At that point, letting Anthropic host the loop and the sandbox is far less work than building and securing your own.
Further Reading
Frequently Asked Questions
What are Claude Managed Agents?
Managed Agents is a surface where Anthropic runs the agent loop and hosts a sandboxed container per session. You define a persisted Agent config (model, system prompt, tools) and an Environment (the container template), then start Sessions that reference them. Tool execution — bash, file operations, web fetch — runs inside the Anthropic-hosted container.
Do I need my own scheduler for a recurring Claude agent?
No. A scheduled deployment runs an agent on a recurring cron schedule, and each firing creates a session automatically. There is no client-side cron job, queue, or always-on server to maintain — Anthropic fires the sessions for you.
How is a Managed Agent different from a session?
The agent is a persisted, versioned configuration you create once — model, system prompt, and tools live on it. A session is a single run that references the agent by ID. The rule is: create the agent once, start a session (or a scheduled deployment) every run. Never put model or tools on the session.
Where does the agent's output go?
The agent writes files to /mnt/session/outputs/ inside its container. After the run, you list and download them through the Files API scoped to the session ID. That gives you a clean bridge for reports, spreadsheets, or any artifact the agent produces.
Can I test a scheduled deployment before it fires on schedule?
Yes. Trigger a manual run with the deployment run endpoint — it creates a session immediately, and it works even while the deployment is paused. This is the easiest way to smoke-test the whole pipeline without waiting for the next cron occurrence.
What happens if a scheduled run fails?
Every trigger attempt writes a deployment run record. A failed run carries an error type — for example a missing environment or vault. Non-recoverable failures, like an archived agent, automatically pause the deployment; rate limits are recorded but simply retry at the next occurrence.
When should I use Managed Agents instead of a cron job that calls the API?
Use a plain scheduled Route Handler when you need a single model call. Reach for Managed Agents when the task needs a real agent loop with sandboxed tools — running commands, reading and writing files, browsing docs — and you would rather not host and secure that execution environment yourself.
More Articles
Need help with this?
I'm available for Sharetribe Flex, Shopify, Next.js, and AI integration projects.
Get In Touch