# Workplace groups

Part of the [Proof API](/api/proof/index.md). Host: `https://api.geodynamics.dev`.

Workplace groups bundle workplaces so access can be granted per group rather than per site. A group has a name, an optional description, an optional internal code of your own, a set of workplaces, and a set of users who may reach them.

Every call needs a service key with the matching scope, sent as `Authorization: Bearer sk_...`. A key belongs to one environment, so the environment is never a parameter. A key that authenticates but lacks the scope for the call gets `403`, not `401`.

Errors come back in two shapes. A plain problem document carries `title`, `status` and `traceId`. A validation problem adds an `errors` object keyed by field. Which one you get for an unknown group id depends on the call: reading gives `404`, updating and deleting give `400` with `general.notFound` in `errors`, and setting the workplaces or users gives `404`. Handle both rather than switching on the status code alone.

## POST /proof/v1/workplacegroup

`https://api.geodynamics.dev/proof/v1/workplacegroup`

Privilege required: `public:scope:workplacegroup:manage`

Create workplace group

Creates a workplace group. Returns the new group's `id`.

The group starts empty. Add workplaces and users with their own calls.

### Request body (required)

Type: `object`

| Field | Type | Required |
| --- | --- | --- |
| `name` Display name of the group. | string | required |
| `description` Optional free-text description. | string | optional |
| `internalCode` Optional code of your own, to match the group against your system. Proof stores it and hands it back, nothing else. | string | optional |

```json
{
  "name": "Site North",
  "description": "All workplaces on the northern sites",
  "internalCode": "GRP-NORTH-01"
}
```

### Responses

#### 200: OK

| Field | Type |
| --- | --- |
| `id` Id of the created group. | string |

```json
{
  "id": "00000000-0000-0000-0000-000000000000"
}
```

#### 400: Validation failed. The body names the offending fields.

#### 401: Unauthorized. Missing or invalid service key.

#### 403: Forbidden. The key lacks the required scope.

## GET /proof/v1/workplacegroup/list

`https://api.geodynamics.dev/proof/v1/workplacegroup/list`

Privilege required: `public:scope:workplacegroup:read`

Get workplace groups

Returns a paged list of workplace groups in the key's environment, 100 per page.

`workplaceCount` and `userCount` are what the group holds today, so you can spot an empty group without fetching it.

### Query parameters

| Name | Type | Required | Example |
| --- | --- | --- | --- |
| `page` Zero-based page index. Defaults to 0. | integer | optional | `0` |

### Responses

#### 200: OK

| Field | Type |
| --- | --- |
| `items` Groups on this page. | array |
| `filter` Paging state for this query: page, pageSize, totalCount, and the sort and search fields the public API does not use. | object |

```json
{
  "items": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "name": "Site North",
      "description": "All workplaces on the northern sites",
      "internalCode": "GRP-NORTH-01",
      "workplaceCount": 12,
      "userCount": 4
    }
  ],
  "filter": {
    "page": 0,
    "pageSize": 100,
    "totalCount": 1,
    "sortAscending": true,
    "sortString": "",
    "searchString": ""
  }
}
```

#### 401: Unauthorized. Missing or invalid service key.

#### 403: Forbidden. The key lacks the required scope.

## GET /proof/v1/workplacegroup/{id}

`https://api.geodynamics.dev/proof/v1/workplacegroup/{id}`

Privilege required: `public:scope:workplacegroup:read`

Get workplace group by Id

Returns one group with its workplace identifications and the users attached to it.

Workplaces come back as identifications, the same handles you send when you set them, so a read and a write round-trip on the same values.

### Path parameters

| Name | Type | Required | Example |
| --- | --- | --- | --- |
| `id` Id of the group. | string | required | `00000000-0000-0000-0000-000000000000` |

### Responses

#### 200: OK

| Field | Type |
| --- | --- |
| `id` Id of the group. | string |
| `name` Display name. | string |
| `description` Free-text description, may be null. | string |
| `internalCode` Your own code for this group, may be null. | string |
| `workplaceIdentifications` Identifications of the workplaces in this group. | array |
| `users` Users attached to the group, with name, email and role. | array |

```json
{
  "id": "00000000-0000-0000-0000-000000000000",
  "name": "Site North",
  "description": "All workplaces on the northern sites",
  "internalCode": "GRP-NORTH-01",
  "workplaceIdentifications": [
    "WP-0042"
  ],
  "users": [
    {
      "userId": "00000000-0000-0000-0000-000000000000",
      "name": "John Doe",
      "email": "john.doe@example.com",
      "role": "Manager"
    }
  ]
}
```

#### 401: Unauthorized. Missing or invalid service key.

#### 403: Forbidden. The key lacks the required scope.

#### 404: No group with this id in the key's environment.

## PUT /proof/v1/workplacegroup/{id}

`https://api.geodynamics.dev/proof/v1/workplacegroup/{id}`

Privilege required: `public:scope:workplacegroup:manage`

Update workplace group

Replaces the name, description and internal code of a group, and returns its `id`. Workplaces and users are not touched, set those with their own calls.

This is a full-state replace of the three fields. Leaving out `description` or `internalCode` clears the stored value, it does not keep it. Send the fields you want the group to end up with.

