emit.run

Worker Events

Progress, checkpoints, custom events, keepalive, and log ingestion

Worker Events

Endpoints for reporting progress, saving checkpoints, emitting custom events, extending timeouts, and appending logs during job execution.


Report Progress

POST /api/v1/jobs/:jobId/progress

Scope: jobs:progress

Send a progress update for a running job. The body supports:

  • percent — number from 0 to 100
  • message — non-empty user-facing string
  • subProgress (optional) — object keyed by stage name, each with percent and optional message

Progress events are broadcast to WebSocket subscribers and reset the execution timeout window.

curl -X POST https://emit.run/api/v1/jobs/$JOB_ID/progress \
  -H "x-api-key: $EMIT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "percent": 65,
    "message": "Encoding video",
    "subProgress": {
      "download": { "percent": 100, "message": "Source fetched" },
      "transcode": { "percent": 42, "message": "Pass 1/2" }
    }
  }'
await fetch(`${API}/jobs/${jobId}/progress`, {
  method: "POST",
  headers: { "x-api-key": key, "Content-Type": "application/json" },
  body: JSON.stringify({
    percent: 65,
    message: "Encoding video",
    subProgress: {
      download: { percent: 100, message: "Source fetched" },
      transcode: { percent: 42, message: "Pass 1/2" },
    },
  }),
});

Checkpoint

POST /api/v1/jobs/:jobId/checkpoint

Scope: jobs:event

Store checkpoint data for resumable work. If the job fails and retries, your worker can read past checkpoints to resume where it left off. Resets the execution timeout.

curl -X POST https://emit.run/api/v1/jobs/$JOB_ID/checkpoint \
  -H "x-api-key: $EMIT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"checkpoint": "processed_frames", "count": 1500, "last_offset": 45000}'
await fetch(`${API}/jobs/${jobId}/checkpoint`, {
  method: "POST",
  headers: { "x-api-key": key, "Content-Type": "application/json" },
  body: JSON.stringify({
    checkpoint: "processed_frames",
    count: 1500,
    last_offset: 45000,
  }),
});

Custom Event

POST /api/v1/jobs/:jobId/event

Scope: jobs:event

Store an arbitrary debug/admin event payload on the job. Does not change job state — only records and broadcasts. Resets the execution timeout.

curl -X POST https://emit.run/api/v1/jobs/$JOB_ID/event \
  -H "x-api-key: $EMIT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phase":"encoder","host":"worker-3","note":"switched bitrate ladder"}'
await fetch(`${API}/jobs/${jobId}/event`, {
  method: "POST",
  headers: { "x-api-key": key, "Content-Type": "application/json" },
  body: JSON.stringify({
    phase: "encoder",
    host: "worker-3",
    note: "switched bitrate ladder",
  }),
});

Keepalive

POST /api/v1/jobs/:jobId/keepalive

Scope: jobs:keepalive

Extend the job's timeout. Call this periodically for long-running jobs to prevent timeout. Each call resets the timeout window.

curl -X POST https://emit.run/api/v1/jobs/$JOB_ID/keepalive \
  -H "x-api-key: $EMIT_KEY"
const interval = setInterval(async () => {
  await fetch(`${API}/jobs/${jobId}/keepalive`, {
    method: "POST",
    headers: { "x-api-key": key },
  });
}, 60_000);

try {
  await doLongRunningWork();
  await fetch(`${API}/jobs/${jobId}/complete`, { method: "POST", headers });
} finally {
  clearInterval(interval);
}

Append Logs

POST /api/v1/jobs/:jobId/logs

Scope: jobs:event

Append one or more log lines to a job. Allowed for any job status so workers can emit diagnostics before ack, during retries, and during teardown.

Supported formats

  1. JSON (application/json) — array of entries, or object with logs: [...]
  2. NDJSON (application/x-ndjson) — one log entry per line
  3. gzip — send Content-Encoding: gzip for JSON or NDJSON batches

Each entry can be a string or an object:

{
  "message": "transcode pass started",
  "level": "info",
  "timestamp": "2026-03-01T09:21:01.120Z",
  "source": "worker-a",
  "metadata": { "pass": 1 }
}
curl -X POST https://emit.run/api/v1/jobs/$JOB_ID/logs \
  -H "x-api-key: $EMIT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "logs": [
      { "level": "info", "message": "worker boot" },
      { "level": "warn", "message": "fallback codec selected" },
      "done with preflight"
    ]
  }'
printf '%s\n' \
  '{"level":"debug","message":"started"}' \
  '{"level":"error","message":"disk full"}' \
  | gzip \
  | curl -X POST https://emit.run/api/v1/jobs/$JOB_ID/logs \
      -H "x-api-key: $EMIT_KEY" \
      -H "Content-Type: application/x-ndjson" \
      -H "Content-Encoding: gzip" \
      --data-binary @-
await fetch(`${API}/jobs/${jobId}/logs`, {
  method: "POST",
  headers: { "x-api-key": key, "Content-Type": "application/json" },
  body: JSON.stringify({
    logs: [
      { level: "info", message: "worker boot" },
      { level: "debug", message: "download 42%" },
    ],
  }),
});

Response:

{
  "success": true,
  "inserted": 2,
  "fromSeq": 1201,
  "toSeq": 1202
}

On this page