# Open Mercato API

Version: 0.9.0

Auto-generated OpenAPI definition for all enabled modules.

## Servers
- https://hr.office.dac.systems/api – Default environment

## DELETE `/api_keys/keys`

Delete API key

Removes an API key by identifier. The key must belong to the current tenant and fall within the requester organization scope.

**Tags:** API Keys

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. API key identifier to delete |

### Responses

**200** – Key deleted successfully

Content-Type: `application/json`

```json
{
  "success": true
}
```

**400** – Missing or invalid identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Organization outside allowed scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Key not found within scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/api_keys/keys?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/api_keys/keys`

List API keys

Returns paginated API keys visible to the current user, including per-key role assignments and organization context.

**Tags:** API Keys

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |

### Responses

**200** – Collection of API keys

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "name": "string",
      "description": null,
      "keyPrefix": "string",
      "organizationId": null,
      "organizationName": null,
      "createdAt": "string",
      "lastUsedAt": null,
      "expiresAt": null,
      "roles": [
        {
          "id": "string",
          "name": null
        }
      ]
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

**400** – Tenant context missing

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden by organization scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/api_keys/keys" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/api_keys/keys`

Create API key

Creates a new API key, returning the one-time secret value together with the generated key prefix and scope details.

**Tags:** API Keys

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "name": "string",
  "description": null,
  "tenantId": null,
  "organizationId": null,
  "roles": [],
  "expiresAt": null
}
```

### Responses

**201** – API key created successfully

Content-Type: `application/json`

```json
{
  "id": "string",
  "name": "string",
  "keyPrefix": "string",
  "tenantId": null,
  "organizationId": null,
  "roles": [
    {
      "id": "string",
      "name": null
    }
  ]
}
```

**400** – Invalid payload or missing tenant context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Organization outside allowed scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/api_keys/keys" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\",
  \"description\": null,
  \"tenantId\": null,
  \"organizationId\": null,
  \"roles\": [],
  \"expiresAt\": null
}"
```

## DELETE `/attachments`

Delete attachment

Removes an uploaded attachment and deletes the stored asset.

**Tags:** Attachments

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required |

### Responses

**200** – Attachment deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing attachment identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Attachment not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/attachments?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/attachments`

List attachments for a record

Returns uploaded attachments for the given entity record, ordered by newest first.

**Tags:** Attachments

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Required. Entity identifier that owns the attachments |
| recordId | query | any | Required. Record identifier within the entity |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – Attachments found for the record

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "url": "string",
      "fileName": "string",
      "fileSize": 1,
      "createdAt": "string",
      "mimeType": null,
      "content": null
    }
  ]
}
```

**400** – Missing entity or record identifiers

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/attachments?entityId=string&recordId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/attachments`

Upload attachment

Uploads a new attachment using multipart form-data and stores metadata for later retrieval.

**Tags:** Attachments

**Requires authentication.**

### Request Body

Content-Type: `multipart/form-data`

```text
entityId=string
recordId=string
file=string
```

### Responses

**200** – Attachment stored successfully

Content-Type: `application/json`

```json
{
  "ok": true,
  "item": {
    "id": "string",
    "url": "string",
    "fileName": "string",
    "fileSize": 1,
    "content": null
  }
}
```

**400** – Payload validation error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Attachment violates field constraints

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/attachments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: multipart/form-data" \
  -d "{
  \"entityId\": \"string\",
  \"recordId\": \"string\",
  \"file\": \"string\"
}"
```

## GET `/attachments/file/{id}`

Download or serve attachment file

Returns the raw file content for an attachment. Path parameter: {id} - Attachment UUID. Query parameter: ?download=1 - Force file download with Content-Disposition header. Access control is enforced based on partition settings.

**Tags:** Attachments

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – File content with appropriate MIME type

Content-Type: `application/json`

**400** – Missing attachment ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized - authentication required for private partitions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden - insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Attachment or file not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Partition misconfigured

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/attachments/file/:id" \
  -H "Accept: application/json"
```

## GET `/attachments/image/{id}/{slug}`

Serve image with optional resizing

Returns an image attachment with optional on-the-fly resizing and cropping. Resized images are cached for performance. Only works with image MIME types. Path parameter: {id} - Attachment UUID. Query parameters: ?width=N (1-4000 pixels), ?height=N (1-4000 pixels), ?cropType=cover|contain (resize behavior).

**Tags:** Attachments

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| slug | path | any | Optional |

### Responses

**200** – Binary image content (Content-Type: image/jpeg, image/png, etc.)

Content-Type: `application/json`

**400** – Invalid parameters, missing ID, or non-image attachment

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized - authentication required for private partitions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden - insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Image not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Partition misconfigured or image rendering failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/attachments/image/:id/:slug" \
  -H "Accept: application/json"
```

## GET `/attachments/library`

List attachments

Returns paginated list of attachments with optional filtering by search term, partition, and tags. Includes available tags and partitions.

**Tags:** Attachments

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional. Page number for pagination |
| pageSize | query | any | Optional. Number of items per page (max 100) |
| search | query | any | Optional. Search by file name (case-insensitive) |
| partition | query | any | Optional. Filter by partition code |
| tags | query | any | Optional. Filter by tags (comma-separated) |
| sortField | query | any | Optional. Field to sort by |
| sortDir | query | any | Optional. Sort direction |

### Responses

**200** – Attachments list with pagination and metadata

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "fileName": "string",
      "fileSize": 1,
      "mimeType": "string",
      "partitionCode": "string",
      "partitionTitle": null,
      "url": null,
      "createdAt": "string",
      "tags": [
        "string"
      ],
      "assignments": [],
      "content": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1,
  "availableTags": [
    "string"
  ],
  "partitions": [
    {
      "code": "string",
      "title": "string",
      "description": null,
      "isPublic": true
    }
  ]
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/attachments/library?page=1&pageSize=25" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/attachments/library/{id}`

Delete attachment

Permanently deletes an attachment file from storage and database. Emits CRUD side effects.

**Tags:** Attachments

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Attachment deleted successfully

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid attachment ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Attachment not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/attachments/library/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/attachments/library/{id}`

Get attachment details

Returns complete details of an attachment including metadata, tags, assignments, and custom fields.

**Tags:** Attachments

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Attachment details

Content-Type: `application/json`

```json
{
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "fileName": "string",
    "fileSize": 1,
    "mimeType": "string",
    "partitionCode": "string",
    "partitionTitle": null,
    "tags": [
      "string"
    ],
    "assignments": [],
    "content": null,
    "customFields": null
  }
}
```

**400** – Invalid attachment ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Attachment not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/attachments/library/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/attachments/library/{id}`

Update attachment metadata

Updates attachment tags, assignments, and custom fields. Emits CRUD side effects for indexing and events.

**Tags:** Attachments

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Attachment updated successfully

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload or attachment ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Attachment not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to save attributes

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/attachments/library/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## DELETE `/attachments/partitions`

Delete partition

Deletes a partition. Default partitions cannot be deleted. Partitions with existing attachments cannot be deleted.

**Tags:** Attachments

**Requires authentication.**

### Responses

**200** – Partition deleted successfully

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid ID or default partition deletion attempt

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Partitions locked in demo mode

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Partition not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Partition in use

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/attachments/partitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/attachments/partitions`

List all attachment partitions

Returns all configured attachment partitions with storage settings, OCR configuration, and access control settings.

**Tags:** Attachments

**Requires authentication.**

### Responses

**200** – List of partitions

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "code": "string",
      "title": "string",
      "description": null,
      "isPublic": true,
      "requiresOcr": true,
      "ocrModel": null,
      "configJson": null,
      "createdAt": null,
      "updatedAt": null,
      "envKey": "string"
    }
  ]
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/attachments/partitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/attachments/partitions`

Create new partition

Creates a new attachment partition with specified storage and OCR settings. Requires unique partition code.

**Tags:** Attachments

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "code": "string",
  "title": "string",
  "description": null,
  "ocrModel": null,
  "storageDriver": "local",
  "configJson": null
}
```

### Responses

**201** – Partition created successfully

Content-Type: `application/json`

```json
{
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "code": "string",
    "title": "string",
    "description": null,
    "isPublic": true,
    "requiresOcr": true,
    "ocrModel": null,
    "configJson": null,
    "createdAt": null,
    "updatedAt": null,
    "envKey": "string"
  }
}
```

**400** – Invalid payload or partition code

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Partitions locked in demo mode

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Partition code already exists

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/attachments/partitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"code\": \"string\",
  \"title\": \"string\",
  \"description\": null,
  \"ocrModel\": null,
  \"storageDriver\": \"local\",
  \"configJson\": null
}"
```

## PUT `/attachments/partitions`

Update partition

Updates an existing partition. Partition code cannot be changed. Title, description, OCR settings, and access control can be modified.

**Tags:** Attachments

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "code": "string",
  "title": "string",
  "description": null,
  "ocrModel": null,
  "storageDriver": "local",
  "configJson": null,
  "id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Partition updated successfully

Content-Type: `application/json`

```json
{
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "code": "string",
    "title": "string",
    "description": null,
    "isPublic": true,
    "requiresOcr": true,
    "ocrModel": null,
    "configJson": null,
    "createdAt": null,
    "updatedAt": null,
    "envKey": "string"
  }
}
```

**400** – Invalid payload or code change attempt

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Partitions locked in demo mode

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Partition not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/attachments/partitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"code\": \"string\",
  \"title\": \"string\",
  \"description\": null,
  \"ocrModel\": null,
  \"storageDriver\": \"local\",
  \"configJson\": null,
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## POST `/attachments/transfer`

Transfer attachments to different record

Transfers one or more attachments from one record to another within the same entity type. Updates attachment assignments and metadata to reflect the new record.

**Tags:** Attachments

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "attachmentIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "toRecordId": "string"
}
```

### Responses

**200** – Attachments transferred successfully

Content-Type: `application/json`

```json
{
  "ok": true,
  "updated": 1
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Attachments not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Attachment model missing

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/attachments/transfer" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"attachmentIds\": [
    \"00000000-0000-4000-8000-000000000000\"
  ],
  \"toRecordId\": \"string\"
}"
```

## GET `/audit_logs/audit-logs/access`

Retrieve access logs

Fetches paginated access audit logs scoped to the authenticated user. Tenant administrators can optionally expand the search to other actors or organizations.

**Tags:** Audit & Action Logs

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| organizationId | query | any | Optional. Limit results to a specific organization |
| actorUserId | query | any | Optional. Filter by actor user id (tenant administrators only) |
| resourceKind | query | any | Optional. Restrict to a resource kind such as `order` or `product` |
| accessType | query | any | Optional. Access type filter, e.g. `read` or `export` |
| page | query | any | Optional. Page number (default 1) |
| pageSize | query | any | Optional. Page size (default 50) |
| limit | query | any | Optional. Explicit maximum number of records when paginating manually |
| before | query | any | Optional. Return logs created before this ISO-8601 timestamp |
| after | query | any | Optional. Return logs created after this ISO-8601 timestamp |

### Responses

**200** – Access logs returned successfully

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "resourceKind": "string",
      "resourceId": "string",
      "accessType": "string",
      "actorUserId": null,
      "actorUserName": null,
      "tenantId": null,
      "tenantName": null,
      "organizationId": null,
      "organizationName": null,
      "fields": [
        "string"
      ],
      "context": null,
      "createdAt": "string"
    }
  ],
  "canViewTenant": true,
  "page": 1,
  "pageSize": 1,
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid filters supplied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Caller has no resolved tenant scope and is not a superadmin

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/audit_logs/audit-logs/access" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/audit_logs/audit-logs/actions`

Fetch action logs

Returns recent action audit log entries. Tenant administrators can widen the scope to other actors or organizations, and callers can optionally restrict results to undoable actions.

**Tags:** Audit & Action Logs

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| organizationId | query | any | Optional. Limit results to a specific organization |
| actorUserId | query | any | Optional. Filter logs created by specific actor IDs (tenant administrators only). Accepts a single UUID or a comma-separated UUID list. |
| resourceKind | query | any | Optional. Filter by resource kind (e.g., "order", "product") |
| resourceId | query | any | Optional. Filter by resource ID (UUID of the specific record) |
| actionType | query | any | Optional. Filter by action type (`create`, `edit`, `delete`, `assign`). Accepts a single value or a comma-separated list. |
| fieldName | query | any | Optional. Filter to entries where the given field changed. Accepts a single field name or a comma-separated list. |
| includeRelated | query | any | Optional. When `true`, also returns changes to child entities linked via parentResourceKind/parentResourceId |
| includeTotal | query | any | Optional. When `true`, the response includes the filtered total count. |
| undoableOnly | query | any | Optional. When `true`, only undoable actions are returned |
| limit | query | any | Optional. Maximum number of records to return (default 50, max 1000) |
| offset | query | any | Optional. Zero-based record offset for pagination (legacy — prefer page/pageSize) |
| page | query | any | Optional. Page number (default 1) |
| pageSize | query | any | Optional. Page size (default 50, max 200) |
| sortField | query | any | Optional. Sort field: `createdAt`, `user`, `action`, `field`, or `source`. |
| sortDir | query | any | Optional. Sort direction: `asc` or `desc`. |
| before | query | any | Optional. Return actions created before this ISO-8601 timestamp |
| after | query | any | Optional. Return actions created after this ISO-8601 timestamp |

### Responses

**200** – Action logs retrieved successfully

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "commandId": "string",
      "actionLabel": null,
      "executionState": "done",
      "actorUserId": null,
      "actorUserName": null,
      "tenantId": null,
      "tenantName": null,
      "organizationId": null,
      "organizationName": null,
      "resourceKind": null,
      "resourceId": null,
      "parentResourceKind": null,
      "parentResourceId": null,
      "undoToken": null,
      "createdAt": "string",
      "updatedAt": "string",
      "snapshotBefore": null,
      "snapshotAfter": null,
      "changes": null,
      "context": null
    }
  ],
  "canViewTenant": true,
  "page": 1,
  "pageSize": 1,
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid filter values

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Caller has no resolved tenant scope and is not a superadmin

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/audit_logs/audit-logs/actions?includeRelated=false&includeTotal=false&undoableOnly=false" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/audit_logs/audit-logs/actions/export`

Export action logs as CSV

Returns a CSV attachment containing filtered action audit log entries. Tenant administrators can widen the scope to other actors or organizations.

**Tags:** Audit & Action Logs

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| organizationId | query | any | Optional. Limit results to a specific organization |
| actorUserId | query | any | Optional. Filter logs created by specific actor IDs (tenant administrators only). Accepts a single UUID or a comma-separated UUID list. |
| resourceKind | query | any | Optional. Filter by resource kind (e.g., "order", "product") |
| resourceId | query | any | Optional. Filter by resource ID (UUID of the specific record) |
| actionType | query | any | Optional. Filter by action type (`create`, `edit`, `delete`, `assign`). Accepts a single value or a comma-separated list. |
| fieldName | query | any | Optional. Filter to entries where the given field changed. Accepts a single field name or a comma-separated list. |
| includeRelated | query | any | Optional. When `true`, also returns changes to child entities linked via parentResourceKind/parentResourceId |
| undoableOnly | query | any | Optional. When `true`, only undoable actions are returned |
| limit | query | any | Optional. Maximum number of records to export (default 1000, capped at 1000) |
| sortField | query | any | Optional. Sort field: `createdAt`, `user`, `action`, `field`, or `source`. |
| sortDir | query | any | Optional. Sort direction: `asc` or `desc`. |
| before | query | any | Optional. Return actions created before this ISO-8601 timestamp |
| after | query | any | Optional. Return actions created after this ISO-8601 timestamp |

### Responses

**200** – CSV export generated successfully

Content-Type: `application/json`

```json
{
  "file": "csv"
}
```

**400** – Invalid filter values

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Caller has no resolved tenant scope and is not a superadmin

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/audit_logs/audit-logs/actions/export?includeRelated=false&undoableOnly=false" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/audit_logs/audit-logs/actions/redo`

Redo by action log id

Redoes the latest undone command owned by the caller. Requires the action to still be eligible for redo within tenant and organization scope.

**Tags:** Audit & Action Logs

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "logId": "string"
}
```

### Responses

**200** – Redo executed successfully

Content-Type: `application/json`

```json
{
  "ok": true,
  "logId": null,
  "undoToken": null
}
```

**400** – Log not eligible for redo

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Redo blocked by scope checks

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/audit_logs/audit-logs/actions/redo" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"logId\": \"string\"
}"
```

## POST `/audit_logs/audit-logs/actions/undo`

Undo action by token

Replays the undo handler registered for a command. The provided undo token must match the latest undoable log entry accessible to the caller.

**Tags:** Audit & Action Logs

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "undoToken": "string"
}
```

### Responses

**200** – Undo applied successfully

Content-Type: `application/json`

```json
{
  "ok": true,
  "logId": "string"
}
```

**400** – Invalid or unavailable undo token

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Undo blocked by organization or tenant scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Undo deliberately blocked by a beforeUndo command interceptor. The interceptor chooses the status (any 4xx/5xx) and may replace the body.

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/audit_logs/audit-logs/actions/undo" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"undoToken\": \"string\"
}"
```

## GET `/auth_google/callback`

GET /auth_google/callback

**Tags:** Google Sign-In

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth_google/callback" \
  -H "Accept: application/json"
```

## GET `/auth_google/start`

GET /auth_google/start

**Tags:** Google Sign-In

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth_google/start" \
  -H "Accept: application/json"
```

## GET `/auth_google/status`

GET /auth_google/status

**Tags:** Google Sign-In

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth_google/status" \
  -H "Accept: application/json"
```

## GET `/auth/admin/nav`

Resolve backend chrome bootstrap payload

Returns the backend chrome payload available to the authenticated administrator after applying scope, RBAC, role defaults, and personal sidebar preferences.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Responses

**200** – Backend chrome payload

Content-Type: `application/json`

```json
{
  "brand": null,
  "groups": [
    {
      "name": "string",
      "items": [
        {
          "href": "string",
          "title": "string"
        }
      ]
    }
  ],
  "settingsSections": [
    {
      "id": "string",
      "label": "string",
      "items": [
        {
          "id": "string",
          "label": "string",
          "href": "string"
        }
      ]
    }
  ],
  "settingsPathPrefixes": [
    "string"
  ],
  "profileSections": [
    {
      "id": "string",
      "label": "string",
      "items": [
        {
          "id": "string",
          "label": "string",
          "href": "string"
        }
      ]
    }
  ],
  "profilePathPrefixes": [
    "string"
  ],
  "grantedFeatures": [
    "string"
  ],
  "roles": [
    "string"
  ],
  "currentOrganization": null
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth/admin/nav" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/auth/autologin`

Auto sign-in using env-configured demo credentials

When OM_AUTOLOGIN_EMAIL / OM_AUTOLOGIN_PASSWORD are configured, signs the visitor in with those credentials and redirects into the app. Intended for single-tenant demo instances only. Falls back to the login page when disabled or misconfigured.

**Tags:** Authentication & Accounts

### Responses

**200** – Success response

Content-Type: `application/json`

**307** – Redirect into the app (or to /login on failure)

Content-Type: `text/html`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth/autologin" \
  -H "Accept: application/json"
```

## POST `/auth/feature-check`

Check feature grants for the current user

Evaluates which of the requested features are available to the signed-in user within the active tenant / organization context.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "features": [
    "string"
  ]
}
```

### Responses

**200** – Evaluation result

Content-Type: `application/json`

```json
{
  "ok": true,
  "granted": [
    "string"
  ],
  "userId": "string"
}
```

**400** – Invalid request — features array missing, too large, or contains invalid entries

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/auth/feature-check" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"features\": [
    \"string\"
  ]
}"
```

## GET `/auth/features`

List declared feature flags

Returns all static features contributed by the enabled modules along with their module source.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Responses

**200** – Aggregated feature catalog

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "title": "string",
      "module": "string"
    }
  ],
  "modules": [
    {
      "id": "string",
      "title": "string"
    }
  ]
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth/features" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/auth/locale`

Set locale and redirect

Stores the selected locale in a cookie and redirects to a safe local path.

**Tags:** Authentication & Accounts

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| locale | query | any | Required. A locale code this tenant serves — one of the `servable` entries returned by `GET /api/translations/locales`. Codes are canonicalized (`de-AT` → `de`). |
| redirect | query | any | Optional |

### Responses

**200** – Success response

Content-Type: `application/json`

**302** – Locale cookie set and request redirected

Content-Type: `application/json`

**400** – Invalid locale

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth/locale?locale=string" \
  -H "Accept: application/json"
```

## POST `/auth/locale`

Set locale

Stores the selected locale in a cookie and returns a JSON success response.

**Tags:** Authentication & Accounts

### Request Body

Content-Type: `application/json`

```json
{
  "locale": "string"
}
```

### Responses

**200** – Locale cookie set

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid locale or malformed request body

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/auth/locale" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"locale\": \"string\"
}"
```

## POST `/auth/login`

Authenticate user credentials

Validates the submitted credentials and issues a bearer token cookie for subsequent API calls.

**Tags:** Authentication & Accounts

### Request Body

Content-Type: `application/x-www-form-urlencoded`

```text
email=user%40example.com&password=string
```

### Responses

**200** – Authentication succeeded

Content-Type: `application/json`

```json
{
  "ok": true,
  "token": "string",
  "redirect": null
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**401** – Invalid credentials

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**403** – User lacks required role

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**429** – Too many login attempts

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/auth/login" \
  -H "Accept: application/json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "email=user%40example.com&password=string"
```

## POST `/auth/logout`

Invalidate session and redirect

Clears authentication cookies and redirects the browser to the login page.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Responses

**201** – Success response

Content-Type: `application/json`

**302** – Redirect to login after successful logout

Content-Type: `text/html`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/auth/logout" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/auth/profile`

Get current profile

Returns the email address, display name, and roles for the signed-in user. The display name is null when unset.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Responses

**200** – Profile payload

Content-Type: `application/json`

```json
{
  "email": "user@example.com",
  "name": null,
  "roles": [
    "string"
  ]
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – User not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth/profile" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/auth/profile`

Update current profile

Updates the email address or password for the signed-in user.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Profile updated

Content-Type: `application/json`

```json
{
  "ok": true,
  "email": "user@example.com"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/auth/profile" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/auth/reset`

Send reset email

Requests a password reset email for the given account. The endpoint always returns `ok: true` to avoid leaking account existence.

**Tags:** Authentication & Accounts

### Request Body

Content-Type: `application/x-www-form-urlencoded`

```text
email=user%40example.com
```

### Responses

**200** – Reset email dispatched (or ignored for unknown accounts)

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid request origin

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Too many password reset requests

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Password reset email origin is not configured

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/auth/reset" \
  -H "Accept: application/json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "email=user%40example.com"
```

## POST `/auth/reset/confirm`

Complete password reset

Validates the reset token and updates the user password. The returned `redirect` carries the resolved user's tenant as a navigation hint (`/login?tenant=...`) so a tenant-bound user lands on the tenant login entry; tenantless users get `/login`.

**Tags:** Authentication & Accounts

### Request Body

Content-Type: `application/x-www-form-urlencoded`

```text
token=string&password=string
```

### Responses

**200** – Password reset succeeded

Content-Type: `application/json`

```json
{
  "ok": true,
  "redirect": "string"
}
```

**400** – Invalid token or payload

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**429** – Too many reset confirmation attempts

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/auth/reset/confirm" \
  -H "Accept: application/json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=string&password=string"
```

## POST `/auth/reset/validate`

Check whether a password reset token is still usable

Reports whether a reset token exists, is unused, and has not expired, so the reset page can render a terminal state instead of a form the token can never submit. The token is never consumed and the response never distinguishes unknown, used, and expired tokens.

**Tags:** Authentication & Accounts

### Request Body

Content-Type: `application/x-www-form-urlencoded`

```text
token=string
```

### Responses

**200** – Token state resolved

Content-Type: `application/json`

```json
{
  "ok": true,
  "valid": true
}
```

**429** – Too many token validation attempts

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/auth/reset/validate" \
  -H "Accept: application/json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=string"
```

## DELETE `/auth/roles`

Delete role

Deletes a role by identifier. Fails when users remain assigned.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. Role identifier |

### Responses

**200** – Role deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Role cannot be deleted

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Role not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/auth/roles?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/auth/roles`

List roles

Returns available roles within the current tenant. Super administrators receive visibility across tenants.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| tenantId | query | any | Optional |

### Responses

**200** – Role collection

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "usersCount": 1,
      "tenantId": null,
      "tenantName": null,
      "updatedAt": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth/roles?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/auth/roles`

Create role

Creates a new role anchored to the caller's tenant. Non-superadmins cannot target another tenant; supplying a foreign `tenantId` is rejected.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "name": "string"
}
```

### Responses

**201** – Role created

Content-Type: `application/json`

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

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/auth/roles" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\"
}"
```

## PUT `/auth/roles`

Update role

Updates mutable fields on an existing role.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Role updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Role not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/auth/roles" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/auth/roles/acl`

Fetch role ACL

Returns the feature and organization assignments associated with a role within the current tenant.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| roleId | query | any | Required |
| tenantId | query | any | Optional |

### Responses

**200** – Role ACL entry

Content-Type: `application/json`

```json
{
  "isSuperAdmin": true,
  "features": [
    "string"
  ],
  "organizations": null,
  "updatedAt": null
}
```

**400** – Invalid role id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Role not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth/roles/acl?roleId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/auth/roles/acl`

Update role ACL

Replaces the feature list, super admin flag, and optional organization assignments for a role.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "roleId": "00000000-0000-4000-8000-000000000000",
  "organizations": null
}
```

### Responses

**200** – Role ACL updated

Content-Type: `application/json`

```json
{
  "ok": true,
  "sanitized": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient privileges to modify ACL

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Role not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/auth/roles/acl" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"roleId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizations\": null
}"
```

## GET `/auth/session/refresh`

Refresh auth cookie from session token (browser)

Exchanges an existing `session_token` cookie for a fresh JWT auth cookie and redirects the browser.

**Tags:** Authentication & Accounts

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| redirect | query | any | Optional. Absolute or relative URL to redirect after refresh |

### Responses

**200** – Success response

Content-Type: `application/json`

**302** – Redirect to target location when session is valid

Content-Type: `text/html`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth/session/refresh" \
  -H "Accept: application/json"
```

## POST `/auth/session/refresh`

Refresh access token (API/mobile)

Exchanges a refresh token for a new JWT access token. Pass the refresh token obtained from login in the request body.

**Tags:** Authentication & Accounts

### Request Body

Content-Type: `application/json`

```json
{
  "refreshToken": "string"
}
```

### Responses

**200** – New access token issued

Content-Type: `application/json`

```json
{
  "ok": true,
  "accessToken": "string",
  "expiresIn": 1
}
```

**400** – Missing refresh token

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**401** – Invalid or expired token

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**429** – Too many refresh attempts

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/auth/session/refresh" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"refreshToken\": \"string\"
}"
```

## DELETE `/auth/sidebar/preferences`

Delete a role sidebar variant

Removes the role variant for the current tenant + locale. Idempotent. Requires `auth.sidebar.manage`.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Responses

**200** – Variant deleted (or never existed)

Content-Type: `application/json`

```json
{
  "ok": true,
  "scope": {
    "type": "user"
  }
}
```

**400** – Missing roleId query parameter

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing features

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Role not found in current tenant scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/auth/sidebar/preferences" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/auth/sidebar/preferences`

Get sidebar preferences

Returns sidebar customization for the current user (default) or the specified role (`?roleId=…`, requires `auth.sidebar.manage`).

**Tags:** Authentication & Accounts

**Requires authentication.**

### Responses

**200** – Current sidebar configuration

Content-Type: `application/json`

```json
{
  "locale": "string",
  "settings": {
    "version": 1,
    "groupOrder": [
      "string"
    ],
    "groupLabels": {
      "key": "string"
    },
    "itemLabels": {
      "key": "string"
    },
    "hiddenItems": [
      "string"
    ],
    "itemOrder": {
      "key": [
        "string"
      ]
    }
  },
  "canApplyToRoles": true,
  "roles": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "hasPreference": true
    }
  ],
  "scope": {
    "type": "user"
  },
  "updatedAt": null
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing features for role-scope read

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Role not found in current tenant scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth/sidebar/preferences" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/auth/sidebar/preferences`

Update sidebar preferences

Updates sidebar configuration. With `scope.type === "user"` (default) writes the calling user's personal preferences and may optionally apply the same settings to selected roles via `applyToRoles[]`. With `scope.type === "role"` writes the named role variant directly (requires `auth.sidebar.manage`); `applyToRoles[]` and `clearRoleIds[]` are rejected in this mode.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Preferences saved

Content-Type: `application/json`

```json
{
  "locale": "string",
  "settings": {
    "version": 1,
    "groupOrder": [
      "string"
    ],
    "groupLabels": {
      "key": "string"
    },
    "itemLabels": {
      "key": "string"
    },
    "hiddenItems": [
      "string"
    ],
    "itemOrder": {
      "key": [
        "string"
      ]
    }
  },
  "canApplyToRoles": true,
  "roles": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "hasPreference": true
    }
  ],
  "scope": {
    "type": "user"
  },
  "updatedAt": null,
  "appliedRoles": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "clearedRoles": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing features for role-wide updates

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Role not found in current tenant scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/auth/sidebar/preferences" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## GET `/auth/sidebar/variants`

List sidebar variants

Returns the named sidebar variants saved by the current user for the current tenant + locale.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Responses

**200** – Variant list

Content-Type: `application/json`

```json
{
  "locale": "string",
  "variants": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "isActive": true,
      "settings": {
        "version": 1,
        "groupOrder": [
          "string"
        ],
        "groupLabels": {
          "key": "string"
        },
        "itemLabels": {
          "key": "string"
        },
        "hiddenItems": [
          "string"
        ],
        "itemOrder": {
          "key": [
            "string"
          ]
        }
      },
      "createdAt": "string",
      "updatedAt": null
    }
  ]
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth/sidebar/variants" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/auth/sidebar/variants`

Create a sidebar variant

Creates a new variant. If `name` is omitted or blank, an auto-name like "My preferences", "My preferences 2", … is assigned.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Variant created

Content-Type: `application/json`

```json
{
  "locale": "string",
  "variant": {
    "id": "00000000-0000-4000-8000-000000000000",
    "name": "string",
    "isActive": true,
    "settings": {
      "version": 1,
      "groupOrder": [
        "string"
      ],
      "groupLabels": {
        "key": "string"
      },
      "itemLabels": {
        "key": "string"
      },
      "hiddenItems": [
        "string"
      ],
      "itemOrder": {
        "key": [
          "string"
        ]
      }
    },
    "createdAt": "string",
    "updatedAt": null
  }
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/auth/sidebar/variants" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## DELETE `/auth/sidebar/variants/{id}`

Delete a sidebar variant

Soft-deletes the variant (sets deleted_at).

**Tags:** Authentication & Accounts

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Variant deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Variant not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/auth/sidebar/variants/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/auth/sidebar/variants/{id}`

Get a sidebar variant

**Tags:** Authentication & Accounts

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Variant

Content-Type: `application/json`

```json
{
  "locale": "string",
  "variant": {
    "id": "00000000-0000-4000-8000-000000000000",
    "name": "string",
    "isActive": true,
    "settings": {
      "version": 1,
      "groupOrder": [
        "string"
      ],
      "groupLabels": {
        "key": "string"
      },
      "itemLabels": {
        "key": "string"
      },
      "hiddenItems": [
        "string"
      ],
      "itemOrder": {
        "key": [
          "string"
        ]
      }
    },
    "createdAt": "string",
    "updatedAt": null
  }
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Variant not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth/sidebar/variants/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/auth/sidebar/variants/{id}`

Update a sidebar variant

Updates the variant's name, settings, and/or isActive flag. Setting `isActive: true` deactivates other variants in the same scope (only one active per user/tenant/locale).

**Tags:** Authentication & Accounts

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Variant updated

Content-Type: `application/json`

```json
{
  "locale": "string",
  "variant": {
    "id": "00000000-0000-4000-8000-000000000000",
    "name": "string",
    "isActive": true,
    "settings": {
      "version": 1,
      "groupOrder": [
        "string"
      ],
      "groupLabels": {
        "key": "string"
      },
      "itemLabels": {
        "key": "string"
      },
      "hiddenItems": [
        "string"
      ],
      "itemOrder": {
        "key": [
          "string"
        ]
      }
    },
    "createdAt": "string",
    "updatedAt": null
  }
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Variant not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/auth/sidebar/variants/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## DELETE `/auth/users`

Delete user

Deletes a user by identifier. Rejected when the target is the last active holder of a protected role in the tenant. Undo support is provided via the command bus.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. User identifier |

### Responses

**200** – User deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – User cannot be deleted, or is the last active holder of a protected role

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – User not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/auth/users?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/auth/users`

List users

Returns users for the effective selected tenant and organization scope. Search matches email, organization name, and role name. Super administrators may scope the response via the topbar context, organization filters, or role filters. Pass scopeToActiveOrganization=1 to restrict results to the caller's active organization (used by recipient/assignee pickers so suggestions stay within the org that owns the resulting record). Pass ids=<uuid>,<uuid> (max 100) to resolve a known set of users in one request, for example to label a list of foreign keys; it intersects with id and roleId, and a supplied ids value that contains no valid identifier matches nothing.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| ids | query | any | Optional. Comma-separated user identifiers, at most 100 |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| name | query | any | Optional |
| organizationId | query | any | Optional |
| scopeToActiveOrganization | query | any | Optional |
| roleIds | query | any | Optional |

### Responses

**200** – User collection

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "email": "user@example.com",
      "name": null,
      "organizationId": null,
      "organizationName": null,
      "tenantId": null,
      "tenantName": null,
      "roles": [
        "string"
      ],
      "isConfirmed": true,
      "updatedAt": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth/users?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/auth/users`

Create user

Creates a new confirmed user within the specified organization, optional display name, and optional roles.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "email": "user@example.com",
  "name": null,
  "organizationId": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**201** – User created

Content-Type: `application/json`

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

**400** – Invalid payload or duplicate email

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Attempted to assign privileged roles

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/auth/users" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"email\": \"user@example.com\",
  \"name\": null,
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## PUT `/auth/users`

Update user

Updates profile fields including display name, organization assignment, credentials, or role memberships. A destination organization must be within the caller's descendant-expanded organization scope. Retained and newly assigned roles must belong to the destination tenant and be grantable by the caller. Setting isConfirmed=false deactivates the account: the user can no longer sign in and every active session is revoked; isConfirmed=true reactivates it. A tenant cannot drop below a protected role's minimum active holder count, so revoking the role from, deactivating, moving, or deleting the last active administrator is rejected.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "name": null
}
```

### Responses

**200** – User updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload, duplicate email, or the update would remove the last active holder of a protected role

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Destination organization is outside caller scope, or a retained or assigned role is not grantable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – User or destination organization not found in the caller tenant scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/auth/users" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"name\": null
}"
```

## GET `/auth/users/acl`

Fetch user ACL

Returns custom ACL overrides for a user, scoped to the requested tenant when supplied and to the actor or target user tenant otherwise.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| userId | query | any | Required |
| tenantId | query | any | Optional |

### Responses

**200** – User ACL entry

Content-Type: `application/json`

```json
{
  "hasCustomAcl": true,
  "isSuperAdmin": true,
  "features": [
    "string"
  ],
  "organizations": null,
  "updatedAt": null
}
```

**400** – Invalid user id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient privileges for the requested tenant scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth/users/acl?userId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/auth/users/acl`

Update user ACL

Updates a per-user ACL override. Omitted super admin, feature, and organization fields preserve their stored values. Authorization evaluates the merged ACL, so a partial request returns 403 when a preserved grant is outside the actor's grantable ACL. An organization-scoped non-super-admin override requires at least one feature grant.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "userId": "00000000-0000-4000-8000-000000000000",
  "organizations": null
}
```

### Responses

**200** – User ACL updated

Content-Type: `application/json`

```json
{
  "ok": true,
  "sanitized": true
}
```

**400** – Invalid payload or unresolved tenant scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient privileges to modify ACL

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/auth/users/acl" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"userId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizations\": null
}"
```

## GET `/auth/users/consents`

List user consents

Returns all consent records for a given user, with integrity verification status.

**Tags:** Auth

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| userId | query | any | Required |

### Responses

**200** – Consent list returned

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/auth/users/consents?userId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/auth/users/resend-invite`

Resend invitation email

Resends the invitation email to a user who has not yet set up their password. Generates a new 48-hour setup token and invalidates prior tokens.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Invite email sent

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid request origin

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – User not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – User already has a password

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Validation error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Invitation email origin is not configured

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/auth/users/resend-invite" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## POST `/business_rules/execute`

Execute rules for given context

Manually executes applicable business rules for the specified entity type, event, and data. Supports dry-run mode to test rules without executing actions.

**Tags:** Business Rules

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityType": "string",
  "dryRun": false
}
```

### Responses

**200** – Rules executed successfully

Content-Type: `application/json`

```json
{
  "allowed": true,
  "executedRules": [
    {
      "ruleId": "string",
      "ruleName": "string",
      "conditionResult": true,
      "executionTime": 1
    }
  ],
  "totalExecutionTime": 1
}
```

**400** – Invalid request payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Execution error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/business_rules/execute" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityType\": \"string\",
  \"dryRun\": false
}"
```

## POST `/business_rules/execute/{ruleId}`

Execute a specific rule by its database UUID

Directly executes a specific business rule identified by its UUID, bypassing the normal entityType/eventType discovery mechanism. Useful for workflows and targeted rule execution.

**Tags:** Business Rules

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| ruleId | path | any | Required. The database UUID of the business rule to execute |

### Request Body

Content-Type: `application/json`

```json
{
  "dryRun": false
}
```

### Responses

**200** – Rule executed successfully

Content-Type: `application/json`

```json
{
  "success": true,
  "ruleId": "string",
  "ruleName": "string",
  "conditionResult": true,
  "actionsExecuted": null,
  "executionTime": 1
}
```

**400** – Invalid request payload or rule ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Rule not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Execution error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/business_rules/execute/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"dryRun\": false
}"
```

## GET `/business_rules/logs`

List rule execution logs

Returns rule execution history for the current tenant and organization with filtering and pagination. Useful for audit trails and debugging.

**Tags:** Business Rules

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| ruleId | query | any | Optional |
| entityId | query | any | Optional |
| entityType | query | any | Optional |
| executionResult | query | any | Optional |
| executedBy | query | any | Optional |
| executedAtFrom | query | any | Optional |
| executedAtTo | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Rule execution logs collection

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "ruleId": "string",
      "ruleName": "string",
      "ruleType": "string",
      "entityId": "00000000-0000-4000-8000-000000000000",
      "entityType": "string",
      "executionResult": "SUCCESS",
      "inputContext": null,
      "outputContext": null,
      "errorMessage": null,
      "executionTimeMs": 1,
      "executedAt": "string",
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": null,
      "executedBy": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/business_rules/logs?page=1&pageSize=50&sortDir=desc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/business_rules/logs/{id}`

Get execution log detail

Returns detailed information about a specific rule execution, including full context and results.

**Tags:** Business Rules

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Log entry details

Content-Type: `application/json`

```json
{
  "id": "string",
  "rule": {
    "id": "00000000-0000-4000-8000-000000000000",
    "ruleId": "string",
    "ruleName": "string",
    "ruleType": "string",
    "entityType": "string"
  },
  "entityId": "00000000-0000-4000-8000-000000000000",
  "entityType": "string",
  "executionResult": "SUCCESS",
  "inputContext": null,
  "outputContext": null,
  "errorMessage": null,
  "executionTimeMs": 1,
  "executedAt": "string",
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": null,
  "executedBy": null
}
```

**400** – Invalid log id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Log entry not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/business_rules/logs/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/business_rules/openmercato-call-options`

