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.
How it works
Three API calls. Full lifecycle.
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.
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.
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...
job.created
Job enqueued with callback URL
job.delivered
Worker claimed job via poll
job.acked
Worker confirmed receipt
job.progress
34% — "Fetching source data"
job.progress
78% — "Writing output"
job.completed
Result payload stored
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.