A job is a claimable runtime record that Mobius creates when a worker action step becomes ready to execute in a run. Workers long-poll to claim jobs one at a time, heartbeat to hold the lease, and call complete to report the outcome. The action log records every state transition.

The model

Each job has:

  • A step identity — the step_name and parent run_id that identify which step in which run the job represents.
  • Parameters — resolved inputs for the step, derived from the workflow spec and prior step outputs.
  • A queue — the routing label that targets the job to workers subscribed to that queue. Steps that omit a queue use default.
  • A lease fence — the worker_id and attempt pair that prevents double-execution. Heartbeat and complete calls are rejected with 409 if the fence does not match the current lease holder.
  • An attempt counter — starts at 1. When a job fails and attempt < max_attempts, Mobius creates a new job for the same step with attempt incremented by one.

Claiming, heartbeating, and completing a job

# 1. Claim the next pending job (long-poll for up to 10 s)
curl -X POST "$API_BASE/v1/projects/$PROJECT/jobs/claim" \
  -H "Authorization: Bearer $MOBIUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "worker_id": "worker-1",
    "queues": ["default"],
    "wait_seconds": 10
  }'
 
# Response includes job_id, step_name, action, parameters, attempt,
# and heartbeat_interval_seconds.
 
# 2. Heartbeat to extend the lease (repeat at heartbeat_interval_seconds cadence)
curl -X POST "$API_BASE/v1/projects/$PROJECT/jobs/$JOB_ID/heartbeat" \
  -H "Authorization: Bearer $MOBIUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"worker_id": "worker-1", "attempt": 1}'
 
# 3. Complete the job with base64-encoded output
curl -X POST "$API_BASE/v1/projects/$PROJECT/jobs/$JOB_ID/complete" \
  -H "Authorization: Bearer $MOBIUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "worker_id": "worker-1",
    "attempt": 1,
    "status": "completed",
    "result_b64": "eyJzdW1tYXJ5IjogImRvbmUifQ=="
  }'

claim returns 204 when the poll window closes with no available jobs. When complete is called with status: completed, the workflow engine decodes result_b64 and may pass it as input to downstream steps. If a heartbeat response contains directives.should_cancel: true, stop processing and call complete with status: failed — the run has received a cancellation request.

Lifecycle

StateEntered whenLeft when
pendingWorkflow engine creates the job for a ready stepWorker claims it
claimedWorker calls POST .../jobs/claimWorker calls complete, or the lease expires
completedWorker calls complete with status: completed— (terminal)
failedWorker calls complete with status: failed— (terminal; if attempt < max_attempts, a new job is created for the same step)

Where you see it

  • API — the Jobs tag group exposes claimJob, heartbeatJob, completeJob, and emitJobEvents.
  • Worker registry — each successful claim call registers or refreshes the calling worker. Query registered workers via GET /v1/projects/{project}/workers.

See also

  • Workflows — define the steps that produce jobs.
  • Runs — the execution context each job belongs to.
  • Workers — the processes that claim and complete jobs.
  • Action log — the append-only record of job execution events.