Skip to main content
Schedules let you run any workflow automatically on a recurring basis. Define a cron expression and timezone, and Skyvern triggers the workflow at each interval. Scheduled runs appear in your run history with trigger_type: "scheduled" so you can distinguish them from manual or API-triggered runs.

How schedules work

A schedule links a cron expression and a timezone to a workflow. At each scheduled time, Skyvern creates a new workflow run with the parameters you configured when creating the schedule. Each scheduled run is identical to a manually triggered run — same blocks, same parameters, same outputs. The only difference is the trigger_type field, which is set to "scheduled" instead of "manual" or "api".

Cron expression format

Schedules use standard 5-field cron expressions:
┌───────── minute (0-59)
│ ┌─────── hour (0-23)
│ │ ┌───── day of month (1-31)
│ │ │ ┌─── month (1-12)
│ │ │ │ ┌─ day of week (0-6, Sun=0)
│ │ │ │ │
* * * * *
Common patterns:
Cron expressionDescription
0 9 * * 1-5Every weekday at 9:00 AM
0 */6 * * *Every 6 hours
0 9 * * 1Every Monday at 9:00 AM
0 0 1 * *First day of every month at midnight
*/30 * * * *Every 30 minutes
0 8,17 * * *Twice daily at 8:00 AM and 5:00 PM
The minimum interval is 5 minutes. Cron expressions that resolve to intervals shorter than 5 minutes return a validation error:
{
  "detail": "Cron interval must be at least 5 minutes"
}
Invalid cron syntax returns "Invalid cron expression".

Timezone handling

Schedules use IANA timezone identifiers (e.g., America/New_York, Europe/London, Asia/Tokyo). If you don’t specify a timezone, it defaults to UTC.

Create a schedule

Send a POST request to /api/v1/workflows/{workflow_permanent_id}/schedules with your cron expression and timezone.
import os
import asyncio
from skyvern import Skyvern

async def main():
    client = Skyvern(api_key=os.getenv("SKYVERN_API_KEY"))

    result = await client.agent.create_workflow_schedule(
        workflow_permanent_id="wpid_123456789",
        cron_expression="0 9 * * 1-5",
        timezone="America/New_York",
        name="Weekday morning report",
        description="Runs the data extraction workflow every weekday at 9 AM ET",
        parameters={
            "url": "https://example.com/dashboard",
            "output_format": "csv"
        }
    )

    print(f"Schedule ID: {result.schedule.workflow_schedule_id}")

asyncio.run(main())
All Python examples on this page assume you’ve initialized the client as shown above:
client = Skyvern(api_key=os.getenv("SKYVERN_API_KEY"))

Request body

FieldTypeRequiredDescription
cron_expressionstringYes5-field cron expression (minimum 5-minute interval)
timezonestringYesIANA timezone identifier (e.g., America/New_York)
namestringNoHuman-readable name for the schedule
descriptionstringNoDescription of what this schedule does
parametersobjectNoWorkflow parameters to pass on each run
enabledbooleanNoWhether the schedule is active. Defaults to true

Example response

{
  "schedule": {
    "workflow_schedule_id": "wfs_510037469402471882",
    "organization_id": "o_510009610576515030",
    "workflow_permanent_id": "wpid_510013188284271440",
    "cron_expression": "0 9 * * 1-5",
    "timezone": "America/New_York",
    "enabled": true,
    "parameters": null,
    "temporal_schedule_id": "wf-sched-wfs_510037469402471882",
    "name": "Weekday morning report",
    "description": "Runs the data extraction workflow every weekday at 9 AM ET",
    "created_at": "2026-03-25T10:45:52.193974",
    "modified_at": "2026-03-25T10:45:52.221999",
    "deleted_at": null
  },
  "next_runs": [
    "2026-03-25T13:00:00Z",
    "2026-03-26T13:00:00Z",
    "2026-03-27T13:00:00Z",
    "2026-03-30T13:00:00Z",
    "2026-03-31T13:00:00Z"
  ]
}
Response fields:
FieldTypeDescription
schedule.workflow_schedule_idstringUnique identifier for this schedule (prefixed wfs_)
schedule.organization_idstringThe organization that owns this schedule
schedule.workflow_permanent_idstringThe workflow this schedule triggers
schedule.cron_expressionstringThe cron expression defining the recurrence
schedule.timezonestringIANA timezone for the cron expression
schedule.enabledbooleanWhether the schedule is active (true) or paused (false). Defaults to true on creation
schedule.parametersobject | nullWorkflow parameters passed to each run
schedule.temporal_schedule_idstringInternal identifier used by the scheduling engine
schedule.namestring | nullHuman-readable name
schedule.descriptionstring | nullDescription of the schedule
schedule.created_atstringWhen the schedule was created
schedule.modified_atstringWhen the schedule was last updated
schedule.deleted_atstring | nullWhen the schedule was deleted, if applicable
next_runsarrayUpcoming execution times in UTC based on the cron expression and timezone
Workflow runs triggered by this schedule will include trigger_type: "scheduled" and workflow_schedule_id pointing back to the schedule, so you can distinguish them from manual or API-triggered runs.

