Durable background jobs over HTTP

Background jobs you can actually follow

Emit.run is HTTP-native job infrastructure. Enqueue with a POST, track progress over WebSocket, get callbacks when jobs finish. Workers report state at every step. Your team gets a live dashboard. No SDK required.

Start buildingRead the docsFree while in beta
emit.run / acme-corp / production3 queued1 running847 completed
send-welcome-emailcompleted
generate-invoice-pdf34%running
resize-user-avatarpending
sync-crm-contactcompleted
process-payment-webhookfailed

How it works

Three API calls. Full lifecycle.

1

Enqueue

POST /jobs

Send a job with a name, payload, and optional callback URL. You get back an ID and a realtime WebSocket URL. The job is persisted before the response returns.

2

Process

poll → ack → progress → complete

Your workers poll for jobs, acknowledge them, report progress, and mark them complete or failed. Retries, timeouts, and dead-letter routing are handled automatically.

3

Observe

WebSocket /realtime + callbackUrl

Subscribe to a WebSocket for live status and progress. When the job finishes, Emit.run POSTs the result to your callback URL. The dashboard shows everything.

See it work

Every event, as it happens

This is what Emit.run tracks for a single job — creation, delivery, acknowledgment, progress updates, completion, and callback delivery. All streamed in real time.

generate-invoice-pdf

job_a1b2c3d4e5f6...

pending
T+0ms

job.created

Job enqueued with callback URL

T+120ms

job.delivered

Worker claimed job via poll

T+145ms

job.acked

Worker confirmed receipt

T+1.2s

job.progress

34% — "Fetching source data"

T+3.8s

job.progress

78% — "Writing output"

T+4.1s

job.completed

Result payload stored

T+4.3s

callback.sent

POST yourapp.com/webhooks → 200 OK

const job = await fetch(`${BASE}/jobs`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "generate-invoice-pdf",
    payload: { invoiceId: "inv_8a3f..." },
    callbackUrl: "https://yourapp.com/webhooks/jobs",
  }),
}).then(r => r.json());

// -> { id, status, realtimeUrl }

Why Emit.run

Built for teams that ship background work

Durable by default

Jobs are persisted before your API returns. Worker crashes, deploys, or network blips don't lose work. At-least-once delivery with explicit acknowledgment.

Progress you can show users

Workers report percent and message updates. Subscribe via WebSocket and render real progress bars instead of spinners. No polling required.

Retries that just work

Configure max attempts per job. Failed work goes back to the queue with backoff. After all retries, it lands in dead state with full history.

Callbacks to your backend

Set a callback URL when you enqueue. Emit.run POSTs the result to your endpoint on completion, failure, or death — so downstream systems can react without polling.

Operator dashboard

See every job, its status, progress, retries, and event timeline. Kill stuck jobs. Replay failed ones. Filter by status. All updating in real time.

Plain HTTP, no SDK required

Every operation is a standard HTTP call. Use any language, any runtime. No client library to install, no vendor lock-in. curl works fine.

Full surface area

More than enqueue and complete

Jobs go through a lot more than start and finish. Emit.run tracks and exposes every operation in between.

Progress

Stream percent and message updates to your UI over WebSocket

Checkpoints

Save intermediate state so retried jobs can resume where they left off

Custom events

Attach arbitrary debug or admin payloads to a running job's timeline

Keepalive

Reset the execution timeout window during long-running work

Kill signals

Stop stuck or runaway jobs from the dashboard or API

Replay

Re-run completed, failed, dead, or killed jobs with one click

Scheduled jobs

Enqueue now, execute at a specific future time with scheduledFor

Event timeline

Every state change, progress update, and custom event recorded per job

Callbacks

POST results to your endpoint with custom headers on terminal states

Realtime streams

WebSocket feeds scoped to an entire space or a single job

Scoped tokens

Fine-grained permissions for workers, clients, and operators

Dead-letter routing

Exhausted retries land in dead state for review or replay

Start in five minutes

Create a space, grab an API key, enqueue your first job. No SDK to install — it's just HTTP. Free while in beta.