External Workers#
External workers run handler code outside the engine. They poll the versioned REST API, execute a task, and report completion or failure. Use them to share application code and secrets, scale handler capacity independently, or implement handlers in any language that can send JSON.
Protocol#
Routes below are relative to /api/v1. Secure deployments require x-api-key andx-tenant-id on each request.
1. Poll#
{
"handler_name": "send_email",
"worker_id": "mail-worker-42",
"limit": 10,
"version": "0.7.0"
}| Parameter | Type | Required | Description |
|---|---|---|---|
| handler_name | string | Yes | Handler this worker accepts. |
| worker_id | string | Yes | Unique identity for this process. |
| limit | integer | No | Maximum tasks to claim in this poll. |
| version | string | No | Worker version checked against fleet pins. |
The response is an array. A reclaimed task may includeresume_checkpoint and its monotoniccheckpoint_seq. Resume from that checkpoint instead of repeating completed activity work.
2. Heartbeat and checkpoint#
{
"worker_id": "mail-worker-42",
"checkpoint_seq": 0,
"checkpoint": {
"completed_batches": 12,
"cursor": "next-page-token"
}
}Send a heartbeat every 15–30 seconds for long tasks. The response contains the next checkpoint_seq. A stale sequence or former lease owner receives 409 Conflict. Checkpoints are capped at 256 KiB, encrypted at rest when storage encryption is enabled, and survive retry and stale-lease recovery.
3. Complete#
{
"worker_id": "mail-worker-42",
"output": {
"message_id": "msg-123",
"delivered": true
}
}4. Fail#
{
"worker_id": "mail-worker-42",
"message": "SMTP connection refused",
"retryable": true
}A retryable failure reschedules the instance according to the block retry policy. A permanent failure enters the surrounding TryCatch branch or, without one, the dead letter queue.
Runnable Python worker#
import os
import socket
import time
import httpx
ENGINE = os.getenv("ORCH8_URL", "http://localhost:8080/api/v1")
API_KEY = os.environ["ORCH8_API_KEY"]
TENANT_ID = os.getenv("ORCH8_TENANT_ID", "demo")
WORKER_ID = f"py-worker-{socket.gethostname()}-{os.getpid()}"
def send_email(task):
# Pass task["id"] or another stable value as the provider idempotency key.
return {"message_id": "msg-123"}
with httpx.Client(
base_url=ENGINE,
timeout=30,
headers={"x-api-key": API_KEY, "x-tenant-id": TENANT_ID},
) as http:
while True:
response = http.post("/workers/tasks/poll", json={
"handler_name": "send_email",
"worker_id": WORKER_ID,
"limit": 5,
"version": "0.7.0",
})
response.raise_for_status()
for task in response.json():
try:
output = send_email(task)
result = http.post(
f"/workers/tasks/{task['id']}/complete",
json={"worker_id": WORKER_ID, "output": output},
)
result.raise_for_status()
except Exception as error:
failure = http.post(
f"/workers/tasks/{task['id']}/fail",
json={
"worker_id": WORKER_ID,
"message": str(error),
"retryable": True,
},
)
failure.raise_for_status()
time.sleep(1)Resumable activity loop#
For batch work, restore the checkpoint from the claimed task and update it atomically as each durable unit completes:
checkpoint = task.get("resume_checkpoint") or {
"completed_batches": 0,
"cursor": None,
}
checkpoint_seq = task.get("checkpoint_seq", 0)
while has_more(checkpoint["cursor"]):
checkpoint = process_next_batch(checkpoint)
heartbeat = http.post(
f"/workers/tasks/{task['id']}/heartbeat",
json={
"worker_id": WORKER_ID,
"checkpoint_seq": checkpoint_seq,
"checkpoint": checkpoint,
},
)
heartbeat.raise_for_status()
checkpoint_seq = heartbeat.json()["checkpoint_seq"]Fleet controls#
GET /workersandGET /workers/tasks/statsexpose registrations and queue state.POST /workers/commandssends drain, reload, or ping commands; workers acknowledge commands after applying them.POST /workers/version-pinssets a minimum version for a tenant and handler.- Named queues use
POST /workers/tasks/poll/queue. Push queues deliver a signed envelope to the configured worker URL.
Use @orch8.io/sdk for the official Node client, or implement the REST protocol directly as above.
Ready to try Orch8?
One command to install. Then run your first local sequence.
curl -fsSL https://orch8.io/start.sh | sh