emit.run

Introduction

Core concepts of the emit.run system

Introduction

emit.run is a background job processing system with reliable, stateful job execution — built-in retries, timeouts, and real-time progress tracking.

Core Concepts

Spaces

Jobs are organized into spaces. A space belongs to an organization and acts as an isolated queue — it has its own jobs, API tokens, and WebSocket event stream.

Common patterns:

  • One space per environment: production, staging
  • One space per job type: emails, video-processing, exports
  • One space per team or customer

Jobs

A job represents a unit of work. Each job has:

FieldDescription
nameString identifier like send-email or generate-report
payloadOptional JSON data your worker receives
statuspendingdeliveredrunningcompleted or dead
maxRetriesHow many times to retry on failure (default: 3)
timeoutSecondsMax execution time per attempt (default: 300s)
callbackUrlOptional webhook URL — receives a POST when the job completes or goes dead
callbackHeadersOptional custom headers sent with the callback (e.g., auth tokens)

Job Lifecycle

polldelivery timeoutackcompletefailmax retriesretry (if attempts remain)webhookwebhookpendingdeliveredrunningcompletedfaileddead
transitiondelivery timeoutretrywebhook
  1. Created — Job enters pending state and is queued in the space's dispatch queue
  2. Delivered — Worker polls and receives the job; status moves to delivered and a delivery timeout starts. If the worker never acks, the job reverts to pending automatically.
  3. Running — Worker calls ack; status moves to running and the execution timeout starts
  4. In progress — Worker sends progress updates and checkpoint events
  5. Completed or Dead — Worker calls complete (→ completed) or fail. On fail: retries remaining → re-queued as pending; no retries remaining → dead
  6. Callback — If callbackUrl was set, the system POSTs the result to that URL on completed or dead

Worker Flow

Workers are simple HTTP clients that poll for work:

Worker Loop
1
POST/spaces/:id/jobs/poll

Claim up to 10 jobs. If empty, back off and retry.

2
POST/jobs/:id/ack

"I've received this job." Moves it to running and starts the execution timeout timer.

3
POST/jobs/:id/progressoptional

Send incremental updates. Broadcast to WebSocket subscribers.

4
POST/jobs/:id/keepaliveoptional

Reset the timeout window for long-running jobs.

5
POST/jobs/:id/complete

Report success with an optional result payload.

or
POST/jobs/:id/fail

Report failure. Job re-queues if retries remain, otherwise goes dead.

Scopes

API keys use granular scopes so you can give each client exactly the permissions it needs.

Granular scopes:

ScopeWhat it allows
jobs:createCreate new jobs
jobs:readList jobs, get job details
jobs:pollClaim pending jobs from the queue
jobs:ackAcknowledge receipt of a polled job
jobs:progressSend progress updates
jobs:eventPublish checkpoint events
jobs:completeMark jobs as completed
jobs:failMark jobs as failed
jobs:keepaliveExtend a running job's timeout
jobs:read:progressSubscribe to single-job WebSocket streams — no space-wide feed, no REST

Shorthand scopes (expand to multiple granular scopes):

ShorthandExpands to
jobs:workerjobs:read + jobs:poll + jobs:ack + jobs:progress + jobs:event + jobs:complete + jobs:fail + jobs:keepalive + jobs:read:progress (shorthand for the full worker set)
jobs:writejobs:ack + jobs:progress + jobs:event + jobs:complete + jobs:fail + jobs:keepalive

Typical token setups:

Use caseScopes
Producer (submit jobs)jobs:create
Worker (process jobs)jobs:worker or select individual scopes
Dashboard / monitoringjobs:read
Producer + monitorjobs:create, jobs:read
Third-party client (live job updates)jobs:read:progress

On this page