Routines: Scheduled, API and GitHub-Triggered Claude Code Runs
A Claude Code routine is a saved configuration of a prompt, repositories, and connectors that runs as a full cloud session on a schedule, an authenticated HTTP POST, or a GitHub event. Routines run without approval prompts, so the prompt, repositories, network access, connectors, and the claude/ branch rule are the guardrails.
This page is for developers and tech leads who want recurring agent work to keep running after the laptop closes, on Anthropic-managed cloud infrastructure or your organization’s self-hosted environment. Your on-call morning starts the same way every time: open the tracker, skim what arrived overnight, and guess which alerts are the same alert. The session you left running died with the lid, /loop expired after seven days, and the workflow that covers the gap is 180 lines of YAML nobody wants to touch. A routine replaces that with a groomed queue before you open the laptop, a /fire endpoint that turns a page into a draft pull request, and a pull-request review against your own checklist, all pushed to claude/ branches you can audit.
Facts on this page were checked on 2026-09-26 against the routines documentation and Claude Code v2.1.283. Every version gate named here is v2.1.227 or older, so it holds on both the latest (v2.1.283) and stable (v2.1.274) channels.
What happens when a routine fires?
Section titled “What happens when a routine fires?”Every trigger produces a new cloud session with no permission-mode picker. It runs shell commands, uses skills committed to the repository, and calls every included connector without stopping for approval, apart from some artifact publishing actions. Three configuration decisions carry the weight the approval layer normally does.
-
Each repository is cloned fresh from its default branch. Nothing carries over from the last run or your laptop, including MCP servers you added locally with
claude mcp add. Add one as a connector at claude.ai, or, for a routine with a single repository, declare it in a committed.mcp.json. -
Every connected connector is included by default. “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 your highest-leverage narrowing.
-
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, and Slack messages or Linear tickets use your linked accounts.
The session treats the saved prompt as its assigned task (before v2.1.213 it could refuse it). Nothing can approve anything mid-run, so the prompt has to say everything up front.
How do you create your first routine?
Section titled “How do you create your first routine?”You need a Pro, Max, Team, or Enterprise plan with GitHub connected. The CLI’s /schedule also needs a claude.ai subscription login rather than a Console API key; Claude Code authentication shows how to check the active credential with /status. The web, the Desktop app’s Code tab (choose Cloud), and the CLI’s /schedule (alias /routines) all write to the same account.
- Open claude.ai/code/routines and click New routine. Name it after its outcome, such as
weeknight-issue-triage. - Paste a self-contained prompt, such as the triage prompt below, and pick the model in the prompt’s model selector.
- Add only the repositories the job reads. Each is cloned fresh on every run.
- Keep the Default environment unless the run must reach a host outside the package-registry allowlist.
- Under Select a trigger, choose the weekdays preset at a time a few minutes past the hour.
- Under Connectors, remove every connector the prompt does not name.
- Click Create, then Run now, and open the run’s session to confirm it produced the artifact the prompt asks for.
From the CLI, /schedule weekdays at 7:07, triage new issues starts the same form conversationally: Claude asks about the schedule, repositories, and prompt before saving. /schedule list, /schedule update, and /schedule run manage existing routines. The CLI creates schedule triggers and, from v2.1.225, attaches GitHub triggers; API triggers are web-only.
Which routine trigger fits which job?
Section titled “Which routine trigger fits which job?”A routine can carry any combination of the three trigger types, added and removed under Select a trigger in its edit form.
| Trigger | Starts a run when | Best for |
|---|---|---|
| Scheduled | A recurring cadence comes due, or a one-off timestamp passes | Triage, digests, documentation drift, cleanups |
| API | An authenticated POST reaches the routine’s own endpoint | Alerts, deploy verification, internal tools |
| GitHub | A pull request or release event matches your filters | Review, backports, cross-repository ports, changelogs |
Scheduled triggers: presets, cron, and one-off runs
Section titled “Scheduled triggers: presets, cron, and one-off runs”Presets are hourly, daily, weekdays, and weekly, in your local time zone. A run scheduled exactly on the hour can start several minutes late, so pick 9:07 rather than 9:00 when timing matters.
For any other cadence, set a cron expression with /schedule update in the CLI. The minimum interval is one hour; for minute-level cadence, use /loop or a Desktop scheduled task.
One-off schedules fire once, then auto-disable. They do not count against the daily run cap, so use them for “after the rollout” work:
/schedule in 2 weeks, open a cleanup PR that removes the feature flagAPI triggers: a bearer token, a /fire endpoint, and an untrusted payload
Section titled “API triggers: a bearer token, a /fire endpoint, and an untrusted payload”An API trigger gives the routine its own HTTP endpoint; a POST with the routine’s bearer token starts a new session. You add the trigger on the web: “The CLI cannot currently create or revoke tokens” (checked 2026-09-26). The token is shown once, is scoped to that one routine, and can be regenerated or revoked from the same modal. Store it in your alerting tool’s secret store, never in a script.
To fire it from a shell or CI step, build the body with jq so quotes and newlines in the alert cannot break the JSON:
# ROUTINE_FIRE_URL and ROUTINE_TOKEN come from your secret storejq -n --arg text "$ALERT_BODY" '{text: $text}' | curl -sS -X POST "$ROUTINE_FIRE_URL" \ -H "Authorization: Bearer $ROUTINE_TOKEN" \ -H "anthropic-beta: experimental-cc-routine-2026-04-01" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ --data-binary @-ROUTINE_FIRE_URL is the https://api.anthropic.com/v1/claude_code/routines/<trigger-id>/fire URL the modal shows once the routine is saved; ALERT_BODY is whatever your alerting tool sends. A wrong URL or token returns 401. A successful call returns JSON with claude_code_session_id and claude_code_session_url: have the caller log the URL next to the alert, so whoever reviews the result can open the exact run. The beta header is dated; breaking changes ship behind new header versions.
The text field is where most routines go wrong. It is freeform and unparsed, so JSON arrives as a literal string. It also “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.” Text typed into Run now is wrapped the same way.
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 clone access but installs neither the app nor webhook delivery. From the CLI (v2.1.225 or later), install the app from its GitHub page first, then attach the trigger conversationally:
/schedule add a GitHub trigger to my nightly review for pull requests opened in acme/webappTwo event categories exist: Pull request (opened, closed, assigned, labeled, synchronized, or otherwise updated) and Release (created, published, edited, or deleted). You pick one action, such as pull_request.opened, or every action in the category. Each event gets its own session, so an unfiltered trigger gets expensive fast. Filters narrow it, and all conditions must match. The fields are Author, Title, Body, Base branch, Head branch, Labels, Is draft, and Is merged, and each pairs 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?”Claude pushes to branches prefixed with claude/, which are always accepted. When your prompt points Claude at another branch, Claude Code checks the push first and rejects it if any of these 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.
So a routine cannot append to a colleague’s in-flight branch. Treat the prefix as the contract: everything a routine produces is a branch you can diff, delete, or open as a pull request, with your protected default branch as the backstop.
How much can a routine reach during a run?
Section titled “How much can a routine reach during a run?”Two independent controls decide it.
The cloud environment sets network access, environment variables, and a cached setup script. The Default environment uses Trusted network access: only the default allowlist of package registries, cloud provider APIs, container registries, and common development domains. Requests elsewhere fail with 403 and x-deny-reason: host_not_allowed, visible in the run transcript rather than as a routine error. Switch Network access to Custom to list your own domains, or to Full. Anyone who uses the environment sees its variables, so on Pro and Max store API keys as its API credentials.
The connectors decide which external services the run can touch. Connector traffic goes through Anthropic’s servers, not the session’s network path, so tightening the allowlist does not restrain a connector. Only removing it does.
How do you verify what a routine did without reading every run?
Section titled “How do you verify what a routine did without reading every run?”A green status in the run list “means the session started and exited without an infrastructure error. It does not mean the task in your prompt succeeded.” Failures appear only in the transcript, so build the verification into the routine itself.
-
End every run in a reviewable artifact, not an irreversible action. A draft pull request, a labelled ticket, a summary in a channel. Linear makes the governance case (Leela Senthil Nathan, “Our approach to building the Agent Interaction SDK”, 2025-08-01): “an agent cannot be held accountable”, so “issues can only be assigned to humans, and only delegated to agents.”
-
Make the artifact carry its own evidence. Every prompt on this page asks for test output, the commits it inspected, or an explicit list of what it could not do. Your reviewer checks that evidence against acceptance criteria, not the diff line by line.
-
Let your existing gates judge the code. A
claude/branch opened as a pull request runs the same CI, type checks, linters, and review automation as a human’s. A human reviewer signs off. -
Name an owner and a place. Each routine posts to a channel or queue a named person already watches; otherwise it is unmonitored, whatever its run log says.
-
Audit the routine itself weekly, and roll back cheaply. Pause a misbehaving routine with the on/off switch on its detail page; its configuration survives. Delete its
claude/branches to undo its work.
From v2.1.227 the CLI reads a routine’s run log and explains it, which turns the weekly audit into one prompt.
Routines, /loop, /goal, subagents or the Agent SDK?
Section titled “Routines, /loop, /goal, subagents or the Agent SDK?”Six mechanisms keep Claude Code working without you typing. The three schedulers compare like this:
| Routines (cloud) | Desktop scheduled tasks | /loop | |
|---|---|---|---|
| Runs on | Cloud, Anthropic-managed by default | Your machine | Your machine |
| Machine must be on | No | Yes | Yes |
| Open session required | No | No | Yes |
| Local files | No (fresh clone) | Yes | Yes |
| Permission prompts | No | Configurable per task | Inherits from session |
| Minimum interval | 1 hour | 1 minute | 1 minute |
/loop is session-scoped and its recurring tasks expire after seven days, so it is wrong for anything that must run next month; see the /loop command. The other three differ in shape, not schedule:
/goalis a stop condition, not a cadence. After each turn a small fast model checks whether the condition holds, one goal per session. Reach for it when the job is “keep going until X holds” rather than “run every weekday”. See goal workflows.- Subagents are fan-out inside one run. A routine auditing 400 files delegates inside the run instead of becoming 400 routines. See custom subagents.
- The Agent SDK covers triggers routines do not. When the event is not a clock, an HTTP call, or a GitHub pull request or release, or when the job must run on your own infrastructure with your own credentials, build it with the Claude Agent SDK or run
claude -pfrom CI.
The rule: a clock, an HTTP call, or a GitHub event with no need for local files means a routine.
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. Past either limit, runs continue on metered overage if your organization has usage credits, and are rejected until the window resets if not. During the research preview, GitHub webhook events also have per-routine and per-account hourly caps, and events beyond them are dropped, so a burst of pull requests can go unreviewed. Check consumption at claude.ai/settings/usage. Four levers, in the order they usually pay off:
- Filter the trigger harder. A pull-request trigger on every action with no filters runs on every push, drafts included; Is draft
falseand a base-branch filter cut those runs. - Pick the model per routine. The prompt input has a model selector used on every run. A labelling routine does not need the model a migration needs; see the models hub before you change the default.
- Remove connectors and cache the setup. Fewer connectors shrink what a run can touch and the tools it can search; the cached setup script skips reinstalls.
- Use one-off schedules for one-off work. They do not count against the daily cap.
For team-wide budgets, see Claude Code cost control.
What breaks in an unattended routine run?
Section titled “What breaks in an unattended routine run?”/schedule returns “Unknown command” or does not appear. The CLI shows it only with a claude.ai subscription login, not with a Console API key, a federation credential, or a cloud provider such as Amazon Bedrock. ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, and apiKeyHelper take precedence over the login, and inside a cloud session the command is unavailable by design. Recovery: remove those credentials and /login, or use claude.ai/code/routines.
“Routines are disabled by your organization’s policy.” A Team or Enterprise Owner turned off the Routines toggle in admin settings. It is server-side and stops existing routines too. If instead you see “Cloud sessions are disabled by your organization’s policy”, cloud sessions are off, and routines need them. Recovery: ask an Owner.
The GitHub trigger never fires. The Claude GitHub App is not installed on that repository; /web-setup does not install it. Recovery: install the app, then re-save the trigger.
The routine stopped running on its own. A missing or expired GitHub connection makes the routine skip runs for up to 72 hours, then turn off; reconnect inside that window and it resumes by itself. A paused subscription also puts routines on hold. Recovery: reconnect GitHub or reactivate the subscription, then switch the routine back on if it turned off.
The routine ran and ignored your alert. The saved prompt never referenced the fire payload. Recovery: name the routine-fire-payload block in the prompt.
A pull request or issue steered the run. A routine on public input followed instructions planted in a PR body, diff, or issue and used a write-capable connector as you. Recovery: pause it, restrict Author to your team, remove write-capable connectors the task does not need, and add the treat-as-data line to the prompt.
A network call failed. The transcript shows 403 with x-deny-reason: host_not_allowed: the Trusted allowlist covers registries, not your staging API. Recovery: add the domain under Custom network access, or reach the service through a connector.
A run pushed nothing despite saying it would. The prompt targeted a branch other than claude/ and hit one of the three rejection conditions. Recovery: let the routine push to claude/ and open a pull request.
Nobody noticed for a week. The routine posts nowhere a person looks. Recovery: add an owner and a channel, and run the weekly audit prompt.
How Cursor and Codex do the same thing
Section titled “How Cursor and Codex do the same thing”Cursor Automations start Cloud Agents from a wider set of triggers than routines, including Slack, Linear, Sentry, and PagerDuty (Cursor docs, checked 2026-08-28), and Codex runs scheduled Automations in the Codex app. See Cursor cloud agents and automations, Codex automations, and background and cloud agents compared.