Worker API
Poll, claim, acknowledge, complete, fail, and kill jobs
Worker API
Endpoints for the worker lifecycle: claiming jobs from the queue and reporting final outcomes.
Poll for Jobs
POST /api/v1/spaces/:spaceId/jobs/poll
Scope: jobs:poll
Claim pending jobs from the dispatch queue. Jobs are atomically dequeued — no two workers will receive the same job.
Request body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
count | number | No | 1 | Number of jobs to claim (max: 50) |
type | string | string[] | No | — | Preferred job-type filter. Pass one type or an array of types. |
types | string[] | No | — | Alias for type (array form). |
name | string | string[] | No | — | Backward-compatible alias for type. |
names | string[] | No | — | Alias for name (array form). |
curl -X POST https://emit.run/api/v1/spaces/$SPACE_ID/jobs/poll \
-H "x-api-key: $EMIT_KEY" \
-H "Content-Type: application/json" \
-d '{"count": 5, "type": ["process-video", "generate-thumbnail"]}'const res = await fetch(`${API}/spaces/${spaceId}/jobs/poll`, {
method: "POST",
headers: { "x-api-key": key, "Content-Type": "application/json" },
body: JSON.stringify({ count: 5, type: ["process-video", "generate-thumbnail"] }),
});
const { jobs } = await res.json();res = requests.post(
f"{API}/spaces/{space_id}/jobs/poll",
headers={"x-api-key": key, "Content-Type": "application/json"},
json={"count": 5, "type": ["process-video", "generate-thumbnail"]},
)
jobs = res.json()["jobs"]If you send more than one filter field, precedence is types -> type -> names -> name.
Response:
{
"jobs": [
{
"id": "01JLQX...",
"name": "process-video",
"payload": { "url": "s3://bucket/video.mp4", "format": "720p" },
"timeoutSeconds": 600,
"maxRetries": 5,
"attemptNumber": 0,
"createdAt": "2025-02-24T10:30:00.000Z"
}
]
}Returns {"jobs":[]} if no jobs are available. Your worker should back off and retry.
Claim Specific Job
POST /api/v1/jobs/:jobId/claim
Scope: jobs:poll
Atomically claim one specific pending job by ID. Useful with push/webhook wake signals where your worker receives a job ID and claims that exact job.
If the job is no longer pending (already claimed, running, completed, etc.), the endpoint returns claimed: false and the current status.
curl -X POST https://emit.run/api/v1/jobs/$JOB_ID/claim \
-H "x-api-key: $EMIT_KEY"const res = await fetch(`${API}/jobs/${jobId}/claim`, {
method: "POST",
headers: { "x-api-key": key },
});
const claim = await res.json();
if (claim.claimed) {
const job = claim.job;
}res = requests.post(
f"{API}/jobs/{job_id}/claim",
headers={"x-api-key": key},
)
claim = res.json()
if claim.get("claimed"):
job = claim["job"]Response:
{
"claimed": true,
"job": {
"id": "01JLQX...",
"name": "process-video",
"payload": { "url": "s3://bucket/video.mp4" },
"timeoutSeconds": 600,
"maxRetries": 5,
"attemptNumber": 0,
"createdAt": "2025-02-24T10:30:00.000Z"
}
}{
"claimed": false,
"reason": "not_pending",
"status": "delivered"
}{ "error": "Job not found" }Acknowledge Job
POST /api/v1/jobs/:jobId/ack
Scope: jobs:ack
Tell the system your worker has received the job and is starting work. This moves the job from delivered to running and starts the execution timeout timer.
curl -X POST https://emit.run/api/v1/jobs/$JOB_ID/ack \
-H "x-api-key: $EMIT_KEY"await fetch(`${API}/jobs/${jobId}/ack`, {
method: "POST",
headers: { "x-api-key": key },
});Complete Job
POST /api/v1/jobs/:jobId/complete
Scope: jobs:complete
Mark the job as successfully completed. Optionally include a result payload.
curl -X POST https://emit.run/api/v1/jobs/$JOB_ID/complete \
-H "x-api-key: $EMIT_KEY" \
-H "Content-Type: application/json" \
-d '{"result": {"output_url": "s3://bucket/output.mp4", "duration_ms": 12340}}'await fetch(`${API}/jobs/${jobId}/complete`, {
method: "POST",
headers: { "x-api-key": key, "Content-Type": "application/json" },
body: JSON.stringify({
result: { output_url: "s3://bucket/output.mp4", duration_ms: 12340 },
}),
});Fail Job
POST /api/v1/jobs/:jobId/fail
Scope: jobs:fail
Mark the job as failed. If the job has retries remaining, it will be re-queued as pending. Include an error message or object for debugging.
curl -X POST https://emit.run/api/v1/jobs/$JOB_ID/fail \
-H "x-api-key: $EMIT_KEY" \
-H "Content-Type: application/json" \
-d '{"error": "FFmpeg exited with code 1: out of memory"}'await fetch(`${API}/jobs/${jobId}/fail`, {
method: "POST",
headers: { "x-api-key": key, "Content-Type": "application/json" },
body: JSON.stringify({
error: "FFmpeg exited with code 1: out of memory",
}),
});Kill Job
POST /api/v1/jobs/:jobId/kill
Scope: jobs:kill
Force-stop a job and mark it as killed. Useful for operator intervention, cancellation flows, or control-plane shutdowns.
If the job is already killed, this endpoint is idempotent and returns success.
curl -X POST https://emit.run/api/v1/jobs/$JOB_ID/kill \
-H "x-api-key: $EMIT_KEY" \
-H "Content-Type: application/json" \
-d '{"reason": "manual stop"}'await fetch(`${API}/jobs/${jobId}/kill`, {
method: "POST",
headers: { "x-api-key": key, "Content-Type": "application/json" },
body: JSON.stringify({ reason: "manual stop" }),
});Worker Stop Signal
If a job is force-stopped via POST /jobs/:id/kill, worker mutation endpoints (progress, checkpoint, event, keepalive) return:
{
"error": "Job has been killed",
"code": "JOB_KILLED",
"jobStatus": "killed"
}The HTTP status is 409. Workers should treat this as an immediate stop signal for that job.
POST /jobs/:id/logs remains available after kill so workers can publish teardown diagnostics.
Scope Quick Reference
| Endpoint | Required scope |
|---|---|
POST /spaces/:id/jobs | jobs:create |
GET /spaces/:id/jobs | jobs:read |
POST /spaces/:id/jobs/poll | jobs:poll |
POST /jobs/:id/claim | jobs:poll |
GET /jobs/:id | jobs:read |
GET /jobs/:id/logs | jobs:read |
GET /jobs/:id/progress | jobs:read:progress or jobs:read |
POST /jobs/:id/ack | jobs:ack |
POST /jobs/:id/progress | jobs:progress |
POST /jobs/:id/logs | jobs:event |
POST /jobs/:id/checkpoint | jobs:event |
POST /jobs/:id/event | jobs:event |
POST /jobs/:id/complete | jobs:complete |
POST /jobs/:id/fail | jobs:fail |
POST /jobs/:id/kill | jobs:kill |
POST /jobs/:id/keepalive | jobs:keepalive |
A token with jobs:worker covers all worker scopes.