> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wherobots.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a schedule

> Store a script, create a paused schedule in Cloud or through the REST API, verify a run, and resume recurring execution.

A task schedule runs a Python script or JAR from Amazon S3 on a recurring interval or cron schedule. Each execution has a processing window, run history, logs, and an execution identity.

Use a schedule for recurring work such as a daily catalog-health report or an hourly data transformation. Use a [one-off Job Run](https://docs.wherobots.com/reference/runs.md) to submit an individual execution. Use [Apache Airflow](https://docs.wherobots.com/develop/run-operator.md) when you need to coordinate dependencies across jobs or systems.

The API calls a schedule a **trigger**. Creating one also creates its Organization-owned **task**, which holds the script definition and input schema; you don't need to create a schema yourself.

## Before you start

* A [Wherobots account](https://docs.wherobots.com/get-started/wherobots-cloud/create-account.md) in an active Organization with task scheduling enabled.
* A Python `.py` file or a `.jar` file, or the sample below.
* For the REST API tab, a [Wherobots API key](https://docs.wherobots.com/get-started/wherobots-cloud/api-keys.md), `curl`, and `jq`.

## Write the script

Save this as `window_report.py`. It prints the supplied processing window and arguments; replace the commented example with your processing code when you're ready.

```python window_report.py theme={"system"}
import os
import sys
from datetime import datetime, timezone

WINDOW_FORMAT = "%Y-%m-%dT%H:%M:%SZ"

def window_bound(name):
    value = os.environ.get(name)
    if value is None:
        sys.exit(f"{name} is not set: run this script from a task schedule")
    return datetime.strptime(value, WINDOW_FORMAT).replace(tzinfo=timezone.utc)

task_run_id = os.environ["WBC_TASK_RUN_ID"]
window_start = window_bound("WBC_WINDOW_START")
window_end = window_bound("WBC_WINDOW_END")

print(f"Task run {task_run_id}")
print(f"Window [{window_start.isoformat()}, {window_end.isoformat()})")
print(f"Arguments: {sys.argv[1:]}")

# Process only records in the window: start inclusive, end exclusive.
# For example, with WherobotsDB:
#   sedona.table("org_catalog.my_db.events").where(
#       (col("event_time") >= window_start) & (col("event_time") < window_end)
#   )
```

Arguments that you set in the schedule's `args` input arrive as ordinary command-line arguments, in `sys.argv[1:]`. For the full list of what a script receives, see [Scripts and processing windows](https://docs.wherobots.com/develop/task-schedules/scripts-and-windows.md).

## Store the script

Choose storage based on who will run the schedule:

* **Personal schedules:** use [My Files](https://docs.wherobots.com/develop/storage-management/file-explorer.md) in the schedule's region. Each region has its own My Files. A My Files script must belong to the **Run as** principal; Wherobots checks this on create, edit, and before each run.
* **Scripts other identities must run:** use [Managed Storage](https://docs.wherobots.com/develop/storage-management/managed-storage.md#folders-and-permissions) under `data/shared/`, for example for a schedule that runs as a service principal. Your Organization can read and modify these files. A person's My Files cannot be used by a service principal.
* **Your own buckets:** use an [S3 Storage Integration](https://docs.wherobots.com/develop/storage-management/s3-storage-integration.md) accessible from the schedule's region.
* **BYOC regions:** store the script in an S3 Storage Integration, as for other BYOC Job Runs. See [Run workloads in BYOC](https://docs.wherobots.com/develop/storage-management/byoc.md#run-workloads-in-byoc).

See [script location and changes](https://docs.wherobots.com/develop/task-schedules/scripts-and-windows.md#script-location-and-changes) for access and versioning rules.

## Configure the schedule

Create the schedule paused, verify an explicit window, then resume it. **Run now** starts billable compute.

> [tab] Cloud UI
>
> **1. Create a paused schedule**
>
> Open [**Scheduled Tasks**](https://cloud.wherobots.com/scheduled-tasks) and choose **Create a schedule**. Enter a **Schedule name** and choose **Region**. Select **Create paused**, which is unchecked by default.
>
> Click **Browse**. In **Select script file**, choose `window_report.py` from **My Files** for your region, for example **My Files (aws-us-west-2)**. If you have not uploaded it yet, use the upload button in this dialog. In **Upload File**, confirm **Uploads to My Files**, then upload the script into the open folder. The source menu also offers Managed Storage and storage integrations for scripts other identities must run. For a JAR, you can provide **Main class (optional)**.
>
> ![Select script file dialog with window\_report.py selected in My Files (aws-us-west-2)](https://mintcdn.com/wherobots/Me_qzO_IbxOjqKzV/images/develop/task-schedules/create-a-schedule/select-script-file.png?fit=max\&auto=format\&n=Me_qzO_IbxOjqKzV\&q=85\&s=7a1cb677254cf2842b2ec0752c4c467a)
>
> Select script file, open on My Files in AWS US West (Oregon)
>
> Choose **Runtime**. Set **Arguments** to `--mode report`. Whitespace separates arguments; quote values containing spaces. This field does not execute a shell or expand variables. Under **Advanced execution settings**, you can change **Timeout (seconds)**, **Runtime version**, Spark settings, and dependencies. See [Script inputs](https://docs.wherobots.com/develop/task-schedules/scripts-and-windows.md#script-inputs) for defaults and [the input visibility warning](https://docs.wherobots.com/develop/task-schedules/scripts-and-windows.md#script-inputs) before adding configuration.
>
> Choose a **Timing mode**:
>
> * **Simple interval**: set **Interval count** and **Interval unit**. For intervals of a day or more, provide **Start date**, **Start time**, and **Timezone**.
> * **Advanced cron**: enter five fields in **Cron expression (minute hour day month weekday)** and choose **Timezone**. For this example, use `0 6 * * *` with `UTC`.
>
> Runs must be at least 15 minutes apart. Use cron for a fixed local clock time; review [timezones and daylight saving time](https://docs.wherobots.com/develop/task-schedules/scripts-and-windows.md#timezones-and-daylight-saving-time).
>
> Under **Execution identity and notifications**, leave **Run as** set to yourself for this personal script. See [Who can manage a schedule](https://docs.wherobots.com/develop/task-schedules/monitor-and-recover.md#who-can-manage-a-schedule) to use a service principal. Set **Notification email recipients** if needed; see [Failure emails](https://docs.wherobots.com/develop/task-schedules/monitor-and-recover.md#failure-emails) for fallbacks.
>
> ![Create a schedule form with Create paused checked, window-report as the name, My Files / window\_report.py as the script, --mode report as the arguments, the Micro runtime, and AWS US West (Oregon) as the region](https://mintcdn.com/wherobots/Me_qzO_IbxOjqKzV/images/develop/task-schedules/create-a-schedule/create-schedule-form.png?fit=max\&auto=format\&n=Me_qzO_IbxOjqKzV\&q=85\&s=f12750923adbe24620bced6e6c82c7d0)
>
> Create paused and the Script and compute settings for window-report
>
> Click **Create schedule**.
>
> **2. Run an explicit window**
>
> Choose **Run now**. Set **Window start** to 12:00 AM on October 25, 2026 and **Window end** to 12:00 AM on October 26, 2026, then click **Run now**. Before submitting, check [Run now and backfill](https://docs.wherobots.com/develop/task-schedules/scripts-and-windows.md#run-now-and-backfill) for the display time zone and manual-run behavior.
>
> ![Run now dialog for window-report with Window start 10/25/2026 12:00 AM and Window end 10/26/2026 12:00 AM](https://mintcdn.com/wherobots/Me_qzO_IbxOjqKzV/images/develop/task-schedules/create-a-schedule/run-now.png?fit=max\&auto=format\&n=Me_qzO_IbxOjqKzV\&q=85\&s=d9f7a7adcee167d612b01aa06c76f55d)
>
> Run now with a custom window
>
> **3. Verify the run and its logs**
>
> Find the run under **Run history**. When its outcome is **Succeeded**, click **Details ↗** to open its Job Run, then open the **Logs** tab and compare the output with the [verification checklist](#verify-your-schedule). If it fails, [inspect the outcome](https://docs.wherobots.com/develop/task-schedules/monitor-and-recover.md#read-run-history) before continuing.
>
> **4. Resume recurring execution**
>
> When the output is correct, click **Resume**, then **Resume schedule** to confirm. The schedule shows **Active**, and **Next run** shows its next scheduled time.
>
> ![window-report schedule marked Active with Next run Nov 4, 2026, 10:00 PM PST, and a Succeeded manual run for the Oct 25 to Oct 26 window in Run history](https://mintcdn.com/wherobots/Me_qzO_IbxOjqKzV/images/develop/task-schedules/create-a-schedule/active-schedule.png?fit=max\&auto=format\&n=Me_qzO_IbxOjqKzV\&q=85\&s=a7ef182c3f28dc8bd1083ca35bb1b4af)
>
> window-report after Resume, with the successful manual run in Run history

> [tab] REST API
>
> **1. Upload the script**
>
> Set these shell variables. The API requests use `X-API-Key`; the S3 upload must not carry it.
>
> ```bash theme={"system"}
> export WHEROBOTS_API_KEY="YOUR-API-KEY"
> export API="https://api.cloud.wherobots.com"
> REGION="aws-us-west-2"
>
> # Confirm that your Organization allows this Hosted region before continuing.
> curl -sSf -H "X-API-Key: $WHEROBOTS_API_KEY" "$API/organization" \
>   | jq -e --arg region "$REGION" 'any(.allowedRegions[]; . == $region)'
> ```
>
> This example uses `aws-us-west-2`, a Hosted region. The check confirms that your Organization can run there; if it fails, choose an allowed Hosted region before continuing. For a BYOC region, see [Store the script](#store-the-script).
>
> My Files uses the storage ID `user_files::<region>`. Paths are relative to your personal folder in that region, without a leading slash. Request a [file upload URL](https://docs.wherobots.com/api-reference/storage/create-a-presigned-file-upload-url.md):
>
> ```bash theme={"system"}
> STORAGE_ID="user_files::$REGION"
> SCRIPT_PATH="schedules/window_report.py"
> UPLOAD=$(curl -sSf -X POST -H "X-API-Key: $WHEROBOTS_API_KEY" \
>   "$API/storage/$STORAGE_ID/file-upload-url/$SCRIPT_PATH")
> UPLOAD_URL=$(printf '%s\n' "$UPLOAD" | jq -r '.uploadUrl')
> SCRIPT_URI=$(printf '%s\n' "$UPLOAD" | jq -r '.destination')
> curl -sSf -T window_report.py -H 'Content-Type:' "$UPLOAD_URL"
> ```
>
> `uploadUrl` accepts the raw file bytes in one `PUT` and expires after 15 minutes. `destination` is the resulting `s3://` URI; use it below rather than constructing a URI yourself.
>
> For shared Managed Storage, use `STORAGE_ID="default"` and `SCRIPT_PATH="data/shared/schedules/window_report.py"`. For an integration, use its `id` from `GET /storage` and a path relative to its root, or upload with your AWS tools and set `SCRIPT_URI` to the object's S3 URI.
>
> > [!WARNING]
> >
> > Send no Wherobots credentials to `uploadUrl`. Keep `-H 'Content-Type:'`: Managed Storage URLs sign an empty content type, and adding a nonempty value can cause `403`. Browsers and many HTTP libraries add a content type automatically, so suppress it explicitly. Do not use multipart form data. If you use `--data-binary`, suppress its default content type too.
>
> To retrieve the script later, see [Download a script safely](https://docs.wherobots.com/develop/storage-management/managed-storage.md#download-a-script-safely). For the My Files script uploaded above, use the same storage ID and relative path:
>
> ```bash theme={"system"}
> DOWNLOAD=$(curl -sSf -H "X-API-Key: $WHEROBOTS_API_KEY" \
>   "$API/storage/$STORAGE_ID/file-download-url/$SCRIPT_PATH")
> curl -sSf -o window_report.py "$(printf '%s\n' "$DOWNLOAD" | jq -r '.downloadUrl')"
> ```
>
> **2. Create a paused schedule**
>
> Create the schedule with `"paused": true`, so it doesn't run on its timer while you test it.
>
> ```bash theme={"system"}
> TRIGGER=$(jq -n --arg uri "$SCRIPT_URI" --arg region "$REGION" '{
>   name: "window-report",
>   task: {
>     definition: { scriptKind: "python", uri: $uri },
>     inputs: { runtime: "tiny", timeoutSeconds: 1800, args: ["--mode", "report"] }
>   },
>   region: $region,
>   triggerKind: "schedule",
>   triggerConfig: { cronExpression: "0 6 * * *", timezone: "UTC" },
>   paused: true
> }' | curl -sSf -X POST -H "X-API-Key: $WHEROBOTS_API_KEY" \
>   -H "Content-Type: application/json" --data-binary @- "$API/triggers")
>
> TRIGGER_ID=$(printf '%s\n' "$TRIGGER" | jq -r '.triggerId')
> printf '%s\n' "$TRIGGER" | jq '{triggerId, state, runAs, nextFireTime, version}'
> ```
>
> Every accepted request creates a new schedule. Requests aren't deduplicated, so don't retry a create blindly: list your schedules with `GET /triggers` first.
>
> **Request fields**
>
> | Field | Required | Description |
> | - | - | - |
> | `name` | Yes | A name for the schedule. |
> | `task.definition` | Yes | The script to run. `scriptKind` is `python` or `jar`, and `uri` is the script's `s3://` URI. A JAR can also set `mainClass`. |
> | `task.inputs` | No | Inputs that every task run uses. See [Script inputs](https://docs.wherobots.com/develop/task-schedules/scripts-and-windows.md#script-inputs). |
> | `triggerInputs` | No | Inputs that complete `task.inputs`. They may only set inputs that `task.inputs` leaves unset. |
> | `region` | Yes | The compute region the task runs in, from `allowedRegions` in `GET /organization`. |
> | `triggerKind` | Yes | Always `schedule`. |
> | `triggerConfig` | Yes | One schedule form: `{ "cronExpression": "...", "timezone": "..." }` with a five-field cron expression and an IANA timezone, or `{ "intervalMinutes": N }`. An interval of a day or more (1440 minutes or more) also needs a timezone-aware `startTime`. Runs must be at least 15 minutes apart. |
> | `runAs` | No | Your own user ID, or a service principal ID if you are an Organization administrator. Defaults to you. Another human cannot be assigned. |
> | `notificationEmails` | No | Email addresses for schedule notifications. |
> | `paused` | No | `true` creates the schedule paused. Defaults to `false`, which allows scheduled runs right away. |
>
> `windowStart`, `windowEnd` and `taskRunId` are reserved. A create or update request that sets them in `task.inputs` or `triggerInputs` returns `400`.
>
> The request returns `201`. The response includes the task's input schema in `task.inputSchema` and its unset inputs in `task.openProperties`.
>
> > [accordion] Example 201 response
> >
> > This response is abbreviated. IDs and the My Files URI are illustrative; your script URI is the upload response's `destination`.
> >
> > ```json theme={"system"}
> > {
> >   "triggerId": "t8wq3n5d2rkfja",
> >   "name": "window-report",
> >   "task": {
> >     "taskId": "k2m8q4v7c1x9za",
> >     "name": "window-report",
> >     "definition": {
> >       "scriptKind": "python",
> >       "uri": "s3://wbts-user-assets-aws-us-west-2-prod/your-org/user-files/your-user/schedules/window_report.py"
> >     },
> >     "inputs": {
> >       "runtime": "tiny",
> >       "timeoutSeconds": 1800,
> >       "args": [
> >         "--mode",
> >         "report"
> >       ]
> >     },
> >     "inputSchema": {
> >       "title": "ScriptInputs",
> >       "type": "object"
> >     },
> >     "openProperties": [
> >       "environment",
> >       "version"
> >     ]
> >   },
> >   "region": "aws-us-west-2",
> >   "triggerKind": "schedule",
> >   "triggerConfig": {
> >     "cronExpression": "0 6 * * *",
> >     "timezone": "UTC"
> >   },
> >   "runAs": "a1b2c3d4e5f6g7",
> >   "version": 1,
> >   "state": "PAUSED",
> >   "nextFireTime": null,
> >   "converged": false
> > }
> > ```
>
> See [Read schedule status](https://docs.wherobots.com/develop/task-schedules/monitor-and-recover.md#read-schedule-status) for the response fields used here.
>
> **3. Run an explicit window**
>
> Start one task run now, for a window you choose. This works while the schedule is paused.
>
> ```bash theme={"system"}
> TASK_RUN_ID=""
> if RUN_NOW=$(curl -sSf -X POST -H "X-API-Key: $WHEROBOTS_API_KEY" \
>   -H "Content-Type: application/json" \
>   -d '{"windowStart": "2026-10-25T00:00:00Z", "windowEnd": "2026-10-26T00:00:00Z"}' \
>   "$API/triggers/$TRIGGER_ID/run-now") &&
>   TASK_RUN_ID=$(printf '%s\n' "$RUN_NOW" | jq -er '.taskRunId // empty'); then
>   printf '%s\n' "$TASK_RUN_ID"
> else
>   printf '%s\n' 'Run-now request failed or returned no taskRunId; stop here and inspect run history before retrying.' >&2
> fi
> ```
>
> The request returns `202` with a new `taskRunId`. Each accepted request creates a separate run, so do not retry it blindly. See [Run now and backfill](https://docs.wherobots.com/develop/task-schedules/scripts-and-windows.md#run-now-and-backfill) for window requirements, including scripts that ignore their window.
>
> **4. Read the result and all log pages**
>
> Poll the task run until it leaves the active states `PENDING`, `SUBMITTED`, `RUNNING` and `CANCELLING`:
>
> ```bash theme={"system"}
> while [ -n "${TASK_RUN_ID:-}" ]; do
>   RUN=$(curl -sSf -H "X-API-Key: $WHEROBOTS_API_KEY" \
>     "$API/task-runs/$TASK_RUN_ID") || break
>   STATE=$(printf '%s\n' "$RUN" | jq -er '.state // empty') || { printf '%s\n' 'Could not read task-run state; stop and retry polling.' >&2; break; }
>   printf '%s\n' "$STATE"
>   case "$STATE" in PENDING|SUBMITTED|RUNNING|CANCELLING) sleep 15 ;; *) break ;; esac
> done
> ```
>
> If a request or response parse fails, the loop stops with an error; resolve it and resume polling before reading logs. An interruption is not a terminal run outcome.
>
> See [run states](https://docs.wherobots.com/develop/task-schedules/monitor-and-recover.md#read-run-history) to interpret the outcome. For the full schemas, see [Get a task run](https://docs.wherobots.com/api-reference/task-runs/get-a-task-run.md) and [Get task run logs](https://docs.wherobots.com/api-reference/task-runs/get-task-run-logs.md).
>
> Read every log page, passing `nextSince` unchanged as `since`. An empty page with a cursor is not the end; wait before polling again. Pages containing log lines are read without a delay. Even after the run finishes, keep polling while late log lines arrive; stop only when `nextSince` is null.
>
> ```bash theme={"system"}
> SINCE=""
> while :; do
>   if [ -z "$SINCE" ]; then
>     PAGE=$(curl -sSf -H "X-API-Key: $WHEROBOTS_API_KEY" \
>       "$API/task-runs/$TASK_RUN_ID/logs") || break
>   else
>     PAGE=$(curl -sSf -G -H "X-API-Key: $WHEROBOTS_API_KEY" \
>       --data-urlencode "since=$SINCE" "$API/task-runs/$TASK_RUN_ID/logs") || break
>   fi
>   printf '%s\n' "$PAGE" | jq -r '.items[].message'
>   SINCE=$(printf '%s\n' "$PAGE" | jq -r '.nextSince')
>   [ "$SINCE" = "null" ] && break
>   if [ "$(printf '%s\n' "$PAGE" | jq '.items | length')" -eq 0 ]; then
>     sleep 5
>   fi
> done
> ```
>
> A request error interrupts this loop; it does not mean the logs are complete. Resolve the error and rerun the `while` loop from the last `SINCE`, without resetting it. Compare the output with [Verify your schedule](#verify-your-schedule). To list scheduled and manual runs, use `GET /triggers/{triggerId}/runs`.
>
> **5. Resume recurring execution**
>
> When you're ready for the schedule to run on its timer, resume it with its current `version`:
>
> ```bash theme={"system"}
> VERSION=$(curl -sSf -H "X-API-Key: $WHEROBOTS_API_KEY" "$API/triggers/$TRIGGER_ID" | jq '.version')
> curl -sSf -X POST -H "X-API-Key: $WHEROBOTS_API_KEY" -H "Content-Type: application/json" \
>   -d "{\"version\": $VERSION}" "$API/triggers/$TRIGGER_ID/resume" | jq '{state, converged, nextFireTime}'
> ```
>
> The response shows `state` as `ACTIVE`. `nextFireTime` stays `null` until `converged` is `true`, so poll `GET /triggers/{triggerId}` to read it.
>
> If you receive `409`, follow [Handle version conflicts](https://docs.wherobots.com/develop/task-schedules/monitor-and-recover.md#handle-version-conflicts) instead of retrying unconditionally.

## Verify your schedule

Confirm that the manual run succeeded and the logs contain the bounds you submitted, in UTC, and `Arguments: ['--mode', 'report']`. The run ID differs for each attempt. For the REST API example, the output is:

```text theme={"system"}
Task run r4hz6m2pc9yvqe
Window [2026-10-25T00:00:00+00:00, 2026-10-26T00:00:00+00:00)
Arguments: ['--mode', 'report']
```

A Cloud UI run shows bounds converted from your Settings time zone: for example, 12:00 AM PDT on October 25 appears as `2026-10-25T07:00:00+00:00`.

After resuming, confirm `ACTIVE` and a next scheduled time. Allow time for the scheduler to apply the configuration; see [Read schedule status](https://docs.wherobots.com/develop/task-schedules/monitor-and-recover.md#read-schedule-status). Check the next scheduled run in history to verify recurring execution.

## Next steps

> [card] [Scripts and processing windows](https://docs.wherobots.com/develop/task-schedules/scripts-and-windows.md)
>
> Configure runtime inputs and window-based processing.

> [card] [Monitor and recover](https://docs.wherobots.com/develop/task-schedules/monitor-and-recover.md)
>
> Manage access, pause or archive schedules, and recover windows.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.