Executors
An executor defines how a job runs when it fires. A job points at one executor; an executor can serve many jobs.
type | Runs the job by | Needs a public endpoint? |
|---|---|---|
webhook_url | Sending an HTTP request to webhookUrl | Yes |
cloud_function | Invoking an AWS Lambda, Azure Function or Google Cloud Function | Yes |
local | The scheduler0 CLI on your machine pulling jobs and running command | No |
Executor object
{
"id": 1,
"accountId": 123,
"name": "Production Webhook",
"description": "Sends transactional email to customers",
"tags": ["email", "notifications"],
"type": "webhook_url",
"webhookUrl": "https://api.example.com/webhook",
"webhookMethod": "POST",
"payloadAggregation": false,
"dateCreated": "2026-01-15T10:30:00Z",
"createdBy": "user123"
}
| Field | Description |
|---|---|
name | Required. |
type | Required: webhook_url, cloud_function or local. |
description, tags | Optional free text / labels. POST /ai/schedule uses them to pick the executor that matches a prompt. |
webhookUrl, webhookMethod | Required for webhook_url. Method is GET, POST, PUT or DELETE. |
webhookSecret | Optional. Sent to your endpoint as the X-Webhook-Secret header. |
cloudProvider | aws, azure or gcp for cloud_function. |
cloudResourceUrl | Lambda function ARN, or the Azure/GCP function URL. |
region | AWS region (Lambda only). |
cloudApiKey, cloudApiSecret | AWS access key ID / secret access key for Lambda; cloudApiKey is the function key (x-functions-key) for Azure. Not used for GCP, whose function URL must accept unauthenticated POSTs. |
command, workingDir | For local: the shell command to run for each job and the directory to run it from. command is required. |
payloadAggregation | When true, jobs on this executor that fire at the same time are delivered in one call. |
webhookSecret, cloudApiKey and cloudApiSecret are write-only: they are stored encrypted and appear in exactly one response, the 201 from POST /executors. GET, list and PUT responses omit them.
Endpoints
| Method | Path | Scope | Notes |
|---|---|---|---|
GET | /executors | read | Paginated; orderBy: id, date_created, date_modified, created_by, modified_by, deleted_by |
POST | /executors | write | 201, includes secrets once |
GET | /executors/{id} | read | |
PUT | /executors/{id} | write | Same fields as create plus modifiedBy |
DELETE | /executors/{id} | write | Body {deletedBy} → 204 |
POST | /executors/{id}/test-invoke | execute | Fire a synthetic job now; not for local |
POST | /local-executors | write | Register a local executor → 201 {id} |
GET | /local-executors/{id}/jobs | read | Used by the CLI to pull jobs |
POST | /local-executors/{id}/executions | execute | Used by the CLI to report results |
Payload delivered to your code
Webhook and cloud-function executors receive the job wrapped in an envelope with its last execution status:
{
"job": {
"id": 123,
"projectId": 456,
"executorId": 789,
"spec": "0 0 2 * * *",
"data": "{\"action\": \"process_data\"}",
"timezone": "UTC",
"retryMax": 3,
"status": "active",
"accountId": 1,
"dateCreated": "2026-01-15T10:30:00Z",
"createdBy": "user123"
},
"lastExecutionDateTime": "2026-01-14T02:00:00Z",
"lastExecutionStatus": "success"
}
data is the JSON string you stored on the job; parse it in your handler.
With payloadAggregation: true, when more than one job on the executor fires at the same scheduled time they arrive in a single call. The shape is distinguishable by the top-level aggregated key, and all jobs in the batch succeed or fail (and retry) together:
{
"aggregated": true,
"jobs": [
{ "job": { "id": 123 }, "lastExecutionDateTime": "...", "lastExecutionStatus": "success" },
{ "job": { "id": 124 }, "lastExecutionDateTime": "...", "lastExecutionStatus": "success" }
]
}
Delivery rules:
- Webhook: request uses
webhookMethod,Content-Type: application/json, andX-Webhook-Secretwhen a secret is set. Any response status>= 400counts as a failure and triggers the job's retry policy. - AWS Lambda: invoked with the AWS SDK using
region,cloudApiKey(access key ID) andcloudApiSecret(secret access key);cloudResourceUrlis the function ARN. A function error is a failure. - Azure Functions:
POSTtocloudResourceUrlwithx-functions-key: <cloudApiKey>. - Google Cloud Functions:
POSTtocloudResourceUrl, no credentials attached.
Create a webhook executor
curl -X POST "https://api.scheduler0.com/api/v1/executors" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID" \
-d '{
"name": "Production Webhook",
"description": "Sends transactional email to customers",
"tags": ["email", "notifications"],
"type": "webhook_url",
"webhookUrl": "https://api.example.com/webhook",
"webhookMethod": "POST",
"webhookSecret": "secret_key_123",
"createdBy": "user123"
}'
The 201 response is the executor object including webhookSecret; this is the only time it is returned.
Create an AWS Lambda executor
curl -X POST "https://api.scheduler0.com/api/v1/executors" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID" \
-d '{
"name": "Data Processing Lambda",
"type": "cloud_function",
"cloudProvider": "aws",
"region": "us-east-1",
"cloudResourceUrl": "arn:aws:lambda:us-east-1:123456789012:function:data-processor",
"cloudApiKey": "AKIAIOSFODNN7EXAMPLE",
"cloudApiSecret": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"createdBy": "user123"
}'
For Azure use "cloudProvider": "azure", the function URL in cloudResourceUrl and the function key in cloudApiKey. For GCP use "cloudProvider": "gcp" and the function URL in cloudResourceUrl.
Local executors
A local executor runs jobs as a shell command on a machine you control, with no inbound network access required. The scheduler0 CLI polls the API for the jobs assigned to the executor, runs command for each trigger, and reports results back in batches. It keeps a local SQLite cache so it can keep executing when the server is unreachable and flush results later.
Register one with the dedicated endpoint (the CLI's scheduler0 local-executor register does this for you). name, command and createdBy are required; type is set to local by the server.
curl -X POST "https://api.scheduler0.com/api/v1/local-executors" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID" \
-d '{
"name": "My Local Executor",
"command": "/usr/local/bin/process-job.sh",
"workingDir": "/home/deploy/myapp",
"createdBy": "user123"
}'
{ "success": true, "data": { "id": 42 } }
Assign jobs to it with "executorId": 42, then run scheduler0 local-executor start on the machine. For each trigger the CLI runs command with:
- the job's
datastring on stdin, and - environment variables
SCHEDULER0_JOB_ID,SCHEDULER0_JOB_DATA(same string as stdin),SCHEDULER0_JOB_SPECandSCHEDULER0_EXEC_UNIQUE_ID.
A non-zero exit status marks the execution failed. See CLI — Local executor for the setup walkthrough.
The two CLI-facing endpoints, for anyone building their own runner:
GET /local-executors/{id}/jobs→data: Job[], the active jobs assigned to the executor.400if the executor is not of typelocal.POST /local-executors/{id}/executionswith a JSON array of{ "jobId", "uniqueId", "state", "lastExecutionTime", "nextExecutionTime", "executionVersion", "jobQueueVersion" }wherestateis0scheduled,1success,2failed →200 { "committed": N }. Reported executions count toward the account's execution quota.
List, get, update, delete
curl "https://api.scheduler0.com/api/v1/executors?limit=10&offset=0" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID"
The list response is { "total", "offset", "limit", "executors": [...] }; fields that are zero or empty are omitted.
curl -X PUT "https://api.scheduler0.com/api/v1/executors/1" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID" \
-d '{
"name": "Production Webhook",
"type": "webhook_url",
"webhookUrl": "https://new-api.example.com/webhook",
"webhookMethod": "POST",
"modifiedBy": "user123"
}'
curl -X DELETE "https://api.scheduler0.com/api/v1/executors/1" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID" \
-d '{"deletedBy": "user123"}'
Test-invoke an executor
POST /executors/{id}/test-invoke sends a synthetic job through the executor immediately and synchronously, so you can check that your endpoint or function receives the payload. Nothing is created, scheduled or logged. Local executors return 400.
The body is optional:
| Field | Description |
|---|---|
job | Job attributes to place in the payload (spec, data, timezone, retryMax, ...). Server-managed fields are overridden. |
age | Go duration string such as "24h"; sets the synthetic job's dateCreated/lastExecutionDate to now - age. |
executionTime | RFC3339 time used as the payload's lastExecutionDateTime. Defaults to now. |
curl -X POST "https://api.scheduler0.com/api/v1/executors/1/test-invoke" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID" \
-d '{
"job": { "spec": "0 0 2 * * *", "data": "{\"action\": \"process_data\"}", "timezone": "UTC" },
"age": "24h"
}'
The HTTP status is 200 whenever the invocation ran; whether the target accepted it is in data.success:
{
"success": true,
"data": {
"test": true,
"executorId": 1,
"executorType": "webhook_url",
"success": true,
"startedAt": "2026-01-15T02:00:00Z",
"finishedAt": "2026-01-15T02:00:00.142Z",
"durationMs": 142,
"payload": {
"job": { "spec": "0 0 2 * * *", "data": "{\"action\": \"process_data\"}", "timezone": "UTC", "executorId": 1, "status": "active" },
"lastExecutionDateTime": "2026-01-15T02:00:00Z",
"lastExecutionStatus": "scheduled"
}
}
}
On failure data.success is false and data.error explains why.
Related
- Jobs — assigning jobs to executors, retries
- CLI — Local executor
- AI Job Scheduling — how
description/tagsdrive executor matching