List OpenMercato call action options

Returns executable Open Mercato API endpoint options and safe API key profile metadata for business-rule actions.

**Tags:** Business Rules

**Requires authentication.**

### Responses

**200** – OpenMercato call options

Content-Type: `application/json`

```json
{
  "endpoints": [
    {
      "id": "string",
      "path": "string",
      "method": "GET",
      "label": "string",
      "summary": null,
      "operationId": null
    }
  ],
  "apiKeys": [
    {
      "id": "string",
      "name": "string",
      "keyPrefix": "string",
      "organizationId": null,
      "organizationName": null,
      "roles": [
        {
          "id": "string",
          "name": null
        }
      ]
    }
  ]
}
```

**400** – Tenant context missing

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing required features

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/business_rules/openmercato-call-options" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/business_rules/rules`

Delete business rule

Soft deletes a business rule by identifier.

**Tags:** Business Rules

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. Business rule identifier |

### Responses

**200** – Business rule deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Business rule not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/business_rules/rules?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/business_rules/rules`

List business rules

Returns business rules for the current tenant and organization with filtering and pagination.

**Tags:** Business Rules

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| ruleId | query | any | Optional |
| ruleType | query | any | Optional |
| entityType | query | any | Optional |
| eventType | query | any | Optional |
| enabled | query | any | Optional |
| ruleCategory | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Business rules collection

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "ruleId": "string",
      "ruleName": "string",
      "description": null,
      "ruleType": "GUARD",
      "ruleCategory": null,
      "entityType": "string",
      "eventType": null,
      "enabled": true,
      "priority": 1,
      "version": 1,
      "effectiveFrom": null,
      "effectiveTo": null,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/business_rules/rules?page=1&pageSize=50&sortDir=desc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/business_rules/rules`

Create business rule

Creates a new business rule for the current tenant and organization.

**Tags:** Business Rules

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "ruleId": "string",
  "ruleName": "string",
  "description": null,
  "ruleType": "GUARD",
  "ruleCategory": null,
  "entityType": "string",
  "eventType": null,
  "enabled": true,
  "priority": 100,
  "version": 1,
  "effectiveFrom": null,
  "effectiveTo": null,
  "tenantId": "string",
  "organizationId": "string",
  "createdBy": null,
  "conditionExpression": null,
  "successActions": null,
  "failureActions": null
}
```

### Responses

**201** – Business rule created

Content-Type: `application/json`

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

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/business_rules/rules" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"ruleId\": \"string\",
  \"ruleName\": \"string\",
  \"description\": null,
  \"ruleType\": \"GUARD\",
  \"ruleCategory\": null,
  \"entityType\": \"string\",
  \"eventType\": null,
  \"enabled\": true,
  \"priority\": 100,
  \"version\": 1,
  \"effectiveFrom\": null,
  \"effectiveTo\": null,
  \"tenantId\": \"string\",
  \"organizationId\": \"string\",
  \"createdBy\": null,
  \"conditionExpression\": null,
  \"successActions\": null,
  \"failureActions\": null
}"
```

## PUT `/business_rules/rules`

Update business rule

Updates an existing business rule.

**Tags:** Business Rules

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "description": null,
  "ruleCategory": null,
  "eventType": null,
  "enabled": true,
  "priority": 100,
  "version": 1,
  "effectiveFrom": null,
  "effectiveTo": null,
  "conditionExpression": null,
  "successActions": null,
  "failureActions": null,
  "id": "string"
}
```

### Responses

**200** – Business rule updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Business rule not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/business_rules/rules" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"description\": null,
  \"ruleCategory\": null,
  \"eventType\": null,
  \"enabled\": true,
  \"priority\": 100,
  \"version\": 1,
  \"effectiveFrom\": null,
  \"effectiveTo\": null,
  \"conditionExpression\": null,
  \"successActions\": null,
  \"failureActions\": null,
  \"id\": \"string\"
}"
```

## GET `/business_rules/rules/{id}`

Fetch business rule by ID

Returns complete details of a business rule including conditions and actions.

**Tags:** Business Rules

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Business rule detail

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "ruleId": "string",
  "ruleName": "string",
  "description": null,
  "ruleType": "GUARD",
  "ruleCategory": null,
  "entityType": "string",
  "eventType": null,
  "successActions": null,
  "failureActions": null,
  "enabled": true,
  "priority": 1,
  "version": 1,
  "effectiveFrom": null,
  "effectiveTo": null,
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "createdBy": null,
  "updatedBy": null,
  "createdAt": "string",
  "updatedAt": "string"
}
```

**400** – Invalid identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Business rule not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/business_rules/rules/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/business_rules/sets`

Delete rule set

Soft deletes a rule set by identifier.

**Tags:** Business Rules

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. Rule set identifier |

### Responses

**200** – Rule set deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Rule set not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/business_rules/sets?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/business_rules/sets`

List rule sets

Returns rule sets for the current tenant and organization with filtering and pagination.

**Tags:** Business Rules

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| setId | query | any | Optional |
| enabled | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Rule sets collection

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "setId": "string",
      "setName": "string",
      "description": null,
      "enabled": true,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdBy": null,
      "updatedBy": null,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/business_rules/sets?page=1&pageSize=50&sortDir=asc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/business_rules/sets`

Create rule set

Creates a new rule set for organizing business rules.

**Tags:** Business Rules

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "setId": "string",
  "setName": "string",
  "description": null,
  "enabled": true,
  "tenantId": "string",
  "organizationId": "string",
  "createdBy": null
}
```

### Responses

**201** – Rule set created

Content-Type: `application/json`

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

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/business_rules/sets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"setId\": \"string\",
  \"setName\": \"string\",
  \"description\": null,
  \"enabled\": true,
  \"tenantId\": \"string\",
  \"organizationId\": \"string\",
  \"createdBy\": null
}"
```

## PUT `/business_rules/sets`

Update rule set

Updates an existing rule set.

**Tags:** Business Rules

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "description": null,
  "enabled": true,
  "id": "string"
}
```

### Responses

**200** – Rule set updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Rule set not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/business_rules/sets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"description\": null,
  \"enabled\": true,
  \"id\": \"string\"
}"
```

## GET `/business_rules/sets/{id}`

Get rule set detail

Returns detailed information about a specific rule set, including all member rules.

**Tags:** Business Rules

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Rule set details

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "setId": "string",
  "setName": "string",
  "description": null,
  "enabled": true,
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "createdBy": null,
  "updatedBy": null,
  "createdAt": "string",
  "updatedAt": "string",
  "members": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "ruleId": "00000000-0000-4000-8000-000000000000",
      "ruleName": "string",
      "ruleType": "string",
      "sequence": 1,
      "enabled": true
    }
  ]
}
```

**400** – Invalid rule set id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Rule set not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/business_rules/sets/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/business_rules/sets/{id}/members`

Remove rule from set

Removes a business rule from a rule set (hard delete).

**Tags:** Business Rules

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| memberId | query | any | Required. Member identifier |

### Responses

**200** – Member removed

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Member not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/business_rules/sets/:id/members?memberId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/business_rules/sets/{id}/members`

Add rule to set

Adds a business rule to a rule set with specified sequence and enabled state.

**Tags:** Business Rules

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "ruleId": "00000000-0000-4000-8000-000000000000",
  "sequence": 0,
  "enabled": true
}
```

### Responses

**201** – Member added

Content-Type: `application/json`

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

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Rule set or rule not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Rule already in set

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/business_rules/sets/:id/members" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"ruleId\": \"00000000-0000-4000-8000-000000000000\",
  \"sequence\": 0,
  \"enabled\": true
}"
```

## PUT `/business_rules/sets/{id}/members`

Update set member

Updates sequence or enabled state of a rule set member.

**Tags:** Business Rules

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "memberId": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Member updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Member not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/business_rules/sets/:id/members" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"memberId\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/configs/cache`

Get cache statistics

Returns detailed cache statistics including total entries and breakdown by cache segments. Requires cache service to be available.

**Tags:** Configs

**Requires authentication.**

### Responses

**200** – Cache statistics

Content-Type: `application/json`

```json
{
  "generatedAt": "string",
  "totalKeys": 1,
  "segments": [
    {
      "segment": "string",
      "resource": null,
      "method": null,
      "path": null,
      "keyCount": 1,
      "keys": [
        "string"
      ]
    }
  ]
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to resolve cache stats

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Cache service unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/configs/cache" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/configs/cache`

Purge cache

Purges cache entries. Supports two actions: purgeAll (clears entire cache) or purgeSegment (clears specific segment). Returns updated cache statistics after purge.

**Tags:** Configs

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "action": "purgeAll"
}
```

### Responses

**200** – Cache segment cleared successfully

Content-Type: `application/json`

```json
{
  "action": "purgeSegment",
  "segment": "string",
  "deleted": 1,
  "stats": {
    "generatedAt": "string",
    "totalKeys": 1,
    "segments": [
      {
        "segment": "string",
        "resource": null,
        "method": null,
        "path": null,
        "keyCount": 1,
        "keys": [
          "string"
        ]
      }
    ]
  }
}
```

**400** – Invalid request - missing segment identifier for purgeSegment action

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to purge cache

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Cache service unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/configs/cache" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"action\": \"purgeAll\"
}"
```

## DELETE `/configs/module-telemetry`

Clear module telemetry data

Development-only endpoint that clears in-memory module telemetry and local process telemetry files.

**Tags:** Configs

**Requires authentication.**

### Responses

**200** – Module telemetry cleared

Content-Type: `application/json`

```json
{
  "cleared": true
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden outside development mode

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/configs/module-telemetry" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/configs/module-telemetry`

Get module resource usage telemetry

Returns in-process module resource attribution for API routes, event subscribers, and queue workers.

**Tags:** Configs

**Requires authentication.**

### Responses

**200** – Module resource usage report

Content-Type: `application/json`

```json
{
  "generatedAt": "string",
  "startedAt": "string",
  "enabled": true,
  "bucketIntervalMs": 1,
  "totals": {
    "modules": 1,
    "operations": 1,
    "calls": 1,
    "errors": 1,
    "totalDurationMs": 1,
    "totalCpuMs": 1,
    "positiveHeapDeltaBytes": 1,
    "positiveRssDeltaBytes": 1
  },
  "thresholds": {
    "p95DurationMs": 1,
    "cpuMs": 1,
    "positiveHeapDeltaBytes": 1,
    "positiveRssDeltaBytes": 1,
    "errors": 1
  },
  "modules": [
    {
      "moduleId": "string",
      "calls": 1,
      "errors": 1,
      "totalDurationMs": 1,
      "p95DurationMs": 1,
      "totalCpuMs": 1,
      "positiveHeapDeltaBytes": 1,
      "positiveRssDeltaBytes": 1,
      "surfaces": [
        {
          "surface": "api",
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuMs": 1,
          "positiveHeapDeltaBytes": 1,
          "positiveRssDeltaBytes": 1
        }
      ],
      "topOperations": [
        {
          "moduleId": "string",
          "surface": "api",
          "operation": "string",
          "resourceId": null,
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "maxDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuUserMs": 1,
          "totalCpuSystemMs": 1,
          "maxCpuMs": 1,
          "totalHeapDeltaBytes": 1,
          "positiveHeapDeltaBytes": 1,
          "maxHeapDeltaBytes": 1,
          "totalRssDeltaBytes": 1,
          "positiveRssDeltaBytes": 1,
          "maxRssDeltaBytes": 1,
          "firstSeenAt": "string",
          "lastSeenAt": "string"
        }
      ],
      "candidateReasons": [
        "string"
      ]
    }
  ],
  "candidates": [
    {
      "moduleId": "string",
      "calls": 1,
      "errors": 1,
      "totalDurationMs": 1,
      "p95DurationMs": 1,
      "totalCpuMs": 1,
      "positiveHeapDeltaBytes": 1,
      "positiveRssDeltaBytes": 1,
      "surfaces": [
        {
          "surface": "api",
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuMs": 1,
          "positiveHeapDeltaBytes": 1,
          "positiveRssDeltaBytes": 1
        }
      ],
      "topOperations": [
        {
          "moduleId": "string",
          "surface": "api",
          "operation": "string",
          "resourceId": null,
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "maxDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuUserMs": 1,
          "totalCpuSystemMs": 1,
          "maxCpuMs": 1,
          "totalHeapDeltaBytes": 1,
          "positiveHeapDeltaBytes": 1,
          "maxHeapDeltaBytes": 1,
          "totalRssDeltaBytes": 1,
          "positiveRssDeltaBytes": 1,
          "maxRssDeltaBytes": 1,
          "firstSeenAt": "string",
          "lastSeenAt": "string"
        }
      ],
      "candidateReasons": [
        "string"
      ]
    }
  ],
  "buckets": [
    {
      "bucketStart": "string",
      "bucketEnd": "string",
      "bucketIntervalMs": 1,
      "stage": "startup",
      "partial": true,
      "totals": {
        "modules": 1,
        "calls": 1,
        "errors": 1,
        "totalDurationMs": 1,
        "totalCpuMs": 1,
        "positiveHeapDeltaBytes": 1,
        "positiveRssDeltaBytes": 1
      },
      "modules": [
        {
          "moduleId": "string",
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuMs": 1,
          "positiveHeapDeltaBytes": 1,
          "positiveRssDeltaBytes": 1,
          "surfaces": [
            {
              "surface": "api",
              "calls": 1,
              "errors": 1,
              "totalDurationMs": 1,
              "p95DurationMs": 1,
              "totalCpuMs": 1,
              "positiveHeapDeltaBytes": 1,
              "positiveRssDeltaBytes": 1
            }
          ],
          "topOperations": [
            {
              "moduleId": "string",
              "surface": "api",
              "operation": "string",
              "resourceId": null,
              "calls": 1,
              "errors": 1,
              "totalDurationMs": 1,
              "maxDurationMs": 1,
              "p95DurationMs": 1,
              "totalCpuUserMs": 1,
              "totalCpuSystemMs": 1,
              "maxCpuMs": 1,
              "totalHeapDeltaBytes": 1,
              "positiveHeapDeltaBytes": 1,
              "maxHeapDeltaBytes": 1,
              "totalRssDeltaBytes": 1,
              "positiveRssDeltaBytes": 1,
              "maxRssDeltaBytes": 1,
              "firstSeenAt": "string",
              "lastSeenAt": "string"
            }
          ],
          "candidateReasons": [
            "string"
          ]
        }
      ]
    }
  ]
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/configs/module-telemetry" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/configs/system-status`

Get system health status

Returns comprehensive system health information including environment details, version, resource usage, and service connectivity status.

**Tags:** Configs

**Requires authentication.**

### Responses

**200** – System status snapshot

Content-Type: `application/json`

```json
{
  "generatedAt": "string",
  "runtimeMode": "development",
  "categories": [
    {
      "key": "profiling",
      "labelKey": "string",
      "descriptionKey": null,
      "items": [
        {
          "key": "string",
          "category": "profiling",
          "kind": "boolean",
          "labelKey": "string",
          "descriptionKey": "string",
          "docUrl": null,
          "defaultValue": null,
          "state": "enabled",
          "value": null,
          "normalizedValue": null
        }
      ]
    }
  ]
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load system status

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/configs/system-status" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/configs/system-status`

Clear system cache

Purges the entire cache for the current tenant. Useful for troubleshooting or forcing fresh data loading.

**Tags:** Configs

**Requires authentication.**

### Responses

**200** – Cache cleared successfully

Content-Type: `application/json`

```json
{
  "cleared": true
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to purge cache

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Cache service unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/configs/system-status" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/configs/upgrade-actions`

List pending upgrade actions

Returns a list of pending upgrade actions for the current version. These are one-time setup tasks that need to be executed after upgrading to a new version. Requires organization and tenant context.

**Tags:** Configs

**Requires authentication.**

### Responses

**200** – List of pending upgrade actions

Content-Type: `application/json`

```json
{
  "version": "string",
  "actions": [
    {
      "id": "string",
      "version": "string",
      "message": "string",
      "ctaLabel": "string",
      "successMessage": "string",
      "loadingLabel": "string"
    }
  ]
}
```

**400** – Missing organization or tenant context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load upgrade actions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/configs/upgrade-actions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/configs/upgrade-actions`

Execute upgrade action

Executes a specific upgrade action by ID. Typically used for one-time setup tasks like seeding example data after version upgrade. Returns execution status and localized success message.

**Tags:** Configs

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "actionId": "string"
}
```

### Responses

**200** – Upgrade action executed successfully

Content-Type: `application/json`

```json
{
  "status": "string",
  "message": "string",
  "version": "string"
}
```

**400** – Invalid request body or missing context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Upgrade actions are disabled

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to execute upgrade action

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/configs/upgrade-actions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"actionId\": \"string\"
}"
```

## GET `/dashboards/layout`

Load the current dashboard layout

Returns the saved widget layout together with the widgets the current user is allowed to place.

**Tags:** Dashboards

**Requires authentication.**

### Responses

**200** – Current dashboard layout and available widgets.

Content-Type: `application/json`

```json
{
  "layout": {
    "items": [
      {
        "id": "00000000-0000-4000-8000-000000000000",
        "widgetId": "string",
        "order": 1
      }
    ]
  },
  "allowedWidgetIds": [
    "string"
  ],
  "canConfigure": true,
  "context": {
    "userId": "00000000-0000-4000-8000-000000000000",
    "tenantId": null,
    "organizationId": null,
    "userName": null,
    "userEmail": null,
    "userLabel": "string"
  },
  "widgets": [
    {
      "id": "string",
      "title": "string",
      "description": null,
      "defaultSize": "sm",
      "defaultEnabled": true,
      "defaultSettings": null,
      "features": [
        "string"
      ],
      "moduleId": "string",
      "icon": null,
      "loaderKey": "string",
      "supportsRefresh": true
    }
  ]
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/dashboards/layout" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/dashboards/layout`

Persist dashboard layout changes

Saves the provided widget ordering, sizes, and settings for the current user.

**Tags:** Dashboards

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "widgetId": "string",
      "order": 1
    }
  ]
}
```

### Responses

**200** – Layout updated successfully.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid layout payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing dashboards.configure feature

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Widget registry unavailable — the layout was not saved

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/dashboards/layout" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"items\": [
    {
      \"id\": \"00000000-0000-4000-8000-000000000000\",
      \"widgetId\": \"string\",
      \"order\": 1
    }
  ]
}"
```

## PATCH `/dashboards/layout/{itemId}`

Update a dashboard layout item

Adjusts the size or settings for a single widget within the dashboard layout.

**Tags:** Dashboards

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| itemId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Layout item updated.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload or missing item id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing dashboards.configure feature

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Item not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/dashboards/layout/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## GET `/dashboards/roles/widgets`

Fetch widget assignments for a role

Returns the widgets explicitly assigned to the given role together with the evaluation scope.

**Tags:** Dashboards

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| roleId | query | any | Required |
| tenantId | query | any | Optional |
| organizationId | query | any | Optional |

### Responses

**200** – Current widget configuration for the role.

Content-Type: `application/json`

```json
{
  "widgetIds": [
    "string"
  ],
  "hasCustom": true,
  "scope": {
    "tenantId": null,
    "organizationId": null
  }
}
```

**400** – Missing role identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions to manage role widgets

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/dashboards/roles/widgets?roleId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/dashboards/roles/widgets`

Update widgets assigned to a role

Persists the widget list for a role within the provided tenant and organization scope.

**Tags:** Dashboards

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "roleId": "00000000-0000-4000-8000-000000000000",
  "widgetIds": [
    "string"
  ]
}
```

### Responses

**200** – Widgets updated successfully.

Content-Type: `application/json`

```json
{
  "ok": true,
  "widgetIds": [
    "string"
  ]
}
```

**400** – Invalid payload or unknown widgets

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions to manage role widgets

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/dashboards/roles/widgets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"roleId\": \"00000000-0000-4000-8000-000000000000\",
  \"widgetIds\": [
    \"string\"
  ]
}"
```

## GET `/dashboards/users/widgets`

Read widget overrides for a user

Returns the widgets inherited and explicitly configured for the requested user within the current scope.

**Tags:** Dashboards

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| userId | query | any | Required |
| tenantId | query | any | Optional |
| organizationId | query | any | Optional |

### Responses

**200** – Widget settings for the user.

Content-Type: `application/json`

```json
{
  "mode": "inherit",
  "widgetIds": [
    "string"
  ],
  "hasCustom": true,
  "effectiveWidgetIds": [
    "string"
  ],
  "scope": {
    "tenantId": null,
    "organizationId": null
  }
}
```

**400** – Missing user identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions to manage user widgets

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/dashboards/users/widgets?userId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/dashboards/users/widgets`

Update user-specific dashboard widgets

Sets the widget override mode and allowed widgets for a user. Passing `mode: inherit` clears overrides.

**Tags:** Dashboards

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "userId": "00000000-0000-4000-8000-000000000000",
  "mode": "inherit",
  "widgetIds": [
    "string"
  ]
}
```

### Responses

**200** – Overrides saved.

Content-Type: `application/json`

```json
{
  "ok": true,
  "mode": "inherit",
  "widgetIds": [
    "string"
  ]
}
```

**400** – Invalid payload or unknown widgets

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions to manage user widgets

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/dashboards/users/widgets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"userId\": \"00000000-0000-4000-8000-000000000000\",
  \"mode\": \"inherit\",
  \"widgetIds\": [
    \"string\"
  ]
}"
```

## GET `/dashboards/widgets/catalog`

List available dashboard widgets

Returns the catalog of widgets that modules expose, including defaults and feature requirements.

**Tags:** Dashboards

**Requires authentication.**

### Responses

**200** – Widgets available for assignment.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "title": "string",
      "description": null,
      "defaultSize": "sm",
      "defaultEnabled": true,
      "defaultSettings": null,
      "features": [
        "string"
      ],
      "moduleId": "string",
      "icon": null,
      "loaderKey": "string",
      "supportsRefresh": true
    }
  ]
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions to view widget catalog

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/dashboards/widgets/catalog" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/dashboards/widgets/data`

Fetch aggregated data for dashboard widgets

Executes an aggregation query against the specified entity type and returns the result. Supports date range filtering, grouping, and period-over-period comparison.

**Tags:** Dashboards

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityType": "string",
  "metric": {
    "field": "string",
    "aggregate": "count"
  }
}
```

### Responses

**200** – Aggregated data for the widget.

Content-Type: `application/json`

```json
{
  "value": null,
  "data": [
    {
      "value": null
    }
  ],
  "metadata": {
    "fetchedAt": "string",
    "recordCount": 1,
    "currency": null
  }
}
```

**400** – Invalid request payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing analytics.view feature

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Too many rows to group an encrypted field in application code

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Encryption is configured but the group source cannot currently be resolved

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/dashboards/widgets/data" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityType\": \"string\",
  \"metric\": {
    \"field\": \"string\",
    \"aggregate\": \"count\"
  }
}"
```

## POST `/dashboards/widgets/data/batch`

Fetch aggregated data for multiple dashboard widgets in one request

Resolves a batch of widget data requests with a single authentication, RBAC, organization-scope, and database-context setup. Each request is keyed by an opaque widget id and resolved independently, so a failure in one widget does not fail the batch.

**Tags:** Dashboards

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "requests": [
    {
      "id": "string",
      "request": {
        "entityType": "string",
        "metric": {
          "field": "string",
          "aggregate": "count"
        }
      }
    }
  ]
}
```

### Responses

**200** – Per-widget aggregation results keyed by request id.

Content-Type: `application/json`

```json
{
  "results": [
    {
      "id": "string",
      "ok": true,
      "data": {
        "value": null,
        "data": [
          {
            "value": null
          }
        ],
        "metadata": {
          "fetchedAt": "string",
          "recordCount": 1,
          "currency": null
        }
      }
    }
  ]
}
```

**400** – Invalid request payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/dashboards/widgets/data/batch" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"requests\": [
    {
      \"id\": \"string\",
      \"request\": {
        \"entityType\": \"string\",
        \"metric\": {
          \"field\": \"string\",
          \"aggregate\": \"count\"
        }
      }
    }
  ]
}"
```

## GET `/data_sync/mappings`

List or create field mappings

**Tags:** Data Sync

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/data_sync/mappings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/data_sync/mappings`

List or create field mappings

**Tags:** Data Sync

**Requires authentication.**

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/data_sync/mappings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/data_sync/mappings/{id}`

Get, update, or delete a field mapping

**Tags:** Data Sync

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**204** – Success

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/data_sync/mappings/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/data_sync/mappings/{id}`

Get, update, or delete a field mapping

**Tags:** Data Sync

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/data_sync/mappings/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/data_sync/mappings/{id}`

Get, update, or delete a field mapping

**Tags:** Data Sync

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/data_sync/mappings/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/data_sync/options`

List data sync integration options

**Tags:** Data Sync

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/data_sync/options" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/data_sync/run`

Start a data sync run

**Tags:** Data Sync

**Requires authentication.**

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/data_sync/run" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/data_sync/runs`

List sync runs

**Tags:** Data Sync

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/data_sync/runs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/data_sync/runs/{id}`

Get sync run detail

**Tags:** Data Sync

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/data_sync/runs/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/data_sync/runs/{id}/cancel`

Cancel a running sync

**Tags:** Data Sync

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/data_sync/runs/:id/cancel" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/data_sync/runs/{id}/retry`

Retry a failed sync run

**Tags:** Data Sync

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/data_sync/runs/:id/retry" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/data_sync/schedules`

List or create sync schedules

**Tags:** Data Sync

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/data_sync/schedules" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/data_sync/schedules`

List or create sync schedules

**Tags:** Data Sync

**Requires authentication.**

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/data_sync/schedules" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/data_sync/schedules/{id}`

Manage a sync schedule

**Tags:** Data Sync

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**204** – Success

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/data_sync/schedules/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/data_sync/schedules/{id}`

Manage a sync schedule

**Tags:** Data Sync

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/data_sync/schedules/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/data_sync/schedules/{id}`

Manage a sync schedule

**Tags:** Data Sync

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/data_sync/schedules/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/data_sync/validate`

Validate sync connection

**Tags:** Data Sync

**Requires authentication.**

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/data_sync/validate" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/dictionaries`

List dictionaries

Returns dictionaries accessible to the current organization, optionally including inactive records.

**Tags:** Dictionaries

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| includeInactive | query | any | Optional |

### Responses

**200** – Dictionary collection.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "key": "string",
      "name": "string",
      "description": null,
      "isSystem": true,
      "isActive": true,
      "managerVisibility": null,
      "organizationId": null,
      "createdAt": "string",
      "updatedAt": null
    }
  ]
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load dictionaries

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/dictionaries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/dictionaries`

Create dictionary

Registers a dictionary scoped to the current organization.

**Tags:** Dictionaries

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "key": "string",
  "name": "string"
}
```

### Responses

**201** – Dictionary created.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "key": "string",
  "name": "string",
  "description": null,
  "isSystem": true,
  "isActive": true,
  "managerVisibility": null,
  "organizationId": null,
  "createdAt": "string",
  "updatedAt": null
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Dictionary key already exists

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to create dictionary

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/dictionaries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"key\": \"string\",
  \"name\": \"string\"
}"
```

## DELETE `/dictionaries/{dictionaryId}`

Delete dictionary

Soft deletes the dictionary unless it is the protected currency dictionary.

**Tags:** Dictionaries

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |

### Responses

**200** – Dictionary archived.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Protected dictionary cannot be deleted

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to delete dictionary

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/dictionaries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/dictionaries/{dictionaryId}`

Get dictionary

Returns details for the specified dictionary, including inheritance flags.

**Tags:** Dictionaries

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |

### Responses

**200** – Dictionary details.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "key": "string",
  "name": "string",
  "description": null,
  "isSystem": true,
  "isActive": true,
  "managerVisibility": null,
  "organizationId": null,
  "createdAt": "string",
  "updatedAt": null
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load dictionary

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/dictionaries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/dictionaries/{dictionaryId}`

Update dictionary

Updates mutable attributes of the dictionary. Currency dictionaries are protected from modification.

**Tags:** Dictionaries

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Dictionary updated.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "key": "string",
  "name": "string",
  "description": null,
  "isSystem": true,
  "isActive": true,
  "managerVisibility": null,
  "organizationId": null,
  "createdAt": "string",
  "updatedAt": null
}
```

**400** – Validation failed or protected dictionary

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Dictionary key already exists

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to update dictionary

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/dictionaries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## GET `/dictionaries/{dictionaryId}/entries`

List dictionary entries

Returns entries for the specified dictionary ordered by its configured entry sort mode. The number of entries returned per request is capped at 500; use `limit` and `offset` with the `total`/`hasMore` response fields to page through larger dictionaries.

**Tags:** Dictionaries

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |
| limit | query | any | Optional. Maximum entries to return. Defaults to and is capped at 500. |
| offset | query | any | Optional. Number of entries to skip. Defaults to 0. |

### Responses

**200** – Dictionary entries.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "value": "string",
      "label": "string",
      "color": null,
      "icon": null,
      "position": 1,
      "isDefault": true,
      "createdAt": "string",
      "updatedAt": null
    }
  ],
  "total": 1,
  "limit": 1,
  "offset": 1,
  "hasMore": true,
  "sortMode": "label_asc"
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load dictionary entries

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/dictionaries/00000000-0000-4000-8000-000000000000/entries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/dictionaries/{dictionaryId}/entries`

Create dictionary entry

Creates a new entry in the specified dictionary.

**Tags:** Dictionaries

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "value": "string",
  "color": null,
  "icon": null
}
```

### Responses

**201** – Dictionary entry created.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "value": "string",
  "label": "string",
  "color": null,
  "icon": null,
  "position": 1,
  "isDefault": true,
  "createdAt": "string",
  "updatedAt": null
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to create dictionary entry

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/dictionaries/00000000-0000-4000-8000-000000000000/entries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"value\": \"string\",
  \"color\": null,
  \"icon\": null
}"
```

## DELETE `/dictionaries/{dictionaryId}/entries/{entryId}`

Delete dictionary entry

Deletes the specified dictionary entry via the command bus.

**Tags:** Dictionaries

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |
| entryId | path | any | Required |

### Responses

**200** – Entry deleted.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary or entry not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to delete entry

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/dictionaries/00000000-0000-4000-8000-000000000000/entries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/dictionaries/{dictionaryId}/entries/{entryId}`

Update dictionary entry

Updates the specified dictionary entry using the command bus pipeline.

**Tags:** Dictionaries

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |
| entryId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "color": null,
  "icon": null
}
```

### Responses

**200** – Dictionary entry updated.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "value": "string",
  "label": "string",
  "color": null,
  "icon": null,
  "position": 1,
  "isDefault": true,
  "createdAt": "string",
  "updatedAt": null
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary or entry not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to update entry

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/dictionaries/00000000-0000-4000-8000-000000000000/entries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"color\": null,
  \"icon\": null
}"
```

## POST `/dictionaries/{dictionaryId}/entries/reorder`

Reorder dictionary entries

Updates the position of dictionary entries for drag-and-drop reordering.

**Tags:** Dictionaries

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "entries": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "position": 1
    }
  ]
}
```

### Responses

**200** – Entries reordered.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to reorder entries

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/dictionaries/00000000-0000-4000-8000-000000000000/entries/reorder" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entries\": [
    {
      \"id\": \"00000000-0000-4000-8000-000000000000\",
      \"position\": 1
    }
  ]
}"
```

## POST `/dictionaries/{dictionaryId}/entries/set-default`

Set default dictionary entry

Marks the specified entry as the default for this dictionary, clearing any previous default.

**Tags:** Dictionaries

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "entryId": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Default entry set.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary or entry not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to set default entry

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/dictionaries/00000000-0000-4000-8000-000000000000/entries/set-default" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entryId\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/directory/organization-branding`

Read sidebar branding for the selected organization

Returns the logo URL used by the backend sidebar for the currently selected organization.

**Tags:** Directory

**Requires authentication.**

### Responses

**200** – Organization branding

Content-Type: `application/json`

```json
{
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "organizationName": "string",
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "logoUrl": null,
  "logoPreserveAspectRatio": true,
  "updatedAt": null
}
```

**400** – A concrete organization scope is required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Organization not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/directory/organization-branding" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/directory/organization-branding`

Update sidebar branding for the selected organization

Stores an external image URL or an internal attachment image URL as the selected organization logo.

**Tags:** Directory

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "logoUrl": null
}
```

### Responses

**200** – Updated organization branding

Content-Type: `application/json`

```json
{
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "organizationName": "string",
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "logoUrl": null,
  "logoPreserveAspectRatio": true,
  "updatedAt": null
}
```

**400** – Save failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Organization branding changed since it was loaded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Invalid logo URL

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/directory/organization-branding" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"logoUrl\": null
}"
```

## GET `/directory/organization-switcher`

Load organization switcher menu

Returns the hierarchical menu of organizations the current user may switch to within the active tenant.

**Tags:** Directory

**Requires authentication.**

### Responses

**200** – Organization switcher payload.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "depth": 1,
      "selectable": true,
      "children": []
    }
  ],
  "selectedId": null,
  "canManage": true,
  "canViewAllOrganizations": true,
  "tenantId": null,
  "tenants": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "isActive": true
    }
  ],
  "isSuperAdmin": true
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/directory/organization-switcher" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/directory/organizations`

Delete organization

Soft deletes an organization identified by id.

**Tags:** Directory

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Organization deleted.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing directory.organizations.manage feature

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/directory/organizations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/directory/organizations`

List organizations

Returns organizations using options, tree, or paginated manage view depending on the `view` parameter.

**Tags:** Directory

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| view | query | any | Optional |
| ids | query | any | Optional |
| tenantId | query | any | Optional |
| includeInactive | query | any | Optional |
| status | query | any | Optional |

### Responses

**200** – Organization data for the requested view.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "parentId": null,
      "parentName": null,
      "tenantId": null,
      "tenantName": null,
      "rootId": null,
      "treePath": null
    }
  ]
}
```

**400** – Invalid query or tenant scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/directory/organizations?page=1&pageSize=50&view=options" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/directory/organizations`

Create organization

Creates a new organization within a tenant and optionally assigns hierarchy relationships.

**Tags:** Directory

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "name": "string",
  "slug": null,
  "logoUrl": null,
  "parentId": null
}
```

### Responses

**201** – Organization created.

Content-Type: `application/json`

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

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing directory.organizations.manage feature

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/directory/organizations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\",
  \"slug\": null,
  \"logoUrl\": null,
  \"parentId\": null
}"
```

## PUT `/directory/organizations`

Update organization

Updates organization details and hierarchy assignments. Fields that are omitted keep their current value; send `parentId: null` or `childIds: []` to clear the parent or the children.

**Tags:** Directory

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "slug": null,
  "logoUrl": null,
  "parentId": null
}
```

### Responses

**200** – Organization updated.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing directory.organizations.manage feature

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/directory/organizations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"slug\": null,
  \"logoUrl\": null,
  \"parentId\": null
}"
```

## GET `/directory/organizations/lookup`

Public organization lookup by slug

**Tags:** Directory (Tenants & Organizations)

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/directory/organizations/lookup" \
  -H "Accept: application/json"
```

## DELETE `/directory/tenants`

Delete tenant

Soft deletes the tenant identified by id.

**Tags:** Directory

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Tenant removed.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing directory.tenants.manage feature

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/directory/tenants" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/directory/tenants`

List tenants

Returns tenants visible to the current user with optional search and pagination.

**Tags:** Directory

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| isActive | query | any | Optional |

### Responses

**200** – Paged list of tenants.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "isActive": true,
      "createdAt": null,
      "updatedAt": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Requires super-admin

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/directory/tenants?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/directory/tenants`

Create tenant

Creates a new tenant and returns its identifier.

**Tags:** Directory

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "name": "string"
}
```

### Responses

**201** – Tenant created.

Content-Type: `application/json`

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

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing directory.tenants.manage feature

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/directory/tenants" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\"
}"
```

## PUT `/directory/tenants`

Update tenant

Updates tenant properties such as name or activation state.

**Tags:** Directory

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Tenant updated.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing directory.tenants.manage feature

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/directory/tenants" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/directory/tenants/lookup`

Public tenant lookup

**Tags:** Directory (Tenants & Organizations)

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/directory/tenants/lookup" \
  -H "Accept: application/json"
```

## DELETE `/entities/definitions`

Soft delete custom field definition

Marks the specified definition inactive and tombstones it for the current scope.

**Tags:** Entities

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "key": "string"
}
```

### Responses

**200** – Definition deleted

Content-Type: `application/json`

```json
{
  "ok": true,
  "version": null
}
```

**400** – Missing entity id or key

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Definition not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/entities/definitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"key\": \"string\"
}"
```

## GET `/entities/definitions`

List active custom field definitions

Returns active custom field definitions for the supplied entity ids, respecting tenant scope and tombstones.

**Tags:** Entities

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Optional |
| entityIds | query | any | Optional |
| fieldset | query | any | Optional |

### Responses

**200** – Definition list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "key": "string",
      "kind": "string",
      "label": "string",
      "entityId": "string"
    }
  ]
}
```

**400** – Missing entity id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/entities/definitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/entities/definitions`

Upsert custom field definition

Creates or updates a custom field definition for the current tenant/org scope.

**Tags:** Entities

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "key": "string",
  "kind": "text"
}
```

### Responses

**200** – Definition saved

Content-Type: `application/json`

```json
{
  "ok": true,
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "key": "string",
    "kind": "string",
    "configJson": {}
  }
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/entities/definitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"key\": \"string\",
  \"kind\": \"text\"
}"
```

## POST `/entities/definitions.batch`

Save multiple custom field definitions

Creates or updates multiple definitions for a single entity in one transaction.

**Tags:** Entities

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "definitions": [
    {
      "key": "string",
      "kind": "text"
    }
  ]
}
```

### Responses

**200** – Definitions saved

Content-Type: `application/json`

```json
{
  "ok": true,
  "version": null
}
```

**400** – Validation error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Unexpected failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/entities/definitions.batch" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"definitions\": [
    {
      \"key\": \"string\",
      \"kind\": \"text\"
    }
  ]
}"
```

## GET `/entities/definitions.manage`

Get management snapshot

Returns scoped custom field definitions (including inactive tombstones) for administration interfaces.

**Tags:** Entities

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Required |

### Responses

**200** – Scoped definitions and deleted keys

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "key": "string",
      "kind": "string",
      "configJson": null,
      "organizationId": null,
      "tenantId": null
    }
  ],
  "deletedKeys": [
    "string"
  ],
  "version": null
}
```

**400** – Missing entity id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication or feature

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/entities/definitions.manage?entityId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/entities/definitions.restore`

Restore definition

Reactivates a previously soft-deleted definition within the current tenant/org scope.

**Tags:** Entities

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "key": "string"
}
```

### Responses

**200** – Definition restored

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing entity id or key

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Definition not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/entities/definitions.restore" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"key\": \"string\"
}"
```

## GET `/entities/encryption`

Fetch encryption map

Returns the encrypted field map for the current tenant/organization scope.

**Tags:** Entities

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Required |

### Responses

