A routine keeps working when your laptop is closed. That is the entire pitch, and it is also the entire risk.
Anthropic's documentation is good at telling you how to create one. It is much quieter about what happens when a routine quietly does nothing useful for a week, or burns a chunk of your usage on a task that never had a chance of succeeding. I found that out the hard way, and the receipts from that run are the reason this guide exists.
Here's what a routine actually is, how each trigger type behaves, what the real limits are, and the four ways I've seen this break.
Key Takeaways
- A routine is a saved Claude Code configuration (a prompt, one or more repositories, and connectors) that runs unattended on Anthropic-managed cloud infrastructure.
- Routines launched April 14, 2026 and are still in research preview, so behavior, limits, and the API surface can change (Anthropic, 2026).
- Daily run caps: 5 on Pro, 15 on Max, 25 on Team and Enterprise. One-off runs are exempt from the cap but still draw down normal subscription usage.
- The minimum schedule interval is one hour. Sub-hour cron expressions get rejected. Desktop tasks and
/loopgo down to one minute. - A green run status does not mean the task worked. It means the session started and exited without an infrastructure error. You have to open the transcript.
What is a Claude routine?
A routine is a saved Claude Code configuration, packaged once and run automatically: a prompt, one or more repositories, and a set of connectors. Routines execute on Anthropic-managed cloud infrastructure, or on your organization's self-hosted environment when routed there, so they keep working when your machine is off (Claude Code documentation, 2026).
That last part is the real difference from anything you could already do. A cron job on your laptop stops when the lid closes. A routine doesn't.
They shipped on April 14, 2026, and they're available on Pro, Max, Team, and Enterprise plans with Claude Code on the web enabled. You manage them at claude.ai/code/routines, in the Desktop app, or from the CLI with /schedule. All three write to the same cloud account, so a routine you make in one shows up in the others straight away.
Two properties are worth internalising before you build anything.
Routines belong to you, not your team. They aren't shared, and they count against your account's allowance. Anything a routine does through your connected GitHub identity or your connectors appears as you. Commits and pull requests carry your GitHub user. Slack messages and Linear tickets use your linked accounts.
They run with no permission prompts at all. There is no permission-mode picker for a cloud routine. The session can run shell commands, use skills committed to the cloned repository, and call any connector you include. What it can reach is decided entirely by three things you pick at creation time: the repositories, the environment's network access, and the connectors. Scope each of those to what the task actually needs, because nothing will stop it mid-run to ask.
Team and Enterprise Owners can turn routines off for everyone with a toggle in admin settings. When that happens, existing routines stop running and nobody can create new ones.
It's still a research preview. Treat every number in this guide as dated.
What are the three trigger types?
A routine starts when one of its triggers matches, and there are three kinds: schedule, API, and GitHub. A single routine can carry any combination of them (Claude Code documentation, 2026). A PR review routine can run nightly, fire from a deploy script, and react to every new pull request, all from one saved config.
Schedule triggers run on a recurring cadence. The form gives you hourly, daily, weekdays, and weekly presets, with times entered in your local zone and converted automatically. Your 9am routine runs at 9am your time no matter where the infrastructure sits.
Runs may start a few minutes late because of stagger, and the offset is consistent for each routine. So if your task is timing-sensitive, build the slack into the prompt rather than assuming a precise start.
For anything the presets don't cover, like every two hours or the first of each month, you pick the nearest preset in the form and then run /schedule update in the CLI to set a real cron expression. The floor is one hour. Expressions that run more often are rejected outright.
A schedule can also be a single one-off run at a future timestamp. After it fires, the routine auto-disables and the UI marks it as Ran. This is genuinely useful for the "open a cleanup PR after the rollout finishes" category of work.
API triggers give the routine a dedicated HTTP endpoint. You POST to it with a bearer token and get back a session URL. That's the section below.
GitHub triggers react to repository events. Two categories are supported: pull request events (opened, closed, assigned, labeled, synchronized) and release events (created, published, edited, deleted). Each matching event starts its own session, and there's no session reuse, so two PR updates produce two independent runs.
One trap in the PR filters is worth flagging. Filters pair a field with an operator, and the matches regex operator tests the entire field value, not a substring inside it. If you write hotfix expecting to catch every title containing that word, you'll match only a PR titled exactly hotfix and nothing else. You need .*hotfix.*, or just use the contains operator instead.
During the preview, GitHub webhook events are also subject to per-routine and per-account hourly caps, and events beyond the limit get dropped until the window resets.
How do you create a routine?
You can create a routine from the web, the Desktop app, or the CLI, and the creation form asks for the same five things every time: the prompt, the repositories, the environment, the connectors, and the triggers.
From the CLI, /schedule runs the whole thing conversationally. It also takes a description directly, so /schedule daily PR review at 9am works, as does a one-off like /schedule in 2 weeks, open a cleanup PR that removes the feature flag. The command has an alias, /routines, and it supports /schedule list, /schedule update, and /schedule run for managing what you already have.
One limitation to plan around: /schedule creates scheduled routines only. To add an API or GitHub trigger, you edit the routine on the web.
The prompt matters more here than in an interactive session, because there's nobody to clarify anything. It has to be self-contained and explicit about what success looks like. The prompt input includes a model selector, and Claude uses that model on every single run.
Repositories are cloned fresh at the start of each run, starting from the default branch. Claude pushes its work to claude/-prefixed branches, which are always accepted. If your prompt directs it somewhere else, the push gets checked first and rejected if the branch is protected, if someone else has an open PR from it, or if it carries commits authored by anyone other than you.
The environment controls network access, environment variables, and a cached setup script. The Default environment uses Trusted network access, which permits only an allowlist of package registries, cloud provider APIs, and common development domains. Requests to anything outside that list fail with a 403 and an x-deny-reason: host_not_allowed header. Connector traffic routes through Anthropic's servers instead, so connectors work without touching the allowlist.
Then there's the setting I'd change first, every time.
Watch this one: when you create a routine, every connector on your account is included by default, and Claude can use every tool from an included connector, including writes, without asking permission during the run. Remove the ones the task doesn't need before you save.
That default is convenient and expensive. Every attached connector's full tool catalog rides along on every model call of every run, whether the task touches that connector or not. If you're setting connectors up in the first place, I've covered that separately: how to configure MCP servers and connectors for Claude Code.
Cloud routines vs Desktop tasks vs /loop
Claude Code has three scheduling surfaces, and picking the wrong one is the most common way people end up with automation that silently never runs. The deciding question is simple: does the work need your machine, and how often does it need to happen?
| Cloud routine | Desktop task | /loop |
|
|---|---|---|---|
| Runs on | Anthropic cloud | Your machine | Your machine |
| Machine must be on | No | Yes | Yes |
| Open session required | No | No | Yes |
| Local file access | No (fresh clone) | Yes | Yes |
| Permission prompts | None, runs autonomously | Configurable per task | Inherits from session |
| Minimum interval | 1 hour | 1 minute | 1 minute |
Source: Claude Code documentation, 2026.
The row I'd stare at longest is the permissions one, because the asymmetry there causes real failures.
Cloud routines never prompt. Desktop tasks do, and each task has its own permission mode. If a Desktop task runs in Manual mode and hits a tool it lacks permission for, the run stalls until you approve it. Unattended, at 3am, that means it just sits there. Worse, connector tools your organization has set to ask, and MCP tools marked requiresUserInteraction, prompt on every single call and offer no always-allow option at all. Those runs stall every time, forever.
This is not hypothetical. It's exactly what wrecked a scheduled task of mine: two approval-gated tools in a run that by definition had nobody sitting there to approve them. The task was dead on arrival every single morning from the day I created it, and the system let me set it up that way without a word of warning. The full chain is here: what an unattended Claude run looks like when it goes wrong, with receipts.
Desktop tasks have one more behavior worth knowing. If your computer sleeps through a scheduled time, the run is skipped. On wake, Desktop checks the last seven days and starts exactly one catch-up run for the most recently missed slot, discarding everything older. A daily task that missed six days runs once. That means a task scheduled for 9am might actually execute at 11pm, so if timing matters, say so in the prompt itself.
How do you trigger a routine over HTTP?
An API trigger gives the routine a dedicated endpoint. You POST to it with the routine's bearer token, and it returns a session ID and URL immediately without waiting for the run to finish (Claude Platform documentation, 2026).
curl -X POST https://api.anthropic.com/v1/claude_code/routines/$ROUTINE_ID/fire \
-H "Authorization: Bearer $ROUTINE_TOKEN" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: experimental-cc-routine-2026-04-01" \
-H "Content-Type: application/json" \
-d '{"text": "Sentry alert SEN-4521 fired in prod. Stack trace attached."}'
A few details that will cost you time if you miss them.
The path parameter is called routine_id, but the value is prefixed trig_, not routine_. The beta header is mandatory and requests without it return a 400. The text field is optional, freeform, capped at 65,536 characters, and is not parsed, so if you send JSON it arrives as a literal string.
Tokens are per-routine, shown exactly once, and grant no read access. Generating a new one revokes the previous one, and there's no public API for token management. A leaked token can trigger that one routine and nothing else, which is a sane blast radius.
There's no idempotency key. If your webhook caller retries, you get multiple sessions. Build the dedupe on your side.
| Status | Error type | Cause |
|---|---|---|
| 400 | invalid_request_error |
Missing beta header, text over 65,536 chars, or the routine is paused |
| 401 | authentication_error |
No bearer token, or the token doesn't match this routine |
| 403 | permission_error |
Account or org lacks access to the endpoint |
| 404 | not_found_error |
The routine doesn't exist |
| 429 | rate_limit_error |
Daily run cap or usage limit reached, includes Retry-After
|
| 500 | api_error |
Server error, retry with backoff |
| 503 | overloaded_error |
Temporarily overloaded (the Platform API returns 529 here, this endpoint returns 503) |
Now the part that catches almost everyone building alert triage.
The fire payload is untrusted by default. The
textyou POST does not reach the routine as a plain instruction. It arrives wrapped in a<routine-fire-payload>block that labels it as untrusted data and tells Claude not to follow instructions inside it unless the routine's own prompt says to.
So a routine whose saved prompt never mentions that block will treat your alert body as inert context and appear to ignore it completely. You have to opt in explicitly, with something like "Investigate the alert described in the routine-fire-payload block." The same wrapping applies to text you supply with Run now in the web UI.
That design is deliberate and correct: anyone holding the bearer token can send text, so a leaked token produces labelled data rather than direct instructions to your agent. But it's a silent no-op if you don't know about it.
If you're wiring GitHub into this, the repository side is covered here: connecting GitHub to Claude, setup and real limits.
What do routines actually cost?
Routines draw down your normal subscription usage exactly like interactive sessions do, and on top of that they carry a separate per-account daily run cap. Pro allows 5 routine runs per day, Max allows 15, and Team and Enterprise allow 25 (Anthropic, 2026).
One-off runs are the exception: they're exempt from the daily cap, though they still consume your regular subscription usage like any other session.
When you hit either ceiling, what happens depends on your billing setup. Organizations with usage credits turned on keep running on metered overage. Without them, further runs are rejected until the window resets. On Team and Enterprise, an admin enables that for the whole organization.
The number that actually matters isn't the cap, though. It's what a single bad run can consume.
One of my scheduled runs got stuck in a retry loop and consumed roughly 16 points of a weekly window while producing nothing at all. That same evening, after a full afternoon of genuine work, the bar had moved only two more points. The cost sits in the model calls, not in whether the task achieved anything, which is a very different mental model from paying for results.
For how the plans and their limits actually work, that's its own subject: how Claude Code's Pro, Max, and API pricing and limits really compare.
The four ways routines fail
Every guide I read while researching this covers setup. None of them cover what happens next. These are the four failure modes worth knowing before you schedule anything.
1. A green status does not mean it worked
This is the one I'd put on a poster. Anthropic's own docs state it plainly: a green status in the run list means the session started and exited without an infrastructure error, and it does not mean the task in your prompt succeeded (Claude Code documentation, 2026).
Blocked network requests, missing connector tools, and task-level failures all surface inside the transcript rather than in the status indicator. So a routine can show a tidy column of green dots for a fortnight while doing absolutely nothing. Open the runs. Read them.
2. /schedule returns "Unknown command"
The CLI hides /schedule when any of its requirements aren't met, and the error message doesn't tell you which one. There are four causes.
You're authenticated the wrong way. Routines need a claude.ai subscription login. A Console API key or a cloud provider login (Bedrock, Google Cloud's Agent Platform, Microsoft Foundry) won't work. Critically, if ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN is set in your shell, or apiKeyHelper is set in settings.json, those take precedence over your claude.ai login. Remove them first.
You've disabled feature-flag fetching. This is the one nobody writes about. If DISABLE_TELEMETRY, DO_NOT_TRACK, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC, or DISABLE_GROWTHBOOK is set, in your shell or in the env block of a settings.json, /schedule disappears. It depends on feature-flag fetching, and those variables switch it off. Privacy-minded developers hit this constantly and have no way to guess the cause.
You're inside a Claude Code on the web session. Use the web UI instead.
Your organization turned routines off. An Owner can disable them globally, and it's server-side, so no local config overrides it.
In every one of these cases you can still create and manage routines at claude.ai/code/routines.
3. The approval-gate trap
Covered above, but it deserves its own line because it's a guaranteed rather than occasional failure. An approval-gated tool inside an unattended run is not a risk, it's a certainty, because there is never anyone present to clear the gate.
The fix is either to set the specific tools the task needs to always-allow, or to run the work as a cloud routine, where nothing prompts at all. What you must not do is assume you'll notice. You won't, because of failure mode #1.
4. Version-gated behavior you'll mistake for your own bug
Routines are moving fast, and two behavior changes are documented against specific versions.
Before v2.1.214, a fired routine received its own saved prompt framed as an untrusted background notification, and the session could refuse to act on it. Before v2.1.211, the CLI reported a next run time in the year 1 for routines with no schedule trigger. If you're on an older build and seeing either, that's the build, not you.
When the UI and my memory disagreed about what a task was configured to do, the thing that settled it was querying the routines API directly. /v1/code/triggers returns the stored configuration rather than what you think you set up: the model, the cron expression, last_fired_at, the attached connectors, and the full stored prompt. It also returns persist_session: false, which matters more than it sounds. Each run starts a fresh session, so a runaway transcript is built entirely inside one run. That isn't drift accumulating over days. One run does it.
If you're chasing an error string rather than a behavior, I keep the wider catalogue separately: every Claude Code error code, cause, and fix.
How do you write a prompt that survives unattended?
Unattended prompts fail differently from interactive ones, so they need different construction. The routine's prompt is the most important part of the configuration, because the session runs autonomously and there's no follow-up question coming.
Five habits, in the order I'd apply them.
Be explicit about what done looks like. Interactive prompts get away with vagueness because you course-correct in the next message. A routine gets one shot. State the output, the destination, and what to do when there's nothing to report.
Opt in to the fire payload if you use one. Reference the <routine-fire-payload> block by name, or the text you POST is inert.
Put unattended work on a cheap model. Mine was on the most expensive model available purely because I'd built it interactively and never revisited the setting. A routine that assembles a summary from three searches does not need that. Sonnet is the right default here, and the tradeoff of when a pricier model genuinely earns its cost is worth reading on its own: when the more expensive model actually earns its keep.
Trim the connectors. Every attached connector's tool catalog is sent on every call of every run. Removing the ones you don't use is free and immediate.
Add time guardrails when timing matters. Especially for Desktop tasks, where a catch-up run can fire fourteen hours late. Something as simple as "if it's after 5pm, skip the review and post a summary of what was missed" prevents a lot of nonsense.
One more thing that isn't a prompt habit but belongs here: routines inherit the project instructions in the cloned repository, so whatever discipline you've built there applies to unattended runs too. That's covered in writing CLAUDE.md files that actually change Claude's behavior.
And then check the run history after the first few runs. There is no usage alert, no anomaly detection, and no per-conversation ledger anywhere in the product right now. If you don't look, nothing will tell you.
Frequently Asked Questions
What are Claude Code routines?
A routine is a saved Claude Code configuration, made up of a prompt, one or more repositories, and connectors, that runs unattended on Anthropic-managed cloud infrastructure. Triggers can be scheduled, HTTP API calls, or GitHub events. They're available on Pro, Max, Team, and Enterprise plans with Claude Code on the web enabled.
How many routines can I run per day?
Pro allows 5 routine runs per day, Max allows 15, and Team and Enterprise allow 25 (Anthropic, 2026). One-off runs are exempt from that cap but still draw down normal subscription usage. Hitting the cap rejects further runs unless usage credits are enabled, which continues on metered overage.
Do routines run when my computer is off?
Yes, cloud routines do. They execute on Anthropic-managed infrastructure, so a closed laptop makes no difference. Desktop scheduled tasks and /loop both run on your machine and need it awake. If a Desktop task sleeps through its slot, it's skipped, with one catch-up run on wake for the most recent miss inside seven days.
Why does /schedule say "Unknown command"?
Four causes: you're authenticated with a Console API key or cloud provider instead of a claude.ai login, you have DISABLE_TELEMETRY, DO_NOT_TRACK, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC, or DISABLE_GROWTHBOOK set (these disable the feature-flag fetching /schedule depends on), you're inside a Claude Code on the web session, or an Owner disabled routines for your organization.
Can a routine run more often than once an hour?
No. The minimum interval for a cloud routine is one hour, and cron expressions that run more frequently are rejected (Claude Code documentation, 2026). If you need finer granularity, Desktop scheduled tasks and /loop both go down to one minute, at the cost of needing your machine awake.
The takeaway
Routines are the right tool for a specific shape of work: unattended, repeatable, tied to a clear outcome, and tolerant of running at most once an hour. Backlog grooming, nightly PR review, docs drift, deploy verification. For those, having the work happen without your machine is a genuine change in what's possible.
What I'd carry away is narrower than the feature list. Every failure I've hit lived in the layer around the model rather than in the model itself: an approval gate that could never clear, a status dot that reported infrastructure rather than outcome, a command that vanished because of an unrelated privacy setting. The model did its job in all three cases.
So build the routine, then go and read its first three runs properly. That's the whole discipline. Before you schedule anything on an expensive model with a pile of connectors attached, it's worth seeing how badly this can go: an 8.5-hour unattended run and what it cost.




Top comments (0)