### Path parameters

| Name | Type | Required | Example |
| --- | --- | --- | --- |
| `id` Id of the group. | string | required | `00000000-0000-0000-0000-000000000000` |

### Request body (required)

Type: `object`

| Field | Type | Required |
| --- | --- | --- |
| `name` Display name of the group. | string | required |
| `description` Free-text description. Omitting it clears the stored one. | string | optional |
| `internalCode` Your own code. Omitting it clears the stored one. | string | optional |

```json
{
  "name": "Site North and East",
  "description": "Merged northern and eastern sites",
  "internalCode": "GRP-NORTH-01"
}
```

### Responses

#### 200: OK

| Field | Type |
| --- | --- |
| `id` Id of the updated group. | string |

```json
{
  "id": "00000000-0000-0000-0000-000000000000"
}
```

#### 400: Validation failed. An unknown group id also lands here, as `general.notFound`, rather than as a 404. That differs from the read call on the same path, which does answer 404.

| Field | Type |
| --- | --- |
| `title` | string |
| `status` | integer |
| `errors` Field name to list of error messages. | object |
| `traceId` | string |

```json
{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "instance": "/public/v1/workplacegroup/00000000-0000-0000-0000-000000000000",
  "errors": {
    "general.notFound": [
      "A 'Not Found' error has occurred."
    ]
  },
  "traceId": "00-0000000000000000-0000000000-00"
}
```

#### 401: Unauthorized. Missing or invalid service key.

#### 403: Forbidden. The key lacks the required scope.

## DELETE /proof/v1/workplacegroup/{id}

`https://api.geodynamics.dev/proof/v1/workplacegroup/{id}`

Privilege required: `public:scope:workplacegroup:manage`

Delete workplace group

Deletes a group and returns `true`. The workplaces and users themselves are left alone, only the grouping disappears.

### Path parameters

| Name | Type | Required | Example |
| --- | --- | --- | --- |
| `id` Id of the group. | string | required | `00000000-0000-0000-0000-000000000000` |

### Responses

#### 200: OK

```json
true
```

#### 400: An unknown group id lands here, as `general.notFound`, rather than as a 404. Same shape as the update call above.

#### 401: Unauthorized. Missing or invalid service key.

#### 403: Forbidden. The key lacks the required scope.

## PUT /proof/v1/workplacegroup/{id}/workplaces

`https://api.geodynamics.dev/proof/v1/workplacegroup/{id}/workplaces`

Privilege required: `public:scope:workplacegroup:manage`

Set workplaces on a group

Replaces the full set of workplaces in the group. Send every workplace the group should contain, not just the additions. An empty list clears the group.

Workplaces are addressed by `identification`, the same value `/v1/workplace/list` returns, not by id.

The call is atomic. If any identification does not resolve in the key's environment, nothing is written and the response is a `400` carrying `WorkplacesNotInEnvironment` under `workplaceIdentifications`, with the identifications that failed listed under `unresolvedIdentifications`.

### Path parameters

| Name | Type | Required | Example |
| --- | --- | --- | --- |
| `id` Id of the group. | string | required | `00000000-0000-0000-0000-000000000000` |

### Request body (required)

Type: `object`

| Field | Type | Required |
| --- | --- | --- |
| `workplaceIdentifications` Complete set of workplace identifications for this group. An empty list or a missing field clears the group. | array | optional |

```json
{
  "workplaceIdentifications": [
    "WP-0042",
    "WP-0043"
  ]
}
```

### Responses

#### 200: OK

#### 400: One or more identifications do not exist in this environment. Nothing was written.

| Field | Type |
| --- | --- |
| `title` | string |
| `status` | integer |
| `errors` Field name to list of error messages. | object |
| `traceId` | string |

```json
{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "workplaceIdentifications": [
      "WorkplacesNotInEnvironment"
    ],
    "unresolvedIdentifications": [
      "WP-9999"
    ]
  },
  "traceId": "00-0000000000000000-0000000000-00"
}
```

#### 401: Unauthorized. Missing or invalid service key.

#### 403: Forbidden. The key lacks the required scope.

#### 404: No group with this id in the key's environment.

## PUT /proof/v1/workplacegroup/{id}/users

`https://api.geodynamics.dev/proof/v1/workplacegroup/{id}/users`

Privilege required: `public:scope:workplacegroup:manage`

Set users on a group

Replaces the full set of users attached to the group. Send every user who should have access, not just the additions. An empty list removes everyone.

Users are addressed by id, the `id` from `/v1/user/list`.

### Path parameters

| Name | Type | Required | Example |
| --- | --- | --- | --- |
| `id` Id of the group. | string | required | `00000000-0000-0000-0000-000000000000` |

### Request body (required)

Type: `object`

| Field | Type | Required |
| --- | --- | --- |
| `userIds` Complete set of user ids for this group. An empty list or a missing field removes everyone. | array | optional |

```json
{
  "userIds": [
    "00000000-0000-0000-0000-000000000000"
  ]
}
```

### Responses

#### 200: OK

#### 400: Validation failed. The body names the offending fields.

#### 401: Unauthorized. Missing or invalid service key.

#### 403: Forbidden. The key lacks the required scope.

#### 404: No group with this id in the key's environment.