**200** – Map

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "fields": [
    {
      "field": "string",
      "hashField": null
    }
  ],
  "updatedAt": null
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/entities/encryption?entityId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/entities/encryption`

Upsert encryption map

Creates or updates the encryption map for the current tenant/organization scope. Enforces optimistic locking when the caller sends the expected version header.

**Tags:** Entities

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "tenantId": null,
  "organizationId": null,
  "fields": [
    {
      "field": "string",
      "hashField": null
    }
  ]
}
```

### Responses

**200** – Saved

Content-Type: `application/json`

```json
{
  "ok": true,
  "updatedAt": null
}
```

**409** – Optimistic-lock conflict (stale write)

Content-Type: `application/json`

```json
{
  "error": "string",
  "code": "string",
  "currentUpdatedAt": "string",
  "expectedUpdatedAt": "string"
}
```

**422** – Selected organization is unavailable

Content-Type: `application/json`

```json
{
  "error": "string",
  "code": "organization_selection_invalid"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/entities/encryption" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"tenantId\": null,
  \"organizationId\": null,
  \"fields\": [
    {
      \"field\": \"string\",
      \"hashField\": null
    }
  ]
}"
```

## DELETE `/entities/entities`

Soft delete custom entity

Marks the specified custom entity inactive within the current scope.

**Tags:** Entities

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string"
}
```

### Responses

**200** – Entity deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing entity id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Entity not found in scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/entities/entities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\"
}"
```

## GET `/entities/entities`

List available entities

Returns generated and custom entities scoped to the caller with field counts per entity.

**Tags:** Entities

**Requires authentication.**

### Responses

**200** – List of entities

Content-Type: `application/json`

```json
{
  "items": [
    {
      "entityId": "string",
      "source": "code",
      "label": "string",
      "count": 1
    }
  ]
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/entities/entities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/entities/entities`

Upsert custom entity

Creates or updates a tenant/org scoped custom entity definition.

**Tags:** Entities

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "label": "string",
  "description": null,
  "showInSidebar": false,
  "accessRestricted": false
}
```

### Responses

**200** – Entity saved

Content-Type: `application/json`

```json
{
  "ok": true,
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "entityId": "string",
    "label": "string"
  }
}
```

**400** – Validation error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/entities/entities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"label\": \"string\",
  \"description\": null,
  \"showInSidebar\": false,
  \"accessRestricted\": false
}"
```

## GET `/entities/entity-settings`

Get custom entity settings

Returns the tenant-scoped default-restricted policy for new custom entities.

**Tags:** Entities

**Requires authentication.**

### Responses

**200** – Current settings

Content-Type: `application/json`

```json
{
  "newEntitiesRestrictedByDefault": true,
  "updatedAt": null
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/entities/entity-settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/entities/entity-settings`

Update custom entity settings

Sets the tenant-scoped default-restricted policy for new custom entities.

**Tags:** Entities

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "newEntitiesRestrictedByDefault": true
}
```

### Responses

**200** – Updated settings

Content-Type: `application/json`

```json
{
  "ok": true,
  "newEntitiesRestrictedByDefault": true,
  "updatedAt": null
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Optimistic lock conflict

Content-Type: `application/json`

```json
{
  "code": "string",
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/entities/entity-settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"newEntitiesRestrictedByDefault\": true
}"
```

## DELETE `/entities/records`

Delete record

Soft deletes the specified record within the current tenant/org scope.

**Tags:** Entities

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "recordId": "string"
}
```

### Responses

**200** – Record deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing entity id or record id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Record not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Unexpected failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/entities/records" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"recordId\": \"string\"
}"
```

## GET `/entities/records`

List records

Returns paginated records for the supplied entity. Supports custom field filters, exports, and soft-delete toggles.

**Tags:** Entities

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Required |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| search | query | any | Optional |
| searchFields | query | any | Optional |
| withDeleted | query | any | Optional |
| format | query | any | Optional |
| exportScope | query | any | Optional |
| export_scope | query | any | Optional |
| all | query | any | Optional |
| full | query | any | Optional |

### Responses

**200** – Paginated records

Content-Type: `application/json`

```json
{
  "items": [
    {}
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

**400** – Missing entity id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Unexpected failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/entities/records?entityId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/entities/records`

Create record

Creates a record for the given entity. When `recordId` is omitted or not a UUID the data engine will generate one automatically.

**Tags:** Entities

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "values": {}
}
```

### Responses

**200** – Record created

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Unexpected failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/entities/records" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"values\": {}
}"
```

## PUT `/entities/records`

Update record

Updates an existing record. If the provided recordId is not a UUID the record will be created instead to support optimistic flows.

**Tags:** Entities

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "recordId": "string",
  "values": {}
}
```

### Responses

**200** – Record updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Unexpected failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/entities/records" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"recordId\": \"string\",
  \"values\": {}
}"
```

## GET `/entities/relations/options`

List relation options

Returns up to 200 option entries for populating relation dropdowns, automatically resolving label fields when omitted. An entityId that matches neither an active custom entity nor a registered ORM entity yields an empty option list.

**Tags:** Entities

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Required |
| labelField | query | any | Optional |
| q | query | any | Optional |
| ids | query | any | Optional |
| routeContextFields | query | any | Optional |

### Responses

**200** – Option list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "value": "string",
      "label": "string"
    }
  ]
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/entities/relations/options?entityId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/entities/sidebar-entities`

Get sidebar entities

Returns custom entities flagged with `showInSidebar` for the current tenant/org scope.

**Tags:** Entities

**Requires authentication.**

### Responses

**200** – Sidebar entities for navigation

Content-Type: `application/json`

```json
{
  "items": [
    {
      "entityId": "string",
      "label": "string",
      "href": "string"
    }
  ]
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/entities/sidebar-entities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/events`

List declared events

Returns every declared event, including its declared payloadSchema when the module (or the generated CRUD default) provides one. Filters: category, module, excludeTriggerExcluded (default true).

**Tags:** Events

**Requires authentication.**

### Responses

**200** – Declared events

Content-Type: `application/json`

```json
{
  "data": [
    {
      "id": "string",
      "label": "string"
    }
  ],
  "total": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/events" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/events/stream`

GET /events/stream

**Tags:** Events

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/events/stream" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/feature_toggles/check/boolean`

Check if feature is enabled

Checks if a feature toggle is enabled for the current context.

**Tags:** Feature Toggles

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| identifier | query | any | Required. Feature toggle identifier |

### Responses

**200** – Feature status

Content-Type: `application/json`

```json
{
  "enabled": true,
  "source": "override",
  "toggleId": "string",
  "identifier": "string",
  "tenantId": "string"
}
```

**400** – Bad Request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Tenant not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/feature_toggles/check/boolean?identifier=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/feature_toggles/check/json`

Get json config

Gets the json configuration for a feature toggle.

**Tags:** Feature Toggles

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| identifier | query | any | Required. Feature toggle identifier |

### Responses

**200** – Json config

Content-Type: `application/json`

```json
{
  "valueType": "json",
  "source": "override",
  "toggleId": "string",
  "identifier": "string",
  "tenantId": "string"
}
```

**400** – Bad Request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Tenant not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/feature_toggles/check/json?identifier=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/feature_toggles/check/number`

Get number config

Gets the number configuration for a feature toggle.

**Tags:** Feature Toggles

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| identifier | query | any | Required. Feature toggle identifier |

### Responses

**200** – Number config

Content-Type: `application/json`

```json
{
  "valueType": "number",
  "value": 1,
  "source": "override",
  "toggleId": "string",
  "identifier": "string",
  "tenantId": "string"
}
```

**400** – Bad Request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Tenant not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/feature_toggles/check/number?identifier=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/feature_toggles/check/string`

Get string config

Gets the string configuration for a feature toggle.

**Tags:** Feature Toggles

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| identifier | query | any | Required. Feature toggle identifier |

### Responses

**200** – String config

Content-Type: `application/json`

```json
{
  "valueType": "string",
  "value": "string",
  "source": "override",
  "toggleId": "string",
  "identifier": "string",
  "tenantId": "string"
}
```

**400** – Bad Request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Tenant not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/feature_toggles/check/string?identifier=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/feature_toggles/global`

Delete global feature toggle

Soft deletes a global feature toggle by ID. Requires superadmin role.

**Tags:** Feature Toggles

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. Feature toggle identifier |

### Responses

**200** – Feature toggle deleted

Content-Type: `application/json`

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

**400** – Invalid identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden - superadmin role required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Feature toggle not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/feature_toggles/global?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/feature_toggles/global`

List global feature toggles

Returns all global feature toggles with filtering and pagination. Requires superadmin role.

**Tags:** Feature Toggles

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional. Page number for pagination |
| pageSize | query | any | Optional. Number of items per page (max 200) |
| search | query | any | Optional. Case-insensitive search across identifier, name, description, and category |
| type | query | any | Optional. Filter by toggle type (boolean, string, number, json) |
| category | query | any | Optional. Filter by category (case-insensitive partial match) |
| name | query | any | Optional. Filter by name (case-insensitive partial match) |
| identifier | query | any | Optional. Filter by identifier (case-insensitive partial match) |
| sortField | query | any | Optional. Field to sort by |
| sortDir | query | any | Optional. Sort direction (ascending or descending) |

### Responses

**200** – Feature toggles collection

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "identifier": "string",
      "name": "string",
      "description": null,
      "category": null,
      "type": "boolean",
      "defaultValue": null,
      "createdAt": null,
      "updatedAt": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden - superadmin role required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/feature_toggles/global?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/feature_toggles/global`

Create global feature toggle

Creates a new global feature toggle. Requires superadmin role.

**Tags:** Feature Toggles

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "identifier": "string",
  "name": "string",
  "description": null,
  "category": null,
  "type": "boolean",
  "defaultValue": null
}
```

### Responses

**201** – Feature toggle created

Content-Type: `application/json`

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

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden - superadmin role required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/feature_toggles/global" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"identifier\": \"string\",
  \"name\": \"string\",
  \"description\": null,
  \"category\": null,
  \"type\": \"boolean\",
  \"defaultValue\": null
}"
```

## PUT `/feature_toggles/global`

Update global feature toggle

Updates an existing global feature toggle. Requires superadmin role.

**Tags:** Feature Toggles

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "description": null,
  "category": null,
  "defaultValue": null
}
```

### Responses

**200** – Feature toggle updated

Content-Type: `application/json`

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

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden - superadmin role required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Feature toggle not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/feature_toggles/global" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"description\": null,
  \"category\": null,
  \"defaultValue\": null
}"
```

## GET `/feature_toggles/global/{id}`

Fetch feature toggle by ID

Returns complete details of a feature toggle.

**Tags:** Feature Toggles

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Feature toggle detail

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "identifier": "string",
  "name": "string",
  "description": null,
  "category": null,
  "type": "boolean",
  "defaultValue": null,
  "createdAt": null,
  "updatedAt": null
}
```

**400** – Invalid identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Feature toggle not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/feature_toggles/global/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/feature_toggles/global/{id}/override`

Fetch feature toggle override

Returns feature toggle override.

**Tags:** Feature Toggles

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Feature toggle overrides

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "tenantName": "string",
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "toggleType": "boolean",
  "updatedAt": null
}
```

**400** – Invalid request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Feature toggle not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/feature_toggles/global/:id/override" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/feature_toggles/overrides`

List overrides

Returns list of feature toggle overrides.

**Tags:** Feature Toggles

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| category | query | any | Optional |
| name | query | any | Optional |
| identifier | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – List of overrides

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "toggleId": "00000000-0000-4000-8000-000000000000",
      "tenantName": "string",
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "identifier": "string",
      "name": "string",
      "category": "string",
      "isOverride": true
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1,
  "isSuperAdmin": true
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/feature_toggles/overrides?page=1&pageSize=25" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/feature_toggles/overrides`

Change override state

Enable, disable or inherit a feature toggle for a specific tenant.

**Tags:** Feature Toggles

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "toggleId": "00000000-0000-4000-8000-000000000000",
  "isOverride": true
}
```

### Responses

**200** – Override updated

Content-Type: `application/json`

```json
{
  "ok": true,
  "overrideToggleId": null
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/feature_toggles/overrides" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"toggleId\": \"00000000-0000-4000-8000-000000000000\",
  \"isOverride\": true
}"
```

## GET `/forms`

List forms

Returns paginated forms scoped to the authenticated organization.

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| status | query | any | Optional |
| q | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – Form list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "key": "string",
      "name": "string",
      "description": null,
      "status": "draft",
      "defaultLocale": "string",
      "supportedLocales": [
        "string"
      ],
      "currentPublishedVersionId": null,
      "currentPublishedVersionNumber": null,
      "draftVersionId": null,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

**400** – Tenant or organization context missing

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/forms`

Create form

Creates a new form. Status starts as draft until a version is published.

**Tags:** Forms

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "key": "string",
  "name": "string",
  "description": null,
  "defaultLocale": "string",
  "supportedLocales": [
    "string"
  ],
  "retentionDays": null
}
```

### Responses

**201** – Form created

Content-Type: `application/json`

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

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Form key already taken

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"key\": \"string\",
  \"name\": \"string\",
  \"description\": null,
  \"defaultLocale\": \"string\",
  \"supportedLocales\": [
    \"string\"
  ],
  \"retentionDays\": null
}"
```

## DELETE `/forms/{id}`

Archive form

Soft-archives the form (sets archived_at, status=archived).

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Form archived

Content-Type: `application/json`

```json
{
  "ok": true,
  "updatedAt": "string"
}
```

**404** – Form not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/forms/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/forms/{id}`

Get form detail

Returns the form metadata + version summaries.

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Form detail

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "key": "string",
  "name": "string",
  "description": null,
  "status": "draft",
  "defaultLocale": "string",
  "supportedLocales": [
    "string"
  ],
  "currentPublishedVersionId": null,
  "createdAt": "string",
  "updatedAt": "string",
  "versions": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "versionNumber": 1,
      "status": "draft",
      "schemaHash": "string",
      "registryVersion": "string",
      "publishedAt": null,
      "publishedBy": null,
      "changelog": null,
      "archivedAt": null,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ]
}
```

**404** – Form not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/forms/{id}`

Update form metadata

Updates the form name, description, and/or locale set (supportedLocales + defaultLocale).

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "description": null,
  "retentionDays": null
}
```

### Responses

**200** – Form updated

Content-Type: `application/json`

```json
{
  "ok": true,
  "updatedAt": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Form not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/forms/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"description\": null,
  \"retentionDays\": null
}"
```

## GET `/forms/{id}/analytics`

Aggregate analytics for a form

Tenant-scoped, PII-safe aggregate analytics across all versions of a form: funnel/completion, daily volume, time-to-complete, per-field response counts (value distributions only for non-sensitive enumerable types), and best-effort draft drop-off. Counts only — never raw answer values.

**Tags:** Forms Admin

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Aggregate analytics

Content-Type: `application/json`

```json
{
  "formId": "string",
  "window": {
    "from": null,
    "to": null
  },
  "scan": {
    "limit": 1,
    "scanned": 1,
    "capped": true
  },
  "funnel": {
    "started": 1,
    "submitted": 1,
    "completionRate": 1,
    "byStatus": {
      "key": 1
    }
  },
  "volume": [
    {
      "date": "string",
      "started": 1,
      "submitted": 1
    }
  ],
  "timeToComplete": {
    "sampleSize": 1,
    "medianSeconds": null,
    "averageSeconds": null
  },
  "fields": [
    {
      "fieldKey": "string",
      "type": "string",
      "sensitive": true,
      "answered": 1,
      "blank": 1
    }
  ],
  "dropOff": [
    {
      "sectionKey": "string",
      "count": 1
    }
  ]
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Tenant scope required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Bad query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Computation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/:id/analytics" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/forms/{id}/distributions`

List distributions

Returns all distributions for a form scoped to the authenticated organization.

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Distribution list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "formId": "00000000-0000-4000-8000-000000000000",
      "mode": "open",
      "status": "active",
      "publicSlug": null,
      "title": null,
      "defaultLocale": "string",
      "pinnedVersionId": null,
      "requireCustomerAuth": true,
      "allowMultipleSubmissions": true,
      "maxResponses": null,
      "responseCount": 1,
      "opensAt": null,
      "closesAt": null,
      "redirectUrl": null,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "total": 1
}
```

**400** – Organization context missing

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Form not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/00000000-0000-4000-8000-000000000000/distributions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/forms/{id}/distributions`

Create distribution

Creates an open (public link) or personal (per-recipient invitations) distribution. Open distributions mint a public_slug.

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "mode": "open",
  "pinnedVersionId": null,
  "title": null,
  "defaultLocale": "string",
  "maxResponses": null,
  "opensAt": null,
  "closesAt": null,
  "redirectUrl": null,
  "settings": null
}
```

### Responses

**201** – Distribution created

Content-Type: `application/json`

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

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Form not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/00000000-0000-4000-8000-000000000000/distributions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"mode\": \"open\",
  \"pinnedVersionId\": null,
  \"title\": null,
  \"defaultLocale\": \"string\",
  \"maxResponses\": null,
  \"opensAt\": null,
  \"closesAt\": null,
  \"redirectUrl\": null,
  \"settings\": null
}"
```

## GET `/forms/{id}/run/context`

Get the published form version for the in-app runner.

Returns the schema + uiSchema for the form's currently published version. Authenticated and scoped to the caller's tenant and organization — anonymous runs go through POST /api/forms/public/start instead.

**Tags:** Forms Runtime

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Published form version

Content-Type: `application/json`

```json
{
  "form": {
    "id": "00000000-0000-4000-8000-000000000000",
    "key": "string",
    "name": "string",
    "defaultLocale": "string",
    "supportedLocales": [
      "string"
    ]
  },
  "formVersion": {
    "id": "00000000-0000-4000-8000-000000000000",
    "versionNumber": 1,
    "schemaHash": "string",
    "registryVersion": "string"
  },
  "schema": {},
  "uiSchema": {},
  "requiresCustomerAuth": true
}
```

**400** – Missing tenant or organization context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Form or version not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Form not active or unpublished

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/:id/run/context" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/forms/{id}/run/submissions`

Submit answers via the in-app runner with tamper validation.

Server-side re-runs the evaluator against the posted answers and asserts the claimed ending is reachable. 422 on mismatch. Authenticated and scoped to the caller's tenant and organization; deprecated in favour of POST /api/forms/public/submissions/:id/submit.

**Tags:** Forms Runtime

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Submission accepted (validation only — persistence in phase 1d).

Content-Type: `application/json`

```json
{
  "accepted": true,
  "reachedEndingKey": null
}
```

**400** – Malformed body, or missing tenant/organization context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Form or version not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Form inactive or tamper detected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/:id/run/submissions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/forms/{id}/submissions`

List submissions for a form (admin inbox)

Paginated submission listing scoped to the form id.

**Tags:** Forms Admin

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Submission list

Content-Type: `application/json`

```json
{
  "items": [
    {}
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Bad query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/:id/submissions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/forms/{id}/theme-logo`

Upload a form logo/header image

Stores a brand logo as a public, unscoped attachment and returns its assetId for x-om-theme.logo. Requires forms.design.

**Tags:** Forms Authoring

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**201** – Logo stored

Content-Type: `application/json`

```json
{
  "assetId": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Not a multipart upload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Form not found in scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**413** – File too large

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Missing file or not a valid image

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/:id/theme-logo" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/forms/{id}/versions/{versionId}`

Get version detail

Returns the full schema/uiSchema/roles for a form version.

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| versionId | path | any | Required |

### Responses

**200** – Form version detail

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "formId": "00000000-0000-4000-8000-000000000000",
  "versionNumber": 1,
  "status": "draft",
  "schema": {},
  "uiSchema": {},
  "roles": [
    "string"
  ],
  "schemaHash": "string",
  "registryVersion": "string",
  "publishedAt": null,
  "publishedBy": null,
  "changelog": null,
  "archivedAt": null,
  "createdAt": "string",
  "updatedAt": "string",
  "answerMappings": {
    "key": "string"
  }
}
```

**404** – Version not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/00000000-0000-4000-8000-000000000000/versions/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/forms/{id}/versions/{versionId}`

Update draft version

Updates schema/uiSchema/roles/changelog on a draft version. Rejects with 409 (version_is_frozen) for non-draft versions.

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| versionId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "changelog": null
}
```

### Responses

**200** – Draft updated

Content-Type: `application/json`

```json
{
  "ok": true,
  "updatedAt": "string"
}
```

**404** – Version not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Version is frozen

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Schema validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/forms/00000000-0000-4000-8000-000000000000/versions/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"changelog\": null
}"
```

## GET `/forms/{id}/versions/{versionId}/diff`

Diff form versions

Returns added/removed/modified fields between the URL `versionId` (newer) and the `against` query param (older).

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| versionId | path | any | Required |
| against | query | any | Required |

### Responses

**200** – Diff payload

Content-Type: `application/json`

```json
{
  "base": {
    "id": "00000000-0000-4000-8000-000000000000",
    "versionNumber": 1,
    "schemaHash": "string"
  },
  "against": {
    "id": "00000000-0000-4000-8000-000000000000",
    "versionNumber": 1,
    "schemaHash": "string"
  },
  "diff": [
    {
      "kind": "added",
      "key": "string",
      "field": {
        "key": "string",
        "type": "string",
        "sectionKey": null,
        "sensitive": true,
        "editableBy": [
          "string"
        ],
        "visibleTo": [
          "string"
        ],
        "required": true
      }
    }
  ]
}
```

**404** – Version not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/00000000-0000-4000-8000-000000000000/versions/00000000-0000-4000-8000-000000000000/diff?against=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/forms/{id}/versions/{versionId}/publish`

Publish version

Publishes a draft version. Emits forms.form_version.published, advances form.current_published_version_id, and pins registry_version. Rejects with 422 no_op_publish if schema is unchanged.

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| versionId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "changelog": null
}
```

### Responses

**200** – Version published

Content-Type: `application/json`

```json
{
  "versionId": "00000000-0000-4000-8000-000000000000",
  "versionNumber": 1,
  "updatedAt": "string"
}
```

**404** – Version not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Version is frozen

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – No-op or schema invalid

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/00000000-0000-4000-8000-000000000000/versions/00000000-0000-4000-8000-000000000000/publish" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"changelog\": null
}"
```

## POST `/forms/{id}/versions/fork`

Fork draft

Creates a new draft version. Optionally clones from `fromVersionId`. Rejects if a draft already exists (422 draft_already_exists).

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "fromVersionId": null
}
```

### Responses

**201** – Draft created

Content-Type: `application/json`

```json
{
  "versionId": "00000000-0000-4000-8000-000000000000"
}
```

**404** – Form not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Draft already exists

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/00000000-0000-4000-8000-000000000000/versions/fork" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"fromVersionId\": null
}"
```

## GET `/forms/by-key/{key}/active`

Get active form version by key

Returns the published form version matching the supplied key, sliced to the caller's available roles.

**Tags:** Forms Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| key | path | any | Required |

### Responses

**200** – Active form version

Content-Type: `application/json`

```json
{
  "form": {
    "id": "00000000-0000-4000-8000-000000000000",
    "key": "string",
    "name": "string",
    "defaultLocale": "string",
    "supportedLocales": [
      "string"
    ]
  },
  "formVersion": {
    "id": "00000000-0000-4000-8000-000000000000",
    "versionNumber": 1,
    "schemaHash": "string",
    "registryVersion": "string",
    "roles": [
      "string"
    ]
  },
  "schema": {},
  "uiSchema": {},
  "fieldIndex": {
    "key": {
      "key": "string",
      "type": "string",
      "sectionKey": null,
      "sensitive": true,
      "editableBy": [
        "string"
      ],
      "visibleTo": [
        "string"
      ],
      "required": true
    }
  },
  "callerRoles": [
    "string"
  ]
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Form not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Form is not active or has no published version

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/by-key/:key/active" \
  -H "Accept: application/json"
```

## GET `/forms/distributions/{distributionId}`

Get distribution

Returns a single distribution scoped to the authenticated organization.

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| distributionId | path | any | Required |

### Responses

**200** – Distribution

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "formId": "00000000-0000-4000-8000-000000000000",
  "mode": "open",
  "status": "active",
  "publicSlug": null,
  "title": null,
  "defaultLocale": "string",
  "pinnedVersionId": null,
  "requireCustomerAuth": true,
  "allowMultipleSubmissions": true,
  "maxResponses": null,
  "responseCount": 1,
  "opensAt": null,
  "closesAt": null,
  "redirectUrl": null,
  "settings": null,
  "createdAt": "string",
  "updatedAt": "string"
}
```

**404** – Distribution not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/distributions/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/forms/distributions/{distributionId}`

Update or close distribution

Updates mutable distribution fields. A body of { "status": "closed" } closes the distribution and emits forms.distribution.closed.

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| distributionId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "title": null,
  "maxResponses": null,
  "opensAt": null,
  "closesAt": null,
  "redirectUrl": null,
  "settings": null
}
```

### Responses

**200** – Distribution updated

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "updatedAt": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Distribution not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/forms/distributions/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"title\": null,
  \"maxResponses\": null,
  \"opensAt\": null,
  \"closesAt\": null,
  \"redirectUrl\": null,
  \"settings\": null
}"
```

## GET `/forms/distributions/{distributionId}/invitations`

List invitations

Returns invitations for a distribution with recipient PII decrypted. Token hashes are never returned.

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| distributionId | path | any | Required |

### Responses

**200** – Invitation list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "recipientEmail": null,
      "recipientName": null,
      "recipientRef": null,
      "role": null,
      "status": "pending",
      "locale": null,
      "sentAt": null,
      "openedAt": null,
      "startedAt": null,
      "submittedAt": null,
      "expiresAt": null,
      "sendCount": 1,
      "lastError": null,
      "createdAt": "string"
    }
  ],
  "total": 1
}
```

**404** – Distribution not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/distributions/00000000-0000-4000-8000-000000000000/invitations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/forms/distributions/{distributionId}/invitations`

Bulk-create invitations

Creates one invitation per recipient. For personal distributions, the response returns each invitation rawToken exactly once so the admin can build the personal link.

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| distributionId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "recipients": [
    {}
  ]
}
```

### Responses

**201** – Invitations created

Content-Type: `application/json`

```json
{
  "invitations": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "rawToken": null
    }
  ]
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Distribution not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/distributions/00000000-0000-4000-8000-000000000000/invitations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"recipients\": [
    {}
  ]
}"
```

## DELETE `/forms/distributions/{distributionId}/invitations/{invitationId}`

Revoke invitation

Soft-revokes an invitation — the row is retained and its status becomes revoked so a revoked token resolves to 410 (not 404).

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| distributionId | path | any | Required |
| invitationId | path | any | Required |

### Responses

**200** – Invitation revoked

Content-Type: `application/json`

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

**404** – Invitation not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/forms/distributions/00000000-0000-4000-8000-000000000000/invitations/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/forms/distributions/{distributionId}/invitations/{invitationId}/send`

Send invitation

Marks the invitation sent (sent_at, send_count += 1) and emits forms.invitation.sent so the email subscriber dispatches a reminder. Also the resend path.

**Tags:** Forms

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| distributionId | path | any | Required |
| invitationId | path | any | Required |

### Responses

**200** – Invitation send tracked

Content-Type: `application/json`

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

**404** – Invitation not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Invitation revoked or already submitted

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Invitation has no recipient email

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/distributions/00000000-0000-4000-8000-000000000000/invitations/00000000-0000-4000-8000-000000000000/send" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/forms/form-submissions`

Start a new form submission

Starts a submission for the active version of a form, auto-assigning the starter to the default actor role.

**Tags:** Forms Runtime

### Request Body

Content-Type: `application/json`

```json
{
  "form_key": "string",
  "subject_type": "string",
  "subject_id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**201** – Submission started

Content-Type: `application/json`

```json
{
  "submission": {},
  "revision": {},
  "decoded_data": {},
  "actors": [
    {}
  ]
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Form not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Validation or form-state error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/form-submissions" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"form_key\": \"string\",
  \"subject_type\": \"string\",
  \"subject_id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/forms/form-submissions/{id}`

Get current submission state (role-sliced)

Returns the current revision payload sliced to the calling user's active actor role.

**Tags:** Forms Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Submission state

Content-Type: `application/json`

```json
{
  "submission": {},
  "revision": {},
  "decoded_data": {},
  "actors": [
    {}
  ]
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Submission not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/form-submissions/:id" \
  -H "Accept: application/json"
```

## PATCH `/forms/form-submissions/{id}`

Save (autosave) a submission revision

Appends a revision when the supplied base_revision_id matches; rate-limited to FORMS_AUTOSAVE_INTERVAL_MS / 2.

**Tags:** Forms Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "base_revision_id": "00000000-0000-4000-8000-000000000000",
  "patch": {}
}
```

### Responses

**200** – Revision saved

Content-Type: `application/json`

```json
{
  "revision": {},
  "coalesced": true
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – No active actor row for user

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Submission not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Stale base_revision_id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Save rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/forms/form-submissions/:id" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"base_revision_id\": \"00000000-0000-4000-8000-000000000000\",
  \"patch\": {}
}"
```

## POST `/forms/form-submissions/{id}/attachments`

Upload an attachment (authenticated)

Stores an encrypted file for a file-typed field. Caller must be an active actor on the submission.

**Tags:** Forms Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**201** – Attachment stored

Content-Type: `application/json`

```json
{
  "id": "string",
  "filename": "string",
  "contentType": "string",
  "sizeBytes": 1
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – No active actor row for user

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**413** – File empty or oversize

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Disallowed type / invalid field / scan rejected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/form-submissions/:id/attachments" \
  -H "Accept: application/json"
```

## GET `/forms/form-submissions/{id}/resume-token`

Issue a resume token for a submission

Returns a signed token that another device can use to resume the submission. TTL governed by FORMS_RESUME_TOKEN_TTL_S.

**Tags:** Forms Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Resume token issued

Content-Type: `application/json`

```json
{
  "token": "string",
  "expiresAt": "string"
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – No active actor row for user

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Submission not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/form-submissions/:id/resume-token" \
  -H "Accept: application/json"
```

## POST `/forms/form-submissions/{id}/submit`

Submit a form submission

Final submission of a draft submission. Verifies base_revision_id and stores the submit metadata.

**Tags:** Forms Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "base_revision_id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Submission submitted

Content-Type: `application/json`

```json
{
  "submission": {}
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Submission not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Stale base_revision_id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Submission already in terminal state

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/form-submissions/:id/submit" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"base_revision_id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/forms/form-submissions/by-subject/{subject_type}/{subject_id}`

List submissions for a subject

Returns submissions accessible to the caller for the supplied (subject_type, subject_id).

**Tags:** Forms Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| subject_type | path | any | Required |
| subject_id | path | any | Required |

### Responses

**200** – Submission list

Content-Type: `application/json`

```json
{
  "items": [
    {}
  ]
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Bad subject identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/form-submissions/by-subject/:subject_type/:subject_id" \
  -H "Accept: application/json"
```

## GET `/forms/public/distributions/{slug}`

Resolve an open-link distribution

Returns the form context served by an open-mode distribution identified by its public slug.

**Tags:** Forms Public Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| slug | path | any | Required |

### Responses

**200** – Distribution form context

Content-Type: `application/json`

```json
{
  "distribution_id": "00000000-0000-4000-8000-000000000000",
  "form": {
    "key": "string",
    "name": "string",
    "defaultLocale": "string",
    "supportedLocales": [
      "string"
    ]
  },
  "schema": {},
  "ui_schema": {},
  "fieldIndex": {},
  "completion": {
    "title": null,
    "message": null
  },
  "redirect_url": null,
  "embed": null,
  "requires_customer_auth": true,
  "default_locale": "string"
}
```

**404** – Distribution or form not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**410** – Distribution closed, not yet open, or response cap reached

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Rate limiting unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/public/distributions/:slug" \
  -H "Accept: application/json"
```

## GET `/forms/public/distributions/{slug}/embed-policy`

Resolve the embed framing policy for a distribution

Returns the Content-Security-Policy frame-ancestors directive authorizing which origins may frame the /embed/:slug host page. Fails closed to frame-ancestors none for non-embeddable or unknown distributions.

**Tags:** Forms Public Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| slug | path | any | Required |

### Responses

**200** – Embed framing policy

Content-Type: `application/json`

```json
{
  "frame_ancestors": "string",
  "embeddable": true
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

**503** – Rate limiting unavailable

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/public/distributions/:slug/embed-policy" \
  -H "Accept: application/json"
```

## GET `/forms/public/embed-loader`

Serve the external embed loader script

Returns a framework-free JS loader that injects an iframe per [data-om-form] element pointing at /embed/:slug and wires postMessage auto-resize.

**Tags:** Forms Public Runtime

### Responses

**200** – The embed loader script (application/javascript)

Content-Type: `application/json`

```json
"string"
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/public/embed-loader" \
  -H "Accept: application/json"
```

## GET `/forms/public/invitations/{token}`

Resolve a personal invitation

Returns the form context for a personal-mode invitation identified by its raw token, marking the invitation opened.

**Tags:** Forms Public Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| token | path | any | Required |

### Responses

**200** – Invitation form context

Content-Type: `application/json`

```json
{
  "distribution_id": "00000000-0000-4000-8000-000000000000",
  "form": {
    "key": "string",
    "name": "string",
    "defaultLocale": "string",
    "supportedLocales": [
      "string"
    ]
  },
  "schema": {},
  "ui_schema": {},
  "fieldIndex": {},
  "completion": {
    "title": null,
    "message": null
  },
  "redirect_url": null,
  "requires_customer_auth": true,
  "default_locale": "string",
  "invitation": {
    "id": "00000000-0000-4000-8000-000000000000",
    "status": "string",
    "locale": null
  }
}
```

**404** – Invitation, distribution, or form not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**410** – Invitation submitted, expired, or revoked; distribution unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Rate limiting unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/public/invitations/:token" \
  -H "Accept: application/json"
```

## POST `/forms/public/start`

Begin an anonymous submission

Resolves a distribution by slug or token and bootstraps an anonymous submission, returning a submission access token.

**Tags:** Forms Public Runtime

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**201** – Submission started

Content-Type: `application/json`

```json
{
  "submission": {},
  "revision": {},
  "decoded_data": {},
  "access_token": "string",
  "expires_at": "string"
}
```

**404** – Distribution, invitation, or form not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Distribution requires customer authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**410** – Distribution / invitation unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Validation failed, CAPTCHA required, or CAPTCHA verification failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – CAPTCHA or rate limiting is unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/public/start" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## GET `/forms/public/submissions/{id}`

Resume a public submission

Returns the role-sliced current state for the submission named in the access token (or customer session).

**Tags:** Forms Public Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Submission state

Content-Type: `application/json`

```json
{
  "submission": {},
  "revision": {},
  "decoded_data": {},
  "actors": [
    {}
  ]
}
```

**401** – Missing or invalid access token / session

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Submission not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Rate limiting unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/public/submissions/:id" \
  -H "Accept: application/json"
```

## PATCH `/forms/public/submissions/{id}`

Autosave a public submission

Appends a revision when base_revision_id matches; re-issues the access token (token-authorized requests) with a slid TTL.

**Tags:** Forms Public Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "base_revision_id": "00000000-0000-4000-8000-000000000000",
  "patch": {}
}
```

### Responses

**200** – Revision saved

Content-Type: `application/json`

```json
{
  "revision": {}
}
```

**401** – Missing or invalid access token / session

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Submission not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Stale base_revision_id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Rate limiting unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/forms/public/submissions/:id" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"base_revision_id\": \"00000000-0000-4000-8000-000000000000\",
  \"patch\": {}
}"
```

## POST `/forms/public/submissions/{id}/attachments`

Upload an attachment (public/anonymous)

Stores an encrypted file for a file-typed field. Authorized via access token or portal session.

**Tags:** Forms Public Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**201** – Attachment stored

Content-Type: `application/json`

```json
{
  "id": "string",
  "filename": "string",
  "contentType": "string",
  "sizeBytes": 1
}
```

**401** – Missing or invalid access token / session

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**413** – File empty or oversize

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Disallowed type / invalid field / scan rejected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Rate limiting unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/public/submissions/:id/attachments" \
  -H "Accept: application/json"
```

## GET `/forms/public/submissions/{id}/attachments/{attachmentId}`

Download a submission attachment (public/anonymous)

Streams the decrypted user-upload bytes for the owning participant. Authorized via access token or portal session.

**Tags:** Forms Public Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| attachmentId | path | any | Required |

### Responses

**200** – Attachment stream

Content-Type: `application/octet-stream`

**401** – Missing or invalid access token / session

Content-Type: `application/json`

**404** – Attachment not found

Content-Type: `application/json`

**429** – Rate limit exceeded

Content-Type: `application/json`

**503** – Rate limiting unavailable

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/public/submissions/:id/attachments/:attachmentId" \
  -H "Accept: application/json"
```

## GET `/forms/public/submissions/{id}/pdf`

Download a submission PDF snapshot (public/anonymous)

Streams the immutable signed-PDF snapshot for the owning participant. Authorized via access token or portal session. Generates the snapshot on first request when missing.

**Tags:** Forms Public Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – PDF stream

Content-Type: `application/pdf`

**401** – Missing or invalid access token / session

Content-Type: `application/json`

**404** – Submission not found

Content-Type: `application/json`

**409** – Submission not yet submitted

Content-Type: `application/json`

**429** – Rate limit exceeded

Content-Type: `application/json`

**503** – Rate limiting unavailable

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/public/submissions/:id/pdf" \
  -H "Accept: application/json"
```

## POST `/forms/public/submissions/{id}/submit`

Submit a public submission

Finalizes the submission named in the access token / session, enforcing the distribution response cap atomically before submitting.

**Tags:** Forms Public Runtime

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "base_revision_id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Submission submitted

Content-Type: `application/json`

```json
{
  "submission": {},
  "redirect_url": null
}
```

**401** – Missing or invalid access token / session

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Submission or invitation not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Stale base_revision_id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**410** – Distribution response cap reached or unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Validation failed or submission in terminal state

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Rate limiting unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/public/submissions/:id/submit" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"base_revision_id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/forms/subjects/{subjectType}/{subjectId}/consents`

List consent records for a data subject

Returns the subject's consent history projected from signed signature fields — active and superseded records. PII-free: clause SHA-256, signed-at, status timestamps, and ids only. Optional `status` and `formId` query filters. Requires `forms.view`.

**Tags:** Forms Compliance

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| subjectType | path | any | Required |
| subjectId | path | any | Required |

### Responses

**200** – Consent records for the subject (newest signed first)

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "formId": "00000000-0000-4000-8000-000000000000",
      "formName": null,
      "formVersionId": "00000000-0000-4000-8000-000000000000",
      "versionNumber": 1,
      "submissionId": "00000000-0000-4000-8000-000000000000",
      "consentFieldKey": "string",
      "clauseSha256": "string",
      "signedAt": "string",
      "status": "string",
      "supersededAt": null,
      "supersededByRecordId": null
    }
  ]
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Tenant scope required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Bad subject identifier or filter

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/subjects/:subjectType/:subjectId/consents" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/forms/subjects/{subjectType}/{subjectId}/export`

Export all submissions for a data subject (GDPR Art. 15/20)

Returns a structured JSON document with every submission for the subject — form key/name, version, status, timestamps, decrypted answers keyed by field with labels, and signature metadata. Attachments are referenced by id. Writes one `export` access-audit row per submission. Requires `forms.submissions.export`.

**Tags:** Forms Compliance

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| subjectType | path | any | Required |
| subjectId | path | any | Required |

### Responses

**200** – Export document (downloaded as an attachment)

Content-Type: `application/json`

```json
{}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Tenant scope required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Bad subject identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/subjects/:subjectType/:subjectId/export" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/forms/submissions/{submissionId}`

Get a submission for the admin inbox

Returns the current submission state for the admin role. Phase 2b will write a form_access_audit row at this call site.

**Tags:** Forms Admin

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| submissionId | path | any | Required |

### Responses

**200** – Submission state

Content-Type: `application/json`

```json
{
  "submission": {},
  "revision": {},
  "decoded_data": {},
  "actors": [
    {}
  ],
  "formVersion": {}
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Submission not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/submissions/:submissionId" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/forms/submissions/{submissionId}/access-audit`

List submission access-audit rows

Returns the access-audit trail for a submission (who/when/purpose/IP). Carries no payload content. Reading the list does not write a new audit row.

**Tags:** Forms Compliance

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| submissionId | path | any | Required |

### Responses

**200** – Access-audit rows

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "accessedBy": "00000000-0000-4000-8000-000000000000",
      "accessedAt": "string",
      "accessPurpose": "view",
      "ip": null,
      "revisionId": null
    }
  ]
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Submission not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/submissions/:submissionId/access-audit" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/forms/submissions/{submissionId}/actors`

