Skip to content

Job Fields

A job is one row in the registry. Fields are grouped by what they govern; a job only reads the group matching its job_type and schedule_type.

PUT expects the full object — there are no partial updates. GET the current row, change what you mean to, and send the whole thing back.

Field Type Default Notes
id uuid server-assigned
name string — Required.
group_name string "" Optional. Groups can be enabled/disabled together, and a disabled group gates its jobs. A / nests groups, up to 16 levels: Backups/Nightly appears as Nightly inside Backups. Limited to 255 bytes and 16 levels deep — the name is normalized first (each /-separated segment is trimmed of whitespace and blank segments are dropped), and the limits apply to the normalized name.
enabled bool true Checked both by the scheduler and again by the worker at execution time.
created_at / updated_at timestamp server-managed
Field Type Default Notes
schedule_type enum cron cron, rate, one_off, or webhook.
cron_expression string — Standard 5-field cron (no seconds) or a descriptor. For rate, must match @every <duration>. Validated at write time. Ignored for one_off and webhook.
run_at timestamp — one_off only. Must be in the future until fired, then immutable.
run_at_fired_at timestamp server-managed null until the one-off run has been inserted. Ignored on write. Cleared if schedule_type changes away from one_off.
one_off_after_run enum keep keep disables the row and leaves it; delete removes it. Unknown values fall through to keep.
webhook_token_hint string server-managed First 8 characters of the job’s webhook token, for identification. Set only when schedule_type is webhook; ignored on write.
webhook_token_rotated_at timestamp server-managed When the current webhook token was issued. Ignored on write.

A webhook token’s plaintext is shown exactly once — in the response to the create or update that set schedule_type to webhook, or from POST /jobs/{id}/webhook/rotate. Only a sha256 digest is stored, and it is never returned by the API, so the token cannot be recovered afterwards; webhook_token_hint is what identifies it from then on.

See Scheduling.

Field Type Default Notes
job_type enum http http, bash, javascript, typescript, groovy. Immutable after creation — changing it returns 409.

These fields are also the columns of a CSV import file — the same immutability, validation, and script-permission rules apply to an imported row.

Must all be empty on a script job.

Field Type Default Notes
http_method string GET
url string — Required for HTTP jobs. Supports {{variable}}.
query_params object {} Substitution applies to keys and values. Overrides same-named params already in the URL.
body raw JSON — Sent verbatim. Substitution applied to the whole serialized body.
headers object {} Keys and values substituted. Content-Type: application/json is added only if a body exists and you didn’t set one.
auth_type enum none none, bearer, basic, api_key.
auth_config object {} Shape depends on auth_type; see below. Every field is substituted.
auth_type Shape Validation
bearer {"token": "..."} token must be non-empty.
basic {"username": "...", "password": "..."} username must be non-empty; empty password allowed.
api_key {"api_key": "...", "header_name": "..."} api_key must be non-empty; header_name defaults to X-API-Key.

Unknown keys in auth_config are ignored.

token, password, and api_key are encrypted at rest (see Environment Variables) and are never returned by the admin API. A GET/list response’s auth_config carries only the non-secret fields (username, header_name); a has_auth_secret: true/false field alongside it says whether a secret is currently set. Sending an empty string for the secret field on PUT keeps the existing value rather than clearing it — send a non-empty value to change it. This does not apply to create, where there is no existing value to keep: an empty secret there just means no secret.

Must both be empty on an HTTP job.

Field Type Default Notes
script_body string — The whole script, written to a temp file at run time. Used verbatim: {{variable}} is not substituted; read variables from GLOBALVAR_* environment variables instead.
script_args string[] [] Appended after the script path. Each element is passed as written, with no {{variable}} substitution.
script_secret_vars string[] [] Names of secret global variables this job is allowed to receive as GLOBALVAR_<NAME>. A named variable is injected only when it also has allow_in_scripts set on the global variable itself — see below.
capabilities object {} What the script may reach. Shape: {"volumes": [{"name": "reports", "mode": "rw"}]}. mode is ro or rw and cannot exceed the volume’s max_mode; each volume appears once; every volume must exist under Settings → Script volumes. {} and {"volumes": []} mean none. Unknown keys are rejected, and egress is reserved and rejected as not yet supported. Re-checked against the current volume list on every attempt.

Requires the jobs:script permission. See Script jobs.