List schedules

To list all schedules in your organization:
result = await client.agent.list_organization_schedules()

for schedule in result.schedules:
    status = "active" if schedule.enabled else "paused"
    print(f"{schedule.workflow_schedule_id}: {schedule.name} ({status})")

Example response

{
  "schedules": [
    {
      "workflow_schedule_id": "wfs_510037469402471882",
      "organization_id": "o_510009610576515030",
      "workflow_permanent_id": "wpid_510013188284271440",
      "workflow_title": "New Workflow",
      "cron_expression": "0 9 * * 1-5",
      "timezone": "America/New_York",
      "enabled": true,
      "parameters": null,
      "name": "Weekday morning report",
      "description": "Runs the data extraction workflow every weekday at 9 AM ET",
      "next_run": "2026-03-25T13:00:00Z",
      "created_at": "2026-03-25T10:45:52.193974",
      "modified_at": "2026-03-25T10:45:52.221999"
    }
  ],
  "total_count": 1,
  "page": 1,
  "page_size": 10
}
Response fields:
FieldTypeDescription
schedulesarrayList of schedule objects
schedules[].workflow_schedule_idstringSchedule identifier
schedules[].workflow_permanent_idstringThe workflow this schedule triggers
schedules[].workflow_titlestringTitle of the associated workflow
schedules[].enabledbooleanWhether the schedule is active
schedules[].next_runstringNext scheduled execution time in UTC
total_countintegerTotal number of schedules matching the query
pageintegerCurrent page number
page_sizeintegerNumber of results per page
To list schedules for a specific workflow:
result = await client.agent.list_workflow_schedules(
    workflow_permanent_id="wpid_123456789"
)

Get schedule details

Retrieve a single schedule, including upcoming run times in next_runs.
result = await client.agent.get_workflow_schedule(
    workflow_permanent_id="wpid_123456789",
    workflow_schedule_id="wfs_abc123"
)

print(f"Next runs: {result.next_runs}")
The response has the same shape as the create response — a schedule object with a next_runs array.

Update a schedule

Change the cron expression, timezone, name, description, or parameters.
result = await client.agent.update_workflow_schedule(
    workflow_permanent_id="wpid_123456789",
    workflow_schedule_id="wfs_abc123",
    cron_expression="0 8 * * 1-5",
    timezone="America/Chicago",
    name="Updated morning report",
    description="Updated description"
)
Updates replace all fields — any optional field you omit (like description or parameters) will be set to null. Re-send all fields you want to keep.
The response has the same shape as the create response.

Enable and disable a schedule

You can pause a schedule by disabling it. Compared to deleting a schedule, this keeps its configuration saved to be re-enabled at any time.
# Disable
await client.agent.disable_workflow_schedule(
    workflow_permanent_id="wpid_123456789",
    workflow_schedule_id="wfs_abc123"
)

# Re-enable
await client.agent.enable_workflow_schedule(
    workflow_permanent_id="wpid_123456789",
    workflow_schedule_id="wfs_abc123"
)
Both endpoints return the same shape as the create response, with enabled set to false or true.

Delete a schedule

Permanently remove a schedule. This cannot be undone. Runs that were already triggered by this schedule are not affected.
await client.agent.delete_workflow_schedule_route(
    workflow_permanent_id="wpid_123456789",
    workflow_schedule_id="wfs_abc123"
)

Example response

{
  "ok": true
}

Limits

Self-hosted (OSS)Skyvern Cloud
Schedules per workflowUnlimitedConfigurable per plan
Total schedules per orgUnlimitedConfigurable per plan
Minimum interval5 minutes5 minutes

What’s next

Cloud UI: Scheduling

Create and manage schedules from the Skyvern dashboard

Cost Control

Set step limits and optimize scheduled workflow costs

Webhooks

Get notified when scheduled runs complete

Run History

View and filter scheduled run results