Assign an actor to a submission

Assigns a (user, role) pair as an active actor on the submission. The role MUST be declared on the form version.

**Tags:** Forms Admin

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| submissionId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "user_id": "00000000-0000-4000-8000-000000000000",
  "role": "string"
}
```

### Responses

**201** – Actor assigned

Content-Type: `application/json`

```json
{
  "actor": {}
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Submission not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Invalid role for the form version

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/submissions/:submissionId/actors" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"user_id\": \"00000000-0000-4000-8000-000000000000\",
  \"role\": \"string\"
}"
```

## DELETE `/forms/submissions/{submissionId}/actors/{actorId}`

Revoke a submission actor

Revokes the supplied actor row by setting revoked_at.

**Tags:** Forms Admin

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| submissionId | path | any | Required |
| actorId | path | any | Required |

### Responses

**200** – Actor revoked

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Actor not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/forms/submissions/:submissionId/actors/:actorId" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/forms/submissions/{submissionId}/anonymize`

Anonymize a submission (irreversible)

Tombstones every sensitive field across every revision. Requires body `{ "confirm": "DELETE" }`. Preserves actor rows and access-audit rows. Emits `forms.submission.anonymized`.

**Tags:** Forms Compliance

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| submissionId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "confirm": "DELETE"
}
```

### Responses

**200** – Anonymization complete

Content-Type: `application/json`

```json
{
  "submissionId": "00000000-0000-4000-8000-000000000000",
  "revisionsAnonymized": 1,
  "anonymizedAt": "string"
}
```

**404** – Submission not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Missing typed confirmation or other validation failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/submissions/:submissionId/anonymize" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"confirm\": \"DELETE\"
}"
```

## GET `/forms/submissions/{submissionId}/attachments/{attachmentId}`

Download a submission attachment (admin)

Streams the decrypted user-upload bytes. Writes an access-audit row with access_purpose = "export".

**Tags:** Forms Compliance

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| submissionId | path | any | Required |
| attachmentId | path | any | Required |

### Responses

**200** – Attachment stream

Content-Type: `application/octet-stream`

**401** – Not authenticated

Content-Type: `application/json`

**403** – Tenant scope required

Content-Type: `application/json`

**404** – Attachment not found

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/submissions/:submissionId/attachments/:attachmentId" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/forms/submissions/{submissionId}/export`

Export a single submission (GDPR Art. 15/20)

Returns the structured export document for one submission — decrypted answers keyed by field with labels, signature metadata, attachment references. Writes one `export` access-audit row. Requires `forms.submissions.export`.

**Tags:** Forms Compliance

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| submissionId | path | any | Required |

### Responses

**200** – Export document (downloaded as an attachment)

Content-Type: `application/json`

```json
{}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Tenant scope required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Submission not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/submissions/:submissionId/export" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/forms/submissions/{submissionId}/pdf`

Download the PDF snapshot of a submitted form

Streams the immutable PDF snapshot, generating it on first request when the on-submit job has not produced it yet. Never re-renders an existing snapshot. Writes an audit row with `access_purpose = "export"`.

**Tags:** Forms Compliance

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| submissionId | path | any | Required |

### Responses

**200** – PDF stream

Content-Type: `application/pdf`

**404** – Submission not found or not yet submitted

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/submissions/:submissionId/pdf" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/forms/submissions/{submissionId}/reopen`

Reopen a submitted submission

Transitions status from submitted back to reopened so further saves are allowed.

**Tags:** Forms Admin

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| submissionId | path | any | Required |

### Responses

**200** – Submission reopened

Content-Type: `application/json`

```json
{
  "submission": {}
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Submission not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Submission not in submitted state

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/forms/submissions/:submissionId/reopen" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/forms/submissions/{submissionId}/revisions`

List submission revisions

Returns the append-only revision timeline for an admin viewer.

**Tags:** Forms Admin

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| submissionId | path | any | Required |

### Responses

**200** – Revisions

Content-Type: `application/json`

```json
{
  "submission": {},
  "revisions": [
    {}
  ]
}
```

**401** – Not authenticated

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Submission not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/forms/submissions/:submissionId/revisions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/hr_directory/me`

The caller's own employee card

Always includes the extended fields for the caller themselves, plus the contact editing state.

**Tags:** HR directory

**Requires authentication.**

### Responses

**200** – The own card.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "displayName": "string",
  "firstName": null,
  "lastName": null,
  "workEmail": null,
  "branch": null,
  "position": null,
  "department": null,
  "birthday": null,
  "employmentDate": null,
  "isActive": true,
  "extended": {
    "birthYear": null,
    "mobile": null,
    "sex": null,
    "privateEmail": null,
    "linkedin": null,
    "employmentType": null,
    "company": null,
    "superior": null,
    "buddy": null
  },
  "updatedAt": null,
  "editing": {
    "enabled": true,
    "sandbox": true,
    "canEdit": true
  }
}
```

**400** – No single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing hr_directory.view

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – No active employee card is linked to the account (profile_not_found)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_directory/me" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/hr_directory/me/contact`

Update the caller's private email, mobile phone and LinkedIn

Open while hr_records_editing or hr_sandbox_mode is on. Empty values clear a field. Saves through staff.team-members.update and notifies holders of hr_directory.profile_changes.notify about the changed fields (without values).

**Tags:** HR directory

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "privateEmail": null,
  "mobile": null,
  "linkedin": null,
  "updatedAt": "string"
}
```

### Responses

**200** – The own card after the save.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "displayName": "string",
  "firstName": null,
  "lastName": null,
  "workEmail": null,
  "branch": null,
  "position": null,
  "department": null,
  "birthday": null,
  "employmentDate": null,
  "isActive": true,
  "extended": {
    "birthYear": null,
    "mobile": null,
    "sex": null,
    "privateEmail": null,
    "linkedin": null,
    "employmentType": null,
    "company": null,
    "superior": null,
    "buddy": null
  },
  "updatedAt": null,
  "editing": {
    "enabled": true,
    "sandbox": true,
    "canEdit": true
  }
}
```

**400** – Malformed body or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing hr_directory.profile.edit

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – No active employee card is linked to the account (profile_not_found)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – tomHRM is still the master (records_master_tomhrm) or the card changed since it was loaded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Invalid email, phone or LinkedIn URL (validation_failed, fieldErrors)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/hr_directory/me/contact" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"privateEmail\": null,
  \"mobile\": null,
  \"linkedin\": null,
  \"updatedAt\": \"string\"
}"
```

## GET `/hr_directory/people`

List people in the directory

Active people of the selected organization with the basic fields. Callers with staff.view also get `extended` (HR record fields, birth year) and may pass includeInactive=true.

**Tags:** HR directory

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| q | query | any | Optional |
| departmentId | query | any | Optional |
| branch | query | any | Optional |
| includeInactive | query | any | Required |
| sort | query | any | Optional |
| sortDir | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – A page of people.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "displayName": "string",
      "firstName": null,
      "lastName": null,
      "workEmail": null,
      "branch": null,
      "position": null,
      "department": null,
      "birthday": null,
      "employmentDate": null,
      "isActive": true
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "canViewExtended": true,
  "facets": {
    "departments": [
      {
        "id": "00000000-0000-4000-8000-000000000000",
        "name": "string"
      }
    ],
    "branches": [
      "string"
    ]
  }
}
```

**400** – Invalid query or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing hr_directory.view, or includeInactive without staff.view

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_directory/people?sort=lastName&sortDir=asc&page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/hr_directory/people/{id}`

One person in the directory

Basic fields for every caller; `extended` for callers with staff.view.

**Tags:** HR directory

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – The person.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "displayName": "string",
  "firstName": null,
  "lastName": null,
  "workEmail": null,
  "branch": null,
  "position": null,
  "department": null,
  "birthday": null,
  "employmentDate": null,
  "isActive": true,
  "canViewExtended": true
}
```

**400** – No single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing hr_directory.view

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Unknown person, another organization, or inactive and the caller lacks staff.view

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_directory/people/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/hr_org/departments/{id}`

Change parent and/or manager

Allowed only while the tenant toggle hr_org_structure_editing is on. Runs the staff team update command (audited, undoable) after checking cycles, self references, active targets in the same organization and the record version.

**Tags:** HR organisation

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "parentId": null,
  "managerId": null,
  "updatedAt": "string"
}
```

### Responses

**200** – Saved; returns the new record version.

Content-Type: `application/json`

```json
{
  "ok": true,
  "updatedAt": null
}
```

**400** – Invalid payload or no organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing hr_org.manage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not an active department of the organization

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – editing_disabled, or the record changed since it was read

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – cycle, self_reference or inactive_target

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/hr_org/departments/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"parentId\": null,
  \"managerId\": null,
  \"updatedAt\": \"string\"
}"
```

## GET `/hr_org/members/{id}/lines`

Department, manager, superior and company of a team member

**Tags:** HR organisation

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Reporting lines.

Content-Type: `application/json`

```json
{
  "department": null,
  "manager": null,
  "superior": null,
  "company": null,
  "position": null
}
```

**400** – No single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing hr_org.view

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not an active team member of the organization

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_org/members/00000000-0000-4000-8000-000000000000/lines" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/hr_org/members/{id}/structure`

Change department and/or superior

Allowed only while the tenant toggle hr_org_structure_editing is on. Runs the staff update command (audited, undoable) after checking cycles, self references, active targets in the same organization and the record version.

**Tags:** HR organisation

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "departmentId": null,
  "superiorId": null,
  "updatedAt": "string"
}
```

### Responses

**200** – Saved; returns the new record version.

Content-Type: `application/json`

```json
{
  "ok": true,
  "updatedAt": null
}
```

**400** – Invalid payload or no organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing hr_org.manage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not an active team member of the organization

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – editing_disabled, or the record changed since it was read

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – cycle, self_reference or inactive_target

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/hr_org/members/00000000-0000-4000-8000-000000000000/structure" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"departmentId\": null,
  \"superiorId\": null,
  \"updatedAt\": \"string\"
}"
```

## GET `/hr_org/structure`

Departments, people and both reporting lines

Every active department and person of the selected organization with department, department manager, superior, position and company. Versions and structure warnings are returned to holders of hr_org.manage only.

**Tags:** HR organisation

**Requires authentication.**

### Responses

**200** – Organisation structure.

Content-Type: `application/json`

```json
{
  "organization": {
    "id": "00000000-0000-4000-8000-000000000000",
    "name": null
  },
  "departments": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "parentId": null,
      "managerId": null,
      "memberCount": 1,
      "updatedAt": null
    }
  ],
  "people": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "displayName": "string",
      "position": null,
      "company": null,
      "departmentId": null,
      "superiorId": null,
      "managerId": null,
      "managerIsSelf": true,
      "updatedAt": null
    }
  ],
  "editing": {
    "enabled": true,
    "sandbox": true,
    "allowed": true
  },
  "viewer": {
    "canOpenRecords": true
  }
}
```

**400** – No single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing hr_org.view

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_org/structure" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/hr_time_off/company-days`

Delete company day off

Removes a company day off.

**Tags:** Time off

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Company day off deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/hr_time_off/company-days" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/hr_time_off/company-days`

List company days off

Returns a paginated collection of company days off.

**Tags:** Time off

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| id | query | any | Optional |
| from | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated company days off

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "date": "string",
      "label": "string",
      "updated_at": "string"
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_time_off/company-days?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/hr_time_off/company-days`

Create company day off

Adds a company day off. Existing requests are not recounted.

**Tags:** Time off

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "date": "string",
  "label": "string"
}
```

### Responses

**201** – Company day off created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/hr_time_off/company-days" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"date\": \"string\",
  \"label\": \"string\"
}"
```

## PUT `/hr_time_off/company-days`

Update company day off

Changes a company day off.

**Tags:** Time off

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Company day off updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/hr_time_off/company-days" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/hr_time_off/employment-types/options`

Employment types for leave type rules

**Tags:** Time off

**Requires authentication.**

### Responses

**200** – Dictionary values and labels.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "value": "string",
      "label": "string"
    }
  ]
}
```

**400** – Invalid input or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Not allowed for this caller or stage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_time_off/employment-types/options" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/hr_time_off/me/balances`

Balances of the signed-in employee

**Tags:** Time off

**Requires authentication.**

### Responses

**200** – Pools with days, adjustment, used, reserved and left.

Content-Type: `application/json`

```json
{
  "memberId": null,
  "ownership": "tomhrm",
  "sandbox": true,
  "isAdmin": true,
  "pools": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "member": null,
      "type": {
        "id": "00000000-0000-4000-8000-000000000000",
        "name": "string",
        "code": "string"
      },
      "validFrom": "string",
      "validTo": "string",
      "balance": {
        "poolId": "00000000-0000-4000-8000-000000000000",
        "days": 1,
        "adjustment": 1,
        "used": 1,
        "reserved": 1,
        "left": 1
      },
      "adjustmentNote": null,
      "note": null,
      "source": "tomhrm",
      "updatedAt": "string"
    }
  ]
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_time_off/me/balances" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/hr_time_off/people/options`

Active people for pickers

**Tags:** Time off

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| search | query | any | Optional |
| ids | query | any | Optional |

### Responses

**200** – Up to 50 options.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "value": "00000000-0000-4000-8000-000000000000",
      "label": "string"
    }
  ]
}
```

**400** – Invalid input or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Not allowed for this caller or stage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_time_off/people/options" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/hr_time_off/pools`

Delete leave pool

Deletes a pool without requests.

**Tags:** Time off

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Leave pool deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/hr_time_off/pools" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/hr_time_off/pools`

List leave pools

Returns a paginated collection of leave pools.

**Tags:** Time off

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| id | query | any | Optional |
| memberId | query | any | Optional |
| typeId | query | any | Optional |
| validOn | query | any | Optional |
| withAdjustment | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated leave pools

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "member_id": "00000000-0000-4000-8000-000000000000",
      "member_name": null,
      "type_id": "00000000-0000-4000-8000-000000000000",
      "type_name": null,
      "valid_from": "string",
      "valid_to": "string",
      "days": "string",
      "adjustment_days": "string",
      "adjustment_note": null,
      "note": null,
      "source": "string",
      "used": 1,
      "reserved": 1,
      "left": 1,
      "updated_at": "string"
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_time_off/pools?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/hr_time_off/pools`

Create leave pool

Grants days of a type to a person for a period.

**Tags:** Time off

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "memberId": "00000000-0000-4000-8000-000000000000",
  "typeId": "00000000-0000-4000-8000-000000000000",
  "validFrom": "string",
  "validTo": "string",
  "days": 1,
  "adjustmentDays": 0,
  "adjustmentNote": null,
  "note": null
}
```

### Responses

**201** – Leave pool created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/hr_time_off/pools" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"memberId\": \"00000000-0000-4000-8000-000000000000\",
  \"typeId\": \"00000000-0000-4000-8000-000000000000\",
  \"validFrom\": \"string\",
  \"validTo\": \"string\",
  \"days\": 1,
  \"adjustmentDays\": 0,
  \"adjustmentNote\": null,
  \"note\": null
}"
```

## PUT `/hr_time_off/pools`

Update leave pool

Changes a pool; days cannot drop below what its requests use (422 pool_in_use).

**Tags:** Time off

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "adjustmentNote": null,
  "note": null
}
```

### Responses

**200** – Leave pool updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/hr_time_off/pools" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"adjustmentNote\": null,
  \"note\": null
}"
```

## GET `/hr_time_off/reports/monthly`

Approved leave per person and type in one month

Days of a request inside the month are its stored days; a request crossing the month boundary counts the working days of its part in the month.

**Tags:** Time off

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| month | query | any | Required |

### Responses

**200** – One row per person and leave type.

Content-Type: `application/json`

```json
{
  "month": "string",
  "from": "string",
  "to": "string",
  "workingDays": 1,
  "items": [
    {
      "member": {
        "id": "00000000-0000-4000-8000-000000000000",
        "displayName": "string",
        "isActive": true
      },
      "department": null,
      "employmentType": null,
      "type": {
        "id": "00000000-0000-4000-8000-000000000000",
        "name": "string"
      },
      "days": 1,
      "requests": 1,
      "periods": [
        {
          "startDate": "string",
          "endDate": "string",
          "days": 1
        }
      ]
    }
  ]
}
```

**400** – Invalid input or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Not allowed for this caller or stage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_time_off/reports/monthly?month=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/hr_time_off/requests`

List leave requests

scope=mine|to_decide|team|all; `all` needs hr_time_off.admin. Each item carries the actions the caller may take.

**Tags:** Time off

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| scope | query | any | Optional |
| memberId | query | any | Optional |
| typeId | query | any | Optional |
| status | query | any | Optional |
| from | query | any | Optional |
| to | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – A page of requests.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "member": null,
      "type": null,
      "startDate": "string",
      "endDate": "string",
      "days": 1,
      "status": "pending_manager",
      "substitute": null,
      "manager": null,
      "decidedAs": null,
      "managerDecidedAt": null,
      "adminDecidedAt": null,
      "cancelledAt": null,
      "source": "tomhrm",
      "createdAt": "string",
      "updatedAt": "string",
      "note": null,
      "decisionComment": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1
}
```

**400** – Invalid input or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Not allowed for this caller or stage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_time_off/requests?scope=mine&page=1&pageSize=25" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/hr_time_off/requests`

Submit a leave request

For yourself; a leave administrator may pass memberId and enter HR-only types (saved as approved).

**Tags:** Time off

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "typeId": "00000000-0000-4000-8000-000000000000",
  "startDate": "string",
  "endDate": "string",
  "note": null,
  "substituteMemberId": null
}
```

### Responses

**201** – Created.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "pending_manager",
  "days": 1
}
```

**400** – Invalid input or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Not allowed for this caller or stage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Request not found or not visible

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – tomhrm_is_master, invalid_transition or stale version

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Business rule violated (no_working_days, overlap, no_pool, insufficient_balance, type_not_allowed, note_required, invalid_substitute, too_far_in_past)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – ownership_unknown: the ownership flag cannot be read

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/hr_time_off/requests" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"typeId\": \"00000000-0000-4000-8000-000000000000\",
  \"startDate\": \"string\",
  \"endDate\": \"string\",
  \"note\": null,
  \"substituteMemberId\": null
}"
```

## GET `/hr_time_off/requests/{id}`

Get a leave request

Visible to the requester, their manager and superior, and leave administrators; 404 for everyone else.

**Tags:** Time off

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – The request with the note and the actions the caller may take.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "member": null,
  "type": null,
  "startDate": "string",
  "endDate": "string",
  "days": 1,
  "status": "pending_manager",
  "substitute": null,
  "manager": null,
  "decidedAs": null,
  "managerDecidedAt": null,
  "adminDecidedAt": null,
  "cancelledAt": null,
  "source": "tomhrm",
  "createdAt": "string",
  "updatedAt": "string",
  "note": null,
  "decisionComment": null
}
```

**400** – Invalid input or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Not allowed for this caller or stage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Request not found or not visible

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_time_off/requests/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/hr_time_off/requests/{id}`

Edit a pending request (requester only)

**Tags:** Time off

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "note": null,
  "substituteMemberId": null
}
```

### Responses

**200** – Updated; the request returns to its first stage.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "pending_manager",
  "days": 1
}
```

**400** – Invalid input or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Not allowed for this caller or stage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Request not found or not visible

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – tomhrm_is_master, invalid_transition or stale version

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Business rule violated (no_working_days, overlap, no_pool, insufficient_balance, type_not_allowed, note_required, invalid_substitute, too_far_in_past)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – ownership_unknown: the ownership flag cannot be read

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/hr_time_off/requests/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"note\": null,
  \"substituteMemberId\": null
}"
```

## POST `/hr_time_off/requests/{id}/cancel`

Cancel a request

The requester while pending or before the first day; a leave administrator at any time. Days return to the balance.

**Tags:** Time off

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "comment": null
}
```

### Responses

**200** – New status.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "pending_manager",
  "days": 1
}
```

**400** – Invalid input or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Not allowed for this caller or stage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Request not found or not visible

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – tomhrm_is_master, invalid_transition or stale version

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Business rule violated (no_working_days, overlap, no_pool, insufficient_balance, type_not_allowed, note_required, invalid_substitute, too_far_in_past)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – ownership_unknown: the ownership flag cannot be read

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/hr_time_off/requests/00000000-0000-4000-8000-000000000000/cancel" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"comment\": null
}"
```

## POST `/hr_time_off/requests/{id}/decision`

Approve or reject a request

Stage 1: the routed manager or a leave administrator; stage 2: a leave administrator. Nobody decides their own request.

**Tags:** Time off

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "decision": "approve",
  "comment": null
}
```

### Responses

**200** – New status.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "pending_manager",
  "days": 1
}
```

**400** – Invalid input or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Not allowed for this caller or stage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Request not found or not visible

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – tomhrm_is_master, invalid_transition or stale version

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Business rule violated (no_working_days, overlap, no_pool, insufficient_balance, type_not_allowed, note_required, invalid_substitute, too_far_in_past)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – ownership_unknown: the ownership flag cannot be read

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/hr_time_off/requests/00000000-0000-4000-8000-000000000000/decision" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"decision\": \"approve\",
  \"comment\": null
}"
```

## POST `/hr_time_off/requests/{id}/end-early`

Shorten approved leave (HR)

Moves the end date earlier and recounts the days.

**Tags:** Time off

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "endDate": "string"
}
```

### Responses

**200** – New status.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "pending_manager",
  "days": 1
}
```

**400** – Invalid input or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Not allowed for this caller or stage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Request not found or not visible

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – tomhrm_is_master, invalid_transition or stale version

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Business rule violated (no_working_days, overlap, no_pool, insufficient_balance, type_not_allowed, note_required, invalid_substitute, too_far_in_past)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – ownership_unknown: the ownership flag cannot be read

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/hr_time_off/requests/00000000-0000-4000-8000-000000000000/end-early" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"endDate\": \"string\"
}"
```

## GET `/hr_time_off/team`

Balances and upcoming leave of people I manage or lead

**Tags:** Time off

**Requires authentication.**

### Responses

**200** – One row per person.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "member": {
        "id": "00000000-0000-4000-8000-000000000000",
        "displayName": "string"
      },
      "relation": "manager",
      "pools": [
        {
          "id": "00000000-0000-4000-8000-000000000000",
          "member": null,
          "type": {
            "id": "00000000-0000-4000-8000-000000000000",
            "name": "string",
            "code": "string"
          },
          "validFrom": "string",
          "validTo": "string",
          "balance": {
            "poolId": "00000000-0000-4000-8000-000000000000",
            "days": 1,
            "adjustment": 1,
            "used": 1,
            "reserved": 1,
            "left": 1
          },
          "adjustmentNote": null,
          "note": null,
          "source": "tomhrm",
          "updatedAt": "string"
        }
      ],
      "upcoming": [
        {
          "id": "00000000-0000-4000-8000-000000000000",
          "startDate": "string",
          "endDate": "string",
          "days": 1,
          "status": "string",
          "typeName": "string"
        }
      ]
    }
  ]
}
```

**400** – Invalid input or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Not allowed for this caller or stage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_time_off/team" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/hr_time_off/types`

Delete leave type

Deletes an unused leave type (422 type_in_use otherwise).

**Tags:** Time off

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Leave type deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/hr_time_off/types" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/hr_time_off/types`

List leave types

Returns a paginated collection of leave types.

**Tags:** Time off

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| id | query | any | Optional |
| includeInactive | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated leave types

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "code": "string",
      "category": "string",
      "uses_balance": true,
      "hr_only": true,
      "allowed_employment_types": [
        "string"
      ],
      "requires_note": true,
      "is_active": true,
      "source": "string",
      "updated_at": "string"
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_time_off/types?page=1&pageSize=100" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/hr_time_off/types`

Create leave type

Creates a leave type (OM is the master; 409 before cutover).

**Tags:** Time off

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "name": "string",
  "code": "string",
  "category": "holiday",
  "usesBalance": false,
  "hrOnly": false,
  "allowedEmploymentTypes": [],
  "requiresNote": false,
  "isActive": true
}
```

### Responses

**201** – Leave type created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/hr_time_off/types" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\",
  \"code\": \"string\",
  \"category\": \"holiday\",
  \"usesBalance\": false,
  \"hrOnly\": false,
  \"allowedEmploymentTypes\": [],
  \"requiresNote\": false,
  \"isActive\": true
}"
```

## PUT `/hr_time_off/types`

Update leave type

Updates a leave type.

**Tags:** Time off

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Leave type updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/hr_time_off/types" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/hr_time_off/types/options`

Leave type options

**Tags:** Time off

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| memberId | query | any | Optional |

### Responses

**200** – Options for the request form.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "value": "00000000-0000-4000-8000-000000000000",
      "label": "string",
      "usesBalance": true,
      "hrOnly": true,
      "requiresNote": true
    }
  ]
}
```

**400** – Invalid input or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Not allowed for this caller or stage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_time_off/types/options" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/hr_time_off/working-days`

Count working days

Monday–Friday minus Polish public holidays and company days off.

**Tags:** Time off

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| from | query | any | Required |
| to | query | any | Required |

### Responses

**200** – Days and the non-working dates.

Content-Type: `application/json`

```json
{
  "days": 1,
  "nonWorking": [
    {
      "date": "string",
      "reason": "weekend"
    }
  ]
}
```

**400** – Invalid input or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Not allowed for this caller or stage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_time_off/working-days?from=string&to=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/hr_time_tracking/reports/monthly`

Hours per person and project in one month, with working days under 8 hours

Company-wide. Minutes per person and project, and per person the working days (Mon–Fri, not a PL holiday, company day off or approved leave, inside the employment dates, before today) with less than 480 minutes. Without the hr_time_off calendar the check is off (gapCheck: unavailable).

**Tags:** HR time tracking

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| month | query | any | Required |

### Responses

**200** – People with their gap days, and minutes per person and project.

Content-Type: `application/json`

```json
{
  "month": "string",
  "from": "string",
  "to": "string",
  "checkedThrough": null,
  "dailyMinutes": 1,
  "gapCheck": "ok",
  "people": [
    {
      "member": {
        "id": "00000000-0000-4000-8000-000000000000",
        "displayName": "string",
        "isActive": true
      },
      "department": null,
      "employmentType": null,
      "minutes": 1,
      "workingDays": null,
      "gapDays": null
    }
  ],
  "items": [
    {
      "memberId": "00000000-0000-4000-8000-000000000000",
      "project": null,
      "minutes": 1
    }
  ]
}
```

**400** – Invalid month, or no single organization selected

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing hr_time_tracking.reports.view

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/hr_time_tracking/reports/monthly?month=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/integrations`

List integrations

Returns a paginated collection of integrations.

**Tags:** Integrations

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| q | query | any | Optional |
| category | query | any | Optional |
| bundleId | query | any | Optional |
| isEnabled | query | any | Optional |
| healthStatus | query | any | Optional |
| sort | query | any | Optional |
| order | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated integrations

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "title": "string",
      "description": null,
      "category": null,
      "tags": [
        "string"
      ],
      "hub": null,
      "providerKey": null,
      "bundleId": null,
      "author": null,
      "company": null,
      "version": null,
      "hasCredentials": true,
      "isEnabled": true,
      "apiVersion": null,
      "healthStatus": "healthy",
      "lastHealthCheckedAt": null,
      "lastHealthLatencyMs": null,
      "enabledAt": null,
      "analytics": {
        "lastActivityAt": null,
        "totalCount": 1,
        "errorCount": 1,
        "errorRate": 1,
        "dailyCounts": [
          1
        ]
      }
    }
  ],
  "total": 1,
  "totalPages": 1,
  "bundles": [
    {
      "id": "string",
      "title": "string",
      "description": "string",
      "icon": null,
      "integrationCount": 1,
      "enabledCount": 1
    }
  ]
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/integrations?order=asc&page=1&pageSize=100" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/integrations/{id}`

Get integration detail

**Tags:** Integrations

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/integrations/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/integrations/{id}/credentials`

Get or save integration credentials

**Tags:** Integrations

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/integrations/:id/credentials" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/integrations/{id}/credentials`

Get or save integration credentials

**Tags:** Integrations

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/integrations/:id/credentials" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/integrations/{id}/health`

Run health check for an integration

**Tags:** Integrations

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/integrations/:id/health" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/integrations/{id}/state`

Update integration state

**Tags:** Integrations

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/integrations/:id/state" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/integrations/{id}/version`

Change integration API version

**Tags:** Integrations

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/integrations/:id/version" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/integrations/logs`

List integration logs

**Tags:** Integrations

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/integrations/logs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/messages`

List messages

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| folder | query | any | Optional |
| status | query | any | Optional |
| type | query | any | Optional |
| visibility | query | any | Optional |
| sourceEntityType | query | any | Optional |
| sourceEntityId | query | any | Optional |
| externalEmail | query | any | Optional |
| hasObjects | query | any | Optional |
| hasAttachments | query | any | Optional |
| hasActions | query | any | Optional |
| senderId | query | any | Optional |
| search | query | any | Optional |
| since | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – Message list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "type": "string",
      "visibility": null,
      "sourceEntityType": null,
      "sourceEntityId": null,
      "externalEmail": null,
      "externalName": null,
      "subject": "string",
      "bodyPreview": "string",
      "senderUserId": "00000000-0000-4000-8000-000000000000",
      "senderName": null,
      "senderEmail": null,
      "priority": "string",
      "status": "string",
      "hasObjects": true,
      "objectCount": 1,
      "hasAttachments": true,
      "attachmentCount": 1,
      "recipientCount": 1,
      "hasActions": true,
      "actionTaken": null,
      "sentAt": null,
      "readAt": null,
      "threadId": null
    }
  ],
  "page": 1,
  "pageSize": 1,
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/messages?folder=inbox&page=1&pageSize=20" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/messages`

Compose a message

**Tags:** Messages

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "type": "default",
  "visibility": null,
  "recipients": [],
  "subject": "",
  "body": "",
  "bodyFormat": "text",
  "priority": "normal",
  "sendViaEmail": false,
  "isDraft": false
}
```

### Responses

**201** – Message created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "threadId": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/messages" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"type\": \"default\",
  \"visibility\": null,
  \"recipients\": [],
  \"subject\": \"\",
  \"body\": \"\",
  \"bodyFormat\": \"text\",
  \"priority\": \"normal\",
  \"sendViaEmail\": false,
  \"isDraft\": false
}"
```

## DELETE `/messages/{id}`

Delete message for current sender/recipient context

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Message deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Message was modified concurrently (optimistic lock conflict)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/messages/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/messages/{id}`

Get message detail

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Message detail with actor-visible thread items only

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "updatedAt": null,
  "type": "string",
  "isDraft": true,
  "canEditDraft": true,
  "canArchive": true,
  "isArchived": true,
  "visibility": null,
  "sourceEntityType": null,
  "sourceEntityId": null,
  "externalEmail": null,
  "externalName": null,
  "typeDefinition": {
    "labelKey": "string",
    "icon": "string",
    "color": null,
    "allowReply": true,
    "allowForward": true,
    "ui": null
  },
  "threadId": null,
  "parentMessageId": null,
  "senderUserId": "00000000-0000-4000-8000-000000000000",
  "senderName": null,
  "senderEmail": null,
  "subject": "string",
  "body": "string",
  "bodyFormat": "text",
  "priority": "string",
  "sentAt": null,
  "actionData": null,
  "actionTaken": null,
  "actionTakenAt": null,
  "actionTakenByUserId": null,
  "recipients": [
    {
      "userId": "00000000-0000-4000-8000-000000000000",
      "type": "to",
      "status": "string",
      "readAt": null
    }
  ],
  "objects": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "entityModule": "string",
      "entityType": "string",
      "entityId": "00000000-0000-4000-8000-000000000000",
      "actionRequired": true,
      "actionType": null,
      "actionLabel": null,
      "snapshot": null,
      "preview": null
    }
  ],
  "thread": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "senderUserId": "00000000-0000-4000-8000-000000000000",
      "senderName": null,
      "senderEmail": null,
      "externalName": null,
      "externalEmail": null,
      "sourceEntityType": null,
      "body": "string",
      "sentAt": null
    }
  ],
  "isRead": true,
  "conversationArchived": true,
  "conversationAllUnread": true,
  "_channel": null,
  "_channelPayload": null,
  "_channelContact": null
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/messages/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/messages/{id}`

Update draft message

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "visibility": null
}
```

### Responses

**200** – Draft updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Only drafts can be edited, or the draft was modified concurrently (optimistic lock conflict)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/messages/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"visibility\": null
}"
```

## POST `/messages/{id}/actions/{actionId}`

Execute message action

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| actionId | path | any | Required |

### Request Body

Content-Type: `application/json`

No example available for this content type.

### Responses

**200** – Action executed

Content-Type: `application/json`

```json
{
  "ok": true,
  "actionId": "string"
}
```

**403** – Access denied

Content-Type: `application/json`

**404** – Action not found

Content-Type: `application/json`

**409** – Action already taken, or the message was modified concurrently (optimistic lock conflict)

Content-Type: `application/json`

**410** – Action expired

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/messages/:id/actions/:actionId" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/messages/{id}/archive`

Unarchive message

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Message unarchived

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**403** – Access denied or sender-only message cannot be unarchived

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/messages/:id/archive" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/messages/{id}/archive`

Archive message

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Message archived

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**403** – Access denied or sender-only message cannot be archived

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/messages/:id/archive" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/messages/{id}/attachments`

Unlink attachments from draft message

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Attachments unlinked

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Only draft messages can be edited

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/messages/:id/attachments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## GET `/messages/{id}/attachments`

List message attachments

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Attachments

Content-Type: `application/json`

```json
{
  "attachments": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "fileName": "string",
      "fileSize": 1,
      "mimeType": "string",
      "url": "string"
    }
  ]
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/messages/:id/attachments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/messages/{id}/attachments`

Link attachments to draft message

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "attachmentIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

### Responses

**200** – Attachments linked

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Only draft messages can be edited

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/messages/:id/attachments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"attachmentIds\": [
    \"00000000-0000-4000-8000-000000000000\"
  ]
}"
```

## GET `/messages/{id}/confirmation`

Read message confirmation status

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Confirmation status

Content-Type: `application/json`

```json
{
  "messageId": "00000000-0000-4000-8000-000000000000",
  "confirmed": true,
  "confirmedAt": null,
  "confirmedByUserId": null
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/messages/:id/confirmation" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/messages/{id}/conversation`

Delete conversation for current actor

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Conversation deleted

Content-Type: `application/json`

```json
{
  "ok": true,
  "affectedCount": 1
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/messages/:id/conversation" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/messages/{id}/conversation/archive`

Unarchive conversation for current actor

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Conversation unarchived

Content-Type: `application/json`

```json
{
  "ok": true,
  "affectedCount": 1
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/messages/:id/conversation/archive" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/messages/{id}/conversation/archive`

Archive conversation for current actor

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Conversation archived

Content-Type: `application/json`

```json
{
  "ok": true,
  "affectedCount": 1
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/messages/:id/conversation/archive" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/messages/{id}/conversation/read`

Mark entire conversation as unread for current actor

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Conversation marked unread

Content-Type: `application/json`

```json
{
  "ok": true,
  "affectedCount": 1
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/messages/:id/conversation/read" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/messages/{id}/conversation/read`

Mark entire conversation as read for current actor

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Conversation marked read

Content-Type: `application/json`

```json
{
  "ok": true,
  "affectedCount": 1
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/messages/:id/conversation/read" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/messages/{id}/forward`

Forward a message and optionally include attachments from the forwarded conversation slice

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "recipients": [
    {
      "userId": "00000000-0000-4000-8000-000000000000",
      "type": "to"
    }
  ],
  "includeAttachments": true,
  "sendViaEmail": false
}
```

### Responses

**201** – Message forwarded

Content-Type: `application/json`

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

**403** – Access denied

Content-Type: `application/json`

**404** – Message not found

Content-Type: `application/json`

**409** – Forward not allowed for message type

Content-Type: `application/json`

**413** – Forward body exceeds maximum length

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/messages/:id/forward" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"recipients\": [
    {
      \"userId\": \"00000000-0000-4000-8000-000000000000\",
      \"type\": \"to\"
    }
  ],
  \"includeAttachments\": true,
  \"sendViaEmail\": false
}"
```

## GET `/messages/{id}/forward-preview`

Get forward preview for a message

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Forward preview generated

Content-Type: `application/json`

```json
{
  "subject": "string",
  "body": "string"
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**413** – Forward body exceeds maximum length

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/messages/:id/forward-preview" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/messages/{id}/read`

Mark message as unread

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Message marked unread

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/messages/:id/read" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/messages/{id}/read`

Mark message as read

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Message marked read

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/messages/:id/read" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/messages/{id}/reply`

Reply to message

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "body": "string",
  "bodyFormat": "text",
  "replyAll": false,
  "sendViaEmail": false
}
```

### Responses

**201** – Reply created

Content-Type: `application/json`

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

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Message not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – No recipients available for reply

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/messages/:id/reply" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"body\": \"string\",
  \"bodyFormat\": \"text\",
  \"replyAll\": false,
  \"sendViaEmail\": false
}"
```

## GET `/messages/object-types`

List registered message object types for a message type

**Tags:** Messages

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| messageType | query | any | Required |

### Responses

**200** – Message object types

Content-Type: `application/json`

```json
{
  "items": [
    {
      "module": "string",
      "entityType": "string",
      "labelKey": "string",
      "icon": "string",
      "actions": [
        {
          "id": "string",
          "labelKey": "string"
        }
      ]
    }
  ]
}
```

**400** – Invalid query

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/messages/object-types?messageType=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/messages/token/{token}`

Access message via token

**Tags:** Messages

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| token | path | any | Required |

### Responses

**200** – Message detail via token

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "type": "string",
  "subject": "string",
  "body": "string",
  "bodyFormat": "text",
  "priority": "low",
  "senderUserId": "00000000-0000-4000-8000-000000000000",
  "sentAt": null,
  "actionData": null,
  "actionTaken": null,
  "actionTakenAt": null,
  "actionTakenByUserId": null,
  "objects": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "entityModule": "string",
      "entityType": "string",
      "entityId": "00000000-0000-4000-8000-000000000000",
      "actionRequired": true,
      "actionType": null,
      "actionLabel": null,
      "snapshot": null
    }
  ],
  "requiresAuth": true,
  "recipientUserId": "00000000-0000-4000-8000-000000000000"
}
```

**404** – Invalid or expired link

Content-Type: `application/json`

**409** – Token usage exceeded

Content-Type: `application/json`

**410** – Token expired

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/messages/token/:token" \
  -H "Accept: application/json"
