HTTP Jobs
An HTTP job calls a URL on a schedule. It is the default job_type.
curl -X POST https://<your-host>/api/jobs \ -H 'Content-Type: application/json' \ -d '{ "name": "nightly-sync", "job_type": "http", "schedule_type": "cron", "cron_expression": "0 2 * * *", "http_method": "POST", "url": "https://example.com/api/sync", "headers": {"X-Request-Source": "zilean"}, "body": {"mode": "incremental"}, "exec_timeout_ms": 60000, "auth_type": "bearer", "auth_config": {"token": "{{SYNC_TOKEN}}"} }'How the request is built
Section titled “How the request is built”In order:
- Method —
http_method, defaulting toGET. - Query params —
query_params, with{{variable}}substitution applied to both keys and values. - URL —
urlwith substitution applied, then query params merged in. A param overrides a same-named param already present in the raw URL. - Body —
body(raw JSON) as a string, with substitution applied, sent verbatim. - Headers —
headers, keys and values substituted. - Content-Type — set to
application/jsononly if there’s a body and you didn’t setContent-Typeyourself. - Auth — applied last, so it overrides a manually-set header of the same name.
Authentication
Section titled “Authentication”auth_type |
auth_config |
Effect |
|---|---|---|
none (default) |
ignored | Nothing. |
bearer |
{"token": "..."} |
Authorization: Bearer <token>. Errors if token is empty. |
basic |
{"username": "...", "password": "..."} |
HTTP Basic. Errors if username is empty; an empty password is allowed. |
api_key |
{"api_key": "...", "header_name": "..."} |
Sets header_name: <api_key>. header_name defaults to X-API-Key. Errors if api_key is empty. |
Every field of auth_config gets {{variable}} substitution before use, so credentials
can live in global variables rather than inline.
Success and failure
Section titled “Success and failure”Success is strictly 200 ≤ status < 300. Everything else fails:
endpoint returned status 404: <first 500 chars of body>Also failures: transport errors, and errors reading the response body. A response body
larger than 4 MiB fails the attempt too (and is retried like any other failure) rather
than being truncated, since a cut-off payload can’t be trusted for loop_response_path.
A configuration error is different. An unparseable URL, an unknown auth_type, or a
missing required auth field cancels the job rather than retrying it — it can never
succeed, so it doesn’t burn the retry budget.
A failed attempt keeps its real status code and duration, but response snippets and
error text are redacted before they’re recorded, so a body that echoes back a credential
reads REDACTED in call history, River UI, and failure notifications. Only the recorded
copy is affected: the request itself sends the real values, and loop_response_path is
evaluated against the real response, so looping is unaffected.
Timeouts
Section titled “Timeouts”exec_timeout_ms bounds one attempt.
- Default: 60 000 ms (60 s) if unset or
0. - Maximum: 1 800 000 ms (30 min). Higher is rejected at write time.
Above that sits River’s 35-minute safety net, which exists only to kill a job that somehow outlives its own timeout — not to enforce per-job limits.
Pooling
Section titled “Pooling”With pool_count > 1, each parallel call receives a pool_idx query parameter
(0 … pool_count − 1), which overwrites any pool_idx you set yourself.
At pool_count: 1 the parameter is not added at all, so a non-pooled job’s request
is byte-identical to what it would have been before pooling existed.
Global variables
Section titled “Global variables”Any text field supports {{name}} placeholders — URL, query params, body, headers and
every auth_config field. Whitespace inside the braces is tolerated ({{ name }}).
Names may contain letters, digits and underscores.
Variables are loaded fresh on every attempt, so changing a variable’s value takes effect
on the next attempt with no job edit. Manage them under Settings → Global variables,
or via /api/global-variables. A variable marked secret is write-only: after saving, the
UI shows “Value hidden” and the API returns an empty value, so you can replace a secret
but not read it back. Secret values are AES-256-GCM-encrypted at rest with
SETTINGS_ENCRYPTION_KEY, masked in the audit log, and redacted from what Zilean records
about a call: the recorded URL, the response snippet, the error text, and the failure
notification all show REDACTED in place of the value. A job’s resolved
auth credentials are redacted the same way, whether or not they came
from a variable.
Looping through a backlog
Section titled “Looping through a backlog”Set loop_enabled and point loop_response_path at a count of remaining work. The job
re-calls itself on success while that number stays positive:
{ "loop_enabled": true, "loop_response_path": "result.remaining", "loop_delay_ms": 2000, "loop_jitter_ms": 300, "loop_max_iterations": 100}Your endpoint would return something like {"result": {"remaining": 42}}, and 0 when
done. The path is gjson syntax, and only a strictly
positive number continues the loop.
Inspecting what happened
Section titled “Inspecting what happened”curl https://<your-host>/api/jobs/<id>/callsEach entry carries the queue state, attempt number, timestamps, last error, pool index,
loop iteration, and a last_call object with the method, URL, status code, duration and
a 500-character response snippet.
See Monitoring jobs.