# Spaces

> A space is a named queue of jobs with its own settings, such as retries, timeouts and who may follow its jobs.

A **space** is a named set of jobs in your cluster, with its own settings. Use one per area of your product, like `emails`, `billing` or `imports`, so each can have its own retry policy, its own limits and its own tokens.

Every job lives in exactly one space, and the space is part of every job's address: `/v1/spaces/emails/jobs/{id}`. An SDK client works in one space, which you name when you create it.

## Creating a space

You don't have to. A space is created by its first job, with the default settings. To choose its settings first, set it up on the cluster's **Spaces** tab in the dashboard, or configure it through the API before you send any jobs.

A space's name is up to 256 characters, with no `/`, and can't start with `_emit.`. Spaces can't be deleted, but an empty space costs nothing.

## Settings

A job takes its space's defaults unless it says otherwise when it's created.

| Setting | Default | What it does |
| --- | --- | --- |
| Max attempts (`defaultMaxAttempts`) | 4 | Tries for each job, counting the first. `1` means no retries. At most 100. |
| First retry after (`defaultBackoffMs`) | 1 second | The wait after the first failure. |
| Retry growth (`defaultBackoff`) | exponential, ×2, up to an hour, no jitter | How the waits after that grow: `exponential` (by a factor), `linear` (the first wait times the retry number) or `constant`. The longest wait is at most an hour. Jitter spreads retries out: `full` waits anywhere up to the wait, `equal` from half of it to all of it. |
| Timeout (`defaultTimeoutMs`) | 5 minutes | How long a worker holds a job before its [lease](/docs/concepts/jobs#leases-and-timeouts) runs out, unless the worker renews it. The SDKs renew it for you. |
| Fail jobs nobody claims after (`unclaimedMs`) | 7 days | A job no worker claims within this time fails, with the error `not claimed within 7d`. `0` turns it off. From a second to a year. |
| Events kept per job (`maxEvents`) | 1,000 | Up to 3,000. |
| In-flight limit (`maxUnfinished`) | none | New jobs wait while the space has this many unfinished; a batch larger than the limit is refused. See [When emit is busy](/docs/concepts/jobs#when-emit-is-busy). |
| Claims per minute (`claimsPerMinute`) | none | How many jobs workers may take from the space in a minute. |
| In order (`fifo`) | off | Hands jobs out in the order they were created. See [FIFO spaces](#fifo-spaces). |
| Who may follow a job (`publicSubscriptions`) | signed | Whether a browser may follow a job without an API token. See [Show progress in a browser](/docs/guides/browser-progress). |
| Webhook secret (`webhookSecret`) | none | Signs the space's callbacks and wake-ups. Write-only: it's never shown again. |
| Wake URL (`wake`) | none | A URL emit calls when jobs are waiting, for workers that can't hold a connection open. See [Wake-ups](#wake-ups). |

A cluster splits each space's jobs over a few **shards** so it can use all its machines. The in-flight limit and claims per minute are applied on each shard separately, so the space's total can be a few times the number you set. Use them as a brake on runaway load, not an exact count.

Changes to retries and timeouts apply to jobs created after the change. Everything else applies at once.

### Changing settings

In the dashboard, open the space from the cluster's **Spaces** tab and choose **Space settings**. Through an SDK or the API, a token with the `admin` scope sends the whole set of settings. A setting you leave out goes back to its default, except the webhook secret, the retry growth and the unclaimed window, which stay as they are. To change one setting, read the space first and send it back changed:

:::code-group
```ts TypeScript
const { admin } = new Emit({ url: process.env.EMIT_URL!, token: process.env.EMIT_ADMIN_TOKEN!, space: null });

const { name, ...settings } = await admin.spaces.get("emails");
await admin.spaces.configure("emails", { ...settings, defaultMaxAttempts: 8 });
```
```go Go
admin := emit.New(emit.Options{URL: os.Getenv("EMIT_URL"), Token: os.Getenv("EMIT_ADMIN_TOKEN")}).Admin

space, err := admin.Spaces.Get(ctx, "emails")
if err != nil {
	return err
}
space.DefaultMaxAttempts = 8
if _, err := admin.Spaces.Configure(ctx, space); err != nil {
	return err
}
```
```bash curl
curl -s "$EMIT_URL/v1/spaces/emails" -H "Authorization: Bearer $EMIT_ADMIN_TOKEN" \
  | jq '.defaultMaxAttempts = 8' \
  | curl -s -X PUT "$EMIT_URL/v1/spaces/emails" \
  -H "Authorization: Bearer $EMIT_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d @-
```
:::

The [Spaces API](/docs/api/spaces) lists every field.

## FIFO spaces

Without FIFO, workers get the oldest jobs first, but not in strict order: a space is spread over your whole cluster so it can use all of it, and jobs created close together may be handed out in a slightly different order.

A **FIFO** space hands jobs out strictly in the order they were created. A job that's delayed, or waiting to be retried, takes its place in the order when its wait ends. The price is throughput: a FIFO space runs on a single shard, so it can't spread its work over the whole cluster.

FIFO is fixed when the space gets its first job. Turn it on before then, when you set the space up.

## Wake-ups

A worker usually holds a connection open and waits for jobs. A serverless function can't. Give the space a **wake URL**, and emit POSTs to it when jobs are waiting that nobody has been woken for, and again every 30 seconds while they're still unclaimed. The body names the space and how many jobs of each name are waiting:

```json
{ "event": "work.pending", "space": "emails", "names": { "send-welcome-email": 3 }, "claim": { "count": 3 }, "timestamp": 1790726272531 }
```

Your function then claims jobs and reports on them like any worker. A wake-up is signed like a callback, with the same `Emit-Signature` header. You can limit wake-ups to some job names, and set how many jobs one wake-up stands for.

## Tokens and spaces

An API token can be limited to some spaces. A token for `emails` can't read or create jobs in `billing`. See [Authentication](/docs/api/authentication).