```

## GET `/messages/types`

List registered message types

**Tags:** Messages

**Requires authentication.**

### Responses

**200** – Message types

Content-Type: `application/json`

```json
{
  "items": [
    {
      "type": "string",
      "module": "string",
      "labelKey": "string",
      "icon": "string",
      "color": null,
      "allowReply": true,
      "allowForward": true,
      "actionsExpireAfterHours": null,
      "ui": null
    }
  ]
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/messages/types" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/messages/unread-count`

Get unread message count

**Tags:** Messages

**Requires authentication.**

### Responses

**200** – Unread count

Content-Type: `application/json`

```json
{
  "unreadCount": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/messages/unread-count" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/notifications`

List notifications

Returns a paginated collection of notifications.

**Tags:** Notifications

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| status | query | any | Optional |
| type | query | any | Optional |
| severity | query | any | Optional |
| sourceEntityType | query | any | Optional |
| sourceEntityId | query | any | Optional |
| since | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated notifications

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "type": "string",
      "title": "string",
      "body": null,
      "titleKey": null,
      "bodyKey": null,
      "titleVariables": null,
      "bodyVariables": null,
      "icon": null,
      "severity": "string",
      "status": "string",
      "actions": [
        {
          "id": "string",
          "label": "string"
        }
      ],
      "sourceModule": null,
      "sourceEntityType": null,
      "sourceEntityId": null,
      "linkHref": null,
      "createdAt": "string",
      "readAt": null,
      "actionTaken": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

**403** – Request could not be resolved to a tenant scope (code: tenant_scope_required)

Content-Type: `application/json`

```json
{
  "error": "string",
  "code": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/notifications?page=1&pageSize=20" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/notifications`

Create notification

Creates a notification for a user.

**Tags:** Notifications

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "type": "string",
  "severity": "info",
  "recipientUserId": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**201** – Notification created

Content-Type: `application/json`

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

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/notifications" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"type\": \"string\",
  \"severity\": \"info\",
  \"recipientUserId\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## POST `/notifications/{id}/action`

POST /notifications/{id}/action

**Tags:** Notifications

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/notifications/:id/action" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/notifications/{id}/dismiss`

PUT /notifications/{id}/dismiss

**Tags:** Notifications

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/notifications/:id/dismiss" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/notifications/{id}/read`

PUT /notifications/{id}/read

**Tags:** Notifications

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/notifications/:id/read" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/notifications/{id}/restore`

PUT /notifications/{id}/restore

**Tags:** Notifications

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/notifications/:id/restore" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/notifications/admin/preferences`

GET /notifications/admin/preferences

**Tags:** Notifications

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/notifications/admin/preferences" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/notifications/admin/preferences`

PUT /notifications/admin/preferences

**Tags:** Notifications

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/notifications/admin/preferences" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/notifications/batch`

POST /notifications/batch

**Tags:** Notifications

**Requires authentication.**

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/notifications/batch" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/notifications/channels`

GET /notifications/channels

**Tags:** Notifications

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/notifications/channels" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/notifications/feature`

POST /notifications/feature

**Tags:** Notifications

**Requires authentication.**

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/notifications/feature" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/notifications/mark-all-read`

PUT /notifications/mark-all-read

**Tags:** Notifications

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/notifications/mark-all-read" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/notifications/preferences`

GET /notifications/preferences

**Tags:** Notifications

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/notifications/preferences" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/notifications/preferences`

PUT /notifications/preferences

**Tags:** Notifications

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/notifications/preferences" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/notifications/role`

POST /notifications/role

**Tags:** Notifications

**Requires authentication.**

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/notifications/role" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/notifications/settings`

GET /notifications/settings

**Tags:** Notifications

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/notifications/settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/notifications/settings`

POST /notifications/settings

**Tags:** Notifications

**Requires authentication.**

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/notifications/settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/notifications/types`

GET /notifications/types

**Tags:** Notifications

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/notifications/types" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/notifications/types`

PATCH /notifications/types

**Tags:** Notifications

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/notifications/types" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/notifications/types/{id}/channels/{channel}`

DELETE /notifications/types/{id}/channels/{channel}

**Tags:** Notifications

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| channel | path | any | Required |

### Responses

**204** – Success

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/notifications/types/:id/channels/:channel" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/notifications/types/{id}/channels/{channel}`

PUT /notifications/types/{id}/channels/{channel}

**Tags:** Notifications

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| channel | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/notifications/types/:id/channels/:channel" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/notifications/unread-count`

GET /notifications/unread-count

**Tags:** Notifications

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/notifications/unread-count" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/planner/availability`

Delete availability rule

Deletes an availability rule by id.

**Tags:** Planner

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Availability rule deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/planner/availability" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/planner/availability`

List availability rules

Returns a paginated collection of availability rules scoped to the authenticated organization.

**Tags:** Planner

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| subjectType | query | any | Optional |
| subjectIds | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated availability rules

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": null,
      "organization_id": null,
      "tenant_id": null,
      "subject_type": null,
      "subject_id": null,
      "timezone": null,
      "rrule": null,
      "exdates": null,
      "kind": null,
      "note": null,
      "unavailability_reason_entry_id": null,
      "unavailability_reason_value": null,
      "created_at": null,
      "updated_at": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/planner/availability?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/planner/availability`

Create availability rule

Creates an availability rule for the selected subject.

**Tags:** Planner

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "subjectType": "member",
  "subjectId": "00000000-0000-4000-8000-000000000000",
  "timezone": "string",
  "rrule": "string",
  "exdates": [],
  "kind": "availability",
  "note": null,
  "unavailabilityReasonEntryId": null,
  "unavailabilityReasonValue": null
}
```

### Responses

**201** – Availability rule created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/planner/availability" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"subjectType\": \"member\",
  \"subjectId\": \"00000000-0000-4000-8000-000000000000\",
  \"timezone\": \"string\",
  \"rrule\": \"string\",
  \"exdates\": [],
  \"kind\": \"availability\",
  \"note\": null,
  \"unavailabilityReasonEntryId\": null,
  \"unavailabilityReasonValue\": null
}"
```

## PUT `/planner/availability`

Update availability rule

Updates an availability rule by id.

**Tags:** Planner

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "note": null,
  "unavailabilityReasonEntryId": null,
  "unavailabilityReasonValue": null
}
```

### Responses

**200** – Availability rule updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/planner/availability" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"note\": null,
  \"unavailabilityReasonEntryId\": null,
  \"unavailabilityReasonValue\": null
}"
```

## POST `/planner/availability-date-specific`

Replace date-specific availability

Replaces date-specific availability rules for the subject in a single request.

**Tags:** Planner

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "subjectType": "member",
  "subjectId": "00000000-0000-4000-8000-000000000000",
  "timezone": "string",
  "windows": [],
  "isAvailable": true,
  "note": null,
  "unavailabilityReasonEntryId": null,
  "unavailabilityReasonValue": null
}
```

### Responses

**200** – Date-specific availability updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Conflict — the subject/date availability was modified by another edit

Content-Type: `application/json`

```json
{
  "error": "string",
  "code": "string",
  "currentUpdatedAt": "string",
  "expectedUpdatedAt": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/planner/availability-date-specific" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"subjectType\": \"member\",
  \"subjectId\": \"00000000-0000-4000-8000-000000000000\",
  \"timezone\": \"string\",
  \"windows\": [],
  \"isAvailable\": true,
  \"note\": null,
  \"unavailabilityReasonEntryId\": null,
  \"unavailabilityReasonValue\": null
}"
```

## DELETE `/planner/availability-rule-sets`

Delete availability rule set

Deletes an availability rule set by id.

**Tags:** Planner

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Availability rule set deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/planner/availability-rule-sets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/planner/availability-rule-sets`

List availability rule sets

Returns a paginated collection of availability rule sets scoped to the authenticated organization.

**Tags:** Planner

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |
| search | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Paginated availability rule sets

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": null,
      "organization_id": null,
      "tenant_id": null,
      "name": null,
      "description": null,
      "timezone": null,
      "created_at": null,
      "updated_at": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/planner/availability-rule-sets?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/planner/availability-rule-sets`

Create availability rule set

Creates a reusable availability rule set.

**Tags:** Planner

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "name": "string",
  "description": null,
  "timezone": "string"
}
```

### Responses

**201** – Availability rule set created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/planner/availability-rule-sets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"name\": \"string\",
  \"description\": null,
  \"timezone\": \"string\"
}"
```

## PUT `/planner/availability-rule-sets`

Update availability rule set

Updates an availability rule set by id.

**Tags:** Planner

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "description": null
}
```

### Responses

**200** – Availability rule set updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/planner/availability-rule-sets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"description\": null
}"
```

## POST `/planner/availability-weekly`

Replace weekly availability

Replaces weekly availability rules for the subject in a single request.

**Tags:** Planner

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "subjectType": "member",
  "subjectId": "00000000-0000-4000-8000-000000000000",
  "timezone": "string",
  "windows": []
}
```

### Responses

**200** – Weekly availability updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Optimistic lock conflict

Content-Type: `application/json`

```json
{
  "error": "string",
  "code": "optimistic_lock_conflict",
  "currentUpdatedAt": "string",
  "expectedUpdatedAt": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/planner/availability-weekly" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"subjectType\": \"member\",
  \"subjectId\": \"00000000-0000-4000-8000-000000000000\",
  \"timezone\": \"string\",
  \"windows\": []
}"
```

## GET `/progress/active`

GET /progress/active

**Tags:** Progress

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/progress/active" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/progress/jobs`

List progressjobs

Returns a paginated collection of progressjobs scoped to the authenticated tenant.

**Tags:** Progress

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| status | query | any | Optional |
| jobType | query | any | Optional |
| parentJobId | query | any | Optional |
| includeCompleted | query | any | Optional |
| completedSince | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated progressjobs

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "jobType": "string",
      "name": "string",
      "description": null,
      "status": "string",
      "progressPercent": 1,
      "processedCount": 1,
      "totalCount": null,
      "etaSeconds": null,
      "cancellable": true,
      "startedAt": null,
      "finishedAt": null,
      "errorMessage": null,
      "createdAt": null,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/progress/jobs?page=1&pageSize=20" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/progress/jobs`

Create progressjob

Creates a new progress job for tracking a long-running operation.

**Tags:** Progress

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "jobType": "string",
  "name": "string",
  "cancellable": false
}
```

### Responses

**201** – ProgressJob created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/progress/jobs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"jobType\": \"string\",
  \"name\": \"string\",
  \"cancellable\": false
}"
```

## DELETE `/progress/jobs/{id}`

DELETE /progress/jobs/{id}

**Tags:** Progress

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**204** – Success

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/progress/jobs/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/progress/jobs/{id}`

GET /progress/jobs/{id}

**Tags:** Progress

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/progress/jobs/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/progress/jobs/{id}`

PUT /progress/jobs/{id}

**Tags:** Progress

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/progress/jobs/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/query_index/purge`

Purge query index records

Queues a purge job to remove indexed records for an entity type within the active scope.

**Tags:** Query Index

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityType": "string"
}
```

### Responses

**200** – Purge job accepted.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing entity type

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/query_index/purge" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityType\": \"string\"
}"
```

## POST `/query_index/reindex`

Trigger query index rebuild

Queues a reindex job for the specified entity type within the current tenant scope.

**Tags:** Query Index

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entityType": "string"
}
```

### Responses

**200** – Reindex job accepted.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing entity type

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/query_index/reindex" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityType\": \"string\"
}"
```

## GET `/query_index/status`

Inspect query index coverage

Returns entity counts comparing base tables with the query index along with the latest job status.

**Tags:** Query Index

**Requires authentication.**

### Responses

**200** – Current query index status.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "entityId": "string",
      "label": "string",
      "baseCount": null,
      "indexCount": null,
      "vectorCount": null,
      "fulltextCount": null,
      "ok": true,
      "job": {
        "status": "idle",
        "startedAt": null,
        "finishedAt": null,
        "heartbeatAt": null,
        "processedCount": null,
        "totalCount": null,
        "scope": null
      },
      "refreshedAt": null
    }
  ],
  "errors": [
    {
      "id": "string",
      "source": "string",
      "handler": "string",
      "entityType": null,
      "recordId": null,
      "tenantId": null,
      "organizationId": null,
      "message": "string",
      "stack": null,
      "payload": null,
      "occurredAt": "string"
    }
  ],
  "logs": [
    {
      "id": "string",
      "source": "string",
      "handler": "string",
      "level": "info",
      "entityType": null,
      "recordId": null,
      "tenantId": null,
      "organizationId": null,
      "message": "string",
      "details": null,
      "occurredAt": "string"
    }
  ]
}
```

**400** – Tenant or organization context required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Organization access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/query_index/status" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/scheduler/jobs`

Delete scheduledjob

Deletes a scheduled job by ID.

**Tags:** Scheduler

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "string"
}
```

### Responses

**200** – ScheduledJob deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/scheduler/jobs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\"
}"
```

## GET `/scheduler/jobs`

List scheduledjobs

Returns a paginated collection of scheduledjobs scoped to the authenticated organization.

**Tags:** Scheduler

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| id | query | any | Optional |
| search | query | any | Optional |
| scopeType | query | any | Optional |
| isEnabled | query | any | Required |
| sourceType | query | any | Optional |
| sourceModule | query | any | Optional |
| sort | query | any | Optional |
| order | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated scheduledjobs

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "description": null,
      "scopeType": "system",
      "organizationId": null,
      "tenantId": null,
      "scheduleType": "cron",
      "scheduleValue": "string",
      "timezone": "string",
      "targetType": "queue",
      "targetQueue": null,
      "targetCommand": null,
      "targetPayload": null,
      "requireFeature": null,
      "isEnabled": true,
      "lastRunAt": null,
      "nextRunAt": null,
      "sourceType": "user",
      "sourceModule": null,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/scheduler/jobs?page=1&pageSize=20" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/scheduler/jobs`

Create scheduledjob

Creates a new scheduled job with cron or interval-based scheduling.

**Tags:** Scheduler

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "name": "string",
  "description": null,
  "scopeType": "system",
  "organizationId": null,
  "tenantId": null,
  "scheduleType": "cron",
  "scheduleValue": "string",
  "timezone": "UTC",
  "targetType": "queue",
  "targetQueue": null,
  "targetCommand": null,
  "targetPayload": null,
  "requireFeature": null,
  "isEnabled": true
}
```

### Responses

**201** – ScheduledJob created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/scheduler/jobs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\",
  \"description\": null,
  \"scopeType\": \"system\",
  \"organizationId\": null,
  \"tenantId\": null,
  \"scheduleType\": \"cron\",
  \"scheduleValue\": \"string\",
  \"timezone\": \"UTC\",
  \"targetType\": \"queue\",
  \"targetQueue\": null,
  \"targetCommand\": null,
  \"targetPayload\": null,
  \"requireFeature\": null,
  \"isEnabled\": true
}"
```

## PUT `/scheduler/jobs`

Update scheduledjob

Updates an existing scheduled job by ID.

**Tags:** Scheduler

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "string",
  "description": null,
  "targetQueue": null,
  "targetCommand": null,
  "targetPayload": null,
  "requireFeature": null
}
```

### Responses

**200** – ScheduledJob updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/scheduler/jobs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\",
  \"description\": null,
  \"targetQueue\": null,
  \"targetCommand\": null,
  \"targetPayload\": null,
  \"requireFeature\": null
}"
```

## GET `/scheduler/jobs/{id}/executions`

Get execution history for a schedule

Fetch recent executions from BullMQ for a scheduled job. Requires QUEUE_STRATEGY=async.

**Tags:** Scheduler

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| pageSize | query | any | Optional |

### Responses

**200** – Execution history

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "scheduleId": "00000000-0000-4000-8000-000000000000",
      "startedAt": "string",
      "finishedAt": null,
      "status": "running",
      "triggerType": "scheduled",
      "triggeredByUserId": null,
      "errorMessage": null,
      "errorStack": null,
      "durationMs": null,
      "queueJobId": "string",
      "queueName": "string",
      "attemptsMade": 1,
      "result": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1
}
```

**400** – Local strategy not supported

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Schedule not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/scheduler/jobs/:id/executions?pageSize=20" \
  -H "Accept: application/json"
```

## GET `/scheduler/queue-jobs/{jobId}`

Get BullMQ job details and logs

Fetch detailed information and logs for a queue job. Requires QUEUE_STRATEGY=async.

**Tags:** Scheduler

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| jobId | path | any | Required |
| queue | query | any | Required |

### Responses

**200** – Job details and logs

Content-Type: `application/json`

```json
{
  "id": "string",
  "name": "string",
  "state": "waiting",
  "progress": null,
  "returnvalue": null,
  "failedReason": null,
  "stacktrace": null,
  "attemptsMade": 1,
  "processedOn": null,
  "finishedOn": null,
  "logs": [
    "string"
  ]
}
```

**400** – Invalid request or local strategy not supported

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Job not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/scheduler/queue-jobs/:jobId?queue=string" \
  -H "Accept: application/json"
```

## GET `/scheduler/targets`

List available queues and commands

Returns available queue names and scheduler-safe command IDs for schedule target selection.

**Tags:** Scheduler

### Responses

**200** – Available targets

Content-Type: `application/json`

```json
{
  "queues": [
    {
      "value": "string",
      "label": "string"
    }
  ],
  "commands": [
    {
      "value": "string",
      "label": "string"
    }
  ]
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/scheduler/targets" \
  -H "Accept: application/json"
```

## POST `/scheduler/trigger`

Manually trigger a schedule

Executes a scheduled job immediately, bypassing the scheduled time. Only works with async queue strategy. The attempt is audited against the calling user whether it succeeds, is refused, or fails, except when a before command interceptor blocks it before the command runs.

**Tags:** Scheduler

### Request Body

Content-Type: `application/json`

```json
{
  "id": "string"
}
```

### Responses

**200** – Schedule triggered successfully

Content-Type: `application/json`

```json
{
  "ok": true,
  "jobId": "string",
  "message": "string"
}
```

**400** – Invalid request or local strategy not supported

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Schedule not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Trigger deliberately blocked by a before command interceptor. The interceptor chooses the status (any 4xx/5xx) and may replace the body.

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/scheduler/trigger" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\"
}"
```

## GET `/search/embeddings`

Get embeddings configuration

Returns current embedding provider and model configuration.

**Tags:** Search

**Requires authentication.**

### Responses

**200** – Embeddings settings

Content-Type: `application/json`

```json
{
  "settings": {
    "openaiConfigured": true,
    "autoIndexingEnabled": true,
    "autoIndexingLocked": true,
    "lockReason": null,
    "embeddingConfig": null,
    "configuredProviders": [
      "openai"
    ],
    "indexedDimension": null,
    "reindexRequired": true,
    "documentCount": null
  }
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/search/embeddings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/search/embeddings`

Update embeddings configuration

Updates the embedding provider and model settings.

**Tags:** Search

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Updated settings

Content-Type: `application/json`

```json
{
  "settings": {
    "openaiConfigured": true,
    "autoIndexingEnabled": true,
    "autoIndexingLocked": true,
    "lockReason": null,
    "embeddingConfig": null,
    "configuredProviders": [
      "openai"
    ],
    "indexedDimension": null,
    "reindexRequired": true,
    "documentCount": null
  }
}
```

**400** – Invalid request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Auto-indexing disabled via environment

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Update failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Configuration service unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/search/embeddings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/search/embeddings/reindex`

Trigger vector reindex

Starts a vector embedding reindex operation.

**Tags:** Search

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Reindex result

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Reindex already in progress

Content-Type: `application/json`

```json
{
  "error": "string",
  "lock": {
    "type": "fulltext",
    "action": "string",
    "startedAt": "string",
    "elapsedMinutes": 1,
    "processedCount": null,
    "totalCount": null
  }
}
```

**500** – Reindex failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Search indexer unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/search/embeddings/reindex" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/search/embeddings/reindex/cancel`

Cancel vector reindex

Cancels an in-progress vector reindex operation.

**Tags:** Search

**Requires authentication.**

### Responses

**200** – Cancel result

Content-Type: `application/json`

```json
{
  "ok": true,
  "jobsRemoved": 1
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/search/embeddings/reindex/cancel" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/search/index`

Purge vector index

Purges entries from the vector search index. Requires confirmAll=true when purging all entities.

**Tags:** Search

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Optional. Specific entity ID to purge (e.g., "customers:customer_person_profile", "catalog:catalog_product") |
| confirmAll | query | any | Optional. Required when purging all entities |

### Responses

**200** – Purge result

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing confirmAll parameter

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Purge failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Search indexer unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/search/index" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/search/index`

List vector index entries

Returns paginated list of entries in the vector search index.

**Tags:** Search

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Optional. Filter by entity ID (e.g., "customers:customer_person_profile", "catalog:catalog_product") |
| limit | query | any | Optional. Maximum entries to return (default: 50, max: 200) |
| offset | query | any | Optional. Offset for pagination (default: 0) |

### Responses

**200** – Index entries

Content-Type: `application/json`

```json
{
  "entries": [
    {
      "id": "string",
      "entityId": "string",
      "recordId": "string",
      "tenantId": "string",
      "organizationId": null
    }
  ],
  "limit": 1,
  "offset": 1
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to fetch index

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Vector strategy unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/search/index" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/search/reindex`

Trigger fulltext reindex

Starts a fulltext (Meilisearch) reindex operation. Can clear, recreate, or fully reindex.

**Tags:** Search

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Reindex result

Content-Type: `application/json`

```json
{
  "ok": true,
  "action": "clear",
  "entityId": null
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Reindex already in progress

Content-Type: `application/json`

```json
{
  "error": "string",
  "lock": {
    "type": "fulltext",
    "action": "string",
    "startedAt": "string",
    "elapsedMinutes": 1,
    "processedCount": null,
    "totalCount": null
  }
}
```

**500** – Reindex failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Search service unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/search/reindex" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/search/reindex/cancel`

Cancel fulltext reindex

Cancels an in-progress fulltext reindex operation.

**Tags:** Search

**Requires authentication.**

### Responses

**200** – Cancel result

Content-Type: `application/json`

```json
{
  "ok": true,
  "jobsRemoved": 1
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/search/reindex/cancel" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/search/search`

Search across all indexed entities

Performs a search using configured strategies (fulltext, vector, tokens). Use for search playground. Results are limited to the entity types the caller holds the declared view features for; superadmins are exempt.

**Tags:** Search

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| q | query | any | Required. Search query (required) |
| limit | query | any | Optional. Maximum results to return (default: 50, max: 100) |
| strategies | query | any | Optional. Comma-separated strategies to use: fulltext, vector, tokens (e.g., "fulltext,vector") |
| entityTypes | query | any | Optional. Comma-separated entity types to filter results (e.g., "customers:customer_person_profile,catalog:catalog_product,sales:sales_order") |

### Responses

**200** – Search results

Content-Type: `application/json`

```json
{
  "results": [
    {
      "entityId": "string",
      "recordId": "string",
      "score": 1,
      "source": "fulltext"
    }
  ],
  "strategiesUsed": [
    "fulltext"
  ],
  "timing": 1,
  "query": "string",
  "limit": 1
}
```

**400** – Missing query parameter

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Search failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Search service unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/search/search?q=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/search/search/global`

Global search (Cmd+K)

Performs a global search using saved tenant strategies. Does NOT accept strategies from URL.

**Tags:** Search

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| q | query | any | Required. Search query (required) |
| limit | query | any | Optional. Maximum results to return (default: 50, max: 100) |
| entityTypes | query | any | Optional. Comma-separated entity types to filter results (e.g., "customers:customer_person_profile,catalog:catalog_product,sales:sales_order") |

### Responses

**200** – Search results

Content-Type: `application/json`

```json
{
  "results": [
    {
      "entityId": "string",
      "recordId": "string",
      "score": 1,
      "source": "fulltext"
    }
  ],
  "strategiesUsed": [
    "fulltext"
  ],
  "strategiesEnabled": [
    "fulltext"
  ],
  "timing": 1,
  "query": "string",
  "limit": 1
}
```

**400** – Missing query parameter

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Search failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Search service unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/search/search/global?q=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/search/settings`

Get search settings and status

Returns search module configuration, available strategies, and reindex lock status.

**Tags:** Search

**Requires authentication.**

### Responses

**200** – Search settings

Content-Type: `application/json`

```json
{
  "settings": {
    "strategies": [
      {
        "id": "string",
        "name": "string",
        "priority": 1,
        "available": true
      }
    ],
    "fulltextConfigured": true,
    "fulltextStats": null,
    "vectorConfigured": true,
    "tokensEnabled": true,
    "defaultStrategies": [
      "string"
    ],
    "reindexLock": null,
    "fulltextReindexLock": null,
    "vectorReindexLock": null
  }
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/search/settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/search/settings/fulltext`

Get fulltext search configuration

Returns Meilisearch configuration status and index statistics.

**Tags:** Search

**Requires authentication.**

### Responses

**200** – Fulltext settings

Content-Type: `application/json`

```json
{
  "driver": null,
  "configured": true,
  "envVars": {
    "MEILISEARCH_HOST": {
      "set": true,
      "hint": "string"
    },
    "MEILISEARCH_API_KEY": {
      "set": true,
      "hint": "string"
    }
  },
  "optionalEnvVars": {
    "MEILISEARCH_INDEX_PREFIX": {
      "set": true,
      "hint": "string"
    },
    "SEARCH_EXCLUDE_ENCRYPTED_FIELDS": {
      "set": true,
      "hint": "string"
    }
  }
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/search/settings/fulltext" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/search/settings/global-search`

Get global search strategies

Returns the enabled strategies for Cmd+K global search.

**Tags:** Search

**Requires authentication.**

### Responses

**200** – Global search settings

Content-Type: `application/json`

```json
{
  "enabledStrategies": [
    "fulltext"
  ]
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/search/settings/global-search" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/search/settings/global-search`

Update global search strategies

Sets which strategies are enabled for Cmd+K global search.

**Tags:** Search

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "enabledStrategies": [
    "fulltext"
  ]
}
```

### Responses

**200** – Updated settings

Content-Type: `application/json`

```json
{
  "ok": true,
  "enabledStrategies": [
    "fulltext"
  ]
}
```

**400** – Invalid request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/search/settings/global-search" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"enabledStrategies\": [
    \"fulltext\"
  ]
}"
```

## GET `/search/settings/vector-store`

Get vector store configuration

Returns vector store configuration status.

**Tags:** Search

**Requires authentication.**

### Responses

**200** – Vector store settings

Content-Type: `application/json`

```json
{
  "currentDriver": "pgvector",
  "configured": true,
  "drivers": [
    {
      "id": "pgvector",
      "name": "string",
      "configured": true,
      "implemented": true,
      "available": null,
      "unavailableReason": null,
      "envVars": [
        {
          "name": "string",
          "set": true,
          "hint": "string"
        }
      ]
    }
  ]
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/search/settings/vector-store" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/staff/activities`

Delete teammemberactivity

Deletes a team member activity.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – TeamMemberActivity deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/activities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/activities`

List teammemberactivitys

Returns a paginated collection of teammemberactivitys scoped to the authenticated organization.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| entityId | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated teammemberactivitys

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "member_id": null,
      "activity_type": null,
      "subject": null,
      "body": null,
      "occurred_at": null,
      "author_user_id": null,
      "appearance_icon": null,
      "appearance_color": null,
      "organization_id": null,
      "tenant_id": null,
      "created_at": null,
      "updated_at": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/activities?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/activities`

Create teammemberactivity

Adds an activity to a team member timeline.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "entityId": "00000000-0000-4000-8000-000000000000",
  "activityType": "string",
  "appearanceIcon": null,
  "appearanceColor": null
}
```

### Responses

**201** – TeamMemberActivity created

Content-Type: `application/json`

```json
{
  "id": null,
  "authorUserId": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/activities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"entityId\": \"00000000-0000-4000-8000-000000000000\",
  \"activityType\": \"string\",
  \"appearanceIcon\": null,
  \"appearanceColor\": null
}"
```

## PUT `/staff/activities`

Update teammemberactivity

Updates a team member activity.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "appearanceIcon": null,
  "appearanceColor": null
}
```

### Responses

**200** – TeamMemberActivity updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/activities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"appearanceIcon\": null,
  \"appearanceColor\": null
}"
```

## DELETE `/staff/addresses`

Delete teammemberaddress

Deletes a team member address.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – TeamMemberAddress deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/addresses" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/addresses`

List teammemberaddresss

Returns a paginated collection of teammemberaddresss scoped to the authenticated organization.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| entityId | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated teammemberaddresss

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "member_id": null,
      "name": null,
      "purpose": null,
      "company_name": null,
      "address_line1": null,
      "address_line2": null,
      "building_number": null,
      "flat_number": null,
      "city": null,
      "region": null,
      "postal_code": null,
      "country": null,
      "latitude": null,
      "longitude": null,
      "is_primary": null,
      "organization_id": null,
      "tenant_id": null,
      "created_at": null,
      "updated_at": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/addresses?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/addresses`

Create teammemberaddress

Adds a team member address.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "entityId": "00000000-0000-4000-8000-000000000000",
  "addressLine1": "string"
}
```

### Responses

**201** – TeamMemberAddress created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/addresses" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"entityId\": \"00000000-0000-4000-8000-000000000000\",
  \"addressLine1\": \"string\"
}"
```

## PUT `/staff/addresses`

Update teammemberaddress

Updates a team member address.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – TeamMemberAddress updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/addresses" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## DELETE `/staff/comments`

Delete teammembercomment

Deletes a team member note.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – TeamMemberComment deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/comments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/comments`

List teammembercomments

Returns a paginated collection of teammembercomments scoped to the authenticated organization.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| entityId | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated teammembercomments

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "member_id": null,
      "body": null,
      "author_user_id": null,
      "appearance_icon": null,
      "appearance_color": null,
      "organization_id": null,
      "tenant_id": null,
      "created_at": null,
      "updated_at": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/comments?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/comments`

Create teammembercomment

Adds a note to a team member timeline.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "entityId": "00000000-0000-4000-8000-000000000000",
  "body": "string",
  "appearanceIcon": null,
  "appearanceColor": null
}
```

### Responses

**201** – TeamMemberComment created

Content-Type: `application/json`

```json
{
  "id": null,
  "authorUserId": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/comments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"entityId\": \"00000000-0000-4000-8000-000000000000\",
  \"body\": \"string\",
  \"appearanceIcon\": null,
  \"appearanceColor\": null
}"
```

## PUT `/staff/comments`

Update teammembercomment

Updates a team member note.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "appearanceIcon": null,
  "appearanceColor": null
}
```

### Responses

**200** – TeamMemberComment updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/comments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"appearanceIcon\": null,
  \"appearanceColor\": null
}"
```

## DELETE `/staff/job-histories`

Delete teammemberjobhistory

Deletes a team member job history entry.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – TeamMemberJobHistory deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/job-histories" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/job-histories`

List teammemberjobhistorys

Returns a paginated collection of teammemberjobhistorys scoped to the authenticated organization.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| entityId | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated teammemberjobhistorys

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "member_id": null,
      "name": null,
      "company_name": null,
      "description": null,
      "start_date": null,
      "end_date": null,
      "organization_id": null,
      "tenant_id": null,
      "created_at": null,
      "updated_at": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/job-histories?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/job-histories`

Create teammemberjobhistory

Adds a team member job history entry.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "entityId": "00000000-0000-4000-8000-000000000000",
  "name": "string",
  "companyName": null,
  "description": null,
  "startDate": "2025-01-01T00:00:00.000Z",
  "endDate": null
}
```

### Responses

**201** – TeamMemberJobHistory created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/job-histories" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"entityId\": \"00000000-0000-4000-8000-000000000000\",
  \"name\": \"string\",
  \"companyName\": null,
  \"description\": null,
  \"startDate\": \"2025-01-01T00:00:00.000Z\",
  \"endDate\": null
}"
```

## PUT `/staff/job-histories`

Update teammemberjobhistory

Updates a team member job history entry.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "companyName": null,
  "description": null,
  "endDate": null
}
```

### Responses

**200** – TeamMemberJobHistory updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/job-histories" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"companyName\": null,
  \"description\": null,
  \"endDate\": null
}"
```

## DELETE `/staff/leave-requests`

Delete leave request

Deletes a leave request by id.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Leave request deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/leave-requests" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/leave-requests`

List leave requests

Returns a paginated collection of leave requests scoped to the authenticated organization.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| status | query | any | Optional |
| memberId | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Paginated leave requests

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": null,
      "organization_id": null,
      "tenant_id": null,
      "member_id": null,
      "start_date": null,
      "end_date": null,
      "timezone": null,
      "status": null,
      "unavailability_reason_entry_id": null,
      "unavailability_reason_value": null,
      "note": null,
      "decision_comment": null,
      "submitted_by_user_id": null,
      "decided_by_user_id": null,
      "decided_at": null,
      "created_at": null,
      "updated_at": null,
      "member": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/leave-requests?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/leave-requests`

Create leave request

Creates a leave request for a staff member.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "memberId": "00000000-0000-4000-8000-000000000000",
  "timezone": "string",
  "startDate": "2025-01-01T00:00:00.000Z",
  "endDate": "2025-01-01T00:00:00.000Z",
  "unavailabilityReasonEntryId": null,
  "unavailabilityReasonValue": null,
  "note": null,
  "submittedByUserId": null
}
```

### Responses

**201** – Leave request created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/leave-requests" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"memberId\": \"00000000-0000-4000-8000-000000000000\",
  \"timezone\": \"string\",
  \"startDate\": \"2025-01-01T00:00:00.000Z\",
  \"endDate\": \"2025-01-01T00:00:00.000Z\",
  \"unavailabilityReasonEntryId\": null,
  \"unavailabilityReasonValue\": null,
  \"note\": null,
  \"submittedByUserId\": null
}"
```

## PUT `/staff/leave-requests`

Update leave request

Updates a leave request by id.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "unavailabilityReasonEntryId": null,
  "unavailabilityReasonValue": null,
  "note": null
}
```

### Responses

**200** – Leave request updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/leave-requests" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"unavailabilityReasonEntryId\": null,
  \"unavailabilityReasonValue\": null,
  \"note\": null
}"
```

## POST `/staff/leave-requests/accept`

Approve leave request

Approves a leave request and adds unavailability rules for the staff member.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "decisionComment": null,
  "decidedByUserId": null
}
```

### Responses

**200** – Leave request approved

Content-Type: `application/json`

```json
{
  "ok": true,
  "id": null
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/leave-requests/accept" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"decisionComment\": null,
  \"decidedByUserId\": null
}"
```

## POST `/staff/leave-requests/reject`

Reject leave request

Rejects a leave request with an optional comment.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "decisionComment": null,
  "decidedByUserId": null
}
```

### Responses

**200** – Leave request rejected

Content-Type: `application/json`

```json
{
  "ok": true,
  "id": null
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/leave-requests/reject" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"decisionComment\": null,
  \"decidedByUserId\": null
}"
```

## GET `/staff/portal/time-reports`

List the signed-in customer’s closed time reports

Returns the closed, non-deleted `staff_time_reports` rows whose `customer_id` matches the portal session’s own customer entity, inside that session’s tenant and organization. Draft reports are never listed. The response carries hours only — no rate, cost, amount or currency — because `staff.timesheets.rates.view` is a staff feature a portal identity cannot hold.

**Tags:** Staff Portal

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – Closed reports for the signed-in customer

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "reference": "string",
      "title": "string",
      "periodFrom": "string",
      "periodTo": "string",
      "closedAt": null,
      "totalBillableMinutes": 1,
      "totalNonbillableMinutes": 1
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

**401** – Not signed in to the portal

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**403** – The portal account is not linked to a customer

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/portal/time-reports?page=1&pageSize=20" \
  -H "Accept: application/json"
```

## GET `/staff/portal/time-reports/{id}`

Read one closed time report belonging to the signed-in customer

Loads the report by id **together with** the tenant, organization, customer and closed-status predicates, so a report belonging to another customer of the same organization answers 404. Returns the frozen minute totals per project; no rate, cost, amount or currency is present in the response.

**Tags:** Staff Portal

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – The report

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "reference": "string",
  "title": "string",
  "periodFrom": "string",
  "periodTo": "string",
  "closedAt": null,
  "totalBillableMinutes": 1,
  "totalNonbillableMinutes": 1,
  "projects": [
    {
      "timeProjectId": null,
      "projectName": null,
      "billableMinutes": 1,
      "nonbillableMinutes": 1
    }
  ]
}
```

**401** – Not signed in to the portal

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**403** – The portal account is not linked to a customer

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**404** – No closed report with that id belongs to this customer

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/portal/time-reports/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json"
```

## DELETE `/staff/team-members`

Delete team member

Deletes a team member by id.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Team member deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/team-members" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/team-members`

List team members

Returns a paginated collection of team members scoped to the authenticated organization.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| isActive | query | any | Optional |
| teamId | query | any | Optional |
| roleId | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Paginated team members

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": null,
      "organization_id": null,
      "tenant_id": null,
      "team_id": null,
      "display_name": null,
      "description": null,
      "user_id": null,
      "availability_rule_set_id": null,
      "is_active": null,
      "created_at": null,
      "updated_at": null,
      "user": null,
      "team": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/team-members?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/team-members`

Create team member

Creates a team member for staff assignments.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "teamId": null,
  "displayName": "string",
  "description": null,
  "userId": null,
  "roleIds": [],
  "tags": [],
  "availabilityRuleSetId": null
}
```

### Responses

**201** – Team member created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/team-members" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"teamId\": null,
  \"displayName\": \"string\",
  \"description\": null,
  \"userId\": null,
  \"roleIds\": [],
  \"tags\": [],
  \"availabilityRuleSetId\": null
}"
```

## PUT `/staff/team-members`

Update team member

Updates a team member by id.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "teamId": null,
  "description": null,
  "userId": null,
  "availabilityRuleSetId": null
}
```

### Responses

**200** – Team member updated

Content-Type: `application/json`

```json
{
  "ok": true,
  "updatedAt": null
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/team-members" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"teamId\": null,
  \"description\": null,
  \"userId\": null,
  \"availabilityRuleSetId\": null
}"
```

## GET `/staff/team-members/assignable`

List staff members that can be assigned from customer flows

Returns active staff members linked to auth users. Access requires either customers.roles.manage or customers.activities.manage. Owned by the staff module; consumed from customer flows via this canonical URL. Replaces the deprecated /api/customers/assignable-staff route.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |

### Responses

**200** – Assignable staff members

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "teamMemberId": "00000000-0000-4000-8000-000000000000",
      "userId": "00000000-0000-4000-8000-000000000000",
      "displayName": "string",
      "email": null,
      "teamName": null,
      "user": null,
      "team": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/team-members/assignable?page=1&pageSize=24" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/staff/team-members/self`

Get current user team member profile

Returns the staff team member linked to the current user, if any.

**Tags:** Staff

**Requires authentication.**

### Responses

**200** – Team member profile

Content-Type: `application/json`

```json
{
  "member": null
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/team-members/self" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/team-members/self`

Create current user team member profile

Creates a team member profile for the signed-in user.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "displayName": "string",
  "description": null
}
```

### Responses

**201** – Team member created

Content-Type: `application/json`

```json
{
  "id": null
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Already exists

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/team-members/self" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"displayName\": \"string\",
  \"description\": null
}"
```

## POST `/staff/team-members/tags/assign`

Assign team member tag

Assigns a tag to a staff team member.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "memberId": "00000000-0000-4000-8000-000000000000",
  "tag": "string"
}
```

