emit.run

Quick Start

Get started with emit.run in minutes

Quick Start

This guide walks through the full flow: create a space, get an API key, submit a job, process it with a worker, and watch it complete.

1. Create a Space

In the dashboard, go to SpacesCreate Space. Give it a name like production or email-jobs.

2. Create API Tokens

Open your space and go to TokensCreate Token.

You'll typically want two tokens:

TokenScopesUsed by
my-producerjobs:createYour app that submits jobs
my-workerWorker preset (or jobs:worker)Your worker that processes jobs

Copy each key when it appears — it's shown only once.

3. Create a Job

Submit a job to the dispatch queue. The name is how your worker knows what to do; payload is the data it receives.

const res = await fetch(
  `https://emit.run/api/v1/spaces/${spaceId}/jobs`,
  {
    method: "POST",
    headers: {
      "x-api-key": process.env.EMIT_PRODUCER_KEY!,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "send-email",
      payload: {
        to: "user@example.com",
        subject: "Welcome!",
        template: "onboarding",
      },
    }),
  }
);

const job = await res.json();
console.log("Created job:", job.id); // { id: "01JLQX...", status: "pending", ... }
curl -X POST https://emit.run/api/v1/spaces/YOUR_SPACE_ID/jobs \
  -H "x-api-key: emit_YOUR_PRODUCER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "send-email",
    "payload": {
      "to": "user@example.com",
      "subject": "Welcome!",
      "template": "onboarding"
    }
  }'
import requests

res = requests.post(
f"https://emit.run/api/v1/spaces/{space_id}/jobs",
headers={
"x-api-key": EMIT_PRODUCER_KEY,
"Content-Type": "application/json",
},
json={
"name": "send-email",
"payload": {
"to": "user@example.com",
"subject": "Welcome!",
"template": "onboarding",
},
},
)

job = res.json()
print("Created job:", job["id"]) # { "id": "01JLQX...", "status": "pending", ... }

Optional: webhook callback

Add callbackUrl to get a POST notification when the job completes or goes dead — no polling required:

{
  "name": "send-email",
  "payload": { "to": "user@example.com", "template": "onboarding" },
  "callbackUrl": "https://example.com/webhooks/jobs",
  "callbackHeaders": { "Authorization": "Bearer whsec_your_secret" }
}

See Callbacks for the full payload shape.

4. Build a Worker

A worker is a loop that polls for jobs, processes them, and reports back. Workers need no SDK — it's plain HTTP.

const API = "https://emit.run/api/v1";
const KEY = process.env.EMIT_WORKER_KEY!;
const SPACE = process.env.EMIT_SPACE_ID!;
const headers = { "x-api-key": KEY, "Content-Type": "application/json" };

async function work() {
while (true) {
// 1. Poll for up to 5 jobs
const pollRes = await fetch(`${API}/spaces/${SPACE}/jobs/poll`, {
method: "POST",
headers,
body: JSON.stringify({ count: 5 }),
});
const { jobs } = await pollRes.json();

    if (jobs.length === 0) {
      await new Promise((r) => setTimeout(r, 2000)); // back off
      continue;
    }

    for (const job of jobs) {
      // 2. Ack — tells the system we're starting; starts the timeout timer
      await fetch(`${API}/jobs/${job.id}/ack`, { method: "POST", headers });

      try {
        // 3. Do the actual work
        const result = await processJob(job);

        // 4. Report success
        await fetch(`${API}/jobs/${job.id}/complete`, {
          method: "POST",
          headers,
          body: JSON.stringify({ result }),
        });
      } catch (err) {
        // 4. Report failure — retries automatically if attempts remain
        await fetch(`${API}/jobs/${job.id}/fail`, {
          method: "POST",
          headers,
          body: JSON.stringify({ error: String(err) }),
        });
      }
    }

}
}

async function processJob(job: { name: string; payload: any }) {
switch (job.name) {
case "send-email":
// send the email...
return { sent: true };
default:
throw new Error(`Unknown job type: ${job.name}`);
}
}

work();
import os
import time
import requests

API = "https://emit.run/api/v1"
KEY = os.environ["EMIT_WORKER_KEY"]
SPACE = os.environ["EMIT_SPACE_ID"]
HEADERS = {"x-api-key": KEY, "Content-Type": "application/json"}

def work():
    while True:
        # 1. Poll for up to 5 jobs
        res = requests.post(
            f"{API}/spaces/{SPACE}/jobs/poll",
            headers=HEADERS,
            json={"count": 5},
        )
        jobs = res.json()["jobs"]

        if not jobs:
            time.sleep(2)  # back off
            continue

        for job in jobs:
            # 2. Ack — tells the system we're starting; starts the timeout timer
            requests.post(f"{API}/jobs/{job['id']}/ack", headers=HEADERS)

            try:
                # 3. Do the actual work
                result = process_job(job)

                # 4. Report success
                requests.post(
                    f"{API}/jobs/{job['id']}/complete",
                    headers=HEADERS,
                    json={"result": result},
                )
            except Exception as e:
                # 4. Report failure — retries automatically if attempts remain
                requests.post(
                    f"{API}/jobs/{job['id']}/fail",
                    headers=HEADERS,
                    json={"error": str(e)},
                )

def process_job(job):
    if job["name"] == "send-email":
        # send the email...
        return {"sent": True}
    raise ValueError(f"Unknown job type: {job['name']}")

work()

5. Watch Progress (Optional)

The recommended pattern for client-facing apps: your backend creates the job and returns the job ID, your frontend connects using the space's public token to stream live updates.

Every space comes with a pre-generated public token (jobs:read:progress scope) that's safe to embed in client-side code — it can only subscribe to individual job streams and can't create, poll, or see job results.

Find it in the dashboard under TokensPublic read-only (progress).

// Backend: create the job, return its ID to the client
app.post("/api/send-email", async (req, res) => {
  const job = await fetch(`${API}/spaces/${SPACE}/jobs`, {
    method: "POST",
    headers: { "x-api-key": process.env.EMIT_PRODUCER_KEY!, "Content-Type": "application/json" },
    body: JSON.stringify({ name: "send-email", payload: req.body }),
  }).then((r) => r.json());

  res.json({ jobId: job.id });
  // PUBLIC_TOKEN comes from your space dashboard — safe to expose to clients
});

// Frontend: watch the job using the public token
const { jobId } = await submitEmailRequest(formData);
const ws = new WebSocket(
  `wss://emit.run/api/v1/realtime/${jobId}?token=${SPACE_PUBLIC_TOKEN}`
);

ws.onmessage = (e) => {
  const event = JSON.parse(e.data);

  switch (event.type) {
    case "progress":
      updateProgressBar(event.data.percent);
      break;
    case "completed":
      showSuccess(); // result payload is stripped — client just knows it's done
      ws.close();
      break;
    case "dead":
      showError(); // error detail is stripped — client just knows it failed
      ws.close();
      break;
  }
};

See WebSockets for the full event reference and space-level streams.

Next Steps

  • Authentication — Full scope reference and auth details
  • Jobs API — Complete endpoint reference with examples
  • Callbacks — Webhook notifications on job completion
  • WebSockets — Real-time streaming setup

On this page