# Scheduled deployments

Run an agent on a recurring cron schedule and inspect its run history.

---

A **scheduled deployment** allows an [agent](/docs/en/managed-agents/agent-setup) to kick off [sessions](/docs/en/managed-agents/sessions) autonomously, enabling task completion over a predictable cadence.

<Note>
All Managed Agents API requests require the `managed-agents-2026-04-01` beta header. The SDK sets the beta header automatically.
</Note>

## Create a scheduled deployment

When creating a deployment, you pass the [session configurations](/docs/en/managed-agents/sessions) required for execution, in addition to a `schedule`.

- Deployments require [agent configuration](/docs/en/managed-agents/agent-setup) and [environment configuration](/docs/en/managed-agents/environments), and optionally accept [files](/docs/en/managed-agents/files), [GitHub](/docs/en/managed-agents/github), [memory stores](/docs/en/managed-agents/memory), and [vaults](/docs/en/managed-agents/vaults).
- Deployments also require an initial `user.message` event that starts the session's work.
- In the `schedule`, you define a cron `expression` and a `timezone`. Maximum granularity supported is at the minute level.

<CodeGroup defaultLanguage="CLI">
  
````bash
DEPLOYMENT_ID=$(
  curl --fail-with-body -sS "https://api.anthropic.com/v1/deployments?beta=true" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01" \
    -H "content-type: application/json" \
    -d @- <<EOF | jq -er '.id'
{
  "name": "Weekly compliance scan",
  "agent": "$AGENT_ID",
  "environment_id": "$ENVIRONMENT_ID",
  "initial_events": [
    {"type": "user.message", "content": [{"type": "text", "text": "Run the weekly compliance scan."}]}
  ],
  "schedule": {
    "type": "cron",
    "expression": "0 20 * * 5",
    "timezone": "America/New_York"
  }
}
EOF
)
````

  
````bash
DEPLOYMENT_ID=$(ant beta:deployments create <<YAML | jq -er '.id'
name: Weekly compliance scan
agent: $AGENT_ID
environment_id: $ENVIRONMENT_ID
initial_events:
  - type: user.message
    content:
      - type: text
        text: Run the weekly compliance scan.
schedule:
  type: cron
  expression: "0 20 * * 5"
  timezone: America/New_York
YAML
)
````

  
````python
deployment = client.beta.deployments.create(
    name="Weekly compliance scan",
    agent=agent.id,
    environment_id=environment.id,
    initial_events=[
        {
            "type": "user.message",
            "content": [{"type": "text", "text": "Run the weekly compliance scan."}],
        },
    ],
    schedule={
        "type": "cron",
        "expression": "0 20 * * 5",
        "timezone": "America/New_York",
    },
)
````

  
````typescript
const deployment = await client.beta.deployments.create({
  name: "Weekly compliance scan",
  agent: agent.id,
  environment_id: environment.id,
  initial_events: [
    {
      type: "user.message",
      content: [{ type: "text", text: "Run the weekly compliance scan." }],
    },
  ],
  schedule: {
    type: "cron",
    expression: "0 20 * * 5",
    timezone: "America/New_York",
  },
});
````

  
````csharp
var deployment = await client.Beta.Deployments.Create(new()
{
    Name = "Weekly compliance scan",
    Agent = agent.ID,
    EnvironmentID = environment.ID,
    InitialEvents =
    [
        new BetaManagedAgentsUserMessageEventParams
        {
            Type = BetaManagedAgentsUserMessageEventParamsType.UserMessage,
            Content =
            [
                new BetaManagedAgentsTextBlock
                {
                    Type = BetaManagedAgentsTextBlockType.Text,
                    Text = "Run the weekly compliance scan.",
                },
            ],
        },
    ],
    Schedule = new BetaManagedAgentsScheduleParams
    {
        Type = BetaManagedAgentsScheduleParamsType.Cron,
        Expression = "0 20 * * 5",
        Timezone = "America/New_York",
    },
});
````

  
````go
deployment, err := client.Beta.Deployments.New(ctx, anthropic.BetaDeploymentNewParams{
	Name:          "Weekly compliance scan",
	Agent:         anthropic.BetaDeploymentNewParamsAgentUnion{OfString: anthropic.String(agent.ID)},
	EnvironmentID: environment.ID,
	InitialEvents: []anthropic.BetaManagedAgentsDeploymentInitialEventParamsUnion{{
		OfUserMessage: &anthropic.BetaManagedAgentsUserMessageEventParams{
			Type: anthropic.BetaManagedAgentsUserMessageEventParamsTypeUserMessage,
			Content: []anthropic.BetaManagedAgentsUserMessageEventParamsContentUnion{{
				OfText: &anthropic.BetaManagedAgentsTextBlockParam{
					Type: anthropic.BetaManagedAgentsTextBlockTypeText,
					Text: "Run the weekly compliance scan.",
				},
			}},
		},
	}},
	Schedule: anthropic.BetaManagedAgentsScheduleParams{
		Type:       anthropic.BetaManagedAgentsScheduleParamsTypeCron,
		Expression: "0 20 * * 5",
		Timezone:   "America/New_York",
	},
})
if err != nil {
	panic(err)
}
````

  
````java
var deployment = client.beta().deployments().create(
    DeploymentCreateParams.builder()
        .name("Weekly compliance scan")
        .agent(agent.id())
        .environmentId(environment.id())
        .addInitialEvent(
            BetaManagedAgentsUserMessageEventParams.builder()
                .type(BetaManagedAgentsUserMessageEventParams.Type.USER_MESSAGE)
                .addTextContent("Run the weekly compliance scan.")
                .build()
        )
        .schedule(
            BetaManagedAgentsScheduleParams.builder()
                .type(BetaManagedAgentsScheduleParams.Type.CRON)
                .expression("0 20 * * 5")
                .timezone("America/New_York")
                .build()
        )
        .build()
);
````

  
````php
$deployment = $client->beta->deployments->create(
    name: 'Weekly compliance scan',
    agent: $agent->id,
    environmentID: $environment->id,
    initialEvents: [
        [
            'type' => 'user.message',
            'content' => [['type' => 'text', 'text' => 'Run the weekly compliance scan.']],
        ],
    ],
    schedule: [
        'type' => 'cron',
        'expression' => '0 20 * * 5',
        'timezone' => 'America/New_York',
    ],
);
````

  
````ruby
deployment = client.beta.deployments.create(
  name: "Weekly compliance scan",
  agent: agent.id,
  environment_id: environment.id,
  initial_events: [
    {
      type: "user.message",
      content: [{type: "text", text: "Run the weekly compliance scan."}]
    }
  ],
  schedule: {
    type: "cron",
    expression: "0 20 * * 5",
    timezone: "America/New_York"
  }
)
````

