Wire a worker process into Mobius to execute workflow steps — for a service that pulls pending jobs, runs them, and reports results back to the run. No separate registration call is needed; the first claim request registers the worker.

Before you start

  • You have a Mobius API key exported as MOBIUS_API_KEY. See Create an API key if you do not.
  • Export MOBIUS_BASE_URL (typically https://api.mobius.deepnoodle.ai) and PROJECT (your project handle).
  • Choose a stable string for WORKER_ID — this is the lease fence used on every heartbeat and complete call — and export it.
  • A workflow run is in progress with at least one pending worker step. See Run a workflow from your code to start one.

Walkthrough

  1. Long-poll for the next pending job with POST /v1/projects/{project}/jobs/claim. The server holds the connection open for up to wait_seconds seconds (maximum 30). A 200 response carries job_id, attempt, and heartbeat_interval_seconds; capture them as $JOB_ID and $ATTEMPT. A 204 means the poll window closed empty — loop and retry.

    curl -X POST "$MOBIUS_BASE_URL/v1/projects/$PROJECT/jobs/claim" \
      -H "Authorization: Bearer $MOBIUS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "worker_id": "'"$WORKER_ID"'",
        "queues": ["default"],
        "wait_seconds": 30
      }'
  2. Keep the lease alive by calling POST /v1/projects/{project}/jobs/{id}/heartbeat at the cadence from heartbeat_interval_seconds. The response includes directives.should_cancel; when it is true, stop executing and move to step 4 with status: failed.

    curl -X POST "$MOBIUS_BASE_URL/v1/projects/$PROJECT/jobs/$JOB_ID/heartbeat" \
      -H "Authorization: Bearer $MOBIUS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "worker_id": "'"$WORKER_ID"'",
        "attempt": '"$ATTEMPT"'
      }'
  3. Emit progress events with POST /v1/projects/{project}/jobs/{id}/events. Events are appended to the run's durable event store and fanned out to live SSE subscribers. The mobius. type prefix is rejected with 400; use any other identifier.

    curl -X POST "$MOBIUS_BASE_URL/v1/projects/$PROJECT/jobs/$JOB_ID/events" \
      -H "Authorization: Bearer $MOBIUS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "worker_id": "'"$WORKER_ID"'",
        "attempt": '"$ATTEMPT"',
        "events": [
          { "type": "progress", "payload": { "pct": 50 } }
        ]
      }'
  4. Report the terminal result with POST /v1/projects/{project}/jobs/{id}/complete. Returns 204. On status: failed, the workflow engine retries the step if attempt < max_attempts; otherwise the run transitions to failed.

    curl -X POST "$MOBIUS_BASE_URL/v1/projects/$PROJECT/jobs/$JOB_ID/complete" \
      -H "Authorization: Bearer $MOBIUS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "worker_id": "'"$WORKER_ID"'",
        "attempt": '"$ATTEMPT"',
        "status": "completed"
      }'

Follow-ups

Troubleshooting

  • 204 on claim: no pending job matched the subscribed queues within the poll window → loop back and retry the claim.
  • 409 on heartbeat or complete: the lease fence (worker_id + attempt) does not match the server's current record → the lease has expired; discard this claim and start a new claim loop.
  • directives.should_cancel: true: the run received a cancellation request → call POST /complete with status: failed immediately.

See also