Jobs API
Lua API for creating and managing background jobs. For a conceptual overview, see Background Jobs.
jobs table
Section titled “jobs table”The jobs table is available in all script contexts (procedures, queries, record scripts, label scripts, and job scripts). It provides functions for queuing new jobs.
jobs.create(job_type, input[, opts])
Section titled “jobs.create(job_type, input[, opts])”Enqueue a new background job.
Parameters:
| Parameter | Type | Description |
|---|---|---|
job_type | string | The job type name. Must match a job.run:<type> script trigger. |
input | table | Input data passed to the job script via job.input. |
opts | table? | Optional settings (see below). |
Options:
| Key | Type | Default | Description |
|---|---|---|---|
auth | boolean | false | Inherit the caller’s PDS auth. When true, the job script can use r:save(), r:delete(), and blob uploads as the creating user. When false, only local operations (r:save_local(), r:delete_local()) are available. |
Returns: string — the new job’s UUID.
Requires: An authenticated caller (caller_did must be set). Raises an error in unauthenticated contexts.
-- Enqueue a job without PDS auth (default)function handle() local job_id = jobs.create("stats.rebuild", { collection = collection, }) return { job_id = job_id }end-- Enqueue a job that needs to write records on behalf of the callerfunction handle() local job_id = jobs.create("export", { collection = collection, format = input.format, }, { auth = true }) return { job_id = job_id }endJobs can enqueue other jobs — a job script can call jobs.create() to spawn follow-up work:
-- Inside a job script: fan out to per-collection jobsfunction handle() local collections = job.input.collections local child_ids = {} for _, col in ipairs(collections) do table.insert(child_ids, jobs.create("export.collection", { collection = col, parent_job = job.id, })) end return { children = child_ids }endjob table
Section titled “job table”The job table is available only inside job scripts (trigger job.run:<type>). It is nil in all other script contexts.
job.id
Section titled “job.id”Type: string
The job’s UUID.
job.input
Section titled “job.input”Type: table
The input table that was passed to jobs.create() when the job was queued.
job.progress(data)
Section titled “job.progress(data)”Persist a progress snapshot to the database.
Parameters:
| Parameter | Type | Description |
|---|---|---|
data | table | Any Lua table — stored as JSONB. |
Each call overwrites the previous progress value. The snapshot is visible in the job detail panel in the dashboard and via GET /admin/jobs/:id.
job.progress({ phase = "fetching", fetched = 250, total = 1000 })job.should_stop()
Section titled “job.should_stop()”Check whether the job has been paused or cancelled.
Returns: boolean — true if the operator requested a pause or cancel.
Pause and cancel are cooperative. The worker sets a flag when the operator requests it, but the script must call job.should_stop() and exit gracefully. If your script never checks, pause and cancel requests wait until the script finishes on its own.
for i, item in ipairs(items) do if job.should_stop() then return { partial = true, last = i } end process(item)endjob.wait(seconds)
Section titled “job.wait(seconds)”Sleep for the specified duration.
Parameters:
| Parameter | Type | Description |
|---|---|---|
seconds | number | Duration in seconds (clamped to 0–3600). |
Values below 0 are clamped to 0. Values above 3600 are clamped to 3600.
-- Poll an external API with a delay between requestsfor _, batch in ipairs(batches) do local resp = http.post("https://api.example.com/import", { body = json.encode(batch), }) job.wait(1) -- rate limitendAvailable APIs
Section titled “Available APIs”Job scripts have access to all standard Lua APIs:
db.*— database querieshttp.*— HTTP clientxrpc.*— XRPC callsatproto.*— DID resolution, labels, signingRecord.*— record operationsjson.*— JSON encode/decodejobs.*— queue follow-up jobslog(),now(),TID(),toarray()— utility globalsenv.<KEY>— script variables
Unlike XRPC and record scripts, job scripts have no instruction count limit — they can run arbitrarily long computations.

