# Semaphores

A semaphore limits how many tests use one shared resource at a time, such as a sandbox account or a rate-limited vendor API, across every worker and every run in the workspace. Runs take, renew and release leases with the run token from starting a run or a retry, at `POST /v1/workspaces/{slug}/semaphores/{name}/leases`, `POST …/leases/{lease}/renew` and `DELETE …/leases/{lease}`. The first lease creates the semaphore with its capacity, and finishing a run releases every lease it still holds.

## The Semaphore object

A named limit on how many tests may use a resource at once, with its holders.

### Attributes

- `name` (string, required): The semaphore's name in the workspace, such as payments-sandbox.
- `capacity` (number, required): How many leases it allows at once, 1 to 100. The first lease sets it.
- `holders` (array of objects, required): The leases held now, oldest first.
  - `lease` (string, required): The lease's ID.
  - `run` (nullable number, required): The number of the run that holds it, or null.
  - `retry` (nullable number, required): The number of the retry that holds it, or null when the run itself does.
  - `test` (nullable string, required): The test that took it, as the run reported, or null.
  - `since` (string, required): When the lease was taken, as an ISO 8601 timestamp.
  - `expiresAt` (string, required): When the lease ends unless its holder renews it, as an ISO 8601 timestamp.

### Example

```json
{
  "name": "payments-sandbox",
  "capacity": 1,
  "holders": [
    {
      "lease": "Ls4TnV8qWm2C",
      "run": 1289,
      "retry": null,
      "test": "checkout › applies a coupon",
      "since": "2026-10-14T09:40:20.000Z",
      "expiresAt": "2026-10-14T09:41:20.000Z"
    }
  ]
}
```

## List semaphores

`GET /v1/workspaces/{slug}/semaphores`

Returns each semaphore with its capacity and the leases held now.

### Parameters

- `slug` (string, in path, required): The workspace's slug or ID, such as acme.

### Returns

Returns an object with these fields. The status is 200.

- `semaphores` (array of objects, required): Every semaphore in the workspace, by name. See [the Semaphore object](https://docs.qa.clankership.net/api/semaphores/object.md).
  - `name` (string, required): The semaphore's name in the workspace, such as payments-sandbox.
  - `capacity` (number, required): How many leases it allows at once, 1 to 100. The first lease sets it.
  - `holders` (array of objects, required): The leases held now, oldest first.
    - `lease` (string, required): The lease's ID.
    - `run` (nullable number, required): The number of the run that holds it, or null.
    - `retry` (nullable number, required): The number of the retry that holds it, or null when the run itself does.
    - `test` (nullable string, required): The test that took it, as the run reported, or null.
    - `since` (string, required): When the lease was taken, as an ISO 8601 timestamp.
    - `expiresAt` (string, required): When the lease ends unless its holder renews it, as an ISO 8601 timestamp.

### Request

```bash
curl https://api.qa.clankership.net/v1/workspaces/{slug}/semaphores \
  -H "Authorization: Bearer $CLANKER_API_KEY"
```

### Response

```json
{
  "semaphores": [
    {
      "name": "payments-sandbox",
      "capacity": 1,
      "holders": [
        {
          "lease": "Ls4TnV8qWm2C",
          "run": 1289,
          "retry": null,
          "test": "checkout › applies a coupon",
          "since": "2026-10-14T09:40:20.000Z",
          "expiresAt": "2026-10-14T09:41:20.000Z"
        }
      ]
    }
  ]
}
```

### Errors

- `400` `InvalidInput`: The request is malformed, or a value is not allowed.
- `401` `Unauthorized`: The request has no valid API key.
- `403` `Forbidden`: The key's user or role may not do this.
- `404` `NotFound`: The workspace or object does not exist, or the caller cannot see it.
- `409` `Conflict`: The change conflicts with current data, such as a slug that is already in use.
- `500` `Internal`: Clanker Ship failed to handle the request. Retry, or report the response's x-request-id.
