Workers Overview

Workers are the executors of Service Tasks in your BPMN processes. When the Priostack engine reaches a Service Task, it creates a Job. Workers poll the API to activate and process jobs, then report completion (or failure) back to the engine. Your API key is all a worker needs: registration and heartbeats are optional and never consulted by activation.

The Polling Pattern

Priostack uses an outbound job worker pattern: workers actively poll the engine for available jobs. This design means workers can run anywhere - behind firewalls, on-premise, or in any cloud - without requiring inbound connectivity to the engine.


  Worker                         Priostack Engine · POST /api/v1/jobs/activate · { "type": "payment-processor", · "maxJobsToActivate": 5 } · ---------------------------------> · (answers at once; {"jobs": []} when nothing waits) · HTTP 200 { "jobs": [...] } · <--------------------------------- · [Worker processes the job] · POST /api/v1/jobs/{key}/complete · { "variables": { "result": ... } } · ---------------------------------> · HTTP 204 No Content · <--------------------------------- · [Token advances in process]       |
    
No long polling: activation never holds the request open. It answers at once, with {"jobs": []} when nothing waits. Sleep 1 to 5 seconds after an empty or failed poll. A tight loop goes over the limit of 300 requests per minute per IP, gets 429, and three strikes ban the IP for 2 hours (see Rate Limits).

Job Activation

POST /api/v1/jobs/activate
Content-Type: application/json
X-API-Key: your_key

{
  "type": "payment-processor",
  "maxJobsToActivate": 10
}
FieldTypeDescription
typestringAlways send it. Matches the zeebe:taskDefinition type of the service task, case-sensitive. Without type or jobType, the call activates waiting jobs of every type you own, including user tasks and message waits.
jobTypestringAccepted alias of type, used only when type is empty. Prefer type.
maxJobsToActivateintegerOptional, with no server default and no upper bound. Omitted, 0 or negative activates every waiting job of that type in one response; a positive value caps the batch. Always send an explicit value.
workerstringAccepted and ignored (not stored, not echoed).
timeoutinteger (ms)Accepted and ignored. It sets neither a lock nor the deadline.

Every other field is ignored, including fetchVariables and requestTimeout. The API key goes in X-API-Key, in Authorization: Bearer <key>, or as ?api_key=. The body is read before the key is checked: an empty or invalid body answers 400 {"error":"invalid json"}, and a missing key answers 401 {"error":"unauthorized"}. Activation only hands out jobs of your own instances and cases.

Activation Response

{
  "jobs": [
    {
      "key": "a99f579d62ae71f8764f",
      "type": "payment-processor",
      "processInstanceKey": "run_1ce49bbc5260b39423bd",
      "processDefinitionKey": "7f3c2a91b04e5d6c8a12",
      "variables": {
        "orderId": "ORD-001",
        "amount": 149.99,
        "currency": "EUR"
      },
      "retries": 3,
      "deadline": 1746456780000,
      "createdAt": "0001-01-01T00:00:00Z"
    }
  ]
}
FieldTypeDescription
keystringThe job key, 20 lowercase hex characters. Use it in the complete and fail paths.
typestringThe job type.
processInstanceKeystringThe instance (or case) the job belongs to, currently run_ plus 20 hex characters. Older instances may carry a bare hex key, so never parse the format.
processDefinitionKeystring20 hex characters; empty for CMMN case jobs.
variablesobjectAll input variables of the job, unfiltered.
retriesintegerAttempts left. See Job Policies.
deadlineintegerUnix epoch milliseconds: activation time + 300 000 (5 minutes). Informational only: nothing happens when it passes.
createdAtstringAlways "0001-01-01T00:00:00Z" in this response.

Every key is an opaque JSON string, never a number: a Go client must declare them as string, and an int64 field makes the whole response fail to decode. The job object has no bpmnProcessId, elementId or customHeaders field. Business identifiers such as orderId are variables, not keys.

Completing a Job

POST /api/v1/jobs/{key}/complete
Content-Type: application/json
X-API-Key: your_key

{
  "variables": {
    "transactionId": "txn_abc123",
    "paymentStatus": "success"
  }
}

Success is 204 No Content with an empty body. variables is optional and an empty body is allowed. Completing a service job costs no credits.

Failing a Job

POST /api/v1/jobs/{key}/fail
Content-Type: application/json
X-API-Key: your_key

{
  "retries": 2,
  "errorMessage": "Payment gateway returned 503"
}

Success is 204 No Content. retries is the absolute number of attempts left, stored as sent: the server does not decrement it. Send job.retries - 1 to use up one attempt, or job.retries unchanged to hand the job back without using one. Above 0 the job can be activated again immediately, with no delay. At 0 or below the job is removed, the instance state becomes INCIDENT, and an incident record (error_code JOB_FAILED) appears in GET /api/v1/incidents. An omitted retries counts as 0, so a body with only errorMessage raises an incident at once.

Errors on Complete and Fail

StatusBodyWhen
401{"error":"unauthorized"}No API key.
400{"error":"invalid json"}The body is not valid JSON.
404{"error":"job not found"}The key is unknown, already completed, already failed to zero retries, belongs to a cancelled instance, or belongs to another account. These cases are deliberately indistinguishable.
404{"error":"<engine message>"}The engine refuses the call, for example on a job the runtime owns (a timer).

