FAQ
General
What is Scheduler0?
A distributed cron scheduler exposed as an HTTP API. You define jobs (a cron spec or a one-time start date plus a payload) and executors (a webhook, a cloud function, or a command run by the CLI on your own machine); Scheduler0 fires the jobs, records every execution, and retries failures. It runs as a Raft cluster with an embedded SQLite database, so it has no dependency on a particular cloud provider.
Hosted or self-hosted?
Both. The hosted service is at app.scheduler0.com with the API at https://api.scheduler0.com. Self-hosting (running the server binary on your own infrastructure) is available on the Paid and Teams plans. The API is identical; the self-hosted server additionally exposes the operator endpoints described in Account Management, Cluster Management and Backup and Restore.
How are resources organised?
Account
├── Credentials API key/secret pairs with scopes
├── Projects groups of jobs
│ └── Jobs cron spec or one-time date + payload + executor
├── Executors how a job is delivered
└── Executions one record per job run
Authentication
Which headers do I send?
X-API-Key, X-Secret-Key and X-Account-ID on every request except GET /healthcheck. Missing or invalid values return 401. See Credentials.
What do the scopes mean?
read for GET, write for creating/updating/deleting resources, execute for the AI endpoints, local-executor result reporting and execution-log cleanup, admin for operator endpoints (and it implies the other three). The wrong scope returns 403 credential missing required scope: <scope>. Full table in Credentials → Scopes.
When do credentials expire, and how do I rotate one?
By default 90 days after creation; expiresInSeconds on POST /credentials can shorten this (minimum 5 minutes, maximum 90 days). An expired credential returns 401 credential expired, and the server archives expired credentials automatically. To rotate: create a new credential, deploy its apiKey/plaintextSecret, then POST /credentials/{oldId}/archive (CLI: scheduler0 credentials create and scheduler0 credentials archive <id>). There is no rotate endpoint. See Credentials → Rotate.
What is Basic Auth with X-Peer for?
Self-hosted operators can call the operator endpoints (/accounts/*, /account/rotate-secret, /cluster/*) with the server's username/password and X-Peer: cmd instead of an admin credential. GET /features and GET /async-tasks/{id} are ordinary read-scope endpoints, not operator endpoints.
Scheduling
What cron format is used?
Six fields with a leading seconds field (sec min hour dom month dow), plus @hourly-style descriptors and @every <duration>. A standard five-field expression is accepted but misread. See Jobs → Cron format.
How do time zones work?
Set timezone to an IANA name such as America/New_York; it is required (UTC is fine, Local is rejected). timezoneOffset is stored but not used for scheduling.
Can I schedule a one-time job?
Yes: omit spec and set startDate (which must be in the future). See Jobs → One-time jobs.
Can I pause a job?
Yes. PUT /jobs/{id} with "status": "inactive" pauses, "active" resumes. PUT replaces the job, so send the other fields you want to keep along with modifiedBy. See Jobs → Pause and resume.
Can I change a job's schedule?
No. spec and projectId are not updated by PUT /jobs/{id}. Delete the job and create a new one.
Why did POST /jobs return 202?
Job creation is validated synchronously (400 on error) and then inserted asynchronously. The 202 body is the request id and the Location header is /async-tasks/<id>; GET /api/v1/async-tasks/<id> blocks until the task finishes and returns the created jobs in output. See Jobs → Check the result.
Execution
How do retries work?
A failed invocation (webhook status ≥ 400, cloud function error, non-zero exit for local executors) is retried up to retryMax times; each attempt is its own execution record with state 2. retryMax is capped at 3, or 15 with the increased_retry_max_by_five feature. retryMax: 0 disables retries.
What are the execution states?
0 scheduled, 1 success, 2 failed. The list filter uses the words (state=failed); the response field is the integer.
How long are execution logs kept?
There is no automatic retention sweep in the current server. Delete old logs with POST /executions/cleanup-old-logs (execute scope, retentionMonths > 0). See Executions.
Is there an analytics endpoint?
GET /executions/analytics returns hourly scheduled/success/failed counts for a 12-hour window around a given UTC date and time; GET /executions/totals returns lifetime totals. See Executions.
AI
What do the AI endpoints do?
POST /ai/prompt turns a sentence into proposed job configurations; POST /ai/schedule also creates the project, picks an executor and creates the jobs; POST /ai/prompt/classify only runs the intent guardrail; POST /ai/suggestions/analyze extracts commitments and deadlines from a conversation; POST /ai/suggestions/time recommends send times. All require the execute scope. See AI Job Scheduling and Conversation Suggestions.
What limits apply to AI calls?
Two monthly quotas of 1,000 requests each (prompt: /ai/prompt + /ai/schedule; classify: /ai/prompt/classify + /ai/suggestions/analyze), raised to 100,000 by account features; exhaustion returns 429 with X-RateLimit-* headers. On the hosted service, prompt and schedule runs on the platform model are also billed from a dollar credit balance and return 402 when it is empty. Runs on your own provider key do not use credits. See Quotas and credits.
Can I use my own model?
Yes. PUT /ai/settings stores provider keys (OpenAI, Anthropic, AWS Bedrock, OpenRouter) and an ordered list of active_models; the first is primary, the rest are fallbacks. Only models listed by GET /ai/models are accepted. See BYOK.
Limits
| Limit | Default | With account feature |
|---|---|---|
| Executions per month | 1,000 | 100,000 (increased_number_of_job_executions_100_k_per_month) |
| AI prompt requests per month | 1,000 | 100,000 (increased_number_of_prompt_requests_100_k_per_month) |
| AI classify requests per month | 1,000 | 100,000 (increased_number_of_classify_requests_100_k_per_month) |
Job data size | 3,072 bytes | 1 MB (increased_job_payload_size_to_1mb) |
retryMax | 3 | 15 (increased_retry_max_by_five) |
Jobs per POST /jobs | 100 | — |
limit on list endpoints | default 10, max 100 (429 above) | — |
On the hosted service the features are granted by plan. GET /executions has a default limit of 50 and no maximum. When the monthly execution allowance is exhausted the account's jobs are set to inactive; see Account Management → Execution count.
Clients
| Client | Install |
|---|---|
| Go | go get github.com/scheduler0/scheduler0-go-client/v2 |
| Node.js / TypeScript | npm install @scheduler0/scheduler0-node-client |
| Python | pip install scheduler0 |
| CLI | Install |
| OpenAPI | api-reference.scheduler0.com |
The CLI is the supported way to run a local executor; the underlying endpoints are documented under Executors → Local executors if you want to build your own runner.
Self-hosting
What does the server store data in?
SQLite, replicated between nodes with Raft. Node discovery uses etcd.
Which endpoints need the admin scope?
/accounts/*, POST /account/rotate-secret and /cluster/* (including backup and restore). Everything else uses read/write/execute.
How do backups work?
POST /cluster/backup snapshots the receiving node's database with SQLite's online backup API and uploads it to the configured S3 bucket; POST /cluster/restore replaces the database from a backup (taking a .pre-restore-backup copy first). Both are asynchronous. See Backup and Restore.