# Routines

A routine is work that repeats by itself, giving its worker a new task each time it runs.

A routine is work you want done again and again, without giving the task each time. It names a worker, says what each new task starts with, and says what makes it run: a schedule, another start, or you by hand. Each time it runs, the worker gets a task. To read the work, use the task routes.

A routine is a deployment in Claude Managed Agents. This page explains what a routine holds, what starts it, what each run records, and the limits.

## What a routine holds

| Field | What it is |
| --- | --- |
| `worker` | The worker that gets each task. Give its id, or `{ "type": "worker", "id", "version" }` to pin a version. Read back, it shows the version. |
| `name` | 1 to 256 characters. |
| `description` | Up to 2,048 characters, or `null`. |
| `initial_events` | What each new task starts with: one or more `user.message` events with text blocks, and at most one `user.define_outcome`. The lines of a text rubric become each task's Done Means, up to 8 lines of up to 200 characters. A routine holds 1 to 50 events. |
| `schedule` | When it runs on its own, or `null`. |
| `triggers` | Other starts, beside the schedule. |
| `budget` | The budget copied onto each task. `null` gives each task the default of $5.00. |
| `resources` | Files, repositories and memory stores copied onto each task. They follow the rules of a task's `resources`. |
| `password_set_ids` | The password sets each task signs in with, in order. Left out, a routine reads back the worker's own set and the workspace's shared set. |
| `metadata` | Your own labels. |
| `status` | `active` or `paused`. |
| `paused_reason` | Why it is paused, or `null`. |
| `version` | A whole number that goes up with each change. |
| `archived_at` | When it was archived, or `null`. An archived routine reads `active` with this set. |

## What makes it run

A routine runs from a schedule, from a trigger, or when you run it by hand with `POST /routines/{routine_id}/run`. A routine with no `schedule` and no `triggers` runs only by hand.

### A schedule

A schedule is `{ "type": "cron", "expression", "timezone" }`: a five-field cron expression and an IANA time zone, such as `America/Chicago`. `@every 2h` works too. An expression holds up to 256 characters. A time zone that does not exist is `400` with `error_code` `invalid_timezone`.

Runs must be at least 5 minutes apart. A tighter expression is `400`.

A schedule reads back with `last_run_at` and `upcoming_runs_at`, the next 5 exact times it will run. Each start waits a fixed delay, the same for every run of one routine. The delay is somewhere from 0 up to 15 percent of the gap between runs, in a window of at least 5 seconds and at most 9 minutes. So a daily routine can start a few minutes after its time.

Some times do not go as planned:

- **A time the clock skips.** When a time change skips a clock time, nothing runs. The firing is recorded with `nonexistent_local_time_error`.
- **A time the clock repeats.** It runs once.
- **Little Worker was down.** The run starts when it is back, if that is within an hour of the planned time. Later than that, nothing runs, and the firing is recorded with `more_than_one_hour_late_error`.
- **The worker is busy.** If the worker has other work in hand or waiting, nothing runs, and the firing is recorded with `worker_busy_error`. The next planned time runs as usual.
- **The routine is paused.** Runs missed while it was paused are not made up.

### Triggers

`triggers` is a list of other starts. Each type may appear once.

| Trigger | When it starts the routine |
| --- | --- |
| `{ "type": "webhook" }` | A request arrives at the routine's webhook. A person makes the webhook's key in the app, because this API has no key for it yet. |
| `{ "type": "email", "inbox" }` | An email arrives at one of the workspace's inboxes. |
| `{ "type": "event", "app", "event" }` | An app sends the named event. |

## What each run records

Every firing is a routine run, whether it made a task or not. A routine run has an id that starts with `routine_run_`, and exactly one of `task_id` and `error` is set.