A repeated completion answers 404, never 409, and there is no idempotency key. If a response is lost and the retried call gets 404, the first call most likely went through: read GET /api/v1/process-instances/{processInstanceKey} to reconcile.

Business Errors

The only actions under /api/v1/jobs/{key}/ are complete and fail. A worker cannot throw a BPMN error or change retries over REST: /error, /throw and other actions answer 404 {"error":"unknown action"}. To route a business outcome such as a declined card, complete the job with an outcome variable and branch on it with an exclusive gateway after the task:

POST /api/v1/jobs/{key}/complete
Content-Type: application/json
X-API-Key: your_key

{
  "variables": { "paymentStatus": "declined", "declineReason": "insufficient_funds" }
}

Variable Passing

Concurrency

Idempotency: Design job handlers to be idempotent on the job key. A job comes back to the queue when a worker fails it with retries above 0, and when the server restarts (every outstanding job is restored to waiting with the same key and retries), so a handler can see the same key twice.

Worker API Reference

Every call a worker makes, with its parameters and a worked example. This was a separate page at /docs-workers until the two were merged; the sections above are the same endpoints explained in order of use. The first two calls are optional: nothing in activation, job handling or incidents reads them.

POST /api/v1/workers/register Register a worker (optional)

Records a worker in your account's worker list. It is informational only: activation does not need it and never reads it.

Request body

FieldTypeRequiredDescription
topicsstring[]requiredThe job types this worker handles. Must not be empty, or the call answers 400 {"error":"topics must not be empty"}.
group_namestringoptionalA label to group workers.

Example request

{
  "topics": ["payment:charge", "payment:refund"],
  "group_name": "payments"
}

Example response 201 Created

{
  "id": "wkr-3f9a0c1e7b2d4a6f8e1c5b7d9a2f4e6c",
  "api_key": "ps_...",
  "topics": ["payment:charge", "payment:refund"],
  "last_heartbeat": "2026-04-06T09:00:00Z",
  "registered_at": "2026-04-06T09:00:00Z",
  "active": true,
  "group_name": "payments"
}

The response currently echoes your full API key in api_key; do not log it.

GET /api/v1/workers/heartbeat?worker_id={id} Send heartbeat (optional)

A GET with the worker id as a query parameter and no body. A worker reads as active while its last heartbeat is under 90 seconds old (checked every 60 seconds). Missed heartbeats release nothing: jobs stay with whoever activated them.

Example response 200 OK

{ "status": "ok", "worker_id": "wkr-3f9a0c1e7b2d4a6f8e1c5b7d9a2f4e6c" }

An unknown id, or an id registered under another API key, answers 404.

POST /api/v1/jobs/activate Activate jobs

Activates and returns waiting jobs of the given type from your own instances and cases, up to maxJobsToActivate. It answers at once. An activated job is not handed out again while it is active, and there is no lock timeout.

Request body

FieldTypeRequiredDescription
typestringrequiredTask definition type to activate. Without it, jobs of every type are activated.
jobTypestringoptionalAlias of type, used only when type is empty.
maxJobsToActivateintegeroptionalNo default and no maximum: omitted, 0 or negative returns every waiting job of that type. Always send a value.
workerstringoptionalAccepted and ignored.
timeoutintegeroptionalAccepted and ignored.

Example request

{
  "type": "payment:charge",
  "maxJobsToActivate": 5
}

Example response 200 OK

{
  "jobs": [
    {
      "key": "a99f579d62ae71f8764f",
      "type": "payment:charge",
      "processInstanceKey": "run_1ce49bbc5260b39423bd",
      "processDefinitionKey": "7f3c2a91b04e5d6c8a12",
      "variables": { "orderId": "ORD-42", "amount": 9900, "currency": "EUR" },
      "retries": 3,
      "deadline": 1775466060000,
      "createdAt": "0001-01-01T00:00:00Z"
    }
  ]
}
POST /api/v1/jobs/{jobKey}/complete Complete a job

Marks a job as successfully completed. Optionally merges output variables back into the process instance. The process engine resumes from the next step.

Request body

FieldTypeRequiredDescription
variablesobjectoptionalOutput variables to merge into the process instance scope.

Example request

{ "variables": { "chargeId": "ch_stripe_abc", "status": "succeeded" } }

Response 204 No Content

404 {"error":"job not found"} when the job is unknown, already completed or no longer yours.

POST /api/v1/jobs/{jobKey}/fail Fail a job

Marks a job as failed. retries is stored as the number of attempts left. Above 0 the job is re-queued at once; at 0 or below the instance becomes INCIDENT and an incident record is created.

Request body

FieldTypeRequiredDescription
retriesintegerrequiredAttempts left, stored as sent (not decremented by the server). Omitted counts as 0, which raises an incident immediately.
errorMessagestringoptionalHuman-readable failure reason, shown in the incident record.

Example request

{ "retries": 2, "errorMessage": "Stripe timeout" }

Response 204 No Content

GET /api/v1/workers List registered workers

Returns the workers registered with your API key.

Example response 200 OK

{
  "items": [
    {
      "id": "wkr-3f9a0c1e7b2d4a6f8e1c5b7d9a2f4e6c",
      "api_key": "ps_...",
      "topics": ["payment:charge"],
      "last_heartbeat": "2026-04-06T09:00:55Z",
      "registered_at": "2026-04-06T09:00:00Z",
      "active": true,
      "group_name": "payments"
    }
  ],
  "total": 1
}

Language-Specific Guides