### Responses

**201** – Tag assignment created

Content-Type: `application/json`

```json
{
  "id": null
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/team-members/tags/assign" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"memberId\": \"00000000-0000-4000-8000-000000000000\",
  \"tag\": \"string\"
}"
```

## POST `/staff/team-members/tags/unassign`

Unassign team member tag

Removes a tag from a staff team member.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "memberId": "00000000-0000-4000-8000-000000000000",
  "tag": "string"
}
```

### Responses

**200** – Tag assignment removed

Content-Type: `application/json`

```json
{
  "id": null
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/team-members/tags/unassign" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"memberId\": \"00000000-0000-4000-8000-000000000000\",
  \"tag\": \"string\"
}"
```

## DELETE `/staff/team-roles`

Delete team role

Deletes a team role by id.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Team role deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/team-roles" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/team-roles`

List team roles

Returns a paginated collection of team roles scoped to the authenticated organization.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |
| teamId | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Paginated team roles

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": null,
      "organization_id": null,
      "tenant_id": null,
      "team_id": null,
      "name": null,
      "description": null,
      "appearance_icon": null,
      "appearance_color": null,
      "created_at": null,
      "updated_at": null,
      "team": null,
      "memberCount": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/team-roles?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/team-roles`

Create team role

Creates a team role for staff team members.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "teamId": null,
  "name": "string",
  "description": null,
  "appearanceIcon": null,
  "appearanceColor": null
}
```

### Responses

**201** – Team role created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/team-roles" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"teamId\": null,
  \"name\": \"string\",
  \"description\": null,
  \"appearanceIcon\": null,
  \"appearanceColor\": null
}"
```

## PUT `/staff/team-roles`

Update team role

Updates a team role by id.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "teamId": null,
  "description": null,
  "appearanceIcon": null,
  "appearanceColor": null
}
```

### Responses

**200** – Team role updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/team-roles" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"teamId\": null,
  \"description\": null,
  \"appearanceIcon\": null,
  \"appearanceColor\": null
}"
```

## DELETE `/staff/teams`

Delete team

Deletes a staff team by id.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Team deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/teams" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/teams`

List teams

Returns a paginated collection of teams scoped to the authenticated organization.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |
| isActive | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Paginated teams

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": null,
      "organization_id": null,
      "tenant_id": null,
      "name": null,
      "description": null,
      "is_active": null,
      "created_at": null,
      "updated_at": null,
      "memberCount": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/teams?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/teams`

Create team

Creates a staff team.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "name": "string",
  "description": null
}
```

### Responses

**201** – Team created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/teams" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"name\": \"string\",
  \"description\": null
}"
```

## PUT `/staff/teams`

Update team

Updates a staff team by id.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "description": null
}
```

### Responses

**200** – Team updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/teams" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"description\": null
}"
```

## POST `/staff/timesheets/access-requests`

Request time tracking project access

Notifies every holder of staff.timesheets.projects.manage in the caller scope that a team member asked for project access. Omitting timeProjectId sends a general request from the no-assignments empty state.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "timeProjectId": null
}
```

### Responses

**200** – Access request accepted (deduplicated when an identical request is still pending)

Content-Type: `application/json`

```json
{
  "ok": true,
  "deduplicated": true
}
```

**400** – Invalid request body or missing organization scope

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.view

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/access-requests" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"timeProjectId\": null
}"
```

## GET `/staff/timesheets/my-projects`

List assigned time project memberships for the current user

Returns staff_time_project_members where the authenticated user is an active member. Used by the My Timesheets grid to resolve assigned project IDs (spec N+1 mitigation query 1).

**Tags:** Staff

**Requires authentication.**

### Responses

**200** – Assigned project memberships

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "time_project_id": "00000000-0000-4000-8000-000000000000",
      "staff_member_id": "00000000-0000-4000-8000-000000000000",
      "role": null,
      "status": null,
      "assigned_start_date": null,
      "assigned_end_date": null,
      "show_in_grid": true
    }
  ],
  "total": 1
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – No staff member linked

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/my-projects" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/staff/timesheets/my-projects/{projectId}`

Toggle grid visibility for one of the caller's assigned time projects

Self-service endpoint. Only the authenticated user can toggle `show_in_grid` on their own active membership for the given project. Does not require the admin-only `staff.timesheets.projects.manage` feature.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| projectId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "showInGrid": true
}
```

### Responses

**200** – Visibility updated

Content-Type: `application/json`

```json
{
  "ok": true,
  "showInGrid": true
}
```

**400** – Invalid input

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – No staff member linked

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Project membership not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/staff/timesheets/my-projects/:projectId" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"showInGrid\": true
}"
```

## GET `/staff/timesheets/my-work`

Today, this week and this month for the calling staff member

One aggregate for the time tracking landing page: day/week/month/non-billable totals with working-day targets, today’s entries, the caller’s projects with budget burn, and the tasks they last logged against. Always scoped to the caller — there is no way to ask about another person. Rate and amount-budget fields are present only for holders of `staff.timesheets.rates.view`.

**Tags:** Staff

**Requires authentication.**

### Responses

**200** – My work summary

Content-Type: `application/json`

```json
{
  "staffMember": null,
  "today": "string",
  "kpis": {
    "todayMinutes": 1,
    "weekMinutes": 1,
    "monthMinutes": 1,
    "monthNonBillableMinutes": 1,
    "dailyTargetMinutes": null,
    "weekTargetMinutes": null,
    "monthTargetMinutes": null,
    "weekWorkingDays": 1,
    "monthWorkingDays": 1,
    "nonBillableSharePercent": null
  },
  "entries": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "date": "string",
      "taskId": null,
      "taskTitle": null,
      "timeProjectId": null,
      "projectName": null,
      "description": null,
      "startedAt": null,
      "endedAt": null,
      "durationMinutes": 1,
      "isBillable": true,
      "isLocked": true
    }
  ],
  "projects": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "code": null,
      "color": null,
      "customerName": null,
      "myMinutes": 1,
      "totalMinutes": 1,
      "hourlyRate": null,
      "currencyCode": null,
      "budget": null
    }
  ],
  "recentTasks": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "title": "string",
      "timeProjectId": "00000000-0000-4000-8000-000000000000",
      "projectName": null
    }
  ]
}
```

**400** – Missing scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Aggregation failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/my-work" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/staff/timesheets/projects/kpis`

Aggregate KPIs for the timesheets Projects page

Returns a role-aware KPI payload. Users with `staff.timesheets.projects.manage` get the PM shape (portfolio totals, monthly team hours, active team member count). Other viewers get the Collaborator shape scoped to their own memberships and hours.

**Tags:** Staff

**Requires authentication.**

### Responses

**200** – PM or Collaborator KPIs

Content-Type: `application/json`

```json
{
  "role": "pm",
  "totals": {
    "total": 1,
    "active": 1,
    "onHold": 1,
    "completed": 1
  },
  "hoursWeek": {
    "current": 1,
    "previous": 1,
    "deltaPct": null
  },
  "hoursMonth": {
    "current": 1,
    "previous": 1,
    "deltaPct": null
  },
  "teamActive": {
    "count": 1
  },
  "assignedToMe": {
    "total": 1,
    "active": 1
  }
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing viewing feature or no staff member linked

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Aggregation failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/projects/kpis" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/staff/timesheets/reports`

Delete timereport

Soft-deletes a draft report. A closed report is refused with 409 report_closed, because deleting it would strand the entries it froze.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – TimeReport deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/timesheets/reports" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/timesheets/reports`

List time reports

Returns a paginated collection of time reports scoped to the authenticated organization.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| q | query | any | Optional |
| id | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |
| customerId | query | any | Optional |
| status | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Paginated time reports

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": null,
      "organization_id": null,
      "tenant_id": null,
      "customer_id": null,
      "customer_snapshot": null,
      "reference": null,
      "title": null,
      "period_kind": null,
      "period_from": null,
      "period_to": null,
      "currency_code": null,
      "grouping": null,
      "nonbillable_mode": null,
      "include_already_reported": null,
      "show_rates": null,
      "rounding_unit_minutes": null,
      "rounding_direction": null,
      "status": null,
      "total_billable_minutes": null,
      "total_nonbillable_minutes": null,
      "total_amount": null,
      "closed_at": null,
      "closed_by_user_id": null,
      "created_by_user_id": null,
      "created_at": null,
      "updated_at": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/reports?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/timesheets/reports`

Create timereport

Creates a draft customer report and allocates its RAP-<year>-<seq> reference. Every selected project must belong to `customerId` and the projects must agree on a currency; a mismatch is refused with 422 report_currency_conflict naming the offenders.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "customerId": "00000000-0000-4000-8000-000000000000",
  "title": "string",
  "periodKind": "week",
  "periodFrom": "2025-01-01T00:00:00.000Z",
  "periodTo": "2025-01-01T00:00:00.000Z",
  "grouping": "project_task",
  "nonbillableMode": "separate",
  "includeAlreadyReported": false,
  "showRates": true,
  "timeProjectIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

### Responses

**201** – TimeReport created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/reports" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"customerId\": \"00000000-0000-4000-8000-000000000000\",
  \"title\": \"string\",
  \"periodKind\": \"week\",
  \"periodFrom\": \"2025-01-01T00:00:00.000Z\",
  \"periodTo\": \"2025-01-01T00:00:00.000Z\",
  \"grouping\": \"project_task\",
  \"nonbillableMode\": \"separate\",
  \"includeAlreadyReported\": false,
  \"showRates\": true,
  \"timeProjectIds\": [
    \"00000000-0000-4000-8000-000000000000\"
  ]
}"
```

## PUT `/staff/timesheets/reports`

Update timereport

Updates a draft report. A closed report is refused with 409 report_closed — unlock it first. Replacing `timeProjectIds` re-asserts the single-customer and single-currency rules.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – TimeReport updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/timesheets/reports" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## POST `/staff/timesheets/reports/{id}/close`

Close and lock a customer report

Freezes the report: writes one staff_time_report_entries snapshot per covered entry (raw minutes, rounded minutes, rate, currency, amount, billable), stamps locked_report_id / locked_at on entries that are not already locked by an earlier report, freezes the report totals and appends a `closed` event — all in one transaction. Exporting never locks; this endpoint is the only thing that does.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Report closed

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "closed",
  "lockedEntryCount": 1,
  "totalAmount": 1,
  "totalBillableMinutes": 1,
  "totalNonbillableMinutes": 1
}
```

**400** – Missing report id or scope

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.lock, or refused by a registered report approval policy

Content-Type: `application/json`

**404** – Report not found or not accessible

Content-Type: `application/json`

**409** – Report is already closed (report_closed)

Content-Type: `application/json`

**422** – Report covers no entries (report_empty)

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/reports/:id/close" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/staff/timesheets/reports/{id}/export`

Export a customer report

Renders the report as `pdf` (mirrors the on-screen sheet, client-facing), `csv` or `xlsx` (raw accounting columns: date, project, task, person, description, raw and rounded minutes, billable, rate, amount). Honours the report grouping — overridable per request with `?grouping=` — plus its filters, rounding and currency. Appends an `exported` report event; exporting never locks entries. Rates and amounts are omitted for a caller without staff.timesheets.rates.view.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| format | query | any | Required |
| grouping | query | any | Optional |

### Responses

**200** – The rendered report file

Content-Type: `application/json`

**400** – Unsupported format

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.reports.view

Content-Type: `application/json`

**404** – Report not found or not accessible

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/reports/:id/export?format=pdf" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/staff/timesheets/reports/{id}/sheet`

Read a customer report sheet

Returns the report header, the grouped sheet, the raw entry rows and the close/unlock/export history. A draft is computed live; a closed report is rebuilt from its own frozen snapshots so a later rounding or rate change cannot restate it. `?grouping=project_task|project_person|project_day` re-groups the same numbers without persisting the choice — the grand total is invariant under it (D-7). Amounts are omitted for a caller without staff.timesheets.rates.view.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Report sheet

Content-Type: `application/json`

```json
{
  "report": {
    "id": "00000000-0000-4000-8000-000000000000",
    "reference": "string",
    "title": "string",
    "status": "draft",
    "currencyCode": null,
    "periodFrom": null,
    "periodTo": null,
    "showRates": true
  },
  "totals": {
    "entryCount": 1,
    "billableMinutes": 1,
    "nonbillableMinutes": 1,
    "totalAmount": null
  },
  "rowCount": 1,
  "rowsTruncated": true
}
```

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.reports.view

Content-Type: `application/json`

**404** – Report not found or not accessible

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/reports/:id/sheet" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/timesheets/reports/{id}/unlock`

Unlock a closed customer report

Removes the freeze records of this report so a previously billed hour stops being counted as already reported by it, returns the report to draft and appends an `unlocked` event carrying the reason, the actor and the totals that were frozen. Clears locked_report_id / locked_at on every entry this report locked that no other closed report quotes; an entry another closed report re-included stays locked, and its lock moves to that report. `unlockedEntryCount` counts the entries actually freed. The reason is mandatory. Requires staff.timesheets.reports.unlock, which is deliberately separate from staff.timesheets.lock.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "reason": "string"
}
```

### Responses

**200** – Report unlocked

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "draft",
  "unlockedEntryCount": 1
}
```

**400** – Missing or empty reason

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.reports.unlock, or refused by a registered report approval policy

Content-Type: `application/json`

**404** – Report not found or not accessible

Content-Type: `application/json`

**409** – Report is not closed (report_not_closed)

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/reports/:id/unlock" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"reason\": \"string\"
}"
```

## POST `/staff/timesheets/reports/preview`

Preview a customer report

Computes report totals for a project selection and period WITHOUT persisting anything. Powers the live per-project numbers and the range summary of the report config screen. Money is rounded at the entry and summed upward (D-7), so the grand total does not change when the grouping changes. Entries already frozen in a closed report are excluded unless `includeAlreadyReported` is set, and are reported as `alreadyReportedCount` / `alreadyReportedMinutes` / `alreadyReportedIn` either way (D-5). Amounts and rates are omitted for a caller without staff.timesheets.rates.view.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "customerId": null,
  "periodKind": "custom",
  "periodFrom": "2025-01-01T00:00:00.000Z",
  "periodTo": "2025-01-01T00:00:00.000Z",
  "grouping": "project_task",
  "nonbillableMode": "separate",
  "includeAlreadyReported": false,
  "showRates": true,
  "timeProjectIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

### Responses

**200** – Computed totals

Content-Type: `application/json`

```json
{
  "currencyCode": null,
  "grouping": "string",
  "nonbillableMode": "separate",
  "includeAlreadyReported": true,
  "showRates": true,
  "projects": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "hourlyRate": null,
      "currencyCode": null,
      "entryCount": 1,
      "billableMinutes": 1,
      "nonbillableMinutes": 1,
      "amount": null
    }
  ],
  "groups": [
    {
      "key": "string",
      "kind": "project",
      "label": "string",
      "rate": null,
      "minutes": 1,
      "amount": 1,
      "entryCount": 1,
      "lines": [
        {
          "key": "string",
          "label": "string",
          "minutes": 1,
          "rate": null,
          "amount": 1,
          "entryCount": 1,
          "hasOverride": true,
          "children": []
        }
      ]
    }
  ],
  "totals": {
    "entryCount": 1,
    "billableMinutes": 1,
    "nonbillableMinutes": 1,
    "totalAmount": null
  },
  "alreadyReportedCount": 1,
  "alreadyReportedMinutes": 1,
  "alreadyReportedIn": [
    {
      "reportId": "00000000-0000-4000-8000-000000000000",
      "reference": null,
      "title": null,
      "entryCount": 1,
      "minutes": 1
    }
  ],
  "rounding": {
    "unitMinutes": 1,
    "direction": "string"
  }
}
```

**400** – Invalid body or missing scope

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.reports.view

Content-Type: `application/json`

**422** – Selected projects disagree on a currency, or one is not accessible

Content-Type: `application/json`

```json
{
  "code": "report_currency_conflict",
  "error": "string",
  "currencies": [
    "string"
  ],
  "offenders": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "currencyCode": null
    }
  ]
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/reports/preview" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"customerId\": null,
  \"periodKind\": \"custom\",
  \"periodFrom\": \"2025-01-01T00:00:00.000Z\",
  \"periodTo\": \"2025-01-01T00:00:00.000Z\",
  \"grouping\": \"project_task\",
  \"nonbillableMode\": \"separate\",
  \"includeAlreadyReported\": false,
  \"showRates\": true,
  \"timeProjectIds\": [
    \"00000000-0000-4000-8000-000000000000\"
  ]
}"
```

## GET `/staff/timesheets/settings`

Read time tracking settings

**Tags:** Staff

**Requires authentication.**

### Responses

**200** – Tenant time tracking settings

Content-Type: `application/json`

```json
{
  "rounding": {
    "unitMinutes": 0,
    "direction": "up"
  },
  "defaults": {
    "billable": true,
    "chainStartFromPreviousEnd": true
  },
  "targets": {
    "dailyHours": 8
  },
  "warnings": {
    "overlap": true,
    "runningTimer": true
  },
  "access": {
    "assignmentGraceDays": 14
  }
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/staff/timesheets/settings`

Update time tracking settings

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "rounding": {
    "unitMinutes": 0,
    "direction": "up"
  },
  "defaults": {
    "billable": true,
    "chainStartFromPreviousEnd": true
  },
  "targets": {
    "dailyHours": 8
  },
  "warnings": {
    "overlap": true,
    "runningTimer": true
  },
  "access": {
    "assignmentGraceDays": 14
  }
}
```

### Responses

**200** – Updated time tracking settings

Content-Type: `application/json`

```json
{
  "rounding": {
    "unitMinutes": 0,
    "direction": "up"
  },
  "defaults": {
    "billable": true,
    "chainStartFromPreviousEnd": true
  },
  "targets": {
    "dailyHours": 8
  },
  "warnings": {
    "overlap": true,
    "runningTimer": true
  },
  "access": {
    "assignmentGraceDays": 14
  }
}
```

**400** – Invalid request body

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.settings.manage

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/timesheets/settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"rounding\": {
    \"unitMinutes\": 0,
    \"direction\": \"up\"
  },
  \"defaults\": {
    \"billable\": true,
    \"chainStartFromPreviousEnd\": true
  },
  \"targets\": {
    \"dailyHours\": 8
  },
  \"warnings\": {
    \"overlap\": true,
    \"runningTimer\": true
  },
  \"access\": {
    \"assignmentGraceDays\": 14
  }
}"
```

## POST `/staff/timesheets/settings/reapply-rounding`

Queue a retroactive rounding job

Enqueues a `ProgressJob` that recomputes `rounded_minutes` on existing time entries under the tenant’s current rounding rule. Entries locked into a closed report are excluded — a billed amount is never restated. Returns `202` with `progressJobId`, or `200` with a null job id when there is nothing to restate.

**Tags:** Staff

**Requires authentication.**

### Responses

**200** – Nothing to restate

Content-Type: `application/json`

```json
{
  "ok": true,
  "progressJobId": null,
  "candidateCount": 1
}
```

**202** – Job queued

Content-Type: `application/json`

```json
{
  "ok": true,
  "progressJobId": null,
  "candidateCount": 1
}
```

**400** – Missing scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing staff.timesheets.settings.manage

Content-Type: `application/json`

**500** – Queueing failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/settings/reapply-rounding" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/staff/timesheets/settings/rounding-impact`

Project the billing impact of a rounding rule over a recent window

Answers what the last `windowDays` (default 90) would have billed under a candidate rounding rule, before it is saved. Entries already locked into a closed report are excluded from the projection — they cannot be restated — and reported separately as `lockedEntryCount`. Rounding is applied per entry (D-7) through the same helper the write path uses.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| unitMinutes | query | any | Optional |
| direction | query | any | Optional |
| windowDays | query | any | Optional |

### Responses

**200** – Rounding impact projection

Content-Type: `application/json`

```json
{
  "windowDays": 1,
  "from": "string",
  "to": "string",
  "rounding": {
    "unitMinutes": 0,
    "direction": "up"
  },
  "projected": {
    "entryCount": 1,
    "rawMinutes": 1,
    "roundedMinutes": 1,
    "deltaMinutes": 1
  },
  "current": {
    "entryCount": 1,
    "rawMinutes": 1,
    "roundedMinutes": 1,
    "deltaMinutes": 1
  },
  "lockedEntryCount": 1
}
```

**400** – Invalid request or missing scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing staff.timesheets.settings.manage

Content-Type: `application/json`

**500** – Projection failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/settings/rounding-impact" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/staff/timesheets/tags`

Delete timetag

Soft-deletes a tag and removes every task and time entry assignment it had, reporting how many of each were removed.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – TimeTag deleted

Content-Type: `application/json`

```json
{
  "ok": true,
  "removedTaskAssignments": 1,
  "removedEntryAssignments": 1
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/timesheets/tags" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/timesheets/tags`

List time tags

Returns a paginated collection of time tags scoped to the authenticated organization.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| q | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Paginated time tags

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": null,
      "organization_id": null,
      "tenant_id": null,
      "slug": null,
      "label": null,
      "color": null,
      "created_at": null,
      "updated_at": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/tags?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/timesheets/tags`

Create timetag

Creates a time tracking tag. The slug is unique per organization and tenant; a collision answers 409 with fieldErrors.slug.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "slug": "string",
  "label": "string",
  "color": null
}
```

### Responses

**201** – TimeTag created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/tags" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"slug\": \"string\",
  \"label\": \"string\",
  \"color\": null
}"
```

## PUT `/staff/timesheets/tags`

Update timetag

Renames, re-slugs or recolours a tag. A slug collision answers 409 with fieldErrors.slug.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "color": null
}
```

### Responses

**200** – TimeTag updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/timesheets/tags" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"color\": null
}"
```

## DELETE `/staff/timesheets/tags/entry-assignments`

Unassign tags from a time entry

Removes tags from a time entry. Idempotent — a tag the entry does not carry comes back in notAssignedTagIds.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "timeEntryId": "00000000-0000-4000-8000-000000000000",
  "tagIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

### Responses

**200** – Tags unassigned

Content-Type: `application/json`

```json
{
  "timeEntryId": "00000000-0000-4000-8000-000000000000",
  "tagIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "removedTagIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "notAssignedTagIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

**400** – Invalid payload or missing organization scope

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.tasks.manage

Content-Type: `application/json`

**404** – Time entry not found or not accessible

Content-Type: `application/json`

**409** – Time entry locked in a closed report

Content-Type: `application/json`

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/timesheets/tags/entry-assignments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"timeEntryId\": \"00000000-0000-4000-8000-000000000000\",
  \"tagIds\": [
    \"00000000-0000-4000-8000-000000000000\"
  ]
}"
```

## POST `/staff/timesheets/tags/entry-assignments`

Assign tags to a time entry

Adds tags to a time entry. Idempotent — a tag the entry already carries comes back in alreadyAssignedTagIds. Refused with 404 when the entry is outside the caller's project access, and with 409 when it is locked in a closed report.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "timeEntryId": "00000000-0000-4000-8000-000000000000",
  "tagIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

### Responses

**200** – Tags assigned

Content-Type: `application/json`

```json
{
  "timeEntryId": "00000000-0000-4000-8000-000000000000",
  "tagIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "assignedTagIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "alreadyAssignedTagIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

**400** – Invalid payload or missing organization scope

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.tasks.manage

Content-Type: `application/json`

**404** – Time entry not found or not accessible

Content-Type: `application/json`

**409** – Time entry locked in a closed report

Content-Type: `application/json`

**422** – Unknown tag ids

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/tags/entry-assignments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"timeEntryId\": \"00000000-0000-4000-8000-000000000000\",
  \"tagIds\": [
    \"00000000-0000-4000-8000-000000000000\"
  ]
}"
```

## DELETE `/staff/timesheets/tags/task-assignments`

Unassign tags from a task

Removes tags from a task. Idempotent — a tag the task does not carry comes back in notAssignedTagIds instead of failing.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "taskId": "00000000-0000-4000-8000-000000000000",
  "tagIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

### Responses

**200** – Tags unassigned

Content-Type: `application/json`

```json
{
  "taskId": "00000000-0000-4000-8000-000000000000",
  "tagIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "removedTagIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "notAssignedTagIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

**400** – Invalid payload or missing organization scope

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.tasks.manage

Content-Type: `application/json`

**404** – Task not found or not accessible

Content-Type: `application/json`

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/timesheets/tags/task-assignments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"taskId\": \"00000000-0000-4000-8000-000000000000\",
  \"tagIds\": [
    \"00000000-0000-4000-8000-000000000000\"
  ]
}"
```

## POST `/staff/timesheets/tags/task-assignments`

Assign tags to a task

Adds tags to a task. Idempotent — a tag the task already carries comes back in alreadyAssignedTagIds instead of failing. Refused with 404 when the task is outside the caller's project access.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "taskId": "00000000-0000-4000-8000-000000000000",
  "tagIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

### Responses

**200** – Tags assigned

Content-Type: `application/json`

```json
{
  "taskId": "00000000-0000-4000-8000-000000000000",
  "tagIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "assignedTagIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "alreadyAssignedTagIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

**400** – Invalid payload or missing organization scope

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.tasks.manage

Content-Type: `application/json`

**404** – Task not found or not accessible

Content-Type: `application/json`

**422** – Unknown tag ids

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/tags/task-assignments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"taskId\": \"00000000-0000-4000-8000-000000000000\",
  \"tagIds\": [
    \"00000000-0000-4000-8000-000000000000\"
  ]
}"
```

## DELETE `/staff/timesheets/task-statuses`

Delete timetaskstatus

Soft-deletes a Kanban column. Refused with 409 when it is the project's last column or still holds tasks.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – TimeTaskStatus deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/timesheets/task-statuses" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/timesheets/task-statuses`

List time task statuses

Returns the Kanban columns of the projects the caller can see. A caller without `staff.timesheets.projects.manage` is narrowed to the projects they are an active member of, so `timeProjectId` can only ever select a board they already have access to.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |
| timeProjectId | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Paginated time task statuses

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": null,
      "organization_id": null,
      "tenant_id": null,
      "time_project_id": null,
      "name": null,
      "slug": null,
      "color": null,
      "position": null,
      "is_default": null,
      "is_done": null,
      "created_at": null,
      "updated_at": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/task-statuses?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/timesheets/task-statuses`

Create timetaskstatus

Creates a Kanban column on a time project.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "timeProjectId": "00000000-0000-4000-8000-000000000000",
  "name": "string",
  "color": null
}
```

### Responses

**201** – TimeTaskStatus created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/task-statuses" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"timeProjectId\": \"00000000-0000-4000-8000-000000000000\",
  \"name\": \"string\",
  \"color\": null
}"
```

## PUT `/staff/timesheets/task-statuses`

Update timetaskstatus

Updates a Kanban column. Supplying `position` re-orders the board; the last default column cannot be unset and the last done column cannot be cleared.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "color": null
}
```

### Responses

**200** – TimeTaskStatus updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/timesheets/task-statuses" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"color\": null
}"
```

## DELETE `/staff/timesheets/tasks`

Delete timetask

Soft-deletes a task. Deleting a parent soft-deletes its subtasks in the same transaction; time entries keep pointing at the deleted task so closed reports still resolve their labels.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – TimeTask deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/timesheets/tasks" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/timesheets/tasks`

List time tasks

Returns a paginated collection of time tasks scoped to the authenticated organization.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| q | query | any | Optional |
| reference | query | any | Optional |
| id | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |
| timeProjectId | query | any | Optional |
| taskStatusId | query | any | Optional |
| assigneeStaffMemberId | query | any | Optional |
| parentTaskId | query | any | Optional |
| tagIds | query | any | Optional |
| topLevelOnly | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Paginated time tasks

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": null,
      "organization_id": null,
      "tenant_id": null,
      "time_project_id": null,
      "parent_task_id": null,
      "task_status_id": null,
      "sequence_number": null,
      "reference": null,
      "title": null,
      "description": null,
      "assignee_staff_member_id": null,
      "position": null,
      "created_by_user_id": null,
      "closed_at": null,
      "created_at": null,
      "updated_at": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/tasks?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/timesheets/tasks`

Create timetask

Creates a task. Only `title` is required beyond scoping and `timeProjectId`: the task lands on the project default column with the creator assigned unless the request says otherwise. Supplying `parentTaskId` creates a subtask; a parent that is itself a subtask is refused with 400 subtask_depth_exceeded.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "timeProjectId": "00000000-0000-4000-8000-000000000000",
  "parentTaskId": null,
  "taskStatusId": null,
  "title": "string",
  "description": null,
  "assigneeStaffMemberId": null
}
```

### Responses

**201** – TimeTask created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/tasks" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"timeProjectId\": \"00000000-0000-4000-8000-000000000000\",
  \"parentTaskId\": null,
  \"taskStatusId\": null,
  \"title\": \"string\",
  \"description\": null,
  \"assigneeStaffMemberId\": null
}"
```

## PUT `/staff/timesheets/tasks`

Update timetask

Updates a task. Re-parenting under a subtask, or turning a task that already has subtasks into one, is refused with 400 subtask_depth_exceeded; moving a task to another project is refused with 400 task_project_move_unsupported. Moving into a done column stamps closed_at, moving out clears it.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "parentTaskId": null,
  "description": null,
  "assigneeStaffMemberId": null
}
```

### Responses

**200** – TimeTask updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/timesheets/tasks" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"parentTaskId\": null,
  \"description\": null,
  \"assigneeStaffMemberId\": null
}"
```

## DELETE `/staff/timesheets/tasks/{id}/comments`

Delete a task comment

Soft-deletes a comment so its audit trail survives. Only the comment author may delete, unless the caller holds staff.timesheets.manage_all.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – Comment deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing comment id, task id or organization scope

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Not the author and missing staff.timesheets.manage_all

Content-Type: `application/json`

**404** – Task or comment not found or not accessible

Content-Type: `application/json`

**409** – Optimistic lock conflict

Content-Type: `application/json`

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/timesheets/tasks/:id/comments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/timesheets/tasks/{id}/comments`

List task comments

Returns the task comment thread oldest-first, with the author display name resolved for rendering. Answers 404 — identical to a task that does not exist — when the task belongs to a project the caller cannot see.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – Comment thread

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "taskId": "00000000-0000-4000-8000-000000000000",
      "body": "string",
      "authorUserId": null,
      "authorName": null,
      "authorEmail": null,
      "createdAt": null,
      "updatedAt": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

**400** – Missing task id or organization scope

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.tasks.view

Content-Type: `application/json`

**404** – Task not found or not accessible

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/tasks/:id/comments?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/timesheets/tasks/{id}/comments`

Post a task comment

Adds a comment to the task. The author is stamped from the authenticated caller — an authorUserId supplied by the client is ignored.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "body": "string"
}
```

### Responses

**201** – Comment created

Content-Type: `application/json`

```json
{
  "id": null,
  "taskId": "00000000-0000-4000-8000-000000000000",
  "authorUserId": null
}
```

**400** – Invalid payload, missing task id or organization scope

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.tasks.manage

Content-Type: `application/json`

**404** – Task not found or not accessible

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/tasks/:id/comments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"body\": \"string\"
}"
```

## PUT `/staff/timesheets/tasks/{id}/comments`

Edit a task comment

Updates a comment body. Only the comment author may edit, unless the caller holds staff.timesheets.manage_all. Honours the optimistic-lock header.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "body": "string"
}
```

### Responses

**200** – Comment updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload, missing task id or organization scope

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Not the author and missing staff.timesheets.manage_all

Content-Type: `application/json`

**404** – Task or comment not found or not accessible

Content-Type: `application/json`

**409** – Optimistic lock conflict

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/timesheets/tasks/:id/comments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"body\": \"string\"
}"
```

## PATCH `/staff/timesheets/tasks/{id}/status`

Move a task to another board column

Moves a task to the requested status column and slot. `position` is optional — omitting it appends the task to the end of the target column, which is what the drawer subtask tick does. The target status must belong to the task project (422 otherwise). Landing on a done column stamps `closedAt`, leaving one clears it. Send the record version in `x-om-ext-optimistic-lock-expected-updated-at`: a stale version answers 409 so an optimistic board can roll the card back to its origin column. Emits `staff.timesheets.time_task.status_changed`, which is bridged to connected boards.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "taskStatusId": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Task moved

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "timeProjectId": "00000000-0000-4000-8000-000000000000",
  "previousTaskStatusId": null,
  "taskStatusId": "00000000-0000-4000-8000-000000000000",
  "position": null,
  "closedAt": null,
  "updatedAt": null
}
```

**400** – Invalid task id or body

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.tasks.manage

Content-Type: `application/json`

**404** – Task not found, or not on a project the caller can see

Content-Type: `application/json`

**409** – The task changed since the version the caller sent

Content-Type: `application/json`

```json
{
  "code": "optimistic_lock_conflict",
  "error": "string",
  "currentUpdatedAt": "string",
  "expectedUpdatedAt": "string"
}
```

**422** – The target column does not belong to this project

Content-Type: `application/json`

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/staff/timesheets/tasks/:id/status" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"taskStatusId\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## DELETE `/staff/timesheets/time-entries`

Delete timeentry

Deletes a time entry by id.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – TimeEntry deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/timesheets/time-entries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/timesheets/time-entries`

List timeentries

Returns a paginated collection of timeentries scoped to the authenticated organization.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| staffMemberId | query | any | Optional |
| from | query | any | Optional |
| to | query | any | Optional |
| projectId | query | any | Optional |
| taskId | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |
| running | query | any | Optional |
| billable | query | any | Optional |
| locked | query | any | Optional |
| q | query | any | Optional |
| customerId | query | any | Optional |
| tagIds | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Paginated timeentries

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": null,
      "organization_id": null,
      "tenant_id": null,
      "staff_member_id": null,
      "date": null,
      "duration_minutes": null,
      "rounded_minutes": null,
      "started_at": null,
      "ended_at": null,
      "notes": null,
      "time_project_id": null,
      "task_id": null,
      "customer_id": null,
      "deal_id": null,
      "order_id": null,
      "is_billable": null,
      "rate_override_amount": null,
      "rate_currency_code": null,
      "locked_report_id": null,
      "locked_at": null,
      "source": null,
      "created_at": null,
      "updated_at": null,
      "description": null,
      "roundedMinutes": null,
      "lockedReportId": null,
      "cost": null,
      "currencyCode": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/time-entries?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/timesheets/time-entries`

Create timeentry

Creates a time entry for a staff member. The note is accepted under either `notes` or `description` (`description` wins when both are sent). `roundedMinutes` is derived from the tenant rounding rule at write time and stored; `isBillable` falls back to the project default then the tenant default; `rateCurrencyCode` snapshots the project currency. `tagIds` is applied through the tag assignment command.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "staffMemberId": "00000000-0000-4000-8000-000000000000",
  "date": "2025-01-01T00:00:00.000Z",
  "durationMinutes": 1,
  "startedAt": null,
  "endedAt": null,
  "timeProjectId": null,
  "customerId": null,
  "dealId": null,
  "orderId": null,
  "notes": null,
  "source": "manual",
  "taskId": null,
  "description": null,
  "rateOverrideAmount": null
}
```

### Responses

**201** – TimeEntry created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/time-entries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"staffMemberId\": \"00000000-0000-4000-8000-000000000000\",
  \"date\": \"2025-01-01T00:00:00.000Z\",
  \"durationMinutes\": 1,
  \"startedAt\": null,
  \"endedAt\": null,
  \"timeProjectId\": null,
  \"customerId\": null,
  \"dealId\": null,
  \"orderId\": null,
  \"notes\": null,
  \"source\": \"manual\",
  \"taskId\": null,
  \"description\": null,
  \"rateOverrideAmount\": null
}"
```

## PUT `/staff/timesheets/time-entries`

Update timeentry

Updates a time entry by id. `durationMinutes`, `startedAt` and `endedAt` are reconciled so the stored set never contradicts itself: sending both clocks recomputes the duration from them (a duration sent alongside is advisory), sending one clock with a duration derives the other clock, and sending only a duration shifts `endedAt` while `startedAt` anchors. An `endedAt` earlier than its `startedAt` is stored on the next calendar day (a midnight crossing) and `date` still names the day the work started. Any change to the effective duration recomputes the stored rounded minutes from the tenant rounding rule; changing the project re-snapshots the currency. `tagIds` replaces the entry tag set through the tag assignment commands, which refuse an entry locked in a closed report.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "startedAt": null,
  "endedAt": null,
  "timeProjectId": null,
  "customerId": null,
  "dealId": null,
  "orderId": null,
  "notes": null,
  "taskId": null,
  "description": null,
  "rateOverrideAmount": null
}
```

### Responses

**200** – TimeEntry updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/timesheets/time-entries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"startedAt\": null,
  \"endedAt\": null,
  \"timeProjectId\": null,
  \"customerId\": null,
  \"dealId\": null,
  \"orderId\": null,
  \"notes\": null,
  \"taskId\": null,
  \"description\": null,
  \"rateOverrideAmount\": null
}"
```

## POST `/staff/timesheets/time-entries/{id}/duplicate`

Duplicate a time entry

US-D6. Copies an existing time entry, carrying its task, project, description, billable flag and rate override across. `date` and `durationMinutes` override the copy without a second request; `tagIds` sets the copy tag set (the caller passes the source tags it already holds from the list response). The copy is written through the create command, so ownership, project access, the billable and currency defaults, and the tenant rounding rule apply to it exactly as they do to a hand-made entry. A source locked into a closed report is refused with 409 `time_entry_locked`; a source the caller cannot see answers 404.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**201** – Copy created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "sourceId": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid body or missing scope

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.manage_own

Content-Type: `application/json`

**404** – Time entry not found, deleted, or outside the caller project access

Content-Type: `application/json`

**409** – The source entry is locked into a closed report; nothing was copied

Content-Type: `application/json`

```json
{
  "code": "time_entry_locked",
  "error": "string",
  "lockedReportId": null,
  "lockedEntryIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "lockedReportIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/time-entries/:id/duplicate" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/staff/timesheets/time-entries/{id}/segments`

Add a segment to a time entry

Creates a new work or break segment for the specified time entry.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "timeEntryId": "00000000-0000-4000-8000-000000000000",
  "startedAt": "2025-01-01T00:00:00.000Z",
  "endedAt": null,
  "segmentType": "work"
}
```

### Responses

**201** – Segment created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "timeEntryId": "00000000-0000-4000-8000-000000000000",
  "startedAt": "string",
  "endedAt": null,
  "segmentType": "work",
  "createdAt": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Time entry not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/time-entries/:id/segments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"timeEntryId\": \"00000000-0000-4000-8000-000000000000\",
  \"startedAt\": \"2025-01-01T00:00:00.000Z\",
  \"endedAt\": null,
  \"segmentType\": \"work\"
}"
```

## PATCH `/staff/timesheets/time-entries/{id}/segments/{segmentId}`

Update a time entry segment

Updates fields on an existing time entry segment (startedAt, endedAt, segmentType).

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| segmentId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "endedAt": null
}
```

### Responses

**200** – Segment updated successfully

Content-Type: `application/json`

```json
{
  "ok": true,
  "item": {
    "id": "string",
    "timeEntryId": "string",
    "startedAt": "string",
    "endedAt": null,
    "segmentType": "work",
    "createdAt": "string",
    "updatedAt": "string"
  }
}
```

**400** – Invalid payload or missing segment id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Segment not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/staff/timesheets/time-entries/:id/segments/:segmentId" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"endedAt\": null
}"
```

## POST `/staff/timesheets/time-entries/{id}/timer-start`

Start timer for a time entry