```json
{
  "type": "routine_run",
  "id": "routine_run_0e5a9c3d7f1b4d28a6c4b0e9d3f7a512",
  "routine_id": "routine_6d2b8f4a1c7e4a39b5d0e8f3a2c91b67",
  "worker": { "type": "worker", "id": "worker_bf5ff997fca044b38dc1cc9ea5b60100" },
  "trigger_context": { "type": "schedule", "scheduled_at": "2026-10-26T13:00:00.000Z" },
  "task_id": "task_01M5TTRJKWYAK3XHFX432K5MBV",
  "error": null,
  "created_at": "2026-10-26T13:03:12.418Z"
}
```

`trigger_context` says what started it: `schedule` with `scheduled_at`, `manual`, `webhook`, `email` with the `inbox`, or `event` with the `app` and `event`.

When a firing made no task, `error` is `{ "type", "message" }`. `type` is one of these:

| `error.type` | When it happens |
| --- | --- |
| `worker_archived_error` | The worker was archived. |
| `worker_busy_error` | The worker was busy with other work, or its queue was full. |
| `nonexistent_local_time_error` | The local time did not exist that day. |
| `more_than_one_hour_late_error` | Little Worker was down for more than an hour past the planned time. |
| `routine_paused_error` | The routine was paused. |
| `superseded_edit_error` | An edit replaced the planned time. |
| `unknown_error` | Something else failed. |

A firing that made a task gives the worker a task like any other. The task's `routine_id` names the routine. Read and follow it with the task routes, which [Tasks](/concepts/tasks) explains. It carries the routine's `budget`, so one task never spends more than that.

`GET /routine_runs` lists the runs of all your routines, newest first, up to 1,000 a page. Filter by `routine_id`, by `has_error` (`true` or `false`), or by `trigger_type` (`schedule`, `manual`, `webhook`, `email` or `event`). `GET /routine_runs/{routine_run_id}` reads one.

## Pause, change and archive

- **Pause** with `POST /routines/{routine_id}/pause`. A paused routine stops its schedule and its triggers, but you can still run it by hand. Unpause with `POST /routines/{routine_id}/unpause`. The next run is planned from that moment, so missed times are not made up. Pausing does not stop a task the routine already started. Stop that task like any other.
- **Pause by itself.** A routine pauses on its own when a firing fails in a way that will not clear. `paused_reason` is then `{ "type": "error", "error": { "type" } }` with `worker_archived_error`, `password_set_not_found_error` (a set it names was deleted) or `password_set_archived_error` (a set it names was archived). When a person paused it, `paused_reason` is `{ "type": "manual" }`. Unpausing a routine that names an archived set is `400` until you change its `password_set_ids`.
- **Change** with `POST /routines/{routine_id}`. Each field you send replaces the routine's. `null` clears `description`, `schedule` and `budget`, and `metadata` merges key by key. To avoid overwriting another change, send the `version` you read. If the routine changed since, the answer is `409` with `error_code` `schedule_conflict` and a `current_version`. Without `version`, the last write wins.
- **Archive** with `POST /routines/{routine_id}/archive`. It is one way: the routine stays readable and never fires again. An archived routine is `409` for a change, a pause, an unpause or a run. There is no routine delete. Archiving a worker archives its routines.

Send an `Idempotency-Key` with a create, a change, an archive, a pause, an unpause or a run, and a retry does the work once.

## Limits

| What | Limit |
| --- | --- |
| Routines in a workspace | 1,000. A hosted plan may allow fewer, or ask for a longer gap between runs. Archived routines do not count. |
| Routines for one worker | 50 |
| Gap between runs | At least 5 minutes |
| Name | 256 characters |
| Description | 2,048 characters |
| Schedule expression | 256 characters |
| `initial_events` | 1 to 50 events |
| Budget for each task | $0.01 to $1,000.00 |

A routine past the workspace limit or the worker limit is `409` with `error.type` `conflict_error`. Archive a routine to make room.

## Next steps

- [Tasks](/concepts/tasks): what each task a routine makes does, and how to read its result.
- [Password sets](/concepts/password-sets): the sets a routine's tasks sign in with, and what happens when one is archived.
- [Memory](/concepts/memory): stores a routine can hand to each task, so work builds on the last run.