capabilities must be empty on an HTTP job (400 otherwise). See Volumes.

Responses include a read-only capabilities_invalid boolean. When it is true, the stored volume declaration cannot be read and capabilities shows {}. The job is cancelled each time it would run, rather than running without its volumes. The job list shows a “volumes unreadable” tag, and the edit form warns that saving replaces the declaration.

Setting script_secret_vars requires both jobs:script and globalvars:write — distinct from turning allow_in_scripts on for a global variable itself, which needs only globalvars:write. See Opting a secret variable into a script’s environment.

Field Type Default Constraints
exec_timeout_ms int 60000 > 0 and ≤ 1800000 (30 min). Groovy jobs have a minimum of 10000. Bounds one attempt.
pool_count int 1 1 … 20. Must be 1 for one_off. Fans one firing into that many parallel, independently locked/looped/retried calls.
overlap_policy enum skip skip takes a per-pool-index lock and drops a firing for an index still running; allow never locks.
Field Type Default Notes
loop_enabled bool false
loop_response_path string result.remaining gjson path, evaluated against the response body (HTTP) or stdout (script). Continues only on a strictly positive number.
loop_delay_ms int 1000 Flat delay before the next iteration.
loop_jitter_ms int 0 Random 0–N ms added to the delay. No applied default — 0 means no jitter.
loop_max_iterations int 50 Bounds one logical run; resets on the next firing. On hit: logs an error, releases the lock, and returns cleanly without failing the job.
Field Type Default Notes
retry_enabled bool true When false, a failure is cancelled outright — 1 attempt, no backoff.
retry_base_delay_ms int 1000 Coalesced to 1000 if ≤ 0.
retry_multiplier float 2 Coalesced to 2 if ≤ 1.
retry_max_delay_ms int 30000 Ceiling on the computed delay. Coalesced to 300000 if ≤ 0 — note this differs from the create-time default.
retry_max_attempts int 5 Becomes the queue job’s attempt budget at insert time; editing it doesn’t affect already-queued jobs.

Backoff is min(base × multiplier^(attempt−1), max_delay). No jitter.

Configured through the job wizard’s Notify step, backed by targets and the Apprise server set up under Settings → Notifications — see Configuration.

Field Type Default Notes
notify_on enum none none, success, failure, or both. Fires once a run has fully finished (not on every retry/loop iteration). Does not gate invocation alarms.
notify_all_targets bool false When true, notifies every configured target and notify_targets is ignored.
notify_targets int[] [] Target IDs to notify when notify_all_targets is false.

Targets themselves are configured under Settings → Notifications (Apprise and email) or Settings → Webhooks. A webhook target carries its own event filter (success, failure, retry_exhausted, invocation_alarm) that applies on top of this job-level gate: a target subscribed to failure also receives retry_exhausted, and leaving the filter empty selects all four. Metrics alarms reuse the same registry but offer only Apprise and email targets.

The same target fields govern the job’s invocation alarms, which fire when the job’s invocation count reaches a threshold over a rolling window. notify_all_targets/notify_targets choose their targets, and notify_on does not gate them — a job with notify_on: none still notifies its selected targets when an alarm breaches or recovers. Like success/failure notifications, alarm delivery is subject to a webhook target’s own event filter. See Invocation alarms.

Changing any of these three fields requires the notify:write permission — a caller without it can still edit every other field of a job that already has notifications configured; the stored notify config is carried forward unchanged on that edit rather than silently reset. See Permissions.

Set by the server and ignored on POST/PUT:

Field Notes
running_since_by_pool Overlap locks — one entry per pool index, keyed by its string form ("0", "1"). A lock older than 1 hour is stolen.
invocations_total Lifetime attempt count. One invocation is one attempt — a retry or loop iteration each count separately, not one cron-triggered run.
invocations_completed Lifetime successes.
invocations_failed Lifetime failures.

Rejected at write time:

  • Invalid cron or rate expression.
  • exec_timeout_ms outside 1 … 1800000, or under 10000 for Groovy.
  • pool_count outside 1 … 20, or > 1 on a one-off.
  • Unknown auth_type, overlap_policy, schedule_type or job_type.
  • run_at in the past on an unfired one-off, or changed after firing.
  • group_name over 255 bytes or 16 levels deep.
  • HTTP fields set on a script job, or script fields set on an HTTP job.
  • A job_type change (409), or a script job without jobs:script (403).