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.
Identity
Section titled “Identity”| 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 |
Scheduling
Section titled “Scheduling”| 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.
HTTP jobs
Section titled “HTTP jobs”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_config shapes
Section titled “auth_config shapes”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.
Script jobs
Section titled “Script jobs”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.
Execution
Section titled “Execution”| 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. |
Loop (success path)
Section titled “Loop (success path)”| 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. |
Retry (error path)
Section titled “Retry (error path)”| 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.
Notifications
Section titled “Notifications”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.
Server-managed
Section titled “Server-managed”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. |
Validation summary
Section titled “Validation summary”Rejected at write time:
- Invalid cron or rate expression.
exec_timeout_msoutside1 … 1800000, or under 10000 for Groovy.pool_countoutside1 … 20, or> 1on a one-off.- Unknown
auth_type,overlap_policy,schedule_typeorjob_type. run_atin the past on an unfired one-off, or changed after firing.group_nameover 255 bytes or 16 levels deep.- HTTP fields set on a script job, or script fields set on an HTTP job.
- A
job_typechange (409), or a script job withoutjobs:script(403).