Routines: Scheduled, API and GitHub-Triggered Claude Code Runs
A Claude Code routine is a saved configuration — a prompt, one or more repositories, and a set of connectors — that runs as a full cloud session on a schedule, on an authenticated HTTP POST, or on a GitHub event. Routines execute on Anthropic-managed infrastructure with no permission-mode picker and no approval prompts during a run (research preview, checked 2026-08-28 against code.claude.com/docs/en/routines).
Research preview Pro, Max, Team, Enterprise — with Claude Code on the web enabledYour on-call morning starts the same way every time: open the tracker, skim what arrived overnight, guess which alerts are the same alert, and only then start reading code. The session you left running died when the lid closed, /loop expired after seven days, and the workflow you wrote to cover the gap is 180 lines of YAML nobody wants to touch.
What an unattended routine actually buys you
Section titled “What an unattended routine actually buys you”- A nightly triage run that reads yesterday’s issues through a connector and posts a groomed queue before you open your laptop
- A
/fireHTTP endpoint your alerting tool can POST to, so a paging event opens a draft PR instead of a blank terminal - A
pull_request.openedtrigger applying your own review checklist, filtered so it never wakes on drafts - An auditable push model: work lands on
claude/-prefixed branches, and pushes elsewhere are rejected under three named conditions - A rule for when to reach for a routine instead of
/loop,/goal, a subagent, or the Agent SDK
What happens the moment a routine fires
Section titled “What happens the moment a routine fires”Every trigger produces the same thing: a new cloud session, from scratch. “Routines run autonomously as full Claude Code cloud sessions: there is no permission-mode picker and no approval prompts during a run.” Nobody is at the keyboard to approve a Bash call, so three configuration decisions carry the weight the approval layer used to.
-
Each repository is cloned fresh, from its default branch. Nothing carries over from the last run or from your laptop. Skills committed to the repository are available; MCP servers added locally with
claude mcp addare not, because those live on your machine rather than your claude.ai account. Add one as a connector on claude.ai, or declare it in a committed.mcp.json. -
Every connected connector is included by default, and the docs are explicit about what that means: “Claude can use every tool from an included connector, including writes, without asking for permission during a run.” Removing the ones a routine does not need is the highest-leverage narrowing available.
-
The run happens as you. Routines belong to your individual claude.ai account, are not shared with teammates, and count against your daily run allowance. Commits and pull requests carry your GitHub user; Slack messages and Linear tickets use your linked accounts.
One recent change is worth knowing. The session now receives the saved prompt “as its assigned task and carries it out, rather than treating it as untrusted content that arrived mid-conversation.” Before Claude Code v2.1.213 it arrived framed as an untrusted background notification and could be refused — which is why routines silently no-op on older builds.
Which routine trigger fits which job?
Section titled “Which routine trigger fits which job?”A routine can carry any combination of the three types, added and removed from the same Select a trigger section of the edit form.
| Trigger | Starts a run when | Best for |
|---|---|---|
| Scheduled | A recurring cadence comes due, or a one-off timestamp passes | Triage, digests, drift checks, cleanups you want to forget about |
| API | An authenticated POST hits the routine’s own endpoint | Alerts, deploy verification, anything your systems already know about |
| GitHub | A pull request or release event matches your filters | Review, backports, cross-repo ports, changelog generation |
Create one at claude.ai/code/routines, in the Desktop app’s Code tab under Routines, or from the CLI with /schedule (aliased as /routines) — all three write to the same cloud account. In Desktop, choosing Local instead of Cloud gives you a Desktop scheduled task, which runs on your machine instead.
Scheduled triggers, the one-hour floor, and one-off runs
Section titled “Scheduled triggers, the one-hour floor, and one-off runs”Presets are hourly, daily, weekdays, and weekly. Times are entered in your local zone and converted, so the routine runs at that wall-clock time wherever the infrastructure sits. Runs may start a few minutes late because of stagger, and “the offset is consistent for each routine” — do not rely on it being random.
For anything the presets miss, pick the closest one, then run /schedule update in the CLI to set a cron expression. The minimum interval is one hour; expressions that run more frequently are rejected (checked 2026-08-28). For minute-level cadence you want /loop or a Desktop scheduled task.
One-off schedules are the underrated half of this trigger. They fire once at a timestamp, auto-disable, get marked Ran, and do not count against the daily routine run cap.
/schedule in 2 weeks, open a cleanup PR that removes the feature flagThe API trigger: a bearer token, a /fire endpoint, and an untrusted payload
Section titled “The API trigger: a bearer token, a /fire endpoint, and an untrusted payload”An API trigger gives the routine a dedicated HTTP endpoint; POSTing to it with the routine’s bearer token starts a new session and returns a session URL. Add it from the web: “API triggers are added to an existing routine from the web. The CLI cannot currently create or revoke tokens” (checked 2026-08-28). The token is shown once, so it goes straight into your alerting tool’s secret store.
curl -X POST https://api.anthropic.com/v1/claude_code/routines/trig_01ABCDEFGHJKLMNOPQRSTUVW/fire \ -H "Authorization: Bearer sk-ant-oat01-xxxxx" \ -H "anthropic-beta: experimental-cc-routine-2026-04-01" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{"text": "Sentry alert SEN-4521 fired in prod. Stack trace attached."}'The text field is the part people get wrong. It is freeform and unparsed — send JSON and the routine receives a literal string — and it does not reach Claude as a bare message. 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.” The same wrapping applies to text supplied with Run now.
GitHub triggers: two event categories and eight filter fields
Section titled “GitHub triggers: two event categories and eight filter fields”GitHub triggers need the Claude GitHub App installed on the repository, whichever surface you configure them from. /web-setup grants repository access for cloning but does not install the app and does not enable webhook delivery — worth reading twice, because the routine looks correctly configured and never fires. From the CLI, install the app first, then attach the trigger conversationally (v2.1.225 or later).
/schedule add a GitHub trigger to my nightly review for pull requests opened in acme/webappTwo event categories are supported: Pull request (opened, closed, assigned, labeled, synchronized, or otherwise updated) and Release (created, published, edited, or deleted). Within each you pick a specific action such as pull_request.opened, or react to every action. Sessions are never reused — two PR updates produce two independent sessions.
Filters keep that from becoming a cost problem. All conditions must match, and the fields are Author, Title, Body, Base branch, Head branch, Labels, Is draft, and Is merged, each paired with an operator: equals, contains, starts with, is one of, is not one of, or matches regex.
Where does a routine push its work?
Section titled “Where does a routine push its work?”Work is pushed to branches prefixed with claude/, “which are always accepted”. When your prompt directs Claude at another branch, Claude Code checks the push first and rejects it if any of the following holds:
- The branch is protected on GitHub
- Someone else has an open pull request from that branch
- The branch carries commits authored by someone other than you
The third condition is the interesting one: a routine cannot quietly append to a colleague’s in-flight branch even if your prompt tells it to. Treat the prefix as the contract — everything a routine produces is a branch you can diff, delete, or turn into a PR, and nothing lands where a human is already standing.
How much can a routine reach during a run?
Section titled “How much can a routine reach during a run?”Two dials, and they are independent.
The environment controls network access, environment variables, and the setup script. The Default environment uses Trusted network access, allowing only the default allowlist of package registries, cloud provider APIs, container registries, and common development domains; requests outside it fail with 403 and x-deny-reason: host_not_allowed, which surfaces in the run transcript rather than as a routine-level error. Switch Network access to Custom and list your domains, or to Full. Environment variables are “visible to anyone who uses the environment”, so a shared environment is the wrong home for a credential.
The connectors control which external services the run can touch. Connector traffic routes through Anthropic’s servers rather than the session’s network path, so connectors work without touching Allowed domains — and, conversely, tightening the allowlist restrains a connector not at all. Only removing it does.
Why a routine should propose, not perform
Section titled “Why a routine should propose, not perform”The most reliable routines share one shape: they end by producing an artifact a human approves, not by completing an irreversible action. A draft PR. A filed, labelled ticket. A summary in a channel. Anthropic’s own alert-triage example takes that shape deliberately — the routine “opens a draft pull request with a proposed fix and a link back to the alert. On-call reviews the PR instead of starting from a blank terminal.”
Underneath the ergonomics sits a governance argument. Linear, describing its Agent Interaction SDK (Leela Senthil Nathan, 2025-08-01), puts it plainly: “an agent cannot be held accountable”, so “issues can only be assigned to humans, and only delegated to agents”. A routine that opens a PR keeps a human assignee on the outcome; one that force-pushes to main does not.
Since v2.1.227 the CLI can read a run’s log and explain it:
/schedule why did my nightly review do nothing this morning?Routines, /loop, /goal, subagents or the Agent SDK?
Section titled “Routines, /loop, /goal, subagents or the Agent SDK?”Five things in Claude Code keep work moving without you typing, and they are not interchangeable. The scheduling half of the comparison is documented directly:
| Routines (cloud) | Desktop scheduled tasks | /loop | |
|---|---|---|---|
| Runs on | Cloud, Anthropic-managed by default | Your machine | Your machine |
| Requires open session | No | No | Yes |
| Access to local files | No (fresh clone) | Yes | Yes |
| Permission prompts | No (runs autonomously) | Configurable per task | Inherits from session |
| Minimum interval | 1 hour | 1 minute | 1 minute |
/loop is session-scoped: tasks live in the current conversation, a session holds at most 50, and recurring tasks expire seven days after creation — exactly why it is wrong for anything that must still be running next month. The other three differ in shape rather than schedule:
/goalis a stop condition, not a cadence. Claude keeps working until a small fast model judges the condition met or impossible; up to 4,000 characters, one goal per session. Use it inside a routine’s prompt when the job is “keep going until X holds” — it works non-interactively:claude -p "/goal CHANGELOG.md has an entry for every PR merged this week".- Subagents are fan-out within a run. By default “spawning more than 20 concurrent subagents fails with
Concurrent subagent limit reached”, raisable withCLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS. A routine auditing 400 files should delegate inside the run, not become 400 routines. - The Agent SDK is the escape hatch when the trigger is none of the three. It “gives you the same tools, agent loop, and context management that power Claude Code, programmable in Python and TypeScript” — only those two languages; from anywhere else, run the CLI as a subprocess with
-pand--output-format json.
The heuristic: a clock, an HTTP call, or a GitHub event, plus no need for local files, means a routine. Otherwise one of the other four fits better.
What does a routine cost, and which levers move it?
Section titled “What does a routine cost, and which levers move it?”Routines “draw down subscription usage the same way interactive sessions do”, plus a daily cap on how many runs can start per account. Hit either and organisations with usage credits continue on metered overage; without credits, further runs are rejected until the window resets. During the research preview, GitHub webhook events also carry per-routine and per-account hourly caps, and events beyond them are dropped — silently, from the routine’s point of view.
Four levers, in the order they usually pay off:
- Filter the trigger harder. A
pull_requesttrigger with no filters wakes on every draft push; Is draftfalsealone can halve the run count. - Pick the model per routine. The prompt input includes a model selector, used on every run — a labelling routine does not need what a migration routine needs.
- Remove connectors and let the environment cache work. Fewer connectors mean fewer tool definitions in context on every run, and a cached setup script means you are not reinstalling the world nightly.
- Prefer one-off schedules for one-off work. They do not count against the daily cap.
What breaks in an unattended routine run
Section titled “What breaks in an unattended routine run”/schedule returns “Unknown command”, or never appears. The CLI hides it when a requirement is unmet: you are authenticated with a Console API key, an Anthropic profile or federation credential, or a cloud provider such as Amazon Bedrock, Google Cloud’s Agent Platform, or Microsoft Foundry — /schedule needs a claude.ai subscription login, and ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN in your shell takes precedence over one. Or DISABLE_TELEMETRY, DO_NOT_TRACK, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC, or DISABLE_GROWTHBOOK is set, disabling the feature-flag fetching /schedule depends on. Or you are inside a Claude Code on the web session. claude.ai/code/routines works either way.
“Routines are disabled by your organization’s policy.” An Owner turned off the Routines toggle in Team or Enterprise admin settings. It is server-side; no local configuration overrides it. On v2.1.227 or later it also hides /schedule.
The GitHub trigger never fires. Almost always the Claude GitHub App is not installed on that repository, and /web-setup does not install it.
The routine ran and ignored your alert. The saved prompt never referenced the fire payload, so the wrapped text stayed inert. Name the routine-fire-payload block explicitly.
A network call failed unexpectedly. Look for 403 with x-deny-reason: host_not_allowed: the Trusted allowlist covers package registries, not your staging API.
A run pushed nothing despite saying it would. A prompt targeting a branch other than a claude/ one hits one of the three rejection conditions.
Nobody noticed for a week. A routine that emits nothing into a place a human already looks is unmonitored, whatever the run log says.
How Cursor and Codex do the same thing
Section titled “How Cursor and Codex do the same thing”Cursor’s equivalent is Automations, which “run cloud agents in the background, either on a schedule or in response to events from GitHub, GitLab, Slack, webhooks, Linear, and more” — a broader trigger surface than routines’ three, including Sentry and PagerDuty, with outbound statusChange webhooks and a REST API. Codex’s equivalent is scheduled tasks, which “schedule recurring tasks to run in the background” and fire on Gmail, Slack, and GitHub pull-request activity — with the documented constraint that “one task can use multiple event triggers, but it can’t combine event triggers with a time-based schedule”, which is precisely the combination a routine does allow. All checked 2026-08-28. Our walkthroughs: Cursor cloud agents and automations and Codex automations.