Starts the timer on a time entry by setting startedAt and creating an initial work segment.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Timer started

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Time entry not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Timer already started, another timer is already running for this staff member, or the entry is locked in a closed report (code time_entry_locked)

Content-Type: `application/json`

```json
{
  "error": "string",
  "lockedReportId": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/time-entries/:id/timer-start" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/timesheets/time-entries/{id}/timer-stop`

Stop timer for a time entry

Stops the active timer segment, recalculates total work duration in minutes, and updates the time entry.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Timer stopped

Content-Type: `application/json`

```json
{
  "ok": true,
  "durationMinutes": 1
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Time entry not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – No active timer segment, or the entry is locked in a closed report (code time_entry_locked)

Content-Type: `application/json`

```json
{
  "error": "string",
  "lockedReportId": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/time-entries/:id/timer-stop" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/timesheets/time-entries/bulk`

Bulk save time entries

Creates, updates, or soft-deletes multiple time entries in a single request. Entries with durationMinutes=0 and an existing id are soft-deleted. A row may name a taskId and let timeProjectId follow from it; task, description/notes precedence, isBillable defaulting, the rate override and the project currency snapshot follow the single-entry path exactly. tagIds is rejected with 422 — tags are edited from the time entry itself so their audit trail and undo stay intact. The whole batch is rejected with 409 time_entry_locked when any row targets — by id, or by landing on its (project, date) cell — an entry frozen in a closed report.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "entries": [
    {
      "id": null,
      "date": "2025-01-01T00:00:00.000Z",
      "timeProjectId": null,
      "durationMinutes": 1,
      "notes": null,
      "taskId": null,
      "description": null,
      "rateOverrideAmount": null
    }
  ]
}
```

### Responses

**200** – Bulk save completed

Content-Type: `application/json`

```json
{
  "ok": true,
  "created": 1,
  "updated": 1,
  "deleted": 1
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – One or more rows target a time entry locked in a closed report; nothing was saved

Content-Type: `application/json`

```json
{
  "code": "time_entry_locked",
  "error": "string",
  "lockedReportId": null,
  "lockedEntryIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "lockedReportIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

**422** – Validation error

Content-Type: `application/json`

```json
{
  "ok": false,
  "errors": [
    {
      "path": "string",
      "message": "string"
    }
  ]
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/time-entries/bulk" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entries\": [
    {
      \"id\": null,
      \"date\": \"2025-01-01T00:00:00.000Z\",
      \"timeProjectId\": null,
      \"durationMinutes\": 1,
      \"notes\": null,
      \"taskId\": null,
      \"description\": null,
      \"rateOverrideAmount\": null
    }
  ]
}"
```

## POST `/staff/timesheets/time-entries/copy-day`

Copy a day of time entries

US-D6, screen 1 note 5. Copies every unlocked time entry the caller owns on `fromDate` onto `toDate` as editable drafts, and returns the created rows so they can be reviewed and corrected. Which day is "the previous working day" is the caller decision — this endpoint takes explicit dates and applies no weekend or holiday rule. A source entry locked into a closed report is skipped and reported in `skipped[]` with the shared `time_entry_locked` code (nothing is written to it, so the batch is not failed). The request is refused with 409 `copy_day_target_not_empty` when the target day already holds entries, so a repeated call cannot silently double a day; pass `allowDuplicates: true` to add the copies anyway. `staffMemberId` targets another member and requires `staff.timesheets.manage_all`. Every copy is written through the duplicate command and therefore through the create command, so ownership, project access, the billable and currency defaults and the tenant rounding rule apply to it exactly as they do to a hand-made entry.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "fromDate": "2025-01-01T00:00:00.000Z",
  "toDate": "2025-01-01T00:00:00.000Z",
  "allowDuplicates": false
}
```

### Responses

**200** – Copies created, with the skipped sources reported

Content-Type: `application/json`

```json
{
  "ok": true,
  "fromDate": "string",
  "toDate": "string",
  "staffMemberId": "00000000-0000-4000-8000-000000000000",
  "copied": 1,
  "created": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "sourceId": "00000000-0000-4000-8000-000000000000",
      "date": "string",
      "durationMinutes": 1,
      "roundedMinutes": null,
      "timeProjectId": null,
      "taskId": null,
      "isBillable": true,
      "description": null,
      "tagIds": [
        "00000000-0000-4000-8000-000000000000"
      ]
    }
  ],
  "skipped": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "reason": "string",
      "lockedReportId": null,
      "error": null
    }
  ]
}
```

**400** – Invalid body, missing scope, or fromDate equal to toDate

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.manage_own, no linked staff member, or another member day without staff.timesheets.manage_all

Content-Type: `application/json`

**409** – The target day already has entries; nothing was copied

Content-Type: `application/json`

```json
{
  "code": "copy_day_target_not_empty",
  "error": "string",
  "toDate": "string",
  "existingEntryCount": 1,
  "existingEntryIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/time-entries/copy-day" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"fromDate\": \"2025-01-01T00:00:00.000Z\",
  \"toDate\": \"2025-01-01T00:00:00.000Z\",
  \"allowDuplicates\": false
}"
```

## GET `/staff/timesheets/time-entries/overlaps`

List time entries overlapping a candidate span

Advisory, read-only lookup used by the time entry form to warn about conflicting entries. Overlapping time is allowed, so the result never blocks a save. Returns an empty list when the supplied time information is insufficient. Checking another staff member requires staff.timesheets.manage_all; without it the request is silently scoped to the caller.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| date | query | any | Required |
| startedAt | query | any | Optional |
| endedAt | query | any | Optional |
| durationMinutes | query | any | Optional |
| excludeId | query | any | Optional |
| staffMemberId | query | any | Optional |

### Responses

**200** – Overlapping entries

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "staff_member_id": "00000000-0000-4000-8000-000000000000",
      "date": "string",
      "started_at": "string",
      "ended_at": "string",
      "duration_minutes": 1,
      "overlap_minutes": 1,
      "crosses_midnight": true,
      "task_id": null,
      "task_title": null,
      "time_project_id": null,
      "project_name": null,
      "notes": null
    }
  ],
  "total": 1,
  "decision": "allow"
}
```

**400** – Invalid query or missing scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/time-entries/overlaps?date=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/timesheets/time-entries/start-timer`

Start a timesheet timer

Atomically creates a timer-sourced time entry and starts it (sets startedAt and creates the initial work segment) in a single transaction, so a partial failure cannot leave an orphaned, unstarted entry. An optional `taskId` is validated and stored by the same write, so starting a timer from a board card or the task drawer takes one request instead of a start followed by a link that can fail; when only `taskId` is sent, the project follows from the task.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "staffMemberId": "00000000-0000-4000-8000-000000000000",
  "date": "2025-01-01T00:00:00.000Z",
  "timeProjectId": null,
  "taskId": null,
  "notes": null
}
```

### Responses

**201** – Timer started

Content-Type: `application/json`

```json
{
  "ok": true,
  "id": null
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Another timer is already running for this staff member

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Referenced time project or task not found, out of scope, or the task belongs to another project

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/time-entries/start-timer" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"staffMemberId\": \"00000000-0000-4000-8000-000000000000\",
  \"date\": \"2025-01-01T00:00:00.000Z\",
  \"timeProjectId\": null,
  \"taskId\": null,
  \"notes\": null
}"
```

## DELETE `/staff/timesheets/time-projects`

Delete timeproject

Soft-deletes a time project by id.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – TimeProject deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/timesheets/time-projects" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/timesheets/time-projects`

List time projects

Returns a paginated collection of time projects scoped to the authenticated organization. Rows are intersected with the caller's project access: without staff.timesheets.projects.manage only projects the caller is an active member of are returned. Asking for a single id the caller may not see answers 404 with reason=no_project_access and never names the project or its customer; the same 404 covers ids that do not exist, so the endpoint cannot be used to enumerate projects.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| q | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |
| projectType | query | any | Optional |
| status | query | any | Optional |
| customerId | query | any | Optional |
| mine | query | any | Optional |
| include | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Paginated time projects

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": null,
      "organization_id": null,
      "tenant_id": null,
      "name": null,
      "code": null,
      "description": null,
      "project_type": null,
      "color": null,
      "status": null,
      "customer_id": null,
      "customer_snapshot": null,
      "owner_user_id": null,
      "cost_center": null,
      "start_date": null,
      "hourly_rate": null,
      "currency_code": null,
      "billable_by_default": null,
      "budget_kind": null,
      "budget_value": null,
      "budget_warn_at_percent": null,
      "created_at": null,
      "updated_at": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**404** – The caller is not a member of the requested project (or it does not exist)

Content-Type: `application/json`

```json
{
  "error": "string",
  "reason": "no_project_access"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/time-projects?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/timesheets/time-projects`

Create timeproject

Creates a time project.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "name": "string",
  "customerId": "00000000-0000-4000-8000-000000000000",
  "customerSnapshot": null,
  "code": "string",
  "description": null,
  "projectType": null,
  "color": null,
  "status": "active",
  "ownerUserId": null,
  "costCenter": null,
  "startDate": null,
  "hourlyRate": null,
  "currencyCode": null,
  "budgetKind": "none",
  "budgetValue": null
}
```

### Responses

**201** – TimeProject created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/time-projects" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"name\": \"string\",
  \"customerId\": \"00000000-0000-4000-8000-000000000000\",
  \"customerSnapshot\": null,
  \"code\": \"string\",
  \"description\": null,
  \"projectType\": null,
  \"color\": null,
  \"status\": \"active\",
  \"ownerUserId\": null,
  \"costCenter\": null,
  \"startDate\": null,
  \"hourlyRate\": null,
  \"currencyCode\": null,
  \"budgetKind\": \"none\",
  \"budgetValue\": null
}"
```

## PUT `/staff/timesheets/time-projects`

Update timeproject

Updates a time project by id.

**Tags:** Staff

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "customerId": null,
  "customerSnapshot": null,
  "description": null,
  "projectType": null,
  "color": null,
  "ownerUserId": null,
  "costCenter": null,
  "startDate": null,
  "hourlyRate": null,
  "budgetValue": null
}
```

### Responses

**200** – TimeProject updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/timesheets/time-projects" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"customerId\": null,
  \"customerSnapshot\": null,
  \"description\": null,
  \"projectType\": null,
  \"color\": null,
  \"ownerUserId\": null,
  \"costCenter\": null,
  \"startDate\": null,
  \"hourlyRate\": null,
  \"budgetValue\": null
}"
```

## POST `/staff/timesheets/time-projects/{id}/change-currency`

Change a time project currency

Relabels the project currency without converting any historical amount. Requires an explicit `acknowledged: true` and is refused while any of the project entries is locked into a closed report.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "acknowledged": true
}
```

### Responses

**200** – Currency relabelled

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "currencyCode": "string",
  "previousCurrencyCode": null,
  "converted": false
}
```

**400** – Invalid body, or the change was not acknowledged

Content-Type: `application/json`

**401** – Unauthorized

Content-Type: `application/json`

**403** – Missing staff.timesheets.projects.manage

Content-Type: `application/json`

**404** – Time project not found

Content-Type: `application/json`

**409** – Project has entries locked into a closed report

Content-Type: `application/json`

```json
{
  "code": "project_has_locked_entries",
  "error": "string",
  "lockedEntryCount": 1,
  "lockedReports": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "reference": null,
      "title": null
    }
  ]
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/time-projects/:id/change-currency" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"acknowledged\": true
}"
```

## DELETE `/staff/timesheets/time-projects/{id}/employees`

Delete timeprojectmember

Unassigns an employee from a time project.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

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

### Responses

**200** – TimeProjectMember deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/staff/timesheets/time-projects/:id/employees" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/staff/timesheets/time-projects/{id}/employees`

List time project members

Returns a paginated collection of time project members scoped to the authenticated organization.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| timeProjectId | query | any | Optional |
| status | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated time project members

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": null,
      "organization_id": null,
      "tenant_id": null,
      "time_project_id": null,
      "staff_member_id": null,
      "role": null,
      "status": null,
      "assigned_start_date": null,
      "assigned_end_date": null,
      "created_at": null,
      "updated_at": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/staff/timesheets/time-projects/:id/employees?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/staff/timesheets/time-projects/{id}/employees`

Create timeprojectmember

Assigns an employee to a time project.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "timeProjectId": "00000000-0000-4000-8000-000000000000",
  "staffMemberId": "00000000-0000-4000-8000-000000000000",
  "role": null,
  "status": "active",
  "assignedStartDate": "2025-01-01T00:00:00.000Z",
  "assignedEndDate": null
}
```

### Responses

**201** – TimeProjectMember created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/staff/timesheets/time-projects/:id/employees" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tenantId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"timeProjectId\": \"00000000-0000-4000-8000-000000000000\",
  \"staffMemberId\": \"00000000-0000-4000-8000-000000000000\",
  \"role\": null,
  \"status\": \"active\",
  \"assignedStartDate\": \"2025-01-01T00:00:00.000Z\",
  \"assignedEndDate\": null
}"
```

## PUT `/staff/timesheets/time-projects/{id}/employees`

Update timeprojectmember

Updates an existing assignment (role, status, assignment end date) without unassigning the employee.

**Tags:** Staff

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "role": null,
  "assignedEndDate": null
}
```

### Responses

**200** – TimeProjectMember updated

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/staff/timesheets/time-projects/:id/employees" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"role\": null,
  \"assignedEndDate\": null
}"
```

## DELETE `/storage-providers/s3/delete`

Delete file from S3

**Tags:** S3-Compatible Storage

### Responses

**204** – Success

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/storage-providers/s3/delete" \
  -H "Accept: application/json"
```

## GET `/storage-providers/s3/download`

Download file from S3

**Tags:** S3-Compatible Storage

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/storage-providers/s3/download" \
  -H "Accept: application/json"
```

## GET `/storage-providers/s3/list`

List S3 objects

**Tags:** S3-Compatible Storage

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/storage-providers/s3/list" \
  -H "Accept: application/json"
```

## PUT `/storage-providers/s3/signed-upload/:token`

Upload through a bounded one-time compatibility URL

**Tags:** S3-Compatible Storage

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/storage-providers/s3/signed-upload/:token" \
  -H "Accept: application/json"
```

## POST `/storage-providers/s3/signed-url`

Generate S3 pre-signed URL

**Tags:** S3-Compatible Storage

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/storage-providers/s3/signed-url" \
  -H "Accept: application/json"
```

## POST `/storage-providers/s3/upload`

Upload file to S3

**Tags:** S3-Compatible Storage

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/storage-providers/s3/upload" \
  -H "Accept: application/json"
```

## DELETE `/translations/{entityType}/{entityId}`

Delete entity translations

Removes all translations for an entity.

**Tags:** Translations

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityType | path | any | Required |
| entityId | path | any | Required |

### Responses

**204** – Translations deleted.

**401** – Authentication required

Content-Type: `application/json`

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/translations/string/string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/translations/{entityType}/{entityId}`

Get entity translations

Returns the full translation record for a single entity.

**Tags:** Translations

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityType | path | any | Required |
| entityId | path | any | Required |

### Responses

**200** – Translation record found.

Content-Type: `application/json`

**401** – Authentication required

Content-Type: `application/json`

**404** – No translations found for this entity

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/translations/string/string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/translations/{entityType}/{entityId}`

Create or update entity translations

Full replacement of translations JSONB for an entity.

**Tags:** Translations

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityType | path | any | Required |
| entityId | path | any | Required |

### Responses

**200** – Translations saved.

Content-Type: `application/json`

**400** – Validation failed

Content-Type: `application/json`

**401** – Authentication required

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/translations/string/string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/translations/locales`

List supported translation locales

**Tags:** Entity Translations

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/translations/locales" \
  -H "Accept: application/json"
```

## PUT `/translations/locales`

Update supported translation locales

**Tags:** Entity Translations

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/translations/locales" \
  -H "Accept: application/json"
```

## GET `/version`

Deployed Open Mercato version

**Tags:** API Documentation

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/version" \
  -H "Accept: application/json"
```

## GET `/workflows/command-settings`

List workflow-safe command candidates with tenant enablement

Returns every command declared through registerWorkflowSafeCommands together with whether this tenant has switched it on. `configured: false` means the tenant has never saved the setting and the answers come from the grandfathered defaults.

**Tags:** Workflows

**Requires authentication.**

### Responses

**200** – Catalogue with tenant enablement

Content-Type: `application/json`

```json
{
  "configured": true,
  "items": [
    {
      "commandId": "string",
      "requiredFeatures": [
        "string"
      ],
      "labelKey": null
    }
  ]
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/command-settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/workflows/command-settings`

Set which workflow-safe commands are enabled for the tenant

Replaces the tenant's enabled set. Every submitted id must be present in the code-declared catalogue; an unknown id is rejected with 400 rather than stored. Stored ids whose declaring module is not loaded in this process are retained so a save can never silently untick them.

**Tags:** Workflows

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "enabledCommandIds": [
    "string"
  ]
}
```

### Responses

**200** – Updated enablement

Content-Type: `application/json`

```json
{
  "configured": true,
  "items": [
    {
      "commandId": "string",
      "requiredFeatures": [
        "string"
      ],
      "labelKey": null
    }
  ],
  "ok": true
}
```

**400** – Invalid payload or unknown command id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Settings storage unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/workflows/command-settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"enabledCommandIds\": [
    \"string\"
  ]
}"
```

## GET `/workflows/commands`

List commands allowlisted for UPDATE_ENTITY activities

Returns the registered workflow-safe command catalogue consumed by the UPDATE_ENTITY command picker, each entry carrying whether the caller's tenant has enabled it. Entries with enabled:false, and commands outside the catalogue entirely, can still be authored but fail at runtime.

**Tags:** Workflows

### Responses

**200** – Allowlisted workflow-safe commands

Content-Type: `application/json`

```json
{
  "items": [
    {
      "commandId": "string",
      "requiredFeatures": [
        "string"
      ],
      "labelKey": null
    }
  ]
}
```

**400** – Missing tenant context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/commands" \
  -H "Accept: application/json"
```

## GET `/workflows/definitions`

List workflow definitions

Get a list of workflow definitions with optional filters. Supports pagination and search.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| workflowId | query | any | Required |
| enabled | query | any | Optional |
| search | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – List of workflow definitions with pagination

Content-Type: `application/json`

```json
{
  "data": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "workflowId": "checkout-flow",
      "workflowName": "Checkout Flow",
      "description": "Complete checkout workflow for processing orders",
      "version": 1,
      "definition": {
        "steps": [
          {
            "stepId": "start",
            "stepName": "Start",
            "stepType": "START"
          },
          {
            "stepId": "validate-cart",
            "stepName": "Validate Cart",
            "stepType": "AUTOMATED"
          },
          {
            "stepId": "end",
            "stepName": "End",
            "stepType": "END"
          }
        ],
        "transitions": [
          {
            "transitionId": "start-to-validate",
            "fromStepId": "start",
            "toStepId": "validate-cart",
            "trigger": "auto"
          },
          {
            "transitionId": "validate-to-end",
            "fromStepId": "validate-cart",
            "toStepId": "end",
            "trigger": "auto"
          }
        ]
      },
      "enabled": true,
      "tenantId": "123e4567-e89b-12d3-a456-426614174001",
      "organizationId": "123e4567-e89b-12d3-a456-426614174002",
      "createdAt": "2025-12-08T10:00:00.000Z",
      "updatedAt": "2025-12-08T10:00:00.000Z"
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 50,
    "offset": 0,
    "hasMore": false
  }
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/definitions?workflowId=string&limit=50&offset=0" \
  -H "Accept: application/json"
```

## POST `/workflows/definitions`

Create workflow definition

Create a new workflow definition. The definition must include at least START and END steps with at least one transition connecting them.

**Tags:** Workflows

### Request Body

Content-Type: `application/json`

```json
{
  "workflowId": "checkout-flow",
  "workflowName": "Checkout Flow",
  "description": "Complete checkout workflow for processing orders",
  "version": 1,
  "definition": {
    "steps": [
      {
        "stepId": "start",
        "stepName": "Start",
        "stepType": "START"
      },
      {
        "stepId": "validate-cart",
        "stepName": "Validate Cart",
        "stepType": "AUTOMATED",
        "description": "Validate cart items and check inventory"
      },
      {
        "stepId": "payment",
        "stepName": "Process Payment",
        "stepType": "AUTOMATED",
        "description": "Charge payment method",
        "retryPolicy": {
          "maxAttempts": 3,
          "backoffMs": 1000
        }
      },
      {
        "stepId": "end",
        "stepName": "End",
        "stepType": "END"
      }
    ],
    "transitions": [
      {
        "transitionId": "start-to-validate",
        "fromStepId": "start",
        "toStepId": "validate-cart",
        "trigger": "auto"
      },
      {
        "transitionId": "validate-to-payment",
        "fromStepId": "validate-cart",
        "toStepId": "payment",
        "trigger": "auto"
      },
      {
        "transitionId": "payment-to-end",
        "fromStepId": "payment",
        "toStepId": "end",
        "trigger": "auto",
        "activities": [
          {
            "activityName": "Send Order Confirmation",
            "activityType": "SEND_EMAIL",
            "config": {
              "to": "{{context.customerEmail}}",
              "subject": "Order Confirmation #{{context.orderId}}",
              "template": "order_confirmation"
            }
          }
        ]
      }
    ]
  },
  "enabled": true
}
```

### Responses

**201** – Workflow definition created successfully

Content-Type: `application/json`

```json
{
  "data": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "workflowId": "checkout-flow",
    "workflowName": "Checkout Flow",
    "description": "Complete checkout workflow for processing orders",
    "version": 1,
    "definition": {
      "steps": [
        {
          "stepId": "start",
          "stepName": "Start",
          "stepType": "START"
        },
        {
          "stepId": "validate-cart",
          "stepName": "Validate Cart",
          "stepType": "AUTOMATED"
        },
        {
          "stepId": "payment",
          "stepName": "Process Payment",
          "stepType": "AUTOMATED"
        },
        {
          "stepId": "end",
          "stepName": "End",
          "stepType": "END"
        }
      ],
      "transitions": [
        {
          "transitionId": "start-to-validate",
          "fromStepId": "start",
          "toStepId": "validate-cart",
          "trigger": "auto"
        },
        {
          "transitionId": "validate-to-payment",
          "fromStepId": "validate-cart",
          "toStepId": "payment",
          "trigger": "auto"
        },
        {
          "transitionId": "payment-to-end",
          "fromStepId": "payment",
          "toStepId": "end",
          "trigger": "auto"
        }
      ]
    },
    "enabled": true,
    "tenantId": "123e4567-e89b-12d3-a456-426614174001",
    "organizationId": "123e4567-e89b-12d3-a456-426614174002",
    "createdAt": "2025-12-08T10:00:00.000Z",
    "updatedAt": "2025-12-08T10:00:00.000Z"
  },
  "message": "Workflow definition created successfully"
}
```

**400** – Validation error - invalid workflow structure

Content-Type: `application/json`

```json
{
  "error": "Validation failed",
  "details": [
    {
      "path": [
        "definition",
        "steps"
      ],
      "code": "custom",
      "message": "Workflow must have at least START and END steps"
    }
  ]
}
```

**409** – Conflict - workflow with same ID and version already exists

Content-Type: `application/json`

```json
{
  "error": "Workflow definition with ID \"checkout-flow\" and version 1 already exists"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/definitions" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"workflowId\": \"string\",
  \"workflowName\": \"string\",
  \"description\": null,
  \"version\": 1,
  \"definition\": {
    \"steps\": [
      {
        \"stepId\": \"string\",
        \"stepName\": \"string\",
        \"stepType\": \"START\"
      }
    ],
    \"transitions\": [
      {
        \"transitionId\": \"string\",
        \"fromStepId\": \"string\",
        \"toStepId\": \"string\",
        \"trigger\": \"auto\",
        \"condition\": null,
        \"continueOnActivityFailure\": false,
        \"priority\": 0
      }
    ]
  },
  \"metadata\": null,
  \"enabled\": true,
  \"grantedFeatures\": null
}"
```

## DELETE `/workflows/definitions/{id}`

Delete workflow definition

Soft delete a workflow definition. Cannot be deleted if there are active workflow instances (RUNNING or WAITING status) using this definition.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Workflow definition deleted successfully

Content-Type: `application/json`

```json
{
  "message": "Workflow definition deleted successfully"
}
```

**404** – Workflow definition not found

Content-Type: `application/json`

```json
{
  "error": "Workflow definition not found"
}
```

**409** – Cannot delete - active workflow instances exist

Content-Type: `application/json`

```json
{
  "error": "Cannot delete workflow definition with 3 active instance(s)"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/workflows/definitions/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json"
```

## GET `/workflows/definitions/{id}`

Get workflow definition

Get a single workflow definition by ID. Returns the complete workflow structure including steps and transitions (with embedded activities).

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required. UUID for DB definitions, or "code:<workflowId>" for code-based definitions |

### Responses

**200** – Workflow definition found

Content-Type: `application/json`

```json
{
  "data": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "workflowId": "checkout-flow",
    "workflowName": "Checkout Flow",
    "description": "Complete checkout workflow for processing orders",
    "version": 1,
    "definition": {
      "steps": [
        {
          "stepId": "start",
          "stepName": "Start",
          "stepType": "START"
        },
        {
          "stepId": "validate-cart",
          "stepName": "Validate Cart",
          "stepType": "AUTOMATED",
          "description": "Validate cart items and check inventory"
        },
        {
          "stepId": "payment",
          "stepName": "Process Payment",
          "stepType": "AUTOMATED",
          "description": "Charge payment method",
          "retryPolicy": {
            "maxAttempts": 3,
            "backoffMs": 1000
          }
        },
        {
          "stepId": "end",
          "stepName": "End",
          "stepType": "END"
        }
      ],
      "transitions": [
        {
          "transitionId": "start-to-validate",
          "fromStepId": "start",
          "toStepId": "validate-cart",
          "trigger": "auto"
        },
        {
          "transitionId": "validate-to-payment",
          "fromStepId": "validate-cart",
          "toStepId": "payment",
          "trigger": "auto"
        },
        {
          "transitionId": "payment-to-end",
          "fromStepId": "payment",
          "toStepId": "end",
          "trigger": "auto",
          "activities": [
            {
              "activityName": "Send Order Confirmation",
              "activityType": "SEND_EMAIL",
              "config": {
                "to": "{{context.customerEmail}}",
                "subject": "Order Confirmation #{{context.orderId}}",
                "template": "order_confirmation"
              }
            }
          ]
        }
      ]
    },
    "enabled": true,
    "tenantId": "123e4567-e89b-12d3-a456-426614174001",
    "organizationId": "123e4567-e89b-12d3-a456-426614174002",
    "createdAt": "2025-12-08T10:00:00.000Z",
    "updatedAt": "2025-12-08T10:00:00.000Z"
  }
}
```

**404** – Workflow definition not found

Content-Type: `application/json`

```json
{
  "error": "Workflow definition not found"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/definitions/string" \
  -H "Accept: application/json"
```

## PUT `/workflows/definitions/{id}`

Update workflow definition

Update an existing workflow definition. Supports partial updates - only provided fields will be updated.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "definition": {
    "steps": [
      {
        "stepId": "start",
        "stepName": "Start",
        "stepType": "START"
      },
      {
        "stepId": "validate-cart",
        "stepName": "Validate Cart",
        "stepType": "AUTOMATED"
      },
      {
        "stepId": "payment",
        "stepName": "Process Payment",
        "stepType": "AUTOMATED"
      },
      {
        "stepId": "confirmation",
        "stepName": "Order Confirmation",
        "stepType": "AUTOMATED"
      },
      {
        "stepId": "end",
        "stepName": "End",
        "stepType": "END"
      }
    ],
    "transitions": [
      {
        "transitionId": "start-to-validate",
        "fromStepId": "start",
        "toStepId": "validate-cart",
        "trigger": "auto"
      },
      {
        "transitionId": "validate-to-payment",
        "fromStepId": "validate-cart",
        "toStepId": "payment",
        "trigger": "auto"
      },
      {
        "transitionId": "payment-to-confirmation",
        "fromStepId": "payment",
        "toStepId": "confirmation",
        "trigger": "auto"
      },
      {
        "transitionId": "confirmation-to-end",
        "fromStepId": "confirmation",
        "toStepId": "end",
        "trigger": "auto"
      }
    ]
  },
  "enabled": true
}
```

### Responses

**200** – Workflow definition updated successfully

Content-Type: `application/json`

```json
{
  "data": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "workflowId": "checkout-flow",
    "workflowName": "Checkout Flow",
    "description": "Complete checkout workflow for processing orders",
    "version": 1,
    "definition": {
      "steps": [
        {
          "stepId": "start",
          "stepName": "Start",
          "stepType": "START"
        },
        {
          "stepId": "validate-cart",
          "stepName": "Validate Cart",
          "stepType": "AUTOMATED"
        },
        {
          "stepId": "payment",
          "stepName": "Process Payment",
          "stepType": "AUTOMATED"
        },
        {
          "stepId": "confirmation",
          "stepName": "Order Confirmation",
          "stepType": "AUTOMATED"
        },
        {
          "stepId": "end",
          "stepName": "End",
          "stepType": "END"
        }
      ],
      "transitions": [
        {
          "transitionId": "start-to-validate",
          "fromStepId": "start",
          "toStepId": "validate-cart",
          "trigger": "auto"
        },
        {
          "transitionId": "validate-to-payment",
          "fromStepId": "validate-cart",
          "toStepId": "payment",
          "trigger": "auto"
        },
        {
          "transitionId": "payment-to-confirmation",
          "fromStepId": "payment",
          "toStepId": "confirmation",
          "trigger": "auto"
        },
        {
          "transitionId": "confirmation-to-end",
          "fromStepId": "confirmation",
          "toStepId": "end",
          "trigger": "auto"
        }
      ]
    },
    "enabled": true,
    "tenantId": "123e4567-e89b-12d3-a456-426614174001",
    "organizationId": "123e4567-e89b-12d3-a456-426614174002",
    "createdAt": "2025-12-08T10:00:00.000Z",
    "updatedAt": "2025-12-08T11:30:00.000Z"
  },
  "message": "Workflow definition updated successfully"
}
```

**400** – Validation error

Content-Type: `application/json`

```json
{
  "error": "Validation failed",
  "details": [
    {
      "path": [
        "definition"
      ],
      "code": "invalid_type",
      "message": "Invalid input: expected object, received string",
      "expected": "object",
      "got": "string"
    }
  ]
}
```

**404** – Workflow definition not found

Content-Type: `application/json`

```json
{
  "error": "Workflow definition not found"
}
```

**409** – Structural change refused while instances are still running (or optimistic-lock conflict). Publish a new version and apply the change there.

Content-Type: `application/json`

```json
{
  "error": "Structural changes require a new version while instances are still running",
  "code": "WORKFLOW_STRUCTURAL_EDIT_REQUIRES_NEW_VERSION",
  "definitionId": "123e4567-e89b-12d3-a456-426614174000",
  "activeInstanceCount": 3,
  "activeStatuses": [
    "RUNNING",
    "PAUSED",
    "WAITING_FOR_ACTIVITIES",
    "FORKED",
    "COMPENSATING"
  ],
  "changes": [
    {
      "kind": "transitionRetargeted",
      "id": "t_lz3k9_ab12cd34e"
    }
  ],
  "remedy": {
    "action": "createVersion",
    "method": "POST",
    "endpoint": "/api/workflows/definitions/123e4567-e89b-12d3-a456-426614174000/publish"
  }
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/workflows/definitions/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"description\": null,
  \"metadata\": null,
  \"effectiveFrom\": null,
  \"effectiveTo\": null,
  \"grantedFeatures\": null
}"
```

## GET `/workflows/definitions/{id}/callers`

List sub-workflow callers and breaking mappings

Returns parent definitions that invoke this workflow as a sub-workflow and the mappings its current port contract would break.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required. Workflow definition id |

### Responses

**200** – Callers

Content-Type: `application/json`

```json
{
  "callers": [
    {
      "workflowId": "order-flow",
      "version": 1,
      "stepId": "sub",
      "brokenMappings": [
        "input:orderId"
      ]
    }
  ]
}
```

**404** – Not found

Content-Type: `application/json`

```json
{
  "error": "Workflow definition not found"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/definitions/string/callers" \
  -H "Accept: application/json"
```

## GET `/workflows/definitions/{id}/context-schema`

Get the per-step context ledger

Computes what context paths are available at each step of the workflow definition, with type, presence (always/maybe), and producer source. Presence degrades to "maybe" at joins unless the entry arrives on every incoming route. Use ?stepId=<id> to narrow the response to one step.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required. UUID for DB definitions, or "code:<workflowId>" for code-based definitions |
| stepId | query | any | Optional. Narrow the response to this step (404 when the step does not exist) |

### Responses

**200** – Per-step context ledger

Content-Type: `application/json`

```json
{
  "steps": {
    "approve": {
      "entries": [
        {
          "path": "orderId",
          "type": "text",
          "presence": "always",
          "source": {
            "kind": "contextSchema",
            "label": "contextSchema.input"
          }
        },
        {
          "path": "discount",
          "type": "number",
          "presence": "maybe",
          "source": {
            "kind": "userTask",
            "stepId": "review",
            "label": "userTask:review"
          }
        }
      ]
    }
  }
}
```

**400** – Missing tenant context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow definition or requested step not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/definitions/string/context-schema" \
  -H "Accept: application/json"
```

## POST `/workflows/definitions/{id}/customize`

Customize code-based workflow definition

Creates a DB override for a code-based workflow definition, seeded from the current code registry values. The id param must be of the form "code:<workflowId>".

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required. Must be of the form "code:<workflowId>" |

### Responses

**200** – Workflow definition customized successfully

Content-Type: `application/json`

```json
{
  "data": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "workflowId": "workflows.simple-approval",
    "workflowName": "Simple Approval Workflow",
    "source": "code_override"
  },
  "message": "Workflow definition customized successfully"
}
```

**400** – Not a code-based id

Content-Type: `application/json`

```json
{
  "error": "Customize is only supported for code-based workflow definitions"
}
```

**404** – Code workflow not found

Content-Type: `application/json`

```json
{
  "error": "Workflow definition not found"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/definitions/string/customize" \
  -H "Accept: application/json"
```

## DELETE `/workflows/definitions/{id}/draft`

Discard the current user's draft

Deletes the authenticated user's draft for the workflow definition. Idempotent: discarding a non-existent draft succeeds.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required. Workflow definition UUID (server drafts exist only for saved definitions) |

### Responses

**200** – Draft discarded (or no draft existed)

Content-Type: `application/json`

```json
{
  "message": "string"
}
```

**400** – Invalid definition id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://hr.office.dac.systems/api/workflows/definitions/string/draft" \
  -H "Accept: application/json"
```

## GET `/workflows/definitions/{id}/draft`

Get the current user's draft

Returns the authenticated user's autosaved draft for the workflow definition. Drafts are private to the user who saved them.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required. Workflow definition UUID (server drafts exist only for saved definitions) |

### Responses

**200** – Draft found

Content-Type: `application/json`

```json
{
  "data": {
    "id": "string",
    "definitionId": null,
    "definition": {
      "steps": [
        {}
      ],
      "transitions": [
        {}
      ]
    },
    "metadata": null,
    "baseUpdatedAt": null,
    "createdAt": null,
    "updatedAt": null
  }
}
```

**400** – Invalid definition id (only saved definitions have server drafts)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – No draft exists for this user and definition

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/definitions/string/draft" \
  -H "Accept: application/json"
```

## PUT `/workflows/definitions/{id}/draft`

Save the current user's draft

Creates or updates the authenticated user's draft for the workflow definition. The definition payload is validated shape-only (steps and transitions must be arrays of objects) so a mid-edit, structurally incomplete graph still saves; full validation runs when the draft is promoted via the definition PUT. Draft saves never participate in the definition's optimistic lock.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required. Workflow definition UUID (server drafts exist only for saved definitions) |

### Request Body

Content-Type: `application/json`

```json
{
  "definition": {
    "steps": [
      {
        "stepId": "start",
        "stepName": "Start",
        "stepType": "START"
      }
    ],
    "transitions": []
  },
  "baseUpdatedAt": "2026-07-27T10:00:00.000Z"
}
```

### Responses

**200** – Draft saved

Content-Type: `application/json`

```json
{
  "data": {
    "id": "string",
    "definitionId": null,
    "definition": {
      "steps": [
        {}
      ],
      "transitions": [
        {}
      ]
    },
    "metadata": null,
    "baseUpdatedAt": null,
    "createdAt": null,
    "updatedAt": null
  },
  "message": "string"
}
```

**400** – Malformed draft payload or invalid definition id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow definition not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/workflows/definitions/string/draft" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"definition\": {
    \"steps\": [
      {}
    ],
    \"transitions\": [
      {}
    ]
  },
  \"metadata\": null,
  \"baseUpdatedAt\": null
}"
```

## POST `/workflows/definitions/{id}/publish`

Publish a new workflow definition version

Mints a frozen published version snapshotting the definition and its IO port contract. Returns affected sub-workflow callers; pass acknowledgeBreakingChanges=true to publish despite breaking changes.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required. Workflow definition id |

### Request Body

Content-Type: `application/json`

No example available for this content type.

### Responses

**200** – Published

Content-Type: `application/json`

```json
{
  "data": {
    "id": "…",
    "workflowId": "verify-policy",
    "version": 2,
    "lifecycle": "published"
  },
  "breakingChanges": [],
  "message": "Workflow definition published successfully"
}
```

**404** – Not found

Content-Type: `application/json`

```json
{
  "error": "Workflow definition not found"
}
```

**409** – Breaking changes not acknowledged

Content-Type: `application/json`

```json
{
  "error": "Publishing would break existing sub-workflow mappings",
  "breakingChanges": [
    {
      "workflowId": "order-flow",
      "version": 1,
      "stepId": "sub",
      "brokenMappings": [
        "input:orderId"
      ]
    }
  ]
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/definitions/string/publish" \
  -H "Accept: application/json"
```

## POST `/workflows/definitions/{id}/reset-to-code`

Reset workflow definition to code version

Deletes the DB override for a code-based workflow definition, reverting it to the original code registry version. Cannot be reset if there are active instances.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Workflow definition reset to code version

Content-Type: `application/json`

```json
{
  "data": {
    "id": "code:checkout-flow",
    "workflowId": "checkout-flow",
    "workflowName": "Checkout Flow",
    "description": "Code-defined checkout workflow",
    "version": 1,
    "source": "code",
    "isCodeBased": true
  },
  "message": "Workflow definition reset to code version"
}
```

**400** – Definition is not a code-based override

Content-Type: `application/json`

```json
{
  "error": "This workflow definition is not a code-based override and cannot be reset"
}
```

**404** – Workflow definition not found

Content-Type: `application/json`

```json
{
  "error": "Workflow definition not found"
}
```

**409** – Cannot reset - active workflow instances exist

Content-Type: `application/json`

```json
{
  "error": "Cannot reset workflow definition with 3 active instance(s)"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/definitions/00000000-0000-4000-8000-000000000000/reset-to-code" \
  -H "Accept: application/json"
```

## POST `/workflows/definitions/{id}/test-step`

Dry-run one activity config with mock-first semantics

Interpolates the supplied activity config against the sample context (plus a synthetic workflow scope and the server env allowlist) and runs the activity type's would-do mock. Never executes the real activity. Activity types that declare no mock or opt out with "refuse" respond 200 with a structured refusal the editor renders.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required. UUID for DB definitions, or "code:<workflowId>" for code-based definitions |

### Request Body

Content-Type: `application/json`

```json
{
  "stepId": "notify",
  "activityType": "SEND_EMAIL",
  "config": {
    "to": "{{context.customerEmail}}",
    "subject": "Order {{context.orderId}} approved"
  },
  "context": {
    "customerEmail": "jane@example.com",
    "orderId": "ORD-42"
  }
}
```

### Responses

**200** – Simulated mock output, or a structured refusal when the type cannot be simulated

Content-Type: `application/json`

```json
{
  "simulated": true,
  "activityType": "SEND_EMAIL",
  "output": {
    "sent": false,
    "simulated": true,
    "wouldSendTo": "jane@example.com",
    "subject": "Order ORD-42 approved"
  },
  "interpolatedConfig": {
    "to": "jane@example.com",
    "subject": "Order ORD-42 approved"
  }
}
```

**400** – Missing tenant context or malformed payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow definition or activity type not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/definitions/string/test-step" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"activityType\": \"string\",
  \"config\": {}
}"
```

## POST `/workflows/definitions/generate`

Author a draft workflow definition from natural language

Runs the propose-only workflows.workflow_author agent against this installation's activity, command, function and event catalogs and returns a draft definition plus the Problems-panel evaluation for it. Persists nothing: the draft is returned to the Studio for a human to apply, save and enable. Refusals ("unavailable" when no AI provider or agent registry is present, "failed" when the model call errored, "malformedResponse"/"schemaError" when the output is not a workflow definition) respond 200 with a structured body.

**Tags:** Workflows

### Request Body

Content-Type: `application/json`

```json
{
  "prompt": "string"
}
```

### Responses

**200** – The generated draft with its Problems-panel evaluation, or a structured refusal

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid request body

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Generation failed unexpectedly

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/definitions/generate" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"prompt\": \"string\"
}"
```

## GET `/workflows/endpoints`

List endpoints available to CALL_API activities

Returns a trimmed projection of the OpenAPI surface (path, method, summary, tag, parameters, and declared request/response schemas) consumed by the CALL_API endpoint picker. Endpoints outside this catalog can still be authored as free text. Responses without a declared schema omit responseSchema so consumers degrade to unknown typing.

**Tags:** Workflows

### Responses

**200** – Endpoint catalog projected from the OpenAPI surface

Content-Type: `application/json`

```json
{
  "items": [
    {
      "path": "string",
      "method": "string",
      "summary": "string",
      "tag": "string",
      "params": [
        {
          "name": "string",
          "in": "path",
          "required": true,
          "type": "string"
        }
      ],
      "hasRequestSchema": true
    }
  ]
}
```

**400** – Missing tenant context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/endpoints" \
  -H "Accept: application/json"
```

## GET `/workflows/events`

List all workflow events

Get a paginated list of all workflow events with filtering options

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| eventType | query | any | Optional |
| workflowInstanceId | query | any | Optional |
| userId | query | any | Optional |
| occurredAtFrom | query | any | Optional |
| occurredAtTo | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – List of workflow events

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
      "stepInstanceId": null,
      "eventType": "string",
      "occurredAt": "string",
      "userId": null,
      "workflowInstance": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/events?page=1&pageSize=50&sortField=occurredAt&sortDir=desc" \
  -H "Accept: application/json"
```

## GET `/workflows/events/{id}`

Get workflow event by ID

Get detailed information about a specific workflow event

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Workflow event details

Content-Type: `application/json`

```json
{
  "id": "string",
  "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
  "stepInstanceId": null,
  "eventType": "string",
  "occurredAt": "string",
  "userId": null,
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "workflowInstance": null
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow event not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/events/:id" \
  -H "Accept: application/json"
```

## GET `/workflows/functions`

List registered workflow function descriptors for EXECUTE_FUNCTION activities

Returns the registered workflow function descriptors consumed by the EXECUTE_FUNCTION function picker. Functions outside this list can still be authored but fail at runtime when no matching workflowFunction:<name> DI registration exists.

**Tags:** Workflows

### Responses

**200** – Registered workflow function descriptors

Content-Type: `application/json`

```json
{
  "items": [
    {
      "name": "string"
    }
  ]
}
```

**400** – Missing tenant context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/functions" \
  -H "Accept: application/json"
```

## GET `/workflows/grantable-features`

List grantable execution features

Declared ACL feature ids/titles intersected with the calling user’s own current grants (wildcard-aware). Drives the definition editor’s execution-permissions picker; gated by workflows.definitions.grant_features.

**Tags:** Workflows

**Requires authentication.**

### Responses

**200** – Grantable feature catalog

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "title": "string",
      "module": "string"
    }
  ]
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Missing workflows.definitions.grant_features

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/grantable-features" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/workflows/instances`

