CLI
scheduler0 is a command-line client for the Scheduler0 API, written in Go and distributed as a single binary for macOS, Linux and Windows. Source: github.com/scheduler0/scheduler0-cli.
Installation
Install script (macOS, Linux)
Detects OS and architecture, downloads the latest release, verifies its SHA-256 checksum and installs to /usr/local/bin (using sudo if needed):
curl -fsSL https://raw.githubusercontent.com/scheduler0/scheduler0-cli/main/install.sh | bash
Pin a version or change the install directory:
curl -fsSL https://raw.githubusercontent.com/scheduler0/scheduler0-cli/main/install.sh | VERSION=0.0.4 bash
curl -fsSL https://raw.githubusercontent.com/scheduler0/scheduler0-cli/main/install.sh | INSTALL_DIR=$HOME/.local/bin bash
Release binaries
Archives for every release are on the Releases page, named scheduler0_<version>_<os>_<arch>.tar.gz (.zip on Windows), alongside a checksums.txt with SHA-256 sums.
curl -fsSL -o scheduler0.tar.gz \
https://github.com/scheduler0/scheduler0-cli/releases/latest/download/scheduler0_0.0.4_darwin_arm64.tar.gz
tar -xzf scheduler0.tar.gz
sudo mv scheduler0 /usr/local/bin/
scheduler0 --version
Replace darwin_arm64 with darwin_amd64, linux_amd64 or linux_arm64 as needed. On Windows, extract scheduler0.exe from the .zip and put it on your PATH.
If macOS Gatekeeper blocks the binary: xattr -d com.apple.quarantine /usr/local/bin/scheduler0.
Docker
Multi-arch images (linux/amd64, linux/arm64) are pushed to Amazon ECR Public on every release, tagged latest and <version>. The image is public.ecr.aws/p2b2u7y5/scheduler0-cli; scheduler0 is the entrypoint, so pass the subcommand as the container arguments.
docker run --rm \
-v "$HOME/.scheduler0:/root/.scheduler0" \
public.ecr.aws/p2b2u7y5/scheduler0-cli:latest \
projects list
Mount ~/.scheduler0 so the container can read the session written by scheduler0 login, or pass SCHEDULER0_* environment variables instead (see CI).
From source
git clone https://github.com/scheduler0/scheduler0-cli.git
cd scheduler0-cli
go build -o scheduler0 .
sudo mv scheduler0 /usr/local/bin/
Sign in
scheduler0 login
Opens app.scheduler0.com in your browser, where you approve access with your dashboard session. A short-lived credential is written to ~/.scheduler0/config.json and used for all subsequent commands. Re-run login when it expires; scheduler0 logout removes it.
Your signed-in identity is used for createdBy/modifiedBy/deletedBy/archivedBy; there are no --created-by flags.
Point at another environment with --app-url and --base-url:
scheduler0 login --app-url https://app.staging.scheduler0.com --base-url https://api.staging.scheduler0.com
Headless / SSH
The default flow receives the authorization code on a loopback URL (http://127.0.0.1:<port>/callback), which cannot work when the browser runs on a different machine. In an SSH session without a display the CLI switches automatically to a device-code flow: it prints a one-time code and a URL (https://app.scheduler0.com/cli/device); open the URL on any device, enter the code, and the CLI polls until approved. Force it with --device (alias --no-browser).
Authenticating in CI
login is interactive, so in CI set environment variables instead. When SCHEDULER0_API_KEY, SCHEDULER0_API_SECRET and SCHEDULER0_ACCOUNT_ID are present they take priority over any on-disk session.
| Variable | Required | Purpose |
|---|---|---|
SCHEDULER0_API_KEY | yes | Credential API key |
SCHEDULER0_API_SECRET | yes | Credential secret (plaintextSecret shown once at creation) |
SCHEDULER0_ACCOUNT_ID | yes | Account the credential belongs to |
SCHEDULER0_BASE_URL | no | Defaults to https://api.scheduler0.com |
SCHEDULER0_ACTOR | for write commands | Value recorded as createdBy/modifiedBy/deletedBy/archivedBy, e.g. github-actions |
SCHEDULER0_EXPIRES_AT | no | RFC3339 expiry for an early local check; otherwise the server rejects an expired credential with 401 |
Mint the credential once, interactively:
scheduler0 credentials create --scopes read,write,execute
# save apiKey → SCHEDULER0_API_KEY, plaintextSecret → SCHEDULER0_API_SECRET
Credentials expire after 90 days by default; create a new one and credentials archive the old one before then.
Global flags
--base-url and --account-id are accepted by every command and override the stored session for that invocation.
Commands
Output is JSON unless noted. <id> arguments are positional.
Projects
scheduler0 projects list [--limit 10] [--offset 0] [--order-by date_created] [--order-direction desc]
scheduler0 projects get <project-id>
scheduler0 projects create --name "Billing" --description "Invoice jobs" # both required
scheduler0 projects update <project-id> --description "New description" # name cannot be changed
scheduler0 projects delete <project-id>
Jobs
scheduler0 jobs list [--project-id <id>] [--limit 10] [--offset 0] [--order-by date_created] [--order-direction desc]
scheduler0 jobs get <job-id>
scheduler0 jobs create \
--project-id 123 \
--executor-id 456 \
--timezone "UTC" \
--spec "0 30 * * * *" \
--data '{"key": "value"}' \
[--start-date <RFC3339>] [--end-date <RFC3339>] [--retry-max 3] [--status active]
scheduler0 jobs update <job-id> --status inactive # pause
scheduler0 jobs update <job-id> --status active # resume
scheduler0 jobs delete <job-id>
--spec is a six-field cron expression (seconds first; see Jobs). For a one-time job omit --spec and pass --start-date. jobs create returns the async task id; check it with scheduler0 async-tasks get <request-id>.
Executors
scheduler0 executors list [--limit 10] [--offset 0] [--order-by date_created] [--order-direction desc]
scheduler0 executors get <executor-id>
scheduler0 executors create \
--name "orders-webhook" \
--type webhook_url \
--webhook-url "https://example.com/hooks/orders" \
--webhook-method POST \
--webhook-secret "..." \
[--description "Sends order emails"] [--tags email,orders] [--payload-aggregation]
scheduler0 executors create \
--name "reports-lambda" \
--type cloud_function \
--cloud-provider aws \
--region us-east-1 \
--cloud-resource-url "arn:aws:lambda:us-east-1:123456789012:function:reports" \
--cloud-api-key "AKIA..." \
--cloud-api-secret "..."
scheduler0 executors update <executor-id> --name "new-name" --type webhook_url # name and type are required on update
scheduler0 executors delete <executor-id>
scheduler0 executors test-invoke <executor-id> [--age 24h] [--execution-time <RFC3339>]
--type accepts webhook_url or cloud_function; local executors are created with local-executor register below. --description and --tags are what scheduler0 schedule uses to pick an executor.
Local executor
Runs jobs as a shell command on a machine you control. The CLI pulls jobs assigned to the executor, runs the command, and reports results back. Details of the protocol are in Executors → Local executors.
# 1. Sign in
scheduler0 login
# 2. Register (creates the executor on the server and saves it to ~/.scheduler0/config.json)
scheduler0 local-executor register \
--name "data-processor" \
--command "/home/deploy/scripts/process.sh" \
[--working-dir /home/deploy]
# 3. Assign jobs to the executor id printed above (dashboard, API or `jobs create --executor-id`)
# 4. Run the worker in the foreground
scheduler0 local-executor start [--poll-interval 1m]
Per job, the command receives the job's data on stdin and these environment variables:
| Variable | Value |
|---|---|
SCHEDULER0_JOB_ID | Job id |
SCHEDULER0_JOB_DATA | Raw data field |
SCHEDULER0_JOB_SPEC | Cron spec |
SCHEDULER0_EXEC_UNIQUE_ID | Unique id of this execution |
A non-zero exit status marks the execution failed. Results are cached in ~/.scheduler0/local-executor.db and reported in batches: every 5 minutes, or immediately once 50 unreported results accumulate. If the server is unreachable, cached jobs keep running and results are flushed when connectivity returns. Ctrl+C stops the worker gracefully.
Executions
scheduler0 executions [--start-date <RFC3339>] [--end-date <RFC3339>] [--project-id <id>] [--job-id <id>] [--limit 10] [--offset 0]
scheduler0 executions analytics --start-date 2026-01-01 --start-time 12:00 # UTC; 12-hour window, hourly buckets
scheduler0 executions totals
scheduler0 executions cleanup-old-logs --account-id <id> --retention-months 6
Credentials
scheduler0 credentials list [--limit 10] [--offset 0] [--order-by date_created] [--order-direction desc] [--output table]
scheduler0 credentials get <credential-id>
scheduler0 credentials create [--scopes read,write,execute] # prints apiKey and plaintextSecret once
scheduler0 credentials update <credential-id> [--archived] # PUT: only the archived flag can change; omitting it un-archives
scheduler0 credentials archive <credential-id>
scheduler0 credentials delete <credential-id>
--scopes defaults to read,write,execute; admin can only be granted by an admin-scoped session. apiKey, apiSecret, scopes and expiresAt cannot be changed after creation; to replace a key, credentials create then credentials archive the old one.
AI
# Propose job configurations (POST /ai/prompt)
scheduler0 prompt --prompt "Send weekly reports every Monday at 9 AM" \
[--purposes reporting] [--events weekly_cycle] [--recipients team@example.com] [--channels email] \
[--timezone America/New_York] [--locale en-US]
# Intent guardrail only (POST /ai/prompt/classify); English-only --locale
scheduler0 prompt classify --prompt "What is Kubernetes?"
# Full pipeline: create project, match executor, create jobs (POST /ai/schedule)
scheduler0 schedule --prompt "Remind the sales team every Monday at 9am" \
[--channels email] [--purposes sales] [--timezone America/New_York] [--locale en] \
[--project-id <id>] [--executor-id <id>]
# Conversation and send-time suggestions; body from --file or stdin
scheduler0 suggestions analyze --file conversation.json
scheduler0 suggestions time --file send-time-request.json
# Prompt request log (GET /ai/prompt-requests)
scheduler0 ai-prompt-requests [--provider openai] [--model ...] [--status success] [--search ...] \
[--start-date <RFC3339>] [--end-date <RFC3339>] [--order ASC|DESC] [--limit 25] [--offset 0]
# Model catalog and BYOK settings
scheduler0 ai-models # GET /ai/models (also: scheduler0 ai-settings models)
scheduler0 ai-settings get # keys redacted
scheduler0 ai-settings upsert \
--active-models '[{"provider":"openai","model":"gpt-4.1-mini"},{"provider":"anthropic","model":"claude-sonnet-4-5"}]' \
--openai-api-key "sk-..." --anthropic-api-key "sk-ant-..." \
[--openrouter-api-key ...] [--bedrock-access-key-id ...] [--bedrock-secret-key ...] [--bedrock-region ...]
Prompt limits (160 characters; arrays of at most 5 items × 36 characters), quotas, credits and response shapes are described in AI Job Scheduling. schedule returns 409 when no executor can be matched.
Async tasks, features, health
scheduler0 async-tasks get <request-id> # blocks until the task finishes
scheduler0 features # GET /features
scheduler0 healthcheck # no auth required
Operator commands (self-hosted)
These call endpoints that require the admin scope; sign in with an admin credential or use SCHEDULER0_* variables for one.
# Accounts
scheduler0 accounts create --name "Team A"
scheduler0 accounts get <account-id>
scheduler0 accounts update <account-id> --name "Team B"
scheduler0 accounts execution-count get <account-id>
scheduler0 accounts execution-count increase <account-id> --count 1000
scheduler0 accounts ai-usage <account-id>
scheduler0 accounts feature add <account-id> --feature-id 3
scheduler0 accounts feature remove <account-id> --feature-id 3
scheduler0 accounts features-all add <account-id>
scheduler0 accounts features-all remove <account-id>
scheduler0 accounts tokens get <account-id>
scheduler0 accounts tokens add <account-id> --amount 500
# Server secret-key rotation (see Account Management)
scheduler0 accounts generate-secret-key [--count 1] # offline; prints random AES-256 keys
scheduler0 accounts rotate-secret --old-secret-key <OLD_KEY> # POST /account/rotate-secret
# Backup and restore
scheduler0 backup start
scheduler0 backup restore <file-name>
# Cluster
scheduler0 cluster list-nodes
scheduler0 cluster add-node --node-id 3 --node-address host:7071 --client-address host:9091
scheduler0 cluster remove-node --node-id 3
scheduler0 cluster promote-node --node-id 3
scheduler0 cluster demote-node --node-id 3
scheduler0 cluster transfer-leadership
scheduler0 cluster add-self | remove-self | reset-raft
scheduler0 cluster force-rebuild --seed-node-id 1
scheduler0 cluster dump | schedule-queue | job-executions-cache | job-queues | job-queue-versions
See Account Management, Backup and Restore and Cluster Management for what each endpoint does.
Configuration file
~/.scheduler0/config.json, written by login and local-executor register:
{
"base_url": "https://api.scheduler0.com",
"app_url": "https://app.scheduler0.com",
"api_key": "…",
"api_secret": "…",
"account_id": "…",
"clerk_user_id": "user_…",
"expires_at": "2026-07-07T12:00:00Z",
"scopes": ["read", "write", "execute"],
"local_executor": {
"id": "42",
"name": "data-processor",
"command": "/home/deploy/scripts/process.sh",
"working_dir": "/home/deploy"
}
}
local_executor is present only after local-executor register.