A workstation is a place where orders are packed, such as a desk in the warehouse. Each order waits at one workstation at a time (`workstationId` on the [order](/docs/api-reference/orders)), or at none (unassigned). Assignment rules send orders to a workstation automatically when they become ready to pack.

Reading needs the `orders:read` scope; changes need `orders:write` and the Growth plan (`403` otherwise). Deleting a workstation is not plan-gated.

## The Workstation object

| Field | Type | Description |
|-------|------|-------------|
| `id` | `string` | UUID |
| `name` | `string` | Name shown to packers, unique in the store |
| `type` | `string` | `packing`, `both` (picking and packing) or `picking`. Only active `packing` and `both` workstations take orders; `picking` is stored for the picking flow. |
| `active` | `boolean` | An inactive workstation takes no orders |
| `position` | `number` | Display order |
| `memberIds` | `string[]` | Team members (ids from `GET /api/v1/team/members`) whose packing view opens on this workstation |
| `waitingCount` | `number` | Orders waiting to be packed there |
| `createdAt`, `updatedAt` | `string` | ISO timestamps |

---

## List Workstations

```
GET /api/v1/workstations
```

```bash
curl -H "Authorization: Bearer your_api_key" \
  https://your-store.yns.store/api/v1/workstations
```

### Response (200)

```json
{
  "data": [
    {
      "id": "0192e3a0-1234-7def-8000-000000000001",
      "name": "Desk 1",
      "type": "packing",
      "active": true,
      "position": 0,
      "memberIds": ["mbr_8f2c1d"],
      "waitingCount": 4,
      "createdAt": "2026-10-12T08:00:00.000Z",
      "updatedAt": "2026-10-12T08:00:00.000Z"
    }
  ]
}
```

---

## Create Workstation

```
POST /api/v1/workstations
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `name` | `string` | – | Required, 1-60 characters, unique in the store |
| `type` | `string` | `packing` | `packing`, `both` or `picking` |
| `active` | `boolean` | `true` | |
| `memberIds` | `string[]` | `[]` | Ids outside the store are ignored |

Returns the created workstation with `201`, or `409` when the name is taken.

```bash
curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Allegro packing" }' \
  https://your-store.yns.store/api/v1/workstations
```

---

## Get Workstation

```
GET /api/v1/workstations/:id
```

Returns one workstation, or `404`.

---

## Update Workstation

```
PATCH /api/v1/workstations/:id
```

Send only the fields that change; `memberIds` replaces the list. A workstation that stops taking orders (switched off or changed to `picking`) hands its waiting orders back to unassigned, where the rules place them again. The response is the workstation plus `releasedOrderCount`.

```bash
curl -X PATCH \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "active": false }' \
  https://your-store.yns.store/api/v1/workstations/0192e3a0-1234-7def-8000-000000000001
```

---

## Delete Workstation

```
DELETE /api/v1/workstations/:id
```

Its waiting orders go back to unassigned, and it is removed from every rule (a rule left with no workstation is deleted).

### Response (200)

```json
{ "id": "0192e3a0-1234-7def-8000-000000000001", "deleted": true, "releasedOrderCount": 4 }
```

---

## Assignment Rules

When an order becomes ready to pack (paid or processing, not packed), the rules run top to bottom and the first match sends it to a workstation. An order assigned by hand – in the admin or with `workstationId` on [Update Order](/docs/api-reference/orders#update-order) – is never moved by the rules. Counter sales are never placed, and a store with no rules leaves every order unassigned.

| Field | Type | Description |
|-------|------|-------------|
| `sources` | `string[]` | Sales channels: `storefront`, `baselinker`, `allegro`, `inpost-buy`, `wholesale`, `admin`, `counter`. Empty matches any. |
| `shippingIds` | `string[]` | Shipping method ids from the [Shipping API](/docs/api-reference/shipping). Empty matches any. |
| `workstationIds` | `string[]` | One workstation, or several: the order goes to the active one with the fewest orders waiting. |

A rule with both `sources` and `shippingIds` empty catches every other order; put it last.

### List Rules

```
GET /api/v1/workstations/rules
```

```json
{
  "data": [
    { "position": 0, "sources": ["allegro"], "shippingIds": [], "workstationIds": ["0192e3a0-1234-7def-8000-000000000002"] },
    { "position": 1, "sources": [], "shippingIds": [], "workstationIds": ["0192e3a0-1234-7def-8000-000000000001", "0192e3a0-1234-7def-8000-000000000003"] }
  ]
}
```

### Replace Rules

```
PUT /api/v1/workstations/rules
```

Replaces the whole list in the order sent, then places the orders already waiting to be decided. Workstation ids from another store are dropped; a rule left with none is a `400`. The response is the saved list plus `placedOrderCount`.

```bash
curl -X PUT \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "rules": [
      { "sources": ["allegro"], "workstationIds": ["0192e3a0-1234-7def-8000-000000000002"] },
      { "workstationIds": ["0192e3a0-1234-7def-8000-000000000001", "0192e3a0-1234-7def-8000-000000000003"] }
    ]
  }' \
  https://your-store.yns.store/api/v1/workstations/rules
```

---

## Orders and workstations

- `GET /api/v1/orders?workstation=<id>` lists one workstation's orders; `?workstation=unassigned` lists orders at none.
- Every order carries `workstationId` (null when unassigned). It is left out for storefront lookups made with the `order:lookup` scope.
- `PATCH /api/v1/orders/:id` with `{ "workstationId": "<id>" }` queues the order at a workstation (`null` moves it back to unassigned). See [Update Order](/docs/api-reference/orders#update-order).