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:
| Field | Description |
|---|---|
name | String identifier like send-email or generate-report |
payload | Optional JSON data your worker receives |
status | pending → delivered → running → completed or dead |
maxRetries | How many times to retry on failure (default: 3) |
timeoutSeconds | Max execution time per attempt (default: 300s) |
callbackUrl | Optional webhook URL — receives a POST when the job completes or goes dead |
callbackHeaders | Optional custom headers sent with the callback (e.g., auth tokens) |
Job Lifecycle
- Created — Job enters
pendingstate and is queued in the space's dispatch queue - Delivered — Worker polls and receives the job; status moves to
deliveredand a delivery timeout starts. If the worker never acks, the job reverts topendingautomatically. - Running — Worker calls ack; status moves to
runningand the execution timeout starts - In progress — Worker sends progress updates and checkpoint events
- Completed or Dead — Worker calls complete (→
completed) or fail. On fail: retries remaining → re-queued aspending; no retries remaining →dead - Callback — If
callbackUrlwas set, the system POSTs the result to that URL oncompletedordead
Worker Flow
Workers are simple HTTP clients that poll for work:
/spaces/:id/jobs/pollClaim up to 10 jobs. If empty, back off and retry.
/jobs/:id/ack"I've received this job." Moves it to running and starts the execution timeout timer.
/jobs/:id/progressoptionalSend incremental updates. Broadcast to WebSocket subscribers.
/jobs/:id/keepaliveoptionalReset the timeout window for long-running jobs.
/jobs/:id/completeReport success with an optional result payload.
/jobs/:id/failReport 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:
| Scope | What it allows |
|---|---|
jobs:create | Create new jobs |
jobs:read | List jobs, get job details |
jobs:poll | Claim pending jobs from the queue |
jobs:ack | Acknowledge receipt of a polled job |
jobs:progress | Send progress updates |
jobs:event | Publish checkpoint events |
jobs:complete | Mark jobs as completed |
jobs:fail | Mark jobs as failed |
jobs:keepalive | Extend a running job's timeout |
jobs:read:progress | Subscribe to single-job WebSocket streams — no space-wide feed, no REST |
Shorthand scopes (expand to multiple granular scopes):
| Shorthand | Expands to |
|---|---|
jobs:worker | jobs: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:write | jobs:ack + jobs:progress + jobs:event + jobs:complete + jobs:fail + jobs:keepalive |
Typical token setups:
| Use case | Scopes |
|---|---|
| Producer (submit jobs) | jobs:create |
| Worker (process jobs) | jobs:worker or select individual scopes |
| Dashboard / monitoring | jobs:read |
| Producer + monitor | jobs:create, jobs:read |
| Third-party client (live job updates) | jobs:read:progress |