# Spaces

> Read and change a space's settings, list spaces, and count a space's jobs.

A [space](/docs/concepts/spaces) is created by its first job, or by setting it up here first.

## Get a space

`GET /v1/spaces/{space}` · scope `read`

```bash
curl -s "$EMIT_URL/v1/spaces/emails" -H "Authorization: Bearer $EMIT_TOKEN"
```

```json
{
  "name": "emails",
  "fifo": false,
  "defaultMaxAttempts": 4,
  "defaultBackoffMs": 1000,
  "defaultBackoff": { "kind": "EXPONENTIAL", "maxMs": 3600000, "multiplierPercent": 200, "jitter": "NONE" },
  "defaultTimeoutMs": "300000",
  "unclaimedMs": "604800000",
  "maxEvents": 1000,
  "maxUnfinished": "0",
  "claimsPerMinute": 0,
  "publicSubscriptions": "SIGNED",
  "webhookSecret": ""
}
```

A space that has no jobs and was never set up reads as the defaults. The answer also has `keysHonouredMs`, `retainHotMs` and `retainMs`, which are always `"0"` here: those windows are set for the whole cluster, not per space.

## Change a space

`PUT /v1/spaces/{space}` · scope `admin`

Creates the space, or replaces its settings. The body is the whole set: a setting you leave out goes back to its default, except `webhookSecret`, `defaultBackoff` and `unclaimedMs`, which stay as they are when left out. To change one setting, get the space, change it, and send it all back.

:::fields
:::field defaultMaxAttempts {type="integer" default="4"}
Tries for each job, counting the first. From 1 to 100.
:::
:::field defaultBackoffMs {type="integer" default="1000"}
The wait after a job's first failure, in milliseconds.
:::
:::field defaultBackoff {type="object" default="exponential, ×2, up to an hour"}
How the waits after that grow: `kind` (`EXPONENTIAL`, `LINEAR` or `CONSTANT`), `maxMs` (the longest wait, at most an hour), `multiplierPercent` (100 to 1000; 200 doubles), `jitter` (`NONE`, `FULL` or `EQUAL`). Send `{}` to go back to the default.
:::
:::field defaultTimeoutMs {type="integer" default="300000"}
The lease a worker gets on each attempt, unless it asks for another.
:::
:::field unclaimedMs {type="integer" default="604800000 (7 days)"}
A job no worker claims within this time fails for good. `0` turns it off; otherwise from a second to a year.
:::
:::field maxEvents {type="integer" default="1000"}
Events kept per job, up to 3,000.
:::
:::field maxUnfinished {type="integer" default="0"}
Refuse new jobs with `429` while the space has this many unfinished. Applied on each shard of the cluster separately. `0` is no limit.
:::
:::field claimsPerMinute {type="integer" default="0"}
Jobs handed to workers per minute. Applied on each shard separately. `0` is no limit.
:::
:::field fifo {type="boolean" default="false"}
Hand jobs out strictly in the order they were created. Set it before the space's first job: it can't change after.
:::
:::field publicSubscriptions {type="string" default="SIGNED"}
Who may follow a job without an API token: `SIGNED` (with a follow token), `UNSIGNED` (anyone with the id) or `DISABLED`.
:::
:::field webhookSecret {type="string"}
Signs callbacks and wake-ups, up to 256 characters. Write-only: it always reads back empty. Leave it out, or empty, to keep the current one.
:::
:::field wake {type="object"}
Where to POST when jobs are waiting: `url`, optional `headers`, `names` (only these job names; empty for all), `perWake` (jobs one wake-up stands for; 1 unless set) and `rewakeMs` (wake again after this while jobs are unclaimed; 30,000 unless set). See [Wake-ups](/docs/concepts/spaces#wake-ups).
:::
:::

:::code-group
```bash curl
curl -s -X PUT "$EMIT_URL/v1/spaces/reports" \
  -H "Authorization: Bearer $EMIT_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "defaultMaxAttempts": 6,
    "defaultTimeoutMs": 600000,
    "webhookSecret": "whsec_4f9c1e7b2a8d"
  }'
```
```ts TypeScript
await admin.spaces.configure("reports", { defaultMaxAttempts: 6, defaultTimeoutMs: 600_000, webhookSecret: "whsec_4f9c1e7b2a8d" });
```
```go Go
_, err := admin.Spaces.Configure(ctx, emit.Space{Name: "reports", DefaultMaxAttempts: 6, DefaultTimeoutMs: 600_000, WebhookSecret: "whsec_4f9c1e7b2a8d"})
```
:::

The answer is the space as it is now, in the same shape as [Get a space](#get-a-space).

## List spaces

`GET /v1/spaces` · scope `admin`

```bash
curl -s "$EMIT_URL/v1/spaces" -H "Authorization: Bearer $EMIT_ADMIN_TOKEN"
```

Answers `{"spaces": [...]}`, each as [Get a space](#get-a-space) shows it.

## Count a space's jobs

`GET /v1/spaces/{space}/counts` · scope `read`

```bash
curl -s "$EMIT_URL/v1/spaces/orders/counts" -H "Authorization: Bearer $EMIT_TOKEN"
```

```json
{
  "byStatus": { "pending": "2", "running": "0", "completed": "1", "failed": "0", "cancelled": "1" },
  "byName": {
    "send-receipt": {
      "byStatus": { "pending": "0", "running": "0", "completed": "1", "failed": "0", "cancelled": "0" },
      "claimedAtMs": "1790726132760"
    }
  }
}
```

Pending and running counts are what's there now. Completed, failed and cancelled are totals since the space began. `claimedAtMs` is when a worker last took a job of that name, `"0"` for never: a quick way to spot a job name that nobody serves.
