Skip to content

Your First Job

This walks through creating an HTTP job, confirming it ran, and editing it live — the thing Zilean exists to make cheap.

You’ll need a running instance (Installation) and an account with jobs:write.

Open your instance root. You’ll be redirected to /login. Sign in with the ADMIN_USERNAME email and the password you generated the hash from.

The API uses the same session cookie, so anything below works from the browser or from curl with a session.

The simplest possible job — call a URL every five minutes:

Terminal window
curl -X POST https://<your-host>/api/jobs \
-H 'Content-Type: application/json' \
-d '{
"name": "hello-httpbin",
"job_type": "http",
"schedule_type": "cron",
"cron_expression": "*/5 * * * *",
"http_method": "GET",
"url": "https://httpbin.org/get"
}'

Everything else takes a default: 60-second timeout, retry enabled with 5 attempts, no looping, no pooling, skip overlap policy.

In the admin UI, the same thing is Jobs → New job, a wizard that steps through job type, basics, request, authorization, loop & retry, and a review screen.

Cron expressions are evaluated in the server’s timezone, not yours:

Terminal window
curl https://<your-host>/api/meta
# {"timezone":"America/Los_Angeles"}

If that says UTC and you expected otherwise, TZ isn’t reaching the container — see Configuration.

The scheduler picks up new and changed rows every 30 seconds. You can skip the wait:

Terminal window
curl -X POST https://<your-host>/api/jobs/<id>/run

A manual trigger fires immediately, deliberately skipping the deduplication guard that protects scheduled firings.

Terminal window
curl 'https://<your-host>/api/jobs/<id>/calls?limit=5'

Each entry carries the queue state, attempt number, timestamps, and a last_call object with the method, URL, status code, duration and a snippet of the response.

In the UI: the job’s row has a heartbeat bar, and the row menu offers Calls (recent attempts) and Activity (volume and latency over time). For raw queue internals — states, attempts, errors — use the embedded River UI at /riverui/.

On the Jobs page, groups appear as a tree in a sidebar; select one to filter the table. Tick the checkboxes on several rows to enable, disable or delete them together with the batch actions, and use Import CSV to create jobs in bulk (CSV import). First-run and What’s new tours, a Settings overview tour, and a short tour for every Settings section can be replayed any time from Settings → Tours, which keeps the What’s new tours for the three most recent releases. Each Settings section also has a small “?” button beside its heading that starts that section’s tour in place.

This is the part worth testing, because it’s the product’s whole premise. Change the URL:

Terminal window
# fetch the current row, change what you want, send the whole thing back
curl https://<your-host>/api/jobs/<id> > job.json
# edit job.json, then:
curl -X PUT https://<your-host>/api/jobs/<id> \
-H 'Content-Type: application/json' \
--data @job.json

PUT takes the full object — there are no partial updates.

The change takes effect on the next attempt, with no restart and no reconcile wait. The worker re-reads the job row on every attempt, so only a change to cron_expression itself waits for the next 30-second tick.

Looping is what separates Zilean from a cron entry. Point the job at an endpoint that reports how much work is left, and it will keep calling itself 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 returns {"result": {"remaining": 42}} while there’s work and {"result": {"remaining": 0}} when it’s drained. The path is gjson syntax, and only a strictly positive number continues the loop.

Each iteration is its own attempt, so each one counts separately in the invocation counters — and can retry on its own.

Terminal window
curl -X POST https://<your-host>/api/jobs/<id>/disable

Disabling stops work already queued, not just future firings — the worker re-checks enabled at execution time.

Or delete it:

Terminal window
curl -X DELETE https://<your-host>/api/jobs/<id>