</CodeGroup>

The response includes a deployment object with a populated `schedule.upcoming_runs_at` with the next upcoming fire times, to confirm your schedule was set correctly.

```json
{
  "id": "depl_01xyz",
  "status": "active",
  "paused_reason": null,
  "schedule": {
    "type": "cron",
    "expression": "0 20 * * 5",
    "timezone": "America/New_York",
    "last_run_at": null,
    "upcoming_runs_at": [
      "2026-05-09T00:00:00Z",
      "2026-05-16T00:00:00Z",
      "2026-05-23T00:00:00Z"
    ]
  }
}
```

The upcoming run timestamps are based on the exact schedule configured. However, to distribute load, deployments may apply jitter of up to 10 seconds.

A maximum of **1,000 scheduled deployments** is supported per organization. Contact Anthropic support if you need more.

### Cron and timezone semantics

- **Expression:** Standard POSIX cron (`minute hour day-of-month month day-of-week`). You can generate and validate these cron expressions in the [Claude Console](https://platform.claude.com/workspaces/default/deployments).
- **Timezone:** IANA timezone identifier (for example, `"America/Los_Angeles"`).
- **DST:** Cron schedules use literal wall-clock matching, so `"0 20 * * *"` in `America/New_York` fires at 8:00 PM local time regardless of whether EST or EDT is in effect.

<Note>
Wall-clock times that do not exist on a spring-forward day (such as 2 AM) are not triggered. Wall-clock times that occur twice on a fall-back day fire twice. Schedule outside the 1–3 AM local window, or use UTC, when missed or duplicate executions are unacceptable.
</Note>

## Deployment runs

Deployments can fail to trigger for a variety of reasons: for example, if the `environment` resource has been archived, or if session creation is rate-limited. Each attempt at executing a deployment generates a **deployment run** record, allowing you to track successes and failures independent of the session lifecycle.

Successful deployments generate active sessions, and a successful deployment run contains the associated `session_id`. To follow a session's lifecycle, track the session events through the [event stream](/docs/en/managed-agents/events-and-streaming) or [webhooks](/docs/en/managed-agents/webhooks).

List all deployment runs for a deployment as follows:

<CodeGroup defaultLanguage="CLI">
  
````bash
curl --fail-with-body -sS "https://api.anthropic.com/v1/deployment_runs?beta=true&deployment_id=$DEPLOYMENT_ID" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01"
````

  
````bash
ant beta:deployment-runs list --deployment-id "$DEPLOYMENT_ID"
````

  
````python
for run in client.beta.deployment_runs.list(
    deployment_id=deployment.id,
):
    print(run.created_at, run.session_id or run.error.type)
````

  
````typescript
for await (const run of client.beta.deploymentRuns.list({
  deployment_id: deployment.id,
})) {
  console.log(run.created_at, run.session_id ?? run.error?.type);
}
````

  
````csharp
var runs = await client.Beta.DeploymentRuns.List(
    new() { DeploymentID = deployment.ID }
);
await foreach (var run in runs.Paginate())
{
    // The Error union exposes .Message directly; the discriminator is read
    // from .Json until a common .Type accessor is added.
    var outcome = run.SessionID ?? run.Error!.Json.GetProperty("type").GetString();
    Console.WriteLine($"{run.CreatedAt} {outcome}");
}
````

  
````go
runs := client.Beta.DeploymentRuns.ListAutoPaging(ctx, anthropic.BetaDeploymentRunListParams{
	DeploymentID: anthropic.String(deployment.ID),
})
for runs.Next() {
	run := runs.Current()
	if run.SessionID != "" {
		fmt.Println(run.CreatedAt.Format(time.RFC3339), run.SessionID)
	} else {
		fmt.Println(run.CreatedAt.Format(time.RFC3339), run.Error.Type)
	}
}
if err := runs.Err(); err != nil {
	panic(err)
}
````

  
````java
for (var run : client.beta().deploymentRuns().list(
        DeploymentRunListParams.builder()
            .deploymentId(deployment.id())
            .build()).autoPager()) {
    // The Error union does not yet expose common .type()/.message()
    // accessors; .toString() includes both.
    IO.println(run.createdAt() + " "
        + run.sessionId().orElseGet(() -> run.error().orElseThrow().toString()));
}
````

  
````php
foreach ($client->beta->deploymentRuns->list(
    deploymentID: $deployment->id,
)->pagingEachItem() as $run) {
    $outcome = $run->sessionID ?? $run->error->type;
    echo "{$run->createdAt->format(DATE_ATOM)} {$outcome}\n";
}
````

  
````ruby
client.beta.deployment_runs.list(
  deployment_id: deployment.id
).auto_paging_each do
  puts "#{it.created_at} #{it.session_id || it.error.type}"
end
````

</CodeGroup>

You can additionally filter on deployment runs with errors:

<CodeGroup defaultLanguage="CLI">
  
````bash
curl --fail-with-body -sS "https://api.anthropic.com/v1/deployment_runs?beta=true&deployment_id=$DEPLOYMENT_ID&has_error=true" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01"
````

  
````bash
ant beta:deployment-runs list --deployment-id "$DEPLOYMENT_ID" --has-error
````

  
````python
for run in client.beta.deployment_runs.list(
    deployment_id=deployment.id,
    has_error=True,
):
    print(run.created_at, run.error.type, run.error.message)
````

  
````typescript
for await (const run of client.beta.deploymentRuns.list({
  deployment_id: deployment.id,
  has_error: true,
})) {
  console.log(run.created_at, run.error?.type, run.error?.message);
}
````

  
````csharp
var failedRuns = await client.Beta.DeploymentRuns.List(
    new() { DeploymentID = deployment.ID, HasError = true }
);
await foreach (var failedRun in failedRuns.Paginate())
{
    var error = failedRun.Error!;
    var errorType = error.Json.GetProperty("type").GetString();
    Console.WriteLine($"{failedRun.CreatedAt} {errorType} {error.Message}");
}
````

  
````go
failedRuns := client.Beta.DeploymentRuns.ListAutoPaging(ctx, anthropic.BetaDeploymentRunListParams{
	DeploymentID: anthropic.String(deployment.ID),
	HasError:     anthropic.Bool(true),
})
for failedRuns.Next() {
	failedRun := failedRuns.Current()
	fmt.Println(failedRun.CreatedAt.Format(time.RFC3339), failedRun.Error.Type, failedRun.Error.Message)
}
if err := failedRuns.Err(); err != nil {
	panic(err)
}
````

  
````java
for (var run : client.beta().deploymentRuns().list(
        DeploymentRunListParams.builder()
            .deploymentId(deployment.id())
            .hasError(true)
            .build()).autoPager()) {
    IO.println(run.createdAt() + " " + run.error().orElseThrow());
}
````

  
````php
foreach ($client->beta->deploymentRuns->list(
    deploymentID: $deployment->id,
    hasError: true,
)->pagingEachItem() as $run) {
    echo "{$run->createdAt->format(DATE_ATOM)} {$run->error->type} {$run->error->message}\n";
}
````

  
````ruby
client.beta.deployment_runs.list(
  deployment_id: deployment.id,
  has_error: true
).auto_paging_each do
  puts "#{it.created_at} #{it.error.type} #{it.error.message}"
end
````

</CodeGroup>

A failed run includes an `error` with a `type` describing why session creation was rejected (for example, `environment_archived_error`, `agent_archived_error`, or `session_rate_limited_error`).

```json
{
  "type": "deployment_run",
  "id": "drun_01abc124",
  "deployment_id": "depl_01xyz",
  "trigger_context": { "type": "schedule", "scheduled_at": "2026-05-09T00:00:00Z" },
  "session_id": null,
  "error": {
    "type": "environment_archived_error",
    "message": "environment `env_01abc` is archived"
  },
  "agent": { "type": "agent", "id": "agent_01ghi789", "version": 3 },
  "created_at": "2026-05-09T00:00:01Z"
}
```

## Managing deployment lifecycle

**Pause** suppresses scheduled triggers on a go-forward basis; running sessions from a prior deployment run continue to execute. Manual runs through the `run` endpoint are still allowed while paused. Pausing sets `paused_reason` to `{"type": "manual"}`; unpausing clears it.

<CodeGroup defaultLanguage="CLI">
  
````bash
curl --fail-with-body -sS -X POST "https://api.anthropic.com/v1/deployments/$DEPLOYMENT_ID/pause?beta=true" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01"
````

  
````bash
ant beta:deployments pause --deployment-id "$DEPLOYMENT_ID"
````

  
````python
client.beta.deployments.pause(deployment.id)
````

  
````typescript
await client.beta.deployments.pause(deployment.id);
````

  
````csharp
await client.Beta.Deployments.Pause(deployment.ID);
````

  
````go
if _, err := client.Beta.Deployments.Pause(ctx, deployment.ID, anthropic.BetaDeploymentPauseParams{}); err != nil {
	panic(err)
}
````

  
````java
client.beta().deployments().pause(deployment.id());
````

  
````php
$client->beta->deployments->pause($deployment->id);
````

  
````ruby
client.beta.deployments.pause(deployment.id)
````

</CodeGroup>

**Unpause** resumes the schedule from the next scheduled occurrence. Missed triggers are not backfilled.

<CodeGroup defaultLanguage="CLI">
  
````bash
curl --fail-with-body -sS -X POST "https://api.anthropic.com/v1/deployments/$DEPLOYMENT_ID/unpause?beta=true" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01"
````

  
````bash
ant beta:deployments unpause --deployment-id "$DEPLOYMENT_ID"
````

  
````python
client.beta.deployments.unpause(deployment.id)
````

  
````typescript
await client.beta.deployments.unpause(deployment.id);
````

  
````csharp
await client.Beta.Deployments.Unpause(deployment.ID);
````

  
````go
if _, err := client.Beta.Deployments.Unpause(ctx, deployment.ID, anthropic.BetaDeploymentUnpauseParams{}); err != nil {
	panic(err)
}
````

  
````java
client.beta().deployments().unpause(deployment.id());
````

  
````php
$client->beta->deployments->unpause($deployment->id);
````

  
````ruby
client.beta.deployments.unpause(deployment.id)
````

</CodeGroup>

**Archive**, unlike **pause**, is terminal: the schedule terminates and the deployment cannot be modified.

<CodeGroup defaultLanguage="CLI">
  
````bash
curl --fail-with-body -sS -X POST "https://api.anthropic.com/v1/deployments/$DEPLOYMENT_ID/archive?beta=true" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01"
````

  
````bash
ant beta:deployments archive --deployment-id "$DEPLOYMENT_ID"
````

  
````python
client.beta.deployments.archive(deployment.id)
````

  
````typescript
await client.beta.deployments.archive(deployment.id);
````

  
````csharp
await client.Beta.Deployments.Archive(deployment.ID);
````

  
````go
if _, err := client.Beta.Deployments.Archive(ctx, deployment.ID, anthropic.BetaDeploymentArchiveParams{}); err != nil {
	panic(err)
}
````

  
````java
client.beta().deployments().archive(deployment.id());
````

  
````php
$client->beta->deployments->archive($deployment->id);
````

  
````ruby
client.beta.deployments.archive(deployment.id)
````

</CodeGroup>

### Failure behavior

Session creation rate-limit responses are recorded immediately as a `session_rate_limited_error` run without retry; the schedule attempts again at the next scheduled occurrence. Rate limits on underlying API calls within a session are handled by the session itself.

If a deployment's agent has been archived or deleted, the deployment is automatically archived in the same operation; no deployment run is recorded. If a subagent referenced by the agent has been archived, the next trigger records a failed run with `error.type: "agent_archived_error"` and the deployment is automatically paused so you can update the agent and resume.

## Trigger a manual run

To run a deployment outside its schedule, call the `run` endpoint. This creates a session immediately and writes a deployment run with `trigger_context.type: "manual"`. This allows you to test a deployment before committing to the schedule.

<CodeGroup defaultLanguage="CLI">
  
````bash
curl --fail-with-body -sS -X POST "https://api.anthropic.com/v1/deployments/$DEPLOYMENT_ID/run?beta=true" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01"
````

  
````bash
ant beta:deployments run --deployment-id "$DEPLOYMENT_ID"
````

  
````python
run = client.beta.deployments.run(deployment.id)
````

  
````typescript
const run = await client.beta.deployments.run(deployment.id);
````

  
````csharp
var manualRun = await client.Beta.Deployments.Run(deployment.ID);
````

  
````go
manualRun, err := client.Beta.Deployments.Run(ctx, deployment.ID, anthropic.BetaDeploymentRunParams{})
if err != nil {
	panic(err)
}
````

  
````java
var run = client.beta().deployments().run(deployment.id());
````

  
````php
$run = $client->beta->deployments->run($deployment->id);
````

  
````ruby
run = client.beta.deployments.run(deployment.id)
````

</CodeGroup>