List workflow instances

Get a list of workflow instances with optional filters. Supports pagination and filtering by status, workflowId, correlationKey, etc.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| workflowId | query | any | Optional |
| status | query | any | Optional |
| outcome | query | any | Optional. Filter by the run VERDICT, independently of status. Comma-separated for several (e.g. partial_failure,failure). An unknown value is a 400. |
| correlationKey | query | any | Optional |
| entityType | query | any | Optional |
| entityId | query | any | Optional |
| parentInstanceId | query | any | Optional. Return only direct sub-workflow children of this parent instance. |
| hasParent | query | any | Optional. false = only top-level/standalone instances; true = only sub-workflow children. Ignored when parentInstanceId is set. |
| attention | query | any | Optional. true = only instances parked by a failure-queue error directive; false = only instances without an attention marker. |
| startedFrom | query | any | Optional. Lower bound on startedAt. A calendar day (YYYY-MM-DD) is taken from 00:00:00.000Z; a full ISO timestamp is taken verbatim. |
| startedTo | query | any | Optional. Upper bound on startedAt, inclusive. A calendar day (YYYY-MM-DD) covers the whole day up to 23:59:59.999Z. |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – List of workflow instances

Content-Type: `application/json`

```json
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "definitionId": "00000000-0000-4000-8000-000000000000",
      "workflowId": "string",
      "version": 1,
      "status": "RUNNING",
      "outcome": null,
      "currentStepId": "string",
      "correlationKey": null,
      "metadata": null,
      "startedAt": "string",
      "completedAt": null,
      "pausedAt": null,
      "cancelledAt": null,
      "errorMessage": null,
      "errorDetails": null,
      "pendingTransition": null,
      "retryCount": 1,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdAt": "string",
      "updatedAt": "string",
      "deletedAt": null
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 1,
    "offset": 1,
    "hasMore": true
  }
}
```

**400** – Invalid date range or unknown outcome

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/instances?limit=50&offset=0" \
  -H "Accept: application/json"
```

## POST `/workflows/instances`

Start workflow instance

Start a new workflow instance from a workflow definition. The workflow will execute immediately.

**Tags:** Workflows

### Request Body

Content-Type: `application/json`

```json
{
  "workflowId": "string"
}
```

### Responses

**201** – Workflow started successfully

Content-Type: `application/json`

```json
{
  "data": {
    "instance": {
      "id": "00000000-0000-4000-8000-000000000000",
      "definitionId": "00000000-0000-4000-8000-000000000000",
      "workflowId": "string",
      "version": 1,
      "status": "RUNNING",
      "outcome": null,
      "currentStepId": "string",
      "correlationKey": null,
      "metadata": null,
      "startedAt": "string",
      "completedAt": null,
      "pausedAt": null,
      "cancelledAt": null,
      "errorMessage": null,
      "errorDetails": null,
      "pendingTransition": null,
      "retryCount": 1,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdAt": "string",
      "updatedAt": "string",
      "deletedAt": null
    },
    "execution": {
      "status": "RUNNING",
      "currentStep": "string",
      "message": "string"
    }
  },
  "message": "string"
}
```

**400** – Bad request - Validation failed or definition disabled/invalid

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden - Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow definition not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/instances" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"workflowId\": \"string\"
}"
```

## GET `/workflows/instances/{id}`

Get workflow instance

Get detailed information about a specific workflow instance including current state, context, and execution status.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Workflow instance details

Content-Type: `application/json`

```json
{
  "data": {
    "id": "00000000-0000-4000-8000-000000000000",
    "definitionId": "00000000-0000-4000-8000-000000000000",
    "workflowId": "string",
    "version": 1,
    "status": "RUNNING",
    "outcome": null,
    "currentStepId": "string",
    "correlationKey": null,
    "metadata": null,
    "startedAt": "string",
    "completedAt": null,
    "pausedAt": null,
    "cancelledAt": null,
    "errorMessage": null,
    "errorDetails": null,
    "pendingTransition": null,
    "retryCount": 1,
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "createdAt": "string",
    "updatedAt": "string",
    "deletedAt": null
  }
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow instance not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/instances/:id" \
  -H "Accept: application/json"
```

## POST `/workflows/instances/{id}/advance`

Manually advance workflow to next step

Manually advance a workflow instance to the next step. Useful for manual progression, step-by-step testing, user-triggered transitions, and approval flows. Validates transitions and auto-progresses if possible.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Workflow advanced successfully

Content-Type: `application/json`

```json
{
  "data": {
    "instance": {
      "id": "00000000-0000-4000-8000-000000000000",
      "status": "string",
      "currentStepId": null,
      "previousStepId": null,
      "transitionFired": null
    }
  },
  "message": "string"
}
```

**400** – Invalid request, no valid transitions, or workflow already completed/cancelled/failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow instance not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/instances/:id/advance" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/workflows/instances/{id}/cancel`

Cancel workflow instance

Cancel a running or paused workflow instance. The workflow will be marked as CANCELLED and will not execute further.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Workflow cancelled successfully

Content-Type: `application/json`

```json
{
  "data": {
    "id": "00000000-0000-4000-8000-000000000000",
    "definitionId": "00000000-0000-4000-8000-000000000000",
    "workflowId": "string",
    "version": 1,
    "status": "RUNNING",
    "outcome": null,
    "currentStepId": "string",
    "correlationKey": null,
    "metadata": null,
    "startedAt": "string",
    "completedAt": null,
    "pausedAt": null,
    "cancelledAt": null,
    "errorMessage": null,
    "errorDetails": null,
    "pendingTransition": null,
    "retryCount": 1,
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "createdAt": "string",
    "updatedAt": "string",
    "deletedAt": null
  },
  "message": "string"
}
```

**400** – Bad request - Workflow cannot be cancelled in current status

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden - Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow instance not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/instances/:id/cancel" \
  -H "Accept: application/json"
```

## PATCH `/workflows/instances/{id}/context`

Merge a context patch and wake condition waiters

Shallow-merges a partial context patch into a RUNNING/PAUSED/FORKED workflow instance, then re-evaluates every WAIT_FOR_CONDITION waiter in that instance and resumes the ones whose predicate now holds. Reserved engine keys are rejected. Requires the workflows.instances.update_context feature.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "context": {}
}
```

### Responses

**200** – Context merged; woken lists the resumed step ids

Content-Type: `application/json`

```json
{
  "ok": true,
  "instanceId": "string",
  "woken": [
    "string"
  ]
}
```

**400** – Invalid body or reserved context key

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Instance not found in the caller scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Instance not updatable or stale version

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://hr.office.dac.systems/api/workflows/instances/:id/context" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"context\": {}
}"
```

## GET `/workflows/instances/{id}/events`

Get workflow instance events

Get a chronological list of events for a workflow instance. Events track all state changes, transitions, and activities.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| eventType | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – List of workflow events

Content-Type: `application/json`

```json
{
  "data": [
    {
      "id": "string",
      "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
      "stepInstanceId": null,
      "eventType": "string",
      "occurredAt": "string",
      "userId": null,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000"
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 1,
    "offset": 1,
    "hasMore": true
  }
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow instance not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/instances/:id/events?limit=100&offset=0" \
  -H "Accept: application/json"
```

## POST `/workflows/instances/{id}/rerun-step`

Rerun a workflow instance from a step

Replay a FAILED or PAUSED instance from one of its executed steps, optionally with an edited context patch. The previous attempt keeps its terminal step-instance row; the replay gets a fresh one, and the rerun is audited as a STEP_RERUN workflow event.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "stepId": "string"
}
```

### Responses

**200** – Rerun started

Content-Type: `application/json`

```json
{
  "data": {
    "instance": {
      "id": "00000000-0000-4000-8000-000000000000",
      "definitionId": "00000000-0000-4000-8000-000000000000",
      "workflowId": "string",
      "version": 1,
      "status": "RUNNING",
      "outcome": null,
      "currentStepId": "string",
      "correlationKey": null,
      "metadata": null,
      "startedAt": "string",
      "completedAt": null,
      "pausedAt": null,
      "cancelledAt": null,
      "errorMessage": null,
      "errorDetails": null,
      "pendingTransition": null,
      "retryCount": 1,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdAt": "string",
      "updatedAt": "string",
      "deletedAt": null
    },
    "editedContextDiff": {}
  },
  "message": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow instance not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – The step cannot be rerun (still parked, changed type, or the instance is not rerunnable)

Content-Type: `application/json`

```json
{
  "error": "string",
  "code": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/instances/:id/rerun-step" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"stepId\": \"string\"
}"
```

## POST `/workflows/instances/{id}/retry`

Retry failed workflow instance

Retry a failed workflow instance from its current step. The workflow will be reset to RUNNING status and execution will continue.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Workflow retry initiated successfully

Content-Type: `application/json`

```json
{
  "data": {
    "instance": {
      "id": "00000000-0000-4000-8000-000000000000",
      "definitionId": "00000000-0000-4000-8000-000000000000",
      "workflowId": "string",
      "version": 1,
      "status": "RUNNING",
      "outcome": null,
      "currentStepId": "string",
      "correlationKey": null,
      "metadata": null,
      "startedAt": "string",
      "completedAt": null,
      "pausedAt": null,
      "cancelledAt": null,
      "errorMessage": null,
      "errorDetails": null,
      "pendingTransition": null,
      "retryCount": 1,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdAt": "string",
      "updatedAt": "string",
      "deletedAt": null
    },
    "execution": {
      "status": "RUNNING",
      "currentStep": "string",
      "events": [
        {
          "eventType": "string",
          "occurredAt": "string"
        }
      ],
      "executionTime": 1
    }
  },
  "message": "string"
}
```

**400** – Bad request - Workflow cannot be retried in its current status or with its current outcome, or execution error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden - Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow instance not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/instances/:id/retry" \
  -H "Accept: application/json"
```

## POST `/workflows/instances/{id}/signal`

Send signal to specific workflow

Sends a signal to a specific workflow instance waiting for a signal. The workflow must be in PAUSED status and waiting for the specified signal.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "signalName": "string"
}
```

### Responses

**200** – Signal sent successfully

Content-Type: `application/json`

```json
{
  "success": true,
  "message": "string"
}
```

**400** – Invalid request body or signal name mismatch

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Instance or definition not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Workflow not paused or not waiting for signal

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error or transition failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/instances/:id/signal" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"signalName\": \"string\"
}"
```

## POST `/workflows/instances/{id}/step-through`

Continue one step, or stop stepping

Releases exactly the step the instance is currently parked on and resumes execution, or clears step-through so the run finishes on its own. To abort the run, use the cancel endpoint.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "action": "continue"
}
```

### Responses

**200** – The run advanced

Content-Type: `application/json`

```json
{
  "data": {
    "instanceId": "string",
    "status": "string",
    "currentStep": "string",
    "stepThrough": null
  }
}
```

**400** – Not a step-through run, or malformed payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow instance not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/instances/00000000-0000-4000-8000-000000000000/step-through" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"action\": \"continue\"
}"
```

## GET `/workflows/instances/{id}/steps`

Get workflow instance step executions

List the step executions recorded for a workflow instance, including per-step input, output, error payload, duration and attempt count.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – List of step executions

Content-Type: `application/json`

```json
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
      "branchInstanceId": null,
      "stepId": "string",
      "stepName": "string",
      "stepType": "string",
      "status": "string",
      "inputData": null,
      "outputData": null,
      "errorData": null,
      "enteredAt": null,
      "exitedAt": null,
      "executionTimeMs": null,
      "retryCount": 1
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 1,
    "offset": 1,
    "hasMore": true
  }
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow instance not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/instances/:id/steps?limit=100&offset=0" \
  -H "Accept: application/json"
```

## GET `/workflows/instances/{id}/would-do`

Get a dry run's "Would do" report

List every side effect a dry run suppressed, in execution order: simulated activities with their would-do output, activity types that refused simulation and stopped the run, user tasks that were not raised, and business rules whose action arm was withheld. A real (non-dry) run answers with an empty report.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – The would-do report

Content-Type: `application/json`

```json
{
  "isDryRun": true,
  "entries": [
    {
      "id": "string",
      "kind": "activity",
      "severity": "error",
      "stepId": null,
      "occurredAt": "string",
      "subject": "string",
      "detail": {}
    }
  ],
  "stoppedByRefusal": true,
  "pagination": {
    "total": 1,
    "limit": 1,
    "offset": 1,
    "hasMore": true
  }
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow instance not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/instances/:id/would-do" \
  -H "Accept: application/json"
```

## POST `/workflows/instances/bulk-replay`

Bulk retry or cancel workflow instances

Queue a bulk retry or cancel over the failure queue. Work runs in a queue worker and reports through the progress module; the response returns the progress job id immediately.

**Tags:** Workflows

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "action": "retry",
  "ids": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

### Responses

**202** – Bulk replay queued

Content-Type: `application/json`

```json
{
  "ok": true,
  "progressJobId": null,
  "message": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "ok": true,
  "progressJobId": null,
  "message": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "ok": true,
  "progressJobId": null,
  "message": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "ok": true,
  "progressJobId": null,
  "message": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "ok": true,
  "progressJobId": null,
  "message": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/instances/bulk-replay" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"action\": \"retry\",
  \"ids\": [
    \"00000000-0000-4000-8000-000000000000\"
  ]
}"
```

## GET `/workflows/instances/failure-queue`

List the workflow failure queue

Triage list of instances parked by a failure-queue error directive together with FAILED instances, plus normalized error groups so a batch of similar failures reads as one row.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| workflowId | query | any | Optional |
| errorGroup | query | any | Optional. Narrow the list to one normalized error group key. |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – Failure queue

Content-Type: `application/json`

```json
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "definitionId": "00000000-0000-4000-8000-000000000000",
      "workflowId": "string",
      "version": 1,
      "status": "RUNNING",
      "outcome": null,
      "currentStepId": "string",
      "correlationKey": null,
      "metadata": null,
      "startedAt": "string",
      "completedAt": null,
      "pausedAt": null,
      "cancelledAt": null,
      "errorMessage": null,
      "errorDetails": null,
      "pendingTransition": null,
      "retryCount": 1,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdAt": "string",
      "updatedAt": "string",
      "deletedAt": null
    }
  ],
  "groups": [
    {
      "key": "string",
      "label": "string",
      "count": 1,
      "workflowIds": [
        "string"
      ],
      "instanceIds": [
        "00000000-0000-4000-8000-000000000000"
      ]
    }
  ],
  "grouping": {
    "scannedCount": 1,
    "scanLimit": 1,
    "truncated": true
  },
  "pagination": {
    "total": 1,
    "limit": 1,
    "offset": 1,
    "hasMore": true
  }
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/instances/failure-queue?limit=50&offset=0" \
  -H "Accept: application/json"
```

## POST `/workflows/instances/validate-start`

Validate if workflow can be started

Evaluates pre-conditions defined on the START step and returns validation errors with localized messages if any fail. Returns canStart: true/false with details.

**Tags:** Workflows

### Request Body

Content-Type: `application/json`

```json
{
  "workflowId": "string"
}
```

### Responses

**200** – Validation result (canStart, errors, validatedRules)

Content-Type: `application/json`

```json
{
  "canStart": true,
  "workflowId": "string"
}
```

**400** – Invalid request body or missing context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/instances/validate-start" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"workflowId\": \"string\"
}"
```

## GET `/workflows/metrics/definitions`

Read per-definition workflow operational KPIs

Batch KPI read for the named workflow ids over a sliding window: runs started, terminal runs by outcome, success rate, run-duration p50/p95 and task SLA hit-rate. Dry runs are excluded from every figure. Served from the precomputed rollup when one is fresh, recomputed live otherwise; each item reports which.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| window | query | any | Optional. Sliding window length. |
| workflowIds | query | any | Required. Comma-separated logical workflow ids, at most 50 per request. |

### Responses

**200** – Per-definition metrics

Content-Type: `application/json`

```json
{
  "data": [
    {
      "workflowId": "string",
      "window": "24h",
      "windowStart": "string",
      "windowEnd": "string",
      "computedAt": "string",
      "source": "rollup",
      "metrics": {
        "runsStarted": 1,
        "runsTerminal": 1,
        "runsCompleted": 1,
        "runsFailed": 1,
        "runsCancelled": 1,
        "successRate": null,
        "durationSampleCount": 1,
        "avgDurationMs": null,
        "p50DurationMs": null,
        "p95DurationMs": null,
        "tasksWithDeadline": 1,
        "tasksMetDeadline": 1,
        "taskSlaHitRate": null
      }
    }
  ]
}
```

**400** – Invalid query or missing tenant context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/metrics/definitions?window=7d&workflowIds=string" \
  -H "Accept: application/json"
```

## GET `/workflows/portal/tasks`

List the portal principal's tasks

Returns the customer-assigned tasks this portal principal may see. A portal admin additionally sees their company members' tasks but may not act on them. Tasks with no entity bindings are never portal-visible.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| status | query | any | Optional. Filter by status (comma-separated for multiple) |
| page | query | any | Optional. Page number |
| pageSize | query | any | Optional. Rows per page (max 100) |

### Responses

**200** – Portal task list

Content-Type: `application/json`

```json
{
  "ok": true,
  "tasks": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
      "stepInstanceId": "00000000-0000-4000-8000-000000000000",
      "branchInstanceId": null,
      "taskName": "string",
      "description": null,
      "status": "PENDING",
      "formSchema": null,
      "formData": null,
      "assignedTo": null,
      "assigneeKind": "user",
      "assignedToRoles": null,
      "entityTypes": null,
      "claimedBy": null,
      "claimedAt": null,
      "dueDate": null,
      "escalatedAt": null,
      "escalatedTo": null,
      "completedBy": null,
      "completedAt": null,
      "comments": null,
      "reassignedBy": null,
      "reassignedAt": null,
      "reassignReason": null,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdAt": "string",
      "updatedAt": "string",
      "kind": "user_task",
      "proposalId": null,
      "priority": null,
      "entityBindings": null
    }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 1,
    "total": 1,
    "totalPages": 1
  }
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**403** – Insufficient permissions or no company association

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/portal/tasks?page=1&pageSize=25" \
  -H "Accept: application/json"
```

## GET `/workflows/portal/tasks/{id}`

Read one portal task

Returns a single customer-assigned task the portal principal owns. Every refusal — foreign tenant, another customer, an unbound task, a nonexistent id — is the same 404.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Portal task

Content-Type: `application/json`

```json
{
  "ok": true,
  "task": {
    "id": "00000000-0000-4000-8000-000000000000",
    "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
    "stepInstanceId": "00000000-0000-4000-8000-000000000000",
    "branchInstanceId": null,
    "taskName": "string",
    "description": null,
    "status": "PENDING",
    "formSchema": null,
    "formData": null,
    "assignedTo": null,
    "assigneeKind": "user",
    "assignedToRoles": null,
    "entityTypes": null,
    "claimedBy": null,
    "claimedAt": null,
    "dueDate": null,
    "escalatedAt": null,
    "escalatedTo": null,
    "completedBy": null,
    "completedAt": null,
    "comments": null,
    "reassignedBy": null,
    "reassignedAt": null,
    "reassignReason": null,
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "createdAt": "string",
    "updatedAt": "string",
    "kind": "user_task",
    "proposalId": null,
    "priority": null,
    "entityBindings": null
  },
  "decisions": [
    {
      "id": "string",
      "label": "string",
      "transitionId": "string"
    }
  ],
  "formKey": null,
  "canComplete": true
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**403** – Insufficient permissions or no company association

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**404** – Task not found

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/portal/tasks/:id" \
  -H "Accept: application/json"
```

## POST `/workflows/portal/tasks/{id}/complete`

Complete a portal task

Completes a customer-assigned task and resumes the workflow. Only the assignee may complete; a portal admin who can read a company member's task is refused.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "formData": {}
}
```

### Responses

**200** – Task completed

Content-Type: `application/json`

```json
{
  "ok": true,
  "task": null
}
```

**400** – Invalid request body or form data

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**401** – Authentication required

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**403** – Insufficient permissions or no company association

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**404** – Task not found or not actionable by this principal

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/portal/tasks/:id/complete" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"formData\": {}
}"
```

## GET `/workflows/rule-usage`

List workflows referencing a business rule

Returns the workflow definitions whose START pre-conditions or transition pre/post-conditions reference the given business rule.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| ruleId | query | any | Required |

### Responses

**200** – Usage references

Content-Type: `application/json`

```json
{
  "ruleId": "ORDER_ABOVE_LIMIT",
  "workflowCount": 1,
  "references": [
    {
      "workflowId": "order-approval",
      "version": 2,
      "kind": "transitionPreCondition",
      "locationId": "e_start_review"
    }
  ]
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "Invalid query parameters"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "Unauthorized"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/rule-usage?ruleId=string" \
  -H "Accept: application/json"
```

## POST `/workflows/signals`

Send signal to workflows by correlation key

Sends a signal to all workflow instances waiting for the specified signal that match the correlation key. Returns the count of workflows that received the signal.

**Tags:** Workflows

### Request Body

Content-Type: `application/json`

```json
{
  "correlationKey": "string",
  "signalName": "string"
}
```

### Responses

**200** – Signal sent to matching workflows

Content-Type: `application/json`

```json
{
  "success": true,
  "message": "string",
  "count": 1
}
```

**400** – Missing tenant or organization context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/signals" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"correlationKey\": \"string\",
  \"signalName\": \"string\"
}"
```

## GET `/workflows/task-settings`

Get tenant task-permission settings

Returns whether the business-context task-permission model (spec §6.4) is enabled for the tenant. Defaults to true.

**Tags:** Workflows

**Requires authentication.**

### Responses

**200** – Current settings

Content-Type: `application/json`

```json
{
  "taskPermissionsBusinessContext": true
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/task-settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/workflows/task-settings`

Update tenant task-permission settings

Sets the tenant-scoped opt-out. Setting it to false restores the pre-6.4 READ filter on the task list, task detail and work inbox; it never affects claiming, completing, tenant/organization scoping or the portal task routes.

**Tags:** Workflows

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "taskPermissionsBusinessContext": true
}
```

### Responses

**200** – Updated settings

Content-Type: `application/json`

```json
{
  "taskPermissionsBusinessContext": true,
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Missing authentication

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Settings storage unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://hr.office.dac.systems/api/workflows/task-settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"taskPermissionsBusinessContext\": true
}"
```

## GET `/workflows/tasks`

List user tasks

Returns paginated list of user tasks with optional filtering by status, assignee, workflow instance, overdue, and myTasks flags.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| status | query | any | Optional. Filter by status (comma-separated for multiple: PENDING,IN_PROGRESS,COMPLETED,CANCELLED,ESCALATED) |
| assignedTo | query | any | Optional. Filter by assigned user ID |
| workflowInstanceId | query | any | Optional. Filter by workflow instance ID |
| overdue | query | any | Optional. Filter overdue tasks (true/false) |
| myTasks | query | any | Optional. Show only tasks assigned to or claimable by current user |
| limit | query | any | Optional. Number of results (max 100) |
| offset | query | any | Optional. Pagination offset |

### Responses

**200** – User tasks list with pagination

Content-Type: `application/json`

```json
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
      "stepInstanceId": "00000000-0000-4000-8000-000000000000",
      "branchInstanceId": null,
      "taskName": "string",
      "description": null,
      "status": "PENDING",
      "formSchema": null,
      "formData": null,
      "assignedTo": null,
      "assigneeKind": "user",
      "assignedToRoles": null,
      "entityTypes": null,
      "claimedBy": null,
      "claimedAt": null,
      "dueDate": null,
      "escalatedAt": null,
      "escalatedTo": null,
      "completedBy": null,
      "completedAt": null,
      "comments": null,
      "reassignedBy": null,
      "reassignedAt": null,
      "reassignReason": null,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdAt": "string",
      "updatedAt": "string",
      "kind": "user_task",
      "proposalId": null,
      "priority": null,
      "entityBindings": null
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 1,
    "offset": 1,
    "hasMore": true
  }
}
```

**400** – Invalid query parameters or missing tenant context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/tasks?limit=50&offset=0" \
  -H "Accept: application/json"
```

## GET `/workflows/tasks/{id}`

Get task details

Returns complete details of a user task by ID.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – User task details

Content-Type: `application/json`

```json
{
  "data": {
    "id": "00000000-0000-4000-8000-000000000000",
    "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
    "stepInstanceId": "00000000-0000-4000-8000-000000000000",
    "branchInstanceId": null,
    "taskName": "string",
    "description": null,
    "status": "PENDING",
    "formSchema": null,
    "formData": null,
    "assignedTo": null,
    "assigneeKind": "user",
    "assignedToRoles": null,
    "entityTypes": null,
    "claimedBy": null,
    "claimedAt": null,
    "dueDate": null,
    "escalatedAt": null,
    "escalatedTo": null,
    "completedBy": null,
    "completedAt": null,
    "comments": null,
    "reassignedBy": null,
    "reassignedAt": null,
    "reassignReason": null,
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "createdAt": "string",
    "updatedAt": "string",
    "kind": "user_task",
    "proposalId": null,
    "priority": null,
    "entityBindings": null,
    "stepId": null,
    "decisions": [
      {
        "id": "string",
        "label": "string",
        "transitionId": "string"
      }
    ],
    "canComplete": true,
    "canClaim": true,
    "canRelease": true,
    "canReassign": true,
    "actBlockedReason": null
  }
}
```

**400** – Missing tenant or organization context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – The task is bound to an entity type the caller may not view. Returned only to callers holding workflows.tasks.view_all (or a superadmin), who already see the row in a list view; everyone else receives the generic 404.

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Task not found, or not visible to the caller

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/tasks/:id" \
  -H "Accept: application/json"
```

## POST `/workflows/tasks/{id}/claim`

Claim a task from role queue

Allows a user to claim a task assigned to their role(s). Once claimed, the task moves to IN_PROGRESS status and is assigned to the claiming user.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Task claimed successfully

Content-Type: `application/json`

```json
{
  "data": {
    "id": "00000000-0000-4000-8000-000000000000",
    "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
    "stepInstanceId": "00000000-0000-4000-8000-000000000000",
    "branchInstanceId": null,
    "taskName": "string",
    "description": null,
    "status": "PENDING",
    "formSchema": null,
    "formData": null,
    "assignedTo": null,
    "assigneeKind": "user",
    "assignedToRoles": null,
    "entityTypes": null,
    "claimedBy": null,
    "claimedAt": null,
    "dueDate": null,
    "escalatedAt": null,
    "escalatedTo": null,
    "completedBy": null,
    "completedAt": null,
    "comments": null,
    "reassignedBy": null,
    "reassignedAt": null,
    "reassignReason": null,
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "createdAt": "string",
    "updatedAt": "string"
  },
  "message": "string"
}
```

**400** – Missing tenant or organization context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – The task is visible to the caller but is not theirs to act on — it has no assignee and no role queue, so it must be reassigned first (§6.4: administration widens seeing, never acting).

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Task not found, or not visible to the caller

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Task already claimed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/tasks/:id/claim" \
  -H "Accept: application/json"
```

## POST `/workflows/tasks/{id}/complete`

Complete a task with form data

Validates form data against task schema, updates task with completion data, merges form data into workflow context, and resumes workflow execution.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "formData": {}
}
```

### Responses

**200** – Task completed successfully

Content-Type: `application/json`

```json
{
  "data": {
    "id": "00000000-0000-4000-8000-000000000000",
    "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
    "stepInstanceId": "00000000-0000-4000-8000-000000000000",
    "branchInstanceId": null,
    "taskName": "string",
    "description": null,
    "status": "PENDING",
    "formSchema": null,
    "formData": null,
    "assignedTo": null,
    "assigneeKind": "user",
    "assignedToRoles": null,
    "entityTypes": null,
    "claimedBy": null,
    "claimedAt": null,
    "dueDate": null,
    "escalatedAt": null,
    "escalatedTo": null,
    "completedBy": null,
    "completedAt": null,
    "comments": null,
    "reassignedBy": null,
    "reassignedAt": null,
    "reassignReason": null,
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "createdAt": "string",
    "updatedAt": "string"
  },
  "message": "string"
}
```

**400** – Invalid request body, validation failed, or missing context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – The task is visible to the caller but is not theirs to act on — it has no assignee and no role queue, so it must be reassigned first (§6.4: administration widens seeing, never acting).

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Task not found, or not visible to the caller

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Task already completed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/tasks/:id/complete" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"formData\": {}
}"
```

## POST `/workflows/tasks/{id}/reassign`

Move a task to another assignee or role queue

Records who moved the task, when, and why (`reassignedBy` / `reassignedAt` / `reassignReason`) and logs a `USER_TASK_REASSIGNED` workflow event. Requires `workflows.tasks.reassign`. An in-flight claim is released and an IN_PROGRESS task returns to PENDING; the status vocabulary is unchanged. Honours the optimistic-lock header `x-om-ext-optimistic-lock-expected-updated-at`.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Task reassigned successfully

Content-Type: `application/json`

```json
{
  "data": {
    "id": "00000000-0000-4000-8000-000000000000",
    "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
    "stepInstanceId": "00000000-0000-4000-8000-000000000000",
    "branchInstanceId": null,
    "taskName": "string",
    "description": null,
    "status": "PENDING",
    "formSchema": null,
    "formData": null,
    "assignedTo": null,
    "assigneeKind": "user",
    "assignedToRoles": null,
    "entityTypes": null,
    "claimedBy": null,
    "claimedAt": null,
    "dueDate": null,
    "escalatedAt": null,
    "escalatedTo": null,
    "completedBy": null,
    "completedAt": null,
    "comments": null,
    "reassignedBy": null,
    "reassignedAt": null,
    "reassignReason": null,
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "createdAt": "string",
    "updatedAt": "string",
    "kind": "user_task",
    "proposalId": null,
    "priority": null,
    "entityBindings": null
  },
  "message": "string"
}
```

**400** – Missing tenant/organization context, or a body naming neither an assignee nor a role queue

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Visible to the caller but refused by the entity gate, with the blocking entity type named

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Task not found, or not visible to the caller

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Task is in a terminal status, or the record changed since it was loaded (optimistic lock)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/tasks/:id/reassign" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/workflows/tasks/{id}/unclaim`

Release a claimed task back to its role queue

Clears the caller's claim and returns the task to PENDING so another holder of the queued role can pick it up. Refused when the task is claimed by somebody else or is no longer in progress.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Task released successfully

Content-Type: `application/json`

```json
{
  "data": {
    "id": "00000000-0000-4000-8000-000000000000",
    "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
    "stepInstanceId": "00000000-0000-4000-8000-000000000000",
    "branchInstanceId": null,
    "taskName": "string",
    "description": null,
    "status": "PENDING",
    "formSchema": null,
    "formData": null,
    "assignedTo": null,
    "assigneeKind": "user",
    "assignedToRoles": null,
    "entityTypes": null,
    "claimedBy": null,
    "claimedAt": null,
    "dueDate": null,
    "escalatedAt": null,
    "escalatedTo": null,
    "completedBy": null,
    "completedAt": null,
    "comments": null,
    "reassignedBy": null,
    "reassignedAt": null,
    "reassignReason": null,
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "createdAt": "string",
    "updatedAt": "string"
  },
  "message": "string"
}
```

**400** – Missing tenant or organization context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – The task is visible to the caller but is not theirs to act on — it has no assignee and no role queue, so it must be reassigned first (§6.4: administration widens seeing, never acting).

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Task not found, not visible to the caller, or not claimed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Task claimed by another user

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/tasks/:id/unclaim" \
  -H "Accept: application/json"
```

## GET `/workflows/templates`

List seeded workflow templates

Returns the shipped workflow template gallery: metadata (id, i18n name/description keys, category, icon) plus the complete workflow definition each template seeds.

**Tags:** Workflows

### Responses

**200** – Available workflow templates

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "nameKey": "string",
      "descriptionKey": "string",
      "category": "string",
      "icon": "string",
      "definition": {
        "steps": [
          {
            "stepId": "string",
            "stepName": "string",
            "stepType": "START"
          }
        ],
        "transitions": [
          {
            "transitionId": "string",
            "fromStepId": "string",
            "toStepId": "string",
            "trigger": "auto",
            "condition": null,
            "continueOnActivityFailure": false,
            "priority": 0
          }
        ]
      }
    }
  ]
}
```

**400** – Missing tenant context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/templates" \
  -H "Accept: application/json"
```

## GET `/workflows/work-inbox`

List work items across every registered source

Merges every registered work-inbox source (workflow user tasks, plus any module that registers a provider) into one queue, filtered by kind/module/entity type/role/priority/status/overdue and ordered by priority, then due date, then age.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| kind | query | any | Optional. Filter by source kind (comma-separated) |
| module | query | any | Optional. Filter by owning module id (comma-separated) |
| entityType | query | any | Optional. Filter by bound entity type (comma-separated) |
| role | query | any | Optional. Filter by queued role (comma-separated) |
| priority | query | any | Optional. Filter by priority (comma-separated: extreme,high,medium,low) |
| status | query | any | Optional. Filter by status (comma-separated) |
| overdue | query | any | Optional. Only items past their due date |
| myWork | query | any | Optional. Only items assigned to, claimed by, or queued to a role of the caller |
| assignedTo | query | any | Optional. Filter by assignee |
| workflowInstanceId | query | any | Optional. Filter by workflow instance |
| limit | query | any | Optional. Number of results (max 100) |
| offset | query | any | Optional. Pagination offset |

### Responses

**200** – Merged work items with pagination

Content-Type: `application/json`

```json
{
  "data": [
    {
      "id": "string",
      "kind": "string",
      "moduleId": "string",
      "title": "string",
      "description": null,
      "status": "string",
      "priority": null,
      "dueDate": null,
      "overdue": true,
      "createdAt": "string",
      "updatedAt": "string",
      "assignedTo": null,
      "assignedToRoles": null,
      "claimedBy": null,
      "entityTypes": [
        "string"
      ],
      "entityBindings": [
        {
          "entityType": "string",
          "entityId": "string",
          "label": null
        }
      ],
      "detailHref": null,
      "actions": [
        {
          "id": "string",
          "labelKey": "string",
          "endpoint": "string",
          "appliesTo": "claimable"
        }
      ]
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 1,
    "offset": 1,
    "hasMore": true
  },
  "meta": {
    "kinds": [
      "string"
    ],
    "degradedKinds": [
      "string"
    ]
  }
}
```

**400** – Missing tenant context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://hr.office.dac.systems/api/workflows/work-inbox?limit=50&offset=0" \
  -H "Accept: application/json"
```

## POST `/workflows/work-inbox/next`

Claim the top claimable item from the work inbox

Walks the caller's claimable queue in inbox order and claims the first item it wins. The claim is a conditional update, so concurrent callers never receive the same item — a caller that loses the race moves on to the next candidate. Returns null data when nothing is claimable.

**Tags:** Workflows

### Responses

**200** – The claimed item, or null when nothing was claimable

Content-Type: `application/json`

```json
{
  "data": null,
  "message": "string"
}
```

**400** – Missing tenant or organization context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://hr.office.dac.systems/api/workflows/work-inbox/next" \
  -H "Accept: application/json"
```