# Quickstart

> Create a cluster, send your first job, and run it in a worker.

You'll create a free cluster, send a `send-welcome-email` job to it, and run that job in a worker on your machine. You need Node.js 20.3 or later, Go 1.23 or later, or just `curl`.

:::steps
:::step Create a cluster
Sign in at [emit.run](https://emit.run) and choose **New cluster**. Give it a name, pick a region and choose the **Free** plan. It needs no card.

A **cluster** is your own emit service, with its own address. Its **Overview** page shows that address, the endpoint your code connects to.
:::
:::step Create an API token
Open the cluster's **Tokens** tab and create a token. Leave **Spaces** blank and keep the scopes it starts with: `create`, `work` and `read`.

The secret is shown once. Copy the `.env` block it offers, which holds both values you need:

```bash .env
EMIT_URL=https://<your cluster's endpoint>
EMIT_TOKEN=emit_...
```

Load them into your shell with `export $(cat .env)`, or however your project reads `.env` files.
:::
:::step Install an SDK
:::code-group
```bash TypeScript
npm init -y
npm pkg set type=module   # the examples use top-level await
npm install @emit.run/sdk
```
```bash Go
go mod init welcome
go get github.com/emit-run/sdk-go
```
```bash curl
# Nothing to install: every step below is a plain HTTP request.
```
:::
:::
:::step Run a worker
A **worker** asks emit for jobs by name, runs them, and reports back. Jobs live in a **space**, a named queue; these examples use one called `emails`, which is created by its first job, so the worker can start before it exists. Returning a value completes the job. Throwing, or returning an error, fails this attempt, and emit tries it again later.

:::code-group
```ts TypeScript
// worker.ts
import { Emit } from "@emit.run/sdk";

const emit = new Emit({
  url: process.env.EMIT_URL!,
  token: process.env.EMIT_TOKEN!,
  space: "emails",
});

type Welcome = { userId: string; email: string };

const worker = emit.worker<Welcome>({ names: ["send-welcome-email"] }, async (job) => {
  await job.progress(50, "rendering");
  console.log(`sending the welcome email to ${job.payload.email}`);
  return { sent: true };
});

// Finish the jobs in hand before the process exits.
process.once("SIGINT", () => worker.stop());
```
```go Go
// main.go
package main

import (
	"context"
	"log"
	"os"
	"os/signal"

	emit "github.com/emit-run/sdk-go"
)

type Welcome struct {
	UserID string `json:"userId"`
	Email  string `json:"email"`
}

func main() {
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
	defer stop()

	c := emit.New(emit.Options{URL: os.Getenv("EMIT_URL"), Token: os.Getenv("EMIT_TOKEN"), Space: "emails"})

	worker := c.Worker(ctx, emit.WorkerOptions{Names: []string{"send-welcome-email"}}, func(ctx context.Context, job *emit.HeldJob) (any, error) {
		var in Welcome
		if err := job.Decode(&in); err != nil {
			return nil, emit.Unrecoverable(err) // a payload we can't read won't get better on a retry
		}
		if err := job.Progress(ctx, 50, "rendering"); err != nil {
			return nil, err
		}
		log.Printf("sending the welcome email to %s", in.Email)
		return map[string]bool{"sent": true}, nil
	})

	select {
	case <-ctx.Done(): // Ctrl-C
	case <-worker.Done(): // the worker stopped on its own, for example a revoked token
	}
	if err := worker.Stop(); err != nil {
		log.Fatal(err)
	}
}
```
```bash curl
# Ask for one send-welcome-email job, waiting up to 30 seconds for one to arrive.
curl -s "$EMIT_URL/v1/spaces/emails/jobs:claim" \
  -H "Authorization: Bearer $EMIT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"names": ["send-welcome-email"], "count": 1, "waitMs": 30000}'

# The answer holds the job's id and attempt. Report the result with both:
curl -s "$EMIT_URL/v1/spaces/emails/jobs:report" \
  -H "Authorization: Bearer $EMIT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"id": "<job id>", "attempt": 1, "complete": {"result": {"sent": true}}}'
```
:::

Start it and leave it running: `npx tsx worker.ts` (npx fetches `tsx` the first time; on Node.js 22.18 or later, `node worker.ts` works too), or `go run .`.
:::
:::step Send a job
From another terminal, create a job and wait for its result (`npx tsx send.ts`, or `go run ./send`).

:::code-group
```ts TypeScript
// send.ts
import { Emit } from "@emit.run/sdk";

const emit = new Emit({
  url: process.env.EMIT_URL!,
  token: process.env.EMIT_TOKEN!,
  space: "emails",
});

const { id } = await emit.jobs.create("send-welcome-email", {
  userId: "usr_42",
  email: "ada@example.com",
});
console.log("created", id);

const result = await emit.jobs.result(id); // follows the job until it ends
console.log("result", result); // { sent: true }
```
```go Go
// send/main.go
package main

import (
	"context"
	"log"
	"os"

	emit "github.com/emit-run/sdk-go"
)

func main() {
	ctx := context.Background()
	c := emit.New(emit.Options{URL: os.Getenv("EMIT_URL"), Token: os.Getenv("EMIT_TOKEN"), Space: "emails"})

	created, err := c.Jobs.Create(ctx, emit.NewJob{
		Name:    "send-welcome-email",
		Payload: map[string]string{"userId": "usr_42", "email": "ada@example.com"},
	})
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("created %s", created.ID)

	var result struct {
		Sent bool `json:"sent"`
	}
	if err := c.Jobs.Result(ctx, created.ID, &result); err != nil { // follows the job until it ends
		log.Fatal(err)
	}
	log.Printf("sent: %v", result.Sent)
}
```
```bash curl
curl -s "$EMIT_URL/v1/spaces/emails/jobs" \
  -H "Authorization: Bearer $EMIT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "send-welcome-email", "payload": {"userId": "usr_42", "email": "ada@example.com"}}'
# {"created":[{"id":"01M3QSGN0103P7EGVBX9D5NTSN","outcome":"NEW","attempt":0,...}]}

# Read it back: its status, progress and result.
curl -s "$EMIT_URL/v1/spaces/emails/jobs/01M3QSGN0103P7EGVBX9D5NTSN" \
  -H "Authorization: Bearer $EMIT_TOKEN"
```
:::

The worker prints the email address, and the sender prints `{ sent: true }`. In the dashboard, open the `emails` space from the cluster's **Spaces** tab to see the job with its progress and result.
:::
:::

## Next

:::cards {cols=2}
:::card Jobs {href="/docs/concepts/jobs"}
Retries, delays, idempotency keys and callbacks.
:::
:::card Show progress in a browser {href="/docs/guides/browser-progress"}
Follow a job live from your frontend.
:::
:::card Workflows {href="/docs/concepts/workflows"}
Chain steps, sleep and wait for events in one function.
:::
:::card TypeScript SDK {href="/docs/sdks/typescript"}
Every option of the client and the worker.
:::
:::
