# Home

Welcome to the intratool API documentation. This guide details the available endpoints and resources for integrating with the intratool platform.

## Introduction

Please refer to the **Introduction** section for essential topics before making API requests:

* [**General**](/introduction/general): Base URL, Tenant information, and Request requirements.
* [**Authorization**](/introduction/authorization): Learn how to authenticate and manage tokens.
* [**Query Manipulation**](/introduction/query-manipulation): How to filter and control results.
* [**Assignment and Targeting**](/introduction/assignment-and-targeting): Shared request fields and resolution rules for static and computed Targetables.
* [**Resource Capabilities**](/introduction/resource-capabilities): Definitions of reusable behavior declared by resource Model Definitions.
* [**Entity Permissions**](/introduction/entity-permissions): Resource-instance grant structure and Restricted Scope enforcement.
* [**System Variables**](/introduction/system-variables): Use dynamic values in automations and templates.
* [**Rich Text**](/introduction/rich-text): Use the canonical HTML structure for formatted text, links, shared content, files, media, and tables.
* [**Additional Attributes**](/introduction/additional-attributes): Work with configurable fields for supported entities.

## API Reference

The **API Reference** contains detailed documentation for all modules and endpoints. You can navigate through the specific sections in the sidebar, such as:

* [Infoboard](/api-reference/infoboard)
* [Manual](/api-reference/manual)
* [Contact Lists](/api-reference/contact-lists)
* [Tasks 2.0](/api-reference/tasks-2)
* [Calendar](/api-reference/calendar)
* [Filemanager](/api-reference/filemanager)
* [Forms](/api-reference/forms)
* [Chat](/api-reference/chat)
* [Surveys](/api-reference/surveys)
* [Legal](/api-reference/legal)
* and more.


# General

The intratool API is tenant-based. Each organization uses its own intratool instance, identified by the `{tenant}` subdomain.

## Base URL and Tenant

Send every API request to the base URL of the affected intratool instance:

`https://{tenant}.intratool.de`

**Tenant placeholder**

Replace `{tenant}` with the organization's subdomain. For an instance available at `my-company.intratool.de`, the API base URL is `https://my-company.intratool.de`.

## Request Headers

Send the `Accept: application/json` header with every API request so error and validation responses use JSON instead of HTML.

{% hint style="info" %}
Administration-only endpoints are identified in the affected API reference sections.
{% endhint %}


# Authorization

The intratool API uses OAuth 2.0 bearer tokens for authentication. API requests run with the permissions of the [User](/api-reference/users) associated with the supplied token.

A user can have multiple OAuth clients and personal access tokens. A token does not extend that user's module permissions, Entity Permissions, or other access rules.

## Authorization Flow

* [Get a Token](/introduction/authorization/get-token) describes how to create a personal access token.
* [Authorize](/introduction/authorization/authorize) describes the required request header.
* [Third-Party Login](/introduction/authorization/third-party-login) describes token-based browser login for third-party applications.


# Authorize

After [creating a personal access token](/introduction/authorization/get-token), send it as a bearer token in the `Authorization` header of every API request.

**Authorization header**

```http
Authorization: Bearer {accessToken}
```

Requests with an invalid token return a `403 Forbidden` response.


# Get a Token

Create a personal access token for the [User](/api-reference/users) whose permissions and data access the API client requires. The complete token is shown only once during creation.

## Personal Access Token

Personal access tokens authenticate API requests as their associated user.

1. Sign in as the user for whom the token is created.
2. Open `/profil/oauth/` to access the OAuth configuration.
3. Select **Token erstellen** to open the creation form.
4. Enter a name for the token and select **Erstellen**.
5. Copy the generated token and store it securely.

{% hint style="warning" %}
The complete token cannot be displayed again after leaving the creation view.
{% endhint %}


# Third-Party Login

Third-party applications can authenticate a [User](/api-reference/users) in a browser environment, for example to provide single sign-on on a shared device.

The login mechanism described here uses a token associated with the user to perform a browser login. The response sets the same cookies that a manual login would set, so the user becomes logged in on the device where the route was called.

This mechanism is also referred to as an auto-login URL.

## User Login Token

[UserLoginTokens](/api-reference/user-login-tokens) are short-lived, user-specific tokens for a one-time browser login.

<mark style="color:green;">`GET`</mark> `https://{tenant}.intratool.de/login/token/{userLoginToken}`

Calling this URL signs in the associated user and redirects the browser to the configured default route.

A `UserLoginToken` remains valid until it is used once or expires, whichever occurs first.

## Access Token

The long-lived access tokens used for [API authorization](/introduction/authorization/authorize) can be passed to the same login route:

<mark style="color:green;">`GET`</mark> `https://{tenant}.intratool.de/login/token/{accessToken}`

Calling this URL signs in the associated user and redirects the browser to the configured default route.

Using an access token for browser login does not revoke it. The token remains valid until its normal expiration.


# Query Manipulation

Most intratool API endpoints return a default query result with the resource's basic fields. Query parameters can filter records, include related data, select fields, and control result size or ordering.

Each resource endpoint determines which fields and relations are publicly available. Query manipulation changes the returned subset but does not bypass authorization, visibility, or Restricted Scope rules.

## Query Controls

* [Constraints](/introduction/query-manipulation/constraints) filter records by the existence or contents of relations.
* [Result Control](/introduction/query-manipulation/result-control) controls includes, selected fields, limits, sorting, relation counts, and computed fields.
* [Value Filters](/introduction/query-manipulation/value-filters) compare public field values against request values.


# Constraints

Constraints filter `GET` results by the existence or contents of relations. Unlike [Value Filters](/introduction/query-manipulation/value-filters), they operate on relation-level conditions instead of comparing only one field value.

All constraints use the `filter` query parameter and can be combined with value filters.

{% hint style="info" %}
Relation constraints and Value Filters can be combined in the same request.
{% endhint %}

## Syntax

* `{route}` - Endpoint from which records are retrieved.
* `?filter` - Query parameter containing the constraint.
* `[{constraint}]` - Constraint key documented below.
* `[{field}]` - Relation or relation field to which the constraint applies.
* `[{valueFilter}]` - Optional Value Filter for related records.
* `{value}` - Value required by the selected constraint or filter.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[{constraint}][{field}][{valueFilter}]={value}`

## Does Not Have

The `doesnt_have` constraint returns records that do not have the specified relation.

Pass `1` when no additional field condition is required.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[doesnt_have][{field}]={value}`

## Has

The `has` constraint returns records that have the specified relation.

Pass `1` when no additional field condition is required.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[has][{field}]={value}`

## Where One of

The `where_one_of` constraint returns records that have at least one of the specified relations.

Pass the relations as a comma-separated list. Value Filters can add field conditions for the affected relations.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[where_one_of][{fields}]={value}`


# Result Control

Result Control query parameters determine which public fields and relations the intratool API returns and how it orders, limits, or paginates the result. These controls do not bypass resource authorization, visibility, or Restricted Scope rules.

Each endpoint defines the fields, relations, computed properties, and pagination behavior it supports. Resource-specific query extensions are not part of the shared Result Control contract unless the affected API reference documents them explicitly.

## Query Parameters

| Key          | Type      | Default             | Description                                                                                                                               |
| ------------ | --------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `select`     | `string`  | Endpoint fields     | Comma-separated fields to return.                                                                                                         |
| `with_count` | `string`  | No counts           | Pipe-separated relations whose counts are returned.                                                                                       |
| `include`    | `string`  | Endpoint relations  | Pipe-separated relations to return.                                                                                                       |
| `filter`     | `object`  | `{}`                | Standard [Value Filters](/introduction/query-manipulation/value-filters) and [Constraints](/introduction/query-manipulation/constraints). |
| `sort`       | `string`  | Endpoint order      | Comma-separated fields used for ordering.                                                                                                 |
| `limit`      | `integer` | No additional limit | Maximum number of records.                                                                                                                |
| `page`       | `integer` | `1`                 | Page number for pagination-enabled endpoints.                                                                                             |
| `per_page`   | `integer` | `25`                | Page size for pagination-enabled endpoints.                                                                                               |
| `appends`    | `string`  | Endpoint appends    | Comma-separated computed fields to add.                                                                                                   |

## Includes

Use `include` to return related resources without additional API requests. The relation must be listed as public in the affected resource's Model Definition.

**Syntax**

* `{route}` - Endpoint from which records are retrieved.
* `?include` - Query parameter containing the relations.
* `{relations}` - Relation identifiers separated by `|`; use `.` for nested relations.

<mark style="color:green;">`GET`</mark> `/api/{route}?include={relations}`

### Relation Fields

Append `:` and comma-separated field keys to select fields on an included relation.

`?include={relation}:{field1},{field2}`

{% hint style="warning" %}
Include every key needed to resolve the relation. For example, a selected child record may need its primary and foreign keys for the relation to be returned correctly.
{% endhint %}

### Relation Options

Append relation options after the selected fields. Options use square brackets, are separated by commas, and are case-sensitive. A relation without field selection places the first option directly after `:`.

`?include={relation}:{field1},[{option}={value}]`

| Option       | Type      | Description                                                                                                             |
| ------------ | --------- | ----------------------------------------------------------------------------------------------------------------------- |
| `withCounts` | `string`  | Add counts for one or more related relations. Separate multiple relation names with `+`.                                |
| `order`      | `string`  | Order related records by one or more fields. Separate fields with `+` and prefix a field with `-` for descending order. |
| `limit`      | `integer` | Limit the number of related records.                                                                                    |
| `appends`    | `string`  | Add one or more computed fields to related records. Separate field names with `+`.                                      |
| `nestLevels` | `integer` | Repeat a self-referential relation for the specified number of additional nesting levels.                               |

**Combined example**

<mark style="color:green;">`GET`</mark> `/api/{route}?include=manualEntries:id,title,manual_chapter_id,[withCounts=seenUsers+readUsers],[order=-id],[limit=2],[appends=hash]`

**Self-referential example**

<mark style="color:green;">`GET`</mark> `/api/{route}?include=children:id,title,parent_id,[nestLevels=2]`

Relation options affect only supported, authorized relations. Invalid or unavailable relations are not made accessible by including them.

## Selects

Use `select` to restrict the response to specified public fields.

**Syntax**

* `{route}` - Endpoint from which records are retrieved.
* `?select` - Query parameter containing the fields.
* `{fields}` - Field keys separated by `,`.

<mark style="color:green;">`GET`</mark> `/api/{route}?select={fields}`

{% hint style="warning" %}
When combining `select` with `include`, include the field required to resolve each requested relation.
{% endhint %}

## Limiting

Use `limit` to control the maximum number of returned records.

**Syntax**

* `{route}` - Endpoint from which records are retrieved.
* `?limit` - Query parameter containing the maximum result count.
* `{value}` - Maximum number of records to return.

<mark style="color:green;">`GET`</mark> `/api/{route}?limit={value}`

For paginated endpoints with an exact count, `limit` also caps the reported total and can therefore reduce the last available page. Count-less endpoints still stop returning records after the limit is reached.

## Pagination

Pagination-enabled endpoints accept `page` and `per_page`. They default to page `1` with `25` records per page.

**Syntax**

* `{route}` - Pagination-enabled endpoint from which records are retrieved.
* `?page` - Query parameter containing the page number.
* `?per_page` - Query parameter containing the page size.

<mark style="color:green;">`GET`</mark> `/api/{route}?page=2&per_page=25`

Paginated responses contain `current_page`, `last_page`, `per_page`, `total`, `from`, `to`, and `data`. Endpoints that do not enable pagination ignore these shared pagination controls or document different behavior.

### Count-less Pagination

Some entity-selection and preview endpoints avoid an exact count. Their response keeps the standard pagination fields but returns `total` as `null`. While another page may exist, `last_page` is the next page number; otherwise it equals `current_page`. Clients can therefore test whether `last_page` is greater than `current_page` without treating it as an exact total page count.

## Sorting

Use `sort` to define the order of returned records.

**Syntax**

* `{route}` - Endpoint from which records are retrieved.
* `?sort` - Query parameter containing the sort fields.
* `{fields}` - Field keys separated by `,`; prefix a field with `-` for descending order.

<mark style="color:green;">`GET`</mark> `/api/{route}?sort={fields}`

For example, `?sort=-updated_at,title` sorts by `updated_at` descending and then by `title` ascending.

## Relation Counts

Use `with_count` to return the number of related records without returning those resource objects.

**Syntax**

* `{route}` - Endpoint from which records are retrieved.
* `?with_count` - Query parameter containing the relations.
* `{relations}` - Relation identifiers separated by `|`.

<mark style="color:green;">`GET`</mark> `/api/{route}?with_count={relations}`

Each count uses the snake-case relation name followed by `_count`. For example, `with_count=seenUsers|readUsers` adds `seen_users_count` and `read_users_count`.

## Appends

Use `appends` to add supported computed fields to API responses.

**Syntax**

* `{route}` - Endpoint from which records are retrieved.
* `?appends` - Query parameter containing the computed fields.
* `{fields}` - Computed field keys separated by `,`.

<mark style="color:green;">`GET`</mark> `/api/{route}?appends={fields}`

**Top-level example**

<mark style="color:green;">`GET`</mark> `/api/manual/entries/1?appends=hash`

Use the `appends` relation option for included relations. Top-level fields are separated by `,`, while relation-option fields are separated by `+`.

<mark style="color:green;">`GET`</mark> `/api/forms/1?include=formFields:[appends=field_validation_string]`

{% hint style="warning" %}
If an appended field depends on an attribute excluded by `select`, the appended output can be incomplete.
{% endhint %}

## Filters

The shared `filter` object accepts the field comparisons documented under [Value Filters](/introduction/query-manipulation/value-filters) and the relation conditions documented under [Constraints](/introduction/query-manipulation/constraints).

Resource-specific shortcut filters added on top of those shared operators are intentionally not part of the public API documentation yet.


# Value Filters

Value Filters restrict `GET` results by comparing public fields with request values. Filters use `AND` semantics when a request contains multiple conditions.

Filtering a related field adds a relation constraint. Public fields on the requested resource and its publicly accessible relations can be addressed with dot notation.

## Syntax

* `{route}` - Endpoint from which records are retrieved.
* `?filter` - Query parameter containing the field filters.
* `[{field}]` - Public field to which the filter applies.
* `[{filter}]` - Filter key documented below. Defaults to [`value`](#value) when omitted.
* `{value}` - Request value compared with the affected field.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[{field}][{filter}]={value}`

{% hint style="info" %}
Value Filters can address fields on related records and nested relations.

`?filter[user.role.department.title]=TestDepartment`

Every relation addressed by a filter is resolved and returned by the API.
{% endhint %}

{% hint style="warning" %}
Use `camelCase` for relation names and `snake_case` for field keys.
{% endhint %}

## Value

The `value` filter performs a partial comparison equivalent to `LIKE`. It returns records whose affected field value is contained in `{value}`.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[{field}][value]={value}`

## Is Value

The `is` filter performs an exact `=` comparison and returns records whose affected field equals `{value}`.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[{field}][is]={value}`

## Is Not Value

The `not` filter performs a `!=` comparison and returns records whose affected field does not equal `{value}`.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[{field}][not]={value}`

## Greater Than

The `gt` filter performs a `>` comparison and returns records whose affected field is greater than `{value}`.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[{field}][gt]={value}`

## Greater Than or Equal

The `gte` filter performs a `>=` comparison and returns records whose affected field is greater than or equal to `{value}`.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[{field}][gte]={value}`

## Less Than

The `lt` filter performs a `<` comparison and returns records whose affected field is less than `{value}`.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[{field}][lt]={value}`

## Less Than or Equal

The `lte` filter performs a `<=` comparison and returns records whose affected field is less than or equal to `{value}`.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[{field}][lte]={value}`

## Null Checks

The `is_null` filter accepts `0` or `1`.

Pass `1` to return records whose affected field is `null`.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[{field}][is_null]={1}`

Pass `0` to return records whose affected field is not `null`.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[{field}][is_null]={0}`

## In

The `in` filter accepts one or more comma-separated values and returns records whose affected field equals any supplied value.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[{field}][in]={value}`

## Not In

The `nin` filter accepts multiple comma-separated values and returns records whose affected field does not equal any supplied value.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[{field}][nin]={value}`

## Period

The `period` filter accepts two comma-separated dates and returns records whose affected field lies within that period.

<mark style="color:green;">`GET`</mark> `/api/{route}?filter[{field}][period]={value}`


# Assignment and Targeting

Assignment and targeting define the shared request fields and resolution rules for a resource's [Targetables capability](/introduction/resource-capabilities/targetables). Targetables select [Users](/api-reference/users) or [Departments](/api-reference/departments) for participation, responsibility, notifications, or resource-specific visibility.

A resource's Model Definition states whether it supports Targetables directly or inherits them from a parent. The resource and endpoint documentation define the accepted assignment modes, request fields, defaults, and effects.

Targetables do not create permission-key grants. [Entity Permissions](/introduction/entity-permissions) remain a separate resource-access contract.

## Static Assignment

Static assignment stores an assignment mode and, for explicit selection modes, associations to users or departments.

### Request Fields

| Key              | Type               | Description                                      |
| ---------------- | ------------------ | ------------------------------------------------ |
| `assign_mode`    | `string` \| `null` | Determines how Targetables are resolved.         |
| `user_ids`       | `array`            | User IDs used by explicit selection modes.       |
| `department_ids` | `array`            | Department IDs used by explicit selection modes. |

Only send these fields where the affected endpoint documents them.

### Assignment Modes

* `null` - No users or departments are targeted.
* `all` - All users and departments are targeted.
* `all_users` - All regular user accounts are targeted.
* `all_group_accounts` - All group accounts are targeted.
* `any_of` - Explicit users and departments use resource-specific any-of behavior.
* `one_of` - One of the explicit users or departments can satisfy the resource-specific action.
* `any_of_user` - Explicit users and users resolved from explicit departments use resource-specific any-of behavior.
* `one_of_user` - One resolved user can satisfy the resource-specific action.

For `null`, `all`, `all_users`, and `all_group_accounts`, `user_ids` and `department_ids` must be empty. The explicit selection modes require at least one user or department. Individual resources can support only a subset of these modes.

### Static Assignment Example

```json
{
  "assign_mode": "any_of",
  "user_ids": [3, 4, 5],
  "department_ids": [4, 5]
}
```

## Dynamic Assignment

Dynamic assignment resolves Targetables when an automation action runs. The `computed_targetable_type` selects the resolution source; the action documentation defines its accepted subset and any required configuration fields.

### Execution Context

* `for_executing_department`: Uses the department of the user who is executing the action as the target.
* `for_executing_real_department`: Uses the real department of the executing user, ignoring any proxy or delegation.
* `for_executing_user`: Uses the user who is executing the action as the target.
* `for_executing_real_user`: Uses the real user (not a proxy or delegate) who is executing the action.

### Related Assignments and Workflow Nodes

* `from_task_assignment`: Uses the assignment from the related [TaskAssignment](/api-reference/tasks-2/task-assignments) as the target.
* `from_workflow_node`: Uses the related workflow node as the target.
* `for_workflow_node_department`: Uses the department associated with the workflow node as the target.
* `for_workflow_node_executed_user_department`: Uses the department of the user who executed the workflow node as the target.
* `for_workflow_node_user`: Uses the user associated with the workflow node as the target.
* `for_workflow_node_executed_user`: Uses the user who executed the workflow node as the target.

A workflow node is one executed step in a chain of automation actions. Workflow-node resolution can select a previous step by its zero-based index through `computed_targetables_workflow_node_index`.

```json
{
  "computed_targetables_workflow_node_index": 0
}
```

### Entity-Select Fields

* `from_entity_select_fields_users`: Uses users selected in entity select fields as the target.
* `from_entity_select_fields_departments`: Uses departments selected in entity select fields as the target.

These types require the relevant field IDs in `computed_targetables_entity_select_field_ids`.

```json
{
  "computed_targetables_entity_select_field_ids": [123, 456]
}
```

### System Variables

* `for_system_variable_users`: Uses users resolved from System Variables as the target.
* `for_system_variable_departments`: Uses departments resolved from System Variables as the target.

These types require a [key-only System Variable](/introduction/system-variables#key-only-values) in `computed_targetables_system_variable`. The variable must resolve to a user, department, or corresponding list that matches the selected type.

```json
{
  "computed_targetables_system_variable": "system.user"
}
```


# Resource Capabilities

Resource model definitions use **Capabilities** to identify reusable behavior exposed through the public API. Capability names describe what API clients can use; they do not mirror PHP traits, interfaces, or other framework implementation details.

Each capability page defines the reusable resource behavior without maintaining an inventory of implementing resources. A resource's Model Definition links the capabilities it supports and describes the resource-specific fields, effects, and constraints. Shared request, resolution, and enforcement mechanisms remain in their dedicated Introduction sections.

## Module Permissions, Targetables, and Entity Permissions

[Module Permissions](/api-reference/permissions) are application-level prerequisites that determine whether the authenticated user's role can use a module or operation.

[Targetables](/introduction/resource-capabilities/targetables) define the [Users](/api-reference/users) and [Departments](/api-reference/departments) assigned to a resource. Depending on the resource, they represent participants, recipients, or responsible parties and may affect visibility.

[Entity Permissions](/introduction/resource-capabilities/entity-permissions) grant resource-specific permission keys to Users, [Roles](/api-reference/roles), or Departments. A resource can own these grants directly or inherit their effect through a parent resource.

A resource may combine these concepts. A module permission does not make a user a Targetable, and being targeted does not create an Entity Permission.

## Capabilities

* [Targetables](/introduction/resource-capabilities/targetables)
* [Entity Permissions](/introduction/resource-capabilities/entity-permissions)
* [URL Context](/introduction/resource-capabilities/url-context)
* [Translations](/introduction/resource-capabilities/translations)
* [Notifications](/introduction/resource-capabilities/notifications)
* [Reactions](/introduction/resource-capabilities/reactions)
* [Seen and Read Tracking](/introduction/resource-capabilities/seen-and-read-tracking)


# Targetables

The Targetables capability identifies resources that define or inherit target [Users](/api-reference/users) and [Departments](/api-reference/departments). Depending on the resource, Targetables can represent participants, notification recipients, eligible voters, or responsible parties and can affect visibility.

## Direct Targetables

A resource with direct Targetables owns its assignment mode and user or department associations. Its Model Definition and endpoints define the accepted fields, assignment modes, and resource-specific effects.

## Inherited Targetables

A resource with inherited Targetables resolves users and departments through a documented parent resource. It does not accept independent assignment input unless its own endpoint documentation states otherwise.

## Relationship to Entity Permissions

Targetables do not create permission-key grants. Resources that also use resource-instance grants list [Entity Permissions](/introduction/resource-capabilities/entity-permissions) as a separate capability.

[Assignment and Targeting](/introduction/assignment-and-targeting) defines the shared request fields, assignment modes, and computed resolution rules.


# Entity Permissions

The Entity Permissions capability identifies resources whose access can depend on permission-key grants for individual resource instances. Grants can address a [User](/api-reference/users), [Role](/api-reference/roles), or [Department](/api-reference/departments); supported keys and their endpoint-specific effects remain resource-specific.

## Direct Entity Permissions

A directly controlled resource owns its Entity Permissions. Its Model Definition states whether it exposes an `entityPermissions` relation, and its endpoints state whether they accept `entity_permissions` or apply a Restricted Scope.

## Inherited Entity Permissions

A dependent resource can derive effective Entity Permissions from a documented parent resource. It does not accept or persist independent grants unless its own Model Definition also declares direct Entity Permissions.

## Restricted Scope

The [Restricted Scope](/introduction/entity-permissions/restricted-scope) applies direct or inherited Entity Permissions to queries and operations. Administration, ownership, and other alternatives remain endpoint-specific.

## Relationship to Targetables

Entity Permissions grant named operations on a resource instance. [Targetables](/introduction/resource-capabilities/targetables) select users or departments without creating permission-key grants.

The [Entity Permissions introduction](/introduction/entity-permissions) defines the shared grant structure and enforcement model.


# URL Context

Resources with this capability have intratool URLs that the [UrlContext API](/api-reference/url-context) can resolve into a canonical URL and contextual metadata. Context details are returned only when the authenticated user may view the resolved resource.

Resolution normalizes the supplied URL into its context type, stable URL, current URL, and any type-specific metadata available for the resource. The resource's Model Definition identifies URL Context support; the API contract defines the common response structure.

The access check uses the resource's applicable module permissions, Targetables, Entity Permissions, ownership, and state rules. When access is unavailable, protected context information is omitted. URL Context does not grant access by itself.


# Translations

Resources with this capability expose one or more fields to the [Translations API](/api-reference/translations). The resource's Model Definition identifies the translatable fields.

The capability applies only to the fields declared by the resource. Clients can request translations for all declared fields or for a documented subset, while other fields remain unchanged and cannot be added to the translation request merely because they contain text.

Translations are stored for a source field and target language. Changing the source field removes its stored translations, so translated values must be generated again from the updated source content.


# Notifications

The Notifications capability identifies resources whose lifecycle events can create user-specific [Notifications](/api-reference/notifications). A notification links its recipient to the affected resource and carries the type and content required by the client.

The resource's Model Definition describes the resource-specific operations that can trigger notifications. Retrieve the resulting records and their seen or read state through the Notifications API; resource-local notification back-references are internal and are not part of the public Result Control contract.

The capability does not imply that every create or update operation sends a notification. Recipient resolution can depend on module permissions, Entity Permissions, notification settings, Targetables, and resource-specific mute or archive state.


# Reactions

The Reactions capability identifies resources that accept emoji feedback through the [Reactions API](/api-reference/reactions). Each reaction belongs to one [User](/api-reference/users) and one resource instance.

Each user has at most one current reaction per resource. Submitting another value for the same user and resource updates the existing reaction, while deleting it removes that user's feedback without changing reactions from other users.

Clients can retrieve individual reactions and aggregated reaction summaries where the resource exposes them. Access to reaction operations follows the authorization and state rules of the affected resource; declaring the capability does not grant access by itself.


# Seen and Read Tracking

Resources with this capability expose engagement records for each [User](/api-reference/users). A seen record indicates that a User has observed the resource; a read record is an explicit confirmation for content that requires reading confirmation.

Seen and read are separate states. A resource can expose only seen tracking, or it can additionally require an explicit read confirmation; creating a read record can also establish the seen state when the resource contract defines that behavior.

The resource's Model Definition identifies the tracking capability. Its tracking endpoints define authorization, idempotency, notification updates, and any state transition caused by creating a tracking record.


# Entity Permissions

[Entity Permissions](/api-reference/entity-permissions) attach permission-key grants to a specific resource instance. A grant assigns a named operation to a [User](/api-reference/users), [Role](/api-reference/roles), or [Department](/api-reference/departments).

A resource that uses these grants lists the [Entity Permissions capability](/introduction/resource-capabilities/entity-permissions) in its Model Definition. The resource documentation defines its permission keys, direct or inherited mode, accepted request fields, and endpoint-specific effects.

## Grant Structure

Each Entity Permission contains:

* `key`: The resource-specific permission key.
* `permission_entity` and `permission_entity_id`: The user, role, or department receiving the grant.
* `target_entity` and `target_entity_id`: The resource instance controlled by the grant.

## Direct and Inherited Permissions

Direct Entity Permissions belong to the resource instance itself. A directly controlled resource can expose an `entityPermissions` relation and documented create or update endpoints can accept `entity_permissions`.

Inherited Entity Permissions are evaluated through a documented parent resource. A dependent resource does not own independent grants unless its Model Definition also declares direct Entity Permissions.

## Permission Enforcement

[Module Permissions](/api-reference/permissions) determine whether the authenticated user's role can use a module or operation. Entity Permissions then restrict access to individual resource instances. Ownership and administration rules can provide documented alternatives.

[Restricted Scope](/introduction/entity-permissions/restricted-scope) defines how direct and inherited Entity Permissions filter queries and authorize operations.

## Relationship to Targetables

[Targetables](/introduction/resource-capabilities/targetables) select users or departments as participants, recipients, or responsible parties. They do not grant permission keys. A resource can support either or both concepts, and its Model Definition documents their effects separately.


# Restricted Scope

Restricted Scope applies [Entity Permissions](/introduction/entity-permissions) to resource queries and individual operations. The affected resource documentation identifies the required permission key and any ownership or administration alternatives.

## Query Filtering

Permission filtering is endpoint-specific. List and show operations can return only accessible resources, while administration endpoints can use another permission key or apply documented privileged access. A grant for one operation does not imply access to another operation.

## Direct Access

Directly controlled resources evaluate their own Entity Permissions. They can expose an `entityPermissions` relation, and their Restricted Scope matches grants for the authenticated [User](/api-reference/users), [Role](/api-reference/roles), or accessible [Departments](/api-reference/departments).

## Inherited Access

Dependent resources can apply the Restricted Scope of a documented parent relation. The dependent resource does not necessarily expose or persist independent Entity Permissions.

## Permission Input

Documented create and update endpoints for directly controlled resources may accept `entity_permissions`. The value is an array of permission definitions; the endpoint derives the target resource from the resource being created or updated.

The following example grants the `view` key to two users:

```json
[
  {
    "key": "view",
    "permission_entity": "user",
    "permission_entity_id": 25
  },
  {
    "key": "view",
    "permission_entity": "user",
    "permission_entity_id": 26
  }
]
```

Only send `entity_permissions` where the endpoint's Request Keys document it. Supported permission keys and permission-entity types remain resource-specific.


# System Variables

System Variables expose dynamic data while intratool evaluates supported fields and automation configurations. The available entry points depend on the current evaluation context.

Values can be rendered as text, compared in conditions, or passed to features such as dynamic Targetables. The affected resource or action documentation determines whether a field supports System Variables.

## Always Available Variables

* `system.date`
* `system.temporaryUser`
* `system.user`

## Evaluation Contexts

Additional variables become available for specific evaluation contexts.

| Context Type                                                          | Available When                                                      | Available System Variables                                                                                                                                |
| --------------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`AttributeSet`](/api-reference/additional-attributes/attribute-sets) | a default value for an attribute is chosen                          | `attributeSet`                                                                                                                                            |
| `EntitySelect`                                                        | an entity selection from a field of type entity\_select is resolved | `selectableEntity` ([User](/api-reference/users) or [Department](/api-reference/departments), depending on entity type)                                   |
| [`Form`](/api-reference/forms/forms)                                  | a default value for a field is chosen                               | `form`                                                                                                                                                    |
| [`FormMessage`](/api-reference/forms/form-messages)                   | a form is being submitted                                           | `formMessage`, `form`, `field.{formFieldKey}` + `field.{formFieldKey}.value`, `field.{formFieldKey}.request_value`, `field.{formFieldKey}.resolved_value` |
| [`MenuItem`](/api-reference/menu/menu-items)                          | a menu item is being rendered                                       | `menuItem`                                                                                                                                                |
| [`TaskAssignment`](/api-reference/tasks-2/task-assignments)           | a task assignment context is evaluated                              | `taskAssignment`                                                                                                                                          |
| [`TaskExecution`](/api-reference/tasks-2/task-executions)             | a task has been finished                                            | `taskExecution`, `field.{formFieldKey}` + `field.{formFieldKey}.value`, `field.{formFieldKey}.request_value`, `field.{formFieldKey}.resolved_value`       |

## Properties and Relations

System Variables are entry points to public properties and relations. For example, `system.user.id` accesses the current User ID, while `system.user.role.name` traverses the User's [Role](/api-reference/roles) relation and returns its name.

The affected resource's Model Definition documents the available properties and relations. Query [Result Control](/introduction/query-manipulation/result-control) uses the same public relation names.

Form and task contexts additionally expose field values through `field.{formFieldKey}` and its value properties.

## Syntax

### Rendered Templates

Wrap a System Variable in double braces when the field renders text:

```
{{ systemVariableKey }}
```

Use dot notation to access properties and relations:

```
{{ systemVariableKey.property }}
```

```
{{ systemVariableKey.relation.property }}
```

Rendered-template fields accept ordinary text around System Variable expressions. Every expression must use valid template syntax. Arithmetic operators such as `+`, `-`, `*`, `/`, `%`, and `**` are not supported, and an additional-attribute set key inside an expression must use the documented attribute-set key format. Slashes remain valid in ordinary text and quoted formatter arguments.

### Key-only Values

When a field evaluates only the variable key, omit the braces. `systemVariable` conditions and computed Targetables use this key-only form.

A key is a dot-separated path. Its first segment starts with a letter or underscore, and every segment contains only letters, digits, or underscores. Later segments may be numeric. Template braces, formatters, empty segments, and slashes are not valid in key-only values.

## Examples

| Key                                                                                                     | Description                                                                                                                                                 |
| ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `system.date`                                                                                           | Current date/time context object. Example access: `{{ system.date }}` or nested properties depending on formatter/context.                                  |
| `system.user`                                                                                           | Current authenticated user. Example: `{{ system.user.id }}`, `{{ system.user.role.name }}`.                                                                 |
| `system.temporaryUser`                                                                                  | Temporary authenticated user (if available in current context). Example: `{{ system.temporaryUser.id }}`.                                                   |
| `form`                                                                                                  | Current form context object (where available). Example: `{{ form.name }}`.                                                                                  |
| `formMessage`                                                                                           | Submitted form message object in `FormMessage` context. Example: `{{ formMessage.id }}`.                                                                    |
| `taskAssignment`                                                                                        | Task assignment object in `TaskAssignment` context. Example: `{{ taskAssignment.title }}`.                                                                  |
| `taskExecution`                                                                                         | Task execution object in `TaskExecution` context. Example: `{{ taskExecution.id }}`.                                                                        |
| `attributeSet`                                                                                          | Attribute set object in `AttributeSet` context. Example: `{{ attributeSet.title }}`.                                                                        |
| `menuItem`                                                                                              | Menu item object in `MenuItem` context. Example: `{{ menuItem.id }}`.                                                                                       |
| `selectableEntity`                                                                                      | Selected entity in `EntitySelect` context (user or department). Example: `{{ selectableEntity.id }}` or `{{ selectableEntity.name }}`.                      |
| `field.{formFieldKey}`                                                                                  | Full field context object in `FormMessage` and `TaskExecution` contexts. Use this when you need structured access to the field.                             |
| `field.{formFieldKey}.value`                                                                            | Raw stored field value (usually normalized to the persisted representation). Useful for direct comparisons.                                                 |
| `field.{formFieldKey}.request_value`                                                                    | Original value from the incoming request before full resolving/formatting. Useful for debugging or strict input-based logic.                                |
| `field.{formFieldKey}.resolved_value`                                                                   | Final resolved/rendered value after processing, for example formatting or entity resolution. Useful for output-ready text and notifications.                |
| `system.user.role.additional_attributes.{attributeSetKey}.{attributeFieldId}`                           | Access an additional attribute that is linked to the current user's role. Without sub-key, this resolves to the field `value` (same behavior as `field.*`). |
| `system.user.role.additional_attributes.{attributeSetKey}.{attributeFieldId}.value`                     | Stored value of a role-bound additional attribute field (normalized output value).                                                                          |
| `system.user.role.additional_attributes.{attributeSetKey}.{attributeFieldId}.request_value`             | Original request value of a role-bound additional attribute field.                                                                                          |
| `system.user.role.additional_attributes.{attributeSetKey}.{attributeFieldId}.resolved_value`            | Resolved/rendered value of a role-bound additional attribute field.                                                                                         |
| `system.user.role.department.additional_attributes.{attributeSetKey}.{attributeFieldId}`                | Access an additional attribute that is linked to the current user's department. Without sub-key, this resolves to the field `value`.                        |
| `system.user.role.department.additional_attributes.{attributeSetKey}.{attributeFieldId}.value`          | Stored value of a department-bound additional attribute field (normalized output value).                                                                    |
| `system.user.role.department.additional_attributes.{attributeSetKey}.{attributeFieldId}.request_value`  | Original request value of a department-bound additional attribute field.                                                                                    |
| `system.user.role.department.additional_attributes.{attributeSetKey}.{attributeFieldId}.resolved_value` | Resolved/rendered value of a department-bound additional attribute field.                                                                                   |

## Field Value Resolution

Field context objects expose the stored, requested, and resolved representations of a submitted value.

### Text Field

For a text field with ID `123` and the submitted value `Max`, the properties resolve as follows:

* `field.123` → `Max`
* `field.123.value` → `Max`
* `field.123.request_value` → `Max`
* `field.123.resolved_value` → `null`

`field.123` and `field.123.value` both return the stored field value.

### Entity-Select Field

For an entity-select field with ID `456` and a selected user with ID `789`, the properties resolve as follows:

* `field.456` → `Max Mustermann (Operations, ID: 789)`
* `field.456.value` → `Max Mustermann (Operations, ID: 789)`
* `field.456.request_value` → `789`
* `field.456.resolved_value` → `User` object

The resolved object exposes its documented public properties, for example `field.456.resolved_value.phone`.

### Using Resolved Values

An object in `resolved_value` cannot be rendered directly as text. Use `value`, `request_value`, or a public property of the resolved object for text output. Use `resolved_value` when another mechanism requires the entity itself, such as a [dynamic assignment](/introduction/assignment-and-targeting).

Depending on the field type these object types can be expected as resolved values:

* An `entity-select` field for users resolves to a `User` object.
* An `entity-select` field for departments resolves to a `Department` object.
* A `file-upload` field resolves to a [FileManagerFile](/api-reference/filemanager/filemanager-files) object.
* All other field types return `null` as `resolved_value`.

## Formatters

When a System Variable is rendered, its value can be passed through a [formatter](/introduction/system-variables/formatters).

## Unavailable Values

If a variable or property is unavailable in the current context, rendering replaces it with an empty string.


# Formatters

Formatters transform System Variable values during rendering. They provide consistent output for dates, names, addresses, HTML, and other supported values.

Multiple formatters can be chained, and each formatter receives the output of the previous formatter.

## Syntax

```twig
{{ variable|formatter_key }}
{{ variable|formatter_key(arg1, arg2) }}
```

Chain multiple formatters with additional pipe characters:

```twig
{{ variable|formatter_key1|formatter_key2(arg) }}
```

## Available Formatters

| Formatter key               | Description                                                                                                              | Arguments (optional)                                                                           | Example                                                            | Result (example output)                |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | -------------------------------------- |
| `date`                      | Twig-compatible date formatter (overrides Twig default to return empty string for empty values).                         | `format: string`, `timezone: string`                                                           | `{{ system.date\|date('Y-m-d H:i', 'Europe/Berlin') }}`            | `2026-04-29 14:30`                     |
| `format_date`               | Formats a value as date.                                                                                                 | `dateFormat: string`                                                                           | `{{ system.date\|format_date('d.m.Y') }}`                          | `29.04.2026`                           |
| `format_date_database`      | Formats a value using database date format.                                                                              | `dateFormat: string`                                                                           | `{{ system.date\|format_date_database }}`                          | `2026-04-29`                           |
| `format_datetime`           | Formats a value as date + time.                                                                                          | `dateFormat: string`                                                                           | `{{ system.date\|format_datetime('d.m.Y H:i') }}`                  | `29.04.2026 14:30`                     |
| `format_datetime_database`  | Formats a value using database datetime format.                                                                          | `dateFormat: string`                                                                           | `{{ system.date\|format_datetime_database }}`                      | `2026-04-29 14:30:00`                  |
| `format_date_period`        | Formats a period value with `start` and `end`; if both are equal, only one date is returned.                             | `dateFormat: string`, `delimiter: string`                                                      | `{{ period\|format_date_period('d.m.Y', 'to') }}`                  | `29.04.2026 to 02.05.2026`             |
| `format_datetime_period`    | Like `format_date_period`, but with datetime format.                                                                     | `dateFormat: string`, `delimiter: string`                                                      | `{{ period\|format_datetime_period('d.m.Y H:i', 'to') }}`          | `29.04.2026 08:00 to 29.04.2026 17:00` |
| `format_time`               | Formats a value as time only.                                                                                            | `dateFormat: string`                                                                           | `{{ system.date\|format_time('H:i') }}`                            | `14:30`                                |
| `format_time_period`        | Formats a `start`/`end` period as time only.                                                                             | `dateFormat: string`, `delimiter: string`                                                      | `{{ period\|format_time_period('H:i', '-') }}`                     | `08:00 - 17:00`                        |
| `format_department_name`    | Resolves and formats a department display name (expects a department-like value with `id`).                              | -                                                                                              | `{{ system.user.role.department\|format_department_name }}`        | `Operations`                           |
| `format_form_field_results` | Renders form field results as HTML output (supports form messages and task executions, including child task executions). | `includeEmptyFields: bool`, `includeDescriptionFields: bool`, `useGrid: bool`                  | `{{ formMessage\|format_form_field_results(false, false, true) }}` | `<p><strong>Field</strong>: Value</p>` |
| `format_user_address`       | Formats a user's address (expects a user-like value with `id`).                                                          | `multiLine: bool`                                                                              | `{{ system.user\|format_user_address(true) }}`                     | `Main St 1\n12345 Berlin`              |
| `format_user_name`          | Formats a user's display name (expects a user-like value with `id`).                                                     | `showUserGroup: bool`, `showUsername: bool`, `showGroupAccount: bool`, `reverseFullName: bool` | `{{ system.user\|format_user_name(true, false, false, false) }}`   | `Max Mustermann (Operations)`          |
| `format_task_status`        | Translates a task status value (e.g. `open`, `done`) to localized output.                                                | -                                                                                              | `{{ taskExecution.status\|format_task_status }}`                   | `Open`                                 |
| `p2br`                      | Converts paragraph HTML blocks to `<br>`-based line breaks.                                                              | `pTag: string`                                                                                 | `{{ formMessage.body\|p2br('p') }}`                                | `Line 1<br>Line 2`                     |
| `strip_html`                | Removes HTML tags; supports keeping selected tags and line-break conversion options.                                     | `ignoreTags: string`, `p2br: bool`, `br2nl: bool`                                              | `{{ formMessage.body\|strip_html('<a><br>', true, false) }}`       | `Hello <a href="#">link</a><br>World`  |

## Formatter Behavior

* Unknown formatter keys are ignored at runtime and result in an empty replacement for that formatter call.
* `format_user_name`, `format_user_address`, and `format_department_name` require resolvable entity IDs in the input value.


# Rich Text

Rich-text fields accept and return HTML strings. intratool uses TipTap as its editor, but TipTap JSON is an internal editing representation and is not part of the API or database contract.

Use the canonical markup below so content can be opened, changed, and serialized by the editor without losing structure. Individual fields can expose only a subset of the general editor features; the affected resource page remains authoritative for whether a field accepts rich text and whether it is nullable.

## API Values

Send the complete HTML document fragment as a JSON string. A request library handles JSON escaping; the value itself remains HTML.

```json
{
  "text": "<p>Welcome to the <strong>project</strong>.</p>"
}
```

Responses return the same field as an HTML string. Do not send TipTap JSON documents, arrays of nodes, Markdown, or editor state.

## Document Structure

Wrap every prose block in a paragraph. Use an empty paragraph only for an intentional blank line between non-empty blocks, and use `<br>` only for a line break within the same paragraph.

```html
<p>First paragraph.</p>
<p></p>
<p>Second paragraph.<br>Same paragraph, new line.</p>
```

Do not submit bare text such as `First paragraph.` when the field accepts rich text. An entirely empty editor is represented as `null` when the resource field is nullable; an empty paragraph is not a substitute for a required value.

## Inline Formatting

| Format           | Canonical HTML                                                                                         |
| ---------------- | ------------------------------------------------------------------------------------------------------ |
| Bold             | `<strong>Bold text</strong>`                                                                           |
| Italic           | `<em>Italic text</em>`                                                                                 |
| Underline        | `<u>Underlined text</u>`                                                                               |
| Strikethrough    | `<s>Removed text</s>`                                                                                  |
| Text color       | `<span style="color: #f44336">Red text</span>`                                                         |
| Background color | `<mark data-color="#ffff00" style="background-color: #ffff00; color: inherit">Highlighted text</mark>` |
| Font size        | `<span style="font-size: 150%">Large text</span>`                                                      |
| Small text       | `<small>Small text</small>`                                                                            |
| Emoji            | `✅` as a Unicode character                                                                             |

Formatting marks can be nested inside a paragraph. Keep the block structure outside the marks.

```html
<p><strong>Important:</strong> Submit the <em>signed</em> document.</p>
```

## Blocks and Alignment

Headings use `<h1>` through `<h6>`. Paragraphs and headings can select one of the supported [Text Alignments](#text-alignments) through `text-align`.

**Text Alignments**

* `left` - Aligns content with the left edge.
* `center` - Centers the content horizontally.
* `right` - Aligns content with the right edge.
* `justify` - Distributes content across the available width.

```html
<h2 style="text-align: center">Safety Instructions</h2>
<p style="text-align: justify">Read every step before starting the machine.</p>
<blockquote><p>Stop the machine before opening the cover.</p></blockquote>
<hr>
```

A horizontal rule can additionally use one of the height classes `hr-height-2` through `hr-height-5` and a hexadecimal background color, for example `<hr class="hr-height-2" style="background-color: #607d8b">`.

## Lists

Use `<ul>` for unordered lists and `<ol>` for ordered lists. Wrap textual list-item content in paragraphs, including nested list items.

```html
<ul>
  <li><p>Inspect the work area.</p></li>
  <li>
    <p>Check the equipment.</p>
    <ol>
      <li><p>Confirm the serial number.</p></li>
      <li><p>Record the condition.</p></li>
    </ol>
  </li>
</ul>
```

An ordered list can set a non-default starting number with the `start` attribute, for example `<ol start="3">`.

## Links and Shared Items

Use a normal anchor for a text link. Use the resource's API-provided `static_url` for an internal link instead of constructing a frontend path; internal URLs should be relative and open in the same window. External URLs should use a new window and the protective relation values shown below.

```html
<p><a href="/handbuch/eintrag/j7x2q9m4" target="_self">Open the manual entry</a></p>
<p><a href="https://example.com/safety" target="_blank" rel="noopener noreferrer">External safety guide</a></p>
```

Add `data-show-url-context` when intratool should render more than a normal link. Select one of the supported [URL Context Display Types](#url-context-display-types); the target URL must resolve through [URL Context](/introduction/resource-capabilities/url-context).

**URL Context Display Types**

* `inline` - Renders the resolved context within the surrounding text flow.
* `card` - Renders the resolved context as a standalone card.
* `embedded` - Embeds the resolved context in the rich-text content.

```html
<p>See <a href="/handbuch/eintrag/j7x2q9m4" target="_self" data-show-url-context="inline">Safety instructions</a>.</p>
<a href="/handbuch/eintrag/j7x2q9m4" target="_self" data-show-url-context="card">Safety instructions</a>
<a href="/handbuch/eintrag/j7x2q9m4" target="_self" data-show-url-context="embedded">Safety instructions</a>
```

Use a [SharedItem](/api-reference/shared-items) when the linked resource needs a stable share URL. Create the Shared Item for the target resource and store the returned `static_url`; do not construct grant-token URLs or copy temporary access URLs into rich text.

## Files and Media

Use URLs returned for [FilemanagerFiles](/api-reference/filemanager/filemanager-files), or the `static_url` returned for a `SharedItem` when the file must be shared through rich text. Do not store filesystem paths, storage keys, or base64 data as persistent file references.

Use an anchor for a downloadable or previewable file, `<img>` for an image, and a `<source>` child for audio or video.

```html
<p><a href="{fileOrSharedItemUrl}" target="_self">Safety checklist.pdf</a></p>
<p><img src="{imageContentUrl}" alt="Completed safety inspection"></p>
<audio controls=""><source src="{audioContentUrl}" type="audio/mpeg"></audio>
<video poster="{posterContentUrl}" controls=""><source src="{videoContentUrl}" type="video/mp4"></video>
```

Images can select one supported [Image Size Class](#image-size-classes) and can additionally select one [Image Float Class](#image-float-classes).

**Image Size Classes**

* `image-fullwidth` - Uses the full available width.
* `image-three-quarters` - Uses three quarters of the available width.
* `image-two-thirds` - Uses two thirds of the available width.
* `image-half` - Uses half of the available width.
* `image-one-third` - Uses one third of the available width.
* `image-one-quarter` - Uses one quarter of the available width.

**Image Float Classes**

* `float-left` - Floats the image to the left of surrounding content.
* `float-right` - Floats the image to the right of surrounding content.

```html
<p><img class="image-half float-right" src="{imageContentUrl}" alt="Machine controls"></p>
```

The file URL must return the actual media content when used in `src`. Use the file-content or playback URL supplied by the API; a Filemanager page URL or a Shared Item landing-page URL is not a media source by itself.

For a Shared Item media source, append exactly one supported [Media Target URL Parameter](#media-target-url-parameters).

**Media Target URL Parameters**

* `showFileContent=1` - Returns image content.
* `downloadFileContent=1` - Returns audio or video content.
* `filePoster=1` - Returns a video poster.

## Tables

Use standard table markup with `<th>` for header cells and `<td>` for data cells. Wrap the content of every cell in a paragraph.

```html
<table>
  <tbody>
    <tr>
      <th><p>Item</p></th>
      <th><p>Status</p></th>
    </tr>
    <tr>
      <td><p>Emergency stop</p></td>
      <td><p><strong>Checked</strong></p></td>
    </tr>
  </tbody>
</table>
```

Merged cells can use `colspan` and `rowspan`. Preserve an existing `colwidth` attribute when round-tripping editor-generated column widths, but do not add viewer-only responsive wrappers to the stored HTML.

## Dynamic Content

Fields that support [System Variables](/introduction/system-variables) can place a variable inside ordinary rich-text nodes. Keep the complete variable expression together instead of splitting it across formatting tags.

```html
<p>Hello <strong>{{ system.user.first_name }}</strong>, your task is ready.</p>
```

## Unsupported Markup

Send only the documented elements and attributes. Scripts, event-handler attributes, iframes, arbitrary CSS, and application-rendering wrappers are outside the rich-text contract, and a later edit in TipTap can normalize or remove unsupported markup.


# Additional Attributes

Additional Attributes provide configurable fields for API resources. An [AttributeSet](/api-reference/additional-attributes/attribute-sets) groups [AttributeFields](/api-reference/additional-attributes/attribute-fields), while each affected resource stores its own values for those fields.

Resource endpoints that support Additional Attributes document the shared linking and value structure described here.

## Request Flow

Create an `AttributeSet` and its `AttributeFields` before linking the set to another resource. The set defines the grouping and ordering, while each field defines one configurable value. The target resource request can then link sets and assign values in one operation.

## Request Structure

Affected create and update endpoints can accept the following keys when their Request Keys table declares them.

| Key                     | Type     | Description                                                                 |
| ----------------------- | -------- | --------------------------------------------------------------------------- |
| `attribute_set_ids`     | `array`  | Links and optionally orders `AttributeSets` for the affected resource.      |
| `additional_attributes` | `object` | Assigns values by `attributeSet.key` and field key (`field-{formFieldId}`). |

### `attribute_set_ids` Formats

`attribute_set_ids` supports two formats:

#### IDs Only

```json
{
  "attribute_set_ids": [1, 2, 3]
}
```

#### IDs with Sorting

```json
{
  "attribute_set_ids": [
    { "id": 1, "sort_number": 1 },
    { "id": 2, "sort_number": 2 }
  ]
}
```

### `additional_attributes` Structure

Values are passed per `attributeSet.key`. Inside each set, field keys follow the pattern `field-{formFieldId}`.

```json
{
  "additional_attributes": {
    "employee-data": {
      "field-17": "Max Mustermann",
      "field-18": "DE"
    },
    "contract": {
      "field-24": "2026-05-01"
    }
  }
}
```

### Combined Request Example

```json
{
  "name": "Operations",
  "attribute_set_ids": [
    { "id": 1, "sort_number": 1 },
    { "id": 2, "sort_number": 2 }
  ],
  "additional_attributes": {
    "employee-data": {
      "field-17": "Max Mustermann",
      "field-18": "DE"
    }
  }
}
```

## See Also

* [Additional Attributes API](/api-reference/additional-attributes)
* [System Variables](/introduction/system-variables)


# Infoboard

The Infoboard publishes [InfoboardPosts](/api-reference/infoboard/infoboard-posts) within optional [InfoboardChannels](/api-reference/infoboard/infoboard-channels). Posts can target departments, require reading confirmation, and allow [InfoboardComments](/api-reference/infoboard/infoboard-comments).

[InfoboardDepartmentSettings](/api-reference/infoboard/infoboard-department-settings) store department-level options such as a default channel. [InfoboardPostSeenUsers](/api-reference/infoboard/infoboard-post-seen-users) and [InfoboardPostReadUsers](/api-reference/infoboard/infoboard-post-read-users) track which users have seen or explicitly confirmed a post.

Post visibility combines [Targetables](/introduction/resource-capabilities/targetables) with [Entity Permissions](/introduction/resource-capabilities/entity-permissions) inherited from the assigned channel. Comments inherit the resulting access through their parent post.


# InfoboardPosts

## Introduction

`InfoboardPosts` are the primary Infoboard content resource. A post can belong to an [InfoboardChannel](/api-reference/infoboard/infoboard-channels), target [Users](/api-reference/users) or [Departments](/api-reference/departments), accept [InfoboardComments](/api-reference/infoboard/infoboard-comments), and require explicit reading confirmation.

Post visibility combines Targetables with inherited channel Entity Permissions when a channel is assigned.

The `text` field uses the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`infoboardPost`

**Relations**

| Key                | Relation                                                                     | Type            | Relation Field(s)                                                         |
| ------------------ | ---------------------------------------------------------------------------- | --------------- | ------------------------------------------------------------------------- |
| `user`             | [User](/api-reference/users)                                                 | Belongs to      | `user_id`                                                                 |
| `departments`      | [Departments](/api-reference/departments)                                    | Belongs to many | `department_infoboard_post`                                               |
| `users`            | [Users](/api-reference/users)                                                | Belongs to many | `user_infoboard_post`                                                     |
| `infoboardChannel` | [InfoboardChannel](/api-reference/infoboard/infoboard-channels)              | Belongs to      | `infoboard_channel_id`                                                    |
| `seenUsers`        | [InfoboardPostSeenUsers](/api-reference/infoboard/infoboard-post-seen-users) | Has many        | `infoboard_post_seen_users.post_id`                                       |
| `readUsers`        | [InfoboardPostReadUsers](/api-reference/infoboard/infoboard-post-read-users) | Has many        | `infoboard_post_read_users.post_id`                                       |
| `comments`         | [InfoboardComments](/api-reference/infoboard/infoboard-comments)             | Has many        | `infoboard_comments.post_id`                                              |
| `latestComment`    | [InfoboardComment](/api-reference/infoboard/infoboard-comments)              | Has one         | `infoboard_comments.post_id`                                              |
| `reactions`        | [Reactions](/api-reference/reactions)                                        | Morph many      | `reactions.reactable_type`, `reactions.reactable_id`                      |
| `currentReaction`  | [Reaction](/api-reference/reactions)                                         | Morph one       | `reactions.reactable_type`, `reactions.reactable_id`, `reactions.user_id` |
| `tags`             | [Tags](/api-reference/tags/tags)                                             | Belongs to many | `tag_taggable.taggable_type`, `tag_taggable.taggable_id`                  |

**Assignment modes**

* `null` - Do not target users or departments. This is unavailable when the assigned channel has `apply_allowed_departments` enabled.
* `any_of` - Target the explicitly assigned departments. Direct user assignment is currently ignored by the create and update endpoints.

**Comment visibility constraint types**

* `department` - Limit comments to the author's department.
* `private` - Limit comments to users with the applicable private-comment access and allowed department context.
* `null` - Do not impose a comment visibility constraint.

**Computed Properties**

* `hash` - Hashed representation of the post `id`.

**Capabilities**

* [Targetables](/introduction/resource-capabilities/targetables) - `assign_mode` and `departments` define direct post visibility; only `null` and `any_of` are supported, and direct user targets are currently removed from requests.
* [Entity Permissions](/introduction/resource-capabilities/entity-permissions) - Posts inherit the `view` and administration effects of their assigned channel and do not own independent Entity Permissions.
* [URL Context](/introduction/resource-capabilities/url-context) - Post URLs resolve to access-checked context information.
* [Translations](/introduction/resource-capabilities/translations) - The `title` and `text` fields are translatable.
* [Notifications](/introduction/resource-capabilities/notifications) - Publishing and relevant updates can notify eligible targeted users when the parent channel enables notifications.
* [Reactions](/introduction/resource-capabilities/reactions) - Posts accept reactions when `reactions_forbidden` is `false`.
* [Seen and Read Tracking](/introduction/resource-capabilities/seen-and-read-tracking) - Seen records track visible posts, read records capture explicit confirmations, and `reading_confirmation` tells clients whether a confirmation is required.

## User Can Create Posts

Check whether the authenticated user can create at least one `InfoboardPost` globally or in an accessible channel.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/infoboard/user-can-create-posts`

**Behavior**

* The result accounts for both the global post-creation permission and channel-specific creation grants.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/infoboard/user-can-create-posts', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": true
}
```

## Unseen and Unread Statistics

List unseen and unread post counts grouped by channel for the authenticated user.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/infoboard/unseen-unread-posts-statistics`

**Behavior**

* Shared value filters can narrow the posts before counts are calculated.
* Only posts the user may mark as seen or read are counted.
* `infoboard_channel_id` is `null` for posts outside a channel.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/infoboard/unseen-unread-posts-statistics', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": [
    {
      "infoboard_channel_id": null,
      "unseen_posts_count": 1,
      "unread_posts_count": 0
    },
    {
      "infoboard_channel_id": 1,
      "unseen_posts_count": 2,
      "unread_posts_count": 1
    }
  ]
}
```

## List

List `InfoboardPosts` visible to the current authenticated user.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/infoboard/posts`

**Behavior**

* Visibility includes owned posts and posts available through Targetables, inherited channel Entity Permissions, or an explicit entity grant.
* Scheduled posts remain hidden until their publication time unless the applicable ownership or scheduling access permits them.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/infoboard/posts', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "infoboard_channel_id": 1,
    "lang_id": "en-US",
    "title": "Quarterly town hall",
    "text": "<p>The next town hall takes place on Friday.</p>",
    "reading_confirmation": true,
    "reactions_forbidden": false,
    "comments_allowed": true,
    "comment_user_confirmation_required": false,
    "comment_visibility_constraint_type": null,
    "force_comment_visibility_constraint": false,
    "pinned": true,
    "pinned_end": "2026-08-08 09:00:00",
    "planned_publish_at": null,
    "published_at": "2026-08-06 09:30:00",
    "delete_interval": null,
    "assign_mode": "any_of",
    "created_at": "2026-08-06 09:30:00",
    "updated_at": "2026-08-06 09:30:00",
    "deleted_at": null,
    "hash": "wrv2jd6x8g1nmlk4o970mqen"
  },
  {
    "id": 2,
    "user_id": 4,
    "infoboard_channel_id": null,
    "lang_id": "en-US",
    "title": "Office maintenance",
    "text": "<p>The west wing will be closed for maintenance on Monday.</p>",
    "reading_confirmation": false,
    "reactions_forbidden": true,
    "comments_allowed": false,
    "comment_user_confirmation_required": false,
    "comment_visibility_constraint_type": null,
    "force_comment_visibility_constraint": false,
    "pinned": false,
    "pinned_end": null,
    "planned_publish_at": null,
    "published_at": "2026-08-06 10:00:00",
    "delete_interval": null,
    "assign_mode": null,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:00:00",
    "deleted_at": null,
    "hash": "q6n1vx8m4p2kr9dw5e7z0yla"
  }
]
```

## Count

Count `InfoboardPosts` visible to the current authenticated user.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/infoboard/posts/count`

**Behavior**

* The same visibility, publication, and shared filter rules as the list endpoint apply.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/infoboard/posts/count', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
1
```

## Show

Show one visible `InfoboardPost` by ID or hash.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/infoboard/posts/{infoboardPost}`

**Route Parameters**

| Parameter       | Type                  | Description               |
| --------------- | --------------------- | ------------------------- |
| `infoboardPost` | `integer` \| `string` | InfoboardPost ID or hash. |

**Behavior**

* The same Targetables, inherited Entity Permissions, grants, ownership, and publication rules as the list endpoint apply.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/infoboard/posts/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "infoboard_channel_id": 1,
  "lang_id": "en-US",
  "title": "Quarterly town hall",
  "text": "<p>The next town hall takes place on Friday.</p>",
  "reading_confirmation": true,
  "reactions_forbidden": false,
  "comments_allowed": true,
  "comment_user_confirmation_required": false,
  "comment_visibility_constraint_type": null,
  "force_comment_visibility_constraint": false,
  "pinned": true,
  "pinned_end": "2026-08-08 09:00:00",
  "planned_publish_at": null,
  "published_at": "2026-08-06 09:30:00",
  "delete_interval": null,
  "assign_mode": "any_of",
  "created_at": "2026-08-06 09:30:00",
  "updated_at": "2026-08-06 09:30:00",
  "deleted_at": null,
  "hash": "wrv2jd6x8g1nmlk4o970mqen"
}
```

## Create

Create a new `InfoboardPost`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/infoboard/posts`

**Request Keys**

| Key                                   | Type                 | Default         | Description                                                                                    |
| ------------------------------------- | -------------------- | --------------- | ---------------------------------------------------------------------------------------------- |
| `lang_id`                             | `string`             | system language | Language key for the translatable content.                                                     |
| `infoboard_channel_id`                | `integer` \| `null`  | `null`          | ID of the [InfoboardChannel](/api-reference/infoboard/infoboard-channels) containing the post. |
| `title`\*                             | `string`             | -               | Post title.                                                                                    |
| `text`\*                              | `string`             | -               | Post content, which can contain HTML.                                                          |
| `reading_confirmation`                | `boolean`            | `false`         | Whether users must explicitly confirm reading.                                                 |
| `comments_allowed`                    | `boolean`            | `true`          | Whether comments can be created.                                                               |
| `comment_user_confirmation_required`  | `boolean`            | `false`         | Whether comment creation requires a confirmed non-group user identity.                         |
| `comment_visibility_constraint_type`  | `string` \| `null`   | `null`          | Default [comment visibility constraint](#comment-visibility-constraint-types).                 |
| `force_comment_visibility_constraint` | `boolean`            | `false`         | Whether comments must use the post's configured constraint.                                    |
| `pinned`                              | `boolean`            | `false`         | Whether the post is pinned.                                                                    |
| `pinned_end`                          | `datetime` \| `null` | `null`          | Future date at which pinning ends. Relative dates are accepted.                                |
| `planned_publish_at`                  | `datetime` \| `null` | `null`          | Future publication date. Relative dates are accepted.                                          |
| `delete_interval`                     | `string` \| `null`   | `null`          | ISO 8601 date interval after which the post can be deleted automatically.                      |
| `assign_mode`                         | `string` \| `null`   | derived         | Supported [assignment mode](#assignment-modes).                                                |
| `department_ids`                      | `array`              | `[]`            | IDs of departments targeted by the post.                                                       |

Keys with `*` are required.

**Behavior**

* The authenticated user becomes the owner; client-provided `user_id` and `user_ids` values are ignored.
* Supplying departments defaults `assign_mode` to `any_of`; otherwise it defaults to `null`. A channel with `apply_allowed_departments` enabled requires at least one department and removes department IDs the author may not assign.
* Without a future `planned_publish_at` and scheduling permission, the post is published immediately. A valid future value stores `published_at` as `null` until publication. Client-provided `published_at` is normalized by this rule.
* `pinned` and `pinned_end` apply only with pin permission. Otherwise `pinned` becomes `false` and `pinned_end` becomes `null`.
* `delete_interval` applies only when the user may delete the post; otherwise it becomes `null`.
* Creating a published post can trigger notifications for eligible targeted users.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/infoboard/posts', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'infoboard_channel_id' => 1,
        'title' => 'Quarterly town hall',
        'text' => '<p>The next town hall takes place on Friday.</p>',
        'reading_confirmation' => true,
        'comments_allowed' => true,
        'department_ids' => [1, 2]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "infoboard_channel_id": 1,
    "lang_id": "en-US",
    "title": "Quarterly town hall",
    "text": "<p>The next town hall takes place on Friday.</p>",
    "reading_confirmation": true,
    "reactions_forbidden": false,
    "comments_allowed": true,
    "comment_user_confirmation_required": false,
    "comment_visibility_constraint_type": null,
    "force_comment_visibility_constraint": false,
    "pinned": false,
    "pinned_end": null,
    "planned_publish_at": null,
    "published_at": "2026-08-06 09:30:00",
    "delete_interval": null,
    "assign_mode": "any_of",
    "created_at": "2026-08-06 09:30:00",
    "updated_at": "2026-08-06 09:30:00",
    "deleted_at": null,
    "hash": "wrv2jd6x8g1nmlk4o970mqen"
  }
}
```

## Reset Relations

Reset selected dependent relations of an `InfoboardPost`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/infoboard/posts/{infoboardPost}/reset`

**Route Parameters**

| Parameter       | Type                  | Description               |
| --------------- | --------------------- | ------------------------- |
| `infoboardPost` | `integer` \| `string` | InfoboardPost ID or hash. |

**Request Keys**

| Key                   | Type      | Default | Description                                               |
| --------------------- | --------- | ------- | --------------------------------------------------------- |
| `seenUsers`           | `boolean` | `false` | Delete seen-user records when `true`.                     |
| `readUsers`           | `boolean` | `false` | Delete read-user records when `true`.                     |
| `comments`            | `boolean` | `false` | Delete comments when `true`.                              |
| `reactions`           | `boolean` | `false` | Delete reactions when `true`.                             |
| `targetedSurveyVotes` | `boolean` | `false` | Delete votes from surveys targeting the post when `true`. |

**Behavior**

* At least one resettable relation must be set to `true`; otherwise the operation returns an error response.
* Relation-specific delete behavior runs for each selected relation, including comment notification cleanup.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/infoboard/posts/1/reset', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'seenUsers' => true,
        'readUsers' => true
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "infoboard_channel_id": 1,
    "lang_id": "en-US",
    "title": "Quarterly town hall",
    "text": "<p>The next town hall takes place on Friday.</p>",
    "reading_confirmation": true,
    "reactions_forbidden": false,
    "comments_allowed": true,
    "comment_user_confirmation_required": false,
    "comment_visibility_constraint_type": null,
    "force_comment_visibility_constraint": false,
    "pinned": false,
    "pinned_end": null,
    "planned_publish_at": null,
    "published_at": "2026-08-06 09:30:00",
    "delete_interval": null,
    "assign_mode": "any_of",
    "created_at": "2026-08-06 09:30:00",
    "updated_at": "2026-08-06 09:30:00",
    "deleted_at": null,
    "hash": "wrv2jd6x8g1nmlk4o970mqen"
  }
}
```

## Update

Update an existing `InfoboardPost`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/infoboard/posts/{infoboardPost}`

**Route Parameters**

| Parameter       | Type                  | Description               |
| --------------- | --------------------- | ------------------------- |
| `infoboardPost` | `integer` \| `string` | InfoboardPost ID or hash. |

**Request Keys**

| Key                                   | Type                 | Description                                                                                    |
| ------------------------------------- | -------------------- | ---------------------------------------------------------------------------------------------- |
| `lang_id`                             | `string`             | Language key for the translatable content.                                                     |
| `infoboard_channel_id`                | `integer` \| `null`  | ID of the [InfoboardChannel](/api-reference/infoboard/infoboard-channels) containing the post. |
| `title`                               | `string`             | Post title.                                                                                    |
| `text`                                | `string`             | Post content, which can contain HTML.                                                          |
| `reading_confirmation`                | `boolean`            | Whether users must explicitly confirm reading.                                                 |
| `comments_allowed`                    | `boolean`            | Whether comments can be created.                                                               |
| `comment_user_confirmation_required`  | `boolean`            | Whether comment creation requires a confirmed non-group user identity.                         |
| `comment_visibility_constraint_type`  | `string` \| `null`   | Default [comment visibility constraint](#comment-visibility-constraint-types).                 |
| `force_comment_visibility_constraint` | `boolean`            | Whether comments must use the post's configured constraint.                                    |
| `pinned`                              | `boolean`            | Whether the post is pinned.                                                                    |
| `pinned_end`                          | `datetime` \| `null` | Future date at which pinning ends. Relative dates are accepted.                                |
| `planned_publish_at`                  | `datetime` \| `null` | Future publication date. Relative dates are accepted.                                          |
| `delete_interval`                     | `string` \| `null`   | ISO 8601 date interval after which the post can be deleted automatically.                      |
| `assign_mode`                         | `string` \| `null`   | Supported [assignment mode](#assignment-modes).                                                |
| `department_ids`                      | `array` \| `null`    | IDs of departments targeted by the post.                                                       |

**Behavior**

* A client-provided `user_id` or `user_ids` value is ignored. Changing `infoboard_channel_id`, `title`, or `text` makes the authenticated user the owner.
* Changing `title` or `text` removes stored translations for the changed source fields.
* Changing the channel without `department_ids` clears department targets. Any channel change also resets seen users, read users, comments, reactions, and targeted survey votes before new departments are synchronized.
* Department assignment follows the same `null` or `any_of` rules and allowed-department filtering as creation.
* Changing `planned_publish_at` to a valid future value schedules the post and clears `published_at`; changing it to `null`, now, or the past publishes immediately. A published post cannot be republished before its existing `published_at` value.
* Pin fields are ignored without pin permission, and `delete_interval` is ignored without delete permission.
* Relevant content, publication, and Targetable changes can trigger updated-post notifications.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/infoboard/posts/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Quarterly town hall and Q&A',
        'text' => '<p>The town hall and Q&A take place on Friday.</p>',
        'pinned' => true,
        'pinned_end' => '2026-08-08 09:00:00'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "infoboard_channel_id": 1,
    "lang_id": "en-US",
    "title": "Quarterly town hall and Q&A",
    "text": "<p>The town hall and Q&A take place on Friday.</p>",
    "reading_confirmation": true,
    "reactions_forbidden": false,
    "comments_allowed": true,
    "comment_user_confirmation_required": false,
    "comment_visibility_constraint_type": null,
    "force_comment_visibility_constraint": false,
    "pinned": true,
    "pinned_end": "2026-08-08 09:00:00",
    "planned_publish_at": null,
    "published_at": "2026-08-06 09:30:00",
    "delete_interval": null,
    "assign_mode": "any_of",
    "created_at": "2026-08-06 09:30:00",
    "updated_at": "2026-08-06 10:30:00",
    "deleted_at": null,
    "hash": "wrv2jd6x8g1nmlk4o970mqen"
  }
}
```

## Delete

Delete an existing `InfoboardPost`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/infoboard/posts/{infoboardPost}`

**Route Parameters**

| Parameter       | Type                  | Description               |
| --------------- | --------------------- | ------------------------- |
| `infoboardPost` | `integer` \| `string` | InfoboardPost ID or hash. |

**Behavior**

* The post is soft-deleted.
* Notifications targeting the post are deleted, and every related comment runs its normal deletion lifecycle.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/infoboard/posts/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# InfoboardChannels

## Introduction

`InfoboardChannels` organize [InfoboardPosts](/api-reference/infoboard/infoboard-posts) and own the Entity Permissions that control channel access and administration.

The `apply_allowed_departments` setting additionally constrains which departments can be targeted by posts moved to or created in a channel.

The `notifications_enabled` setting controls whether posts, comments, and reactions in the channel can dispatch notifications. It does not affect resource visibility or seen and read tracking.

## Model Definition

**Alias**

`infoboardChannel`

**Move Posts Assignment Modes**

* `any_of` - Assigns every moved post to the supplied departments.
* `null` - Removes explicit targeting when the target channel permits untargeted posts.

**Relations**

| Key                 | Relation                                                   | Type       | Relation Field(s)                                                            |
| ------------------- | ---------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------- |
| `user`              | [User](/api-reference/users)                               | Belongs to | `user_id`                                                                    |
| `folder`            | [Folder](/api-reference/folders)                           | Belongs to | `folder_id`                                                                  |
| `icon`              | [Icon](/api-reference/icons)                               | Belongs to | `icon_id`                                                                    |
| `entityPermissions` | [EntityPermissions](/api-reference/entity-permissions)     | Morph many | `entity_permissions.restrictable_type`, `entity_permissions.restrictable_id` |
| `infoboardPosts`    | [InfoboardPosts](/api-reference/infoboard/infoboard-posts) | Has many   | `infoboard_posts.infoboard_channel_id`                                       |

**Capabilities**

* [Entity Permissions](/introduction/resource-capabilities/entity-permissions) - Channels own direct grants; their Restricted Scope controls viewing, post creation, commenting, and administration without changing post Targetables.
* [URL Context](/introduction/resource-capabilities/url-context) - Channel URLs resolve to access-checked context information.
* [Translations](/introduction/resource-capabilities/translations) - The `title` field is translatable.

## List

List `InfoboardChannels` visible to the current authenticated [User](/api-reference/users).

**Definition**

<mark style="color:green;">`GET`</mark> `/api/infoboard/channels`

**Behavior**

* The channel Restricted Scope uses direct Entity Permissions with the `view` key and includes ownership and configured extended department access.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/infoboard/channels', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "lang_id": "en-US",
    "folder_id": null,
    "icon_id": 1,
    "title": "Company updates",
    "color": "#2457C5",
    "apply_allowed_departments": false,
    "notifications_enabled": true,
    "sort_number": 1,
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 09:00:00",
    "deleted_at": null
  },
  {
    "id": 2,
    "user_id": 3,
    "lang_id": "en-US",
    "folder_id": null,
    "icon_id": 2,
    "title": "Operations",
    "color": null,
    "apply_allowed_departments": true,
    "notifications_enabled": false,
    "sort_number": 2,
    "created_at": "2026-08-06 09:15:00",
    "updated_at": "2026-08-06 09:15:00",
    "deleted_at": null
  }
]
```

## Show

Show one visible `InfoboardChannel`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/infoboard/channels/{infoboardChannel}`

**Route Parameters**

| Parameter          | Type      | Description          |
| ------------------ | --------- | -------------------- |
| `infoboardChannel` | `integer` | InfoboardChannel ID. |

**Behavior**

* The same direct Entity Permissions and ownership rules as the list endpoint apply.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/infoboard/channels/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "lang_id": "en-US",
  "folder_id": null,
  "icon_id": 1,
  "title": "Company updates",
  "color": "#2457C5",
  "apply_allowed_departments": false,
  "notifications_enabled": true,
  "sort_number": 1,
  "created_at": "2026-08-06 09:00:00",
  "updated_at": "2026-08-06 09:00:00",
  "deleted_at": null
}
```

## Admin: List

List `InfoboardChannels` available for administration.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/infoboard/channels`

**Behavior**

* Without unrestricted channel edit or delete permission, the result contains channels owned by the authenticated user or granted through direct Entity Permissions for administration.
* Soft-deleted channels can be included through the shared query controls.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/infoboard/channels', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "lang_id": "en-US",
    "folder_id": null,
    "icon_id": 1,
    "title": "Company updates",
    "color": "#2457C5",
    "apply_allowed_departments": false,
    "notifications_enabled": true,
    "sort_number": 1,
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 09:00:00",
    "deleted_at": null
  },
  {
    "id": 2,
    "user_id": 4,
    "lang_id": "en-US",
    "folder_id": null,
    "icon_id": 2,
    "title": "Operations",
    "color": null,
    "apply_allowed_departments": true,
    "notifications_enabled": false,
    "sort_number": 2,
    "created_at": "2026-08-06 09:15:00",
    "updated_at": "2026-08-06 09:15:00",
    "deleted_at": null
  }
]
```

## Admin: Show

Show one `InfoboardChannel` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/infoboard/channels/{infoboardChannel}`

**Route Parameters**

| Parameter          | Type      | Description          |
| ------------------ | --------- | -------------------- |
| `infoboardChannel` | `integer` | InfoboardChannel ID. |

**Behavior**

* Access requires ownership, a direct administration grant, or unrestricted channel edit or delete permission.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/infoboard/channels/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "lang_id": "en-US",
  "folder_id": null,
  "icon_id": 1,
  "title": "Company updates",
  "color": "#2457C5",
  "apply_allowed_departments": false,
  "notifications_enabled": true,
  "sort_number": 1,
  "created_at": "2026-08-06 09:00:00",
  "updated_at": "2026-08-06 09:00:00",
  "deleted_at": null
}
```

## Admin: Create

Create a new `InfoboardChannel`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/infoboard/channels`

**Request Keys**

| Key                         | Type                | Default         | Description                                                                       |
| --------------------------- | ------------------- | --------------- | --------------------------------------------------------------------------------- |
| `lang_id`                   | `string`            | system language | Language key for the translatable title.                                          |
| `folder_id`                 | `integer` \| `null` | `null`          | ID of the [Folder](/api-reference/folders) containing the channel.                |
| `icon_id`\*                 | `integer`           | -               | ID of the [Icon](/api-reference/icons) used by the channel.                       |
| `title`\*                   | `string`            | -               | Channel title.                                                                    |
| `color`                     | `string` \| `null`  | `null`          | Channel color as a client-defined string.                                         |
| `apply_allowed_departments` | `boolean`           | `false`         | Whether post department targets are limited to departments the author may assign. |
| `notifications_enabled`     | `boolean`           | `true`          | Whether posts, comments, and reactions in the channel may dispatch notifications. |
| `sort_number`               | `integer`           | end of folder   | Position among channels with the same `folder_id`.                                |

Keys with `*` are required.

**Behavior**

* The authenticated user becomes the owner; a client-provided `user_id` is ignored.
* New channels enable notification dispatch by default. Setting `notifications_enabled` to `false` suppresses channel post, comment, and reaction notifications.
* Missing, invalid, or out-of-range `sort_number` values are normalized within the selected folder.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/infoboard/channels', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'icon_id' => 3,
        'title' => 'People and culture',
        'color' => '#7B3FB5'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "lang_id": "en-US",
    "folder_id": null,
    "icon_id": 3,
    "title": "People and culture",
    "color": "#7B3FB5",
    "apply_allowed_departments": false,
    "notifications_enabled": true,
    "sort_number": 3,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:00:00",
    "deleted_at": null
  }
}
```

## Admin: Update

Update an existing `InfoboardChannel`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/infoboard/channels/{infoboardChannel}`

**Route Parameters**

| Parameter          | Type      | Description          |
| ------------------ | --------- | -------------------- |
| `infoboardChannel` | `integer` | InfoboardChannel ID. |

**Request Keys**

| Key                         | Type                | Description                                                                       |
| --------------------------- | ------------------- | --------------------------------------------------------------------------------- |
| `lang_id`                   | `string`            | Language key for the translatable title.                                          |
| `folder_id`                 | `integer` \| `null` | ID of the [Folder](/api-reference/folders) containing the channel.                |
| `icon_id`                   | `integer`           | ID of the [Icon](/api-reference/icons) used by the channel.                       |
| `title`                     | `string`            | Channel title.                                                                    |
| `color`                     | `string` \| `null`  | Channel color as a client-defined string.                                         |
| `apply_allowed_departments` | `boolean`           | Whether post department targets are limited to departments the author may assign. |
| `notifications_enabled`     | `boolean`           | Whether posts, comments, and reactions in the channel may dispatch notifications. |
| `sort_number`               | `integer`           | Position among channels with the same `folder_id`.                                |

**Behavior**

* A client-provided `user_id` is ignored.
* Disabling `notifications_enabled` suppresses future channel post, comment, and reaction notifications; it does not change existing notification records.
* Changing `folder_id` or `sort_number` normalizes the affected folder order.
* Changing `title` removes stored translations for the previous title.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/infoboard/channels/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'People, culture and benefits',
        'color' => '#653394',
        'notifications_enabled' => false
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "lang_id": "en-US",
    "folder_id": null,
    "icon_id": 3,
    "title": "People, culture and benefits",
    "color": "#653394",
    "apply_allowed_departments": false,
    "notifications_enabled": false,
    "sort_number": 3,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:15:00",
    "deleted_at": null
  }
}
```

## Admin: Move Posts

Move every `InfoboardPost` from one channel to another channel or out of channel scope.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/infoboard/channels/{infoboardChannel}/move-posts`

**Route Parameters**

| Parameter          | Type      | Description                 |
| ------------------ | --------- | --------------------------- |
| `infoboardChannel` | `integer` | Source InfoboardChannel ID. |

**Request Keys**

| Key                    | Type                | Description                                                                               |
| ---------------------- | ------------------- | ----------------------------------------------------------------------------------------- |
| `infoboard_channel_id` | `integer` \| `null` | Target InfoboardChannel ID; omit or send `null` to remove the channel assignment.         |
| `assign_mode`          | `string` \| `null`  | Selects one of the supported [Move Posts Assignment Modes](#move-posts-assignment-modes). |
| `department_ids`       | `array` \| `null`   | Department IDs assigned to every moved post.                                              |

**Behavior**

* The target channel must differ from the source channel, and the authenticated user must be allowed to administer both when a target is supplied.
* The authenticated user becomes the owner of every successfully moved post. Client-provided `user_id` and `user_ids` values are ignored.
* Providing departments defaults `assign_mode` to `any_of`. Without departments it defaults to `null`.
* For no target channel or a target with `apply_allowed_departments` enabled, disallowed department IDs are removed. A target with that setting enabled requires a valid `any_of` assignment with at least one remaining department.
* Changing the channel resets each post's seen users, read users, comments, reactions, and targeted survey votes before the supplied departments are synchronized.
* The response data is the number of posts successfully moved; update events and their notification side effects run for each moved post.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/infoboard/channels/1/move-posts', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'infoboard_channel_id' => 2,
        'department_ids' => [1, 2]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": 2
}
```

## Admin: Delete

Delete an existing `InfoboardChannel`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/infoboard/channels/{infoboardChannel}`

**Route Parameters**

| Parameter          | Type      | Description          |
| ------------------ | --------- | -------------------- |
| `infoboardChannel` | `integer` | InfoboardChannel ID. |

**Behavior**

* The channel is soft-deleted and removed from its sort range.
* All posts in the channel are deleted through their normal deletion lifecycle.
* Every department setting that used this channel as `default_channel_id` is changed to `null`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/infoboard/channels/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# InfoboardComments

## Introduction

`InfoboardComments` provide discussion on an [InfoboardPost](/api-reference/infoboard/infoboard-posts). Their visibility always depends on the parent post and can be narrowed by a comment-specific visibility constraint.

Comment author metadata is derived from the authenticated identity and can account for temporary authentication and group-account confirmation.

The `text` field uses the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`infoboardComment`

**Relations**

| Key               | Relation                                                  | Type       | Relation Field(s)                                                         |
| ----------------- | --------------------------------------------------------- | ---------- | ------------------------------------------------------------------------- |
| `user`            | [User](/api-reference/users)                              | Belongs to | `user_id`                                                                 |
| `post`            | [InfoboardPost](/api-reference/infoboard/infoboard-posts) | Belongs to | `post_id`                                                                 |
| `reactions`       | [Reactions](/api-reference/reactions)                     | Morph many | `reactions.reactable_type`, `reactions.reactable_id`                      |
| `currentReaction` | [Reaction](/api-reference/reactions)                      | Morph one  | `reactions.reactable_type`, `reactions.reactable_id`, `reactions.user_id` |

**Visibility constraint types**

* `department` - Limit the comment to the author's department.
* `private` - Limit the comment to users with the applicable private-comment access and allowed department context.
* `null` - Do not impose a comment-specific visibility constraint.

**Computed Properties**

* `hash` - Hashed representation of the comment `id`.

**Capabilities**

* [Targetables](/introduction/resource-capabilities/targetables) - Comments inherit visibility from the parent post's Targetables and do not accept independent assignment input.
* [Entity Permissions](/introduction/resource-capabilities/entity-permissions) - Comments inherit channel Entity Permissions through their parent post and do not own independent grants.
* [URL Context](/introduction/resource-capabilities/url-context) - Comment URLs resolve to access-checked context information.
* [Translations](/introduction/resource-capabilities/translations) - The `text` field is translatable.
* [Notifications](/introduction/resource-capabilities/notifications) - Creating or changing comment text can notify eligible users connected to the parent post when its channel enables notifications.
* [Reactions](/introduction/resource-capabilities/reactions) - Comments accept reactions when their parent post allows them.

## List

List `InfoboardComments` visible to the current authenticated [User](/api-reference/users).

**Definition**

<mark style="color:green;">`GET`</mark> `/api/infoboard/posts/comments`

**Behavior**

* A comment must be visible through its parent post and satisfy its own `visibility_constraint_type`.
* Comment owners, parent-post owners, and explicit entity grants can provide additional access where the applicable permission strategy allows it.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/infoboard/posts/comments', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 4,
    "group_account_user_id": null,
    "department_id": 1,
    "post_id": 1,
    "lang_id": "en-US",
    "text": "<p>Please add the recording after the town hall.</p>",
    "visibility_constraint_type": null,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:00:00",
    "deleted_at": null,
    "hash": "x9v2km7q4n1pz6rd8ew5g0la"
  },
  {
    "id": 2,
    "user_id": 5,
    "group_account_user_id": null,
    "department_id": 2,
    "post_id": 1,
    "lang_id": "en-US",
    "text": "<p>The slides are available in the shared folder.</p>",
    "visibility_constraint_type": "department",
    "created_at": "2026-08-06 10:10:00",
    "updated_at": "2026-08-06 10:10:00",
    "deleted_at": null,
    "hash": "k4m8pz2q7v1nr5dx9e6w0yla"
  }
]
```

## List by Post

List visible `InfoboardComments` for one `InfoboardPost`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/infoboard/posts/{infoboardPost}/comments`

**Route Parameters**

| Parameter       | Type                  | Description               |
| --------------- | --------------------- | ------------------------- |
| `infoboardPost` | `integer` \| `string` | InfoboardPost ID or hash. |

**Behavior**

* The parent post and every returned comment must be visible to the authenticated user.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/infoboard/posts/1/comments', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 4,
    "group_account_user_id": null,
    "department_id": 1,
    "post_id": 1,
    "lang_id": "en-US",
    "text": "<p>Please add the recording after the town hall.</p>",
    "visibility_constraint_type": null,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:00:00",
    "deleted_at": null,
    "hash": "x9v2km7q4n1pz6rd8ew5g0la"
  },
  {
    "id": 2,
    "user_id": 5,
    "group_account_user_id": null,
    "department_id": 2,
    "post_id": 1,
    "lang_id": "en-US",
    "text": "<p>The slides are available in the shared folder.</p>",
    "visibility_constraint_type": "department",
    "created_at": "2026-08-06 10:10:00",
    "updated_at": "2026-08-06 10:10:00",
    "deleted_at": null,
    "hash": "k4m8pz2q7v1nr5dx9e6w0yla"
  }
]
```

## Show

Show one visible `InfoboardComment` by ID or hash.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/infoboard/posts/comments/{infoboardComment}`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `infoboardComment` | `integer` \| `string` | InfoboardComment ID or hash. |

**Behavior**

* Parent-post visibility and the comment's visibility constraint both apply.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/infoboard/posts/comments/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 4,
  "group_account_user_id": null,
  "department_id": 1,
  "post_id": 1,
  "lang_id": "en-US",
  "text": "<p>Please add the recording after the town hall.</p>",
  "visibility_constraint_type": null,
  "created_at": "2026-08-06 10:00:00",
  "updated_at": "2026-08-06 10:00:00",
  "deleted_at": null,
  "hash": "x9v2km7q4n1pz6rd8ew5g0la"
}
```

## Create

Create a new `InfoboardComment` for one `InfoboardPost`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/infoboard/posts/{infoboardPost}/comments`

**Route Parameters**

| Parameter       | Type                  | Description               |
| --------------- | --------------------- | ------------------------- |
| `infoboardPost` | `integer` \| `string` | InfoboardPost ID or hash. |

**Request Keys**

| Key                          | Type               | Default         | Description                                                            |
| ---------------------------- | ------------------ | --------------- | ---------------------------------------------------------------------- |
| `lang_id`                    | `string`           | system language | Language key for the translatable comment text.                        |
| `text`\*                     | `string`           | -               | Comment content, which can contain HTML.                               |
| `visibility_constraint_type` | `string` \| `null` | `null`          | [Visibility constraint](#visibility-constraint-types) for the comment. |

Keys with `*` are required.

**Behavior**

* The parent post must allow comments, and the authenticated real user must be permitted to comment on it.
* `post_id`, `department_id`, `user_id`, and `group_account_user_id` are derived from the route and authenticated identity; client-provided values are replaced.
* When the post requires user confirmation, the active confirmed user must be a non-group account. Otherwise the real authenticated user becomes the author.
* When the post forces its comment visibility constraint, the post's `comment_visibility_constraint_type` replaces the client value.
* Creating a comment can trigger notifications for eligible users connected to the parent post.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/infoboard/posts/1/comments', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'text' => '<p>Please add the recording after the town hall.</p>'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 4,
    "group_account_user_id": null,
    "department_id": 1,
    "post_id": 1,
    "lang_id": "en-US",
    "text": "<p>Please add the recording after the town hall.</p>",
    "visibility_constraint_type": null,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:00:00",
    "deleted_at": null,
    "hash": "x9v2km7q4n1pz6rd8ew5g0la"
  }
}
```

## Reset Relations

Reset selected dependent relations of an `InfoboardComment`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/infoboard/posts/comments/{infoboardComment}/reset`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `infoboardComment` | `integer` \| `string` | InfoboardComment ID or hash. |

**Request Keys**

| Key         | Type      | Default | Description                   |
| ----------- | --------- | ------- | ----------------------------- |
| `reactions` | `boolean` | `false` | Delete reactions when `true`. |

**Behavior**

* `reactions` must be `true`; otherwise no relation is reset and the operation returns an error response.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/infoboard/posts/comments/1/reset', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'reactions' => true
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 4,
    "group_account_user_id": null,
    "department_id": 1,
    "post_id": 1,
    "lang_id": "en-US",
    "text": "<p>Please add the recording after the town hall.</p>",
    "visibility_constraint_type": null,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:00:00",
    "deleted_at": null,
    "hash": "x9v2km7q4n1pz6rd8ew5g0la"
  }
}
```

## Update

Update an existing `InfoboardComment`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/infoboard/posts/comments/{infoboardComment}`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `infoboardComment` | `integer` \| `string` | InfoboardComment ID or hash. |

**Request Keys**

| Key                          | Type               | Description                                                            |
| ---------------------------- | ------------------ | ---------------------------------------------------------------------- |
| `lang_id`                    | `string`           | Language key for the translatable comment text.                        |
| `text`                       | `string`           | Comment content, which can contain HTML.                               |
| `visibility_constraint_type` | `string` \| `null` | [Visibility constraint](#visibility-constraint-types) for the comment. |

**Behavior**

* `post_id` cannot be changed. Client-provided author and department fields are removed.
* Changing `text` updates `user_id`, `group_account_user_id`, and `department_id` from the authenticated identity and can trigger updated-comment notifications.
* Changing `text` removes its stored translations.
* `visibility_constraint_type` cannot be changed while the parent post forces its configured comment constraint.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/infoboard/posts/comments/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'text' => '<p>Please add the recording and slides after the town hall.</p>'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 4,
    "group_account_user_id": null,
    "department_id": 1,
    "post_id": 1,
    "lang_id": "en-US",
    "text": "<p>Please add the recording and slides after the town hall.</p>",
    "visibility_constraint_type": null,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:15:00",
    "deleted_at": null,
    "hash": "x9v2km7q4n1pz6rd8ew5g0la"
  }
}
```

## Delete

Delete an existing `InfoboardComment`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/infoboard/posts/comments/{infoboardComment}`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `infoboardComment` | `integer` \| `string` | InfoboardComment ID or hash. |

**Behavior**

* The comment is soft-deleted, and notifications targeting it are deleted.
* When the parent post does not require user confirmation, authorization uses the real authenticated user rather than a leftover temporary identity.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/infoboard/posts/comments/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# InfoboardDepartmentSettings

## Introduction

`InfoboardDepartmentSettings` store one settings object per [Department](/api-reference/departments). The current public setting selects an optional default [InfoboardChannel](/api-reference/infoboard/infoboard-channels).

Administration endpoints can upsert settings for one department or apply the same settings to several departments.

## Model Definition

**Alias**

`infoboardDepartmentSettings`

**Relations**

| Key          | Relation                                 | Type       | Relation Field(s) |
| ------------ | ---------------------------------------- | ---------- | ----------------- |
| `user`       | [User](/api-reference/users)             | Belongs to | `user_id`         |
| `department` | [Department](/api-reference/departments) | Belongs to | `department_id`   |

## Show Current

Show the `InfoboardDepartmentSettings` for the authenticated user's primary department.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/infoboard/department-settings/current`

**Behavior**

* The response is `null` when the department has no settings record.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/infoboard/department-settings/current', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "department_id": 1,
  "settings": {
    "default_channel_id": 1
  },
  "created_at": "2026-08-06 09:00:00",
  "updated_at": "2026-08-06 09:00:00",
  "deleted_at": null
}
```

## Admin: List

List all `InfoboardDepartmentSettings` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/infoboard/department-settings`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/infoboard/department-settings', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "department_id": 1,
    "settings": {
      "default_channel_id": 1
    },
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 09:00:00",
    "deleted_at": null
  },
  {
    "id": 2,
    "user_id": 3,
    "department_id": 2,
    "settings": {
      "default_channel_id": 2
    },
    "created_at": "2026-08-06 09:05:00",
    "updated_at": "2026-08-06 09:05:00",
    "deleted_at": null
  }
]
```

## Admin: Show by Department

Show `InfoboardDepartmentSettings` for one department.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/infoboard/department-settings/{department}`

**Route Parameters**

| Parameter    | Type      | Description    |
| ------------ | --------- | -------------- |
| `department` | `integer` | Department ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/infoboard/department-settings/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "department_id": 1,
  "settings": {
    "default_channel_id": 1
  },
  "created_at": "2026-08-06 09:00:00",
  "updated_at": "2026-08-06 09:00:00",
  "deleted_at": null
}
```

## Admin: Create or Update by Department

Create or update `InfoboardDepartmentSettings` for one department.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/infoboard/department-settings/{department}`

**Route Parameters**

| Parameter    | Type      | Description    |
| ------------ | --------- | -------------- |
| `department` | `integer` | Department ID. |

**Request Keys**

| Key                           | Type                | Default | Description                                                                        |
| ----------------------------- | ------------------- | ------- | ---------------------------------------------------------------------------------- |
| `settings`\*                  | `object`            | -       | Complete Infoboard settings object for the department.                             |
| `settings.default_channel_id` | `integer` \| `null` | `null`  | ID of the default [InfoboardChannel](/api-reference/infoboard/infoboard-channels). |

Keys with `*` are required.

**Behavior**

* `department_id` is derived from the route and `user_id` from the authenticated user; client-provided values are replaced.
* An existing record for the department is updated instead of duplicated.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/infoboard/department-settings/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'settings' => [
            'default_channel_id' => 1
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "department_id": 1,
    "settings": {
      "default_channel_id": 1
    },
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 10:00:00",
    "deleted_at": null
  }
}
```

## Admin: Create or Update Multiple Departments

Apply one settings object to several departments.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/infoboard/department-settings`

**Request Keys**

| Key                           | Type                | Default | Description                                                                        |
| ----------------------------- | ------------------- | ------- | ---------------------------------------------------------------------------------- |
| `department_ids`\*            | `array`             | -       | IDs of departments that receive the settings.                                      |
| `settings`\*                  | `object`            | -       | Complete Infoboard settings object applied to every department.                    |
| `settings.default_channel_id` | `integer` \| `null` | `null`  | ID of the default [InfoboardChannel](/api-reference/infoboard/infoboard-channels). |

Keys with `*` are required.

**Behavior**

* New records use the authenticated user as `user_id`.
* Existing records are matched by `department_id`; their `settings` and timestamps are updated while their existing `user_id` remains unchanged.
* The response is the raw list of affected settings records rather than a success wrapper.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/infoboard/department-settings', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'department_ids' => [1, 2],
        'settings' => [
            'default_channel_id' => 2
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "department_id": 1,
    "settings": {
      "default_channel_id": 2
    },
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 10:15:00",
    "deleted_at": null
  },
  {
    "id": 2,
    "user_id": 3,
    "department_id": 2,
    "settings": {
      "default_channel_id": 2
    },
    "created_at": "2026-08-06 09:05:00",
    "updated_at": "2026-08-06 10:15:00",
    "deleted_at": null
  }
]
```

## Admin: Delete by Department

Delete `InfoboardDepartmentSettings` for one department.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/infoboard/department-settings/{department}`

**Route Parameters**

| Parameter    | Type      | Description    |
| ------------ | --------- | -------------- |
| `department` | `integer` | Department ID. |

**Behavior**

* The settings record is soft-deleted when it exists.
* Deleting settings for an existing department without a settings record is idempotent and still returns success.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/infoboard/department-settings/2', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# InfoboardPostSeenUsers

## Introduction

`InfoboardPostSeenUsers` record that a [User](/api-reference/users) has seen an [InfoboardPost](/api-reference/infoboard/infoboard-posts). Each record belongs to one user and one post and cannot be updated directly.

## Model Definition

**Relations**

| Key    | Relation                                                  | Type       | Relation Field(s) |
| ------ | --------------------------------------------------------- | ---------- | ----------------- |
| `user` | [User](/api-reference/users)                              | Belongs to | `user_id`         |
| `post` | [InfoboardPost](/api-reference/infoboard/infoboard-posts) | Belongs to | `post_id`         |

## List by Post

List visible `InfoboardPostSeenUsers` for one `InfoboardPost`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/infoboard/posts/{infoboardPost}/seen-users`

**Route Parameters**

| Parameter       | Type                  | Description               |
| --------------- | --------------------- | ------------------------- |
| `infoboardPost` | `integer` \| `string` | InfoboardPost ID or hash. |

**Behavior**

* The parent post must be visible to the authenticated user.
* Internal intratool users are excluded.
* Without permission to see all post seen users, records are limited to users from departments accessible to the authenticated user.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/infoboard/posts/1/seen-users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 4,
    "post_id": 1,
    "created_at": "2026-08-06 10:05:00"
  },
  {
    "id": 2,
    "user_id": 5,
    "post_id": 1,
    "created_at": "2026-08-06 10:10:00"
  }
]
```

## Create

Record that the authenticated user has seen an `InfoboardPost`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/infoboard/posts/{infoboardPost}/seen-users`

**Route Parameters**

| Parameter       | Type                  | Description               |
| --------------- | --------------------- | ------------------------- |
| `infoboardPost` | `integer` \| `string` | InfoboardPost ID or hash. |

**Behavior**

* `post_id` is derived from the route and `user_id` from the authenticated user. The request body is discarded.
* The operation is idempotent for the same user and post and returns the existing record on repeated requests.
* Creating the record marks matching post notifications as seen and read; it does not create an `InfoboardPostReadUser`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/infoboard/posts/1/seen-users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 4,
    "post_id": 1,
    "created_at": "2026-08-06 10:05:00"
  }
}
```


# InfoboardPostReadUsers

## Introduction

`InfoboardPostReadUsers` record an explicit read confirmation by a [User](/api-reference/users) for an [InfoboardPost](/api-reference/infoboard/infoboard-posts). Each record belongs to one user and one post and cannot be updated directly.

## Model Definition

**Relations**

| Key    | Relation                                                  | Type       | Relation Field(s) |
| ------ | --------------------------------------------------------- | ---------- | ----------------- |
| `user` | [User](/api-reference/users)                              | Belongs to | `user_id`         |
| `post` | [InfoboardPost](/api-reference/infoboard/infoboard-posts) | Belongs to | `post_id`         |

## List by Post

List visible `InfoboardPostReadUsers` for one `InfoboardPost`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/infoboard/posts/{infoboardPost}/read-users`

**Route Parameters**

| Parameter       | Type                  | Description               |
| --------------- | --------------------- | ------------------------- |
| `infoboardPost` | `integer` \| `string` | InfoboardPost ID or hash. |

**Behavior**

* The parent post must be visible to the authenticated user.
* Internal intratool users are excluded.
* Without permission to see all post read users, records are limited to users from departments accessible to the authenticated user.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/infoboard/posts/1/read-users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 4,
    "post_id": 1,
    "created_at": "2026-08-06 10:20:00"
  },
  {
    "id": 2,
    "user_id": 5,
    "post_id": 1,
    "created_at": "2026-08-06 10:25:00"
  }
]
```

## Create

Record an explicit read confirmation by the authenticated user for an `InfoboardPost`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/infoboard/posts/{infoboardPost}/read-users`

**Route Parameters**

| Parameter       | Type                  | Description               |
| --------------- | --------------------- | ------------------------- |
| `infoboardPost` | `integer` \| `string` | InfoboardPost ID or hash. |

**Behavior**

* `post_id` is derived from the route and `user_id` from the authenticated user. The request body is discarded.
* Group accounts cannot create read confirmations.
* The operation is idempotent for the same user and post and returns the existing record on repeated requests.
* A matching `InfoboardPostSeenUser` is created first when missing, and matching post notifications are marked as seen and read.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/infoboard/posts/1/read-users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 4,
    "post_id": 1,
    "created_at": "2026-08-06 10:20:00"
  }
}
```


# Manual

The Manual module organizes long-form internal content in nested [ManualChapters](/api-reference/manual/manual-chapters). Chapters own direct Entity Permissions and can contain child chapters.

[ManualEntries](/api-reference/manual/manual-entries) contain the published or scheduled content. Entries can be nested, inherit access constraints from their parent entries and chapter, support translations and reactions, and can require reading confirmation.

[ManualEntrySeenUsers](/api-reference/manual/manual-entry-seen-users) record when users view an entry, while [ManualEntryReadUsers](/api-reference/manual/manual-entry-read-users) record explicit reading confirmations.


# ManualChapters

## Introduction

`ManualChapters` organize [ManualEntries](/api-reference/manual/manual-entries) in a nested structure and define the Entity Permissions inherited by their descendants.

## Model Definition

**Alias**

`manualChapter`

**Relations**

| Key                 | Relation                                                | Type            | Relation Field(s)                                                            |
| ------------------- | ------------------------------------------------------- | --------------- | ---------------------------------------------------------------------------- |
| `user`              | [User](/api-reference/users)                            | Belongs to      | `user_id`                                                                    |
| `parent`            | [ManualChapter](/api-reference/manual/manual-chapters)  | Belongs to      | `parent_id`                                                                  |
| `children`          | [ManualChapters](/api-reference/manual/manual-chapters) | Has many        | `parent_id`                                                                  |
| `manualEntries`     | [ManualEntries](/api-reference/manual/manual-entries)   | Has many        | `manual_chapter_id`                                                          |
| `icon`              | [Icon](/api-reference/icons)                            | Belongs to      | `icon_id`                                                                    |
| `entityPermissions` | [EntityPermissions](/api-reference/entity-permissions)  | Morph many      | `entity_permissions.restrictable_type`, `entity_permissions.restrictable_id` |
| `tags`              | [Tags](/api-reference/tags/tags)                        | Belongs to many | `tag_taggable.taggable_type`, `tag_taggable.taggable_id`                     |

**Computed Properties**

* `hash` - Hashed representation of the chapter `id`.

**Capabilities**

* [Entity Permissions](/introduction/resource-capabilities/entity-permissions) - Chapters own direct `view` and `administrate` grants; descendant chapters and entries are additionally constrained by their ancestors.
* [URL Context](/introduction/resource-capabilities/url-context) - Chapter URLs resolve to access-checked context information.
* [Translations](/introduction/resource-capabilities/translations) - The `title` field is translatable.
* [Notifications](/introduction/resource-capabilities/notifications) - Creating or updating an active chapter can notify eligible users selected through its inherited access chain.

## List

List `ManualChapters` visible to the current authenticated [User](/api-reference/users).

**Definition**

<mark style="color:green;">`GET`</mark> `/api/manual/chapters`

**Behavior**

* The Restricted Scope combines direct `view` grants with the access constraints of every parent chapter.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/manual/chapters', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "parent_id": null,
    "lang_id": "en-US",
    "icon_id": 20,
    "title": "Employee handbook",
    "slug": "employee-handbook",
    "enumeration": false,
    "active": true,
    "sort_number": 1,
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 09:00:00",
    "deleted_at": null,
    "hash": "zn7m24owk63qolxryge8pj05"
  },
  {
    "id": 2,
    "user_id": 4,
    "parent_id": 1,
    "lang_id": "en-US",
    "icon_id": 21,
    "title": "Policies",
    "slug": "policies",
    "enumeration": true,
    "active": false,
    "sort_number": 1,
    "created_at": "2026-08-06 09:10:00",
    "updated_at": "2026-08-06 09:15:00",
    "deleted_at": null,
    "hash": "6o8m0kz5yw10x1pr9e4vxj27"
  }
]
```

## Show

Show a single `ManualChapter` by ID, hash, or slug.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/manual/chapters/{manualChapter}`

**Route Parameters**

| Parameter       | Type                  | Description                      |
| --------------- | --------------------- | -------------------------------- |
| `manualChapter` | `integer` \| `string` | ManualChapter ID, hash, or slug. |

**Behavior**

* Access is checked recursively against the chapter and every parent chapter.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/manual/chapters/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "parent_id": null,
  "lang_id": "en-US",
  "icon_id": 20,
  "title": "Employee handbook",
  "slug": "employee-handbook",
  "enumeration": false,
  "active": true,
  "sort_number": 1,
  "created_at": "2026-08-06 09:00:00",
  "updated_at": "2026-08-06 09:00:00",
  "deleted_at": null,
  "hash": "zn7m24owk63qolxryge8pj05"
}
```

## Admin: List

List `ManualChapters` available in the administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/manual/chapters`

**Behavior**

* Administration permissions determine whether the result is unrestricted or limited to owned and permitted chapters.
* Soft-deleted chapters are included.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/manual/chapters', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "parent_id": null,
    "lang_id": "en-US",
    "icon_id": 20,
    "title": "Employee handbook",
    "slug": "employee-handbook",
    "enumeration": false,
    "active": true,
    "sort_number": 1,
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 09:00:00",
    "deleted_at": null,
    "hash": "zn7m24owk63qolxryge8pj05"
  },
  {
    "id": 2,
    "user_id": 4,
    "parent_id": 1,
    "lang_id": "en-US",
    "icon_id": 21,
    "title": "Policies",
    "slug": "policies",
    "enumeration": true,
    "active": false,
    "sort_number": 1,
    "created_at": "2026-08-06 09:10:00",
    "updated_at": "2026-08-06 09:15:00",
    "deleted_at": null,
    "hash": "6o8m0kz5yw10x1pr9e4vxj27"
  }
]
```

## Admin: Show

Show a single `ManualChapter` in the administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/manual/chapters/{manualChapter}`

**Route Parameters**

| Parameter       | Type                  | Description                      |
| --------------- | --------------------- | -------------------------------- |
| `manualChapter` | `integer` \| `string` | ManualChapter ID, hash, or slug. |

**Behavior**

* The user must have unrestricted chapter administration access, own the chapter, or receive an applicable `administrate` grant through its hierarchy.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/manual/chapters/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "parent_id": null,
  "lang_id": "en-US",
  "icon_id": 20,
  "title": "Employee handbook",
  "slug": "employee-handbook",
  "enumeration": false,
  "active": true,
  "sort_number": 1,
  "created_at": "2026-08-06 09:00:00",
  "updated_at": "2026-08-06 09:00:00",
  "deleted_at": null,
  "hash": "zn7m24owk63qolxryge8pj05"
}
```

## Admin: Create

Create a new `ManualChapter`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/manual/chapters`

**Request Keys**

| Key                  | Type                | Default                 | Description                                                                            |
| -------------------- | ------------------- | ----------------------- | -------------------------------------------------------------------------------------- |
| `parent_id`          | `integer` \| `null` | `null`                  | ID of the parent [ManualChapter](/api-reference/manual/manual-chapters).               |
| `lang_id`            | `string`            | system language         | Language key for the translatable title.                                               |
| `icon_id`            | `integer`           | configured chapter icon | ID of the [Icon](/api-reference/icons) used by the chapter.                            |
| `title`\*            | `string`            | -                       | Chapter title, unique among siblings with the same `parent_id`.                        |
| `slug`               | `string`            | slugged `title`         | Chapter slug, unique among siblings with the same `parent_id`.                         |
| `enumeration`        | `boolean`           | `false`                 | Whether clients should enumerate the chapter.                                          |
| `active`             | `boolean`           | `true`                  | Whether the chapter is active.                                                         |
| `sort_number`        | `integer`           | end of parent           | Position among chapters with the same `parent_id`.                                     |
| `entity_permissions` | `array`             | -                       | Direct [EntityPermissions](/api-reference/entity-permissions) assigned to the chapter. |

Keys with `*` are required.

**Behavior**

* The authenticated user becomes the owner; a client-provided `user_id` is ignored.
* The slug is normalized from `slug` or `title`, and missing, invalid, or out-of-range `sort_number` values are normalized within the selected parent.
* Creating an active chapter can notify eligible users selected by its direct and inherited `view` grants.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/manual/chapters', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'icon_id' => 20,
        'title' => 'Safety procedures',
        'enumeration' => true
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "parent_id": null,
    "lang_id": "en-US",
    "icon_id": 20,
    "title": "Safety procedures",
    "slug": "safety-procedures",
    "enumeration": true,
    "active": true,
    "sort_number": 2,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:00:00",
    "deleted_at": null,
    "hash": "wrv2jd6x8g1nmlk4o970mqen"
  }
}
```

## Admin: Update

Update an existing `ManualChapter`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/manual/chapters/{manualChapter}`

**Route Parameters**

| Parameter       | Type                  | Description                      |
| --------------- | --------------------- | -------------------------------- |
| `manualChapter` | `integer` \| `string` | ManualChapter ID, hash, or slug. |

**Request Keys**

| Key                  | Type                | Description                                                                                  |
| -------------------- | ------------------- | -------------------------------------------------------------------------------------------- |
| `parent_id`          | `integer` \| `null` | New parent chapter ID.                                                                       |
| `lang_id`            | `string`            | New language key for the translatable title.                                                 |
| `icon_id`            | `integer`           | New [Icon](/api-reference/icons) ID.                                                         |
| `title`              | `string`            | New title, unique among siblings with the resulting `parent_id`.                             |
| `slug`               | `string`            | New slug, unique among siblings with the resulting `parent_id`.                              |
| `enumeration`        | `boolean`           | Whether clients should enumerate the chapter.                                                |
| `active`             | `boolean`           | Whether the chapter is active.                                                               |
| `sort_number`        | `integer`           | New position among chapters with the resulting `parent_id`.                                  |
| `entity_permissions` | `array`             | Complete direct [EntityPermissions](/api-reference/entity-permissions) state to synchronize. |

**Behavior**

* A client-provided `user_id` is ignored. Changing the parent, title, or slug transfers ownership to the authenticated user.
* When `parent_id` is omitted, the current parent is retained for validation. Changing the parent or sort position normalizes both affected sibling orders.
* The slug is normalized from an explicitly supplied `slug` or the updated `title`.
* Changing `title` removes stored translations for the previous title. Activating a chapter or changing its title can trigger notifications.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/manual/chapters/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Safety and emergency procedures',
        'active' => false
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "parent_id": null,
    "lang_id": "en-US",
    "icon_id": 20,
    "title": "Safety and emergency procedures",
    "slug": "safety-and-emergency-procedures",
    "enumeration": true,
    "active": false,
    "sort_number": 2,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:15:00",
    "deleted_at": null,
    "hash": "wrv2jd6x8g1nmlk4o970mqen"
  }
}
```

## Admin: Delete

Delete an existing `ManualChapter`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/manual/chapters/{manualChapter}`

**Route Parameters**

| Parameter       | Type                  | Description                      |
| --------------- | --------------------- | -------------------------------- |
| `manualChapter` | `integer` \| `string` | ManualChapter ID, hash, or slug. |

**Behavior**

* The chapter is soft-deleted, its position is removed from the sibling order, and its notifications are deleted.
* Descendant chapters and contained entries are deleted recursively.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/manual/chapters/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# ManualEntries

## Introduction

`ManualEntries` contain the Manual content organized by [ManualChapters](/api-reference/manual/manual-chapters). An entry can belong directly to a chapter or to another entry, and its visibility is constrained by every parent in that hierarchy.

Entries can be published immediately or scheduled, support translations and reactions, and expose [seen](/api-reference/manual/manual-entry-seen-users) and [read](/api-reference/manual/manual-entry-read-users) tracking.

The `text` field uses the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`manualEntry`

**Relations**

| Key                 | Relation                                                              | Type            | Relation Field(s)                                                            |
| ------------------- | --------------------------------------------------------------------- | --------------- | ---------------------------------------------------------------------------- |
| `user`              | [User](/api-reference/users)                                          | Belongs to      | `user_id`                                                                    |
| `manualChapter`     | [ManualChapter](/api-reference/manual/manual-chapters)                | Belongs to      | `manual_chapter_id`                                                          |
| `parent`            | [ManualEntry](/api-reference/manual/manual-entries)                   | Belongs to      | `parent_id`                                                                  |
| `children`          | [ManualEntries](/api-reference/manual/manual-entries)                 | Has many        | `parent_id`                                                                  |
| `icon`              | [Icon](/api-reference/icons)                                          | Belongs to      | `icon_id`                                                                    |
| `entityPermissions` | [EntityPermissions](/api-reference/entity-permissions)                | Morph many      | `entity_permissions.restrictable_type`, `entity_permissions.restrictable_id` |
| `seenUsers`         | [ManualEntrySeenUsers](/api-reference/manual/manual-entry-seen-users) | Has many        | `manual_entry_seen_users.manual_entry_id`                                    |
| `readUsers`         | [ManualEntryReadUsers](/api-reference/manual/manual-entry-read-users) | Has many        | `manual_entry_read_users.manual_entry_id`                                    |
| `reactions`         | [Reactions](/api-reference/reactions)                                 | Morph many      | `reactions.reactable_type`, `reactions.reactable_id`                         |
| `currentReaction`   | [Reaction](/api-reference/reactions)                                  | Morph one       | `reactions.reactable_type`, `reactions.reactable_id`, `reactions.user_id`    |
| `tags`              | [Tags](/api-reference/tags/tags)                                      | Belongs to many | `tag_taggable.taggable_type`, `tag_taggable.taggable_id`                     |

**Computed Properties**

* `hash` - Hashed representation of the entry `id`.

**Capabilities**

* [Targetables](/introduction/resource-capabilities/targetables) - Eligible users and departments are derived from the intersecting `view` grants of the entry, its parent entries, and its root chapter.
* [Entity Permissions](/introduction/resource-capabilities/entity-permissions) - Entries own direct `view` and `administrate` grants and inherit additional constraints from parent entries and their chapter.
* [URL Context](/introduction/resource-capabilities/url-context) - Entry URLs resolve to access-checked context information.
* [Translations](/introduction/resource-capabilities/translations) - The `title` and `text` fields are translatable.
* [Notifications](/introduction/resource-capabilities/notifications) - Publishing and relevant content or confirmation changes can notify eligible users.
* [Reactions](/introduction/resource-capabilities/reactions) - Entries accept reactions when `reactions_forbidden` is `false`.
* [Seen and Read Tracking](/introduction/resource-capabilities/seen-and-read-tracking) - Seen records track views, read records capture explicit confirmations, and `reading_confirmation` tells clients whether confirmation is required.

## List

List `ManualEntries` visible to the current authenticated [User](/api-reference/users).

**Definition**

<mark style="color:green;">`GET`</mark> `/api/manual/entries`

**Behavior**

* The Restricted Scope applies the entry's direct Entity Permissions together with every parent entry and chapter constraint.
* Scheduled entries are visible to their owner and to users with applicable administration access; other users see them only after publication.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/manual/entries', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "manual_chapter_id": 1,
    "lang_id": "en-US",
    "parent_id": null,
    "icon_id": 21,
    "title": "Welcome to the company",
    "slug": "welcome-to-the-company",
    "text": "<p>This guide explains the first steps for new employees.</p>",
    "reading_confirmation": true,
    "reactions_forbidden": false,
    "planned_publish_at": null,
    "published_at": "2026-08-06 09:20:00",
    "sort_number": 1,
    "created_at": "2026-08-06 09:20:00",
    "updated_at": "2026-08-06 09:20:00",
    "deleted_at": null,
    "hash": "zn7m24owk63qolxryge8pj05"
  },
  {
    "id": 2,
    "user_id": 3,
    "manual_chapter_id": null,
    "lang_id": "en-US",
    "parent_id": 1,
    "icon_id": 22,
    "title": "First-week checklist",
    "slug": "first-week-checklist",
    "text": "<p>Complete your profile and review the security guidelines.</p>",
    "reading_confirmation": false,
    "reactions_forbidden": false,
    "planned_publish_at": "2026-08-08 09:00:00",
    "published_at": null,
    "sort_number": 1,
    "created_at": "2026-08-06 09:25:00",
    "updated_at": "2026-08-06 09:25:00",
    "deleted_at": null,
    "hash": "6o8m0kz5yw10x1pr9e4vxj27"
  }
]
```

## Show

Show a single `ManualEntry` by ID, hash, or slug.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/manual/entries/{manualEntry}`

**Route Parameters**

| Parameter     | Type                  | Description                    |
| ------------- | --------------------- | ------------------------------ |
| `manualEntry` | `integer` \| `string` | ManualEntry ID, hash, or slug. |

**Behavior**

* Access is checked recursively against the entry, its parent entries, and its chapter.
* A scheduled entry is visible before publication only to its owner or a user with applicable administration access.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/manual/entries/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "manual_chapter_id": 1,
  "lang_id": "en-US",
  "parent_id": null,
  "icon_id": 21,
  "title": "Welcome to the company",
  "slug": "welcome-to-the-company",
  "text": "<p>This guide explains the first steps for new employees.</p>",
  "reading_confirmation": true,
  "reactions_forbidden": false,
  "planned_publish_at": null,
  "published_at": "2026-08-06 09:20:00",
  "sort_number": 1,
  "created_at": "2026-08-06 09:20:00",
  "updated_at": "2026-08-06 09:20:00",
  "deleted_at": null,
  "hash": "zn7m24owk63qolxryge8pj05"
}
```

## Admin: List

List `ManualEntries` available in the administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/manual/entries`

**Behavior**

* Administration permissions determine whether the result is unrestricted or limited to owned and permitted entries.
* Soft-deleted and scheduled entries are included when they satisfy the administration scope.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/manual/entries', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "manual_chapter_id": 1,
    "lang_id": "en-US",
    "parent_id": null,
    "icon_id": 21,
    "title": "Welcome to the company",
    "slug": "welcome-to-the-company",
    "text": "<p>This guide explains the first steps for new employees.</p>",
    "reading_confirmation": true,
    "reactions_forbidden": false,
    "planned_publish_at": null,
    "published_at": "2026-08-06 09:20:00",
    "sort_number": 1,
    "created_at": "2026-08-06 09:20:00",
    "updated_at": "2026-08-06 09:20:00",
    "deleted_at": null,
    "hash": "zn7m24owk63qolxryge8pj05"
  },
  {
    "id": 2,
    "user_id": 3,
    "manual_chapter_id": null,
    "lang_id": "en-US",
    "parent_id": 1,
    "icon_id": 22,
    "title": "First-week checklist",
    "slug": "first-week-checklist",
    "text": "<p>Complete your profile and review the security guidelines.</p>",
    "reading_confirmation": false,
    "reactions_forbidden": false,
    "planned_publish_at": "2026-08-08 09:00:00",
    "published_at": null,
    "sort_number": 1,
    "created_at": "2026-08-06 09:25:00",
    "updated_at": "2026-08-06 09:25:00",
    "deleted_at": null,
    "hash": "6o8m0kz5yw10x1pr9e4vxj27"
  }
]
```

## Admin: Show

Show a single `ManualEntry` in the administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/manual/entries/{manualEntry}`

**Route Parameters**

| Parameter     | Type                  | Description                    |
| ------------- | --------------------- | ------------------------------ |
| `manualEntry` | `integer` \| `string` | ManualEntry ID, hash, or slug. |

**Behavior**

* The user must have unrestricted entry administration access, own the entry, or receive an applicable `administrate` grant through its entry and chapter hierarchy.
* Scheduled entries remain available in this scope.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/manual/entries/2', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 2,
  "user_id": 3,
  "manual_chapter_id": null,
  "lang_id": "en-US",
  "parent_id": 1,
  "icon_id": 22,
  "title": "First-week checklist",
  "slug": "first-week-checklist",
  "text": "<p>Complete your profile and review the security guidelines.</p>",
  "reading_confirmation": false,
  "reactions_forbidden": false,
  "planned_publish_at": "2026-08-08 09:00:00",
  "published_at": null,
  "sort_number": 1,
  "created_at": "2026-08-06 09:25:00",
  "updated_at": "2026-08-06 09:25:00",
  "deleted_at": null,
  "hash": "6o8m0kz5yw10x1pr9e4vxj27"
}
```

## Admin: Create

Create a new `ManualEntry`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/manual/entries`

**Request Keys**

| Key                    | Type                 | Default               | Description                                                                                                         |
| ---------------------- | -------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `parent_id`            | `integer` \| `null`  | `null`                | ID of the parent [ManualEntry](/api-reference/manual/manual-entries). Required when `manual_chapter_id` is omitted. |
| `manual_chapter_id`    | `integer` \| `null`  | `null`                | ID of the containing [ManualChapter](/api-reference/manual/manual-chapters). Required when `parent_id` is omitted.  |
| `lang_id`              | `string`             | system language       | Language key for the translatable content.                                                                          |
| `icon_id`              | `integer`            | configured entry icon | ID of the [Icon](/api-reference/icons) used by the entry.                                                           |
| `title`\*              | `string`             | -                     | Entry title, unique across the selected chapter hierarchy.                                                          |
| `slug`                 | `string`             | slugged `title`       | Entry slug, unique across the selected chapter hierarchy.                                                           |
| `text`\*               | `string`             | -                     | Entry content, which can contain HTML.                                                                              |
| `reading_confirmation` | `boolean`            | `false`               | Whether users must explicitly confirm reading.                                                                      |
| `planned_publish_at`   | `datetime` \| `null` | `null`                | Future publication date. Relative dates are accepted.                                                               |
| `sort_number`          | `integer`            | end of parent         | Position among entries with the same `manual_chapter_id` and `parent_id`.                                           |
| `entity_permissions`   | `array`              | -                     | Direct [EntityPermissions](/api-reference/entity-permissions) assigned to the entry.                                |

Keys with `*` are required.

**Behavior**

* The authenticated user becomes the owner; a client-provided `user_id` is ignored.
* At least one of `parent_id` and `manual_chapter_id` is required. When both are supplied, `parent_id` takes precedence and `manual_chapter_id` becomes `null`.
* The slug is normalized from `slug` or `title`, and missing, invalid, or out-of-range `sort_number` values are normalized within the selected parent.
* A future `planned_publish_at` stores `published_at` as `null`. A missing, current, or past value publishes immediately and stores `planned_publish_at` as `null`; clients cannot set `published_at` independently.
* Creating an immediately published entry can notify eligible users. The notification requests confirmation when `reading_confirmation` is `true`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/manual/entries', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'manual_chapter_id' => 1,
        'lang_id' => 'en-US',
        'icon_id' => 21,
        'title' => 'Emergency procedures',
        'text' => '<p>Follow these steps during an emergency.</p>',
        'reading_confirmation' => true,
        'planned_publish_at' => '2026-08-08 10:00:00'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "manual_chapter_id": 1,
    "lang_id": "en-US",
    "parent_id": null,
    "icon_id": 21,
    "title": "Emergency procedures",
    "slug": "emergency-procedures",
    "text": "<p>Follow these steps during an emergency.</p>",
    "reading_confirmation": true,
    "reactions_forbidden": false,
    "planned_publish_at": "2026-08-08 10:00:00",
    "published_at": null,
    "sort_number": 2,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:00:00",
    "deleted_at": null,
    "hash": "wrv2jd6x8g1nmlk4o970mqen"
  }
}
```

## Admin: Reset Relations

Reset selected tracking and reaction relations of an existing `ManualEntry`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/manual/entries/{manualEntry}/reset`

**Route Parameters**

| Parameter     | Type                  | Description                    |
| ------------- | --------------------- | ------------------------------ |
| `manualEntry` | `integer` \| `string` | ManualEntry ID, hash, or slug. |

**Request Keys**

| Key         | Type      | Description                                                                                   |
| ----------- | --------- | --------------------------------------------------------------------------------------------- |
| `seenUsers` | `boolean` | Delete all [ManualEntrySeenUsers](/api-reference/manual/manual-entry-seen-users) when `true`. |
| `readUsers` | `boolean` | Delete all [ManualEntryReadUsers](/api-reference/manual/manual-entry-read-users) when `true`. |
| `reactions` | `boolean` | Delete all [Reactions](/api-reference/reactions) when `true`.                                 |

**Behavior**

* Only the three resettable relation keys are retained from the request.
* At least one relation must be set to `true`; an empty request or all-`false` request returns an error.
* Resetting relations is idempotent with respect to already absent records.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/manual/entries/3/reset', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'seenUsers' => true,
        'readUsers' => true
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "manual_chapter_id": 1,
    "lang_id": "en-US",
    "parent_id": null,
    "icon_id": 21,
    "title": "Emergency procedures",
    "slug": "emergency-procedures",
    "text": "<p>Follow these steps during an emergency.</p>",
    "reading_confirmation": true,
    "reactions_forbidden": false,
    "planned_publish_at": "2026-08-08 10:00:00",
    "published_at": null,
    "sort_number": 2,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:00:00",
    "deleted_at": null,
    "hash": "wrv2jd6x8g1nmlk4o970mqen"
  }
}
```

## Admin: Update

Update an existing `ManualEntry`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/manual/entries/{manualEntry}`

**Route Parameters**

| Parameter     | Type                  | Description                    |
| ------------- | --------------------- | ------------------------------ |
| `manualEntry` | `integer` \| `string` | ManualEntry ID, hash, or slug. |

**Request Keys**

| Key                    | Type                 | Description                                                                                  |
| ---------------------- | -------------------- | -------------------------------------------------------------------------------------------- |
| `parent_id`            | `integer` \| `null`  | New parent [ManualEntry](/api-reference/manual/manual-entries) ID.                           |
| `manual_chapter_id`    | `integer` \| `null`  | New [ManualChapter](/api-reference/manual/manual-chapters) ID.                               |
| `lang_id`              | `string`             | New language key for the translatable content.                                               |
| `icon_id`              | `integer`            | New [Icon](/api-reference/icons) ID.                                                         |
| `title`                | `string`             | New title, unique across the resulting chapter hierarchy.                                    |
| `slug`                 | `string`             | New slug, unique across the resulting chapter hierarchy.                                     |
| `text`                 | `string`             | New content, which can contain HTML.                                                         |
| `reading_confirmation` | `boolean`            | Whether users must explicitly confirm reading.                                               |
| `planned_publish_at`   | `datetime` \| `null` | New future publication date, or `null` to publish immediately. Relative dates are accepted.  |
| `sort_number`          | `integer`            | New position among entries with the resulting parent.                                        |
| `entity_permissions`   | `array`              | Complete direct [EntityPermissions](/api-reference/entity-permissions) state to synchronize. |

**Behavior**

* A client-provided `user_id` is ignored. Changing the parent, chapter, title, slug, or text transfers ownership to the authenticated user.
* When neither parent key is supplied, the current hierarchy is retained. When both are supplied, `parent_id` takes precedence; supplying one sets the other to `null`.
* Changing the parent or sort position normalizes both affected sibling orders. The slug is normalized from an explicitly supplied `slug` or the updated `title`.
* A changed future `planned_publish_at` schedules the entry and clears `published_at`. Setting it to `null`, the current time, or a past time publishes immediately. An already published entry cannot be assigned an earlier `published_at` value.
* Changing `title` or `text` removes stored translations for the previous content. Publishing or changing content or reading-confirmation state can trigger notifications.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/manual/entries/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'manual_chapter_id' => 1,
        'title' => 'Emergency and evacuation procedures',
        'planned_publish_at' => null
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "manual_chapter_id": 1,
    "lang_id": "en-US",
    "parent_id": null,
    "icon_id": 21,
    "title": "Emergency and evacuation procedures",
    "slug": "emergency-and-evacuation-procedures",
    "text": "<p>Follow these steps during an emergency.</p>",
    "reading_confirmation": true,
    "reactions_forbidden": false,
    "planned_publish_at": null,
    "published_at": "2026-08-06 10:15:00",
    "sort_number": 2,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:15:00",
    "deleted_at": null,
    "hash": "wrv2jd6x8g1nmlk4o970mqen"
  }
}
```

## Admin: Delete

Delete an existing `ManualEntry`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/manual/entries/{manualEntry}`

**Route Parameters**

| Parameter     | Type                  | Description                    |
| ------------- | --------------------- | ------------------------------ |
| `manualEntry` | `integer` \| `string` | ManualEntry ID, hash, or slug. |

**Behavior**

* The entry is soft-deleted, its position is removed from the sibling order, and its notifications are deleted.
* Descendant entries are deleted recursively.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/manual/entries/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# ManualEntrySeenUsers

## Introduction

`ManualEntrySeenUsers` record when a [User](/api-reference/users) views a [ManualEntry](/api-reference/manual/manual-entries). A user has at most one effective seen record per entry because the create endpoint is idempotent.

## Model Definition

**Relations**

| Key           | Relation                                            | Type       | Relation Field(s) |
| ------------- | --------------------------------------------------- | ---------- | ----------------- |
| `user`        | [User](/api-reference/users)                        | Belongs to | `user_id`         |
| `manualEntry` | [ManualEntry](/api-reference/manual/manual-entries) | Belongs to | `manual_entry_id` |

## List by ManualEntry

List `ManualEntrySeenUsers` visible for a `ManualEntry`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/manual/entries/{manualEntry}/seen-users`

**Route Parameters**

| Parameter     | Type                  | Description                    |
| ------------- | --------------------- | ------------------------------ |
| `manualEntry` | `integer` \| `string` | ManualEntry ID, hash, or slug. |

**Behavior**

* The user must be able to show the parent entry.
* intratool accounts are excluded. Without the permission to see all entry seen users, the result is limited to users in allowed departments.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/manual/entries/1/seen-users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "manual_entry_id": 1,
    "created_at": "2026-08-06 09:30:00"
  },
  {
    "id": 2,
    "user_id": 4,
    "manual_entry_id": 1,
    "created_at": "2026-08-06 09:35:00"
  }
]
```

## Mark as Seen

Mark a `ManualEntry` as seen by the current authenticated `User`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/manual/entries/{manualEntry}/seen-users`

**Route Parameters**

| Parameter     | Type                  | Description                    |
| ------------- | --------------------- | ------------------------------ |
| `manualEntry` | `integer` \| `string` | ManualEntry ID, hash, or slug. |

**Behavior**

* `user_id` comes from authentication and `manual_entry_id` comes from the route; request-body values are ignored.
* Repeated requests return the existing record instead of creating duplicates.
* Creating the record marks notifications for the entry as read, which also marks those notifications as seen.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/manual/entries/1/seen-users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "manual_entry_id": 1,
    "created_at": "2026-08-06 09:30:00"
  }
}
```


# ManualEntryReadUsers

## Introduction

`ManualEntryReadUsers` record explicit reading confirmations for a [ManualEntry](/api-reference/manual/manual-entries). Creating a read record also ensures that the same [User](/api-reference/users) has a corresponding [ManualEntrySeenUser](/api-reference/manual/manual-entry-seen-users).

## List by ManualEntry

List `ManualEntryReadUsers` visible for a `ManualEntry`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/manual/entries/{manualEntry}/read-users`

**Route Parameters**

| Parameter     | Type                  | Description                    |
| ------------- | --------------------- | ------------------------------ |
| `manualEntry` | `integer` \| `string` | ManualEntry ID, hash, or slug. |

**Behavior**

* The user must be able to show the parent entry.
* intratool accounts are excluded. Without the permission to see all entry read users, the result is limited to users in allowed departments.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/manual/entries/1/read-users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "manual_entry_id": 1,
    "created_at": "2026-08-06 09:40:00"
  },
  {
    "id": 2,
    "user_id": 4,
    "manual_entry_id": 1,
    "created_at": "2026-08-06 09:45:00"
  }
]
```

## Mark as Read

Mark a `ManualEntry` as read by the current authenticated `User`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/manual/entries/{manualEntry}/read-users`

**Route Parameters**

| Parameter     | Type                  | Description                    |
| ------------- | --------------------- | ------------------------------ |
| `manualEntry` | `integer` \| `string` | ManualEntry ID, hash, or slug. |

**Behavior**

* `user_id` comes from authentication and `manual_entry_id` comes from the route; request-body values are ignored.
* Group accounts cannot create read records.
* The endpoint first creates the corresponding seen record when it does not exist. Repeated requests return the existing read record instead of creating duplicates.
* Creating the record marks notifications for the entry as read, which also marks those notifications as seen.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/manual/entries/1/read-users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "manual_entry_id": 1,
    "created_at": "2026-08-06 09:40:00"
  }
}
```


# Contact Lists

[ContactLists](/api-reference/contact-lists/contact-lists) group contact records and control which users and departments receive each list. The contained [Contacts](/api-reference/contact-lists/contacts) store organization and contact-person details.

All endpoints require the Contact Lists module and the [`contact-lists-show` permission](/api-reference/permissions#available-permissions). The `contact-lists-administration-rights` permission satisfies every resource-specific Contact Lists permission check.


# ContactLists

## Introduction

`ContactLists` group [Contacts](/api-reference/contact-lists/contacts). Assignments to [Departments](/api-reference/departments) and [Users](/api-reference/users) restrict which lists appear in list results.

All endpoints require the [`contact-lists-show` permission](/api-reference/permissions#available-permissions).

## Model Definition

**Relations**

| Key           | Relation                                          | Type            | Relation Field(s)         |
| ------------- | ------------------------------------------------- | --------------- | ------------------------- |
| `user`        | [User](/api-reference/users)                      | Belongs to      | `user_id`                 |
| `contacts`    | [Contacts](/api-reference/contact-lists/contacts) | Has many        | `contact_list_id`         |
| `departments` | [Departments](/api-reference/departments)         | Belongs to many | `department_contact_list` |
| `users`       | [Users](/api-reference/users)                     | Belongs to many | `user_contact_list`       |

These relations are included by default. Pass an empty `relations` value to suppress them.

**Search Types**

* `contact-lists` - Filters lists by `name`.
* `contacts` - Filters the included `contacts` relation by contact data without removing top-level lists.

**Order Directions**

* `asc` - Sorts values in ascending order.
* `desc` - Sorts values in descending order.

Uppercase `ASC` and `DESC` are also accepted.

## List

List visible `ContactLists`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/contact-lists`

**Request Keys**

| Key               | Type      | Default           | Description                                                             |
| ----------------- | --------- | ----------------- | ----------------------------------------------------------------------- |
| `selects`         | `string`  | All fields        | Comma-separated fields to return.                                       |
| `relations`       | `string`  | Default relations | Pipe-separated [relations](#relations); an empty value suppresses them. |
| `search_text`     | `string`  | -                 | Text used by the selected search behavior.                              |
| `search_type`     | `string`  | `contact-lists`   | [Search type](#search-types).                                           |
| `limit`           | `integer` | No limit          | Maximum number of lists. Minimum `1`.                                   |
| `order_field`     | `string`  | `name`            | Field used for ordering.                                                |
| `order_direction` | `string`  | `asc`             | [Order direction](#order-directions).                                   |

**Behavior**

* Users with `contact-lists-administration-rights` receive every non-deleted list except lists created by the reserved intratool Admin account.
* Other users receive lists they created themselves. For other lists, each configured department and user restriction must match; an unconfigured restriction does not limit visibility.
* `search_type=contacts` affects the included `contacts` relation only. Include that relation to receive the matching contacts.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/contact-lists', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'relations' => '',
        'limit' => 2,
        'order_field' => 'sort_number',
        'order_direction' => 'asc'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "name": "Emergency Contacts",
    "sort_number": 1,
    "created_at": "2026-08-01 09:00:00",
    "updated_at": "2026-08-01 09:00:00",
    "deleted_at": null
  },
  {
    "id": 2,
    "user_id": 4,
    "name": "Business Partners",
    "sort_number": 2,
    "created_at": "2026-08-02 10:30:00",
    "updated_at": "2026-08-05 14:15:00",
    "deleted_at": null
  }
]
```

## Show

Show one `ContactList`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/contact-lists/{id}`

**Route Parameters**

| Parameter | Type      | Description      |
| --------- | --------- | ---------------- |
| `id`      | `integer` | Contact list ID. |

**Request Keys**

| Key         | Type     | Default           | Description                                                             |
| ----------- | -------- | ----------------- | ----------------------------------------------------------------------- |
| `selects`   | `string` | All fields        | Comma-separated fields to return.                                       |
| `relations` | `string` | Default relations | Pipe-separated [relations](#relations); an empty value suppresses them. |

**Behavior**

The endpoint resolves the ID independently of the department and user assignment filters applied by List.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/contact-lists/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => ['relations' => '']
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "name": "Emergency Contacts",
  "sort_number": 1,
  "created_at": "2026-08-01 09:00:00",
  "updated_at": "2026-08-01 09:00:00",
  "deleted_at": null
}
```

## Create

Create a new `ContactList`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/contact-lists`

**Request Keys**

| Key              | Type               | Default     | Description                                               |
| ---------------- | ------------------ | ----------- | --------------------------------------------------------- |
| `name`\*         | `string`           | -           | Unique list name.                                         |
| `sort_number`    | `integer`          | End of list | Requested list position.                                  |
| `department_ids` | `string` \| `null` | `null`      | Comma-separated department IDs that may receive the list. |
| `user_ids`       | `string` \| `null` | `null`      | Comma-separated user IDs that may receive the list.       |

Keys with `*` are required.

**Behavior**

* Requires `contact-lists-create-contact-lists` or `contact-lists-administration-rights`.
* The authenticated user is stored as `user_id`.
* `sort_number` is normalized into the existing sequence. Omission appends the list.
* Department and user IDs are attached after creation. Empty assignment values are normalized to no assignments.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/contact-lists', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'name' => 'Service Providers',
        'department_ids' => '1,2',
        'user_ids' => '4,5'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "name": "Service Providers",
    "sort_number": 3,
    "created_at": "2026-08-07 11:00:00",
    "updated_at": "2026-08-07 11:00:00"
  }
}
```

## Update

Update an existing `ContactList`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/contact-lists/{id}`

**Route Parameters**

| Parameter | Type      | Description      |
| --------- | --------- | ---------------- |
| `id`      | `integer` | Contact list ID. |

**Request Keys**

| Key              | Type               | Description                                                        |
| ---------------- | ------------------ | ------------------------------------------------------------------ |
| `name`           | `string`           | Unique list name.                                                  |
| `sort_number`    | `integer`          | Requested list position.                                           |
| `department_ids` | `string` \| `null` | Comma-separated department IDs; an empty value clears assignments. |
| `user_ids`       | `string` \| `null` | Comma-separated user IDs; an empty value clears assignments.       |

**Behavior**

* Requires `contact-lists-edit-all-contact-lists`, or ownership together with `contact-lists-edit-own-contact-lists`. Administration rights satisfy either check.
* Changing `sort_number` reorders the remaining lists.
* Omitted assignment keys preserve their current values; provided values replace the complete affected assignment set.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/contact-lists/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'name' => 'Approved Service Providers',
        'sort_number' => 1,
        'department_ids' => '2',
        'user_ids' => null
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "name": "Approved Service Providers",
    "sort_number": 1,
    "created_at": "2026-08-07 11:00:00",
    "updated_at": "2026-08-07 11:15:00",
    "deleted_at": null
  }
}
```

## Delete

Delete an existing `ContactList`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/contact-lists/{id}`

**Route Parameters**

| Parameter | Type      | Description      |
| --------- | --------- | ---------------- |
| `id`      | `integer` | Contact list ID. |

**Behavior**

* Requires `contact-lists-delete-all-contact-lists`, or ownership together with `contact-lists-delete-own-contact-lists`. Administration rights satisfy either check.
* The list and all contained contacts are soft-deleted. Remaining lists are reordered.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/contact-lists/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": []
}
```


# Contacts

## Introduction

`Contacts` store organization and contact-person details within a [ContactList](/api-reference/contact-lists/contact-lists).

The `notes` field uses the shared [Rich Text](/introduction/rich-text) HTML format. All endpoints require the [`contact-lists-show` permission](/api-reference/permissions#available-permissions).

## Model Definition

**Relations**

| Key           | Relation                                                  | Type       | Relation Field(s) |
| ------------- | --------------------------------------------------------- | ---------- | ----------------- |
| `user`        | [User](/api-reference/users)                              | Belongs to | `user_id`         |
| `contactList` | [ContactList](/api-reference/contact-lists/contact-lists) | Belongs to | `contact_list_id` |

These relations are included by default. Pass an empty `relations` value to suppress them.

**Search Fields**

* `name` - Contact name.
* `phone` - General phone number.
* `fax` - Fax number.
* `email` - General email address.
* `street` - Street address.
* `zipcode` - Postal code.
* `city` - City.
* `notes` - Notes content.
* `contact_person_name` - Contact person's name.
* `contact_person_position` - Contact person's position.
* `contact_person_phone` - Contact person's phone number.
* `contact_person_mobile_phone` - Contact person's mobile phone number.
* `contact_person_email` - Contact person's email address.

**Order Directions**

* `asc` - Sorts values in ascending order.
* `desc` - Sorts values in descending order.

Uppercase `ASC` and `DESC` are also accepted.

**CSV Columns**

* `contact_list` - Contact list name resolved to `contact_list_id`.
* `name` - Contact name.
* `phone` - General phone number.
* `fax` - Fax number.
* `email` - General email address.
* `street` - Street address.
* `zipcode` - Postal code.
* `city` - City.
* `notes` - Notes content.
* `contact_person_name` - Contact person's name.
* `contact_person_position` - Contact person's position.
* `contact_person_phone` - Contact person's phone number.
* `contact_person_mobile_phone` - Contact person's mobile phone number.
* `contact_person_email` - Contact person's email address.

## List

List `Contacts`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/contacts`

**Request Keys**

| Key               | Type      | Default           | Description                                                             |
| ----------------- | --------- | ----------------- | ----------------------------------------------------------------------- |
| `selects`         | `string`  | All fields        | Comma-separated fields to return.                                       |
| `relations`       | `string`  | Default relations | Pipe-separated [relations](#relations); an empty value suppresses them. |
| `search_text`     | `string`  | -                 | Text used to filter the selected field.                                 |
| `search_type`     | `string`  | `name`            | [Contact field](#search-fields) to search.                              |
| `limit`           | `integer` | No limit          | Maximum number of contacts. Minimum `1`.                                |
| `order_field`     | `string`  | `name`            | Field used for ordering.                                                |
| `order_direction` | `string`  | `asc`             | [Order direction](#order-directions).                                   |

**Behavior**

Contact-list department and user assignments do not restrict this endpoint. Contacts created by the reserved intratool Admin account are omitted.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/contacts', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'relations' => '',
        'limit' => 2,
        'order_field' => 'name',
        'order_direction' => 'asc'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "contact_list_id": 1,
    "name": "Example Logistics Ltd.",
    "phone": "+44 20 7946 0100",
    "fax": "+44 20 7946 0101",
    "email": "office@example.com",
    "street": "1 Example Road",
    "zipcode": "SW1A 1AA",
    "city": "London",
    "notes": "<p>Available around the clock for urgent deliveries.</p>",
    "contact_person_name": "Ada Lovelace",
    "contact_person_position": "Operations Manager",
    "contact_person_phone": "+44 20 7946 0110",
    "contact_person_mobile_phone": "+44 7700 900100",
    "contact_person_email": "ada@example.com",
    "created_at": "2026-08-01 09:00:00",
    "updated_at": "2026-08-05 14:15:00",
    "deleted_at": null
  },
  {
    "id": 2,
    "user_id": 4,
    "contact_list_id": 2,
    "name": "Northside Clinic",
    "phone": "+1 202 555 0142",
    "fax": null,
    "email": "reception@example.org",
    "street": null,
    "zipcode": null,
    "city": "Washington",
    "notes": null,
    "contact_person_name": null,
    "contact_person_position": null,
    "contact_person_phone": null,
    "contact_person_mobile_phone": null,
    "contact_person_email": null,
    "created_at": "2026-08-02 10:30:00",
    "updated_at": "2026-08-02 10:30:00",
    "deleted_at": null
  }
]
```

## Show

Show one `Contact`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/contacts/{id}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `id`      | `integer` | Contact ID. |

**Request Keys**

| Key         | Type     | Default           | Description                                                             |
| ----------- | -------- | ----------------- | ----------------------------------------------------------------------- |
| `selects`   | `string` | All fields        | Comma-separated fields to return.                                       |
| `relations` | `string` | Default relations | Pipe-separated [relations](#relations); an empty value suppresses them. |

**Behavior**

Contact-list department and user assignments do not restrict this endpoint.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/contacts/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => ['relations' => '']
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "contact_list_id": 1,
  "name": "Example Logistics Ltd.",
  "phone": "+44 20 7946 0100",
  "fax": "+44 20 7946 0101",
  "email": "office@example.com",
  "street": "1 Example Road",
  "zipcode": "SW1A 1AA",
  "city": "London",
  "notes": "<p>Available around the clock for urgent deliveries.</p>",
  "contact_person_name": "Ada Lovelace",
  "contact_person_position": "Operations Manager",
  "contact_person_phone": "+44 20 7946 0110",
  "contact_person_mobile_phone": "+44 7700 900100",
  "contact_person_email": "ada@example.com",
  "created_at": "2026-08-01 09:00:00",
  "updated_at": "2026-08-05 14:15:00",
  "deleted_at": null
}
```

## Create

Create a new `Contact`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/contacts`

**Request Keys**

| Key                           | Type               | Default | Description                                                          |
| ----------------------------- | ------------------ | ------- | -------------------------------------------------------------------- |
| `contact_list_id`\*           | `integer`          | -       | Parent [ContactList](/api-reference/contact-lists/contact-lists) ID. |
| `name`\*                      | `string`           | -       | Contact name.                                                        |
| `phone`                       | `string` \| `null` | `null`  | General phone number.                                                |
| `fax`                         | `string` \| `null` | `null`  | Fax number.                                                          |
| `email`                       | `string` \| `null` | `null`  | Valid general email address.                                         |
| `street`                      | `string` \| `null` | `null`  | Street address.                                                      |
| `zipcode`                     | `string` \| `null` | `null`  | Postal code.                                                         |
| `city`                        | `string` \| `null` | `null`  | City.                                                                |
| `notes`                       | `string` \| `null` | `null`  | Notes in [Rich Text](/introduction/rich-text) HTML.                  |
| `contact_person_name`         | `string` \| `null` | `null`  | Contact person's name.                                               |
| `contact_person_position`     | `string` \| `null` | `null`  | Contact person's position.                                           |
| `contact_person_phone`        | `string` \| `null` | `null`  | Contact person's phone number.                                       |
| `contact_person_mobile_phone` | `string` \| `null` | `null`  | Contact person's mobile phone number.                                |
| `contact_person_email`        | `string` \| `null` | `null`  | Contact person's email address.                                      |

Keys with `*` are required.

**Behavior**

* Requires `contact-lists-create-contacts` or `contact-lists-administration-rights`.
* The authenticated user is stored as `user_id`.
* Empty optional contact values are normalized to `null`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/contacts', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'contact_list_id' => 1,
        'name' => 'Example Engineering Inc.',
        'phone' => '+1 202 555 0168',
        'fax' => null,
        'email' => 'office@example.net',
        'street' => '100 Example Avenue',
        'zipcode' => '20001',
        'city' => 'Washington',
        'notes' => '<p>Preferred supplier for replacement components.</p>',
        'contact_person_name' => 'Grace Hopper',
        'contact_person_position' => 'Technical Director',
        'contact_person_phone' => '+1 202 555 0170',
        'contact_person_mobile_phone' => '+1 202 555 0171',
        'contact_person_email' => 'grace@example.net'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "contact_list_id": 1,
    "name": "Example Engineering Inc.",
    "phone": "+1 202 555 0168",
    "fax": null,
    "email": "office@example.net",
    "street": "100 Example Avenue",
    "zipcode": "20001",
    "city": "Washington",
    "notes": "<p>Preferred supplier for replacement components.</p>",
    "contact_person_name": "Grace Hopper",
    "contact_person_position": "Technical Director",
    "contact_person_phone": "+1 202 555 0170",
    "contact_person_mobile_phone": "+1 202 555 0171",
    "contact_person_email": "grace@example.net",
    "created_at": "2026-08-07 11:00:00",
    "updated_at": "2026-08-07 11:00:00"
  }
}
```

## Update

Update an existing `Contact`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/contacts/{id}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `id`      | `integer` | Contact ID. |

**Request Keys**

| Key                           | Type               | Description                                                          |
| ----------------------------- | ------------------ | -------------------------------------------------------------------- |
| `contact_list_id`             | `integer`          | Parent [ContactList](/api-reference/contact-lists/contact-lists) ID. |
| `name`                        | `string`           | Contact name.                                                        |
| `phone`                       | `string` \| `null` | General phone number.                                                |
| `fax`                         | `string` \| `null` | Fax number.                                                          |
| `email`                       | `string` \| `null` | Valid general email address.                                         |
| `street`                      | `string` \| `null` | Street address.                                                      |
| `zipcode`                     | `string` \| `null` | Postal code.                                                         |
| `city`                        | `string` \| `null` | City.                                                                |
| `notes`                       | `string` \| `null` | Notes in [Rich Text](/introduction/rich-text) HTML.                  |
| `contact_person_name`         | `string` \| `null` | Contact person's name.                                               |
| `contact_person_position`     | `string` \| `null` | Contact person's position.                                           |
| `contact_person_phone`        | `string` \| `null` | Contact person's phone number.                                       |
| `contact_person_mobile_phone` | `string` \| `null` | Contact person's mobile phone number.                                |
| `contact_person_email`        | `string` \| `null` | Contact person's email address.                                      |

**Behavior**

* Requires `contact-lists-edit-all-contacts`, or ownership together with `contact-lists-edit-own-contacts`. Administration rights satisfy either check.
* Empty optional contact values are normalized to `null`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/contacts/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'phone' => '+1 202 555 0180',
        'notes' => '<p>Preferred supplier. Use the updated service number for urgent requests.</p>',
        'contact_person_mobile_phone' => null
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "contact_list_id": 1,
    "name": "Example Engineering Inc.",
    "phone": "+1 202 555 0180",
    "fax": null,
    "email": "office@example.net",
    "street": "100 Example Avenue",
    "zipcode": "20001",
    "city": "Washington",
    "notes": "<p>Preferred supplier. Use the updated service number for urgent requests.</p>",
    "contact_person_name": "Grace Hopper",
    "contact_person_position": "Technical Director",
    "contact_person_phone": "+1 202 555 0170",
    "contact_person_mobile_phone": null,
    "contact_person_email": "grace@example.net",
    "created_at": "2026-08-07 11:00:00",
    "updated_at": "2026-08-07 11:15:00",
    "deleted_at": null
  }
}
```

## Delete

Delete an existing `Contact`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/contacts/{id}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `id`      | `integer` | Contact ID. |

**Behavior**

* Requires `contact-lists-delete-all-contacts`, or ownership together with `contact-lists-delete-own-contacts`. Administration rights satisfy either check.
* The contact is soft-deleted.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/contacts/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": []
}
```

## Download Import Template

Download a CSV template containing the supported contact columns.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/contacts/import-template`

**Request Keys**

| Key                           | Type     | Default                       | Description                                                    |
| ----------------------------- | -------- | ----------------------------- | -------------------------------------------------------------- |
| `contact_list`                | `string` | `contact_list`                | Header name for the `contact_list` [CSV column](#csv-columns). |
| `name`                        | `string` | `name`                        | Header name for the `name` CSV column.                         |
| `phone`                       | `string` | `phone`                       | Header name for the `phone` CSV column.                        |
| `fax`                         | `string` | `fax`                         | Header name for the `fax` CSV column.                          |
| `email`                       | `string` | `email`                       | Header name for the `email` CSV column.                        |
| `street`                      | `string` | `street`                      | Header name for the `street` CSV column.                       |
| `zipcode`                     | `string` | `zipcode`                     | Header name for the `zipcode` CSV column.                      |
| `city`                        | `string` | `city`                        | Header name for the `city` CSV column.                         |
| `notes`                       | `string` | `notes`                       | Header name for the `notes` CSV column.                        |
| `contact_person_name`         | `string` | `contact_person_name`         | Header name for the `contact_person_name` CSV column.          |
| `contact_person_position`     | `string` | `contact_person_position`     | Header name for the `contact_person_position` CSV column.      |
| `contact_person_phone`        | `string` | `contact_person_phone`        | Header name for the `contact_person_phone` CSV column.         |
| `contact_person_mobile_phone` | `string` | `contact_person_mobile_phone` | Header name for the `contact_person_mobile_phone` CSV column.  |
| `contact_person_email`        | `string` | `contact_person_email`        | Header name for the `contact_person_email` CSV column.         |

**Behavior**

* This compatibility endpoint uses application-session authentication instead of the API bearer-token guard.
* The response downloads `intratool-kontaktdaten-vorlage.csv` with one header row. Provided request values replace the matching default column names.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/contacts/import-template', [
    'headers' => ['Cookie' => '{sessionCookie}'],
    'query' => [
        'contact_list' => 'list',
        'name' => 'full_name',
        'email' => 'email_address'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```http
HTTP/1.1 200 OK
Content-Disposition: attachment; filename=intratool-kontaktdaten-vorlage.csv

list,full_name,phone,fax,email_address,street,zipcode,city,notes,contact_person_name,contact_person_position,contact_person_phone,contact_person_mobile_phone,contact_person_email
```

## Parse CSV

Parse an uploaded contacts CSV file into proposed create, update, and delete operations.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/contacts/csv`

**Request Keys**

| Key                           | Type      | Default                       | Description                                                                 |
| ----------------------------- | --------- | ----------------------------- | --------------------------------------------------------------------------- |
| `contact_data_csv`\*          | `file`    | -                             | CSV or plain-text file with a header row.                                   |
| `update_contacts`             | `boolean` | `false`                       | Include matching contacts in `update_contacts`.                             |
| `delete_contacts`             | `boolean` | `false`                       | Include contacts whose names are absent from the file in `delete_contacts`. |
| `contact_list`                | `string`  | `contact_list`                | Header mapped to the `contact_list` [CSV column](#csv-columns).             |
| `name`                        | `string`  | `name`                        | Header mapped to the `name` CSV column.                                     |
| `phone`                       | `string`  | `phone`                       | Header mapped to the `phone` CSV column.                                    |
| `fax`                         | `string`  | `fax`                         | Header mapped to the `fax` CSV column.                                      |
| `email`                       | `string`  | `email`                       | Header mapped to the `email` CSV column.                                    |
| `street`                      | `string`  | `street`                      | Header mapped to the `street` CSV column.                                   |
| `zipcode`                     | `string`  | `zipcode`                     | Header mapped to the `zipcode` CSV column.                                  |
| `city`                        | `string`  | `city`                        | Header mapped to the `city` CSV column.                                     |
| `notes`                       | `string`  | `notes`                       | Header mapped to the `notes` CSV column.                                    |
| `contact_person_name`         | `string`  | `contact_person_name`         | Header mapped to the `contact_person_name` CSV column.                      |
| `contact_person_position`     | `string`  | `contact_person_position`     | Header mapped to the `contact_person_position` CSV column.                  |
| `contact_person_phone`        | `string`  | `contact_person_phone`        | Header mapped to the `contact_person_phone` CSV column.                     |
| `contact_person_mobile_phone` | `string`  | `contact_person_mobile_phone` | Header mapped to the `contact_person_mobile_phone` CSV column.              |
| `contact_person_email`        | `string`  | `contact_person_email`        | Header mapped to the `contact_person_email` CSV column.                     |

Keys with `*` are required.

**Behavior**

* The endpoint detects the delimiter and source encoding. Rows without a mapped `name` are ignored.
* A `contact_list` value resolves by exact list name. Existing contacts match by `name` and, when resolved, `contact_list_id`.
* The response is a preview and does not persist any create, update, or delete operation.
* With `delete_contacts=true`, deletion candidates are selected across all contact lists by contact name only, not just the lists represented in the file.
* The example assumes `ContactList` ID `1` is named `Professional Network` and contains Ada Lovelace as Contact ID `1` and Grace Hopper as Contact ID `2`, with no other contacts present.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$csv = "name,contact_list,phone,email\n"
    . "Ada Lovelace,Professional Network,+44 20 7946 0200,ada.updated@example.com\n"
    . "Linus Torvalds,Professional Network,,linus@example.com\n";

$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/contacts/csv', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'multipart' => [
        [
            'name' => 'contact_data_csv',
            'contents' => $csv,
            'filename' => 'contacts.csv'
        ],
        [
            'name' => 'update_contacts',
            'contents' => '1'
        ],
        [
            'name' => 'delete_contacts',
            'contents' => '1'
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "new_contacts": [
      {
        "name": "Linus Torvalds",
        "contact_list_id": 1,
        "email": "linus@example.com"
      }
    ],
    "update_contacts": [
      {
        "name": "Ada Lovelace",
        "contact_list_id": 1,
        "phone": "+44 20 7946 0200",
        "email": "ada.updated@example.com",
        "id": 1
      }
    ],
    "delete_contacts": [
      {
        "id": 2,
        "contact_list_id": 1,
        "name": "Grace Hopper"
      }
    ]
  }
}
```


# Tasks 2.0

[TaskTemplates](/api-reference/tasks-2/task-templates) define task content and fields, while [TaskTemplateCompositions](/api-reference/tasks-2/task-template-compositions) combine several templates into one executable sequence.

[TaskAssignments](/api-reference/tasks-2/task-assignments) define recipients, timing, and recurrence. They use [Targetables](/introduction/resource-capabilities/targetables) for user and department assignment and [Entity Permissions](/introduction/resource-capabilities/entity-permissions) for administration access.

[TaskExecutions](/api-reference/tasks-2/task-executions) are the concrete tasks generated from assignments. Their [TaskProgressFields](/api-reference/tasks-2/task-progress-fields) store values submitted for the configured [TaskFields](/api-reference/tasks-2/task-fields).


# TaskTemplates

## Introduction

`TaskTemplates` store what a task is and how progress is reported.

Templates can have related [TaskFields](/api-reference/tasks-2/task-fields) that define which values are captured into [TaskProgressFields](/api-reference/tasks-2/task-progress-fields).

[TaskAssignments](/api-reference/tasks-2/task-assignments) use template data to create concrete [TaskExecutions](/api-reference/tasks-2/task-executions).

The `description` field uses the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`taskTemplate`

**Relations**

| Key               | Relation                                                   | Type             | Relation Field(s)                                  |
| ----------------- | ---------------------------------------------------------- | ---------------- | -------------------------------------------------- |
| `parent`          | [TaskTemplate](/api-reference/tasks-2/task-templates)      | Belongs to       | `parent_id`                                        |
| `children`        | [TaskTemplates](/api-reference/tasks-2/task-templates)     | Has many         | `parent_id`                                        |
| `taskAssignments` | [TaskAssignments](/api-reference/tasks-2/task-assignments) | Has many         | `task_template_id`                                 |
| `taskExecutions`  | [TaskExecutions](/api-reference/tasks-2/task-executions)   | Has many through | `task_template_id`, `task_assignment_id`           |
| `formFields`      | [TaskFields](/api-reference/tasks-2/task-fields)           | Has many         | `form_fields.form_id`, `form_fields.form_type`     |
| `user`            | [User](/api-reference/users)                               | Belongs to       | `user_id`                                          |
| `layouts`         | [Layouts](/api-reference/layouts/layouts)                  | Has many         | `layouts.layoutable_type`, `layouts.layoutable_id` |

**Capabilities**

* [Entity Permissions](/introduction/resource-capabilities/entity-permissions) - Direct grants participate in template administration access together with ownership and module permissions.
* [Translations](/introduction/resource-capabilities/translations) - The `title` and `description` fields are translatable.

## Admin: List

List all `TaskTemplates` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/tasks-2/templates`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/tasks-2/templates', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 6,
    "lang_id": "en-US",
    "title": "Safety Checklist",
    "description": "<p>Describe your template</p>",
    "parent_id": null,
    "sort_number": 39,
    "user_id": 144,
    "grade_scale": null,
    "tour_enabled": true,
    "quick_assignable": true
  },
  {
    "id": 7,
    "lang_id": "en-US",
    "title": "Equipment Inspection",
    "description": "<p>Inspect the assigned equipment.</p>",
    "parent_id": null,
    "sort_number": 40,
    "user_id": 145,
    "grade_scale": 100,
    "tour_enabled": false,
    "quick_assignable": true
  }
]
```

## List Quick-Assignable Templates

List `TaskTemplates` available for quick assignment.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/templates/quick-assignable`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/templates/quick-assignable', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 6,
    "lang_id": "en-US",
    "title": "Safety Checklist",
    "quick_assignable": true
  },
  {
    "id": 7,
    "lang_id": "en-US",
    "title": "Equipment Inspection",
    "quick_assignable": true
  }
]
```

## Admin: Show

Show one `TaskTemplate` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/tasks-2/templates/{taskTemplate}`

**Route Parameters**

| Parameter      | Type      | Description      |
| -------------- | --------- | ---------------- |
| `taskTemplate` | `integer` | TaskTemplate ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/tasks-2/templates/6', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 6,
  "lang_id": "en-US",
  "title": "Safety Checklist",
  "description": "<p>Describe your template</p>",
  "parent_id": null,
  "sort_number": 39,
  "user_id": 144,
  "grade_scale": null,
  "tour_enabled": true,
  "quick_assignable": true
}
```

## Admin: Create

Create a new `TaskTemplate`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/tasks-2/templates`

**Request Keys**

| Key                  | Type                | Default         | Description                                                                          |
| -------------------- | ------------------- | --------------- | ------------------------------------------------------------------------------------ |
| `title`\*            | `string`            | -               | Template title.                                                                      |
| `description`        | `string`            | `null`          | Template description.                                                                |
| `folder_id`          | `integer` \| `null` | `null`          | Related [Folder](/api-reference/folders) ID.                                         |
| `parent_id`          | `integer`           | `null`          | Parent template ID.                                                                  |
| `lang_id`            | `string`            | system language | Language key.                                                                        |
| `sort_number`        | `integer`           | auto            | Sort number.                                                                         |
| `grade_scale`        | `integer`           | `null`          | Grade scale.                                                                         |
| `tour_enabled`       | `boolean`           | `false`         | Enables user tour for executions from this template.                                 |
| `quick_assignable`   | `boolean`           | `false`         | Enables quick-assignment usage.                                                      |
| `grade_mode`         | `string` \| `null`  | `null`          | Grade calculation mode.                                                              |
| `entity_permissions` | `object[]`          | `[]`            | Direct [Entity Permissions](/introduction/resource-capabilities/entity-permissions). |

Keys with `*` are required.

**Behavior**

* `user_id` is set to the authenticated user and cannot select another owner.
* Omitting `lang_id` uses the system language; this example supplies `en-US` explicitly.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/tasks-2/templates', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Safety Checklist',
        'description' => '<p>Describe your template</p>',
        'lang_id' => 'en-US',
        'quick_assignable' => true
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 6,
    "user_id": 144,
    "lang_id": "en-US",
    "title": "Safety Checklist",
    "description": "<p>Describe your template</p>",
    "parent_id": null,
    "sort_number": 39,
    "quick_assignable": true,
    "tour_enabled": false
  }
}
```

## Admin: Update

Update an existing `TaskTemplate`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/tasks-2/templates/{taskTemplate}`

**Route Parameters**

| Parameter      | Type      | Description |
| -------------- | --------- | ----------- |
| `taskTemplate` | `integer` | Template ID |

**Request Keys**

| Key                  | Type                | Default | Description                |
| -------------------- | ------------------- | ------- | -------------------------- |
| `title`              | `string`            | -       | Template title.            |
| `description`        | `string`            | -       | Template description.      |
| `folder_id`          | `integer` \| `null` | -       | Related folder ID.         |
| `parent_id`          | `integer`           | -       | Parent template ID.        |
| `lang_id`            | `string`            | -       | Language key.              |
| `sort_number`        | `integer`           | -       | Sort number.               |
| `grade_scale`        | `integer`           | -       | Grade scale.               |
| `tour_enabled`       | `boolean`           | -       | Tour-enabled flag.         |
| `quick_assignable`   | `boolean`           | -       | Quick-assignable flag.     |
| `grade_mode`         | `string` \| `null`  | -       | Grade calculation mode.    |
| `entity_permissions` | `object[]`          | -       | Direct Entity Permissions. |

**Behavior**

* `user_id` is replaced with the authenticated user when the request is prepared.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/tasks-2/templates/6', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Safety Checklist (updated)'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 6,
    "user_id": 144,
    "lang_id": "en-US",
    "title": "Safety Checklist (updated)",
    "quick_assignable": true
  }
}
```

## Admin: Delete

Delete an existing `TaskTemplate`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/tasks-2/templates/{taskTemplate}`

**Route Parameters**

| Parameter      | Type      | Description      |
| -------------- | --------- | ---------------- |
| `taskTemplate` | `integer` | TaskTemplate ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/tasks-2/templates/6', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# TaskTemplateCompositions

## Introduction

`TaskTemplateCompositions` combine multiple [TaskTemplates](/api-reference/tasks-2/task-templates). A [TaskAssignment](/api-reference/tasks-2/task-assignments) can use a composition to create one execution for each included template.

The `description` field uses the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`taskTemplateComposition`

**Capabilities**

* [Entity Permissions](/introduction/resource-capabilities/entity-permissions) - Direct grants can allow permitted users to view, update, or delete a composition in administration.
* [Translations](/introduction/resource-capabilities/translations) - The `title` and `description` fields are translatable.

## Admin: List

List all accessible compositions, including soft-deleted records.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/tasks-2/templates/compositions`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/tasks-2/templates/compositions', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 12,
    "user_id": 3,
    "lang_id": "en-US",
    "folder_id": null,
    "title": "Opening Checklist",
    "description": "<p>Complete both opening tasks.</p>",
    "sort_number": 1,
    "deleted_at": null
  },
  {
    "id": 13,
    "user_id": 4,
    "lang_id": "en-US",
    "folder_id": 8,
    "title": "Closing Checklist",
    "description": "<p>Finish the closing and security tasks.</p>",
    "sort_number": 2,
    "deleted_at": null
  }
]
```

## Admin: Show

Show one composition, including a soft-deleted record.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/tasks-2/templates/compositions/{taskTemplateComposition}`

**Route Parameters**

| Parameter                 | Type      | Description     |
| ------------------------- | --------- | --------------- |
| `taskTemplateComposition` | `integer` | Composition ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/tasks-2/templates/compositions/12', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 12,
  "user_id": 3,
  "lang_id": "en-US",
  "folder_id": null,
  "title": "Opening Checklist",
  "description": "<p>Complete both opening tasks.</p>",
  "sort_number": 1,
  "deleted_at": null
}
```

## Admin: Create

Create a composition and optionally attach templates.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/tasks-2/templates/compositions`

**Request Keys**

| Key                  | Type                | Default         | Description                                                                          |
| -------------------- | ------------------- | --------------- | ------------------------------------------------------------------------------------ |
| `lang_id`            | `string`            | System language | Language key. Use `en-US` for this example.                                          |
| `folder_id`          | `integer` \| `null` | `null`          | Related [Folder](/api-reference/folders) ID.                                         |
| `title`\*            | `string`            | -               | Composition title.                                                                   |
| `description`        | `string` \| `null`  | `null`          | HTML description.                                                                    |
| `sort_number`        | `integer`           | Auto            | Position within the folder.                                                          |
| `task_template_ids`  | `integer[]`         | `[]`            | Templates to attach.                                                                 |
| `entity_permissions` | `object[]`          | `[]`            | Direct [Entity Permissions](/introduction/resource-capabilities/entity-permissions). |

Keys with `*` are required.

**Behavior**

* `user_id` is set to the authenticated user; a request-body value cannot select another owner.
* Supplying `task_template_ids` synchronizes the composition's template relations.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/tasks-2/templates/compositions', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'title' => 'Opening Checklist',
        'description' => '<p>Complete both opening tasks.</p>',
        'sort_number' => 1,
        'task_template_ids' => [21, 22]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 12,
    "user_id": 3,
    "lang_id": "en-US",
    "folder_id": null,
    "title": "Opening Checklist",
    "description": "<p>Complete both opening tasks.</p>",
    "sort_number": 1
  }
}
```

## Admin: Update

Update a composition and optionally replace its attached templates.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/tasks-2/templates/compositions/{taskTemplateComposition}`

**Route Parameters**

| Parameter                 | Type      | Description     |
| ------------------------- | --------- | --------------- |
| `taskTemplateComposition` | `integer` | Composition ID. |

**Request Keys**

| Key                  | Type                | Description                                                      |
| -------------------- | ------------------- | ---------------------------------------------------------------- |
| `lang_id`            | `string`            | Language key.                                                    |
| `folder_id`          | `integer` \| `null` | Related folder ID, or `null`.                                    |
| `title`              | `string`            | Composition title.                                               |
| `description`        | `string` \| `null`  | HTML description.                                                |
| `sort_number`        | `integer`           | Position within the folder.                                      |
| `task_template_ids`  | `integer[]`         | Complete replacement set; an empty array detaches all templates. |
| `entity_permissions` | `object[]`          | Direct Entity Permissions.                                       |

**Behavior**

* `user_id` is replaced with the authenticated user.
* `task_template_ids` is synchronized only when the key is present.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/tasks-2/templates/compositions/12', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Opening and Safety Checklist',
        'task_template_ids' => [21, 22, 23]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 12,
    "user_id": 3,
    "lang_id": "en-US",
    "folder_id": null,
    "title": "Opening and Safety Checklist",
    "description": "<p>Complete both opening tasks.</p>",
    "sort_number": 1
  }
}
```

## Admin: Delete

Soft-delete a composition.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/tasks-2/templates/compositions/{taskTemplateComposition}`

**Route Parameters**

| Parameter                 | Type      | Description     |
| ------------------------- | --------- | --------------- |
| `taskTemplateComposition` | `integer` | Composition ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/tasks-2/templates/compositions/12', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# TaskFields

## Introduction

`TaskFields` define which values users can enter while progressing or finishing a [TaskExecution](/api-reference/tasks-2/task-executions).

They are stored as decorated form fields on [TaskTemplates](/api-reference/tasks-2/task-templates).

Rich-text values in `default_value` and type-specific `config` keys use the shared [Rich Text](/introduction/rich-text) HTML format. This applies to the `rich-text` and `description` type values and the `privacy-policy` type's `config.text` value.

## Model Definition

**Alias**

`formField`

**Relations**

| Key                   | Relation                                                                                | Type       | Relation Field(s)                      |
| --------------------- | --------------------------------------------------------------------------------------- | ---------- | -------------------------------------- |
| `form`                | [TaskTemplate](/api-reference/tasks-2/task-templates)                                   | Belongs to | `form_id`, `form_type`                 |
| `fieldType`           | [TaskFieldTypes](/api-reference/tasks-2/task-field-types)                               | Belongs to | `form_field_type_id`                   |
| `fieldValidations`    | [TaskFieldValidations](/api-reference/tasks-2/task-field-validations)                   | Has many   | `form_field_validations.form_field_id` |
| `defaultValueSources` | [TaskFieldDefaultValueSources](/api-reference/tasks-2/task-field-default-value-sources) | Has many   | `default_value_sources.form_field_id`  |
| `displayConditions`   | [TaskFieldDisplayConditions](/api-reference/tasks-2/task-field-display-conditions)      | Morph many | `attachable_type`, `attachable_id`     |

**Computed Properties**

* `key_id` - Stable DOM-style key in the form `field-{id}`.

**Capabilities**

* [Entity Permissions](/introduction/resource-capabilities/entity-permissions) - Task fields inherit administration and viewing access through their parent template.
* [Translations](/introduction/resource-capabilities/translations) - `name`, `default_value`, `placeholder`, and `description` are translatable; individual field types can add translatable configuration keys.

## Admin: List

List all `TaskFields` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/tasks-2/templates/fields`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/tasks-2/templates/fields', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 33,
    "form_type": "taskTemplate",
    "form_id": 21,
    "form_field_type_id": "text",
    "lang_id": "en-US",
    "name": "Comment",
    "key": "comment",
    "config": {
      "scanner": false
    }
  },
  {
    "id": 34,
    "form_type": "taskTemplate",
    "form_id": 22,
    "form_field_type_id": "boolean",
    "lang_id": "en-US",
    "name": "Equipment operational",
    "key": "equipment-operational",
    "config": {
      "type": "labeled_buttons",
      "representation": {
        "true": "Yes",
        "false": "No"
      }
    }
  }
]
```

## Admin: List by Template

List all `TaskFields` for one `TaskTemplate` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/tasks-2/templates/{formEntity}/fields`

**Route Parameters**

| Parameter    | Type      | Description     |
| ------------ | --------- | --------------- |
| `formEntity` | `integer` | TaskTemplate ID |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/tasks-2/templates/21/fields', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 33,
    "form_type": "taskTemplate",
    "form_id": 21,
    "form_field_type_id": "text",
    "lang_id": "en-US",
    "name": "Comment",
    "key": "comment",
    "config": {
      "scanner": false
    }
  },
  {
    "id": 35,
    "form_type": "taskTemplate",
    "form_id": 21,
    "form_field_type_id": "datetime",
    "lang_id": "en-US",
    "name": "Inspection time",
    "key": "inspection-time",
    "config": {
      "type": "datetime",
      "format": "DD.MM.YYYY HH:mm"
    }
  }
]
```

## Admin: Create

Create a new `TaskField`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/tasks-2/templates/fields`

**Request Keys**

| Key                    | Type      | Default             | Description                                                              |
| ---------------------- | --------- | ------------------- | ------------------------------------------------------------------------ |
| `form_type`\*          | `string`  | -                   | Morph alias of container entity. Use `taskTemplate`.                     |
| `form_id`\*            | `integer` | -                   | Related [TaskTemplate](/api-reference/tasks-2/task-templates) ID.        |
| `form_field_type_id`\* | `string`  | -                   | Type key from [TaskFieldTypes](/api-reference/tasks-2/task-field-types). |
| `lang_id`              | `string`  | system language     | Language key for translated field text.                                  |
| `name`\*               | `string`  | -                   | Field label shown in task UI.                                            |
| `slug`                 | `string`  | derived from `name` | Technical slug, normalized automatically.                                |
| `default_value`        | `mixed`   | `null`              | Static default value.                                                    |
| `placeholder`          | `string`  | `null`              | Placeholder text.                                                        |
| `description`          | `string`  | `null`              | Help text shown in UI.                                                   |
| `hidden`               | `boolean` | `false`             | Hide field in UI.                                                        |
| `disabled`             | `boolean` | `false`             | Disable field input.                                                     |
| `config`               | `object`  | type defaults       | Type-specific configuration object.                                      |

Keys with `*` are required.

**Behavior**

* `user_id` is set to the authenticated user.
* `slug` is normalized and stored as the response field `key`.
* Missing `config` values are completed from the selected field type's defaults before validation.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/tasks-2/templates/fields', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'form_type' => 'taskTemplate',
        'form_id' => 21,
        'form_field_type_id' => 'text',
        'lang_id' => 'en-US',
        'name' => 'Comment',
        'config' => [
            'scanner' => false
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 33,
    "form_type": "taskTemplate",
    "form_id": 21,
    "form_field_type_id": "text",
    "lang_id": "en-US",
    "name": "Comment",
    "key": "comment",
    "config": {
      "scanner": false
    },
    "hidden": false,
    "disabled": false
  }
}
```

## Admin: Update

Update an existing `TaskField`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/tasks-2/templates/fields/{formField}`

**Route Parameters**

| Parameter   | Type      | Description         |
| ----------- | --------- | ------------------- |
| `formField` | `integer` | Field ID to update. |

**Request Keys**

| Key                  | Type      | Description                          |
| -------------------- | --------- | ------------------------------------ |
| `form_field_type_id` | `string`  | New field type key.                  |
| `lang_id`            | `string`  | New language key.                    |
| `name`               | `string`  | New label.                           |
| `slug`               | `string`  | New technical slug.                  |
| `default_value`      | `mixed`   | New static default value.            |
| `placeholder`        | `string`  | New placeholder text.                |
| `description`        | `string`  | New help text.                       |
| `hidden`             | `boolean` | New hidden state.                    |
| `disabled`           | `boolean` | New disabled state.                  |
| `config`             | `object`  | Updated type-specific configuration. |

**Behavior**

* `form_id`, `form_type`, and `user_id` cannot be changed by this endpoint.
* Changing `name` without supplying `slug` regenerates the normalized response `key`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/tasks-2/templates/fields/33', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'name' => 'Comment (updated)'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 33,
    "form_type": "taskTemplate",
    "form_id": 21,
    "form_field_type_id": "text",
    "lang_id": "en-US",
    "name": "Comment (updated)",
    "key": "comment-updated",
    "config": {
      "scanner": false
    }
  }
}
```

## Admin: Delete

Delete an existing `TaskField`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/tasks-2/templates/fields/{formField}`

**Route Parameters**

| Parameter   | Type      | Description |
| ----------- | --------- | ----------- |
| `formField` | `integer` | Field ID.   |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/tasks-2/templates/fields/33', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```

## Entity-Select Helpers

These routes are relevant for `entity-select` and `entities-select` fields. Entity lists and filter-option lists use [count-less pagination](/introduction/query-manipulation/result-control#count-less-pagination), so their `total` value is `null`.

## List Preselected Entities

List entities preselected for one `TaskField`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/tasks-2/templates/fields/{formField}/preselected-entities`

**Route Parameters**

| Parameter   | Type      | Description                                    |
| ----------- | --------- | ---------------------------------------------- |
| `formField` | `integer` | `entity-select` or `entities-select` field ID. |

**Request Keys**

| Key        | Type      | Default     | Description       |
| ---------- | --------- | ----------- | ----------------- |
| `page`     | `integer` | `1`         | Result page.      |
| `per_page` | `integer` | API default | Results per page. |

**Behavior**

* Results are derived from the field's `default_value`; the example field stores user IDs `3,4`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/tasks-2/templates/fields/40/preselected-entities', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'page' => 1,
        'per_page' => 10
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "data": [
    {
      "id": 3,
      "title": "Alice Adams",
      "sub_title": "alice.adams",
      "url": "/users/3",
      "tags": []
    },
    {
      "id": 4,
      "title": "Ben Brown",
      "sub_title": "ben.brown",
      "url": "/users/4",
      "tags": []
    }
  ],
  "current_page": 1,
  "total": null,
  "per_page": 10
}
```

## List Selectable Entities

List entities selectable for one `TaskField`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/tasks-2/templates/fields/{formField}/selectable-entities`

**Route Parameters**

| Parameter   | Type      | Description                                    |
| ----------- | --------- | ---------------------------------------------- |
| `formField` | `integer` | `entity-select` or `entities-select` field ID. |

**Request Keys**

| Key        | Type      | Default     | Description                                                                         |
| ---------- | --------- | ----------- | ----------------------------------------------------------------------------------- |
| `page`     | `integer` | `1`         | Result page.                                                                        |
| `per_page` | `integer` | API default | Results per page.                                                                   |
| `filter`   | `object`  | `{}`        | Optional [Result Control](/introduction/query-manipulation/result-control) filters. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/tasks-2/templates/fields/40/selectable-entities', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'filter' => [
            'id' => ['in' => '3,4']
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "data": [
    {
      "id": 3,
      "title": "Alice Adams",
      "sub_title": "alice.adams",
      "url": "/users/3",
      "tags": []
    },
    {
      "id": 4,
      "title": "Ben Brown",
      "sub_title": "ben.brown",
      "url": "/users/4",
      "tags": []
    }
  ],
  "current_page": 1,
  "total": null,
  "per_page": 15
}
```

## List Selectable-Entity Filters

List selectable-entity filters for one `TaskField`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/tasks-2/templates/fields/{formField}/selectable-entities/filters`

**Route Parameters**

| Parameter   | Type      | Description                                    |
| ----------- | --------- | ---------------------------------------------- |
| `formField` | `integer` | `entity-select` or `entities-select` field ID. |

**Behavior**

* Available filter definitions depend on the field's configured `entity_type`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/tasks-2/templates/fields/40/selectable-entities/filters', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "data": [
    {
      "key": "role.department.id",
      "label": "Department",
      "type": "multi-select",
      "operators": []
    },
    {
      "key": "group_account",
      "label": "Group account",
      "type": "multi-select",
      "operators": [
        "is"
      ]
    }
  ]
}
```

## List Selectable-Entity Filter Options

List options for one selectable-entity filter of a `TaskField`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/tasks-2/templates/fields/{formField}/selectable-entities/{filterKey}/options`

**Route Parameters**

| Parameter   | Type      | Description                                               |
| ----------- | --------- | --------------------------------------------------------- |
| `formField` | `integer` | Field ID.                                                 |
| `filterKey` | `string`  | Multi-select filter key returned by the filters endpoint. |

**Request Keys**

| Key        | Type      | Default     | Description       |
| ---------- | --------- | ----------- | ----------------- |
| `page`     | `integer` | `1`         | Result page.      |
| `per_page` | `integer` | API default | Results per page. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/tasks-2/templates/fields/40/selectable-entities/role.department.id/options', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'page' => 1,
        'per_page' => 10
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "data": [
    {
      "value": 7,
      "label": "Production",
      "additionalData": {}
    },
    {
      "value": 8,
      "label": "Sales",
      "additionalData": {}
    }
  ],
  "total": null,
  "per_page": 10,
  "current_page": 1,
  "last_page": 1
}
```

## Admin: List Preselected Entities

List the configured default entities while administering a task field.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/tasks-2/templates/fields/{formField}/preselected-entities`

**Route Parameters**

| Parameter   | Type      | Description |
| ----------- | --------- | ----------- |
| `formField` | `integer` | Field ID.   |

**Request Keys**

| Key        | Type      | Default     | Description       |
| ---------- | --------- | ----------- | ----------------- |
| `page`     | `integer` | `1`         | Result page.      |
| `per_page` | `integer` | API default | Results per page. |

**Behavior**

* The response is calculated from the field's stored `default_value` and configured entity type.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/tasks-2/templates/fields/40/preselected-entities', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => ['page' => 1, 'per_page' => 10]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "data": [
    {
      "id": 3,
      "title": "Alice Adams",
      "sub_title": "alice.adams"
    },
    {
      "id": 4,
      "title": "Ben Brown",
      "sub_title": "ben.brown"
    }
  ],
  "current_page": 1,
  "total": null,
  "per_page": 10
}
```

## Admin: List Selectable Entities

List selectable entities while administering a task field.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/tasks-2/templates/fields/{formField}/selectable-entities`

**Route Parameters**

| Parameter   | Type      | Description |
| ----------- | --------- | ----------- |
| `formField` | `integer` | Field ID.   |

**Request Keys**

| Key        | Type      | Default     | Description                      |
| ---------- | --------- | ----------- | -------------------------------- |
| `page`     | `integer` | `1`         | Result page.                     |
| `per_page` | `integer` | API default | Results per page.                |
| `filter`   | `object`  | `{}`        | Optional Result Control filters. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/tasks-2/templates/fields/40/selectable-entities', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'filter' => [
            'id' => ['in' => '3,4']
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "data": [
    {
      "id": 3,
      "title": "Alice Adams",
      "sub_title": "alice.adams"
    },
    {
      "id": 4,
      "title": "Ben Brown",
      "sub_title": "ben.brown"
    }
  ],
  "current_page": 1,
  "total": null,
  "per_page": 15
}
```

## Admin: List Selectable-Entity Filters

List filter definitions while administering a task field.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/tasks-2/templates/fields/{formField}/selectable-entities/filters`

**Route Parameters**

| Parameter   | Type      | Description |
| ----------- | --------- | ----------- |
| `formField` | `integer` | Field ID.   |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/tasks-2/templates/fields/40/selectable-entities/filters', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "data": [
    {
      "key": "role.department.id",
      "label": "Department",
      "type": "multi-select"
    },
    {
      "key": "group_account",
      "label": "Group account",
      "type": "multi-select"
    }
  ]
}
```

## Admin: List Selectable-Entity Filter Options

List options for one multi-select filter while administering a task field.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/tasks-2/templates/fields/{formField}/selectable-entities/{filterKey}/options`

**Route Parameters**

| Parameter   | Type      | Description              |
| ----------- | --------- | ------------------------ |
| `formField` | `integer` | Field ID.                |
| `filterKey` | `string`  | Multi-select filter key. |

**Request Keys**

| Key        | Type      | Default     | Description       |
| ---------- | --------- | ----------- | ----------------- |
| `page`     | `integer` | `1`         | Result page.      |
| `per_page` | `integer` | API default | Results per page. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/tasks-2/templates/fields/40/selectable-entities/role.department.id/options', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => ['page' => 1, 'per_page' => 10]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "data": [
    {
      "value": 7,
      "label": "Production",
      "additionalData": {}
    },
    {
      "value": 8,
      "label": "Sales",
      "additionalData": {}
    }
  ],
  "total": null,
  "per_page": 10,
  "current_page": 1,
  "last_page": 1
}
```

## Related resources

* [TaskFieldTypes](/api-reference/tasks-2/task-field-types)
* [TaskFieldValidationTypes](/api-reference/tasks-2/task-field-validation-types)
* [TaskFieldValidations](/api-reference/tasks-2/task-field-validations)
* [TaskFieldDefaultValueSources](/api-reference/tasks-2/task-field-default-value-sources)
* [TaskFieldDisplayConditions](/api-reference/tasks-2/task-field-display-conditions)
* [TaskProgressFields](/api-reference/tasks-2/task-progress-fields)


# TaskFieldTypes

## Introduction

`TaskFieldTypes` describe how [TaskFields](/api-reference/tasks-2/task-fields) are rendered and which `config` is valid.

The `rich-text` and `description` type values and the `privacy-policy` type's `config.text` value use the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`formFieldType`

## List

List available `TaskFieldTypes`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/field-types`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/field-types', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": "boolean",
    "icon_id": 43,
    "default_config": {
      "type": "labeled_buttons",
      "representation": {
        "true": "Ja",
        "false": "Nein"
      }
    },
    "unique": false,
    "relevant": true,
    "sort_number": 2
  },
  {
    "id": "checkbox",
    "icon_id": 74,
    "default_config": {
      "options": {
        "1": {
          "title": "Option 1",
          "value": null
        }
      },
      "inline": false
    },
    "unique": false,
    "relevant": true,
    "sort_number": 4
  }
]
```

## Show

Show one `TaskFieldType`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/field-types/{formFieldType}`

**Route Parameters**

| Parameter       | Type     | Description                   |
| --------------- | -------- | ----------------------------- |
| `formFieldType` | `string` | Type ID (for example `text`). |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/field-types/text', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": "text",
  "icon_id": 72,
  "default_config": {
    "scanner": false
  },
  "unique": false,
  "relevant": true,
  "sort_number": 1
}
```

## Allowed type keys

* `text`
* `boolean`
* `rich-text`
* `checkbox`
* `radio`
* `select`
* `datetime`
* `file-upload`
* `entity-select`
* `entities-select`
* `recipient-select`
* `description`
* `privacy-policy`
* `signature`


# TaskFieldValidationTypes

## Introduction

`TaskFieldValidationTypes` define which validation rules can be attached to [TaskFields](/api-reference/tasks-2/task-fields).

## Model Definition

**Alias**

`formFieldValidationType`

## List

List available `TaskFieldValidationTypes`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/field-validation-types`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/field-validation-types', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": "required",
    "default_config": {},
    "sort_number": 1
  },
  {
    "id": "minmax",
    "default_config": {
      "min": 0
    },
    "sort_number": 7
  }
]
```

## Show

Show one `TaskFieldValidationType`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/field-validation-types/{formFieldValidationType}`

**Route Parameters**

| Parameter                 | Type     | Description                                  |
| ------------------------- | -------- | -------------------------------------------- |
| `formFieldValidationType` | `string` | Validation type ID (for example `required`). |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/field-validation-types/required', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": "required",
  "default_config": {},
  "sort_number": 1
}
```

## Allowed validation type keys

* `required`
* `email`
* `phone`
* `url`
* `number`
* `digits_only`
* `minmax`
* `items-count`
* `boolean`
* `datetime`
* `after-before`
* `file`
* `mimetypes`
* `last-modified`
* `in`
* `not-in`
* `entity-selection`
* `regex`


# TaskFieldValidations

## Introduction

`TaskFieldValidations` are attached to [TaskFields](/api-reference/tasks-2/task-fields) and are evaluated on `finish` requests.

## Model Definition

**Alias**

`formFieldValidation`

## Admin: Create

Create a new `TaskFieldValidation`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/tasks-2/templates/fields/validations`

**Request Keys**

| Key                               | Type      | Default      | Description                                                                                              |
| --------------------------------- | --------- | ------------ | -------------------------------------------------------------------------------------------------------- |
| `form_field_id`\*                 | `integer` | -            | Target [TaskField](/api-reference/tasks-2/task-fields) ID.                                               |
| `form_field_validation_type_id`\* | `string`  | -            | Validation type key from [TaskFieldValidationTypes](/api-reference/tasks-2/task-field-validation-types). |
| `config`                          | `object`  | type default | Type-specific validation config.                                                                         |

Keys with `*` are required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/tasks-2/templates/fields/validations', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'form_field_id' => 33,
        'form_field_validation_type_id' => 'required'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 91,
    "form_field_id": 33,
    "form_field_validation_type_id": "required",
    "config": {}
  }
}
```

## Admin: Update

Update an existing `TaskFieldValidation`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/tasks-2/templates/fields/validations/{formFieldValidation}`

**Route Parameters**

| Parameter             | Type      | Description    |
| --------------------- | --------- | -------------- |
| `formFieldValidation` | `integer` | Validation ID. |

**Request Keys**

| Key                             | Type      | Description                          |
| ------------------------------- | --------- | ------------------------------------ |
| `form_field_id`                 | `integer` | New target field ID.                 |
| `form_field_validation_type_id` | `string`  | New validation type ID.              |
| `config`                        | `object`  | Updated type-specific configuration. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/tasks-2/templates/fields/validations/91', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'form_field_validation_type_id' => 'email'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 91,
    "form_field_id": 33,
    "form_field_validation_type_id": "email",
    "config": {}
  }
}
```

## Admin: Delete

Delete an existing `TaskFieldValidation`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/tasks-2/templates/fields/validations/{formFieldValidation}`

**Route Parameters**

| Parameter             | Type      | Description    |
| --------------------- | --------- | -------------- |
| `formFieldValidation` | `integer` | Validation ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/tasks-2/templates/fields/validations/91', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# TaskFieldDefaultValueSources

## Introduction

`TaskFieldDefaultValueSources` define prefilled values for [TaskFields](/api-reference/tasks-2/task-fields).

## Model Definition

**Alias**

`defaultValueSource`

**Source Types**

* `formFieldValue` - Copies a value from another form field.
* `systemVariable` - Resolves a value from a system variable.

## Admin: Create

Create a new `TaskFieldDefaultValueSource`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/tasks-2/templates/fields/default-value-sources`

**Request Keys**

| Key                  | Type                | Default      | Description                                                                  |
| -------------------- | ------------------- | ------------ | ---------------------------------------------------------------------------- |
| `source_type`\*      | `string`            | -            | Selects one of the supported [Source Types](#source-types).                  |
| `form_field_id`\*    | `integer`           | -            | Target [TaskField](/api-reference/tasks-2/task-fields) ID.                   |
| `config`             | `object`            | type default | Source-specific configuration.                                               |
| `source_entity_type` | `string` \| `null`  | `null`       | Morph alias of source entity (typically `formField` for field-value source). |
| `source_entity_id`   | `integer` \| `null` | `null`       | Source entity ID (required with `source_entity_type`).                       |
| `sort_number`        | `integer`           | auto         | Evaluation order per field.                                                  |

Keys with `*` are required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/tasks-2/templates/fields/default-value-sources', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'form_field_id' => 33,
        'source_type' => 'systemVariable',
        'config' => [
            'variable_key' => 'system.user.id',
            'variable_filter' => null
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 52,
    "form_field_id": 33,
    "source_type": "systemVariable",
    "config": {
      "variable_key": "system.user.id",
      "variable_filter": null
    },
    "source_entity_type": null,
    "source_entity_id": null,
    "sort_number": 1
  }
}
```

## Admin: Update

Update an existing `TaskFieldDefaultValueSource`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/tasks-2/templates/fields/default-value-sources/{defaultValueSource}`

**Route Parameters**

| Parameter            | Type      | Description              |
| -------------------- | --------- | ------------------------ |
| `defaultValueSource` | `integer` | Default value source ID. |

**Request Keys**

| Key                  | Type                | Description                                                 |
| -------------------- | ------------------- | ----------------------------------------------------------- |
| `source_type`        | `string`            | Selects one of the supported [Source Types](#source-types). |
| `form_field_id`      | `integer`           | New target field ID.                                        |
| `config`             | `object`            | Updated source configuration.                               |
| `source_entity_type` | `string` \| `null`  | New source entity type.                                     |
| `source_entity_id`   | `integer` \| `null` | New source entity ID.                                       |
| `sort_number`        | `integer`           | New evaluation order.                                       |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/tasks-2/templates/fields/default-value-sources/52', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'source_type' => 'formFieldValue',
        'source_entity_type' => 'formField',
        'source_entity_id' => 33
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 52,
    "form_field_id": 33,
    "source_type": "formFieldValue",
    "config": {},
    "source_entity_type": "formField",
    "source_entity_id": 33,
    "sort_number": 1
  }
}
```

## Admin: Delete

Delete an existing `TaskFieldDefaultValueSource`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/tasks-2/templates/fields/default-value-sources/{defaultValueSource}`

**Route Parameters**

| Parameter            | Type      | Description              |
| -------------------- | --------- | ------------------------ |
| `defaultValueSource` | `integer` | Default value source ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/tasks-2/templates/fields/default-value-sources/52', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```

## Admin: List Source TaskFields

List accessible fields from administrable [Forms](/api-reference/forms/forms) and [TaskTemplates](/api-reference/tasks-2/task-templates) that can provide a `formFieldValue` default value.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/tasks-2/templates/fields/default-value-sources/form-field-value/form-fields`

**Request Keys**

| Key        | Type      | Default     | Description                                             |
| ---------- | --------- | ----------- | ------------------------------------------------------- |
| `page`     | `integer` | `1`         | Result page.                                            |
| `per_page` | `integer` | API default | Results per page.                                       |
| `filter`   | `object`  | `{}`        | Standard Result Control filters, including `form_type`. |

**Behavior**

* Fields are returned only when the authenticated user can administer their parent Form or TaskTemplate.
* Each preview uses the field name as `title`, the localized parent type as `sub_title`, and the parent title as `description`.
* This endpoint uses [count-less pagination](/introduction/query-manipulation/result-control#count-less-pagination), so `total` is `null`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/tasks-2/templates/fields/default-value-sources/form-field-value/form-fields', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'per_page' => 10,
        'filter' => [
            'form_type' => ['is' => 'taskTemplate']
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "data": [
    {
      "id": 33,
      "title": "Comment",
      "sub_title": "Task template",
      "description": "Safety Checklist",
      "additional_data": {
        "source_entity_type": "formField",
        "form_type": "taskTemplate",
        "form_id": 21,
        "form_field_type_id": "text",
        "sort_number": 1
      }
    },
    {
      "id": 34,
      "title": "Equipment operational",
      "sub_title": "Task template",
      "description": "Equipment Inspection",
      "additional_data": {
        "source_entity_type": "formField",
        "form_type": "taskTemplate",
        "form_id": 22,
        "form_field_type_id": "boolean",
        "sort_number": 2
      }
    }
  ],
  "current_page": 1,
  "total": null,
  "per_page": 10
}
```

## Admin: List Source TaskField Filters

List filters available for the source-field preview endpoint.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/tasks-2/templates/fields/default-value-sources/form-field-value/form-fields/filters`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/tasks-2/templates/fields/default-value-sources/form-field-value/form-fields/filters', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "data": [
    {
      "key": "form_id",
      "label": "Template",
      "type": "multi-select"
    },
    {
      "key": "form_type",
      "label": "Template Type",
      "type": "multi-select"
    }
  ]
}
```

## Admin: List Source TaskField Filter Options

List options for one multi-select source-field filter.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/tasks-2/templates/fields/default-value-sources/form-field-value/form-fields/filters/{filterKey}/options`

**Route Parameters**

| Parameter   | Type     | Description                                     |
| ----------- | -------- | ----------------------------------------------- |
| `filterKey` | `string` | Multi-select filter key, for example `form_id`. |

**Request Keys**

| Key        | Type      | Default     | Description                                  |
| ---------- | --------- | ----------- | -------------------------------------------- |
| `page`     | `integer` | `1`         | Result page.                                 |
| `per_page` | `integer` | API default | Results per page.                            |
| `filter`   | `object`  | `{}`        | Filters applied before options are produced. |

**Behavior**

This endpoint uses [count-less pagination](/introduction/query-manipulation/result-control#count-less-pagination), so `total` is `null`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/tasks-2/templates/fields/default-value-sources/form-field-value/form-fields/filters/form_id/options', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'filter' => [
            'form_type' => ['in' => 'taskTemplate']
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "data": [
    {
      "value": 21,
      "label": "Safety Checklist",
      "additionalData": {
        "form_type": "taskTemplate"
      }
    },
    {
      "value": 22,
      "label": "Equipment Inspection",
      "additionalData": {
        "form_type": "taskTemplate"
      }
    }
  ],
  "current_page": 1,
  "total": null,
  "per_page": 15
}
```

## Runtime Default Values for TaskExecutions

Resolve default values for one `TaskExecution`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/executions/{taskExecution}/default-values`

**Route Parameters**

| Parameter       | Type      | Description       |
| --------------- | --------- | ----------------- |
| `taskExecution` | `integer` | Task execution ID |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/executions/812/default-values', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "33": {
    "value": "2"
  }
}
```


# TaskFieldDisplayConditions

## Introduction

`TaskFieldDisplayConditions` attach conditional visibility behavior to [TaskFields](/api-reference/tasks-2/task-fields).

## Model Definition

**Alias**

`formFieldDisplayCondition`

## Admin: Create

Create a new `TaskFieldDisplayCondition`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/tasks-2/templates/fields/display-conditions`

**Request Keys**

| Key                 | Type     | Default | Description                                        |
| ------------------- | -------- | ------- | -------------------------------------------------- |
| `attachable_type`\* | `string` | -       | Morph alias of attachable entity. Use `formField`. |
| `attachable_id`\*   | `string` | -       | ID of attachable task field entity.                |

Keys with `*` are required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/tasks-2/templates/fields/display-conditions', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'attachable_type' => 'formField',
        'attachable_id' => 33
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 77,
    "attachable_type": "formField",
    "attachable_id": "33"
  }
}
```

## Admin: Update

Update an existing `TaskFieldDisplayCondition`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/tasks-2/templates/fields/display-conditions/{formFieldDisplayCondition}`

**Route Parameters**

| Parameter                   | Type      | Description           |
| --------------------------- | --------- | --------------------- |
| `formFieldDisplayCondition` | `integer` | Display condition ID. |

**Request Keys**

| Key               | Type     | Default | Description                 |
| ----------------- | -------- | ------- | --------------------------- |
| `attachable_type` | `string` | -       | New attachable morph alias. |
| `attachable_id`   | `string` | -       | New attachable entity ID.   |

**Behavior**

* When the attachment changes, both `attachable_type` and `attachable_id` must resolve to a supported entity the user may configure.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/tasks-2/templates/fields/display-conditions/77', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'attachable_type' => 'formField',
        'attachable_id' => 34
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 77,
    "attachable_type": "formField",
    "attachable_id": "34"
  }
}
```

## Admin: Delete

Delete an existing `TaskFieldDisplayCondition`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/tasks-2/templates/fields/display-conditions/{formFieldDisplayCondition}`

**Route Parameters**

| Parameter                   | Type      | Description           |
| --------------------------- | --------- | --------------------- |
| `formFieldDisplayCondition` | `integer` | Display condition ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/tasks-2/templates/fields/display-conditions/77', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# TaskAssignments

## Introduction

`TaskAssignments` define when and for whom tasks should be executed.

Assignment data and related [TaskTemplates](/api-reference/tasks-2/task-templates) are used to create [TaskExecutions](/api-reference/tasks-2/task-executions) for users and departments.

The `description` field uses the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`taskAssignment`

**Relations**

| Key                       | Relation                                                                     | Type            | Relation Field(s)                    |
| ------------------------- | ---------------------------------------------------------------------------- | --------------- | ------------------------------------ |
| `taskTemplate`            | [TaskTemplate](/api-reference/tasks-2/task-templates)                        | Belongs to      | `task_template_id`                   |
| `taskTemplateComposition` | [TaskTemplateComposition](/api-reference/tasks-2/task-template-compositions) | Belongs to      | `task_template_composition_id`       |
| `children`                | [TaskAssignments](/api-reference/tasks-2/task-assignments)                   | Has many        | `parent_id`                          |
| `departments`             | [Departments](/api-reference/departments)                                    | Belongs to many | Intermediate table                   |
| `users`                   | [Users](/api-reference/users)                                                | Belongs to many | Intermediate table                   |
| `creator`                 | [User](/api-reference/users)                                                 | Belongs to      | `creator_user_id`                    |
| `taskExecutions`          | [TaskExecutions](/api-reference/tasks-2/task-executions)                     | Has many        | `task_executions.task_assignment_id` |

**Capabilities**

* [Targetables](/introduction/resource-capabilities/targetables) - `assign_mode`, `user_ids`, and `department_ids` determine who receives executions; child assignments can inherit their parent's targets.
* [Entity Permissions](/introduction/resource-capabilities/entity-permissions) - Direct grants participate in administration access together with ownership and module permissions.
* [Translations](/introduction/resource-capabilities/translations) - The `title` and `description` fields are translatable.

## Admin: List

List all `TaskAssignments` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/tasks-2/assignments`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/tasks-2/assignments', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 80,
    "lang_id": "en-US",
    "title": "Morning Shift",
    "task_template_id": 31,
    "assign_mode": "any_of",
    "active": true
  },
  {
    "id": 81,
    "lang_id": "en-US",
    "title": "Weekly Equipment Review",
    "task_template_composition_id": 12,
    "task_template_id": null,
    "assign_mode": "one_of_user",
    "always_executable": false,
    "active": true
  }
]
```

## List

List assignments relevant to the authenticated user.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/assignments`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/assignments', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 80,
    "lang_id": "en-US",
    "title": "Morning Shift",
    "task_template_id": 31,
    "assign_mode": "any_of",
    "start_date": "2026-08-07 00:00:00",
    "always_executable": false
  },
  {
    "id": 82,
    "lang_id": "en-US",
    "title": "Incident Follow-up",
    "task_template_id": 32,
    "assign_mode": "one_of_user",
    "start_date": null,
    "always_executable": true
  }
]
```

## List Always-Executable Assignments

List assignments the authenticated user may execute without a time window.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/assignments/always-executable`

**Behavior**

* The result is restricted to assignments relevant for the authenticated user and allowed by the always-executable assignment permission strategy.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/assignments/always-executable', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 82,
    "lang_id": "en-US",
    "title": "Incident Follow-up",
    "task_template_id": 32,
    "assign_mode": "one_of_user",
    "always_executable": true
  },
  {
    "id": 83,
    "lang_id": "en-US",
    "title": "Unscheduled Safety Report",
    "task_template_id": 33,
    "assign_mode": "all_users",
    "always_executable": true
  }
]
```

## Admin: Show

Show one `TaskAssignment` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/tasks-2/assignments/{taskAssignment}`

**Route Parameters**

| Parameter        | Type      | Description       |
| ---------------- | --------- | ----------------- |
| `taskAssignment` | `integer` | TaskAssignment ID |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/tasks-2/assignments/80', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 80,
  "lang_id": "en-US",
  "title": "Morning Shift",
  "task_template_id": 31,
  "assign_mode": "any_of",
  "active": true,
  "reset_allowed": true
}
```

## Admin: Create

Create a new `TaskAssignment`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/tasks-2/assignments`

**Request Keys**

| Key                            | Type                | Default         | Description                                                                                                                   |
| ------------------------------ | ------------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `title`\*                      | `string`            | -               | Assignment title.                                                                                                             |
| `description`                  | `string`            | `null`          | Assignment description (HTML).                                                                                                |
| `lang_id`                      | `string`            | system language | Language key.                                                                                                                 |
| `folder_id`                    | `integer` \| `null` | `null`          | Related [Folder](/api-reference/folders) ID.                                                                                  |
| `parent_id`                    | `integer` \| `null` | `null`          | Parent composition assignment ID.                                                                                             |
| `inherit_parent_values`        | `boolean`           | `false`         | Inherit timing and targeting from the parent assignment.                                                                      |
| `task_template_id`             | `integer` \| `null` | `null`          | Related [TaskTemplate](/api-reference/tasks-2/task-templates) ID.                                                             |
| `task_template_composition_id` | `integer` \| `null` | `null`          | Related [TaskTemplateComposition](/api-reference/tasks-2/task-template-compositions) ID; required without `task_template_id`. |
| `assign_mode`                  | `string` \| `null`  | `all`           | Selects one of the shared [Assignment Modes](/introduction/assignment-and-targeting#assignment-modes).                        |
| `start_date`                   | `date`              | `null`          | Start date (`YYYY-MM-DD`).                                                                                                    |
| `start_time`                   | `time`              | `null`          | Start time (`H:i:s`).                                                                                                         |
| `show_before_interval`         | `interval`          | `null`          | Visibility lead interval (ISO 8601 duration).                                                                                 |
| `executable_before_interval`   | `interval`          | `null`          | Executable-before interval (ISO 8601 duration).                                                                               |
| `executable_after_interval`    | `interval`          | `null`          | Executable-after interval (ISO 8601 duration).                                                                                |
| `interval`                     | `string`            | `null`          | iCal RRULE.                                                                                                                   |
| `interval_end`                 | `date`              | `null`          | End date for recurrence.                                                                                                      |
| `active`                       | `boolean`           | `true`          | Active state.                                                                                                                 |
| `always_executable`            | `boolean`           | `false`         | Allows execution without time restrictions.                                                                                   |
| `user_confirmation_required`   | `boolean`           | `true`          | Requires real-user confirmation.                                                                                              |
| `delayed_reason_required`      | `boolean`           | `true`          | Requires delayed reason for delayed finish.                                                                                   |
| `deny_reason_required`         | `boolean`           | `true`          | Requires deny reason on deny action.                                                                                          |
| `reset_allowed`                | `boolean`           | `true`          | Allows reset action for related executions.                                                                                   |
| `sort_number`                  | `integer`           | auto            | Sort number.                                                                                                                  |
| `department_ids`               | `array`             | `[]`            | Target departments.                                                                                                           |
| `user_ids`                     | `array`             | `[]`            | Target users.                                                                                                                 |
| `entity_permissions`           | `object[]`          | `[]`            | Direct [Entity Permissions](/introduction/resource-capabilities/entity-permissions).                                          |

Keys with `*` are required. A root assignment must provide either `task_template_id` or `task_template_composition_id`.

**Behavior**

* `creator_user_id`, `revision_id`, `dispatched_by_system`, and the root assignment's `quick_assignment` value are controlled by the backend.
* User targets take precedence over department targets during creation when both arrays are non-empty.
* Timing and recurrence values are cleared when `start_date` is absent or `always_executable` is `true`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/tasks-2/assignments', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Morning Shift',
        'task_template_id' => 31,
        'lang_id' => 'en-US',
        'assign_mode' => 'any_of',
        'active' => true,
        'reset_allowed' => true,
        'user_ids' => [144]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 80,
    "creator_user_id": 144,
    "lang_id": "en-US",
    "title": "Morning Shift",
    "task_template_id": 31,
    "assign_mode": "any_of",
    "active": true,
    "reset_allowed": true
  }
}
```

## Create Quick Assignment

Create a quick `TaskAssignment` for the current authenticated user.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/tasks-2/assignments/quick-assignment`

**Request Keys**

| Key                  | Type       | Default         | Description                                                                          |
| -------------------- | ---------- | --------------- | ------------------------------------------------------------------------------------ |
| `title`\*            | `string`   | -               | Assignment title.                                                                    |
| `task_template_id`\* | `integer`  | -               | Related quick-assignable [TaskTemplate](/api-reference/tasks-2/task-templates).      |
| `lang_id`            | `string`   | system language | Language key.                                                                        |
| `start_date`\*       | `date`     | -               | Start date.                                                                          |
| `start_time`\*       | `time`     | -               | Start time in `H:i:s` format.                                                        |
| `department_ids`     | `array`    | `[]`            | Target departments.                                                                  |
| `user_ids`           | `array`    | `[]`            | Target users.                                                                        |
| `entity_permissions` | `object[]` | `[]`            | Direct [Entity Permissions](/introduction/resource-capabilities/entity-permissions). |

Keys with `*` are required.

**Behavior**

* The endpoint forces `quick_assignment` and `active` to `true`, disables recurrence, and derives `assign_mode` from the retained user or department targets.
* Target IDs the authenticated user may not quick-assign are removed before validation.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/tasks-2/assignments/quick-assignment', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Quick Safety Check',
        'task_template_id' => 31,
        'lang_id' => 'en-US',
        'start_date' => '2026-08-07',
        'start_time' => '09:00:00',
        'user_ids' => [144]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 81,
    "creator_user_id": 144,
    "lang_id": "en-US",
    "title": "Quick Safety Check",
    "task_template_id": 31,
    "quick_assignment": true,
    "start_date": "2026-08-07 00:00:00",
    "start_time": "09:00:00",
    "assign_mode": "one_of"
  }
}
```

## Admin: Update

Update an existing `TaskAssignment`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/tasks-2/assignments/{taskAssignment}`

**Route Parameters**

| Parameter        | Type      | Description        |
| ---------------- | --------- | ------------------ |
| `taskAssignment` | `integer` | TaskAssignment ID. |

**Request Keys**

| Key                            | Type                 | Description                                              |
| ------------------------------ | -------------------- | -------------------------------------------------------- |
| `title`                        | `string` \| `null`   | New assignment title.                                    |
| `description`                  | `string` \| `null`   | New HTML description.                                    |
| `lang_id`                      | `string`             | New language key.                                        |
| `folder_id`                    | `integer` \| `null`  | New related Folder ID.                                   |
| `parent_id`                    | `integer` \| `null`  | New parent composition assignment ID.                    |
| `inherit_parent_values`        | `boolean`            | Whether values are inherited from the parent assignment. |
| `task_template_id`             | `integer` \| `null`  | New TaskTemplate ID.                                     |
| `task_template_composition_id` | `integer` \| `null`  | New TaskTemplateComposition ID.                          |
| `assign_mode`                  | `string` \| `null`   | New assignment mode.                                     |
| `start_date`                   | `date` \| `null`     | New start date.                                          |
| `start_time`                   | `time` \| `null`     | New start time.                                          |
| `show_before_interval`         | `interval` \| `null` | New visibility interval.                                 |
| `executable_before_interval`   | `interval` \| `null` | New executable-before interval.                          |
| `executable_after_interval`    | `interval` \| `null` | New executable-after interval.                           |
| `interval`                     | `string` \| `null`   | New RRULE.                                               |
| `interval_end`                 | `date` \| `null`     | New recurrence end date.                                 |
| `active`                       | `boolean`            | New active state.                                        |
| `always_executable`            | `boolean` \| `null`  | New always-executable state.                             |
| `user_confirmation_required`   | `boolean` \| `null`  | New confirmation requirement.                            |
| `delayed_reason_required`      | `boolean` \| `null`  | New delayed-reason requirement.                          |
| `deny_reason_required`         | `boolean` \| `null`  | New deny-reason requirement.                             |
| `reset_allowed`                | `boolean` \| `null`  | New reset-allowed state.                                 |
| `sort_number`                  | `integer`            | New sort number.                                         |
| `department_ids`               | `array`              | Updated target departments.                              |
| `user_ids`                     | `array`              | Updated target users.                                    |
| `entity_permissions`           | `object[]`           | Updated direct Entity Permissions.                       |

**Behavior**

* `revision_id`, `creator_user_id`, `dispatched_by_system`, and `quick_assignment` cannot be set by the client.
* A changed template, composition, timing, recurrence, active state, assignment mode, inheritance setting, or removed target creates a new assignment revision and soft-deletes the previous record. Adding targets alone does not create a revision.
* An assignment cannot be activated beneath an inactive parent. Timing and recurrence values are cleared when `start_date` is cleared or `always_executable` becomes `true`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/tasks-2/assignments/80', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Morning Shift (updated)',
        'reset_allowed' => false
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 80,
    "lang_id": "en-US",
    "title": "Morning Shift (updated)",
    "reset_allowed": false
  }
}
```

## Admin: Delete

Delete an existing `TaskAssignment`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/tasks-2/assignments/{taskAssignment}`

**Route Parameters**

| Parameter        | Type      | Description       |
| ---------------- | --------- | ----------------- |
| `taskAssignment` | `integer` | TaskAssignment ID |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/tasks-2/assignments/80', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# TaskExecutions

## Introduction

`TaskExecutions` represent concrete task instances for users or departments.

Executions are usually created from [TaskAssignments](/api-reference/tasks-2/task-assignments) and [TaskTemplates](/api-reference/tasks-2/task-templates).

The `deny_reason` and `delayed_reason` fields use the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`taskExecution`

**Relations**

| Key                  | Relation                                                          | Type       | Relation Field(s)                        |
| -------------------- | ----------------------------------------------------------------- | ---------- | ---------------------------------------- |
| `taskProgressFields` | [TaskProgressFields](/api-reference/tasks-2/task-progress-fields) | Has many   | `task_progress_fields.task_execution_id` |
| `taskAssignment`     | [TaskAssignment](/api-reference/tasks-2/task-assignments)         | Belongs to | `task_assignment_id`                     |
| `taskTemplate`       | [TaskTemplate](/api-reference/tasks-2/task-templates)             | Belongs to | `task_template_id`                       |
| `taskStatus`         | `TaskStatus`                                                      | Belongs to | `task_status_id`                         |
| `user`               | [User](/api-reference/users)                                      | Belongs to | `user_id`                                |
| `department`         | [Department](/api-reference/departments)                          | Belongs to | `department_id`                          |
| `lastActivityUser`   | [User](/api-reference/users)                                      | Belongs to | `last_activity_user_id`                  |
| `parent`             | [TaskExecution](/api-reference/tasks-2/task-executions)           | Belongs to | `parent_id`                              |
| `children`           | [TaskExecutions](/api-reference/tasks-2/task-executions)          | Has many   | `parent_id`                              |

**Computed Properties**

* `hash` - Stable encoded representation used by execution links.
* `url` and `static_url` - Link to the execution page.
* `visibility_start_time`, `execution_start_time`, and `execution_end_time` - Time-window boundaries calculated from the assignment.
* `achieved_points` and `max_points` - Grade totals calculated from progress fields and child executions.

**Capabilities**

* [Targetables](/introduction/resource-capabilities/targetables) - Visibility and execution access derive from the parent assignment's users, departments, assignment mode, and active time window.
* [Entity Permissions](/introduction/resource-capabilities/entity-permissions) - Execution grants are managed internally; these endpoints do not accept client-controlled Entity Permissions.
* [URL Context](/introduction/resource-capabilities/url-context) - Execution URLs resolve to access-checked context information using the assignment or template title.
* [Translations](/introduction/resource-capabilities/translations) - The `deny_reason` and `delayed_reason` fields are translatable.

## Task Status Indicators

Possible values for `task_status_id`:

* `open`
* `in_progress`
* `in_progress_delayed`
* `done`
* `done_delayed`
* `denied`
* `missed`

## Admin: List

List executions accessible through administrable assignments, including soft-deleted records.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/tasks-2/executions`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/tasks-2/executions', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 812,
    "user_id": 144,
    "department_id": 7,
    "task_assignment_id": 558,
    "task_template_id": 203,
    "task_status_id": "open",
    "lang_id": "en-US",
    "for_execution_date": "2026-08-07 09:00:00",
    "deleted_at": null
  },
  {
    "id": 813,
    "user_id": null,
    "department_id": 8,
    "task_assignment_id": 559,
    "task_template_id": 204,
    "task_status_id": "done_delayed",
    "lang_id": "en-US",
    "for_execution_date": "2026-08-06 14:00:00",
    "deleted_at": null
  }
]
```

## Admin: Show

Show one execution, including a soft-deleted record or a temporary calculated execution.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/tasks-2/executions/{taskExecution}`

**Route Parameters**

| Parameter       | Type                  | Description                               |
| --------------- | --------------------- | ----------------------------------------- |
| `taskExecution` | `integer` \| `string` | Execution ID or temporary execution hash. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/tasks-2/executions/812', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 812,
  "user_id": 144,
  "department_id": 7,
  "task_assignment_id": 558,
  "task_template_id": 203,
  "task_status_id": "open",
  "lang_id": "en-US",
  "for_execution_date": "2026-08-07 09:00:00",
  "deleted_at": null
}
```

## Admin: List by TaskAssignment

List calculated and persisted executions for an assignment and date range, including executions retained from previous assignment revisions.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/tasks-2/assignments/{taskAssignmentWithTrashed}/executions`

**Route Parameters**

| Parameter                   | Type      | Description                            |
| --------------------------- | --------- | -------------------------------------- |
| `taskAssignmentWithTrashed` | `integer` | Current or soft-deleted assignment ID. |

**Request Keys**

| Key              | Type       | Description              |
| ---------------- | ---------- | ------------------------ |
| `period_start`\* | `datetime` | Start of the date range. |
| `period_end`\*   | `datetime` | End of the date range.   |

Keys with `*` are required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/tasks-2/assignments/558/executions', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'period_start' => '2026-08-07 00:00:00',
        'period_end' => '2026-08-08 23:59:59'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 812,
    "task_assignment_id": 558,
    "task_template_id": 203,
    "task_status_id": "open",
    "lang_id": "en-US",
    "for_execution_date": "2026-08-07 09:00:00"
  },
  {
    "id": 814,
    "task_assignment_id": 558,
    "task_template_id": 203,
    "task_status_id": "done",
    "lang_id": "en-US",
    "for_execution_date": "2026-08-08 09:00:00"
  }
]
```

## List by Date Range

List visible `TaskExecutions` within a date range.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/executions`

**Request Keys**

| Key            | Type       | Default | Description          |
| -------------- | ---------- | ------- | -------------------- |
| `period_start` | `datetime` | -       | Start of date range. |
| `period_end`   | `datetime` | -       | End of date range.   |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/executions', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'period_start' => '2026-08-07 00:00:00',
        'period_end' => '2026-08-07 23:59:59'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 812,
    "task_assignment_id": 558,
    "task_template_id": 203,
    "lang_id": "en-US",
    "task_status_id": "open",
    "for_execution_date": "2026-08-07 09:00:00",
    "achieved_points": 0,
    "max_points": 0
  },
  {
    "id": 813,
    "task_assignment_id": 559,
    "task_template_id": 204,
    "lang_id": "en-US",
    "task_status_id": "in_progress",
    "for_execution_date": "2026-08-07 14:00:00",
    "achieved_points": 40,
    "max_points": 100
  }
]
```

## List by Assignment and Date Range

List visible `TaskExecutions` for one assignment within a date range.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/assignments/{taskAssignment}/executions`

**Route Parameters**

| Parameter        | Type      | Description        |
| ---------------- | --------- | ------------------ |
| `taskAssignment` | `integer` | TaskAssignment ID. |

**Request Keys**

| Key            | Type       | Default | Description          |
| -------------- | ---------- | ------- | -------------------- |
| `period_start` | `datetime` | -       | Start of date range. |
| `period_end`   | `datetime` | -       | End of date range.   |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/assignments/558/executions', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'period_start' => '2026-08-07 00:00:00',
        'period_end' => '2026-08-08 23:59:59'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 812,
    "task_assignment_id": 558,
    "task_template_id": 203,
    "lang_id": "en-US",
    "task_status_id": "open"
  },
  {
    "id": 814,
    "task_assignment_id": 558,
    "task_template_id": 203,
    "lang_id": "en-US",
    "task_status_id": "done",
    "for_execution_date": "2026-08-08 09:00:00"
  }
]
```

## Count

Count visible `TaskExecutions`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/executions/count`

**Request Keys**

| Key              | Type       | Description                      |
| ---------------- | ---------- | -------------------------------- |
| `period_start`\* | `datetime` | Start of the counted date range. |
| `period_end`\*   | `datetime` | End of the counted date range.   |

Keys with `*` are required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/executions/count', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'period_start' => '2026-08-07 00:00:00',
        'period_end' => '2026-08-07 23:59:59'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
12
```

## List Missed Executions

List missed and delayed in-progress executions relevant to the authenticated user.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/executions/missed`

**Request Keys**

| Key                      | Type       | Default            | Description                                                  |
| ------------------------ | ---------- | ------------------ | ------------------------------------------------------------ |
| `period_start`           | `datetime` | User creation time | Start of the inspected range.                                |
| `period_end`             | `datetime` | Current time       | End of the inspected range.                                  |
| `latest_recursions_only` | `boolean`  | `true`             | Return only the latest recurrence for recurring assignments. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/executions/missed', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'period_start' => '2026-08-01 00:00:00',
        'period_end' => '2026-08-06 23:59:59',
        'latest_recursions_only' => false
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 790,
    "task_assignment_id": 550,
    "task_template_id": 200,
    "task_status_id": "missed",
    "lang_id": "en-US",
    "for_execution_date": "2026-08-03 08:00:00"
  },
  {
    "id": 798,
    "task_assignment_id": 553,
    "task_template_id": 201,
    "task_status_id": "in_progress_delayed",
    "lang_id": "en-US",
    "for_execution_date": "2026-08-05 16:00:00"
  }
]
```

## Count Missed Executions

Count executions returned by the missed-executions query.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/executions/missed/count`

**Request Keys**

| Key                      | Type       | Default            | Description                                                 |
| ------------------------ | ---------- | ------------------ | ----------------------------------------------------------- |
| `period_start`           | `datetime` | User creation time | Start of the inspected range.                               |
| `period_end`             | `datetime` | Current time       | End of the inspected range.                                 |
| `latest_recursions_only` | `boolean`  | `true`             | Count only the latest recurrence for recurring assignments. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/executions/missed/count', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'period_start' => '2026-08-01 00:00:00',
        'period_end' => '2026-08-06 23:59:59',
        'latest_recursions_only' => false
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
2
```

## Show

Show one visible `TaskExecution`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/executions/{taskExecution}`

**Route Parameters**

| Parameter       | Type      | Description       |
| --------------- | --------- | ----------------- |
| `taskExecution` | `integer` | TaskExecution ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/executions/812', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 812,
  "task_assignment_id": 558,
  "task_template_id": 203,
  "lang_id": "en-US",
  "task_status_id": "open",
  "for_execution_date": "2026-08-07 09:00:00",
  "achieved_points": 0,
  "max_points": 0
}
```

## Create

Create a new `TaskExecution`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/tasks-2/executions`

**Request Keys**

| Key                    | Type       | Default | Description                                                           |
| ---------------------- | ---------- | ------- | --------------------------------------------------------------------- |
| `task_assignment_id`\* | `integer`  | -       | Related [TaskAssignment](/api-reference/tasks-2/task-assignments) ID. |
| `task_template_id`\*   | `integer`  | -       | Related [TaskTemplate](/api-reference/tasks-2/task-templates) ID.     |
| `parent_id`            | `integer`  | `null`  | Parent TaskExecution ID for child executions.                         |
| `task_status_id`       | `string`   | `open`  | Initial status ID.                                                    |
| `for_execution_date`   | `datetime` | `null`  | Execution date/time.                                                  |

Keys with `*` are required.

**Behavior**

* `user_id` and `department_id` are derived from the authenticated user and the assignment's targeting; client-provided values cannot choose another recipient.
* `last_activity_user_id` is derived from authentication and the initial status.
* If an equivalent relevant execution already exists, the repository returns it instead of creating a duplicate.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/tasks-2/executions', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'task_assignment_id' => 558,
        'task_template_id' => 203
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 812,
    "user_id": 144,
    "department_id": 7,
    "task_assignment_id": 558,
    "task_template_id": 203,
    "lang_id": "en-US",
    "task_status_id": "open"
  }
}
```

## Create from Composition

Create one execution for each permitted child assignment of a composition assignment.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/tasks-2/executions/composition`

**Request Keys**

| Key                    | Type      | Description                                                                            |
| ---------------------- | --------- | -------------------------------------------------------------------------------------- |
| `task_assignment_id`\* | `integer` | Composition-based parent [TaskAssignment](/api-reference/tasks-2/task-assignments) ID. |

Keys with `*` are required.

**Behavior**

* `user_id` and `department_id` are derived from the authenticated user and assignment targeting.
* Child assignments the real user cannot execute are skipped.
* Existing equivalent executions and children that cannot produce an execution are omitted from the returned collection.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/tasks-2/executions/composition', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'task_assignment_id' => 560
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": [
    {
      "id": 815,
      "user_id": 144,
      "department_id": 7,
      "task_assignment_id": 561,
      "task_template_id": 205,
      "task_status_id": "open",
      "lang_id": "en-US"
    },
    {
      "id": 816,
      "user_id": 144,
      "department_id": 7,
      "task_assignment_id": 562,
      "task_template_id": 206,
      "task_status_id": "open",
      "lang_id": "en-US"
    }
  ]
}
```

## Progress

Update the progress values of one `TaskExecution`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/tasks-2/executions/{taskExecution}/progress`

**Route Parameters**

| Parameter       | Type      | Description       |
| --------------- | --------- | ----------------- |
| `taskExecution` | `integer` | TaskExecution ID. |

**Request Keys**

| Key                                  | Type         | Default         | Description                                                                 |
| ------------------------------------ | ------------ | --------------- | --------------------------------------------------------------------------- |
| `lang_id`                            | `string`     | system language | Language key stored with progress fields.                                   |
| `{field-key}`                        | `field type` | -               | Field value keyed by [TaskField](/api-reference/tasks-2/task-fields) `key`. |
| `{field-key}_additional_information` | `string`     | `null`          | Optional additional information for the field value.                        |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/tasks-2/executions/812/progress', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'comment' => 'Started checklist'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 812,
    "lang_id": "en-US",
    "task_status_id": "in_progress"
  }
}
```

## Finish

Finish one `TaskExecution`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/tasks-2/executions/{taskExecution}/progress/finish`

**Route Parameters**

| Parameter       | Type      | Description       |
| --------------- | --------- | ----------------- |
| `taskExecution` | `integer` | TaskExecution ID. |

**Request Keys**

| Key                                  | Type         | Default         | Description                                                                 |
| ------------------------------------ | ------------ | --------------- | --------------------------------------------------------------------------- |
| `lang_id`                            | `string`     | system language | Language key stored with progress fields.                                   |
| `{field-key}`                        | `field type` | -               | Field value keyed by [TaskField](/api-reference/tasks-2/task-fields) `key`. |
| `{field-key}_additional_information` | `string`     | `null`          | Optional additional information for the field value.                        |
| `delayed_reason`                     | `string`     | `null`          | Delayed reason if delayed-state completion requires it.                     |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/tasks-2/executions/812/progress/finish', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'comment' => 'Checklist completed'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 812,
    "lang_id": "en-US",
    "task_status_id": "done"
  }
}
```

## Deny

Deny one `TaskExecution`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/tasks-2/executions/{taskExecution}/deny`

**Route Parameters**

| Parameter       | Type      | Description       |
| --------------- | --------- | ----------------- |
| `taskExecution` | `integer` | TaskExecution ID. |

**Request Keys**

| Key           | Type     | Default         | Description                                                |
| ------------- | -------- | --------------- | ---------------------------------------------------------- |
| `lang_id`     | `string` | system language | Language key for the translated denial reason.             |
| `deny_reason` | `string` | `null`          | Deny reason (required when assignment policy requires it). |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/tasks-2/executions/812/deny', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'deny_reason' => '<p>Equipment unavailable.</p>'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 812,
    "lang_id": "en-US",
    "task_status_id": "denied",
    "deny_reason": "<p>Equipment unavailable.</p>"
  }
}
```

## Reset

Reset one `TaskExecution`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/tasks-2/executions/{taskExecution}/reset`

**Route Parameters**

| Parameter       | Type      | Description       |
| --------------- | --------- | ----------------- |
| `taskExecution` | `integer` | TaskExecution ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/tasks-2/executions/812/reset', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 812,
    "lang_id": "en-US",
    "task_status_id": "in_progress"
  }
}
```

## Update State

Update the status of one `TaskExecution`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/tasks-2/executions/{taskExecution}/status`

**Route Parameters**

| Parameter       | Type      | Description       |
| --------------- | --------- | ----------------- |
| `taskExecution` | `integer` | TaskExecution ID. |

**Request Keys**

| Key                | Type     | Default | Description         |
| ------------------ | -------- | ------- | ------------------- |
| `task_status_id`\* | `string` | -       | New task status ID. |

Keys with `*` are required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/tasks-2/executions/812/status', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'task_status_id' => 'in_progress'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 812,
    "lang_id": "en-US",
    "task_status_id": "in_progress"
  }
}
```

## List Previous Workflow Nodes

List previous workflow nodes for one `TaskAssignment`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/executions/{taskAssignment}/workflow-nodes/previous`

**Route Parameters**

| Parameter        | Type      | Description                              |
| ---------------- | --------- | ---------------------------------------- |
| `taskAssignment` | `integer` | TaskAssignment ID used as workflow root. |

**Request Keys**

| Key      | Type      | Default | Description                                                   |
| -------- | --------- | ------- | ------------------------------------------------------------- |
| `levels` | `integer` | `0`     | Maximum levels to resolve (`0` resolves all previous levels). |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/executions/558/workflow-nodes/previous', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => ['levels' => 2]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 9001,
    "type": "taskExecutionFinished",
    "level": 1,
    "dispatchable_id": 812,
    "dispatchable_type": "taskExecution"
  },
  {
    "id": 9000,
    "type": "formMessageSent",
    "level": 2,
    "dispatchable_id": 301,
    "dispatchable_type": "formMessage"
  }
]
```

## Show Previous Workflow Node

Show one previous workflow node for a `TaskAssignment`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/executions/{taskAssignment}/workflow-nodes/previous/{level}`

**Route Parameters**

| Parameter        | Type      | Description                              |
| ---------------- | --------- | ---------------------------------------- |
| `taskAssignment` | `integer` | TaskAssignment ID used as workflow root. |
| `level`          | `integer` | Previous workflow level to return.       |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/executions/558/workflow-nodes/previous/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 9001,
  "type": "taskExecutionFinished",
  "level": 1,
  "dispatchable_id": 812,
  "dispatchable_type": "taskExecution"
}
```

## Show Previous Workflow Node Dispatchable

Show the dispatchable entity of one previous workflow node.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/tasks-2/executions/{taskAssignment}/workflow-nodes/previous/{level}/dispatchable`

**Route Parameters**

| Parameter        | Type      | Description                                             |
| ---------------- | --------- | ------------------------------------------------------- |
| `taskAssignment` | `integer` | TaskAssignment ID used as workflow root.                |
| `level`          | `integer` | Previous workflow level whose dispatchable is returned. |

**Request Keys**

| Key       | Type     | Default | Description                                |
| --------- | -------- | ------- | ------------------------------------------ |
| `include` | `string` | -       | Optional result-control include parameter. |
| `appends` | `string` | -       | Optional result-control appends parameter. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/tasks-2/executions/558/workflow-nodes/previous/1/dispatchable', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'include' => 'taskTemplate:id,title|taskAssignment:id,title',
        'appends' => 'visibility_start_time'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 812,
  "task_assignment_id": 558,
  "task_template_id": 203,
  "task_status_id": "done",
  "visibility_start_time": "2026-04-29 08:45:00",
  "task_template": {
    "id": 203,
    "title": "Safety Checklist"
  },
  "task_assignment": {
    "id": 558,
    "title": "Morning Shift"
  }
}
```


# TaskProgressFields

## Introduction

`TaskProgressFields` store values submitted according to [TaskField](/api-reference/tasks-2/task-fields) definitions while a user progresses or finishes a [TaskExecution](/api-reference/tasks-2/task-executions).

The `additional_information` field uses the shared [Rich Text](/introduction/rich-text) HTML format. The `value` and `request_value` fields use the same format when the related TaskField type stores rich text.

## Model Definition

**Alias**

`taskProgressField`

**Relations**

| Key             | Relation                                                | Type       | Relation Field(s)   |
| --------------- | ------------------------------------------------------- | ---------- | ------------------- |
| `taskExecution` | [TaskExecution](/api-reference/tasks-2/task-executions) | Belongs to | `task_execution_id` |
| `formField`     | [TaskField](/api-reference/tasks-2/task-fields)         | Belongs to | `form_field_id`     |

**Computed Properties**

* `resolved_value` - The field-type-specific normalized `value`, for example a [FilemanagerFile](/api-reference/filemanager/filemanager-files) for an uploaded file.

**Capabilities**

* [Translations](/introduction/resource-capabilities/translations) - `value`, `request_value`, and `additional_information` are translatable.

## Admin: List by TaskExecution

List progress fields for one execution, including soft-deleted records.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/tasks-2/executions/{taskExecution}/progress-fields`

**Route Parameters**

| Parameter       | Type      | Description       |
| --------------- | --------- | ----------------- |
| `taskExecution` | `integer` | TaskExecution ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/tasks-2/executions/812/progress-fields', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1401,
    "task_execution_id": 812,
    "form_field_id": 33,
    "lang_id": "en-US",
    "value": "All exits are clear.",
    "request_value": "All exits are clear.",
    "additional_information": null,
    "deleted_at": null
  },
  {
    "id": 1402,
    "task_execution_id": 812,
    "form_field_id": 34,
    "lang_id": "en-US",
    "value": "1",
    "request_value": true,
    "additional_information": "<p>Checked on site.</p>",
    "deleted_at": null
  }
]
```


# Calendar

[Calendars](/api-reference/calendar/calendars) categorize [CalendarEvents](/api-reference/calendar/calendar-events) and provide their display colors.

[CalendarEvents](/api-reference/calendar/calendar-events) can be recurring, shared with Departments, resolved through URL Context, and notify eligible users. [DeletedRepeatingCalendarEvents](/api-reference/calendar/deleted-repeating-calendar-events) exclude individual dates from a recurring event without deleting the complete series.


# Calendars

## Introduction

`Calendars` categorize [CalendarEvents](/api-reference/calendar/calendar-events) and provide the color used to display their events.

## Model Definition

**Relations**

| Key      | Relation                                                  | Type     | Relation Field(s)             |
| -------- | --------------------------------------------------------- | -------- | ----------------------------- |
| `events` | [CalendarEvents](/api-reference/calendar/calendar-events) | Has many | `calendar_events.calendar_id` |

## List

List all calendars ordered by `sort_number` by default.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/calendars`

**Request Keys**

| Key               | Type      | Default       | Description                                                              |
| ----------------- | --------- | ------------- | ------------------------------------------------------------------------ |
| `selects`         | `string`  | All fields    | Comma-separated fields to return.                                        |
| `relations`       | `string`  | No relations  | Pipe-separated relations to include. An empty value suppresses defaults. |
| `limit`           | `integer` | No limit      | Maximum number of calendars. Minimum `1`.                                |
| `order_field`     | `string`  | `sort_number` | Field used for ordering.                                                 |
| `order_direction` | `string`  | `asc`         | `asc` or `desc`.                                                         |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/calendars', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'order_field' => 'sort_number',
        'order_direction' => 'asc',
        'limit' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "name": "Company Events",
    "color": "#2196F3",
    "sort_number": 1,
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 09:00:00"
  },
  {
    "id": 2,
    "name": "Absences",
    "color": "#8BC34A",
    "sort_number": 2,
    "created_at": "2026-08-06 09:05:00",
    "updated_at": "2026-08-06 09:05:00"
  }
]
```

## Show

Show one calendar.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/calendars/{idOrHash}`

**Route Parameters**

| Parameter  | Type      | Description  |
| ---------- | --------- | ------------ |
| `idOrHash` | `integer` | Calendar ID. |

**Request Keys**

| Key         | Type     | Default      | Description                          |
| ----------- | -------- | ------------ | ------------------------------------ |
| `selects`   | `string` | All fields   | Comma-separated fields to return.    |
| `relations` | `string` | No relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/calendars/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "name": "Company Events",
  "color": "#2196F3",
  "sort_number": 1,
  "created_at": "2026-08-06 09:00:00",
  "updated_at": "2026-08-06 09:00:00"
}
```

## Create

Create a calendar.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/calendars`

**Request Keys**

| Key           | Type      | Default          | Description                                         |
| ------------- | --------- | ---------------- | --------------------------------------------------- |
| `name`\*      | `string`  | -                | Calendar name.                                      |
| `color`\*     | `string`  | -                | Calendar color, normally a hexadecimal color value. |
| `sort_number` | `integer` | Next sort number | Calendar position.                                  |

Keys with `*` are required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/calendars', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'name' => 'Training Sessions',
        'color' => '#FF9800',
        'sort_number' => 3
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "name": "Training Sessions",
    "color": "#FF9800",
    "sort_number": 3,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:00:00"
  }
}
```

## Update

Update a calendar.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/calendars/{id}`

**Route Parameters**

| Parameter | Type      | Description  |
| --------- | --------- | ------------ |
| `id`      | `integer` | Calendar ID. |

**Request Keys**

| Key           | Type      | Description         |
| ------------- | --------- | ------------------- |
| `name`        | `string`  | New calendar name.  |
| `color`       | `string`  | New calendar color. |
| `sort_number` | `integer` | New position.       |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/calendars/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'name' => 'Mandatory Training',
        'color' => '#F57C00'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "name": "Mandatory Training",
    "color": "#F57C00",
    "sort_number": 3,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:15:00"
  }
}
```

## Delete

Delete a calendar permanently.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/calendars/{id}`

**Route Parameters**

| Parameter | Type      | Description  |
| --------- | --------- | ------------ |
| `id`      | `integer` | Calendar ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/calendars/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": []
}
```


# CalendarEvents

## Introduction

`CalendarEvents` are appointments owned by a [User](/api-reference/users), categorized by a [Calendar](/api-reference/calendar/calendars), and optionally shared with [Departments](/api-reference/departments).

Recurring events are expanded into occurrences by the list endpoint. Individual dates can be excluded through [DeletedRepeatingCalendarEvents](/api-reference/calendar/deleted-repeating-calendar-events).

The `description` field uses the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`calendarEvent`

**Relations**

| Key           | Relation                                      | Type            | Relation Field(s)           |
| ------------- | --------------------------------------------- | --------------- | --------------------------- |
| `user`        | [User](/api-reference/users)                  | Belongs to      | `user_id`                   |
| `calendar`    | [Calendar](/api-reference/calendar/calendars) | Belongs to      | `calendar_id`               |
| `departments` | [Departments](/api-reference/departments)     | Belongs to many | `department_calendar_event` |

**Computed Properties**

* `hash` - Hashed representation of the event `id` used in stable event URLs.
* `color` - Color of the related Calendar, or an empty string when no calendar is loaded.
* `url` and `static_url` - Link to the calendar event page.

**Capabilities**

* [URL Context](/introduction/resource-capabilities/url-context) - Event URLs resolve to access-checked context containing dates, location, a description teaser, and an optional image.
* [Notifications](/introduction/resource-capabilities/notifications) - Creation and relevant updates can notify eligible users in assigned departments according to calendar notification settings; deleting the event also removes its notifications.

## Interval Syntax

| Interval      | Variant          | Syntax              | Description                                                |
| ------------- | ---------------- | ------------------- | ---------------------------------------------------------- |
| Daily         | Every day        | `d:::1`             | Repeat every day.                                          |
| Weekly        | Every week       | `w:::1`             | Repeat every week.                                         |
| Weekly        | Weekdays         | `w:::1d::1,2,3,4,5` | Repeat on Monday through Friday every week.                |
| Monthly       | Day of month     | `m:::1d::15`        | Repeat on the 15th day every month.                        |
| Monthly       | Week and weekday | `m:::1w::1d::1`     | Repeat on the first Monday every month.                    |
| Yearly        | Day              | `y:::1d::1`         | Repeat yearly on the configured day.                       |
| Yearly        | Week and weekday | `y:::1w::1d::1`     | Repeat yearly by week and weekday.                         |
| End condition | Occurrence count | `r:::5`             | End after five occurrences. Only valid for `interval_end`. |
| End condition | Date             | `d:::2027-12-31`    | End on a date. Only valid for `interval_end`.              |

## List

List events visible to the authenticated user. Recurring events are expanded within the requested period.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/calendar-events`

**Request Keys**

| Key               | Type                    | Default                           | Description                                                                   |
| ----------------- | ----------------------- | --------------------------------- | ----------------------------------------------------------------------------- |
| `department_ids`  | `integer[]` \| `string` | All visible                       | Department IDs as an array or comma-separated string.                         |
| `calendar_ids`    | `integer[]` \| `string` | All calendars                     | Calendar IDs as an array or comma-separated string.                           |
| `period_start`    | `date`                  | No lower bound                    | Beginning of the occurrence period.                                           |
| `period_end`      | `date`                  | No upper bound                    | End of the occurrence period.                                                 |
| `search`          | `string`                | No search                         | Search `title` and `description`.                                             |
| `selects`         | `string`                | All fields                        | Comma-separated fields to return for persisted events.                        |
| `relations`       | `string`                | `user`, `calendar`, `departments` | Pipe-separated relations. An empty value suppresses defaults.                 |
| `limit`           | `integer`               | No limit                          | Maximum results after persisted and recurring events are merged. Minimum `1`. |
| `order_field`     | `string`                | `created_at`                      | Field used to order persisted events.                                         |
| `order_direction` | `string`                | `desc`                            | `asc` or `desc`.                                                              |

**Behavior**

* Results are limited by calendar permissions, event ownership, and shared departments.
* Persisted events contain `repeat_date: false`; generated recurring occurrences contain `repeat_date: true` and occurrence-specific `start` and `end` values.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/calendar-events', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'period_start' => '2026-08-10',
        'period_end' => '2026-08-14',
        'calendar_ids' => '1,2',
        'limit' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 41,
    "user_id": 8,
    "calendar_id": 1,
    "title": "Daily Production Briefing",
    "all_day": false,
    "start": "2026-08-10 09:00:00",
    "end": "2026-08-10 09:30:00",
    "interval": {
      "type": "daily",
      "each_days": 1
    },
    "interval_end": {
      "type": "repeats",
      "repeats": 5
    },
    "location": "Production Hall",
    "description": "<p>Daily handover.</p>",
    "repeat_date": true,
    "hash": "6o8m0kz5yw10xx1pr9e4vxj2",
    "color": "#2196F3"
  },
  {
    "id": 42,
    "user_id": 9,
    "calendar_id": 2,
    "title": "Company Holiday",
    "all_day": true,
    "start": "2026-08-12 00:00:00",
    "end": "2026-08-13 00:00:00",
    "interval": null,
    "interval_end": null,
    "location": null,
    "description": null,
    "repeat_date": false,
    "hash": "7p9n1layzx21yyrqs0f5wyk3",
    "color": "#8BC34A"
  }
]
```

## Show

Show one event by numeric ID or hash.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/calendar-events/{calendarEvent}`

**Route Parameters**

| Parameter       | Type                  | Description       |
| --------------- | --------------------- | ----------------- |
| `calendarEvent` | `integer` \| `string` | Event ID or hash. |

**Request Keys**

| Key         | Type     | Default                           | Description                       |
| ----------- | -------- | --------------------------------- | --------------------------------- |
| `selects`   | `string` | All fields                        | Comma-separated fields to return. |
| `relations` | `string` | `user`, `calendar`, `departments` | Pipe-separated relations.         |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/calendar-events/41', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 41,
  "user_id": 8,
  "calendar_id": 1,
  "title": "Daily Production Briefing",
  "all_day": false,
  "start": "2026-08-10 09:00:00",
  "end": "2026-08-10 09:30:00",
  "interval": {
    "type": "daily",
    "each_days": 1
  },
  "interval_end": {
    "type": "repeats",
    "repeats": 5
  },
  "location": "Production Hall",
  "description": "<p>Daily handover.</p>",
  "hash": "6o8m0kz5yw10xx1pr9e4vxj2",
  "color": "#2196F3"
}
```

## Parse ICS

Parse an uploaded iCalendar file into event request data without persisting events.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/calendar-events/ics`

**Request Keys**

| Key                     | Type   | Description              |
| ----------------------- | ------ | ------------------------ |
| `calendar_events_ics`\* | `file` | iCalendar file to parse. |

Keys with `*` are required.

**Behavior**

* UTC timestamps are converted to the application timezone and normalized to whole seconds.
* Text descriptions are converted into HTML paragraphs. Invalid recurrence rules produce `null` interval values instead of failing the complete import.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$ics = "BEGIN:VCALENDAR\r\n"
    . "VERSION:2.0\r\n"
    . "BEGIN:VEVENT\r\n"
    . "UID:safety-training@example.com\r\n"
    . "DTSTART:20260818T070000Z\r\n"
    . "DTEND:20260818T090000Z\r\n"
    . "SUMMARY:Safety Training\r\n"
    . "LOCATION:Training Room\r\n"
    . "END:VEVENT\r\n"
    . "END:VCALENDAR\r\n";

$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/calendar-events/ics', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'multipart' => [[
        'name' => 'calendar_events_ics',
        'contents' => $ics,
        'filename' => 'calendar-events.ics'
    ]]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "datas": [
      {
        "title": "Safety Training",
        "all_day": false,
        "start": "2026-08-18 09:00:00",
        "end": "2026-08-18 11:00:00",
        "interval": null,
        "interval_end": null,
        "location": "Training Room",
        "description": null
      }
    ]
  }
}
```

## Create

Create an event.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/calendar-events`

**Request Keys**

| Key              | Type                    | Default | Description                                                |
| ---------------- | ----------------------- | ------- | ---------------------------------------------------------- |
| `calendar_id`\*  | `integer`               | -       | Related [Calendar](/api-reference/calendar/calendars) ID.  |
| `title`\*        | `string`                | -       | Event title.                                               |
| `all_day`        | `boolean`               | `false` | Whether the event spans complete calendar days.            |
| `start`\*        | `datetime`              | -       | Start date and time.                                       |
| `end`\*          | `datetime`              | -       | End date and time.                                         |
| `interval`       | `string` \| `null`      | `null`  | Recurrence value from [Interval Syntax](#interval-syntax). |
| `interval_end`   | `string` \| `null`      | `null`  | Recurrence end condition.                                  |
| `location`       | `string` \| `null`      | `null`  | Event location.                                            |
| `description`    | `string` \| `null`      | `null`  | Event description, usually HTML.                           |
| `department_ids` | `integer[]` \| `string` | `[]`    | Visible departments as an array or comma-separated string. |

Keys with `*` are required.

**Behavior**

* `user_id` is always set to the authenticated user.
* Department IDs are attached after event creation. Depending on notification settings, eligible users in these departments can receive a creation notification.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/calendar-events', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'calendar_id' => 1,
        'title' => 'Weekly Safety Training',
        'all_day' => false,
        'start' => '2026-08-18 09:00:00',
        'end' => '2026-08-18 11:00:00',
        'interval' => 'w:::1d::2',
        'interval_end' => 'r:::5',
        'location' => 'Training Room',
        'description' => '<p>Bring your protective equipment.</p>',
        'department_ids' => [2, 3]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 43,
    "user_id": 8,
    "calendar_id": 1,
    "title": "Weekly Safety Training",
    "all_day": false,
    "start": "2026-08-18 09:00:00",
    "end": "2026-08-18 11:00:00",
    "interval": {
      "type": "weekly",
      "each_weeks": 1,
      "days_in_week": [2]
    },
    "interval_end": {
      "type": "repeats",
      "repeats": 5
    },
    "location": "Training Room",
    "description": "<p>Bring your protective equipment.</p>",
    "hash": "8q0o2mbzay32zzsrt1g6xzl4",
    "color": "#2196F3"
  }
}
```

## Update

Update an event.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/calendar-events/{calendarEvent}`

**Route Parameters**

| Parameter       | Type                  | Description       |
| --------------- | --------------------- | ----------------- |
| `calendarEvent` | `integer` \| `string` | Event ID or hash. |

**Request Keys**

| Key              | Type                    | Description                   |
| ---------------- | ----------------------- | ----------------------------- |
| `calendar_id`    | `integer`               | New Calendar ID.              |
| `title`          | `string`                | New event title.              |
| `all_day`        | `boolean`               | New all-day state.            |
| `start`          | `datetime`              | New start.                    |
| `end`            | `datetime`              | New end.                      |
| `interval`       | `string` \| `null`      | New recurrence value.         |
| `interval_end`   | `string` \| `null`      | New recurrence end condition. |
| `location`       | `string` \| `null`      | New location.                 |
| `description`    | `string` \| `null`      | New description.              |
| `department_ids` | `integer[]` \| `string` | Replacement department IDs.   |

**Behavior**

* `user_id` cannot be changed.
* Departments are synchronized only when `department_ids` is non-empty. Send all desired IDs when replacing the assignment.
* Changes to title, description, location, dates, or all-day state can trigger update notifications.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/calendar-events/43', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Weekly Safety Training - Hall B',
        'location' => 'Hall B',
        'department_ids' => [2, 4]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 43,
    "user_id": 8,
    "calendar_id": 1,
    "title": "Weekly Safety Training - Hall B",
    "all_day": false,
    "start": "2026-08-18 09:00:00",
    "end": "2026-08-18 11:00:00",
    "interval": {
      "type": "weekly",
      "each_weeks": 1,
      "days_in_week": [2]
    },
    "interval_end": {
      "type": "repeats",
      "repeats": 5
    },
    "location": "Hall B",
    "description": "<p>Bring your protective equipment.</p>",
    "hash": "8q0o2mbzay32zzsrt1g6xzl4",
    "color": "#2196F3"
  }
}
```

## Delete

Delete an event permanently and remove its related notifications.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/calendar-events/{calendarEvent}`

**Route Parameters**

| Parameter       | Type                  | Description       |
| --------------- | --------------------- | ----------------- |
| `calendarEvent` | `integer` \| `string` | Event ID or hash. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/calendar-events/43', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": []
}
```


# DeletedRepeatingCalendarEvents

## Introduction

`DeletedRepeatingCalendarEvents` exclude individual occurrence dates from a recurring [CalendarEvent](/api-reference/calendar/calendar-events) while retaining the event series.

## List

List excluded recurring-event dates.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/deleted-repeating-calendar-events`

**Request Keys**

| Key               | Type      | Default      | Description                             |
| ----------------- | --------- | ------------ | --------------------------------------- |
| `event_id`        | `integer` | All events   | Limit records to one CalendarEvent.     |
| `selects`         | `string`  | All fields   | Comma-separated fields to return.       |
| `limit`           | `integer` | No limit     | Maximum number of records. Minimum `1`. |
| `order_field`     | `string`  | `created_at` | Field used for ordering.                |
| `order_direction` | `string`  | `asc`        | `asc` or `desc`.                        |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/deleted-repeating-calendar-events', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'event_id' => 41,
        'order_field' => 'date',
        'order_direction' => 'asc',
        'limit' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 7,
    "user_id": 8,
    "event_id": 41,
    "date": "2026-08-11",
    "created_at": "2026-08-06 10:30:00"
  },
  {
    "id": 8,
    "user_id": 8,
    "event_id": 41,
    "date": "2026-08-13",
    "created_at": "2026-08-06 10:31:00"
  }
]
```

## Show

Show one excluded occurrence date.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/deleted-repeating-calendar-events/{id}`

**Route Parameters**

| Parameter | Type      | Description                       |
| --------- | --------- | --------------------------------- |
| `id`      | `integer` | DeletedRepeatingCalendarEvent ID. |

**Request Keys**

| Key       | Type     | Default    | Description                       |
| --------- | -------- | ---------- | --------------------------------- |
| `selects` | `string` | All fields | Comma-separated fields to return. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/deleted-repeating-calendar-events/7', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 7,
  "user_id": 8,
  "event_id": 41,
  "date": "2026-08-11",
  "created_at": "2026-08-06 10:30:00"
}
```

## Create

Exclude one date from a recurring event.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/deleted-repeating-calendar-events`

**Request Keys**

| Key          | Type      | Description                         |
| ------------ | --------- | ----------------------------------- |
| `event_id`\* | `integer` | Related recurring CalendarEvent ID. |
| `date`\*     | `date`    | Occurrence date to exclude.         |

Keys with `*` are required.

**Behavior**

* `user_id` is set to the authenticated user and cannot be selected by the client.
* The Calendar list resolver omits matching generated occurrences from the event series.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/deleted-repeating-calendar-events', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'event_id' => 41,
        'date' => '2026-08-11'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 7,
    "user_id": 8,
    "event_id": 41,
    "date": "2026-08-11",
    "created_at": "2026-08-06 10:30:00"
  }
}
```

## Delete

Restore an excluded occurrence by deleting its exclusion record.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/deleted-repeating-calendar-events/{id}`

**Route Parameters**

| Parameter | Type      | Description                       |
| --------- | --------- | --------------------------------- |
| `id`      | `integer` | DeletedRepeatingCalendarEvent ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/deleted-repeating-calendar-events/7', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": []
}
```


# Filemanager

[FilemanagerDirectories](/api-reference/filemanager/filemanager-directories) define the visible directory hierarchy and its user or department assignments. Their paths control access to nested directories and [FilemanagerFiles](/api-reference/filemanager/filemanager-files).

[FilemanagerFiles](/api-reference/filemanager/filemanager-files) store file metadata and expose access-checked URLs for displaying or downloading their content.

## Path Filters

Filemanager list and count endpoints extend [Result Control](/introduction/query-manipulation/result-control) with path-aware filters:

* `filter[path][matches_path]` - Direct children of a directory path.
* `filter[path][matches_path_recursive]` - A directory path and its complete subtree.
* `filter[path][ignore_paths]` - Exclude one or more directory subtrees.


# FilemanagerDirectories

## Introduction

`FilemanagerDirectories` form a persisted directory hierarchy. Each directory stores its `parent_id`, `name`, and normalized absolute `path`; access assignments on parent directories affect visibility throughout the path.

## Model Definition

**Alias**

`filemanagerDirectory`

**Relations**

| Key                | Relation                                                                     | Type            | Relation Field(s)                  |
| ------------------ | ---------------------------------------------------------------------------- | --------------- | ---------------------------------- |
| `user`             | [User](/api-reference/users)                                                 | Belongs to      | `user_id`                          |
| `parent`           | [FilemanagerDirectory](/api-reference/filemanager/filemanager-directories)   | Belongs to      | `parent_id`                        |
| `children`         | [FilemanagerDirectories](/api-reference/filemanager/filemanager-directories) | Has many        | `parent_id`                        |
| `filemanagerFiles` | [FilemanagerFiles](/api-reference/filemanager/filemanager-files)             | Has many        | `filemanager_directory_id`         |
| `users`            | [Users](/api-reference/users)                                                | Belongs to many | `user_filemanager_directory`       |
| `departments`      | [Departments](/api-reference/departments)                                    | Belongs to many | `department_filemanager_directory` |

**Computed Properties**

* `hash` - Hashed representation of the directory `id`.
* `url` and `static_url` - Link to the directory page.

**Capabilities**

* [Targetables](/introduction/resource-capabilities/targetables) - `user_ids` or `department_ids` restrict directory access; an empty assignment is public to users with Filemanager access, while visibility through a nested path also depends on its ancestors.
* [URL Context](/introduction/resource-capabilities/url-context) - Directory URLs resolve to access-checked context containing the directory name, parent path, and last update time.

## List

List all directories visible to the authenticated user.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/filemanager/directories`

**Request Keys**

| Key       | Type     | Default     | Description                                                                                                        |
| --------- | -------- | ----------- | ------------------------------------------------------------------------------------------------------------------ |
| `filter`  | `object` | `{}`        | [Result Control](/introduction/query-manipulation/result-control) filters, including the Filemanager path filters. |
| `sort`    | `string` | API default | Result Control sort expression.                                                                                    |
| `include` | `string` | API default | Relations to include.                                                                                              |
| `appends` | `string` | API default | Computed properties to append.                                                                                     |

**Behavior**

* Recursive path filtering can be more expensive than the direct [List by Path](#list-by-path) endpoint.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/filemanager/directories', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'filter' => ['path' => ['matches_path' => '/Projects/']],
        'sort' => 'name'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 11,
    "user_id": 3,
    "parent_id": 10,
    "path": "/Projects/Reports/",
    "name": "Reports",
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 09:00:00",
    "deleted_at": null,
    "hash": "zn7m24owk63qolxryge8pj05"
  },
  {
    "id": 12,
    "user_id": 4,
    "parent_id": 10,
    "path": "/Projects/Media/",
    "name": "Media",
    "created_at": "2026-08-06 09:05:00",
    "updated_at": "2026-08-06 09:05:00",
    "deleted_at": null,
    "hash": "6o8m0kz5yw10x1pr9e4vxj27"
  }
]
```

## List by Path

List direct child directories of a normalized path. Omitting `path` lists the root.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/filemanager/directories/path/{path?}`

**Route Parameters**

| Parameter | Type     | Description                                                                             |
| --------- | -------- | --------------------------------------------------------------------------------------- |
| `path`    | `string` | Optional. Directory path without a required leading or trailing slash. Defaults to `/`. |

**Request Keys**

| Key      | Type     | Default     | Description                                                     |
| -------- | -------- | ----------- | --------------------------------------------------------------- |
| `filter` | `object` | `{}`        | Optional Result Control filters applied to the direct children. |
| `sort`   | `string` | API default | Result Control sort expression.                                 |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/filemanager/directories/path/Projects', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => ['sort' => 'name']
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 12,
    "user_id": 4,
    "parent_id": 10,
    "path": "/Projects/Media/",
    "name": "Media",
    "hash": "6o8m0kz5yw10x1pr9e4vxj27"
  },
  {
    "id": 11,
    "user_id": 3,
    "parent_id": 10,
    "path": "/Projects/Reports/",
    "name": "Reports",
    "hash": "zn7m24owk63qolxryge8pj05"
  }
]
```

## Count

Count directories visible to the authenticated user after Result Control filters are applied.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/filemanager/directories/count`

**Request Keys**

| Key      | Type     | Default | Description                                     |
| -------- | -------- | ------- | ----------------------------------------------- |
| `filter` | `object` | `{}`    | Result Control filters applied before counting. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/filemanager/directories/count', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => ['filter' => ['path' => ['matches_path_recursive' => '/Projects/']]]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
3
```

## List by Directory

List direct child directories of one visible directory.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/filemanager/directories/{filemanagerDirectory}/directories`

**Route Parameters**

| Parameter              | Type                  | Description                  |
| ---------------------- | --------------------- | ---------------------------- |
| `filemanagerDirectory` | `integer` \| `string` | Parent directory ID or hash. |

**Request Keys**

| Key      | Type     | Default     | Description                                                     |
| -------- | -------- | ----------- | --------------------------------------------------------------- |
| `filter` | `object` | `{}`        | Optional Result Control filters applied to the direct children. |
| `sort`   | `string` | API default | Result Control sort expression.                                 |

**Behavior**

* The parent directory must be visible to the authenticated user. The response contains only its direct visible children.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/filemanager/directories/10/directories', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => ['sort' => 'name']
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 12,
    "user_id": 4,
    "parent_id": 10,
    "path": "/Projects/Media/",
    "name": "Media",
    "created_at": "2026-08-06 09:10:00",
    "updated_at": "2026-08-06 09:10:00",
    "deleted_at": null,
    "hash": "6o8m0kz5yw10x1pr9e4vxj27"
  },
  {
    "id": 11,
    "user_id": 3,
    "parent_id": 10,
    "path": "/Projects/Reports/",
    "name": "Reports",
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 09:00:00",
    "deleted_at": null,
    "hash": "zn7m24owk63qolxryge8pj05"
  }
]
```

## Show

Show one visible directory.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/filemanager/directories/{filemanagerDirectory}`

**Route Parameters**

| Parameter              | Type                  | Description           |
| ---------------------- | --------------------- | --------------------- |
| `filemanagerDirectory` | `integer` \| `string` | Directory ID or hash. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/filemanager/directories/11', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 11,
  "user_id": 3,
  "parent_id": 10,
  "path": "/Projects/Reports/",
  "name": "Reports",
  "created_at": "2026-08-06 09:00:00",
  "updated_at": "2026-08-06 09:00:00",
  "deleted_at": null,
  "hash": "zn7m24owk63qolxryge8pj05"
}
```

## Show Information

Return the number and total byte size of all visible files in a directory and its descendants.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/filemanager/directories/{filemanagerDirectory}/information`

**Route Parameters**

| Parameter              | Type                  | Description           |
| ---------------------- | --------------------- | --------------------- |
| `filemanagerDirectory` | `integer` \| `string` | Directory ID or hash. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/filemanager/directories/10/information', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "files": 8,
  "size": 2748430
}
```

## Create

Create a physical directory and its metadata record.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/filemanager/directories`

**Request Keys**

| Key              | Type                    | Default             | Description                                                                                             |
| ---------------- | ----------------------- | ------------------- | ------------------------------------------------------------------------------------------------------- |
| `path`\*         | `string`                | -                   | Directory path. The backend normalizes leading and trailing slashes and derives `parent_id` and `name`. |
| `parent_id`      | `integer` \| `null`     | Derived from `path` | Parent directory. When supplied, `name` is used to build `path`.                                        |
| `name`           | `string`                | Derived from `path` | Directory name when `parent_id` is supplied.                                                            |
| `user_ids`       | `integer[]` \| `string` | `[]`                | Users allowed to access the directory.                                                                  |
| `department_ids` | `integer[]` \| `string` | `[]`                | Departments allowed to access the directory.                                                            |

Keys with `*` are required unless `parent_id` and `name` provide the hierarchy.

**Behavior**

* `user_id` is set to the authenticated user.
* When `parent_id` is present, `parent_id` and `name` determine the hierarchy and take precedence over a conflicting `path`. A `null` parent creates a root directory.
* When both assignment arrays are non-empty, user assignments take precedence and department assignments are cleared. When both are empty, the directory is public to users with Filemanager access.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/filemanager/directories', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'parent_id' => 10,
        'name' => 'Archive',
        'user_ids' => [3, 8]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 13,
    "user_id": 3,
    "parent_id": 10,
    "path": "/Projects/Archive/",
    "name": "Archive",
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:00:00",
    "deleted_at": null,
    "hash": "8q0o2mbzay32zzsrt1g6xzl4"
  }
}
```

## Update

Rename or move a directory and optionally replace its assignments.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/filemanager/directories/{filemanagerDirectory}`

**Route Parameters**

| Parameter              | Type                  | Description           |
| ---------------------- | --------------------- | --------------------- |
| `filemanagerDirectory` | `integer` \| `string` | Directory ID or hash. |

**Request Keys**

| Key              | Type                              | Description                         |
| ---------------- | --------------------------------- | ----------------------------------- |
| `path`           | `string`                          | New normalized directory path.      |
| `parent_id`      | `integer` \| `null`               | New parent directory.               |
| `name`           | `string`                          | New directory name.                 |
| `user_ids`       | `integer[]` \| `string` \| `null` | Replacement user assignments.       |
| `department_ids` | `integer[]` \| `string` \| `null` | Replacement department assignments. |

**Behavior**

* `user_id` remains the authenticated user. Reserved directory paths cannot be moved or renamed.
* When `parent_id` or `name` is present, those hierarchy fields take precedence over a conflicting `path`; omitted hierarchy fields retain their current values. A `null` parent moves the directory to the root.
* A path change moves the directory, its descendants, and stored files. Assignment relations are synchronized only for keys present in the request.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/filemanager/directories/13', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'name' => 'Archived Reports',
        'department_ids' => [2, 4]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 13,
    "user_id": 3,
    "parent_id": 10,
    "path": "/Projects/Archived Reports/",
    "name": "Archived Reports",
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:15:00",
    "deleted_at": null,
    "hash": "8q0o2mbzay32zzsrt1g6xzl4"
  }
}
```

## Delete

Delete a directory, its storage content, and its descendant directory and file records.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/filemanager/directories/{filemanagerDirectory}`

**Route Parameters**

| Parameter              | Type                  | Description           |
| ---------------------- | --------------------- | --------------------- |
| `filemanagerDirectory` | `integer` \| `string` | Directory ID or hash. |

**Behavior**

* The root directory cannot be deleted.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/filemanager/directories/13', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# FilemanagerFiles

## Introduction

`FilemanagerFiles` store metadata for files in Filemanager storage. Each file stores its nullable `filemanager_directory_id`, `name`, and normalized `path`; nested files belong to their direct [FilemanagerDirectory](/api-reference/filemanager/filemanager-directories), while root files use a `null` directory ID. Missing directory records are created when necessary during upload or movement.

## Model Definition

**Alias**

`filemanagerFile`

**Relations**

| Key                    | Relation                                                                   | Type       | Relation Field(s)          |
| ---------------------- | -------------------------------------------------------------------------- | ---------- | -------------------------- |
| `user`                 | [User](/api-reference/users)                                               | Belongs to | `user_id`                  |
| `filemanagerDirectory` | [FilemanagerDirectory](/api-reference/filemanager/filemanager-directories) | Belongs to | `filemanager_directory_id` |

**Computed Properties**

* `hash` - Hashed representation of the file `id`.
* `url` and `static_url` - Link to the Filemanager metadata page.
* `show_file_content_url` - Access-checked URL that displays the stored content.
* `download_file_content_url` - Content URL with download disposition.

**Capabilities**

* [URL Context](/introduction/resource-capabilities/url-context) - Metadata and content URLs resolve to access-checked context containing filename, MIME type, extension, byte size, location, icon, and image preview information where available.

## List

List files visible to the authenticated user.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/filemanager/files`

**Request Keys**

| Key       | Type     | Default     | Description                                                                                                    |
| --------- | -------- | ----------- | -------------------------------------------------------------------------------------------------------------- |
| `filter`  | `object` | `{}`        | [Result Control](/introduction/query-manipulation/result-control) filters, including Filemanager path filters. |
| `sort`    | `string` | API default | Result Control sort expression.                                                                                |
| `appends` | `string` | API default | Computed properties to append.                                                                                 |

**Behavior**

* File visibility is derived from the accessible directory path. Expensive relations are not included by list endpoints even when requested.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/filemanager/files', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'filter' => ['path' => ['matches_path' => '/Projects/Reports/']],
        'sort' => 'name'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 31,
    "user_id": 3,
    "filemanager_directory_id": 11,
    "path": "/Projects/Reports/quarterly-report.pdf",
    "name": "quarterly-report.pdf",
    "extension": "pdf",
    "mime_type": "application/pdf",
    "size": 87236,
    "created_at": "2026-08-06 09:20:00",
    "updated_at": "2026-08-06 09:20:00",
    "deleted_at": null,
    "hash": "zn7m24owk63qolxryge8pj05"
  },
  {
    "id": 32,
    "user_id": 4,
    "filemanager_directory_id": 11,
    "path": "/Projects/Reports/site-photo.jpg",
    "name": "site-photo.jpg",
    "extension": "jpg",
    "mime_type": "image/jpeg",
    "size": 187607,
    "created_at": "2026-08-06 09:25:00",
    "updated_at": "2026-08-06 09:25:00",
    "deleted_at": null,
    "hash": "6o8m0kz5yw10x1pr9e4vxj27"
  }
]
```

## List by Path

List direct child files of a normalized directory path. Omitting `path` lists root files.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/filemanager/files/path/{path?}`

**Route Parameters**

| Parameter | Type     | Description                                |
| --------- | -------- | ------------------------------------------ |
| `path`    | `string` | Optional. Directory path. Defaults to `/`. |

**Request Keys**

| Key      | Type     | Default     | Description                      |
| -------- | -------- | ----------- | -------------------------------- |
| `filter` | `object` | `{}`        | Optional Result Control filters. |
| `sort`   | `string` | API default | Result Control sort expression.  |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/filemanager/files/path/Projects%2FReports', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => ['sort' => 'name']
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 31,
    "filemanager_directory_id": 11,
    "path": "/Projects/Reports/quarterly-report.pdf",
    "name": "quarterly-report.pdf",
    "extension": "pdf",
    "mime_type": "application/pdf",
    "size": 87236,
    "hash": "zn7m24owk63qolxryge8pj05"
  },
  {
    "id": 32,
    "filemanager_directory_id": 11,
    "path": "/Projects/Reports/site-photo.jpg",
    "name": "site-photo.jpg",
    "extension": "jpg",
    "mime_type": "image/jpeg",
    "size": 187607,
    "hash": "6o8m0kz5yw10x1pr9e4vxj27"
  }
]
```

## List by Directory

List direct child files of one visible directory.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/filemanager/directories/{filemanagerDirectory}/files`

**Route Parameters**

| Parameter              | Type                  | Description           |
| ---------------------- | --------------------- | --------------------- |
| `filemanagerDirectory` | `integer` \| `string` | Directory ID or hash. |

**Request Keys**

| Key      | Type     | Default     | Description                                                     |
| -------- | -------- | ----------- | --------------------------------------------------------------- |
| `filter` | `object` | `{}`        | Optional Result Control filters applied to the direct children. |
| `sort`   | `string` | API default | Result Control sort expression.                                 |

**Behavior**

* The directory must be visible to the authenticated user. The response contains only its direct visible files.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/filemanager/directories/11/files', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => ['sort' => 'name']
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 31,
    "user_id": 3,
    "filemanager_directory_id": 11,
    "path": "/Projects/Reports/quarterly-report.pdf",
    "name": "quarterly-report.pdf",
    "extension": "pdf",
    "mime_type": "application/pdf",
    "size": 87236,
    "created_at": "2026-08-06 09:20:00",
    "updated_at": "2026-08-06 09:20:00",
    "deleted_at": null,
    "hash": "zn7m24owk63qolxryge8pj05"
  },
  {
    "id": 32,
    "user_id": 4,
    "filemanager_directory_id": 11,
    "path": "/Projects/Reports/site-photo.jpg",
    "name": "site-photo.jpg",
    "extension": "jpg",
    "mime_type": "image/jpeg",
    "size": 187607,
    "created_at": "2026-08-06 09:25:00",
    "updated_at": "2026-08-06 09:25:00",
    "deleted_at": null,
    "hash": "6o8m0kz5yw10x1pr9e4vxj27"
  }
]
```

## Count

Count visible files after Result Control filters are applied.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/filemanager/files/count`

**Request Keys**

| Key      | Type     | Default | Description                                     |
| -------- | -------- | ------- | ----------------------------------------------- |
| `filter` | `object` | `{}`    | Result Control filters applied before counting. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/filemanager/files/count', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => ['filter' => ['path' => ['matches_path_recursive' => '/Projects/']]]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
8
```

## Show

Show one visible file.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/filemanager/files/{filemanagerFile}`

**Route Parameters**

| Parameter         | Type                  | Description      |
| ----------------- | --------------------- | ---------------- |
| `filemanagerFile` | `integer` \| `string` | File ID or hash. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/filemanager/files/31', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 31,
  "user_id": 3,
  "filemanager_directory_id": 11,
  "path": "/Projects/Reports/quarterly-report.pdf",
  "name": "quarterly-report.pdf",
  "extension": "pdf",
  "mime_type": "application/pdf",
  "size": 87236,
  "created_at": "2026-08-06 09:20:00",
  "updated_at": "2026-08-06 09:20:00",
  "deleted_at": null,
  "hash": "zn7m24owk63qolxryge8pj05"
}
```

## Show EXIF Data

Return normalized metadata for a JPEG, PNG, or TIFF file.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/filemanager/files/{filemanagerFile}/exif`

**Route Parameters**

| Parameter         | Type                  | Description            |
| ----------------- | --------------------- | ---------------------- |
| `filemanagerFile` | `integer` \| `string` | Image file ID or hash. |

**Behavior**

* Metadata is read from a bounded file prefix. Unsupported formats return an error; supported files without readable metadata return an empty object.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/filemanager/files/32/exif', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "DateTimeOriginal": "2026:08:05 14:32:10",
  "ImageHeight": 3024,
  "ImageWidth": 4032,
  "Make": "Example Camera",
  "Model": "XC-10",
  "Orientation": 1
}
```

## Create

Upload a file and create its metadata record.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/filemanager/files`

**Request Keys**

| Key                        | Type                | Default             | Description                                                                     |
| -------------------------- | ------------------- | ------------------- | ------------------------------------------------------------------------------- |
| `file`\*                   | `file`              | -                   | Uploaded file. Filename and content type determine `extension` and `mime_type`. |
| `path`\*                   | `string`            | -                   | Complete target path. Normalized with a leading slash.                          |
| `filemanager_directory_id` | `integer` \| `null` | Derived from `path` | Direct target directory. When supplied, `name` builds the path.                 |
| `name`                     | `string`            | Derived from `path` | Target filename when a directory ID is supplied.                                |

Keys with `*` are required unless `filemanager_directory_id` and `name` provide the hierarchy.

**Behavior**

* `user_id` is set to the authenticated user. Missing directory records in the supplied path are created before the file relation is resolved.
* When `filemanager_directory_id` is present, it and `name` determine the hierarchy and take precedence over a conflicting `path`. A `null` directory ID creates a root file.
* The upload fails when the tenant's configured available storage is insufficient.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$pdf = "%PDF-1.4\n1 0 obj<</Type/Catalog>>endobj\n%%EOF";

$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/filemanager/files', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'multipart' => [
        [
            'name' => 'file',
            'contents' => $pdf,
            'filename' => 'safety-guidelines.pdf',
            'headers' => ['Content-Type' => 'application/pdf']
        ],
        ['name' => 'filemanager_directory_id', 'contents' => '11'],
        ['name' => 'name', 'contents' => 'safety-guidelines.pdf']
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 33,
    "user_id": 3,
    "filemanager_directory_id": 11,
    "path": "/Projects/Reports/safety-guidelines.pdf",
    "name": "safety-guidelines.pdf",
    "extension": "pdf",
    "mime_type": "application/pdf",
    "size": 45,
    "created_at": "2026-08-06 10:30:00",
    "updated_at": "2026-08-06 10:30:00",
    "deleted_at": null,
    "hash": "8q0o2mbzay32zzsrt1g6xzl4"
  }
}
```

## Update

Replace file content, move the file, or rename it.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/filemanager/files/{filemanagerFile}`

**Route Parameters**

| Parameter         | Type                  | Description      |
| ----------------- | --------------------- | ---------------- |
| `filemanagerFile` | `integer` \| `string` | File ID or hash. |

**Request Keys**

| Key                        | Type                | Description           |
| -------------------------- | ------------------- | --------------------- |
| `file`                     | `file`              | Replacement content.  |
| `path`                     | `string`            | Complete target path. |
| `filemanager_directory_id` | `integer` \| `null` | New direct directory. |
| `name`                     | `string`            | New filename.         |

**Behavior**

* `user_id` remains the authenticated user. The backend rebuilds `path`, `name`, and `filemanager_directory_id` as a consistent hierarchy.
* When `filemanager_directory_id` or `name` is present, those hierarchy fields take precedence over a conflicting `path`; omitted hierarchy fields retain their current values. A `null` directory ID moves the file to the root.
* Replacing content updates the extension, MIME type, and byte size. Moving or replacing a file regenerates its thumbnail or media poster where supported.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/filemanager/files/33', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'filemanager_directory_id' => 12,
        'name' => 'safety-guidelines-2026.pdf'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 33,
    "user_id": 3,
    "filemanager_directory_id": 12,
    "path": "/Projects/Media/safety-guidelines-2026.pdf",
    "name": "safety-guidelines-2026.pdf",
    "extension": "pdf",
    "mime_type": "application/pdf",
    "size": 45,
    "created_at": "2026-08-06 10:30:00",
    "updated_at": "2026-08-06 10:40:00",
    "deleted_at": null,
    "hash": "8q0o2mbzay32zzsrt1g6xzl4"
  }
}
```

## Delete

Delete the metadata record, stored content, and generated thumbnail.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/filemanager/files/{filemanagerFile}`

**Route Parameters**

| Parameter         | Type                  | Description      |
| ----------------- | --------------------- | ---------------- |
| `filemanagerFile` | `integer` \| `string` | File ID or hash. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/filemanager/files/33', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# Forms

[Forms](/api-reference/forms/forms) define configurable questionnaires and contain translated [FormFields](/api-reference/forms/form-fields). Field behavior comes from [FormFieldTypes](/api-reference/forms/form-field-types), while [DefaultValueSources](/api-reference/forms/default-value-sources), [FormFieldValidations](/api-reference/forms/form-field-validations), [FormFieldValidationTypes](/api-reference/forms/form-field-validation-types), and [FormFieldDisplayConditions](/api-reference/forms/form-field-display-conditions) control prefilling, validation, and conditional visibility.

Submitted forms create [FormMessages](/api-reference/forms/form-messages) and corresponding [FormMessageFields](/api-reference/forms/form-message-fields). When receipt or approval is required, [FormMessageConfirmations](/api-reference/forms/form-message-confirmations) expose the token-based confirmation workflow.

Forms use [Entity Permissions](/introduction/resource-capabilities/entity-permissions) for access, [Translations](/introduction/resource-capabilities/translations) for localized content, and [URL Context](/introduction/resource-capabilities/url-context) for stable links.


# Forms

## Introduction

`Forms` define configurable questionnaires whose submissions create [FormMessages](/api-reference/forms/form-messages).

The `email_template` field uses the shared [Rich Text](/introduction/rich-text) HTML format and the email editor's supported subset. Both `subject` and `email_template` accept validated [System Variable templates](/introduction/system-variables#rendered-templates).

## Model Definition

**Alias**

`form`

**Capabilities**

* [Entity Permissions](/introduction/resource-capabilities/entity-permissions) - Restrict who can view and submit a form.
* [Translations](/introduction/resource-capabilities/translations) - Localizes `name`, `subject`, and `email_template`; examples use `en-US`.
* [URL Context](/introduction/resource-capabilities/url-context) - Returns stable form URLs through URL-context requests.

**Relations**

| Key                 | Relation                                               | Type       | Relation Field(s)                                  |
| ------------------- | ------------------------------------------------------ | ---------- | -------------------------------------------------- |
| `formFields`        | [FormFields](/api-reference/forms/form-fields)         | Has many   | `form_id`, `form_type`                             |
| `user`              | [User](/api-reference/users)                           | Belongs to | `user_id`                                          |
| `icon`              | [Icon](/api-reference/icons)                           | Belongs to | `icon_id`                                          |
| `folder`            | [Folder](/api-reference/folders)                       | Belongs to | `folder_id`                                        |
| `layouts`           | [Layouts](/api-reference/layouts/layouts)              | Morph many | `layouts.layoutable_type`, `layouts.layoutable_id` |
| `entityPermissions` | [EntityPermissions](/api-reference/entity-permissions) | Morph many | Target entity type and ID                          |

## List

List visible `Forms`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms`

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of forms.             |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 21,
    "user_id": 7,
    "lang_id": "en-US",
    "icon_id": 55,
    "folder_id": 9,
    "name": "Employee Feedback",
    "slug": "employee-feedback",
    "subject": "New employee feedback",
    "recipient": [
      "people@example.com"
    ],
    "always_send_to_recipient": false,
    "has_personal_info": false,
    "user_confirmation_required": false,
    "request_receipt": false,
    "request_confirmation": false,
    "active": true,
    "sort_number": 1,
    "grade_mode": null,
    "grade_scale": null,
    "send_constraint_type": null,
    "send_empty_fields": false,
    "send_description_fields": true,
    "message_delete_interval": null,
    "message_hide_interval": null,
    "message_receipt_lifetime": null,
    "message_confirmation_lifetime": null,
    "hash": "jR"
  },
  {
    "id": 22,
    "user_id": 8,
    "lang_id": "en-US",
    "icon_id": 61,
    "folder_id": 12,
    "name": "Equipment Request",
    "slug": "equipment-request",
    "subject": "New equipment request",
    "recipient": [
      "it@example.com",
      "purchasing@example.com"
    ],
    "always_send_to_recipient": true,
    "has_personal_info": true,
    "user_confirmation_required": true,
    "request_receipt": true,
    "request_confirmation": true,
    "active": true,
    "sort_number": 2,
    "grade_mode": "scaled",
    "grade_scale": 100,
    "send_constraint_type": "once",
    "send_empty_fields": true,
    "send_description_fields": false,
    "message_delete_interval": "P2Y",
    "message_hide_interval": "P6M",
    "message_receipt_lifetime": "P5D",
    "message_confirmation_lifetime": "P1D",
    "hash": "kS"
  }
]
```

## Show

Show one visible `Form`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/{form}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `form`    | `integer` | Form ID.    |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/21', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 21,
  "user_id": 7,
  "lang_id": "en-US",
  "icon_id": 55,
  "folder_id": 9,
  "name": "Employee Feedback",
  "slug": "employee-feedback",
  "subject": "New employee feedback",
  "recipient": [
    "people@example.com"
  ],
  "always_send_to_recipient": false,
  "has_personal_info": false,
  "user_confirmation_required": false,
  "request_receipt": false,
  "request_confirmation": false,
  "active": true,
  "sort_number": 1,
  "grade_mode": null,
  "grade_scale": null,
  "send_constraint_type": null,
  "send_empty_fields": false,
  "send_description_fields": true,
  "message_delete_interval": null,
  "message_hide_interval": null,
  "message_receipt_lifetime": null,
  "message_confirmation_lifetime": null,
  "hash": "jR"
}
```

## Sendability

Check whether the current user can submit one `Form`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/{form}/sendable`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `form`    | `integer` | Form ID.    |

**Behavior**

This check does not consume temporary authentication. If the form has a send constraint, the response also contains constraint-specific data.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/21/sendable', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "sendable": true,
  "data": [],
  "error_message": null
}
```

## Send

Submit one `Form`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/forms/{form}/send`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `form`    | `integer` | Form ID.    |

**Request Keys**

| Key               | Type     | Default         | Description                                    |
| ----------------- | -------- | --------------- | ---------------------------------------------- |
| `lang_id`         | `string` | System language | Language key. Use `en-US` for this example.    |
| `full-name`\*     | `string` | -               | Value for the required `full-name` text field. |
| `email-address`\* | `string` | -               | Value for the required email field.            |
| `message`\*       | `string` | -               | Value for the required message field.          |

Keys with `*` are required by the configured fields in this example form.

**Behavior**

Request keys for field values are the fields' `key` values. The example supplies every value used to generate the returned message body.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/forms/21/send', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'full-name' => 'Ada Lovelace',
        'email-address' => 'ada@example.com',
        'message' => 'I would like more mentoring sessions.'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 140,
    "user_id": 7,
    "group_account_user_id": null,
    "department_id": 6,
    "form_id": 21,
    "lang_id": "en-US",
    "subject": "New employee feedback",
    "body": "<p><strong>Full name:</strong> Ada Lovelace</p><p><strong>Email address:</strong> ada@example.com</p><p><strong>Message:</strong> I would like more mentoring sessions.</p>",
    "sent_from_public": false,
    "distribution_success": true,
    "hash": "mN"
  }
}
```

## Admin: List

List all `Forms` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/forms`

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of forms.             |

**Behavior**

Administration results include inactive and soft-deleted forms when permitted.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/forms', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 21,
    "user_id": 7,
    "lang_id": "en-US",
    "icon_id": 55,
    "folder_id": 9,
    "name": "Employee Feedback",
    "slug": "employee-feedback",
    "subject": "New employee feedback",
    "recipient": [
      "people@example.com"
    ],
    "always_send_to_recipient": false,
    "has_personal_info": false,
    "user_confirmation_required": false,
    "request_receipt": false,
    "request_confirmation": false,
    "active": true,
    "sort_number": 1,
    "grade_mode": null,
    "grade_scale": null,
    "send_constraint_type": null,
    "send_empty_fields": false,
    "send_description_fields": true,
    "message_delete_interval": null,
    "message_hide_interval": null,
    "message_receipt_lifetime": null,
    "message_confirmation_lifetime": null,
    "hash": "jR"
  },
  {
    "id": 22,
    "user_id": 8,
    "lang_id": "en-US",
    "icon_id": 61,
    "folder_id": 12,
    "name": "Equipment Request",
    "slug": "equipment-request",
    "subject": "New equipment request",
    "recipient": [
      "it@example.com",
      "purchasing@example.com"
    ],
    "always_send_to_recipient": true,
    "has_personal_info": true,
    "user_confirmation_required": true,
    "request_receipt": true,
    "request_confirmation": true,
    "active": false,
    "sort_number": 2,
    "grade_mode": "scaled",
    "grade_scale": 100,
    "send_constraint_type": "once",
    "send_empty_fields": true,
    "send_description_fields": false,
    "message_delete_interval": "P2Y",
    "message_hide_interval": "P6M",
    "message_receipt_lifetime": "P5D",
    "message_confirmation_lifetime": "P1D",
    "hash": "kS",
    "deleted_at": "2026-08-01 08:00:00"
  }
]
```

## Admin: Show

Show one `Form` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/forms/{form}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `form`    | `integer` | Form ID.    |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/forms/22', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 22,
  "user_id": 8,
  "lang_id": "en-US",
  "icon_id": 61,
  "folder_id": 12,
  "name": "Equipment Request",
  "slug": "equipment-request",
  "subject": "New equipment request",
  "recipient": [
    "it@example.com",
    "purchasing@example.com"
  ],
  "always_send_to_recipient": true,
  "has_personal_info": true,
  "user_confirmation_required": true,
  "request_receipt": true,
  "request_confirmation": true,
  "active": true,
  "sort_number": 2,
  "grade_mode": "scaled",
  "grade_scale": 100,
  "send_constraint_type": "once",
  "send_empty_fields": true,
  "send_description_fields": false,
  "message_delete_interval": "P2Y",
  "message_hide_interval": "P6M",
  "message_receipt_lifetime": "P5D",
  "message_confirmation_lifetime": "P1D",
  "hash": "kS"
}
```

## Admin: Create

Create a new `Form`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/forms`

**Request Keys**

| Key                             | Type                          | Default                          | Description                                                                                                      |
| ------------------------------- | ----------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `name`\*                        | `string`                      | -                                | Unique form name.                                                                                                |
| `slug`\*                        | `string`                      | Generated from name when omitted | Unique URL slug.                                                                                                 |
| `subject`\*                     | `string`                      | Localized default                | Message subject using a validated [System Variable template](/introduction/system-variables#rendered-templates). |
| `email_template`\*              | `string`                      | Localized default                | HTML email using a validated [System Variable template](/introduction/system-variables#rendered-templates).      |
| `lang_id`                       | `string`                      | System language                  | Language key. Use `en-US` for this example.                                                                      |
| `icon_id`                       | `integer` \| `null`           | Form icon                        | Icon ID.                                                                                                         |
| `recipient`                     | `array` \| `null`             | `null`                           | Recipient email addresses.                                                                                       |
| `always_send_to_recipient`      | `boolean`                     | `false`                          | Send to configured recipients in addition to field recipients.                                                   |
| `has_personal_info`             | `boolean`                     | `false`                          | Whether the form collects personal information.                                                                  |
| `user_confirmation_required`    | `boolean`                     | `false`                          | Require the user to confirm before submission.                                                                   |
| `request_receipt`               | `boolean`                     | `false`                          | Request receipt confirmation.                                                                                    |
| `request_confirmation`          | `boolean`                     | `false`                          | Request an approval decision.                                                                                    |
| `message_receipt_lifetime`      | `ISO 8601 duration` \| `null` | `P1M` when enabled               | Receipt token lifetime.                                                                                          |
| `message_confirmation_lifetime` | `ISO 8601 duration` \| `null` | `P1D` when enabled               | Approval token lifetime.                                                                                         |
| `grade_mode`                    | `string` \| `null`            | `null`                           | Grade mode; scaled mode requires `grade_scale`.                                                                  |
| `grade_scale`                   | `integer` \| `null`           | `null`                           | Scale used by scaled grading.                                                                                    |
| `send_constraint_type`          | `string` \| `null`            | `null`                           | Optional send constraint.                                                                                        |
| `send_empty_fields`             | `boolean`                     | `false`                          | Include empty fields in output.                                                                                  |
| `send_description_fields`       | `boolean`                     | `false`                          | Include description fields in output.                                                                            |
| `active`                        | `boolean`                     | `true`                           | Whether users may submit the form.                                                                               |
| `sort_number`                   | `integer`                     | Auto                             | Display order.                                                                                                   |
| `entity_permissions`            | `object`                      | Unrestricted                     | [Entity Permissions](/introduction/resource-capabilities/entity-permissions).                                    |

Keys with `*` are required after server defaults are applied.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/forms', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'name' => 'Equipment Request',
        'slug' => 'equipment-request',
        'subject' => 'New equipment request',
        'email_template' => '<p>A new equipment request was submitted.</p>',
        'lang_id' => 'en-US',
        'recipient' => ['it@example.com', 'purchasing@example.com'],
        'always_send_to_recipient' => true,
        'has_personal_info' => true,
        'user_confirmation_required' => true,
        'request_receipt' => true,
        'request_confirmation' => true,
        'message_receipt_lifetime' => 'P5D',
        'message_confirmation_lifetime' => 'P1D',
        'grade_mode' => 'scaled',
        'grade_scale' => 100,
        'send_constraint_type' => 'once',
        'send_empty_fields' => true,
        'send_description_fields' => false,
        'active' => true,
        'sort_number' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 22,
    "user_id": 8,
    "lang_id": "en-US",
    "icon_id": 61,
    "folder_id": 12,
    "name": "Equipment Request",
    "slug": "equipment-request",
    "subject": "New equipment request",
    "recipient": [
      "it@example.com",
      "purchasing@example.com"
    ],
    "always_send_to_recipient": true,
    "has_personal_info": true,
    "user_confirmation_required": true,
    "request_receipt": true,
    "request_confirmation": true,
    "active": true,
    "sort_number": 2,
    "grade_mode": "scaled",
    "grade_scale": 100,
    "send_constraint_type": "once",
    "send_empty_fields": true,
    "send_description_fields": false,
    "message_delete_interval": "P2Y",
    "message_hide_interval": "P6M",
    "message_receipt_lifetime": "P5D",
    "message_confirmation_lifetime": "P1D",
    "hash": "kS"
  }
}
```

## Admin: Update

Update an existing `Form`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/forms/{form}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `form`    | `integer` | Form ID.    |

**Request Keys**

| Key                             | Type                          | Default                     | Description                                                                                                      |
| ------------------------------- | ----------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `name`                          | `string`                      | Current value               | Unique form name.                                                                                                |
| `slug`                          | `string`                      | Generated from changed name | Unique URL slug.                                                                                                 |
| `subject`                       | `string`                      | Current value               | Message subject using a validated [System Variable template](/introduction/system-variables#rendered-templates). |
| `email_template`                | `string`                      | Current value               | HTML email using a validated [System Variable template](/introduction/system-variables#rendered-templates).      |
| `lang_id`                       | `string`                      | Current value               | Language key.                                                                                                    |
| `recipient`                     | `array` \| `null`             | Current value               | Recipient email addresses.                                                                                       |
| `request_receipt`               | `boolean`                     | Current value               | Request receipt confirmation.                                                                                    |
| `request_confirmation`          | `boolean`                     | Current value               | Request an approval decision.                                                                                    |
| `message_receipt_lifetime`      | `ISO 8601 duration` \| `null` | `P5D` when enabled          | Receipt token lifetime.                                                                                          |
| `message_confirmation_lifetime` | `ISO 8601 duration` \| `null` | `P1D` when enabled          | Approval token lifetime.                                                                                         |
| `active`                        | `boolean`                     | Current value               | Whether users may submit the form.                                                                               |
| `entity_permissions`            | `object`                      | Current value               | Updated [Entity Permissions](/introduction/resource-capabilities/entity-permissions).                            |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/forms/22', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'name' => 'IT Equipment Request',
        'slug' => 'it-equipment-request',
        'lang_id' => 'en-US',
        'recipient' => ['it@example.com'],
        'request_receipt' => true,
        'message_receipt_lifetime' => 'P7D',
        'active' => true
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 22,
    "user_id": 8,
    "lang_id": "en-US",
    "icon_id": 61,
    "folder_id": 12,
    "name": "IT Equipment Request",
    "slug": "it-equipment-request",
    "subject": "New equipment request",
    "recipient": [
      "it@example.com"
    ],
    "always_send_to_recipient": true,
    "has_personal_info": true,
    "user_confirmation_required": true,
    "request_receipt": true,
    "request_confirmation": true,
    "active": true,
    "sort_number": 2,
    "grade_mode": "scaled",
    "grade_scale": 100,
    "send_constraint_type": "once",
    "send_empty_fields": true,
    "send_description_fields": false,
    "message_delete_interval": "P2Y",
    "message_hide_interval": "P6M",
    "message_receipt_lifetime": "P7D",
    "message_confirmation_lifetime": "P1D",
    "hash": "kS"
  }
}
```

## Admin: Delete

Delete an existing `Form`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/forms/{form}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `form`    | `integer` | Form ID.    |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/forms/22', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# FormFields

## Introduction

`FormFields` define the inputs and informational elements of [Forms](/api-reference/forms/forms).

Rich-text values in `default_value` and type-specific `config` keys use the shared [Rich Text](/introduction/rich-text) HTML format. This applies to the `rich-text` and `description` type values and the `privacy-policy` type's `config.text` value.

## Model Definition

**Alias**

`formField`

**Capabilities**

* [Entity Permissions](/introduction/resource-capabilities/entity-permissions) - Access is inherited through the parent form.
* [Translations](/introduction/resource-capabilities/translations) - Localizes `name`, `default_value`, `placeholder`, and `description`; examples use `en-US`.

**Relations**

| Key                   | Relation                                                                         | Type          | Relation Field(s)              |
| --------------------- | -------------------------------------------------------------------------------- | ------------- | ------------------------------ |
| `form`                | [Form](/api-reference/forms/forms) or another supported form entity              | Morph to      | `form_type`, `form_id`         |
| `fieldType`           | [FormFieldType](/api-reference/forms/form-field-types)                           | Belongs to    | `form_field_type_id`           |
| `defaultValueSources` | [DefaultValueSources](/api-reference/forms/default-value-sources)                | Has many      | `form_field_id`                |
| `fieldValidations`    | [FormFieldValidations](/api-reference/forms/form-field-validations)              | Has many      | `form_field_id`                |
| `displayConditions`   | [FormFieldDisplayConditions](/api-reference/forms/form-field-display-conditions) | Has many      | Attachables                    |
| `layoutColumns`       | [LayoutColumns](/api-reference/layouts/layout-columns)                           | Morph to many | `layout_column_layout_element` |

## List

List visible `FormFields`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/fields`

**Request Keys**

| Key         | Type      | Default           | Description                                                      |
| ----------- | --------- | ----------------- | ---------------------------------------------------------------- |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.                                |
| `relations` | `string`  | Default relations | Pipe-separated relations to include.                             |
| `limit`     | `integer` | No limit          | Maximum number of form fields.                                   |
| `filters`   | `object`  | No filters        | [Value filters](/introduction/query-manipulation/value-filters). |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/fields', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 301,
    "form_id": 21,
    "form_type": "form",
    "form_field_type_id": "text",
    "lang_id": "en-US",
    "name": "Full name",
    "key": "full-name",
    "default_value": null,
    "placeholder": "First and last name",
    "description": "Enter the name used for follow-up.",
    "config": {
      "scanner": false
    },
    "sort_number": 1,
    "disabled": false,
    "hidden": false
  },
  {
    "id": 302,
    "form_id": 21,
    "form_type": "form",
    "form_field_type_id": "entity-select",
    "lang_id": "en-US",
    "name": "Mentor",
    "key": "mentor",
    "default_value": "7",
    "placeholder": "Select a mentor",
    "description": null,
    "config": {
      "entity_type": "user",
      "selectable_entities_filters": {
        "active": true
      },
      "representation": {
        "label_template": null,
        "result_template": null,
        "display": {
          "type": "select-modal",
          "config": null
        }
      }
    },
    "sort_number": 2,
    "disabled": false,
    "hidden": false
  }
]
```

## List by Form

List visible `FormFields` for one `Form`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/{formEntity}/fields`

**Route Parameters**

| Parameter    | Type      | Description |
| ------------ | --------- | ----------- |
| `formEntity` | `integer` | Form ID.    |

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of fields.            |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/21/fields', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 301,
    "form_id": 21,
    "form_type": "form",
    "form_field_type_id": "text",
    "lang_id": "en-US",
    "name": "Full name",
    "key": "full-name",
    "default_value": null,
    "placeholder": "First and last name",
    "description": "Enter the name used for follow-up.",
    "config": {
      "scanner": false
    },
    "sort_number": 1,
    "disabled": false,
    "hidden": false
  },
  {
    "id": 302,
    "form_id": 21,
    "form_type": "form",
    "form_field_type_id": "entity-select",
    "lang_id": "en-US",
    "name": "Mentor",
    "key": "mentor",
    "default_value": "7",
    "placeholder": "Select a mentor",
    "description": null,
    "config": {
      "entity_type": "user",
      "selectable_entities_filters": {
        "active": true
      },
      "representation": {
        "label_template": null,
        "result_template": null,
        "display": {
          "type": "select-modal",
          "config": null
        }
      }
    },
    "sort_number": 2,
    "disabled": false,
    "hidden": false
  }
]
```

## Show

Show one visible `FormField`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/fields/{formField}`

**Route Parameters**

| Parameter   | Type      | Description    |
| ----------- | --------- | -------------- |
| `formField` | `integer` | Form field ID. |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/fields/302', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 302,
  "form_id": 21,
  "form_type": "form",
  "form_field_type_id": "entity-select",
  "lang_id": "en-US",
  "name": "Mentor",
  "key": "mentor",
  "default_value": "7",
  "placeholder": "Select a mentor",
  "description": null,
  "config": {
    "entity_type": "user",
    "selectable_entities_filters": {
      "active": true
    },
    "representation": {
      "label_template": null,
      "result_template": null,
      "display": {
        "type": "select-modal",
        "config": null
      }
    }
  },
  "sort_number": 2,
  "disabled": false,
  "hidden": false
}
```

## List Preselected Entities

List entities preselected for one `FormField`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/forms/fields/{formField}/preselected-entities`

**Route Parameters**

| Parameter   | Type      | Description                  |
| ----------- | --------- | ---------------------------- |
| `formField` | `integer` | Entity-select form field ID. |

**Request Keys**

| Key        | Type      | Default               | Description                                                                                                           |
| ---------- | --------- | --------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `page`     | `integer` | `1`                   | Result page.                                                                                                          |
| `per_page` | `integer` | Server default        | Results per page.                                                                                                     |
| `filter`   | `object`  | No additional filters | Additional [value filters](/introduction/query-manipulation/value-filters); configured field filters take precedence. |

**Behavior**

* The field must use an entity-select type. Its `default_value` supplies the selected entity IDs.
* This endpoint uses [count-less pagination](/introduction/query-manipulation/result-control#count-less-pagination), so `total` is `null`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/forms/fields/302/preselected-entities', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'page' => 1,
        'per_page' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "current_page": 1,
  "data": [
    {
      "id": 7,
      "title": "Ada Lovelace",
      "sub_title": "ada.lovelace",
      "description": "Engineering"
    },
    {
      "id": 8,
      "title": "Grace Hopper",
      "sub_title": "grace.hopper",
      "description": "Product"
    }
  ],
  "from": 1,
  "last_page": 1,
  "per_page": 2,
  "to": 2,
  "total": null
}
```

## List Selectable Entities

List entities selectable for one `FormField`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/forms/fields/{formField}/selectable-entities`

**Route Parameters**

| Parameter   | Type      | Description                  |
| ----------- | --------- | ---------------------------- |
| `formField` | `integer` | Entity-select form field ID. |

**Request Keys**

| Key        | Type      | Default               | Description                                                                                                           |
| ---------- | --------- | --------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `page`     | `integer` | `1`                   | Result page.                                                                                                          |
| `per_page` | `integer` | Server default        | Results per page.                                                                                                     |
| `filter`   | `object`  | No additional filters | Additional [value filters](/introduction/query-manipulation/value-filters); configured field filters take precedence. |

**Behavior**

* Configured field filters are merged with request filters and take precedence.
* This endpoint uses [count-less pagination](/introduction/query-manipulation/result-control#count-less-pagination), so `total` is `null`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/forms/fields/302/selectable-entities', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'page' => 1,
        'per_page' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "current_page": 1,
  "data": [
    {
      "id": 7,
      "title": "Ada Lovelace",
      "sub_title": "ada.lovelace",
      "description": "Engineering"
    },
    {
      "id": 8,
      "title": "Grace Hopper",
      "sub_title": "grace.hopper",
      "description": "Product"
    }
  ],
  "from": 1,
  "last_page": 1,
  "per_page": 2,
  "to": 2,
  "total": null
}
```

## List Entity Filters

List selectable-entity filters for one `FormField`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/forms/fields/{formField}/selectable-entities/filters`

**Route Parameters**

| Parameter   | Type      | Description                  |
| ----------- | --------- | ---------------------------- |
| `formField` | `integer` | Entity-select form field ID. |

**Request Keys**

| Key      | Type     | Default               | Description                                                                                             |
| -------- | -------- | --------------------- | ------------------------------------------------------------------------------------------------------- |
| `filter` | `object` | No additional filters | Additional value filters used to calculate available options; configured field filters take precedence. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/forms/fields/302/selectable-entities/filters', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'filter' => [
            'active' => true
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "data": [
    {
      "key": "department_id",
      "type": "multi-select",
      "title": "Department"
    },
    {
      "key": "role_id",
      "type": "multi-select",
      "title": "Role"
    }
  ]
}
```

## List Entity Filter Options

List options for one selectable-entity filter of a `FormField`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/forms/fields/{formField}/selectable-entities/{filterKey}/options`

**Route Parameters**

| Parameter   | Type      | Description                  |
| ----------- | --------- | ---------------------------- |
| `formField` | `integer` | Entity-select form field ID. |
| `filterKey` | `string`  | Multi-select filter key.     |

**Request Keys**

| Key        | Type      | Default        | Description       |
| ---------- | --------- | -------------- | ----------------- |
| `page`     | `integer` | `1`            | Result page.      |
| `per_page` | `integer` | Server default | Results per page. |

**Behavior**

This endpoint uses [count-less pagination](/introduction/query-manipulation/result-control#count-less-pagination), so `total` is `null`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/forms/fields/302/selectable-entities/department_id/options', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'page' => 1,
        'per_page' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "current_page": 1,
  "data": [
    {
      "value": 6,
      "label": "Engineering",
      "additionalData": []
    },
    {
      "value": 9,
      "label": "Product",
      "additionalData": []
    }
  ],
  "from": 1,
  "last_page": 1,
  "per_page": 2,
  "to": 2,
  "total": null
}
```

## Admin: List

List all `FormFields` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/forms/fields`

**Request Keys**

| Key         | Type      | Default           | Description                                                      |
| ----------- | --------- | ----------------- | ---------------------------------------------------------------- |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.                                |
| `relations` | `string`  | Default relations | Pipe-separated relations to include.                             |
| `limit`     | `integer` | No limit          | Maximum number of form fields.                                   |
| `filters`   | `object`  | No filters        | [Value filters](/introduction/query-manipulation/value-filters). |

**Behavior**

The administration list may include soft-deleted fields.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/forms/fields', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 301,
    "form_id": 21,
    "form_type": "form",
    "form_field_type_id": "text",
    "lang_id": "en-US",
    "name": "Full name",
    "key": "full-name",
    "default_value": null,
    "placeholder": "First and last name",
    "description": "Enter the name used for follow-up.",
    "config": {
      "scanner": false
    },
    "sort_number": 1,
    "disabled": false,
    "hidden": false
  },
  {
    "id": 302,
    "form_id": 21,
    "form_type": "form",
    "form_field_type_id": "entity-select",
    "lang_id": "en-US",
    "name": "Mentor",
    "key": "mentor",
    "default_value": "7",
    "placeholder": "Select a mentor",
    "description": null,
    "config": {
      "entity_type": "user",
      "selectable_entities_filters": {
        "active": true
      },
      "representation": {
        "label_template": null,
        "result_template": null,
        "display": {
          "type": "select-modal",
          "config": null
        }
      }
    },
    "sort_number": 2,
    "disabled": false,
    "hidden": false,
    "deleted_at": "2026-08-01 08:00:00"
  }
]
```

## Admin: List by Form

List all `FormFields` for one `Form` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/forms/{formEntity}/fields`

**Route Parameters**

| Parameter    | Type      | Description |
| ------------ | --------- | ----------- |
| `formEntity` | `integer` | Form ID.    |

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of fields.            |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/forms/21/fields', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 301,
    "form_id": 21,
    "form_type": "form",
    "form_field_type_id": "text",
    "lang_id": "en-US",
    "name": "Full name",
    "key": "full-name",
    "default_value": null,
    "placeholder": "First and last name",
    "description": "Enter the name used for follow-up.",
    "config": {
      "scanner": false
    },
    "sort_number": 1,
    "disabled": false,
    "hidden": false
  },
  {
    "id": 302,
    "form_id": 21,
    "form_type": "form",
    "form_field_type_id": "entity-select",
    "lang_id": "en-US",
    "name": "Mentor",
    "key": "mentor",
    "default_value": "7",
    "placeholder": "Select a mentor",
    "description": null,
    "config": {
      "entity_type": "user",
      "selectable_entities_filters": {
        "active": true
      },
      "representation": {
        "label_template": null,
        "result_template": null,
        "display": {
          "type": "select-modal",
          "config": null
        }
      }
    },
    "sort_number": 2,
    "disabled": false,
    "hidden": false
  }
]
```

## Admin: Show

Show one `FormField` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/forms/fields/{formField}`

**Route Parameters**

| Parameter   | Type      | Description    |
| ----------- | --------- | -------------- |
| `formField` | `integer` | Form field ID. |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/forms/fields/302', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 302,
  "form_id": 21,
  "form_type": "form",
  "form_field_type_id": "entity-select",
  "lang_id": "en-US",
  "name": "Mentor",
  "key": "mentor",
  "default_value": "7",
  "placeholder": "Select a mentor",
  "description": null,
  "config": {
    "entity_type": "user",
    "selectable_entities_filters": {
      "active": true
    },
    "representation": {
      "label_template": null,
      "result_template": null,
      "display": {
        "type": "select-modal",
        "config": null
      }
    }
  },
  "sort_number": 2,
  "disabled": false,
  "hidden": false
}
```

## Admin: Create

Create a new `FormField`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/forms/fields`

**Request Keys**

| Key                    | Type               | Default                            | Description                                                                                                                                            |
| ---------------------- | ------------------ | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `form_type`\*          | `string`           | `form` for backwards compatibility | Parent morph alias.                                                                                                                                    |
| `form_id`\*            | `integer`          | -                                  | Parent [Form](/api-reference/forms/forms) ID.                                                                                                          |
| `form_field_type_id`\* | `string`           | `text` for backwards compatibility | ID from [Available Form Field Types](/api-reference/forms/form-field-types#available-form-field-types).                                                |
| `lang_id`              | `string`           | System language                    | Language key. Use `en-US` for this example.                                                                                                            |
| `name`\*               | `string`           | -                                  | Field label.                                                                                                                                           |
| `slug`                 | `string`           | Generated from name                | Source for the normalized `key`.                                                                                                                       |
| `default_value`        | `mixed`            | `null`                             | Default field value.                                                                                                                                   |
| `placeholder`          | `string` \| `null` | `null`                             | Input placeholder.                                                                                                                                     |
| `description`          | `string` \| `null` | `null`                             | Help text.                                                                                                                                             |
| `hidden`               | `boolean`          | `false`                            | Hide the field.                                                                                                                                        |
| `disabled`             | `boolean`          | `false`                            | Disable user input.                                                                                                                                    |
| `config`               | `object`           | Type default                       | Complete type-specific configuration documented with the selected [Form Field Type](/api-reference/forms/form-field-types#available-form-field-types). |

Keys with `*` are required after backwards-compatible defaults are applied.

**Behavior**

* When `config` is omitted, the selected type's `default_config` is inserted before validation.
* Defaults containing editor placeholders still require a complete config. In particular, `entity-select` and `entities-select` require a selectable `entity_type`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/forms/fields', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'form_type' => 'form',
        'form_id' => 21,
        'form_field_type_id' => 'entity-select',
        'lang_id' => 'en-US',
        'name' => 'Mentor',
        'slug' => 'mentor',
        'default_value' => '7',
        'placeholder' => 'Select a mentor',
        'description' => null,
        'hidden' => false,
        'disabled' => false,
        'config' => [
            'entity_type' => 'user',
            'selectable_entities_filters' => [
                'active' => true
            ],
            'representation' => [
                'label_template' => null,
                'result_template' => null,
                'display' => [
                    'type' => 'select-modal',
                    'config' => null
                ]
            ]
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 302,
    "form_id": 21,
    "form_type": "form",
    "form_field_type_id": "entity-select",
    "lang_id": "en-US",
    "name": "Mentor",
    "key": "mentor",
    "default_value": "7",
    "placeholder": "Select a mentor",
    "description": null,
    "config": {
      "entity_type": "user",
      "selectable_entities_filters": {
        "active": true
      },
      "representation": {
        "label_template": null,
        "result_template": null,
        "display": {
          "type": "select-modal",
          "config": null
        }
      }
    },
    "sort_number": 2,
    "disabled": false,
    "hidden": false
  }
}
```

## Admin: Update

Update an existing `FormField`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/forms/fields/{formField}`

**Route Parameters**

| Parameter   | Type      | Description    |
| ----------- | --------- | -------------- |
| `formField` | `integer` | Form field ID. |

**Request Keys**

| Key                  | Type               | Default                     | Description                                                                                                                                   |
| -------------------- | ------------------ | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `form_field_type_id` | `string`           | Current value               | New ID from [Available Form Field Types](/api-reference/forms/form-field-types#available-form-field-types).                                   |
| `lang_id`            | `string`           | Current value               | Language key.                                                                                                                                 |
| `name`               | `string`           | Current value               | Field label.                                                                                                                                  |
| `slug`               | `string`           | Generated from changed name | Source for the normalized `key`.                                                                                                              |
| `default_value`      | `mixed`            | Current value               | Default field value.                                                                                                                          |
| `placeholder`        | `string` \| `null` | Current value               | Input placeholder.                                                                                                                            |
| `description`        | `string` \| `null` | Current value               | Help text.                                                                                                                                    |
| `hidden`             | `boolean`          | Current value               | Hide the field.                                                                                                                               |
| `disabled`           | `boolean`          | Current value               | Disable user input.                                                                                                                           |
| `config`             | `object`           | Current or type default     | Type-specific configuration documented with the selected [Form Field Type](/api-reference/forms/form-field-types#available-form-field-types). |

**Behavior**

* When the field type is unchanged and `config` is omitted, validation reuses the current config.
* When `form_field_type_id` changes and `config` is omitted, validation uses the new type's `default_config`. Submit `config` in the same request when that default contains required editor placeholders.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/forms/fields/302', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'name' => 'Preferred mentor',
        'slug' => 'preferred-mentor',
        'default_value' => '8',
        'placeholder' => 'Choose a colleague',
        'config' => [
            'entity_type' => 'user',
            'selectable_entities_filters' => [
                'active' => true
            ],
            'representation' => [
                'label_template' => null,
                'result_template' => null,
                'display' => [
                    'type' => 'select-modal',
                    'config' => null
                ]
            ]
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 302,
    "form_id": 21,
    "form_type": "form",
    "form_field_type_id": "entity-select",
    "lang_id": "en-US",
    "name": "Preferred mentor",
    "key": "preferred-mentor",
    "default_value": "8",
    "placeholder": "Choose a colleague",
    "description": null,
    "config": {
      "entity_type": "user",
      "selectable_entities_filters": {
        "active": true
      },
      "representation": {
        "label_template": null,
        "result_template": null,
        "display": {
          "type": "select-modal",
          "config": null
        }
      }
    },
    "sort_number": 2,
    "disabled": false,
    "hidden": false
  }
}
```

## Admin: Delete

Delete an existing `FormField`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/forms/fields/{formField}`

**Route Parameters**

| Parameter   | Type      | Description    |
| ----------- | --------- | -------------- |
| `formField` | `integer` | Form field ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/forms/fields/302', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# FormFieldTypes

## Introduction

`FormFieldTypes` describe how [FormFields](/api-reference/forms/form-fields) are rendered and which `config` they accept.

The `rich-text` and `description` type values and the `privacy-policy` type's `config.text` value use the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`formFieldType`

**Attributes**

| Key              | Type      | Description                                                                                                                                                       |
| ---------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`             | `string`  | Stable field type ID used by `form_field_type_id`.                                                                                                                |
| `icon_id`        | `integer` | Stable ID from [Available Icons](/api-reference/icons#available-icons) assigned to the field type.                                                                |
| `default_config` | `object`  | Source-defined starting `config` used when a field of this type is created without an explicit configuration; required editor placeholders may still need values. |
| `unique`         | `boolean` | Whether a form can contain only one field of this type.                                                                                                           |
| `relevant`       | `boolean` | Whether fields of this type are included when form results are processed.                                                                                         |

## Available Form Field Types

The following field types are available in the current API version. Each entry combines the complete source-defined default with the accepted configuration keys. The defaults are editor starting points and may contain required placeholders that must be replaced before submission. Dot notation identifies nested object keys; additional unlisted keys are not part of the public contract.

### `text` <a href="#form-field-type-text" id="form-field-type-text"></a>

**Default Config**

```json
{
  "scanner": false
}
```

| Icon ID | Unique  | Relevant |
| ------- | ------- | -------- |
| `72`    | `false` | `true`   |

Plain text input with optional scanner assistance.

**Configuration Keys**

| Key       | Type      | Requirement | Description                         |
| --------- | --------- | ----------- | ----------------------------------- |
| `scanner` | `boolean` | Required    | Enable scanner-assisted text input. |

### `boolean` <a href="#form-field-type-boolean" id="form-field-type-boolean"></a>

**Default Config**

```json
{
  "type": "labeled_buttons",
  "representation": {
    "true": "Ja",
    "false": "Nein"
  }
}
```

| Icon ID | Unique  | Relevant |
| ------- | ------- | -------- |
| `43`    | `false` | `true`   |

Boolean input with configurable labels, icons, and grades.

**Configuration Keys**

| Key                    | Type               | Requirement                 | Description                                                                                       |
| ---------------------- | ------------------ | --------------------------- | ------------------------------------------------------------------------------------------------- |
| `type`                 | `string`           | Required                    | Presentation type: `switch`, `labeled_buttons`, or `icon_buttons`.                                |
| `representation.true`  | `string`           | Required                    | Translatable label for the true value.                                                            |
| `representation.false` | `string`           | Required                    | Translatable label for the false value.                                                           |
| `icons.true`           | `string`           | Required for `icon_buttons` | Icon identifier for the true value. See [Available Icons](/api-reference/icons#available-icons).  |
| `icons.false`          | `string`           | Required for `icon_buttons` | Icon identifier for the false value. See [Available Icons](/api-reference/icons#available-icons). |
| `grades`               | `object` \| `null` | Optional                    | Numeric grades keyed by result value.                                                             |
| `grades.*`             | `mixed`            | Optional                    | Numeric grade for one result value.                                                               |

### `rich-text` <a href="#form-field-type-rich-text" id="form-field-type-rich-text"></a>

**Default Config**

```json
{}
```

| Icon ID | Unique  | Relevant |
| ------- | ------- | -------- |
| `73`    | `false` | `true`   |

[Rich Text](/introduction/rich-text) input without additional configuration keys.

### `checkbox` <a href="#form-field-type-checkbox" id="form-field-type-checkbox"></a>

**Default Config**

```json
{
  "options": {
    "1": {
      "title": "Option 1",
      "value": null
    }
  },
  "inline": false
}
```

| Icon ID | Unique  | Relevant |
| ------- | ------- | -------- |
| `74`    | `false` | `true`   |

Multiple-choice input rendered as checkboxes. The default contains `inline`, but the backend does not validate this key.

**Configuration Keys**

| Key               | Type               | Requirement   | Description                                         |
| ----------------- | ------------------ | ------------- | --------------------------------------------------- |
| `options`         | `object`           | Required      | At least one option keyed by its stable option ID.  |
| `options.*.title` | `string`           | Required      | Translatable option label.                          |
| `options.*.value` | `mixed` \| `null`  | Optional      | Optional value associated with the option.          |
| `option_grades`   | `object` \| `null` | Optional      | Numeric grades keyed by an existing option ID.      |
| `option_grades.*` | `mixed`            | Optional      | Numeric grade for one option.                       |
| `inline`          | `boolean`          | Not validated | Source-defined display flag retained in the config. |

### `radio` <a href="#form-field-type-radio" id="form-field-type-radio"></a>

**Default Config**

```json
{
  "options": {
    "1": {
      "title": "Option 1",
      "value": null
    }
  },
  "inline": false
}
```

| Icon ID | Unique  | Relevant |
| ------- | ------- | -------- |
| `42`    | `false` | `true`   |

Single-choice input rendered as radio buttons. The default contains `inline`, but the backend does not validate this key.

**Configuration Keys**

| Key               | Type               | Requirement   | Description                                         |
| ----------------- | ------------------ | ------------- | --------------------------------------------------- |
| `options`         | `object`           | Required      | At least one option keyed by its stable option ID.  |
| `options.*.title` | `string`           | Required      | Translatable option label.                          |
| `options.*.value` | `mixed` \| `null`  | Optional      | Optional value associated with the option.          |
| `option_grades`   | `object` \| `null` | Optional      | Numeric grades keyed by an existing option ID.      |
| `option_grades.*` | `mixed`            | Optional      | Numeric grade for one option.                       |
| `inline`          | `boolean`          | Not validated | Source-defined display flag retained in the config. |

### `select` <a href="#form-field-type-select" id="form-field-type-select"></a>

**Default Config**

```json
{
  "options": {
    "1": {
      "title": "Option 1",
      "value": null
    }
  },
  "inline": false
}
```

| Icon ID | Unique  | Relevant |
| ------- | ------- | -------- |
| `71`    | `false` | `true`   |

Single-choice input rendered as a select control. The default contains `inline`, but the backend does not validate this key.

**Configuration Keys**

| Key               | Type               | Requirement   | Description                                         |
| ----------------- | ------------------ | ------------- | --------------------------------------------------- |
| `options`         | `object`           | Required      | At least one option keyed by its stable option ID.  |
| `options.*.title` | `string`           | Required      | Translatable option label.                          |
| `options.*.value` | `mixed` \| `null`  | Optional      | Optional value associated with the option.          |
| `option_grades`   | `object` \| `null` | Optional      | Numeric grades keyed by an existing option ID.      |
| `option_grades.*` | `mixed`            | Optional      | Numeric grade for one option.                       |
| `inline`          | `boolean`          | Not validated | Source-defined display flag retained in the config. |

### `datetime` <a href="#form-field-type-datetime" id="form-field-type-datetime"></a>

**Default Config**

```json
{
  "type": "date",
  "format": "DD.MM.YYYY"
}
```

| Icon ID | Unique  | Relevant |
| ------- | ------- | -------- |
| `3`     | `false` | `true`   |

Date and time input with a type-specific display format.

**Configuration Keys**

| Key      | Type     | Requirement | Description                                                                                                                                                                                   |
| -------- | -------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`   | `string` | Required    | Value type: `datetime`, `date`, or `time`.                                                                                                                                                    |
| `format` | `string` | Required    | Format allowed for the selected type: `DD.MM.YYYY HH:mm` or `dddd, Do MMMM YYYY [um] HH:mm` for `datetime`; `DD.MM.YYYY`, `YYYY`, or `dddd, Do MMMM YYYY` for `date`; and `HH:mm` for `time`. |

### `file-upload` <a href="#form-field-type-file-upload" id="form-field-type-file-upload"></a>

**Default Config**

```json
{
  "path": "/Formular-Uploads/",
  "send_as_attachment": false,
  "show_preview": true
}
```

| Icon ID | Unique  | Relevant |
| ------- | ------- | -------- |
| `19`    | `false` | `true`   |

File input with storage, attachment, and preview settings.

**Configuration Keys**

| Key                  | Type      | Requirement | Description                                             |
| -------------------- | --------- | ----------- | ------------------------------------------------------- |
| `path`               | `string`  | Required    | Directory path retained with the upload configuration.  |
| `send_as_attachment` | `boolean` | Required    | Attach uploaded files when distributing a form message. |
| `show_preview`       | `boolean` | Required    | Show a preview for supported uploaded files.            |

### `entity-select` <a href="#form-field-type-entity-select" id="form-field-type-entity-select"></a>

**Default Config**

```json
{
  "entity_type": null,
  "selectable_entities_filters": null,
  "representation": {
    "label_template": null,
    "result_template": null,
    "display": {
      "type": "select-modal",
      "config": null
    }
  }
}
```

| Icon ID | Unique  | Relevant |
| ------- | ------- | -------- |
| `129`   | `false` | `true`   |

Single-entity selection. The default `entity_type: null` is an editor placeholder; a submitted config requires a selectable entity alias.

**Configuration Keys**

| Key                                    | Type               | Requirement                  | Description                                                                                                        |
| -------------------------------------- | ------------------ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `entity_type`                          | `string`           | Required                     | Entity alias for a resource that supports selection.                                                               |
| `selectable_entities_filters`          | `object` \| `null` | Optional                     | Filters applied when resolving selectable entities.                                                                |
| `representation`                       | `object`           | Required                     | Representation settings.                                                                                           |
| `representation.label_template`        | `string` \| `null` | Optional                     | Validated [System Variable template](/introduction/system-variables#rendered-templates) used for selection labels. |
| `representation.result_template`       | `string` \| `null` | Optional                     | Validated [System Variable template](/introduction/system-variables#rendered-templates) used for resolved results. |
| `representation.display`               | `object`           | Required                     | Selection control settings.                                                                                        |
| `representation.display.type`          | `string`           | Required                     | Display type: `radio`, `select`, or `select-modal`.                                                                |
| `representation.display.config`        | `object` \| `null` | Optional                     | Configuration for the selected display type.                                                                       |
| `representation.display.config.inline` | `boolean`          | Required for `radio` display | Display radio choices inline.                                                                                      |

### `entities-select` <a href="#form-field-type-entities-select" id="form-field-type-entities-select"></a>

**Default Config**

```json
{
  "entity_type": null,
  "selectable_entities_filters": null,
  "representation": {
    "label_template": null,
    "result_template": null,
    "display": {
      "type": "select-modal",
      "config": null
    }
  }
}
```

| Icon ID | Unique  | Relevant |
| ------- | ------- | -------- |
| `163`   | `false` | `true`   |

Multiple-entity selection. The default `entity_type: null` is an editor placeholder; a submitted config requires a selectable entity alias.

**Configuration Keys**

| Key                              | Type               | Requirement | Description                                                                                                        |
| -------------------------------- | ------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------ |
| `entity_type`                    | `string`           | Required    | Entity alias for a resource that supports selection.                                                               |
| `selectable_entities_filters`    | `object` \| `null` | Optional    | Filters applied when resolving selectable entities.                                                                |
| `representation`                 | `object`           | Required    | Representation settings.                                                                                           |
| `representation.label_template`  | `string` \| `null` | Optional    | Validated [System Variable template](/introduction/system-variables#rendered-templates) used for selection labels. |
| `representation.result_template` | `string` \| `null` | Optional    | Validated [System Variable template](/introduction/system-variables#rendered-templates) used for resolved results. |
| `representation.display`         | `object`           | Required    | Selection control settings.                                                                                        |
| `representation.display.type`    | `string`           | Required    | Display type: `checkbox` or `select-modal`.                                                                        |
| `representation.display.config`  | `object` \| `null` | Optional    | Configuration for the selected display type.                                                                       |

### `recipient-select` <a href="#form-field-type-recipient-select" id="form-field-type-recipient-select"></a>

**Default Config**

```json
{
  "options": {
    "1": {
      "title": "intratool",
      "value": "noreply@intratool.de"
    }
  },
  "multi_select": false
}
```

| Icon ID | Unique  | Relevant |
| ------- | ------- | -------- |
| `4`     | `false` | `true`   |

Recipient selection backed by configured email-address options.

**Configuration Keys**

| Key               | Type               | Requirement | Description                                                           |
| ----------------- | ------------------ | ----------- | --------------------------------------------------------------------- |
| `options`         | `object`           | Required    | At least one recipient option keyed by its stable option ID.          |
| `options.*.title` | `string`           | Required    | Translatable recipient label.                                         |
| `options.*.value` | `string`           | Required    | One email address or a comma-separated list of valid email addresses. |
| `option_grades`   | `object` \| `null` | Optional    | Numeric grades keyed by an existing option ID.                        |
| `option_grades.*` | `mixed`            | Optional    | Numeric grade for one option.                                         |
| `multi_select`    | `boolean`          | Required    | Allow more than one recipient option.                                 |

### `description` <a href="#form-field-type-description" id="form-field-type-description"></a>

**Default Config**

```json
{}
```

| Icon ID | Unique  | Relevant |
| ------- | ------- | -------- |
| `56`    | `false` | `true`   |

Non-input [Rich Text](/introduction/rich-text) content without additional configuration keys.

### `privacy-policy` <a href="#form-field-type-privacy-policy" id="form-field-type-privacy-policy"></a>

**Default Config**

```json
{
  "text": "<p>Ja, ich habe die {{Datenschutzbestimmungen}} gelesen und akzeptiere diese.</p>",
  "external": true
}
```

| Icon ID | Unique | Relevant |
| ------- | ------ | -------- |
| `127`   | `true` | `true`   |

Privacy-policy acceptance field. A form can contain only one field of this type.

**Configuration Keys**

| Key        | Type      | Requirement | Description                                                                                                                                 |
| ---------- | --------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `text`     | `string`  | Required    | Translatable [Rich Text](/introduction/rich-text) acceptance text. Text inside double curly brackets becomes the privacy-policy link label. |
| `external` | `boolean` | Required    | Link to the external privacy policy when `true`; otherwise link to the internal privacy policy.                                             |

### `signature` <a href="#form-field-type-signature" id="form-field-type-signature"></a>

**Default Config**

```json
{
  "path": "/Formular-Uploads/Signaturen/",
  "send_as_attachment": false
}
```

| Icon ID | Unique  | Relevant |
| ------- | ------- | -------- |
| `128`   | `false` | `true`   |

Signature input with storage and attachment settings.

**Configuration Keys**

| Key                  | Type      | Requirement | Description                                                 |
| -------------------- | --------- | ----------- | ----------------------------------------------------------- |
| `path`               | `string`  | Required    | Directory path retained with the signature configuration.   |
| `send_as_attachment` | `boolean` | Required    | Attach the signature file when distributing a form message. |

## List

List available `FormFieldTypes`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/field-types`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/field-types', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": "text",
    "icon_id": 72,
    "default_config": {
      "scanner": false
    },
    "unique": false,
    "relevant": true,
    "sort_number": 1
  },
  {
    "id": "checkbox",
    "icon_id": 74,
    "default_config": {
      "options": {
        "1": {
          "title": "Option 1",
          "value": null
        }
      },
      "inline": false
    },
    "unique": false,
    "relevant": true,
    "sort_number": 4
  }
]
```

## Show

Show one `FormFieldType`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/field-types/{formFieldType}`

**Route Parameters**

| Parameter       | Type     | Description                                                             |
| --------------- | -------- | ----------------------------------------------------------------------- |
| `formFieldType` | `string` | Type ID from [Available Form Field Types](#available-form-field-types). |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/field-types/text', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": "text",
  "icon_id": 72,
  "default_config": {
    "scanner": false
  },
  "unique": false,
  "relevant": true,
  "sort_number": 1
}
```


# DefaultValueSources

## Introduction

`DefaultValueSources` resolve prefilled values for [FormFields](/api-reference/forms/form-fields).

## Model Definition

**Alias**

`defaultValueSource`

**Relations**

| Key            | Relation                                                                  | Type       | Relation Field(s)                        |
| -------------- | ------------------------------------------------------------------------- | ---------- | ---------------------------------------- |
| `formField`    | [FormField](/api-reference/forms/form-fields)                             | Belongs to | `form_field_id`                          |
| `sourceEntity` | [FormField](/api-reference/forms/form-fields) or another supported entity | Morph to   | `source_entity_type`, `source_entity_id` |

## Resolve Form Default Values

Resolve default values for all fields of one `Form`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/{form}/default-values`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `form`    | `integer` | Form ID.    |

**Behavior**

Sources are evaluated by `sort_number` per field. The first resolvable source wins, and response keys are form field IDs.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/21/default-values', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "301": {
    "value": "ada@example.com",
    "request_value": "ada@example.com",
    "additional_information": null
  },
  "302": {
    "value": "7",
    "request_value": "7",
    "additional_information": {
      "title": "Ada Lovelace"
    }
  }
}
```

## Admin: List Source Form Fields

List accessible source `FormFields` from administrable [Forms](/api-reference/forms/forms) and [TaskTemplates](/api-reference/tasks-2/task-templates) for a field-value default source.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/forms/fields/default-value-sources/form-field-value/form-fields`

**Request Keys**

| Key        | Type      | Default        | Description                                 |
| ---------- | --------- | -------------- | ------------------------------------------- |
| `page`     | `integer` | `1`            | Result page.                                |
| `per_page` | `integer` | Server default | Results per page.                           |
| `filter`   | `object`  | No filters     | Filters applied to available source fields. |

**Behavior**

* Fields are returned only when the authenticated user can administer their parent Form or TaskTemplate.
* Each preview uses the field name as `title`, the localized parent type as `sub_title`, and the parent title as `description`.
* This endpoint uses [count-less pagination](/introduction/query-manipulation/result-control#count-less-pagination), so `total` is `null`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/forms/fields/default-value-sources/form-field-value/form-fields', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'page' => 1,
        'per_page' => 2,
        'filter' => [

        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "current_page": 1,
  "data": [
    {
      "id": 301,
      "title": "Full name",
      "sub_title": "Form",
      "description": "Employee Feedback",
      "additional_data": {
        "source_entity_type": "formField",
        "form_type": "form",
        "form_id": 21,
        "form_field_type_id": "text",
        "sort_number": 1
      }
    },
    {
      "id": 302,
      "title": "Inspection result",
      "sub_title": "Task template",
      "description": "Equipment Inspection",
      "additional_data": {
        "source_entity_type": "formField",
        "form_type": "taskTemplate",
        "form_id": 22,
        "form_field_type_id": "boolean",
        "sort_number": 2
      }
    }
  ],
  "per_page": 2,
  "total": null
}
```

## Admin: List Source Field Filters

List filters for accessible source `FormFields`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/forms/fields/default-value-sources/form-field-value/form-fields/filters`

**Request Keys**

| Key        | Type      | Default        | Description                                 |
| ---------- | --------- | -------------- | ------------------------------------------- |
| `page`     | `integer` | `1`            | Result page.                                |
| `per_page` | `integer` | Server default | Results per page.                           |
| `filter`   | `object`  | No filters     | Filters applied to available source fields. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/forms/fields/default-value-sources/form-field-value/form-fields/filters', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'filter' => [

        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "data": [
    {
      "key": "form_type",
      "label": "Template Type",
      "type": "multi-select",
      "operators": [
        "in",
        "not_in"
      ]
    },
    {
      "key": "form_id",
      "label": "Template",
      "type": "multi-select",
      "operators": [
        "in",
        "not_in"
      ]
    }
  ]
}
```

## Admin: List Source Field Filter Options

List options for one source-field filter.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/forms/fields/default-value-sources/form-field-value/form-fields/filters/{filterKey}/options`

**Route Parameters**

| Parameter   | Type     | Description              |
| ----------- | -------- | ------------------------ |
| `filterKey` | `string` | Multi-select filter key. |

**Request Keys**

| Key        | Type      | Default        | Description                                |
| ---------- | --------- | -------------- | ------------------------------------------ |
| `page`     | `integer` | `1`            | Result page.                               |
| `per_page` | `integer` | Server default | Results per page.                          |
| `filter`   | `object`  | No filters     | Filters applied before option aggregation. |

**Behavior**

This endpoint uses [count-less pagination](/introduction/query-manipulation/result-control#count-less-pagination), so `total` is `null`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/forms/fields/default-value-sources/form-field-value/form-fields/filters/form_id/options', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'page' => 1,
        'per_page' => 2,
        'filter' => [
            'form_type' => ['in' => 'form']
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "current_page": 1,
  "data": [
    {
      "value": 21,
      "label": "Employee Feedback",
      "additionalData": {
        "form_type": "form"
      }
    },
    {
      "value": 22,
      "label": "Equipment Request",
      "additionalData": {
        "form_type": "form"
      }
    }
  ],
  "per_page": 2,
  "total": null
}
```

## Admin: Create

Create a new `DefaultValueSource`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/forms/fields/default-value-sources`

**Request Keys**

| Key                  | Type                | Default      | Description                                                    |
| -------------------- | ------------------- | ------------ | -------------------------------------------------------------- |
| `source_type`\*      | `string`            | -            | `formFieldValue` or `systemVariable`.                          |
| `form_field_id`\*    | `integer`           | -            | Target [FormField](/api-reference/forms/form-fields) ID.       |
| `config`             | `object`            | Type default | Source-specific configuration.                                 |
| `source_entity_type` | `string` \| `null`  | `null`       | Source morph alias, normally `formField` for `formFieldValue`. |
| `source_entity_id`   | `integer` \| `null` | `null`       | Source entity ID; required with `source_entity_type`.          |
| `sort_number`        | `integer`           | Auto         | Evaluation order within the target field.                      |

Keys with `*` are required.

**Behavior**

For `systemVariable`, `config.variable_key` is required and `config.variable_filter` may contain a [formatter](/introduction/system-variables/formatters). For `formFieldValue`, identify the source field through the morph keys.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/forms/fields/default-value-sources', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'source_type' => 'systemVariable',
        'form_field_id' => 301,
        'config' => [
            'variable_key' => 'system.user.email',
            'variable_filter' => null
        ],
        'sort_number' => 1
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 52,
    "form_field_id": 301,
    "source_type": "systemVariable",
    "config": {
      "variable_key": "system.user.email",
      "variable_filter": null
    },
    "source_entity_type": null,
    "source_entity_id": null,
    "sort_number": 1
  }
}
```

## Admin: Update

Update an existing `DefaultValueSource`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/forms/fields/default-value-sources/{defaultValueSource}`

**Route Parameters**

| Parameter            | Type      | Description |
| -------------------- | --------- | ----------- |
| `defaultValueSource` | `integer` | Source ID.  |

**Request Keys**

| Key                  | Type                | Default                 | Description                    |
| -------------------- | ------------------- | ----------------------- | ------------------------------ |
| `source_type`        | `string`            | Current value           | New source type.               |
| `form_field_id`      | `integer`           | Current value           | New target field ID.           |
| `config`             | `object`            | Current or type default | Source-specific configuration. |
| `source_entity_type` | `string` \| `null`  | Current value           | New source morph alias.        |
| `source_entity_id`   | `integer` \| `null` | Current value           | New source entity ID.          |
| `sort_number`        | `integer`           | Current value           | Evaluation order.              |

**Behavior**

When `source_type` changes and `config` is omitted, the new type's default configuration is used.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/forms/fields/default-value-sources/52', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'source_type' => 'formFieldValue',
        'config' => [

        ],
        'source_entity_type' => 'formField',
        'source_entity_id' => 302
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 52,
    "form_field_id": 301,
    "source_type": "formFieldValue",
    "config": {},
    "source_entity_type": "formField",
    "source_entity_id": 302,
    "sort_number": 1
  }
}
```

## Admin: Delete

Delete an existing `DefaultValueSource`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/forms/fields/default-value-sources/{defaultValueSource}`

**Route Parameters**

| Parameter            | Type      | Description |
| -------------------- | --------- | ----------- |
| `defaultValueSource` | `integer` | Source ID.  |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/forms/fields/default-value-sources/52', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# FormFieldValidations

## Introduction

`FormFieldValidations` apply a [FormFieldValidationType](/api-reference/forms/form-field-validation-types) to a [FormField](/api-reference/forms/form-fields).

## Model Definition

**Alias**

`formFieldValidation`

**Relations**

| Key                       | Relation                                                                    | Type       | Relation Field(s)               |
| ------------------------- | --------------------------------------------------------------------------- | ---------- | ------------------------------- |
| `formField`               | [FormField](/api-reference/forms/form-fields)                               | Belongs to | `form_field_id`                 |
| `formFieldValidationType` | [FormFieldValidationType](/api-reference/forms/form-field-validation-types) | Belongs to | `form_field_validation_type_id` |

## Admin: Create

Create a new `FormFieldValidation`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/forms/fields/validations`

**Request Keys**

| Key                               | Type      | Default      | Description                                                                                                                                                                    |
| --------------------------------- | --------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `form_field_id`\*                 | `integer` | -            | Target [FormField](/api-reference/forms/form-fields) ID.                                                                                                                       |
| `form_field_validation_type_id`\* | `string`  | -            | ID from [Available Form Field Validation Types](/api-reference/forms/form-field-validation-types#available-form-field-validation-types).                                       |
| `config`                          | `object`  | Type default | Type-specific configuration documented with the selected [Form Field Validation Type](/api-reference/forms/form-field-validation-types#available-form-field-validation-types). |

Keys with `*` are required.

**Behavior**

* When `config` is omitted, the selected validation type's `default_config` is inserted before validation.
* The `in`, `not-in`, and `regex` defaults are editor placeholders and must be populated in the create request.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/forms/fields/validations', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'form_field_id' => 33,
        'form_field_validation_type_id' => 'required',
        'config' => [

        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 91,
    "form_field_id": 33,
    "form_field_validation_type_id": "required",
    "config": {}
  }
}
```

## Admin: Update

Update an existing `FormFieldValidation`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/forms/fields/validations/{formFieldValidation}`

**Route Parameters**

| Parameter             | Type      | Description    |
| --------------------- | --------- | -------------- |
| `formFieldValidation` | `integer` | Validation ID. |

**Request Keys**

| Key                             | Type      | Default       | Description                                                                                                                                                                    |
| ------------------------------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `form_field_id`                 | `integer` | Current value | Target field ID.                                                                                                                                                               |
| `form_field_validation_type_id` | `string`  | Current value | New ID from [Available Form Field Validation Types](/api-reference/forms/form-field-validation-types#available-form-field-validation-types).                                   |
| `config`                        | `object`  | Current value | Type-specific configuration documented with the selected [Form Field Validation Type](/api-reference/forms/form-field-validation-types#available-form-field-validation-types). |

**Behavior**

* When the validation type is unchanged and `config` is omitted, the current config remains unchanged.
* When `form_field_validation_type_id` changes and `config` is omitted, validation uses the new type's `default_config`. Submit `config` in the same request for `in`, `not-in`, or `regex`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/forms/fields/validations/91', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'form_field_validation_type_id' => 'email',
        'config' => [

        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 91,
    "form_field_id": 33,
    "form_field_validation_type_id": "email",
    "config": {}
  }
}
```

## Admin: Delete

Delete an existing `FormFieldValidation`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/forms/fields/validations/{formFieldValidation}`

**Route Parameters**

| Parameter             | Type      | Description    |
| --------------------- | --------- | -------------- |
| `formFieldValidation` | `integer` | Validation ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/forms/fields/validations/91', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# FormFieldDisplayConditions

## Introduction

`FormFieldDisplayConditions` attach conditional visibility rules to [FormFields](/api-reference/forms/form-fields).

## Model Definition

**Alias**

`formFieldDisplayCondition`

## Admin: Create

Create a new `FormFieldDisplayCondition`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/forms/fields/display-conditions`

**Request Keys**

| Key                 | Type      | Default | Description                                              |
| ------------------- | --------- | ------- | -------------------------------------------------------- |
| `attachable_type`\* | `string`  | -       | Supported morph alias. Use `formField` for a form field. |
| `attachable_id`\*   | `integer` | -       | ID of the entity receiving the condition.                |

Keys with `*` are required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/forms/fields/display-conditions', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'attachable_type' => 'formField',
        'attachable_id' => 33
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 77,
    "attachable_type": "formField",
    "attachable_id": "33",
    "condition_groups": [],
    "single_conditions": []
  }
}
```

## Admin: Update

Update an existing `FormFieldDisplayCondition`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/forms/fields/display-conditions/{formFieldDisplayCondition}`

**Route Parameters**

| Parameter                   | Type      | Description           |
| --------------------------- | --------- | --------------------- |
| `formFieldDisplayCondition` | `integer` | Display condition ID. |

**Request Keys**

| Key               | Type      | Default       | Description                |
| ----------------- | --------- | ------------- | -------------------------- |
| `attachable_type` | `string`  | Current value | New supported morph alias. |
| `attachable_id`   | `integer` | Current value | New attachable entity ID.  |

**Behavior**

When changing the attachment, provide both values so the morph type can be resolved against the new entity.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/forms/fields/display-conditions/77', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'attachable_type' => 'formField',
        'attachable_id' => 34
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 77,
    "attachable_type": "formField",
    "attachable_id": "34",
    "condition_groups": [],
    "single_conditions": []
  }
}
```

## Admin: Delete

Delete an existing `FormFieldDisplayCondition`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/forms/fields/display-conditions/{formFieldDisplayCondition}`

**Route Parameters**

| Parameter                   | Type      | Description           |
| --------------------------- | --------- | --------------------- |
| `formFieldDisplayCondition` | `integer` | Display condition ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/forms/fields/display-conditions/77', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# FormFieldValidationTypes

## Introduction

`FormFieldValidationTypes` define validation rules that can be attached to [FormFields](/api-reference/forms/form-fields).

## Model Definition

**Alias**

`formFieldValidationType`

**Attributes**

| Key              | Type     | Description                                                                                                                                                            |
| ---------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`             | `string` | Stable validation type ID used by `form_field_validation_type_id`.                                                                                                     |
| `default_config` | `object` | Source-defined starting `config` used when a validation of this type is created without an explicit configuration; required editor placeholders may still need values. |

## Available Form Field Validation Types

The following validation types are available in the current API version. Each entry combines the validation's meaning, complete source-defined default, and accepted configuration keys. The defaults are editor starting points and may contain required placeholders that must be replaced before submission. Dot notation identifies nested values; additional unlisted keys are not part of the public contract.

### `required` <a href="#form-field-validation-type-required" id="form-field-validation-type-required"></a>

**Default Config**

```json
{}
```

Requires a value. This validation accepts no configuration keys.

### `email` <a href="#form-field-validation-type-email" id="form-field-validation-type-email"></a>

**Default Config**

```json
{}
```

Requires a valid email address. This validation accepts no configuration keys.

### `phone` <a href="#form-field-validation-type-phone" id="form-field-validation-type-phone"></a>

**Default Config**

```json
{
  "fixed_line": true,
  "mobile": true,
  "country_codes": null,
  "default_country_code": "DE"
}
```

Requires an E.164-formatted phone number. The flags and country list further restrict the accepted phone-number categories.

**Configuration Keys**

| Key                    | Type              | Requirement | Description                                                     |
| ---------------------- | ----------------- | ----------- | --------------------------------------------------------------- |
| `fixed_line`           | `boolean`         | Optional    | Allow fixed-line numbers.                                       |
| `mobile`               | `boolean`         | Optional    | Allow mobile numbers.                                           |
| `country_codes`        | `array` \| `null` | Optional    | Restrict numbers to supported ISO 3166-1 alpha-2 country codes. |
| `country_codes.*`      | `string`          | Optional    | One supported country code.                                     |
| `default_country_code` | `string`          | Required    | Supported country code used as the default country.             |

### `url` <a href="#form-field-validation-type-url" id="form-field-validation-type-url"></a>

**Default Config**

```json
{}
```

Requires a valid URL. This validation accepts no configuration keys.

### `number` <a href="#form-field-validation-type-number" id="form-field-validation-type-number"></a>

**Default Config**

```json
{
  "type": "numeric"
}
```

Requires a numeric or integer value.

**Configuration Keys**

| Key    | Type     | Requirement | Description                           |
| ------ | -------- | ----------- | ------------------------------------- |
| `type` | `string` | Required    | Numeric rule: `numeric` or `integer`. |

### `digits_only` <a href="#form-field-validation-type-digits-only" id="form-field-validation-type-digits-only"></a>

**Default Config**

```json
{}
```

Allows ASCII digits only. This validation accepts no configuration keys.

### `minmax` <a href="#form-field-validation-type-minmax" id="form-field-validation-type-minmax"></a>

**Default Config**

```json
{
  "min": 0
}
```

Restricts a value or length to a minimum, maximum, or both. At least one of `min` and `max` is required.

**Configuration Keys**

| Key   | Type      | Requirement                    | Description                                                          |
| ----- | --------- | ------------------------------ | -------------------------------------------------------------------- |
| `min` | `integer` | Required when `max` is omitted | Minimum accepted value or length, depending on the field value type. |
| `max` | `integer` | Required when `min` is omitted | Maximum accepted value or length, depending on the field value type. |

### `items-count` <a href="#form-field-validation-type-items-count" id="form-field-validation-type-items-count"></a>

**Default Config**

```json
{
  "min": 0
}
```

Restricts the number of selected items. At least one of `min` and `max` is required.

**Configuration Keys**

| Key   | Type      | Requirement                    | Description                       |
| ----- | --------- | ------------------------------ | --------------------------------- |
| `min` | `integer` | Required when `max` is omitted | Minimum number of selected items. |
| `max` | `integer` | Required when `min` is omitted | Maximum number of selected items. |

### `boolean` <a href="#form-field-validation-type-boolean" id="form-field-validation-type-boolean"></a>

**Default Config**

```json
{}
```

Requires a boolean-compatible value. This validation accepts no configuration keys.

### `datetime` <a href="#form-field-validation-type-datetime" id="form-field-validation-type-datetime"></a>

**Default Config**

```json
{
  "format": "Y-m-d H:i:s"
}
```

Requires a date and time matching a server-side format.

**Configuration Keys**

| Key      | Type     | Requirement | Description                                                   |
| -------- | -------- | ----------- | ------------------------------------------------------------- |
| `format` | `string` | Required    | Server-side date format used to validate the submitted value. |

### `after-before` <a href="#form-field-validation-type-after-before" id="form-field-validation-type-after-before"></a>

**Default Config**

```json
{
  "after": "now"
}
```

Restricts a date to a lower bound, upper bound, or both. At least one of `after` and `before` is required.

**Configuration Keys**

| Key      | Type                  | Requirement                       | Description                                                                                         |
| -------- | --------------------- | --------------------------------- | --------------------------------------------------------------------------------------------------- |
| `after`  | `string` \| `integer` | Required when `before` is omitted | Relative or absolute date, such as `now`, or a Unix timestamp. The submitted value must be later.   |
| `before` | `string` \| `integer` | Required when `after` is omitted  | Relative or absolute date, such as `now`, or a Unix timestamp. The submitted value must be earlier. |

### `file` <a href="#form-field-validation-type-file" id="form-field-validation-type-file"></a>

**Default Config**

```json
{}
```

Requires an uploaded file. This validation accepts no configuration keys.

### `mimetypes` <a href="#form-field-validation-type-mimetypes" id="form-field-validation-type-mimetypes"></a>

**Default Config**

```json
{
  "allowed": [
    "image/gif",
    "image/jpeg",
    "image/png",
    "image/tiff"
  ]
}
```

Restricts an uploaded file to configured MIME types.

**Configuration Keys**

| Key         | Type     | Requirement                | Description                                                                                |
| ----------- | -------- | -------------------------- | ------------------------------------------------------------------------------------------ |
| `allowed`   | `array`  | Required                   | MIME type strings accepted for an uploaded file. An empty array applies no MIME-type rule. |
| `allowed.*` | `string` | Not individually validated | One MIME type consumed by the file validator.                                              |

### `last-modified` <a href="#form-field-validation-type-last-modified" id="form-field-validation-type-last-modified"></a>

**Default Config**

```json
{
  "before": "now"
}
```

Restricts a file's modification date to a lower bound, upper bound, or both. At least one of `after` and `before` is required. Clients evaluate this validation; the backend stores and validates the config but does not apply a server-side file-date rule.

**Configuration Keys**

| Key      | Type                  | Requirement                       | Description                                                                                              |
| -------- | --------------------- | --------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `after`  | `string` \| `integer` | Required when `before` is omitted | Relative or absolute date, such as `now`, or a Unix timestamp. The file must have been modified later.   |
| `before` | `string` \| `integer` | Required when `after` is omitted  | Relative or absolute date, such as `now`, or a Unix timestamp. The file must have been modified earlier. |

### `in` <a href="#form-field-validation-type-in" id="form-field-validation-type-in"></a>

**Default Config**

```json
{
  "values": []
}
```

Allows only configured values. The array must contain at least one item; the source-defined empty default is not a valid submitted config until populated.

**Configuration Keys**

| Key        | Type    | Requirement                | Description         |
| ---------- | ------- | -------------------------- | ------------------- |
| `values`   | `array` | Required                   | Allowed values.     |
| `values.*` | `mixed` | Not individually validated | One compared value. |

### `not-in` <a href="#form-field-validation-type-not-in" id="form-field-validation-type-not-in"></a>

**Default Config**

```json
{
  "values": []
}
```

Rejects configured values. The array must contain at least one item; the source-defined empty default is not a valid submitted config until populated.

**Configuration Keys**

| Key        | Type    | Requirement                | Description         |
| ---------- | ------- | -------------------------- | ------------------- |
| `values`   | `array` | Required                   | Rejected values.    |
| `values.*` | `mixed` | Not individually validated | One compared value. |

### `entity-selection` <a href="#form-field-validation-type-entity-selection" id="form-field-validation-type-entity-selection"></a>

**Default Config**

```json
{
  "multiple": false
}
```

Requires either a single-entity or multiple-entity selection.

**Configuration Keys**

| Key        | Type      | Requirement | Description                                                                     |
| ---------- | --------- | ----------- | ------------------------------------------------------------------------------- |
| `multiple` | `boolean` | Required    | Require a multiple-entity value when `true`; otherwise require a single entity. |

### `regex` <a href="#form-field-validation-type-regex" id="form-field-validation-type-regex"></a>

**Default Config**

```json
{
  "pattern": ""
}
```

Requires a value matching a valid regular expression. The source-defined empty default is not a valid submitted config until populated.

**Configuration Keys**

| Key       | Type     | Requirement | Description                                        |
| --------- | -------- | ----------- | -------------------------------------------------- |
| `pattern` | `string` | Required    | Regular expression applied to the submitted value. |

## List

List available `FormFieldValidationTypes`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/field-validation-types`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/field-validation-types', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": "required",
    "default_config": {},
    "sort_number": 1
  },
  {
    "id": "minmax",
    "default_config": {
      "min": 0
    },
    "sort_number": 7
  }
]
```

## Show

Show one `FormFieldValidationType`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/field-validation-types/{formFieldValidationType}`

**Route Parameters**

| Parameter                 | Type     | Description                                                                                              |
| ------------------------- | -------- | -------------------------------------------------------------------------------------------------------- |
| `formFieldValidationType` | `string` | Validation type ID from [Available Form Field Validation Types](#available-form-field-validation-types). |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/field-validation-types/required', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": "required",
  "default_config": {},
  "sort_number": 1
}
```


# FormMessages

## Introduction

`FormMessages` are persisted submissions of [Forms](/api-reference/forms/forms).

The generated `body` field uses the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`formMessage`

**Capabilities**

* [Translations](/introduction/resource-capabilities/translations) - Localizes `subject` and `body`; examples use `en-US`.
* [URL Context](/introduction/resource-capabilities/url-context) - Returns the stable submitted-form URL.

**Relations**

| Key                 | Relation                                                      | Type       | Relation Field(s) |
| ------------------- | ------------------------------------------------------------- | ---------- | ----------------- |
| `form`              | [Form](/api-reference/forms/forms)                            | Belongs to | `form_id`         |
| `formMessageFields` | [FormMessageFields](/api-reference/forms/form-message-fields) | Has many   | `form_message_id` |
| `user`              | [User](/api-reference/users)                                  | Belongs to | `user_id`         |
| `department`        | [Department](/api-reference/departments)                      | Belongs to | `department_id`   |

## List

List visible `FormMessages`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/messages`

**Request Keys**

| Key         | Type      | Default           | Description                                                      |
| ----------- | --------- | ----------------- | ---------------------------------------------------------------- |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.                                |
| `relations` | `string`  | Default relations | Pipe-separated relations to include.                             |
| `limit`     | `integer` | No limit          | Maximum number of messages.                                      |
| `filters`   | `object`  | No filters        | [Value filters](/introduction/query-manipulation/value-filters). |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/messages', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 140,
    "user_id": 7,
    "group_account_user_id": null,
    "department_id": 6,
    "form_id": 21,
    "lang_id": "en-US",
    "subject": "New employee feedback",
    "body": "<p><strong>Full name:</strong> Ada Lovelace</p><p><strong>Message:</strong> More mentoring sessions.</p>",
    "sent_from_public": false,
    "distribution_success": true,
    "hash": "mN",
    "created_at": "2026-08-06 12:00:00",
    "updated_at": "2026-08-06 12:00:00"
  },
  {
    "id": 141,
    "user_id": 8,
    "group_account_user_id": 8,
    "department_id": 9,
    "form_id": 22,
    "lang_id": "en-US",
    "subject": "New equipment request",
    "body": "<p><strong>Device:</strong> Developer laptop</p><p><strong>Urgency:</strong> High</p>",
    "sent_from_public": true,
    "distribution_success": false,
    "hash": "nP",
    "created_at": "2026-08-06 12:15:00",
    "updated_at": "2026-08-06 12:15:00"
  }
]
```

## Show

Show one visible `FormMessage`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/messages/{formMessage}`

**Route Parameters**

| Parameter     | Type      | Description      |
| ------------- | --------- | ---------------- |
| `formMessage` | `integer` | Form message ID. |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/messages/140', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 140,
  "user_id": 7,
  "group_account_user_id": null,
  "department_id": 6,
  "form_id": 21,
  "lang_id": "en-US",
  "subject": "New employee feedback",
  "body": "<p><strong>Full name:</strong> Ada Lovelace</p><p><strong>Message:</strong> More mentoring sessions.</p>",
  "sent_from_public": false,
  "distribution_success": true,
  "hash": "mN",
  "created_at": "2026-08-06 12:00:00",
  "updated_at": "2026-08-06 12:00:00"
}
```

## Delete

Delete an existing `FormMessage`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/forms/messages/{formMessage}`

**Route Parameters**

| Parameter     | Type      | Description      |
| ------------- | --------- | ---------------- |
| `formMessage` | `integer` | Form message ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/forms/messages/140', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# FormMessageFields

## Introduction

`FormMessageFields` store the submitted value of each [FormField](/api-reference/forms/form-fields) in a [FormMessage](/api-reference/forms/form-messages).

For a related FormField type that stores rich text, the applicable `value` and `request_value` fields use the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`formMessageField`

**Capabilities**

* [Translations](/introduction/resource-capabilities/translations) - Localizes `value` and `request_value`; examples use `en-US`.

**Relations**

| Key           | Relation                                          | Type       | Relation Field(s) |
| ------------- | ------------------------------------------------- | ---------- | ----------------- |
| `formMessage` | [FormMessage](/api-reference/forms/form-messages) | Belongs to | `form_message_id` |
| `formField`   | [FormField](/api-reference/forms/form-fields)     | Belongs to | `form_field_id`   |

## List by Message

List `FormMessageFields` for one `FormMessage`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/messages/{formMessage}/fields`

**Route Parameters**

| Parameter     | Type      | Description      |
| ------------- | --------- | ---------------- |
| `formMessage` | `integer` | Form message ID. |

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of fields.            |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/messages/140/fields', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 501,
    "form_message_id": 140,
    "form_field_id": 301,
    "lang_id": "en-US",
    "value": "Ada Lovelace",
    "request_value": "Ada Lovelace"
  },
  {
    "id": 502,
    "form_message_id": 140,
    "form_field_id": 302,
    "lang_id": "en-US",
    "value": "Ada Lovelace",
    "request_value": "7"
  }
]
```

## Show by Message and Field

Show one `FormMessageField` by message and field.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/messages/{formMessage}/fields/{formField}`

**Route Parameters**

| Parameter     | Type      | Description      |
| ------------- | --------- | ---------------- |
| `formMessage` | `integer` | Form message ID. |
| `formField`   | `integer` | Form field ID.   |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/messages/140/fields/302', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 502,
  "form_message_id": 140,
  "form_field_id": 302,
  "lang_id": "en-US",
  "value": "Ada Lovelace",
  "request_value": "7"
}
```


# FormMessageConfirmations

## Introduction

`FormMessageConfirmations` record receipt or approval decisions for submitted [FormMessages](/api-reference/forms/form-messages).

The `comment` field uses the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`formMessageConfirmation`

**Capabilities**

* [Translations](/introduction/resource-capabilities/translations) - Localizes `comment`; examples use `en-US`.

## Show by Token

Show one `FormMessageConfirmation` by token.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/forms/messages/confirmations/{formMessageConfirmationByToken}`

**Route Parameters**

| Parameter                        | Type     | Description                |
| -------------------------------- | -------- | -------------------------- |
| `formMessageConfirmationByToken` | `string` | Signed confirmation token. |

**Request Keys**

| Key       | Type     | Default    | Description                       |
| --------- | -------- | ---------- | --------------------------------- |
| `selects` | `string` | All fields | Comma-separated fields to return. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/forms/messages/confirmations/{confirmationToken}');
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 215,
  "user_id": 7,
  "form_message_id": 140,
  "lang_id": "en-US",
  "referrer": "reviewer@example.com",
  "status": "pending",
  "comment": null,
  "first_visit": "2026-08-06 12:00:00"
}
```

## Update by Token

Update one `FormMessageConfirmation` by token.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/forms/messages/confirmations/{formMessageConfirmationByToken}`

**Route Parameters**

| Parameter                        | Type     | Description                |
| -------------------------------- | -------- | -------------------------- |
| `formMessageConfirmationByToken` | `string` | Signed confirmation token. |

**Request Keys**

| Key          | Type               | Default                        | Description                                                   |
| ------------ | ------------------ | ------------------------------ | ------------------------------------------------------------- |
| `lang_id`    | `string`           | Current value                  | Language key. Use `en-US` for this example.                   |
| `status`     | `string`           | Current value                  | Allowed confirmation status for this request.                 |
| `comment`    | `string` \| `null` | `null`                         | Optional confirmation comment.                                |
| `referrer`\* | `string`           | Token recipient when available | Required when the token does not already carry its recipient. |

Keys with `*` are required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/forms/messages/confirmations/{confirmationToken}', [
    'json' => [
        'lang_id' => 'en-US',
        'status' => 'confirmed',
        'comment' => '<p>Reviewed and approved.</p>',
        'referrer' => 'reviewer@example.com'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 215,
    "user_id": 7,
    "form_message_id": 140,
    "lang_id": "en-US",
    "referrer": "reviewer@example.com",
    "status": "confirmed",
    "comment": "<p>Reviewed and approved.</p>",
    "first_visit": "2026-08-06 12:00:00"
  }
}
```

## Reset

Reset an existing `FormMessageConfirmation`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/forms/messages/confirmations/{formMessageConfirmation}/reset`

**Route Parameters**

| Parameter                 | Type      | Description                 |
| ------------------------- | --------- | --------------------------- |
| `formMessageConfirmation` | `integer` | Confirmation ID to replace. |

**Behavior**

The previous confirmation is deleted, a new token is generated, and the reset notification is distributed.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/forms/messages/confirmations/215/reset', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 216,
    "user_id": 7,
    "form_message_id": 140,
    "lang_id": "en-US",
    "referrer": "reviewer@example.com",
    "status": "pending",
    "comment": null,
    "first_visit": null
  }
}
```


# Chat

Chat supports direct and group communication through [ChatConversations](/api-reference/chat/chat-conversations), which contain [ChatMessages](/api-reference/chat/chat-messages).

[ChatUserConversationSettings](/api-reference/chat/chat-user-conversation-settings) store user-specific mute and archive settings, while [ChatConversationEvents](/api-reference/chat/chat-conversation-events) record changes to a conversation. [ChatMessageSeenUsers](/api-reference/chat/chat-message-seen-users) track which [Users](/api-reference/users) have seen a message.

Chat access combines the `chat-show` module permission with conversation [Targetables](/introduction/resource-capabilities/targetables). Conversations and messages do not use [Entity Permissions](/introduction/resource-capabilities/entity-permissions) for access.


# ChatConversations

## Introduction

`ChatConversations` contain [ChatMessages](/api-reference/chat/chat-messages) and define the users and departments participating in a direct or group conversation.

A conversation may also relate to another intratool resource selected from the supported [relatable types](#relatable-types).

## Model Definition

**Alias**

`chatConversation`

**Relations**

| Key                            | Relation                                                                            | Type            | Relation Field(s)                                                 |
| ------------------------------ | ----------------------------------------------------------------------------------- | --------------- | ----------------------------------------------------------------- |
| `user`                         | [User](/api-reference/users)                                                        | Belongs to      | `user_id`                                                         |
| `language`                     | [Language](/api-reference/languages)                                                | Belongs to      | `lang_id`                                                         |
| `relatable`                    | [Relatable](#relatable-types)                                                       | Morph to        | `relatable_type`, `relatable_id`                                  |
| `departments`                  | [Departments](/api-reference/departments)                                           | Belongs to many | `department_chat_conversation`                                    |
| `users`                        | [Users](/api-reference/users)                                                       | Belongs to many | `user_chat_conversation`                                          |
| `chatMessages`                 | [ChatMessages](/api-reference/chat/chat-messages)                                   | Has many        | `chat_messages.chat_conversation_id`                              |
| `latestChatMessage`            | [ChatMessage](/api-reference/chat/chat-messages)                                    | Has one         | `chat_messages.chat_conversation_id`                              |
| `chatUserConversationSettings` | [ChatUserConversationSettings](/api-reference/chat/chat-user-conversation-settings) | Has one         | `chat_user_conversation_settings.chat_conversation_id`, `user_id` |
| `chatConversationEvents`       | [ChatConversationEvents](/api-reference/chat/chat-conversation-events)              | Has many        | `chat_conversation_events.chat_conversation_id`                   |
| `translations`                 | [Translations](/api-reference/translations)                                         | Morph many      | `translations.translatable_type`, `translations.translatable_id`  |

**Relatable types**

* `filemanagerDirectory` - Relate a [FilemanagerDirectory](/api-reference/filemanager/filemanager-directories).
* `filemanagerFile` - Relate a [FilemanagerFile](/api-reference/filemanager/filemanager-files).
* `form` - Relate a [Form](/api-reference/forms/forms).
* `formMessage` - Relate a [FormMessage](/api-reference/forms/form-messages).
* `infoboardComment` - Relate an [InfoboardComment](/api-reference/infoboard/infoboard-comments).
* `infoboardPost` - Relate an [InfoboardPost](/api-reference/infoboard/infoboard-posts).
* `manualChapter` - Relate a [ManualChapter](/api-reference/manual/manual-chapters).
* `manualEntry` - Relate a [ManualEntry](/api-reference/manual/manual-entries).
* `report` - Relate a `Report`.
* `taskAssignment` - Relate a [TaskAssignment](/api-reference/tasks-2/task-assignments).
* `taskExecution` - Relate a [TaskExecution](/api-reference/tasks-2/task-executions).
* `taskTemplate` - Relate a [TaskTemplate](/api-reference/tasks-2/task-templates).
* `taskTemplateComposition` - Relate a `TaskTemplateComposition`.

**Computed Properties**

* `hash` - The hashed `id` of the `ChatConversation`.

**Capabilities**

* [Targetables](/introduction/resource-capabilities/targetables) - `assign_mode`, `users`, and `departments` define the conversation participants and visibility after the `chat-show` module permission check; they do not create Entity Permissions.
* [URL context](/introduction/resource-capabilities/url-context) - Conversation URLs resolve to access-checked context information.
* [Translations](/introduction/resource-capabilities/translations) - The `title` field is translatable.
* [Notifications](/introduction/resource-capabilities/notifications) - Creating a conversation or adding participants can notify eligible participants who have not muted or archived it.

## List

List `ChatConversations` the current authenticated [User](/api-reference/users) owns or is targeted by.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/chat/conversations`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/chat/conversations', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "lang_id": "en-US",
    "relatable_type": null,
    "relatable_id": null,
    "group": true,
    "title": "Project launch",
    "locked_at": null,
    "assign_mode": "any_of",
    "created_at": "2026-08-06 09:30:00",
    "updated_at": "2026-08-06 09:30:00",
    "deleted_at": null,
    "hash": "wrv2jd6x8g1nmlk4o970mqen"
  },
  {
    "id": 2,
    "user_id": 4,
    "lang_id": "en-US",
    "relatable_type": null,
    "relatable_id": null,
    "group": false,
    "title": null,
    "locked_at": null,
    "assign_mode": "any_of",
    "created_at": "2026-08-06 09:31:00",
    "updated_at": "2026-08-06 09:31:00",
    "deleted_at": null,
    "hash": "q6n1vx8m4p2kr9dw5e7z0yla"
  }
]
```

## Count

Count `ChatConversations` the current authenticated [User](/api-reference/users) owns or is targeted by.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/chat/conversations/count`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/chat/conversations/count', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
1
```

## Show

Show one `ChatConversation` by ID or hash.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/chat/conversations/{chatConversation}`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `chatConversation` | `integer` \| `string` | ChatConversation ID or hash. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/chat/conversations/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "lang_id": "en-US",
  "relatable_type": null,
  "relatable_id": null,
  "group": true,
  "title": "Project launch",
  "locked_at": null,
  "assign_mode": "any_of",
  "created_at": "2026-08-06 09:30:00",
  "updated_at": "2026-08-06 09:30:00",
  "deleted_at": null,
  "hash": "wrv2jd6x8g1nmlk4o970mqen"
}
```

## Create

Create a new `ChatConversation`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/chat/conversations`

**Request Keys**

| Key              | Type                 | Default         | Description                                                                             |
| ---------------- | -------------------- | --------------- | --------------------------------------------------------------------------------------- |
| `lang_id`        | `string`             | system language | Language key for the translatable title.                                                |
| `relatable_type` | `string` \| `null`   | `null`          | [Relatable type](#relatable-types) of the related entity. Required with `relatable_id`. |
| `relatable_id`   | `integer` \| `null`  | `null`          | ID of the related entity. Required with `relatable_type`.                               |
| `group`          | `boolean`            | `false`         | Whether the conversation is a group conversation.                                       |
| `title`          | `string` \| `null`   | `null`          | Conversation title. Required when `group` is `true`.                                    |
| `locked_at`      | `datetime` \| `null` | `null`          | Date from which the conversation is locked.                                             |
| `user_ids`       | `array`              | `[]`            | IDs of [Users](/api-reference/users) targeted by the conversation.                      |
| `department_ids` | `array`              | `[]`            | IDs of [Departments](/api-reference/departments) targeted by the conversation.          |

**Behavior**

* The authenticated user becomes the owner; a client-provided `user_id` is ignored.
* `assign_mode` is set to `any_of` and cannot be selected during creation.
* At least one existing target must be supplied across `user_ids` and `department_ids`; both arrays cannot be empty.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/chat/conversations', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'group' => true,
        'title' => 'Project launch',
        'user_ids' => [4]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "lang_id": "en-US",
    "group": true,
    "title": "Project launch",
    "assign_mode": "any_of",
    "created_at": "2026-08-06 09:30:00",
    "updated_at": "2026-08-06 09:30:00",
    "hash": "wrv2jd6x8g1nmlk4o970mqen"
  }
}
```

## Update

Update an existing `ChatConversation`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/chat/conversations/{chatConversation}`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `chatConversation` | `integer` \| `string` | ChatConversation ID or hash. |

**Request Keys**

| Key              | Type                 | Description                                                                        |
| ---------------- | -------------------- | ---------------------------------------------------------------------------------- |
| `user_id`        | `integer`            | New owner ID. Only the current owner can transfer ownership.                       |
| `lang_id`        | `string`             | New language key for the translatable title.                                       |
| `relatable_type` | `string` \| `null`   | New [relatable type](#relatable-types). Required with `relatable_id`.              |
| `relatable_id`   | `integer` \| `null`  | New related entity ID. Required with `relatable_type`.                             |
| `group`          | `boolean`            | Whether the conversation is a group conversation.                                  |
| `title`          | `string` \| `null`   | New conversation title.                                                            |
| `locked_at`      | `datetime` \| `null` | New date from which the conversation is locked.                                    |
| `user_ids`       | `array`              | New IDs of [Users](/api-reference/users) targeted by the conversation.             |
| `department_ids` | `array`              | New IDs of [Departments](/api-reference/departments) targeted by the conversation. |

**Behavior**

* Only the conversation owner can update it.
* The existing `assign_mode` is retained. Supplying `user_ids` or `department_ids` replaces the corresponding targets.
* The resulting combined user and department targets must remain valid for the retained `assign_mode`; with `any_of`, at least one target must remain.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/chat/conversations/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Project launch planning'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "lang_id": "en-US",
    "relatable_type": null,
    "relatable_id": null,
    "group": true,
    "title": "Project launch planning",
    "locked_at": null,
    "assign_mode": "any_of",
    "created_at": "2026-08-06 09:30:00",
    "updated_at": "2026-08-06 09:50:00",
    "deleted_at": null,
    "hash": "wrv2jd6x8g1nmlk4o970mqen"
  }
}
```

## Update notifications

Update unread [Notifications](/api-reference/notifications) for a `ChatConversation` and its [ChatMessages](/api-reference/chat/chat-messages).

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/chat/conversations/{chatConversation}/notifications`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `chatConversation` | `integer` \| `string` | ChatConversation ID or hash. |

**Request Keys**

| Key       | Type       | Description                                                       |
| --------- | ---------- | ----------------------------------------------------------------- |
| `seen_at` | `datetime` | Date used to mark matching unread notifications as seen.          |
| `read_at` | `datetime` | Date used to mark matching unread notifications as read and seen. |

**Behavior**

At least one of `seen_at` or `read_at` is required. Standard result-control filters can restrict the matching notifications. The response data is the number of updated notifications.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/chat/conversations/1/notifications', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'read_at' => '2026-08-06 10:00:00'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": 4
}
```

## Mark notifications unread

Mark the latest matching [ChatMessage](/api-reference/chat/chat-messages) notification in a `ChatConversation` unread for the current authenticated [User](/api-reference/users).

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/chat/conversations/{chatConversation}/notifications/unread`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `chatConversation` | `integer` \| `string` | ChatConversation ID or hash. |

**Behavior**

* The user must be allowed to view the conversation.
* Only the latest matching chat-message notification is considered. Its `read_at` value is cleared when it is currently read, while `seen_at` is preserved.
* Repeating the request is idempotent. Response data is `1` when a notification changed and `0` when no change was required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/chat/conversations/1/notifications/unread', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": 1
}
```

## Leave

Remove the current authenticated [User](/api-reference/users) from a `ChatConversation`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/chat/conversations/{chatConversation}/leave`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `chatConversation` | `integer` \| `string` | ChatConversation ID or hash. |

**Behavior**

* The owner cannot leave their own conversation.
* The authenticated user must be assigned directly to the conversation. Access through a targeted department alone does not allow the user to leave.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/chat/conversations/1/leave', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "lang_id": "en-US",
    "relatable_type": null,
    "relatable_id": null,
    "group": true,
    "title": "Project launch planning",
    "locked_at": null,
    "assign_mode": "any_of",
    "created_at": "2026-08-06 09:30:00",
    "updated_at": "2026-08-06 09:50:00",
    "deleted_at": null,
    "hash": "wrv2jd6x8g1nmlk4o970mqen"
  }
}
```

## Delete

Delete an existing `ChatConversation`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/chat/conversations/{chatConversation}`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `chatConversation` | `integer` \| `string` | ChatConversation ID or hash. |

**Behavior**

Only the conversation owner can delete it.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/chat/conversations/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# ChatMessages

## Introduction

`ChatMessages` are the content of a [ChatConversation](/api-reference/chat/chat-conversations). A message has a concrete [type](#types), can contain rich text, require a reading confirmation, or relate to another intratool resource selected from the supported [relatable types](#relatable-types).

The `text` field uses the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`chatMessage`

**Relations**

| Key                | Relation                                                            | Type       | Relation Field(s)                                                         |
| ------------------ | ------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------- |
| `user`             | [User](/api-reference/users)                                        | Belongs to | `user_id`                                                                 |
| `chatConversation` | [ChatConversation](/api-reference/chat/chat-conversations)          | Belongs to | `chat_conversation_id`                                                    |
| `language`         | [Language](/api-reference/languages)                                | Belongs to | `lang_id`                                                                 |
| `relatable`        | [Relatable](#relatable-types)                                       | Morph to   | `relatable_type`, `relatable_id`                                          |
| `seenUsers`        | [ChatMessageSeenUsers](/api-reference/chat/chat-message-seen-users) | Has many   | `chat_message_seen_users.chat_message_id`                                 |
| `reactions`        | [Reactions](/api-reference/reactions)                               | Morph many | `reactions.reactable_type`, `reactions.reactable_id`                      |
| `currentReaction`  | [CurrentReaction](/api-reference/reactions)                         | Morph one  | `reactions.reactable_type`, `reactions.reactable_id`, `reactions.user_id` |
| `translations`     | [Translations](/api-reference/translations)                         | Morph many | `translations.translatable_type`, `translations.translatable_id`          |

**Types**

* `default` - A standard message.
* `voiceMessage` - A voice message.

**Relatable types**

* `chatFile` - Relate a `ChatFile`.
* `chatMessage` - Relate another `ChatMessage`.
* `filemanagerDirectory` - Relate a [FilemanagerDirectory](/api-reference/filemanager/filemanager-directories).
* `filemanagerFile` - Relate a [FilemanagerFile](/api-reference/filemanager/filemanager-files).
* `form` - Relate a [Form](/api-reference/forms/forms).
* `formMessage` - Relate a [FormMessage](/api-reference/forms/form-messages).
* `infoboardComment` - Relate an [InfoboardComment](/api-reference/infoboard/infoboard-comments).
* `infoboardPost` - Relate an [InfoboardPost](/api-reference/infoboard/infoboard-posts).
* `manualChapter` - Relate a [ManualChapter](/api-reference/manual/manual-chapters).
* `manualEntry` - Relate a [ManualEntry](/api-reference/manual/manual-entries).
* `report` - Relate a `Report`.
* `survey` - Relate a [Survey](/api-reference/surveys/surveys).
* `taskAssignment` - Relate a [TaskAssignment](/api-reference/tasks-2/task-assignments).
* `taskExecution` - Relate a [TaskExecution](/api-reference/tasks-2/task-executions).
* `taskTemplate` - Relate a [TaskTemplate](/api-reference/tasks-2/task-templates).
* `taskTemplateComposition` - Relate a `TaskTemplateComposition`.

**Computed Properties**

* `hash` - The hashed `id` of the `ChatMessage`.

**Capabilities**

* [Targetables](/introduction/resource-capabilities/targetables) - Message visibility and eligible recipients follow the parent [ChatConversation](/api-reference/chat/chat-conversations); messages do not have independent Entity Permissions.
* [URL context](/introduction/resource-capabilities/url-context) - Message URLs resolve to access-checked context information.
* [Translations](/introduction/resource-capabilities/translations) - The `text` field is translatable.
* [Notifications](/introduction/resource-capabilities/notifications) - Creating a message can notify eligible participants who have not muted or archived the conversation; messages requiring confirmation use a dedicated notification type.
* [Reactions](/introduction/resource-capabilities/reactions) - Users can add one current emoji reaction and retrieve individual or summarized reactions.
* [Seen and read tracking](/introduction/resource-capabilities/seen-and-read-tracking) - The API tracks message visibility and explicit reading confirmations per user.

## List

List `ChatMessages` from conversations available to the current authenticated [User](/api-reference/users).

**Definition**

<mark style="color:green;">`GET`</mark> `/api/chat/messages`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/chat/messages', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "chat_conversation_id": 1,
    "forwarded_id": null,
    "lang_id": "en-US",
    "type": "default",
    "relatable_type": null,
    "relatable_id": null,
    "text": "<p>Welcome to the project chat.</p>",
    "reading_confirmation": false,
    "created_at": "2026-08-06 09:35:00",
    "updated_at": "2026-08-06 09:35:00",
    "deleted_at": null,
    "hash": "vkn978r25g37681dqwzem4px"
  },
  {
    "id": 2,
    "user_id": 4,
    "chat_conversation_id": 2,
    "forwarded_id": null,
    "lang_id": "en-US",
    "type": "default",
    "relatable_type": "infoboardPost",
    "relatable_id": 1,
    "text": "<p>Please review the linked announcement.</p>",
    "reading_confirmation": true,
    "created_at": "2026-08-06 09:40:00",
    "updated_at": "2026-08-06 09:40:00",
    "deleted_at": null,
    "hash": "q6n1vx8m4p2kr9dw5e7z0yla"
  }
]
```

## Count

Count `ChatMessages` from conversations available to the current authenticated [User](/api-reference/users).

**Definition**

<mark style="color:green;">`GET`</mark> `/api/chat/messages/count`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/chat/messages/count', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
1
```

## List by conversation

List `ChatMessages` for a [ChatConversation](/api-reference/chat/chat-conversations).

**Definition**

<mark style="color:green;">`GET`</mark> `/api/chat/conversations/{chatConversation}/messages`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `chatConversation` | `integer` \| `string` | ChatConversation ID or hash. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/chat/conversations/1/messages', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "chat_conversation_id": 1,
    "forwarded_id": null,
    "lang_id": "en-US",
    "type": "default",
    "relatable_type": null,
    "relatable_id": null,
    "text": "<p>Welcome to the project chat.</p>",
    "reading_confirmation": false,
    "created_at": "2026-08-06 09:35:00",
    "updated_at": "2026-08-06 09:35:00",
    "deleted_at": null,
    "hash": "vkn978r25g37681dqwzem4px"
  },
  {
    "id": 2,
    "user_id": 4,
    "chat_conversation_id": 1,
    "forwarded_id": null,
    "lang_id": "en-US",
    "type": "default",
    "relatable_type": "infoboardPost",
    "relatable_id": 1,
    "text": "<p>Please review the linked announcement.</p>",
    "reading_confirmation": true,
    "created_at": "2026-08-06 09:40:00",
    "updated_at": "2026-08-06 09:40:00",
    "deleted_at": null,
    "hash": "q6n1vx8m4p2kr9dw5e7z0yla"
  }
]
```

## Count by conversation

Count `ChatMessages` for a [ChatConversation](/api-reference/chat/chat-conversations).

**Definition**

<mark style="color:green;">`GET`</mark> `/api/chat/conversations/{chatConversation}/messages/count`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `chatConversation` | `integer` \| `string` | ChatConversation ID or hash. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/chat/conversations/1/messages/count', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
1
```

## Show

Show one `ChatMessage` by ID or hash.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/chat/messages/{chatMessage}`

**Route Parameters**

| Parameter     | Type                  | Description             |
| ------------- | --------------------- | ----------------------- |
| `chatMessage` | `integer` \| `string` | ChatMessage ID or hash. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/chat/messages/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "chat_conversation_id": 1,
  "forwarded_id": null,
  "lang_id": "en-US",
  "type": "default",
  "relatable_type": null,
  "relatable_id": null,
  "text": "<p>Welcome to the project chat.</p>",
  "reading_confirmation": false,
  "created_at": "2026-08-06 09:35:00",
  "updated_at": "2026-08-06 09:35:00",
  "deleted_at": null,
  "hash": "vkn978r25g37681dqwzem4px"
}
```

## Create

Create a new `ChatMessage`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/chat/messages`

**Request Keys**

| Key                      | Type                | Default         | Description                                                                             |
| ------------------------ | ------------------- | --------------- | --------------------------------------------------------------------------------------- |
| `chat_conversation_id`\* | `integer`           | -               | ID of the related [ChatConversation](/api-reference/chat/chat-conversations).           |
| `lang_id`                | `string`            | system language | Language key for the translatable text.                                                 |
| `type`\*                 | `string`            | -               | [Type](#types) of the `ChatMessage`.                                                    |
| `relatable_type`         | `string` \| `null`  | `null`          | [Relatable type](#relatable-types) of the related entity. Required with `relatable_id`. |
| `relatable_id`           | `integer` \| `null` | `null`          | ID of the related entity. Required with `relatable_type`.                               |
| `text`                   | `string` \| `null`  | `null`          | Message content. Required when no related entity is supplied.                           |
| `reading_confirmation`   | `boolean`           | `false`         | Whether users must explicitly confirm that they read the message.                       |

Keys with `*` are required.

**Behavior**

The authenticated user becomes the message author; a client-provided `user_id` is ignored. Messages cannot be created in a locked conversation.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/chat/messages', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'chat_conversation_id' => 1,
        'type' => 'default',
        'text' => '<p>Welcome to the project chat.</p>'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "chat_conversation_id": 1,
    "forwarded_id": null,
    "lang_id": "en-US",
    "type": "default",
    "relatable_type": null,
    "relatable_id": null,
    "text": "<p>Welcome to the project chat.</p>",
    "reading_confirmation": false,
    "created_at": "2026-08-06 09:35:00",
    "updated_at": "2026-08-06 09:35:00",
    "deleted_at": null,
    "hash": "vkn978r25g37681dqwzem4px"
  }
}
```

## Forward

Forward a `ChatMessage` to another [ChatConversation](/api-reference/chat/chat-conversations).

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/chat/messages/{chatMessage}/forward`

**Route Parameters**

| Parameter     | Type                  | Description                    |
| ------------- | --------------------- | ------------------------------ |
| `chatMessage` | `integer` \| `string` | Source ChatMessage ID or hash. |

**Request Keys**

| Key                      | Type      | Default | Description                                                                  |
| ------------------------ | --------- | ------- | ---------------------------------------------------------------------------- |
| `chat_conversation_id`\* | `integer` | -       | ID of the target [ChatConversation](/api-reference/chat/chat-conversations). |

Keys with `*` are required.

**Behavior**

The new message retains the content of the source message, stores the source ID in `forwarded_id`, and uses the authenticated user as its author. Related chat files are copied to the target conversation. The target conversation must be unlocked.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/chat/messages/1/forward', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'chat_conversation_id' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 2,
    "user_id": 3,
    "chat_conversation_id": 2,
    "forwarded_id": 1,
    "lang_id": "en-US",
    "type": "default",
    "relatable_type": null,
    "relatable_id": null,
    "text": "<p>Welcome to the project chat.</p>",
    "reading_confirmation": false,
    "created_at": "2026-08-06 10:05:00",
    "updated_at": "2026-08-06 10:05:00",
    "deleted_at": null,
    "hash": "yd6er2o0pz12v6lgnw54qmv7"
  }
}
```

## Mark notifications unread

Mark matching [Notifications](/api-reference/notifications) for a `ChatMessage` unread for the current authenticated [User](/api-reference/users).

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/chat/messages/{chatMessage}/notifications/unread`

**Route Parameters**

| Parameter     | Type                  | Description             |
| ------------- | --------------------- | ----------------------- |
| `chatMessage` | `integer` \| `string` | ChatMessage ID or hash. |

**Behavior**

* The user must be allowed to view the non-deleted message.
* Every currently read chat notification that targets the message has its `read_at` value cleared. Existing `seen_at` values are preserved, and other notification types are unchanged.
* Repeating the request is idempotent. Response data is the number of notifications changed.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/chat/messages/1/notifications/unread', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": 1
}
```

## Reset

Reset supported relations of a `ChatMessage`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/chat/messages/{chatMessage}/reset`

**Route Parameters**

| Parameter     | Type                  | Description             |
| ------------- | --------------------- | ----------------------- |
| `chatMessage` | `integer` \| `string` | ChatMessage ID or hash. |

**Request Keys**

| Key         | Type      | Default | Description                                    |
| ----------- | --------- | ------- | ---------------------------------------------- |
| `reactions` | `boolean` | `false` | Set to `true` to remove all message reactions. |

**Behavior**

At least one resettable relation must be set to `true`. Only the conversation owner can reset message relations.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/chat/messages/1/reset', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'reactions' => true
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "chat_conversation_id": 1,
    "forwarded_id": null,
    "lang_id": "en-US",
    "type": "default",
    "relatable_type": null,
    "relatable_id": null,
    "text": "<p>Welcome to the project chat.</p>",
    "reading_confirmation": false,
    "created_at": "2026-08-06 09:35:00",
    "updated_at": "2026-08-06 09:35:00",
    "deleted_at": null,
    "hash": "vkn978r25g37681dqwzem4px"
  }
}
```

## Update

Update an existing `ChatMessage`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/chat/messages/{chatMessage}`

**Route Parameters**

| Parameter     | Type                  | Description             |
| ------------- | --------------------- | ----------------------- |
| `chatMessage` | `integer` \| `string` | ChatMessage ID or hash. |

**Request Keys**

| Key                    | Type                | Description                                                           |
| ---------------------- | ------------------- | --------------------------------------------------------------------- |
| `lang_id`              | `string`            | New language key for the translatable text.                           |
| `type`                 | `string`            | New [type](#types) of the `ChatMessage`.                              |
| `relatable_type`       | `string` \| `null`  | New [relatable type](#relatable-types). Required with `relatable_id`. |
| `relatable_id`         | `integer` \| `null` | New related entity ID. Required with `relatable_type`.                |
| `text`                 | `string` \| `null`  | New message content. Required when no related entity is supplied.     |
| `reading_confirmation` | `boolean`           | Whether users must explicitly confirm that they read the message.     |

**Behavior**

Only the message author can update it, and only within 15 minutes of creation while the conversation is unlocked. `user_id` and `chat_conversation_id` cannot be changed by this endpoint.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/chat/messages/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'text' => '<p>Welcome to the project launch chat.</p>'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "chat_conversation_id": 1,
    "forwarded_id": null,
    "lang_id": "en-US",
    "type": "default",
    "relatable_type": null,
    "relatable_id": null,
    "text": "<p>Welcome to the project launch chat.</p>",
    "reading_confirmation": false,
    "created_at": "2026-08-06 09:35:00",
    "updated_at": "2026-08-06 09:45:00",
    "deleted_at": null,
    "hash": "vkn978r25g37681dqwzem4px"
  }
}
```

## Delete

Delete an existing `ChatMessage`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/chat/messages/{chatMessage}`

**Route Parameters**

| Parameter     | Type                  | Description             |
| ------------- | --------------------- | ----------------------- |
| `chatMessage` | `integer` \| `string` | ChatMessage ID or hash. |

**Behavior**

Only the message author can delete it while the conversation is unlocked. Deleting a message clears its `text` before soft deletion.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/chat/messages/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# ChatUserConversationSettings

## Introduction

`ChatUserConversationSettings` store mute and archive settings for the current authenticated [User](/api-reference/users) and a [ChatConversation](/api-reference/chat/chat-conversations).

## Model Definition

**Relations**

| Key                | Relation                                                   | Type       | Relation Field(s)      |
| ------------------ | ---------------------------------------------------------- | ---------- | ---------------------- |
| `user`             | [User](/api-reference/users)                               | Belongs to | `user_id`              |
| `chatConversation` | [ChatConversation](/api-reference/chat/chat-conversations) | Belongs to | `chat_conversation_id` |

## Show by conversation

Show the current user's settings for a [ChatConversation](/api-reference/chat/chat-conversations).

**Definition**

<mark style="color:green;">`GET`</mark> `/api/chat/conversations/{chatConversation}/user-settings`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `chatConversation` | `integer` \| `string` | ChatConversation ID or hash. |

**Behavior**

The response is `null` when the current user has no settings for the conversation.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/chat/conversations/1/user-settings', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "chat_conversation_id": 1,
  "settings": {
    "muted_at": null,
    "mute_interval": null,
    "archived_at": "2026-08-06 09:40:00"
  },
  "created_at": "2026-08-06 09:40:00",
  "updated_at": "2026-08-06 09:40:00"
}
```

## Create or update

Create or update the current user's settings for a [ChatConversation](/api-reference/chat/chat-conversations).

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/chat/conversations/{chatConversation}/user-settings`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `chatConversation` | `integer` \| `string` | ChatConversation ID or hash. |

**Request Keys**

| Key                      | Type                 | Default | Description                                     |
| ------------------------ | -------------------- | ------- | ----------------------------------------------- |
| `settings`\*             | `object`             | -       | User-specific conversation settings.            |
| `settings.muted_at`      | `datetime` \| `null` | `null`  | Date from which the conversation is muted.      |
| `settings.mute_interval` | `string` \| `null`   | `null`  | ISO 8601 duration after which the mute expires. |
| `settings.archived_at`   | `datetime` \| `null` | `null`  | Date from which the conversation is archived.   |

Keys with `*` are required.

**Behavior**

Missing nested settings are stored as `null`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/chat/conversations/1/user-settings', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'settings' => [
            'muted_at' => '2026-08-06 09:40:00',
            'mute_interval' => 'PT8H',
            'archived_at' => null
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "chat_conversation_id": 1,
    "settings": {
      "muted_at": "2026-08-06 09:40:00",
      "mute_interval": "PT8H",
      "archived_at": null
    },
    "created_at": "2026-08-06 09:40:00",
    "updated_at": "2026-08-06 09:45:00"
  }
}
```

## Delete

Delete the current user's settings for a [ChatConversation](/api-reference/chat/chat-conversations).

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/chat/conversations/{chatConversation}/user-settings`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `chatConversation` | `integer` \| `string` | ChatConversation ID or hash. |

**Behavior**

Deleting settings is idempotent. The endpoint returns a successful response with `data` set to `null` when no settings exist.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/chat/conversations/1/user-settings', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# ChatConversationEvents

## Introduction

`ChatConversationEvents` record changes to a [ChatConversation](/api-reference/chat/chat-conversations). Each event has a concrete [type](#types) that defines the shape of its `data` field.

## Model Definition

**Relations**

| Key                | Relation                                                   | Type       | Relation Field(s)      |
| ------------------ | ---------------------------------------------------------- | ---------- | ---------------------- |
| `user`             | [User](/api-reference/users)                               | Belongs to | `user_id`              |
| `chatConversation` | [ChatConversation](/api-reference/chat/chat-conversations) | Belongs to | `chat_conversation_id` |

**Types**

| Type                | `data` shape                                                                     |
| ------------------- | -------------------------------------------------------------------------------- |
| `created`           | Empty object.                                                                    |
| `updated`           | `old` and `new` objects containing changed fields.                               |
| `deleted`           | Empty object.                                                                    |
| `titleChanged`      | `old` and `new` title values.                                                    |
| `lockedAtChanged`   | `old` and `new` datetime or `null` values.                                       |
| `departmentAdded`   | `targetable_type` is `department`; `targetable_id` is the integer Department ID. |
| `departmentRemoved` | `targetable_type` is `department`; `targetable_id` is the integer Department ID. |
| `userAdded`         | `targetable_type` is `user`; `targetable_id` is the integer User ID.             |
| `userLeft`          | `targetable_type` is `user`; `targetable_id` is the integer User ID.             |
| `userRemoved`       | `targetable_type` is `user`; `targetable_id` is the integer User ID.             |

Changing a conversation title or `locked_at` creates an `updated` event in addition to the corresponding `titleChanged` or `lockedAtChanged` event.

## List by conversation

List `ChatConversationEvents` for a [ChatConversation](/api-reference/chat/chat-conversations).

**Definition**

<mark style="color:green;">`GET`</mark> `/api/chat/conversations/{chatConversation}/events`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `chatConversation` | `integer` \| `string` | ChatConversation ID or hash. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/chat/conversations/1/events', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "chat_conversation_id": 1,
    "type": "titleChanged",
    "data": {
      "old": "Launch",
      "new": "Project launch"
    },
    "created_at": "2026-08-06 09:32:00",
    "updated_at": "2026-08-06 09:32:00"
  },
  {
    "id": 2,
    "user_id": 4,
    "chat_conversation_id": 1,
    "type": "lockedAtChanged",
    "data": {
      "old": null,
      "new": "2026-08-06 10:00:00"
    },
    "created_at": "2026-08-06 09:34:00",
    "updated_at": "2026-08-06 09:34:00"
  }
]
```

## Show

Show one `ChatConversationEvent`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/chat/conversations/events/{chatConversationEvent}`

**Route Parameters**

| Parameter               | Type      | Description               |
| ----------------------- | --------- | ------------------------- |
| `chatConversationEvent` | `integer` | ChatConversationEvent ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/chat/conversations/events/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "chat_conversation_id": 1,
  "type": "titleChanged",
  "data": {
    "old": "Launch",
    "new": "Project launch"
  },
  "created_at": "2026-08-06 09:32:00",
  "updated_at": "2026-08-06 09:32:00"
}
```


# ChatMessageSeenUsers

## Introduction

When a [User](/api-reference/users) has seen a [ChatMessage](/api-reference/chat/chat-messages), intratool tracks this as a `ChatMessageSeenUser`.

## Model Definition

**Relations**

| Key           | Relation                                         | Type       | Relation Field(s) |
| ------------- | ------------------------------------------------ | ---------- | ----------------- |
| `user`        | [User](/api-reference/users)                     | Belongs to | `user_id`         |
| `chatMessage` | [ChatMessage](/api-reference/chat/chat-messages) | Belongs to | `chat_message_id` |

## List by message

List visible `ChatMessageSeenUsers` for a [ChatMessage](/api-reference/chat/chat-messages).

**Definition**

<mark style="color:green;">`GET`</mark> `/api/chat/messages/{chatMessage}/seen-users`

**Route Parameters**

| Parameter     | Type                  | Description             |
| ------------- | --------------------- | ----------------------- |
| `chatMessage` | `integer` \| `string` | ChatMessage ID or hash. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/chat/messages/1/seen-users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "chat_message_id": 1,
    "created_at": "2026-08-06 09:36:00"
  },
  {
    "id": 2,
    "user_id": 4,
    "chat_message_id": 1,
    "created_at": "2026-08-06 09:37:00"
  }
]
```

## Create by conversation

Create `ChatMessageSeenUsers` for all unseen messages in a [ChatConversation](/api-reference/chat/chat-conversations).

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/chat/conversations/{chatConversation}/messages/seen-users`

**Route Parameters**

| Parameter          | Type                  | Description                  |
| ------------------ | --------------------- | ---------------------------- |
| `chatConversation` | `integer` \| `string` | ChatConversation ID or hash. |

**Behavior**

The authenticated user is used automatically. Existing records are not duplicated, and notifications for the affected messages are marked as read.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/chat/conversations/1/messages/seen-users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": [
    {
      "id": 1,
      "user_id": 3,
      "chat_message_id": 1,
      "created_at": "2026-08-06 09:36:00"
    }
  ]
}
```

## Create by message

Create a `ChatMessageSeenUser` for the current authenticated [User](/api-reference/users) and a [ChatMessage](/api-reference/chat/chat-messages).

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/chat/messages/{chatMessage}/seen-users`

**Route Parameters**

| Parameter     | Type                  | Description             |
| ------------- | --------------------- | ----------------------- |
| `chatMessage` | `integer` \| `string` | ChatMessage ID or hash. |

**Behavior**

The operation is idempotent and marks notifications for the message as read.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/chat/messages/1/seen-users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "chat_message_id": 1,
    "created_at": "2026-08-06 09:36:00"
  }
}
```


# Surveys

[Surveys](/api-reference/surveys/surveys) define the question, targeting, participation rules, result visibility, and available time window. Their selectable choices are represented by [SurveyAnswerOptions](/api-reference/surveys/survey-answer-options).

A submission creates a [SurveyVote](/api-reference/surveys/survey-votes). Its chosen options are stored as read-only [SurveyVoteSelectedAnswerOptions](/api-reference/surveys/survey-vote-selected-answer-options), which also provide aggregate counts.

Survey visibility uses [Targetables](/introduction/resource-capabilities/targetables); administration can additionally use [Entity Permissions](/introduction/resource-capabilities/entity-permissions). Survey text supports [Translations](/introduction/resource-capabilities/translations), and survey links support [URL Context](/introduction/resource-capabilities/url-context).


# Surveys

## Introduction

`Surveys` collect one or more selections from targeted users and departments.

The `description` field uses the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`survey`

**Computed Properties**

* `hash` - Hashed survey ID.
* `url` - Stable survey URL.
* `answer_options_summary` - Vote count per answer option when explicitly appended.
* `current_vote` - Current user's vote when explicitly appended.

**Capabilities**

* [Targetables](/introduction/resource-capabilities/targetables) - Explicit users and departments or a supported targetable parent define eligible voters.
* [Entity Permissions](/introduction/resource-capabilities/entity-permissions) - Direct administration grants supplement module permissions.
* [URL Context](/introduction/resource-capabilities/url-context) - Survey URLs resolve to access-checked context.
* [Translations](/introduction/resource-capabilities/translations) - Localizes `title` and `description`; examples use `en-US`.

**Relations**

| Key                   | Relation                                                            | Type            | Relation Field(s)                       |
| --------------------- | ------------------------------------------------------------------- | --------------- | --------------------------------------- |
| `user`                | [User](/api-reference/users)                                        | Belongs to      | `user_id`                               |
| `folder`              | [Folder](/api-reference/folders)                                    | Belongs to      | `folder_id`                             |
| `targetable`          | Supported targetable entity                                         | Morph to        | `targetable_type`, `targetable_id`      |
| `users`               | [Users](/api-reference/users)                                       | Belongs to many | `survey_user`                           |
| `departments`         | [Departments](/api-reference/departments)                           | Belongs to many | `department_survey`                     |
| `surveyAnswerOptions` | [SurveyAnswerOptions](/api-reference/surveys/survey-answer-options) | Has many        | `survey_id`                             |
| `surveyVotes`         | [SurveyVotes](/api-reference/surveys/survey-votes)                  | Has many        | `survey_id`                             |
| `currentVote`         | [SurveyVote](/api-reference/surveys/survey-votes)                   | Has one         | `survey_id`, `department_id`, `user_id` |
| `entityPermissions`   | [EntityPermissions](/api-reference/entity-permissions)              | Morph many      | `target_entity`, `target_entity_id`     |

**Types**

* `singleChoice` - Allows one selected answer option per vote.
* `multipleChoice` - Allows multiple selected answer options per vote.

**Show Votes Types**

* `none` - Does not expose vote results to voters.
* `always` - Exposes vote results while the survey is available.
* `after_vote` - Exposes vote results after the current voter has submitted a vote.
* `after_survey_end` - Exposes vote results after the survey has ended.

**Targetable Types**

* `chatMessage` - Targets the audience of a [ChatMessage](/api-reference/chat/chat-messages).
* `infoboardPost` - Targets the audience of an [InfoboardPost](/api-reference/infoboard/infoboard-posts).
* `manualEntry` - Targets the audience of a [ManualEntry](/api-reference/manual/manual-entries).

## List

List visible `Surveys`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/surveys`

**Request Keys**

| Key         | Type      | Default           | Description                                                                            |
| ----------- | --------- | ----------------- | -------------------------------------------------------------------------------------- |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.                                                      |
| `relations` | `string`  | Default relations | Pipe-separated relations to include.                                                   |
| `appends`   | `string`  | Default appends   | Pipe-separated computed properties such as `answer_options_summary` or `current_vote`. |
| `limit`     | `integer` | No limit          | Maximum number of surveys.                                                             |
| `filters`   | `object`  | No filters        | [Value filters](/introduction/query-manipulation/value-filters).                       |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/surveys', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 31,
    "user_id": 7,
    "folder_id": 10,
    "targetable_id": null,
    "targetable_type": null,
    "lang_id": "en-US",
    "type": "singleChoice",
    "active": true,
    "title": "Cafeteria satisfaction",
    "description": "<p>How satisfied are you with the cafeteria?</p>",
    "show_votes_type": "after_vote",
    "assign_mode": "any_of",
    "user_confirmation_required": false,
    "one_vote_per_department": false,
    "anonymous": false,
    "sort_number": 1,
    "start_date": "2026-08-01 00:00:00",
    "end_date": "2026-08-31 23:59:59",
    "hash": "qR"
  },
  {
    "id": 32,
    "user_id": 8,
    "folder_id": null,
    "targetable_id": 204,
    "targetable_type": "infoboardPost",
    "lang_id": "en-US",
    "type": "multipleChoice",
    "active": true,
    "title": "Choose the next training topics",
    "description": "<p>Select every topic that interests you.</p>",
    "show_votes_type": "after_survey_end",
    "assign_mode": null,
    "user_confirmation_required": true,
    "one_vote_per_department": true,
    "anonymous": true,
    "sort_number": 2,
    "start_date": null,
    "end_date": "2026-09-15 17:00:00",
    "hash": "rS"
  }
]
```

## Show

Show one visible `Survey`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/surveys/{survey}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |
| `appends`   | `string` | Default appends   | Pipe-separated computed properties.  |

**Behavior**

For anonymous surveys, voter-specific attributes and relations are removed from nested vote data.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/surveys/31', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 31,
  "user_id": 7,
  "folder_id": 10,
  "targetable_id": null,
  "targetable_type": null,
  "lang_id": "en-US",
  "type": "singleChoice",
  "active": true,
  "title": "Cafeteria satisfaction",
  "description": "<p>How satisfied are you with the cafeteria?</p>",
  "show_votes_type": "after_vote",
  "assign_mode": "any_of",
  "user_confirmation_required": false,
  "one_vote_per_department": false,
  "anonymous": false,
  "sort_number": 1,
  "start_date": "2026-08-01 00:00:00",
  "end_date": "2026-08-31 23:59:59",
  "hash": "qR"
}
```

## Admin: List

List all `Surveys` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/surveys`

**Request Keys**

| Key         | Type      | Default           | Description                                                                            |
| ----------- | --------- | ----------------- | -------------------------------------------------------------------------------------- |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.                                                      |
| `relations` | `string`  | Default relations | Pipe-separated relations to include.                                                   |
| `appends`   | `string`  | Default appends   | Pipe-separated computed properties such as `answer_options_summary` or `current_vote`. |
| `limit`     | `integer` | No limit          | Maximum number of surveys.                                                             |
| `filters`   | `object`  | No filters        | [Value filters](/introduction/query-manipulation/value-filters).                       |

**Behavior**

Administration results can include inactive and soft-deleted surveys.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/surveys', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 31,
    "user_id": 7,
    "folder_id": 10,
    "targetable_id": null,
    "targetable_type": null,
    "lang_id": "en-US",
    "type": "singleChoice",
    "active": true,
    "title": "Cafeteria satisfaction",
    "description": "<p>How satisfied are you with the cafeteria?</p>",
    "show_votes_type": "after_vote",
    "assign_mode": "any_of",
    "user_confirmation_required": false,
    "one_vote_per_department": false,
    "anonymous": false,
    "sort_number": 1,
    "start_date": "2026-08-01 00:00:00",
    "end_date": "2026-08-31 23:59:59",
    "hash": "qR"
  },
  {
    "id": 32,
    "user_id": 8,
    "folder_id": null,
    "targetable_id": 204,
    "targetable_type": "infoboardPost",
    "lang_id": "en-US",
    "type": "multipleChoice",
    "active": false,
    "title": "Choose the next training topics",
    "description": "<p>Select every topic that interests you.</p>",
    "show_votes_type": "after_survey_end",
    "assign_mode": null,
    "user_confirmation_required": true,
    "one_vote_per_department": true,
    "anonymous": true,
    "sort_number": 2,
    "start_date": null,
    "end_date": "2026-09-15 17:00:00",
    "hash": "rS",
    "deleted_at": "2026-08-02 09:00:00"
  }
]
```

## Admin: Show

Show one `Survey` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/surveys/{survey}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |
| `appends`   | `string` | Default appends   | Pipe-separated computed properties.  |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/surveys/32', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 32,
  "user_id": 8,
  "folder_id": null,
  "targetable_id": 204,
  "targetable_type": "infoboardPost",
  "lang_id": "en-US",
  "type": "multipleChoice",
  "active": true,
  "title": "Choose the next training topics",
  "description": "<p>Select every topic that interests you.</p>",
  "show_votes_type": "after_survey_end",
  "assign_mode": null,
  "user_confirmation_required": true,
  "one_vote_per_department": true,
  "anonymous": true,
  "sort_number": 2,
  "start_date": null,
  "end_date": "2026-09-15 17:00:00",
  "hash": "rS"
}
```

## Admin: Create

Create a new `Survey`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/surveys`

**Request Keys**

| Key                          | Type                 | Default                 | Description                                                                                 |
| ---------------------------- | -------------------- | ----------------------- | ------------------------------------------------------------------------------------------- |
| `lang_id`                    | `string`             | System language         | Language key. Use `en-US` for this example.                                                 |
| `type`\*                     | `string`             | -                       | Selects one of the supported [Types](#types).                                               |
| `title`\*                    | `string`             | -                       | Survey title.                                                                               |
| `description`                | `string` \| `null`   | `null`                  | HTML description.                                                                           |
| `active`                     | `boolean`            | `false`                 | Whether the survey can be answered.                                                         |
| `show_votes_type`            | `string`             | System default          | Selects one of the supported [Show Votes Types](#show-votes-types).                         |
| `assign_mode`                | `string` \| `null`   | Default assignment mode | [Assignment mode](/introduction/assignment-and-targeting); ignored for a targetable parent. |
| `user_ids`                   | `array` \| `null`    | `[]`                    | Directly targeted user IDs.                                                                 |
| `department_ids`             | `array` \| `null`    | `[]`                    | Directly targeted department IDs.                                                           |
| `targetable_type`            | `string` \| `null`   | `null`                  | Supported targetable morph alias.                                                           |
| `targetable_id`              | `integer` \| `null`  | `null`                  | Targetable entity ID.                                                                       |
| `user_confirmation_required` | `boolean`            | `false`                 | Require confirmed user identity.                                                            |
| `one_vote_per_department`    | `boolean`            | `false`                 | Limit voting by department instead of user.                                                 |
| `anonymous`                  | `boolean`            | `false`                 | Suppress voter information in permitted responses.                                          |
| `start_date`                 | `datetime` \| `null` | `null`                  | Voting start.                                                                               |
| `end_date`                   | `datetime` \| `null` | `null`                  | Voting end.                                                                                 |
| `folder_id`                  | `integer` \| `null`  | `null`                  | Folder ID.                                                                                  |
| `sort_number`                | `integer`            | Auto                    | Display order.                                                                              |
| `entity_permissions`         | `object`             | Unrestricted            | [Entity Permissions](/introduction/resource-capabilities/entity-permissions).               |

Keys with `*` are required for creation.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/surveys', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'type' => 'singleChoice',
        'title' => 'Cafeteria satisfaction',
        'description' => '<p>How satisfied are you with the cafeteria?</p>',
        'active' => true,
        'show_votes_type' => 'after_vote',
        'assign_mode' => 'any_of',
        'user_ids' => [7, 8],
        'department_ids' => [6, 9],
        'user_confirmation_required' => false,
        'one_vote_per_department' => false,
        'anonymous' => false,
        'start_date' => '2026-08-01 00:00:00',
        'end_date' => '2026-08-31 23:59:59',
        'folder_id' => 10,
        'sort_number' => 1
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 31,
    "user_id": 7,
    "folder_id": 10,
    "targetable_id": null,
    "targetable_type": null,
    "lang_id": "en-US",
    "type": "singleChoice",
    "active": true,
    "title": "Cafeteria satisfaction",
    "description": "<p>How satisfied are you with the cafeteria?</p>",
    "show_votes_type": "after_vote",
    "assign_mode": "any_of",
    "user_confirmation_required": false,
    "one_vote_per_department": false,
    "anonymous": false,
    "sort_number": 1,
    "start_date": "2026-08-01 00:00:00",
    "end_date": "2026-08-31 23:59:59",
    "hash": "qR"
  }
}
```

## Admin: Update

Update an existing `Survey`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/surveys/{survey}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Request Keys**

| Key                          | Type                 | Default                 | Description                                                                                 |
| ---------------------------- | -------------------- | ----------------------- | ------------------------------------------------------------------------------------------- |
| `lang_id`                    | `string`             | System language         | Language key. Use `en-US` for this example.                                                 |
| `type`\*                     | `string`             | -                       | Selects one of the supported [Types](#types).                                               |
| `title`\*                    | `string`             | -                       | Survey title.                                                                               |
| `description`                | `string` \| `null`   | `null`                  | HTML description.                                                                           |
| `active`                     | `boolean`            | `false`                 | Whether the survey can be answered.                                                         |
| `show_votes_type`            | `string`             | System default          | Selects one of the supported [Show Votes Types](#show-votes-types).                         |
| `assign_mode`                | `string` \| `null`   | Default assignment mode | [Assignment mode](/introduction/assignment-and-targeting); ignored for a targetable parent. |
| `user_ids`                   | `array` \| `null`    | `[]`                    | Directly targeted user IDs.                                                                 |
| `department_ids`             | `array` \| `null`    | `[]`                    | Directly targeted department IDs.                                                           |
| `targetable_type`            | `string` \| `null`   | `null`                  | Supported targetable morph alias.                                                           |
| `targetable_id`              | `integer` \| `null`  | `null`                  | Targetable entity ID.                                                                       |
| `user_confirmation_required` | `boolean`            | `false`                 | Require confirmed user identity.                                                            |
| `one_vote_per_department`    | `boolean`            | `false`                 | Limit voting by department instead of user.                                                 |
| `anonymous`                  | `boolean`            | `false`                 | Suppress voter information in permitted responses.                                          |
| `start_date`                 | `datetime` \| `null` | `null`                  | Voting start.                                                                               |
| `end_date`                   | `datetime` \| `null` | `null`                  | Voting end.                                                                                 |
| `folder_id`                  | `integer` \| `null`  | `null`                  | Folder ID.                                                                                  |
| `sort_number`                | `integer`            | Auto                    | Display order.                                                                              |
| `entity_permissions`         | `object`             | Unrestricted            | [Entity Permissions](/introduction/resource-capabilities/entity-permissions).               |

**Behavior**

After the first vote, configured immutable attributes such as survey type cannot be changed. Use Reset when those settings must change and existing votes may be removed.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/surveys/31', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'title' => 'Cafeteria and catering satisfaction',
        'description' => '<p>How satisfied are you with food services?</p>',
        'end_date' => '2026-09-07 23:59:59',
        'user_ids' => [7, 8],
        'department_ids' => [6, 9]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 31,
    "user_id": 7,
    "folder_id": 10,
    "targetable_id": null,
    "targetable_type": null,
    "lang_id": "en-US",
    "type": "singleChoice",
    "active": true,
    "title": "Cafeteria and catering satisfaction",
    "description": "<p>How satisfied are you with food services?</p>",
    "show_votes_type": "after_vote",
    "assign_mode": "any_of",
    "user_confirmation_required": false,
    "one_vote_per_department": false,
    "anonymous": false,
    "sort_number": 1,
    "start_date": "2026-08-01 00:00:00",
    "end_date": "2026-09-07 23:59:59",
    "hash": "qR"
  }
}
```

## Admin: Reset

Reset an existing `Survey`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/surveys/{survey}/reset`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Request Keys**

| Key                          | Type                 | Default                 | Description                                                                                 |
| ---------------------------- | -------------------- | ----------------------- | ------------------------------------------------------------------------------------------- |
| `lang_id`                    | `string`             | System language         | Language key. Use `en-US` for this example.                                                 |
| `type`\*                     | `string`             | -                       | Selects one of the supported [Types](#types).                                               |
| `title`\*                    | `string`             | -                       | Survey title.                                                                               |
| `description`                | `string` \| `null`   | `null`                  | HTML description.                                                                           |
| `active`                     | `boolean`            | `false`                 | Whether the survey can be answered.                                                         |
| `show_votes_type`            | `string`             | System default          | Selects one of the supported [Show Votes Types](#show-votes-types).                         |
| `assign_mode`                | `string` \| `null`   | Default assignment mode | [Assignment mode](/introduction/assignment-and-targeting); ignored for a targetable parent. |
| `user_ids`                   | `array` \| `null`    | `[]`                    | Directly targeted user IDs.                                                                 |
| `department_ids`             | `array` \| `null`    | `[]`                    | Directly targeted department IDs.                                                           |
| `targetable_type`            | `string` \| `null`   | `null`                  | Supported targetable morph alias.                                                           |
| `targetable_id`              | `integer` \| `null`  | `null`                  | Targetable entity ID.                                                                       |
| `user_confirmation_required` | `boolean`            | `false`                 | Require confirmed user identity.                                                            |
| `one_vote_per_department`    | `boolean`            | `false`                 | Limit voting by department instead of user.                                                 |
| `anonymous`                  | `boolean`            | `false`                 | Suppress voter information in permitted responses.                                          |
| `start_date`                 | `datetime` \| `null` | `null`                  | Voting start.                                                                               |
| `end_date`                   | `datetime` \| `null` | `null`                  | Voting end.                                                                                 |
| `folder_id`                  | `integer` \| `null`  | `null`                  | Folder ID.                                                                                  |
| `sort_number`                | `integer`            | Auto                    | Display order.                                                                              |
| `entity_permissions`         | `object`             | Unrestricted            | [Entity Permissions](/introduction/resource-capabilities/entity-permissions).               |

**Behavior**

Applies the supplied changes and soft-deletes every existing vote for the survey. Omitted settings keep their current values.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/surveys/31/reset', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'type' => 'multipleChoice',
        'show_votes_type' => 'after_survey_end',
        'anonymous' => true,
        'user_ids' => [7, 8],
        'department_ids' => [6, 9]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 31,
    "user_id": 7,
    "folder_id": 10,
    "targetable_id": null,
    "targetable_type": null,
    "lang_id": "en-US",
    "type": "multipleChoice",
    "active": true,
    "title": "Cafeteria satisfaction",
    "description": "<p>How satisfied are you with the cafeteria?</p>",
    "show_votes_type": "after_survey_end",
    "assign_mode": "any_of",
    "user_confirmation_required": false,
    "one_vote_per_department": false,
    "anonymous": true,
    "sort_number": 1,
    "start_date": "2026-08-01 00:00:00",
    "end_date": "2026-08-31 23:59:59",
    "hash": "qR"
  }
}
```

## Admin: Delete

Delete an existing `Survey`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/surveys/{survey}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/surveys/31', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```

## Admin: List Targeted Users

List users targeted by one `Survey`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/surveys/{survey}/targeted/users`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Request Keys**

| Key       | Type      | Default    | Description                            |
| --------- | --------- | ---------- | -------------------------------------- |
| `selects` | `string`  | All fields | Comma-separated user fields to return. |
| `limit`   | `integer` | No limit   | Maximum number of users.               |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/surveys/31/targeted/users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 7,
    "firstname": "Ada",
    "lastname": "Lovelace",
    "email": "ada@example.com",
    "active": true
  },
  {
    "id": 8,
    "firstname": "Grace",
    "lastname": "Hopper",
    "email": "grace@example.com",
    "active": true
  }
]
```

## Admin: List Targeted Departments

List departments targeted by one `Survey`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/surveys/{survey}/targeted/departments`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Request Keys**

| Key       | Type      | Default    | Description                                  |
| --------- | --------- | ---------- | -------------------------------------------- |
| `selects` | `string`  | All fields | Comma-separated department fields to return. |
| `limit`   | `integer` | No limit   | Maximum number of departments.               |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/surveys/31/targeted/departments', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 6,
    "name": "Engineering",
    "active": true
  },
  {
    "id": 9,
    "name": "Product",
    "active": true
  }
]
```


# SurveyAnswerOptions

## Introduction

`SurveyAnswerOptions` are selectable choices belonging to a [Survey](/api-reference/surveys/surveys).

## Model Definition

**Alias**

`surveyAnswerOption`

**Capabilities**

* [Translations](/introduction/resource-capabilities/translations) - Localizes `title`; examples use `en-US`.

**Relations**

| Key      | Relation                                 | Type       | Relation Field(s) |
| -------- | ---------------------------------------- | ---------- | ----------------- |
| `user`   | [User](/api-reference/users)             | Belongs to | `user_id`         |
| `survey` | [Survey](/api-reference/surveys/surveys) | Belongs to | `survey_id`       |

## List by Survey

List visible `SurveyAnswerOptions` for one `Survey`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/surveys/{survey}/answer-options`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of answer options.    |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/surveys/31/answer-options', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 81,
    "user_id": 7,
    "survey_id": 31,
    "lang_id": "en-US",
    "title": "Very satisfied",
    "sort_number": 1
  },
  {
    "id": 82,
    "user_id": 8,
    "survey_id": 31,
    "lang_id": "en-US",
    "title": "Needs improvement",
    "sort_number": 2
  }
]
```

## Show

Show one visible `SurveyAnswerOption`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/surveys/{survey}/answer-options/{surveyAnswerOption}`

**Route Parameters**

| Parameter            | Type      | Description                               |
| -------------------- | --------- | ----------------------------------------- |
| `survey`             | `integer` | Survey ID.                                |
| `surveyAnswerOption` | `integer` | Answer option ID belonging to the survey. |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/surveys/31/answer-options/81', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 81,
  "user_id": 7,
  "survey_id": 31,
  "lang_id": "en-US",
  "title": "Very satisfied",
  "sort_number": 1
}
```

## Admin: List by Survey

List all `SurveyAnswerOptions` for one `Survey` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/surveys/{survey}/answer-options`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of answer options.    |

**Behavior**

Administration results can include soft-deleted answer options.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/surveys/31/answer-options', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 81,
    "user_id": 7,
    "survey_id": 31,
    "lang_id": "en-US",
    "title": "Very satisfied",
    "sort_number": 1
  },
  {
    "id": 82,
    "user_id": 8,
    "survey_id": 31,
    "lang_id": "en-US",
    "title": "Needs improvement",
    "sort_number": 2,
    "deleted_at": "2026-08-03 10:00:00"
  }
]
```

## Admin: Show

Show one `SurveyAnswerOption` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/surveys/{survey}/answer-options/{surveyAnswerOption}`

**Route Parameters**

| Parameter            | Type      | Description                               |
| -------------------- | --------- | ----------------------------------------- |
| `survey`             | `integer` | Survey ID.                                |
| `surveyAnswerOption` | `integer` | Answer option ID belonging to the survey. |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/surveys/31/answer-options/82', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 82,
  "user_id": 8,
  "survey_id": 31,
  "lang_id": "en-US",
  "title": "Needs improvement",
  "sort_number": 2
}
```

## Admin: Create

Create a new `SurveyAnswerOption`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/surveys/{survey}/answer-options`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Request Keys**

| Key           | Type      | Default         | Description                                 |
| ------------- | --------- | --------------- | ------------------------------------------- |
| `lang_id`     | `string`  | System language | Language key. Use `en-US` for this example. |
| `title`\*     | `string`  | -               | Answer option label.                        |
| `sort_number` | `integer` | Auto            | Order within the survey.                    |

Keys with `*` are required.

**Behavior**

Answer options cannot be added after the survey has received a vote.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/surveys/31/answer-options', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'title' => 'Needs improvement',
        'sort_number' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 82,
    "user_id": 8,
    "survey_id": 31,
    "lang_id": "en-US",
    "title": "Needs improvement",
    "sort_number": 2
  }
}
```

## Admin: Update

Update an existing `SurveyAnswerOption`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/surveys/{survey}/answer-options/{surveyAnswerOption}`

**Route Parameters**

| Parameter            | Type      | Description       |
| -------------------- | --------- | ----------------- |
| `survey`             | `integer` | Survey ID.        |
| `surveyAnswerOption` | `integer` | Answer option ID. |

**Request Keys**

| Key           | Type      | Default       | Description              |
| ------------- | --------- | ------------- | ------------------------ |
| `lang_id`     | `string`  | Current value | Language key.            |
| `title`       | `string`  | Current value | Answer option label.     |
| `sort_number` | `integer` | Current value | Order within the survey. |

**Behavior**

The title cannot be changed after the survey has received a vote; sorting remains independently validated.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/surveys/31/answer-options/82', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'title' => 'Could be improved',
        'sort_number' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 82,
    "user_id": 8,
    "survey_id": 31,
    "lang_id": "en-US",
    "title": "Could be improved",
    "sort_number": 2
  }
}
```

## Admin: Delete

Delete an existing `SurveyAnswerOption`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/surveys/{survey}/answer-options/{surveyAnswerOption}`

**Route Parameters**

| Parameter            | Type      | Description       |
| -------------------- | --------- | ----------------- |
| `survey`             | `integer` | Survey ID.        |
| `surveyAnswerOption` | `integer` | Answer option ID. |

**Behavior**

An answer option cannot be deleted after the survey has received a vote.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/surveys/31/answer-options/82', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# SurveyVotes

## Introduction

`SurveyVotes` record a user's submitted choices for a [Survey](/api-reference/surveys/surveys).

## Model Definition

**Alias**

`surveyVote`

**Relations**

| Key                     | Relation                                                                                      | Type       | Relation Field(s)       |
| ----------------------- | --------------------------------------------------------------------------------------------- | ---------- | ----------------------- |
| `user`                  | [User](/api-reference/users)                                                                  | Belongs to | `user_id`               |
| `groupAccountUser`      | [User](/api-reference/users)                                                                  | Belongs to | `group_account_user_id` |
| `department`            | [Department](/api-reference/departments)                                                      | Belongs to | `department_id`         |
| `survey`                | [Survey](/api-reference/surveys/surveys)                                                      | Belongs to | `survey_id`             |
| `selectedAnswerOptions` | [SurveyVoteSelectedAnswerOptions](/api-reference/surveys/survey-vote-selected-answer-options) | Has many   | `survey_vote_id`        |

## List by Survey

List visible `SurveyVotes` for one `Survey`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/surveys/{survey}/votes`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of votes.             |

**Behavior**

For anonymous surveys, voter identity fields and relations are removed. Visibility also follows the survey's `show_votes_type`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/surveys/31/votes', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 121,
    "user_id": 7,
    "group_account_user_id": null,
    "department_id": 6,
    "survey_id": 31,
    "selected_answer_options": [
      {
        "id": 501,
        "survey_vote_id": 121,
        "survey_answer_option_id": 81
      }
    ],
    "created_at": "2026-08-06 12:00:00",
    "updated_at": "2026-08-06 12:00:00"
  },
  {
    "id": 122,
    "user_id": 8,
    "group_account_user_id": 9,
    "department_id": 9,
    "survey_id": 31,
    "selected_answer_options": [
      {
        "id": 502,
        "survey_vote_id": 122,
        "survey_answer_option_id": 82
      }
    ],
    "created_at": "2026-08-06 12:15:00",
    "updated_at": "2026-08-06 12:15:00"
  }
]
```

## Show Current

Show the current user's `SurveyVote` for one `Survey`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/surveys/{survey}/votes/current`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/surveys/31/votes/current', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 121,
  "user_id": 7,
  "group_account_user_id": null,
  "department_id": 6,
  "survey_id": 31,
  "selected_answer_options": [
    {
      "id": 501,
      "survey_vote_id": 121,
      "survey_answer_option_id": 81
    }
  ],
  "created_at": "2026-08-06 12:00:00",
  "updated_at": "2026-08-06 12:00:00"
}
```

## Show

Show one visible `SurveyVote`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/surveys/{survey}/votes/{surveyVote}`

**Route Parameters**

| Parameter    | Type      | Description                      |
| ------------ | --------- | -------------------------------- |
| `survey`     | `integer` | Survey ID.                       |
| `surveyVote` | `integer` | Vote ID belonging to the survey. |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/surveys/31/votes/121', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 121,
  "user_id": 7,
  "group_account_user_id": null,
  "department_id": 6,
  "survey_id": 31,
  "selected_answer_options": [
    {
      "id": 501,
      "survey_vote_id": 121,
      "survey_answer_option_id": 81
    }
  ],
  "created_at": "2026-08-06 12:00:00",
  "updated_at": "2026-08-06 12:00:00"
}
```

## Create

Create a new `SurveyVote`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/surveys/{survey}/votes`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Request Keys**

| Key                          | Type    | Default | Description                                                                             |
| ---------------------------- | ------- | ------- | --------------------------------------------------------------------------------------- |
| `survey_answer_option_ids`\* | `array` | -       | Answer option IDs belonging to this survey. A single-choice survey accepts at most one. |

Keys with `*` are required.

**Behavior**

The authenticated user, real group-account user, department, and survey are assigned by the server. The example selection is returned in `selected_answer_options`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/surveys/31/votes', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'survey_answer_option_ids' => [81]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 121,
    "user_id": 7,
    "group_account_user_id": null,
    "department_id": 6,
    "survey_id": 31,
    "selected_answer_options": [
      {
        "id": 501,
        "survey_vote_id": 121,
        "survey_answer_option_id": 81
      }
    ],
    "created_at": "2026-08-06 12:00:00",
    "updated_at": "2026-08-06 12:00:00"
  }
}
```

## Delete

Delete an existing `SurveyVote`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/surveys/{survey}/votes/{surveyVote}`

**Route Parameters**

| Parameter    | Type      | Description |
| ------------ | --------- | ----------- |
| `survey`     | `integer` | Survey ID.  |
| `surveyVote` | `integer` | Vote ID.    |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/surveys/31/votes/121', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```

## Admin: List by Survey

List all `SurveyVotes` for one `Survey` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/surveys/{survey}/votes`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of votes.             |

**Behavior**

Administration results include soft-deleted votes when permitted; anonymous surveys still suppress voter identity.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/surveys/31/votes', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 121,
    "user_id": 7,
    "group_account_user_id": null,
    "department_id": 6,
    "survey_id": 31,
    "selected_answer_options": [
      {
        "id": 501,
        "survey_vote_id": 121,
        "survey_answer_option_id": 81
      }
    ],
    "created_at": "2026-08-06 12:00:00",
    "updated_at": "2026-08-06 12:00:00"
  },
  {
    "id": 122,
    "user_id": 8,
    "group_account_user_id": 9,
    "department_id": 9,
    "survey_id": 31,
    "selected_answer_options": [
      {
        "id": 502,
        "survey_vote_id": 122,
        "survey_answer_option_id": 82
      }
    ],
    "created_at": "2026-08-06 12:15:00",
    "updated_at": "2026-08-06 12:15:00",
    "deleted_at": "2026-08-06 13:00:00"
  }
]
```

## Admin: Show

Show one `SurveyVote` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/surveys/{survey}/votes/{surveyVote}`

**Route Parameters**

| Parameter    | Type      | Description                      |
| ------------ | --------- | -------------------------------- |
| `survey`     | `integer` | Survey ID.                       |
| `surveyVote` | `integer` | Vote ID belonging to the survey. |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/surveys/31/votes/122', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 122,
  "user_id": 8,
  "group_account_user_id": 9,
  "department_id": 9,
  "survey_id": 31,
  "selected_answer_options": [
    {
      "id": 502,
      "survey_vote_id": 122,
      "survey_answer_option_id": 82
    }
  ],
  "created_at": "2026-08-06 12:15:00",
  "updated_at": "2026-08-06 12:15:00"
}
```


# SurveyVoteSelectedAnswerOptions

## Introduction

`SurveyVoteSelectedAnswerOptions` connect a [SurveyVote](/api-reference/surveys/survey-votes) with its selected [SurveyAnswerOptions](/api-reference/surveys/survey-answer-options). They are read-only and created with a vote.

## Model Definition

**Alias**

`surveyVoteSelectedAnswerOption`

## List by Survey

List selected answer options for one `Survey`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/surveys/{survey}/selected-answer-options`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Request Keys**

| Key       | Type      | Default    | Description                       |
| --------- | --------- | ---------- | --------------------------------- |
| `selects` | `string`  | All fields | Comma-separated fields to return. |
| `limit`   | `integer` | No limit   | Maximum number of selections.     |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/surveys/31/selected-answer-options', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 501,
    "survey_vote_id": 121,
    "survey_answer_option_id": 81
  },
  {
    "id": 502,
    "survey_vote_id": 122,
    "survey_answer_option_id": 82
  }
]
```

## Summary by Survey

Summarize selected answer options for one `Survey`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/surveys/{survey}/selected-answer-options/summary`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `survey`  | `integer` | Survey ID.  |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/surveys/31/selected-answer-options/summary', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "survey_answer_option_id": 81,
    "count": 14
  },
  {
    "survey_answer_option_id": 82,
    "count": 6
  }
]
```


# Legal

[TermsOfUse](/api-reference/legal/terms-of-use) define the agreements presented to users. Authenticated users record their acceptance of a specific version through [TermsOfUseAcceptedUsers](/api-reference/legal/terms-of-use-accepted-users).

[PrivacyPolicies](/api-reference/legal/privacy-policies) and [Imprints](/api-reference/legal/imprints) provide the legal notices shown by intratool. Their management endpoints, together with the `TermsOfUse` management endpoints, use administration scope and require the [`settings-show` permission](/api-reference/permissions#available-permissions).


# TermsOfUse

## Introduction

`TermsOfUse` store versioned agreements that authenticated users can accept through [TermsOfUseAcceptedUsers](/api-reference/legal/terms-of-use-accepted-users).

The `text` field uses the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`termsOfUse`

**Capabilities**

* [Translations](/introduction/resource-capabilities/translations) - Localizes `text`; examples use `en-US`.

## Admin: List

List all `TermsOfUse` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/terms-of-use`

**Request Keys**

| Key      | Type      | Default          | Description                                                           |
| -------- | --------- | ---------------- | --------------------------------------------------------------------- |
| `select` | `string`  | All fields       | Comma-separated fields to return.                                     |
| `limit`  | `integer` | No limit         | Maximum number of records.                                            |
| `filter` | `object`  | No filters       | [Value filters](/introduction/query-manipulation/value-filters).      |
| `sort`   | `string`  | Repository order | Comma-separated fields; prefix a field with `-` for descending order. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/terms-of-use', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "lang_id": "en-US",
    "text": "<p>By using the employee portal, you agree to follow the information-security policy.</p>",
    "created_at": "2026-08-01 09:00:00"
  },
  {
    "id": 2,
    "user_id": 4,
    "lang_id": "en-US",
    "text": "<p>By using the employee portal, you agree to:</p><ul><li><p>Protect your access credentials.</p></li><li><p>Use company data only for authorized purposes.</p></li></ul>",
    "created_at": "2026-08-07 10:30:00"
  }
]
```

## Admin: Show

Show one `TermsOfUse` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/terms-of-use/{termsOfUse}`

**Route Parameters**

| Parameter    | Type      | Description      |
| ------------ | --------- | ---------------- |
| `termsOfUse` | `integer` | Terms of use ID. |

**Request Keys**

| Key      | Type     | Default    | Description                       |
| -------- | -------- | ---------- | --------------------------------- |
| `select` | `string` | All fields | Comma-separated fields to return. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/terms-of-use/2', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 2,
  "user_id": 4,
  "lang_id": "en-US",
  "text": "<p>By using the employee portal, you agree to:</p><ul><li><p>Protect your access credentials.</p></li><li><p>Use company data only for authorized purposes.</p></li></ul>",
  "created_at": "2026-08-07 10:30:00"
}
```

## Admin: Create

Create new `TermsOfUse`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/terms-of-use`

**Request Keys**

| Key       | Type     | Default         | Description                                                     |
| --------- | -------- | --------------- | --------------------------------------------------------------- |
| `lang_id` | `string` | System language | Language key. Use `en-US` for this example.                     |
| `text`\*  | `string` | -               | Agreement content in [Rich Text](/introduction/rich-text) HTML. |

Keys with `*` are required.

**Behavior**

The authenticated user is stored as the creating user.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/terms-of-use', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'text' => '<p>By using the employee portal, you agree to protect your access credentials.</p>'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "lang_id": "en-US",
    "text": "<p>By using the employee portal, you agree to protect your access credentials.</p>",
    "created_at": "2026-08-07 11:00:00"
  }
}
```

## Admin: Update

Update existing `TermsOfUse`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/terms-of-use/{termsOfUse}`

**Route Parameters**

| Parameter    | Type      | Description      |
| ------------ | --------- | ---------------- |
| `termsOfUse` | `integer` | Terms of use ID. |

**Request Keys**

| Key       | Type     | Description                                                     |
| --------- | -------- | --------------------------------------------------------------- |
| `lang_id` | `string` | Language key.                                                   |
| `text`    | `string` | Agreement content in [Rich Text](/introduction/rich-text) HTML. |

**Behavior**

The authenticated user replaces the creating user stored on the record.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/terms-of-use/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'text' => '<p>By using the employee portal, you agree to protect your access credentials and company data.</p>'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "lang_id": "en-US",
    "text": "<p>By using the employee portal, you agree to protect your access credentials and company data.</p>",
    "created_at": "2026-08-07 11:00:00"
  }
}
```

## Admin: Delete

Delete existing `TermsOfUse`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/terms-of-use/{termsOfUse}`

**Route Parameters**

| Parameter    | Type      | Description      |
| ------------ | --------- | ---------------- |
| `termsOfUse` | `integer` | Terms of use ID. |

**Behavior**

Deleting the record also deletes its acceptance records.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/terms-of-use/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# PrivacyPolicies

## Introduction

`PrivacyPolicies` define internal or external privacy notices as inline content or links.

The `text` field uses the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`privacyPolicy`

**Capabilities**

* [Translations](/introduction/resource-capabilities/translations) - Localizes `text`; examples use `en-US`.

## Admin: List

List all `PrivacyPolicies` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/privacy-policies`

**Request Keys**

| Key      | Type      | Default          | Description                                                           |
| -------- | --------- | ---------------- | --------------------------------------------------------------------- |
| `select` | `string`  | All fields       | Comma-separated fields to return.                                     |
| `limit`  | `integer` | No limit         | Maximum number of records.                                            |
| `filter` | `object`  | No filters       | [Value filters](/introduction/query-manipulation/value-filters).      |
| `sort`   | `string`  | Repository order | Comma-separated fields; prefix a field with `-` for descending order. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/privacy-policies', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "lang_id": "en-US",
    "link": null,
    "text": "<p>We process employee data to provide and secure the employee portal.</p>",
    "external": false,
    "created_at": "2026-08-01 09:00:00",
    "updated_at": null
  },
  {
    "id": 2,
    "user_id": 4,
    "lang_id": "en-US",
    "link": "https://www.example.com/privacy",
    "text": null,
    "external": true,
    "created_at": "2026-08-07 10:30:00",
    "updated_at": null
  }
]
```

## Admin: Show

Show one `PrivacyPolicy` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/privacy-policies/{privacyPolicy}`

**Route Parameters**

| Parameter       | Type      | Description        |
| --------------- | --------- | ------------------ |
| `privacyPolicy` | `integer` | Privacy policy ID. |

**Request Keys**

| Key      | Type     | Default    | Description                       |
| -------- | -------- | ---------- | --------------------------------- |
| `select` | `string` | All fields | Comma-separated fields to return. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/privacy-policies/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "lang_id": "en-US",
  "link": null,
  "text": "<p>We process employee data to provide and secure the employee portal.</p>",
  "external": false,
  "created_at": "2026-08-01 09:00:00",
  "updated_at": null
}
```

## Admin: Create

Create a new `PrivacyPolicy`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/privacy-policies`

**Request Keys**

| Key        | Type               | Default         | Description                                                  |
| ---------- | ------------------ | --------------- | ------------------------------------------------------------ |
| `lang_id`  | `string`           | System language | Language key. Use `en-US` for this example.                  |
| `text`     | `string` \| `null` | `null`          | Notice content in [Rich Text](/introduction/rich-text) HTML. |
| `link`     | `string` \| `null` | `null`          | URL that provides the notice.                                |
| `external` | `boolean`          | `false`         | Whether the notice is intended for external visitors.        |

**Behavior**

* At least one of `text` or `link` is required.
* When `link` has a value, the corresponding privacy notice redirects to that URL instead of rendering `text`.
* The authenticated user is stored as the creating user.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/privacy-policies', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'link' => 'https://www.example.com/privacy',
        'external' => true
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "lang_id": "en-US",
    "link": "https://www.example.com/privacy",
    "text": null,
    "external": true,
    "created_at": "2026-08-07 11:00:00",
    "updated_at": null
  }
}
```

## Admin: Update

Update an existing `PrivacyPolicy`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/privacy-policies/{privacyPolicy}`

**Route Parameters**

| Parameter       | Type      | Description        |
| --------------- | --------- | ------------------ |
| `privacyPolicy` | `integer` | Privacy policy ID. |

**Request Keys**

| Key        | Type               | Description                                                  |
| ---------- | ------------------ | ------------------------------------------------------------ |
| `lang_id`  | `string`           | Language key.                                                |
| `text`     | `string` \| `null` | Notice content in [Rich Text](/introduction/rich-text) HTML. |
| `link`     | `string` \| `null` | URL that provides the notice.                                |
| `external` | `boolean`          | Whether the notice is intended for external visitors.        |

**Behavior**

* Each update must provide at least one of `text` or `link`.
* When `link` has a value, the corresponding privacy notice redirects to that URL instead of rendering `text`.
* The authenticated user replaces the creating user stored on the record.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/privacy-policies/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'text' => '<p>We process visitor data to operate and secure our public website.</p>',
        'link' => null,
        'external' => true
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "lang_id": "en-US",
    "link": null,
    "text": "<p>We process visitor data to operate and secure our public website.</p>",
    "external": true,
    "created_at": "2026-08-07 11:00:00",
    "updated_at": null
  }
}
```

## Admin: Delete

Delete an existing `PrivacyPolicy`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/privacy-policies/{privacyPolicy}`

**Route Parameters**

| Parameter       | Type      | Description        |
| --------------- | --------- | ------------------ |
| `privacyPolicy` | `integer` | Privacy policy ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/privacy-policies/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# Imprints

## Introduction

`Imprints` provide an organization's legal disclosure as inline content or a link.

The `text` field uses the shared [Rich Text](/introduction/rich-text) HTML format.

## Model Definition

**Alias**

`imprint`

**Capabilities**

* [Translations](/introduction/resource-capabilities/translations) - Localizes `text`; examples use `en-US`.

## Admin: List

List all `Imprints` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/imprints`

**Request Keys**

| Key      | Type      | Default          | Description                                                           |
| -------- | --------- | ---------------- | --------------------------------------------------------------------- |
| `select` | `string`  | All fields       | Comma-separated fields to return.                                     |
| `limit`  | `integer` | No limit         | Maximum number of records.                                            |
| `filter` | `object`  | No filters       | [Value filters](/introduction/query-manipulation/value-filters).      |
| `sort`   | `string`  | Repository order | Comma-separated fields; prefix a field with `-` for descending order. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/imprints', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "lang_id": "en-US",
    "link": null,
    "text": "<p><strong>Example Company GmbH</strong><br>Example Street 1<br>10115 Berlin</p>",
    "created_at": "2026-08-01 09:00:00",
    "updated_at": null
  },
  {
    "id": 2,
    "user_id": 4,
    "lang_id": "en-US",
    "link": "https://www.example.com/legal/imprint",
    "text": null,
    "created_at": "2026-08-07 10:30:00",
    "updated_at": null
  }
]
```

## Admin: Show

Show one `Imprint` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/imprints/{imprint}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `imprint` | `integer` | Imprint ID. |

**Request Keys**

| Key      | Type     | Default    | Description                       |
| -------- | -------- | ---------- | --------------------------------- |
| `select` | `string` | All fields | Comma-separated fields to return. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/imprints/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 1,
  "user_id": 3,
  "lang_id": "en-US",
  "link": null,
  "text": "<p><strong>Example Company GmbH</strong><br>Example Street 1<br>10115 Berlin</p>",
  "created_at": "2026-08-01 09:00:00",
  "updated_at": null
}
```

## Admin: Create

Create a new `Imprint`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/imprints`

**Request Keys**

| Key       | Type               | Default         | Description                                                      |
| --------- | ------------------ | --------------- | ---------------------------------------------------------------- |
| `lang_id` | `string`           | System language | Language key. Use `en-US` for this example.                      |
| `text`    | `string` \| `null` | `null`          | Disclosure content in [Rich Text](/introduction/rich-text) HTML. |
| `link`    | `string` \| `null` | `null`          | URL that provides the disclosure.                                |

**Behavior**

* At least one of `text` or `link` is required.
* When `link` has a value, the imprint redirects to that URL instead of rendering `text`.
* The authenticated user is stored as the creating user.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/imprints', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'text' => '<p><strong>Example Company GmbH</strong><br>Example Street 1<br>10115 Berlin</p>'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "lang_id": "en-US",
    "link": null,
    "text": "<p><strong>Example Company GmbH</strong><br>Example Street 1<br>10115 Berlin</p>",
    "created_at": "2026-08-07 11:00:00",
    "updated_at": null
  }
}
```

## Admin: Update

Update an existing `Imprint`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/imprints/{imprint}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `imprint` | `integer` | Imprint ID. |

**Request Keys**

| Key       | Type               | Description                                                      |
| --------- | ------------------ | ---------------------------------------------------------------- |
| `lang_id` | `string`           | Language key.                                                    |
| `text`    | `string` \| `null` | Disclosure content in [Rich Text](/introduction/rich-text) HTML. |
| `link`    | `string` \| `null` | URL that provides the disclosure.                                |

**Behavior**

* Each update must provide at least one of `text` or `link`.
* When `link` has a value, the imprint redirects to that URL instead of rendering `text`.
* The authenticated user replaces the creating user stored on the record.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/imprints/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'link' => 'https://www.example.com/legal/imprint'
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 3,
    "user_id": 3,
    "lang_id": "en-US",
    "link": "https://www.example.com/legal/imprint",
    "text": "<p><strong>Example Company GmbH</strong><br>Example Street 1<br>10115 Berlin</p>",
    "created_at": "2026-08-07 11:00:00",
    "updated_at": null
  }
}
```

## Admin: Delete

Delete an existing `Imprint`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/imprints/{imprint}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `imprint` | `integer` | Imprint ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/imprints/3', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# TermsOfUseAcceptedUsers

## Introduction

`TermsOfUseAcceptedUsers` record that an authenticated [User](/api-reference/users) accepted a specific [TermsOfUse](/api-reference/legal/terms-of-use) version.

## Model Definition

**Attributes**

| Key               | Type                 | Description                            |
| ----------------- | -------------------- | -------------------------------------- |
| `id`              | `integer`            | Acceptance record ID.                  |
| `user_id`         | `integer`            | ID of the user who accepted the terms. |
| `terms_of_use_id` | `integer`            | Accepted terms of use ID.              |
| `created_at`      | `datetime` \| `null` | Time at which the terms were accepted. |

## List by TermsOfUse

List acceptance records for users who accepted one `TermsOfUse` version.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/terms-of-use-accepted-users/{termsOfUse}`

**Route Parameters**

| Parameter    | Type      | Description      |
| ------------ | --------- | ---------------- |
| `termsOfUse` | `integer` | Terms of use ID. |

**Behavior**

The response contains at most one acceptance record per `user_id`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/terms-of-use-accepted-users/1', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 1,
    "user_id": 3,
    "terms_of_use_id": 1,
    "created_at": "2026-08-07 09:15:00"
  },
  {
    "id": 2,
    "user_id": 4,
    "terms_of_use_id": 1,
    "created_at": "2026-08-07 10:45:00"
  }
]
```

## Accept

Accept one `TermsOfUse` version for the authenticated user.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/terms-of-use-accepted-users`

**Request Keys**

| Key                 | Type      | Default | Description                |
| ------------------- | --------- | ------- | -------------------------- |
| `terms_of_use_id`\* | `integer` | -       | Terms of use ID to accept. |

Keys with `*` are required.

**Behavior**

* The authenticated user's ID is stored as `user_id`.
* Repeating the request returns the existing acceptance record instead of creating a duplicate.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/terms-of-use-accepted-users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'terms_of_use_id' => 1
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 1,
    "user_id": 3,
    "terms_of_use_id": 1,
    "created_at": "2026-08-07 09:15:00"
  }
}
```


# Files

## Introduction

The Files endpoints return stored content, short-lived content URLs, and directory archives for [FilemanagerFiles](/api-reference/filemanager/filemanager-files) and [FilemanagerDirectories](/api-reference/filemanager/filemanager-directories).

Use the content URLs returned by the API instead of constructing encoded storage paths manually. Access is checked against the resolved file or directory. [SharedItems](/api-reference/shared-items) provide access-controlled links when content must be shared beyond its regular assignments.

## Show Content by Encoded Path

Show file content addressed by an encoded path.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/files/{path}`

**Route Parameters**

| Parameter | Type     | Description                                                     |
| --------- | -------- | --------------------------------------------------------------- |
| `path`    | `string` | Encoded storage directory followed by the URL-encoded filename. |

**Behavior**

* The response streams the stored content or redirects to a temporary storage URL, depending on the configured filesystem.
* `Content-Type` and `Content-Disposition` are derived from the stored file and resolved filename.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/files/jR8mK4/path-preview.txt', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```http
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline; filename="path-preview.txt"

path based preview
```

## Create Temporary Content URL

Create a temporary URL for file content addressed by an encoded path.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/temp/files/{path}`

**Route Parameters**

| Parameter | Type     | Description                                                     |
| --------- | -------- | --------------------------------------------------------------- |
| `path`    | `string` | Encoded storage directory followed by the URL-encoded filename. |

**Behavior**

The returned storage URL is URL-encoded and expires after 10 seconds. It is intended for short-lived handoff to third-party viewers.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/temp/files/kS9nL5/report.pdf', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
"https%3A%2F%2Ffiles.example.test%2Ffilemanager%2Ftemp%2Freport.pdf%3FX-Amz-Expires%3D10"
```

## Download Directory

Download one `FilemanagerDirectory` as an archive.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/filemanager/directories/{filemanagerDirectory}/download`

**Route Parameters**

| Parameter              | Type              | Description                                                                            |
| ---------------------- | ----------------- | -------------------------------------------------------------------------------------- |
| `filemanagerDirectory` | `integer or hash` | [FilemanagerDirectory](/api-reference/filemanager/filemanager-directories) ID or hash. |

**Request Keys**

| Key      | Type      | Default            | Description                                      |
| -------- | --------- | ------------------ | ------------------------------------------------ |
| `filter` | `object`  | No filters         | Filters applied to recursively selected files.   |
| `sort`   | `string`  | Repository default | File ordering used while building the archive.   |
| `limit`  | `integer` | No limit           | Maximum number of files included in the archive. |

**Behavior**

The endpoint streams a ZIP archive. Only files visible to the current user and matching the supplied result controls are included.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/filemanager/directories/17/download', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'filter' => ['extension' => 'pdf'],
        'sort' => 'name',
        'limit' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```http
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="reports.zip"

<binary ZIP data containing reports/alpha-report.pdf and reports/beta-report.pdf>
```


# Menu

[MenuItems](/api-reference/menu/menu-items) configure additional navigation entries, including translated labels, access restrictions, system-variable links, and iframe behavior.


# MenuItems

## Introduction

`MenuItems` configure additional navigation entries with translated labels, access restrictions, system-variable links, and iframe behavior.

## Model Definition

**Alias**

`menuItem`

**Capabilities**

* [Entity Permissions](/introduction/resource-capabilities/entity-permissions) - Restrict which users receive the item.
* [Translations](/introduction/resource-capabilities/translations) - Localizes `title`; examples use `en-US`.

## List

List visible `MenuItems`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/menu/items`

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of items.             |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/menu/items', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 15,
    "lang_id": "en-US",
    "icon_id": 44,
    "menu_identifier": "help-center",
    "type": "default",
    "title": "Help Center",
    "link": "https://help.example.com/users/{{ system.user.id }}",
    "allow_iframe_access": true,
    "iframe_access": true,
    "user_confirmation_required": false,
    "sort_number": 1
  },
  {
    "id": 16,
    "lang_id": "en-US",
    "icon_id": 51,
    "menu_identifier": "expense-tool",
    "type": "integration",
    "title": "Expense Tool",
    "link": "https://expenses.example.com/menu-items/{{ menuItem.id }}/users/{{ system.user.id }}",
    "allow_iframe_access": false,
    "iframe_access": false,
    "user_confirmation_required": true,
    "sort_number": 2
  }
]
```

## Show

Show one visible `MenuItem`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/menu/items/{menuItem}`

**Route Parameters**

| Parameter  | Type      | Description   |
| ---------- | --------- | ------------- |
| `menuItem` | `integer` | Menu item ID. |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/menu/items/15', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 15,
  "lang_id": "en-US",
  "icon_id": 44,
  "menu_identifier": "help-center",
  "type": "default",
  "title": "Help Center",
  "link": "https://help.example.com/users/{{ system.user.id }}",
  "allow_iframe_access": true,
  "iframe_access": true,
  "user_confirmation_required": false,
  "sort_number": 1
}
```

## Admin: List

List all `MenuItems` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/menu/items`

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of items.             |

**Behavior**

Administration results include soft-deleted items.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/menu/items', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 15,
    "lang_id": "en-US",
    "icon_id": 44,
    "menu_identifier": "help-center",
    "type": "default",
    "title": "Help Center",
    "link": "https://help.example.com/users/{{ system.user.id }}",
    "allow_iframe_access": true,
    "iframe_access": true,
    "user_confirmation_required": false,
    "sort_number": 1
  },
  {
    "id": 16,
    "lang_id": "en-US",
    "icon_id": 51,
    "menu_identifier": "expense-tool",
    "type": "integration",
    "title": "Expense Tool",
    "link": "https://expenses.example.com/menu-items/{{ menuItem.id }}/users/{{ system.user.id }}",
    "allow_iframe_access": false,
    "iframe_access": false,
    "user_confirmation_required": true,
    "sort_number": 2,
    "deleted_at": "2026-08-03 09:00:00"
  }
]
```

## Admin: Show

Show one `MenuItem` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/menu/items/{menuItem}`

**Route Parameters**

| Parameter  | Type      | Description   |
| ---------- | --------- | ------------- |
| `menuItem` | `integer` | Menu item ID. |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/menu/items/16', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 16,
  "lang_id": "en-US",
  "icon_id": 51,
  "menu_identifier": "expense-tool",
  "type": "integration",
  "title": "Expense Tool",
  "link": "https://expenses.example.com/menu-items/{{ menuItem.id }}/users/{{ system.user.id }}",
  "allow_iframe_access": false,
  "iframe_access": false,
  "user_confirmation_required": true,
  "sort_number": 2
}
```

## Admin: Create

Create a new `MenuItem`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/menu/items`

**Request Keys**

| Key                          | Type      | Default         | Description                                                                   |
| ---------------------------- | --------- | --------------- | ----------------------------------------------------------------------------- |
| `lang_id`                    | `string`  | System language | Language key. Use `en-US` for this example.                                   |
| `icon_id`\*                  | `integer` | -               | Icon ID.                                                                      |
| `menu_identifier`\*          | `string`  | -               | Stable client identifier.                                                     |
| `type`\*                     | `string`  | -               | Menu item type.                                                               |
| `title`\*                    | `string`  | -               | Visible label.                                                                |
| `link`\*                     | `string`  | -               | URL or [system-variable](/introduction/system-variables) template.            |
| `allow_iframe_access`        | `boolean` | `true`          | Allow eligible URLs to open inside intratool.                                 |
| `user_confirmation_required` | `boolean` | `false`         | Require confirmation before opening.                                          |
| `sort_number`                | `integer` | Auto            | Display order.                                                                |
| `entity_permissions`         | `object`  | Unrestricted    | [Entity Permissions](/introduction/resource-capabilities/entity-permissions). |

Keys with `*` are required.

**Behavior**

`iframe_access` is calculated by the server from `allow_iframe_access` and whether the resolved URL supports embedding. The example link contains all context used by the resolved navigation URL.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/menu/items', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'icon_id' => 51,
        'menu_identifier' => 'expense-tool',
        'type' => 'integration',
        'title' => 'Expense Tool',
        'link' => 'https://expenses.example.com/menu-items/{{ menuItem.id }}/users/{{ system.user.id }}',
        'allow_iframe_access' => false,
        'user_confirmation_required' => true,
        'sort_number' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 16,
    "lang_id": "en-US",
    "icon_id": 51,
    "menu_identifier": "expense-tool",
    "type": "integration",
    "title": "Expense Tool",
    "link": "https://expenses.example.com/menu-items/{{ menuItem.id }}/users/{{ system.user.id }}",
    "allow_iframe_access": false,
    "iframe_access": false,
    "user_confirmation_required": true,
    "sort_number": 2
  }
}
```

## Admin: Update

Update an existing `MenuItem`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/administration/menu/items/{menuItem}`

**Route Parameters**

| Parameter  | Type      | Description   |
| ---------- | --------- | ------------- |
| `menuItem` | `integer` | Menu item ID. |

**Request Keys**

| Key                          | Type      | Default       | Description                          |
| ---------------------------- | --------- | ------------- | ------------------------------------ |
| `lang_id`                    | `string`  | Current value | Language key.                        |
| `icon_id`                    | `integer` | Current value | Icon ID.                             |
| `menu_identifier`            | `string`  | Current value | Stable client identifier.            |
| `type`                       | `string`  | Current value | Menu item type.                      |
| `title`                      | `string`  | Current value | Visible label.                       |
| `link`                       | `string`  | Current value | URL or system-variable template.     |
| `allow_iframe_access`        | `boolean` | Current value | Allow eligible URLs in intratool.    |
| `user_confirmation_required` | `boolean` | Current value | Require confirmation before opening. |
| `sort_number`                | `integer` | Current value | Display order.                       |
| `entity_permissions`         | `object`  | Current value | Updated Entity Permissions.          |

**Behavior**

The server recalculates `iframe_access` whenever the link or iframe preference is updated.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/administration/menu/items/16', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'lang_id' => 'en-US',
        'title' => 'Travel and Expense Tool',
        'link' => 'https://expenses.example.com/users/{{ system.user.id }}',
        'allow_iframe_access' => true,
        'user_confirmation_required' => false
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 16,
    "lang_id": "en-US",
    "icon_id": 51,
    "menu_identifier": "expense-tool",
    "type": "integration",
    "title": "Travel and Expense Tool",
    "link": "https://expenses.example.com/users/{{ system.user.id }}",
    "allow_iframe_access": true,
    "iframe_access": false,
    "user_confirmation_required": false,
    "sort_number": 2
  }
}
```

## Admin: Delete

Delete an existing `MenuItem`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/menu/items/{menuItem}`

**Route Parameters**

| Parameter  | Type      | Description   |
| ---------- | --------- | ------------- |
| `menuItem` | `integer` | Menu item ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/menu/items/16', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# Layouts

[Layouts](/api-reference/layouts/layouts) belong to a supported layoutable entity and split its content into ordered [LayoutRows](/api-reference/layouts/layout-rows). Each row contains width-constrained [LayoutColumns](/api-reference/layouts/layout-columns).

[LayoutColumnLayoutElements](/api-reference/layouts/layout-column-layout-elements) attach polymorphic elements such as FormFields to one or more columns and store their column-specific order. Layouts, rows, and columns support [Translations](/introduction/resource-capabilities/translations) for their titles.


# Layouts

## Introduction

`Layouts` define a row-and-column structure for a supported layoutable entity.

## Model Definition

**Alias**

`layout`

**Capabilities**

* [Translations](/introduction/resource-capabilities/translations) - Localizes `title`; examples use `en-US`.

**Relations**

| Key          | Relation                                         | Type     | Relation Field(s)                  |
| ------------ | ------------------------------------------------ | -------- | ---------------------------------- |
| `layoutable` | Supported layoutable entity                      | Morph to | `layoutable_type`, `layoutable_id` |
| `layoutRows` | [LayoutRows](/api-reference/layouts/layout-rows) | Has many | `layout_id`                        |

## List

List `Layouts`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/layouts`

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of records.           |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/layouts', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 41,
    "title": "Main content",
    "layoutable_type": "form",
    "layoutable_id": 21,
    "lang_id": "en-US",
    "type": "default",
    "sort_number": 1
  },
  {
    "id": 42,
    "title": "Task details",
    "layoutable_type": "taskTemplate",
    "layoutable_id": 44,
    "lang_id": "en-US",
    "type": "default",
    "sort_number": 1
  }
]
```

## Show

Show one `Layout`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/layouts/{layout}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `layout`  | `integer` | Layout ID.  |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/layouts/41', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 41,
  "title": "Main content",
  "layoutable_type": "form",
  "layoutable_id": 21,
  "lang_id": "en-US",
  "type": "default",
  "sort_number": 1
}
```

## Create

Create a new `Layout`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/layouts`

**Request Keys**

| Key                 | Type               | Default         | Description                                 |
| ------------------- | ------------------ | --------------- | ------------------------------------------- |
| `title`             | `string` \| `null` | `null`          | Layout label.                               |
| `layoutable_type`\* | `string`           | -               | Supported layoutable morph alias.           |
| `layoutable_id`\*   | `integer`          | -               | Layoutable entity ID.                       |
| `lang_id`           | `string`           | System language | Language key. Use `en-US` for this example. |
| `type`\*            | `string`           | -               | Layout type. Currently `default`.           |
| `sort_number`       | `integer`          | Auto            | Order within the layoutable entity.         |

Keys with `*` are required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/layouts', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Task details',
        'layoutable_type' => 'taskTemplate',
        'layoutable_id' => 44,
        'lang_id' => 'en-US',
        'type' => 'default',
        'sort_number' => 1
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 42,
    "title": "Task details",
    "layoutable_type": "taskTemplate",
    "layoutable_id": 44,
    "lang_id": "en-US",
    "type": "default",
    "sort_number": 1
  }
}
```

## Update

Update an existing `Layout`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/layouts/{layout}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `layout`  | `integer` | Layout ID.  |

**Request Keys**

| Key               | Type               | Default       | Description                 |
| ----------------- | ------------------ | ------------- | --------------------------- |
| `title`           | `string` \| `null` | Current value | Layout label.               |
| `layoutable_type` | `string`           | Current value | New layoutable morph alias. |
| `layoutable_id`   | `integer`          | Current value | New layoutable ID.          |
| `lang_id`         | `string`           | Current value | Language key.               |
| `type`            | `string`           | Current value | Layout type.                |
| `sort_number`     | `integer`          | Current value | Display order.              |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/layouts/42', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Task overview',
        'lang_id' => 'en-US',
        'sort_number' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 42,
    "title": "Task overview",
    "layoutable_type": "taskTemplate",
    "layoutable_id": 44,
    "lang_id": "en-US",
    "type": "default",
    "sort_number": 2
  }
}
```

## Delete

Delete an existing `Layout`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/layouts/{layout}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `layout`  | `integer` | Layout ID.  |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/layouts/42', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# LayoutRows

## Introduction

`LayoutRows` divide a [Layout](/api-reference/layouts/layouts) vertically and contain [LayoutColumns](/api-reference/layouts/layout-columns).

## Model Definition

**Alias**

`layoutRow`

**Capabilities**

* [Translations](/introduction/resource-capabilities/translations) - Localizes `title`; examples use `en-US`.

**Relations**

| Key             | Relation                                               | Type       | Relation Field(s) |
| --------------- | ------------------------------------------------------ | ---------- | ----------------- |
| `layout`        | [Layout](/api-reference/layouts/layouts)               | Belongs to | `layout_id`       |
| `layoutColumns` | [LayoutColumns](/api-reference/layouts/layout-columns) | Has many   | `layout_row_id`   |

## List

List `LayoutRows`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/layouts/rows`

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of records.           |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/layouts/rows', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 61,
    "title": "Primary information",
    "layout_id": 41,
    "lang_id": "en-US",
    "top_separator": false,
    "bottom_separator": true,
    "sort_number": 1
  },
  {
    "id": 62,
    "title": "Additional details",
    "layout_id": 41,
    "lang_id": "en-US",
    "top_separator": true,
    "bottom_separator": false,
    "sort_number": 2
  }
]
```

## Show

Show one `LayoutRow`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/layouts/rows/{layoutRow}`

**Route Parameters**

| Parameter   | Type      | Description   |
| ----------- | --------- | ------------- |
| `layoutRow` | `integer` | LayoutRow ID. |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/layouts/rows/61', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 61,
  "title": "Primary information",
  "layout_id": 41,
  "lang_id": "en-US",
  "top_separator": false,
  "bottom_separator": true,
  "sort_number": 1
}
```

## Create

Create a new `LayoutRow`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/layouts/rows`

**Request Keys**

| Key                | Type               | Default         | Description                                 |
| ------------------ | ------------------ | --------------- | ------------------------------------------- |
| `title`            | `string` \| `null` | `null`          | Row label.                                  |
| `layout_id`\*      | `integer`          | -               | Parent Layout ID.                           |
| `lang_id`          | `string`           | System language | Language key. Use `en-US` for this example. |
| `top_separator`    | `boolean`          | `false`         | Draw a separator above the row.             |
| `bottom_separator` | `boolean`          | `false`         | Draw a separator below the row.             |
| `sort_number`      | `integer`          | Auto            | Order within the layout.                    |

Keys with `*` are required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/layouts/rows', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Additional details',
        'layout_id' => 41,
        'lang_id' => 'en-US',
        'top_separator' => true,
        'bottom_separator' => false,
        'sort_number' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 62,
    "title": "Additional details",
    "layout_id": 41,
    "lang_id": "en-US",
    "top_separator": true,
    "bottom_separator": false,
    "sort_number": 2
  }
}
```

## Update

Update an existing `LayoutRow`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/layouts/rows/{layoutRow}`

**Route Parameters**

| Parameter   | Type      | Description |
| ----------- | --------- | ----------- |
| `layoutRow` | `integer` | Row ID.     |

**Request Keys**

| Key                | Type               | Default       | Description              |
| ------------------ | ------------------ | ------------- | ------------------------ |
| `title`            | `string` \| `null` | Current value | Row label.               |
| `layout_id`        | `integer`          | Current value | Parent Layout ID.        |
| `lang_id`          | `string`           | Current value | Language key.            |
| `top_separator`    | `boolean`          | Current value | Separator above the row. |
| `bottom_separator` | `boolean`          | Current value | Separator below the row. |
| `sort_number`      | `integer`          | Current value | Order within the layout. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/layouts/rows/62', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Supporting details',
        'lang_id' => 'en-US',
        'top_separator' => false,
        'sort_number' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 62,
    "title": "Supporting details",
    "layout_id": 41,
    "lang_id": "en-US",
    "top_separator": false,
    "bottom_separator": false,
    "sort_number": 2
  }
}
```

## Delete

Delete an existing `LayoutRow`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/layouts/rows/{layoutRow}`

**Route Parameters**

| Parameter   | Type      | Description   |
| ----------- | --------- | ------------- |
| `layoutRow` | `integer` | LayoutRow ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/layouts/rows/62', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# LayoutColumns

## Introduction

`LayoutColumns` divide a [LayoutRow](/api-reference/layouts/layout-rows) horizontally and receive layout elements.

## Model Definition

**Alias**

`layoutColumn`

**Capabilities**

* [Translations](/introduction/resource-capabilities/translations) - Localizes `title`; examples use `en-US`.

**Relations**

| Key                  | Relation                                                                           | Type          | Relation Field(s)                                |
| -------------------- | ---------------------------------------------------------------------------------- | ------------- | ------------------------------------------------ |
| `layoutRow`          | [LayoutRow](/api-reference/layouts/layout-rows)                                    | Belongs to    | `layout_row_id`                                  |
| `layoutableElements` | [LayoutColumnLayoutElements](/api-reference/layouts/layout-column-layout-elements) | Has many      | `layout_column_layout_elements.layout_column_id` |
| `formFields`         | [FormFields](/api-reference/forms/form-fields)                                     | Morph to many | `layout_column_layout_element`                   |

## List

List `LayoutColumns`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/layouts/columns`

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of records.           |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/layouts/columns', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 71,
    "title": "Contact details",
    "width": 8,
    "layout_row_id": 61,
    "lang_id": "en-US",
    "top_separator": false,
    "right_separator": true,
    "bottom_separator": false,
    "left_separator": false,
    "sort_number": 1
  },
  {
    "id": 72,
    "title": "Owner",
    "width": 4,
    "layout_row_id": 61,
    "lang_id": "en-US",
    "top_separator": false,
    "right_separator": false,
    "bottom_separator": false,
    "left_separator": true,
    "sort_number": 2
  }
]
```

## Show

Show one `LayoutColumn`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/layouts/columns/{layoutColumn}`

**Route Parameters**

| Parameter      | Type      | Description      |
| -------------- | --------- | ---------------- |
| `layoutColumn` | `integer` | LayoutColumn ID. |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/layouts/columns/71', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 71,
  "title": "Contact details",
  "width": 8,
  "layout_row_id": 61,
  "lang_id": "en-US",
  "top_separator": false,
  "right_separator": true,
  "bottom_separator": false,
  "left_separator": false,
  "sort_number": 1
}
```

## Create

Create a new `LayoutColumn`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/layouts/columns`

**Request Keys**

| Key               | Type               | Default         | Description                                                  |
| ----------------- | ------------------ | --------------- | ------------------------------------------------------------ |
| `title`           | `string` \| `null` | `null`          | Column label.                                                |
| `layout_row_id`\* | `integer`          | -               | Parent LayoutRow ID.                                         |
| `lang_id`         | `string`           | System language | Language key. Use `en-US` for this example.                  |
| `width`           | `integer`          | Available width | Column width validated against the other columns in the row. |
| `sort_number`     | `integer`          | Auto            | Order within the row.                                        |

Keys with `*` are required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/layouts/columns', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Owner',
        'layout_row_id' => 61,
        'lang_id' => 'en-US',
        'width' => 4,
        'sort_number' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 72,
    "title": "Owner",
    "width": 4,
    "layout_row_id": 61,
    "lang_id": "en-US",
    "top_separator": false,
    "right_separator": false,
    "bottom_separator": false,
    "left_separator": true,
    "sort_number": 2
  }
}
```

## Update

Update an existing `LayoutColumn`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/layouts/columns/{layoutColumn}`

**Route Parameters**

| Parameter      | Type      | Description |
| -------------- | --------- | ----------- |
| `layoutColumn` | `integer` | Column ID.  |

**Request Keys**

| Key             | Type               | Default       | Description                                          |
| --------------- | ------------------ | ------------- | ---------------------------------------------------- |
| `title`         | `string` \| `null` | Current value | Column label.                                        |
| `layout_row_id` | `integer`          | Current value | Parent LayoutRow ID.                                 |
| `lang_id`       | `string`           | Current value | Language key.                                        |
| `width`         | `integer`          | Current value | Width validated against the row's remaining columns. |
| `sort_number`   | `integer`          | Current value | Order within the row.                                |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/layouts/columns/72', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'title' => 'Responsible person',
        'lang_id' => 'en-US',
        'width' => 4,
        'sort_number' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 72,
    "title": "Responsible person",
    "width": 4,
    "layout_row_id": 61,
    "lang_id": "en-US",
    "top_separator": false,
    "right_separator": false,
    "bottom_separator": false,
    "left_separator": true,
    "sort_number": 2
  }
}
```

## Delete

Delete an existing `LayoutColumn`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/layouts/columns/{layoutColumn}`

**Route Parameters**

| Parameter      | Type      | Description      |
| -------------- | --------- | ---------------- |
| `layoutColumn` | `integer` | LayoutColumn ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/layouts/columns/72', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# LayoutColumnLayoutElements

## Introduction

`LayoutColumnLayoutElements` manage the polymorphic assignment of a layout element, such as a [FormField](/api-reference/forms/form-fields), to [LayoutColumns](/api-reference/layouts/layout-columns).

## Model Definition

**Alias**

`layoutColumnLayoutElement`

**Relations**

| Key             | Relation                                              | Type       | Relation Field(s)                          |
| --------------- | ----------------------------------------------------- | ---------- | ------------------------------------------ |
| `layoutElement` | Supported layout element                              | Morph to   | `layout_element_type`, `layout_element_id` |
| `layoutColumn`  | [LayoutColumn](/api-reference/layouts/layout-columns) | Belongs to | `layout_column_id`                         |

## List Columns for Element

List `LayoutColumns` attached to one layout element.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/layouts/elements/columns`

**Request Keys**

| Key                     | Type      | Default  | Description                           |
| ----------------------- | --------- | -------- | ------------------------------------- |
| `layout_element_type`\* | `string`  | -        | Supported layout-element morph alias. |
| `layout_element_id`\*   | `integer` | -        | Layout element ID.                    |
| `limit`                 | `integer` | No limit | Maximum number of assigned columns.   |

Keys with `*` are required.

**Behavior**

Send the required keys as query parameters. The PHP example includes them in the request's `query` option.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/layouts/elements/columns', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'layout_element_type' => 'formField',
        'layout_element_id' => 301,
        'limit' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 71,
    "title": "Contact details",
    "width": 8,
    "layout_row_id": 61,
    "lang_id": "en-US",
    "top_separator": false,
    "right_separator": true,
    "bottom_separator": false,
    "left_separator": false,
    "sort_number": 1,
    "pivot": {
      "id": 201,
      "layout_element_type": "formField",
      "layout_element_id": 301,
      "layout_column_id": 71,
      "sort_number": 1
    }
  },
  {
    "id": 72,
    "title": "Owner",
    "width": 4,
    "layout_row_id": 61,
    "lang_id": "en-US",
    "top_separator": false,
    "right_separator": false,
    "bottom_separator": false,
    "left_separator": true,
    "sort_number": 2,
    "pivot": {
      "id": 202,
      "layout_element_type": "formField",
      "layout_element_id": 301,
      "layout_column_id": 72,
      "sort_number": 2
    }
  }
]
```

## Attach Columns

Attach `LayoutColumns` to one layout element.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/layouts/elements/columns/attach`

**Request Keys**

| Key                     | Type      | Default | Description                                                                                   |
| ----------------------- | --------- | ------- | --------------------------------------------------------------------------------------------- |
| `layout_element_type`\* | `string`  | -       | Supported layout-element morph alias, for example `formField`.                                |
| `layout_element_id`\*   | `integer` | -       | Layout element ID.                                                                            |
| `layout_column_ids`\*   | `array`   | -       | Column records containing `layout_column_id` and optional pivot values such as `sort_number`. |

Keys with `*` are required.

**Behavior**

Existing pivots are updated; missing pivots are created. The response contains the requested columns still attached to the element.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/layouts/elements/columns/attach', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'layout_element_type' => 'formField',
        'layout_element_id' => 301,
        'layout_column_ids' => [
            [
                'layout_column_id' => 71,
                'sort_number' => 1
            ],
            [
                'layout_column_id' => 72,
                'sort_number' => 2
            ]
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 71,
    "title": "Contact details",
    "width": 8,
    "layout_row_id": 61,
    "lang_id": "en-US",
    "top_separator": false,
    "right_separator": true,
    "bottom_separator": false,
    "left_separator": false,
    "sort_number": 1,
    "pivot": {
      "id": 201,
      "layout_element_type": "formField",
      "layout_element_id": 301,
      "layout_column_id": 71,
      "sort_number": 1
    }
  },
  {
    "id": 72,
    "title": "Owner",
    "width": 4,
    "layout_row_id": 61,
    "lang_id": "en-US",
    "top_separator": false,
    "right_separator": false,
    "bottom_separator": false,
    "left_separator": true,
    "sort_number": 2,
    "pivot": {
      "id": 202,
      "layout_element_type": "formField",
      "layout_element_id": 301,
      "layout_column_id": 72,
      "sort_number": 2
    }
  }
]
```

## Sync Columns

Synchronize the `LayoutColumns` attached to one layout element.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/layouts/elements/columns/sync`

**Request Keys**

| Key                     | Type      | Default | Description                                                                                   |
| ----------------------- | --------- | ------- | --------------------------------------------------------------------------------------------- |
| `layout_element_type`\* | `string`  | -       | Supported layout-element morph alias, for example `formField`.                                |
| `layout_element_id`\*   | `integer` | -       | Layout element ID.                                                                            |
| `layout_column_ids`\*   | `array`   | -       | Column records containing `layout_column_id` and optional pivot values such as `sort_number`. |

Keys with `*` are required.

**Behavior**

Assignments not present in `layout_column_ids` are detached; supplied assignments are updated or created.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/layouts/elements/columns/sync', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'layout_element_type' => 'formField',
        'layout_element_id' => 301,
        'layout_column_ids' => [
            [
                'layout_column_id' => 72,
                'sort_number' => 1
            ]
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 72,
    "title": "Owner",
    "width": 4,
    "layout_row_id": 61,
    "lang_id": "en-US",
    "top_separator": false,
    "right_separator": false,
    "bottom_separator": false,
    "left_separator": true,
    "sort_number": 2,
    "pivot": {
      "id": 202,
      "layout_element_type": "formField",
      "layout_element_id": 301,
      "layout_column_id": 72,
      "sort_number": 1
    }
  }
]
```

## Detach Columns

Detach `LayoutColumns` from one layout element.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/layouts/elements/columns/detach`

**Request Keys**

| Key                     | Type      | Default | Description                                                                                   |
| ----------------------- | --------- | ------- | --------------------------------------------------------------------------------------------- |
| `layout_element_type`\* | `string`  | -       | Supported layout-element morph alias, for example `formField`.                                |
| `layout_element_id`\*   | `integer` | -       | Layout element ID.                                                                            |
| `layout_column_ids`\*   | `array`   | -       | Column records containing `layout_column_id` and optional pivot values such as `sort_number`. |

Keys with `*` are required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/layouts/elements/columns/detach', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'layout_element_type' => 'formField',
        'layout_element_id' => 301,
        'layout_column_ids' => [
            [
                'layout_column_id' => 72
            ]
        ]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[]
```


# Departments

## Introduction

`Departments` represent organizational units used by roles, user membership, targeting, permissions, and notifications.

## Model Definition

**Alias**

`department`

**Capabilities**

* [Targetables](/introduction/resource-capabilities/targetables) - Departments can be selected as explicit targets for supported resources.
* [URL Context](/introduction/resource-capabilities/url-context) - Department URLs resolve to access-checked context.

**Relations**

| Key             | Relation                                                             | Type                   | Relation Field(s)         |
| --------------- | -------------------------------------------------------------------- | ---------------------- | ------------------------- |
| `roles`         | [Roles](/api-reference/roles)                                        | Has many               | `department_id`           |
| `users`         | [Users](/api-reference/users)                                        | Has many through roles | Role membership           |
| `attributeSets` | [AttributeSets](/api-reference/additional-attributes/attribute-sets) | Morph to many          | Target entity type and ID |

## List

List visible `Departments`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/departments`

**Request Keys**

| Key         | Type      | Default           | Description                               |
| ----------- | --------- | ----------------- | ----------------------------------------- |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.         |
| `relations` | `string`  | Default relations | Pipe-separated relations to include.      |
| `limit`     | `integer` | No limit          | Maximum number of departments.            |
| `filter`    | `object`  | No filters        | Filters, including tag-aware text search. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/departments', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 6,
    "name": "Engineering",
    "user_id": 7,
    "folder_id": 12,
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 09:00:00",
    "deleted_at": null
  },
  {
    "id": 9,
    "name": "Product",
    "user_id": 8,
    "folder_id": null,
    "created_at": "2026-08-06 09:05:00",
    "updated_at": "2026-08-06 09:05:00",
    "deleted_at": null
  }
]
```

## Show

Show one visible `Department`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/departments/{department}`

**Route Parameters**

| Parameter    | Type      | Description    |
| ------------ | --------- | -------------- |
| `department` | `integer` | Department ID. |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/departments/6', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 6,
  "name": "Engineering",
  "user_id": 7,
  "folder_id": 12,
  "created_at": "2026-08-06 09:00:00",
  "updated_at": "2026-08-06 09:00:00",
  "deleted_at": null
}
```

## Availability Information

Show availability information for `Departments`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/departments/availability-information`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/departments/availability-information', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "total_available": 20,
  "available": 14,
  "used": 6
}
```

## Create

Create a new `Department`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/departments`

**Request Keys**

| Key                        | Type                | Default                | Description                                                                                                                 |
| -------------------------- | ------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `name`\*                   | `string`            | -                      | Unique department name.                                                                                                     |
| `folder_id`                | `integer` \| `null` | `null`                 | Parent [Folder](/api-reference/folders) ID.                                                                                 |
| `notification_setting_ids` | `array`             | Current or no settings | Notification-setting IDs assigned to the department.                                                                        |
| `attribute_set_ids`        | `array`             | Current or no sets     | [AttributeSets](/api-reference/additional-attributes/attribute-sets) to attach; records may include `id` and `sort_number`. |
| Additional attribute keys  | `mixed`             | Defined by set         | Values for fields from the attached AttributeSets.                                                                          |

Keys with `*` are required.

**Behavior**

The authenticated user becomes `user_id`. The example attaches the department attribute set and supplies both values needed to materialize those additional attributes.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/departments', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'name' => 'Customer Success',
        'folder_id' => 12,
        'notification_setting_ids' => [14],
        'attribute_set_ids' => [[
            'id' => 31,
            'sort_number' => 1
        ]],
        'cost-center' => 'CS-410',
        'office-floor' => 4
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 10,
    "name": "Customer Success",
    "user_id": 7,
    "folder_id": 12,
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 09:00:00",
    "deleted_at": null
  }
}
```

## Update

Update an existing `Department`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/departments/{department}`

**Route Parameters**

| Parameter    | Type      | Description    |
| ------------ | --------- | -------------- |
| `department` | `integer` | Department ID. |

**Request Keys**

| Key                        | Type                | Default                | Description                                                                                                                 |
| -------------------------- | ------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `name`\*                   | `string`            | -                      | Unique department name.                                                                                                     |
| `folder_id`                | `integer` \| `null` | `null`                 | Parent [Folder](/api-reference/folders) ID.                                                                                 |
| `notification_setting_ids` | `array`             | Current or no settings | Notification-setting IDs assigned to the department.                                                                        |
| `attribute_set_ids`        | `array`             | Current or no sets     | [AttributeSets](/api-reference/additional-attributes/attribute-sets) to attach; records may include `id` and `sort_number`. |
| Additional attribute keys  | `mixed`             | Defined by set         | Values for fields from the attached AttributeSets.                                                                          |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/departments/10', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'name' => 'Customer Experience',
        'folder_id' => null,
        'notification_setting_ids' => [14],
        'attribute_set_ids' => [[
            'id' => 31,
            'sort_number' => 1
        ]],
        'cost-center' => 'CX-410',
        'office-floor' => 5
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 10,
    "name": "Customer Experience",
    "user_id": 7,
    "folder_id": null,
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 10:00:00",
    "deleted_at": null
  }
}
```

## Delete

Delete an existing `Department`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/departments/{department}`

**Route Parameters**

| Parameter    | Type      | Description    |
| ------------ | --------- | -------------- |
| `department` | `integer` | Department ID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/departments/10', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# Roles

## Introduction

`Roles` connect users to a primary department, module permissions, extended department access, and optional additional attributes.

## Model Definition

**Alias**

`role`

**Relations**

| Key                               | Relation                                                             | Type            | Relation Field(s)                        |
| --------------------------------- | -------------------------------------------------------------------- | --------------- | ---------------------------------------- |
| `department`                      | [Department](/api-reference/departments)                             | Belongs to      | `department_id`                          |
| `departments`                     | [Departments](/api-reference/departments)                            | Belongs to many | `department_role`                        |
| `infoboardDepartments`            | [Departments](/api-reference/departments)                            | Belongs to many | `infoboard_department_role`              |
| `quickTaskAssignmentsDepartments` | [Departments](/api-reference/departments)                            | Belongs to many | `quick_task_assignments_department_role` |
| `permissions`                     | [Permissions](/api-reference/permissions)                            | Belongs to many | `permission_role`                        |
| `users`                           | [Users](/api-reference/users)                                        | Has many        | `role_id`                                |
| `folder`                          | [Folder](/api-reference/folders)                                     | Belongs to      | `folder_id`                              |
| `attributeSets`                   | [AttributeSets](/api-reference/additional-attributes/attribute-sets) | Morph to many   | Target entity type and ID                |

## List

List visible `Roles`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/roles`

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of roles.             |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/roles', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 14,
    "name": "Engineering member",
    "active": true,
    "user_id": 7,
    "department_id": 6,
    "folder_id": 12,
    "location_key": "berlin",
    "sort_number": 1
  },
  {
    "id": 15,
    "name": "Product lead",
    "active": false,
    "user_id": 8,
    "department_id": 9,
    "folder_id": null,
    "location_key": "remote",
    "sort_number": 2
  }
]
```

## Show

Show one visible `Role`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/roles/{role}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `role`    | `integer` | Role ID.    |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/roles/14', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 14,
  "name": "Engineering member",
  "active": true,
  "user_id": 7,
  "department_id": 6,
  "folder_id": 12,
  "location_key": "berlin",
  "sort_number": 1
}
```

## Availability Information

Show availability information for `Roles`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/roles/availability-information`

**Request Keys**

| Key              | Type                                | Default         | Description                                       |
| ---------------- | ----------------------------------- | --------------- | ------------------------------------------------- |
| `department_ids` | `array or comma-separated integers` | All departments | Departments for which availability is calculated. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/roles/availability-information', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'department_ids' => [6, 9]
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "available_per_department": 5,
  "departments": [
    {
      "department_id": 6,
      "available": 3,
      "used": 2
    },
    {
      "department_id": 9,
      "available": 4,
      "used": 1
    }
  ]
}
```

## Create

Create a new `Role`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/roles`

**Request Keys**

| Key                                     | Type                | Default            | Description                                                                     |
| --------------------------------------- | ------------------- | ------------------ | ------------------------------------------------------------------------------- |
| `name`\*                                | `string`            | -                  | Unique role name.                                                               |
| `department_id`\*                       | `integer`           | -                  | Primary [Department](/api-reference/departments) ID.                            |
| `active`                                | `boolean`           | `true`             | Whether users may use the role.                                                 |
| `folder_id`                             | `integer` \| `null` | `null`             | Parent [Folder](/api-reference/folders) ID.                                     |
| `location_key`                          | `string` \| `null`  | `null`             | Optional location identifier.                                                   |
| `sort_number`                           | `integer`           | Auto               | Order within the folder.                                                        |
| `permission_ids`                        | `array`             | No permissions     | [Permission](/api-reference/permissions) IDs granted through the role.          |
| `department_ids`                        | `array`             | Primary department | Departments whose content the role may access.                                  |
| `infoboard_department_ids`              | `array`             | Primary department | Departments available for Infoboard targeting.                                  |
| `quick_task_assignments_department_ids` | `array`             | Primary department | Departments available for quick task assignments.                               |
| `attribute_set_ids`                     | `array`             | Current or no sets | [AttributeSets](/api-reference/additional-attributes/attribute-sets) to attach. |
| Additional attribute keys               | `mixed`             | Defined by set     | Values for attached AttributeFields.                                            |

Keys with `*` are required for creation.

**Behavior**

The authenticated user becomes `user_id`. The example supplies every relationship collection and additional-attribute value used to configure the new role.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/roles', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'name' => 'Customer Success lead',
        'department_id' => 6,
        'active' => true,
        'folder_id' => 12,
        'location_key' => 'berlin',
        'sort_number' => 3,
        'permission_ids' => [18, 24],
        'department_ids' => [6, 9],
        'infoboard_department_ids' => [6, 9],
        'quick_task_assignments_department_ids' => [6],
        'attribute_set_ids' => [[
            'id' => 32,
            'sort_number' => 1
        ]],
        'approval-limit' => 25000
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 16,
    "name": "Customer Success lead",
    "active": true,
    "user_id": 7,
    "department_id": 6,
    "folder_id": 12,
    "location_key": "berlin",
    "sort_number": 3
  }
}
```

## Update

Update an existing `Role`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/roles/{role}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `role`    | `integer` | Role ID.    |

**Request Keys**

| Key                                     | Type                | Default            | Description                                                                     |
| --------------------------------------- | ------------------- | ------------------ | ------------------------------------------------------------------------------- |
| `name`\*                                | `string`            | -                  | Unique role name.                                                               |
| `department_id`\*                       | `integer`           | -                  | Primary [Department](/api-reference/departments) ID.                            |
| `active`                                | `boolean`           | `true`             | Whether users may use the role.                                                 |
| `folder_id`                             | `integer` \| `null` | `null`             | Parent [Folder](/api-reference/folders) ID.                                     |
| `location_key`                          | `string` \| `null`  | `null`             | Optional location identifier.                                                   |
| `sort_number`                           | `integer`           | Auto               | Order within the folder.                                                        |
| `permission_ids`                        | `array`             | No permissions     | [Permission](/api-reference/permissions) IDs granted through the role.          |
| `department_ids`                        | `array`             | Primary department | Departments whose content the role may access.                                  |
| `infoboard_department_ids`              | `array`             | Primary department | Departments available for Infoboard targeting.                                  |
| `quick_task_assignments_department_ids` | `array`             | Primary department | Departments available for quick task assignments.                               |
| `attribute_set_ids`                     | `array`             | Current or no sets | [AttributeSets](/api-reference/additional-attributes/attribute-sets) to attach. |
| Additional attribute keys               | `mixed`             | Defined by set     | Values for attached AttributeFields.                                            |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/roles/16', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'name' => 'Customer Experience lead',
        'active' => true,
        'department_id' => 9,
        'permission_ids' => [18, 24, 27],
        'department_ids' => [6, 9],
        'infoboard_department_ids' => [9],
        'quick_task_assignments_department_ids' => [6, 9],
        'attribute_set_ids' => [[
            'id' => 32,
            'sort_number' => 1
        ]],
        'approval-limit' => 30000
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 16,
    "name": "Customer Experience lead",
    "active": true,
    "user_id": 7,
    "department_id": 9,
    "folder_id": 12,
    "location_key": "berlin",
    "sort_number": 3
  }
}
```

## Delete

Delete an existing `Role`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/roles/{role}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `role`    | `integer` | Role ID.    |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/roles/16', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# Permissions

## Introduction

`Permissions` are module-level abilities assigned to [Roles](/api-reference/roles). Use their IDs in a role's `permission_ids` request key.

The [Available Permissions](#available-permissions) catalog contains every configured permission.

## Model Definition

**Alias**

`permission`

**Attributes**

| Key           | Type      | Description                                                |
| ------------- | --------- | ---------------------------------------------------------- |
| `id`          | `integer` | Stable configured Permission ID.                           |
| `type`        | `string`  | Module group defined for the permission.                   |
| `name`        | `string`  | Permission key used by authorization checks.               |
| `sort_number` | `integer` | Global order derived from the permission definition order. |

**Available Permissions**

The following configured permissions are ordered by `sort_number`. Each row contains its stable ID, permission key, module type, derived sort number, and meaning.

| ID     | Key                                                          | Type                                | Sort  | Meaning                                     |
| ------ | ------------------------------------------------------------ | ----------------------------------- | ----- | ------------------------------------------- |
| `12`   | `infoboard-show`                                             | `infoboard`                         | `1`   | Infoboard                                   |
| `1`    | `infoboard-administration-rights`                            | `infoboard`                         | `2`   | Administration rights                       |
| `2`    | `infoboard-create-entries`                                   | `infoboard`                         | `3`   | Create entries                              |
| `19`   | `infoboard-see-all-entry-seen-users`                         | `infoboard`                         | `4`   | View all "Viewed users" for entries         |
| `20`   | `infoboard-see-all-entry-read-users`                         | `infoboard`                         | `5`   | View all "read users" for entries           |
| `3`    | `infoboard-edit-own-entries`                                 | `infoboard`                         | `6`   | Edit your own entries                       |
| `14`   | `infoboard-pin-own-entries`                                  | `infoboard`                         | `7`   | Attach your own entries                     |
| `15`   | `infoboard-schedule-own-entries`                             | `infoboard`                         | `8`   | Schedule your own entries                   |
| `21`   | `infoboard-reset-own-entries`                                | `infoboard`                         | `9`   | Reset statistics/comments for own entries   |
| `4`    | `infoboard-delete-own-entries`                               | `infoboard`                         | `10`  | Delete own entries                          |
| `5`    | `infoboard-edit-all-entries`                                 | `infoboard`                         | `11`  | Edit all entries                            |
| `16`   | `infoboard-pin-all-entries`                                  | `infoboard`                         | `12`  | Pin all entries                             |
| `17`   | `infoboard-schedule-all-entries`                             | `infoboard`                         | `13`  | Schedule all entries                        |
| `22`   | `infoboard-reset-all-entries`                                | `infoboard`                         | `14`  | Reset statistics/comments for all entries   |
| `6`    | `infoboard-delete-all-entries`                               | `infoboard`                         | `15`  | Delete all entries                          |
| `7`    | `infoboard-create-comments`                                  | `infoboard`                         | `16`  | Create comments                             |
| `8`    | `infoboard-edit-own-comments`                                | `infoboard`                         | `17`  | Edit your own comments                      |
| `9`    | `infoboard-delete-own-comments`                              | `infoboard`                         | `18`  | Delete own comments                         |
| `10`   | `infoboard-edit-all-comments`                                | `infoboard`                         | `19`  | Edit all comments                           |
| `11`   | `infoboard-delete-all-comments`                              | `infoboard`                         | `20`  | Delete all comments                         |
| `18`   | `infoboard-see-private-comments`                             | `infoboard`                         | `21`  | View private comments via "Extended access" |
| `23`   | `infoboard-show-administration`                              | `infoboard`                         | `22`  | Show administration                         |
| `24`   | `infoboard-create-channels`                                  | `infoboard`                         | `23`  | Create channels                             |
| `25`   | `infoboard-edit-own-channels`                                | `infoboard`                         | `24`  | Edit your own channels                      |
| `26`   | `infoboard-delete-own-channels`                              | `infoboard`                         | `25`  | Delete own channels                         |
| `27`   | `infoboard-edit-permitted-channels`                          | `infoboard`                         | `26`  | Edit shared channels                        |
| `28`   | `infoboard-delete-permitted-channels`                        | `infoboard`                         | `27`  | Delete shared channels                      |
| `29`   | `infoboard-edit-all-channels`                                | `infoboard`                         | `28`  | Edit all channels                           |
| `30`   | `infoboard-delete-all-channels`                              | `infoboard`                         | `29`  | Delete all channels                         |
| `31`   | `infoboard-channels-create-folders`                          | `infoboard`                         | `30`  | Create folders in channels                  |
| `32`   | `infoboard-channels-edit-own-folders`                        | `infoboard`                         | `31`  | Edit own folders in channels                |
| `33`   | `infoboard-channels-delete-own-folders`                      | `infoboard`                         | `32`  | Delete own folders in channels              |
| `34`   | `infoboard-channels-edit-all-folders`                        | `infoboard`                         | `33`  | Edit all folders in channels                |
| `35`   | `infoboard-channels-delete-all-folders`                      | `infoboard`                         | `34`  | Delete all folders in channels              |
| `36`   | `infoboard-edit-department-settings`                         | `infoboard`                         | `35`  | Edit user group settings                    |
| `101`  | `manual-show`                                                | `manual`                            | `36`  | Manuals                                     |
| `100`  | `manual-administration-rights`                               | `manual`                            | `37`  | Administration rights                       |
| `108`  | `manual-show-on-menu`                                        | `manual`                            | `38`  | Show in menu                                |
| `111`  | `manual-show-administration`                                 | `manual`                            | `39`  | Show administration                         |
| `112`  | `manual-create-chapters`                                     | `manual`                            | `40`  | Create chapter                              |
| `114`  | `manual-edit-own-chapters`                                   | `manual`                            | `41`  | Edit your own chapters                      |
| `115`  | `manual-delete-own-chapters`                                 | `manual`                            | `42`  | Delete own chapters                         |
| `116`  | `manual-edit-permitted-chapters`                             | `manual`                            | `43`  | Edit released chapters                      |
| `117`  | `manual-delete-permitted-chapters`                           | `manual`                            | `44`  | Delete released chapters                    |
| `118`  | `manual-edit-all-chapters`                                   | `manual`                            | `45`  | Edit all chapters                           |
| `119`  | `manual-delete-all-chapters`                                 | `manual`                            | `46`  | Delete all chapters                         |
| `102`  | `manual-create-entries`                                      | `manual`                            | `47`  | Create entries                              |
| `125`  | `manual-see-all-entry-seen-users`                            | `manual`                            | `48`  | View all "Viewed users" for entries         |
| `126`  | `manual-see-all-entry-read-users`                            | `manual`                            | `49`  | View all "Read users" for entries           |
| `103`  | `manual-edit-own-entries`                                    | `manual`                            | `50`  | Edit your own entries                       |
| `109`  | `manual-reset-own-entries`                                   | `manual`                            | `51`  | Reset statistics for own entries            |
| `104`  | `manual-delete-own-entries`                                  | `manual`                            | `52`  | Delete your own entries                     |
| `121`  | `manual-edit-permitted-entries`                              | `manual`                            | `53`  | Edit released entries                       |
| `123`  | `manual-reset-permitted-entries`                             | `manual`                            | `54`  | Reset statistics for assigned entries       |
| `124`  | `manual-delete-permitted-entries`                            | `manual`                            | `55`  | Delete released entries                     |
| `105`  | `manual-edit-all-entries`                                    | `manual`                            | `56`  | Edit all entries                            |
| `110`  | `manual-reset-all-entries`                                   | `manual`                            | `57`  | Reset statistics for all entries            |
| `106`  | `manual-delete-all-entries`                                  | `manual`                            | `58`  | Delete all entries                          |
| `150`  | `contact-lists-show`                                         | `contact-lists`                     | `59`  | Contact lists                               |
| `151`  | `contact-lists-administration-rights`                        | `contact-lists`                     | `60`  | Administration rights                       |
| `152`  | `contact-lists-create-contact-lists`                         | `contact-lists`                     | `61`  | Create contact lists                        |
| `153`  | `contact-lists-edit-own-contact-lists`                       | `contact-lists`                     | `62`  | Edit your own contact lists                 |
| `154`  | `contact-lists-delete-own-contact-lists`                     | `contact-lists`                     | `63`  | Delete your own contact lists               |
| `155`  | `contact-lists-edit-all-contact-lists`                       | `contact-lists`                     | `64`  | Edit all contact lists                      |
| `156`  | `contact-lists-delete-all-contact-lists`                     | `contact-lists`                     | `65`  | Delete all contact lists                    |
| `157`  | `contact-lists-create-contacts`                              | `contact-lists`                     | `66`  | Create contacts                             |
| `158`  | `contact-lists-edit-own-contacts`                            | `contact-lists`                     | `67`  | Edit your own contacts                      |
| `159`  | `contact-lists-delete-own-contacts`                          | `contact-lists`                     | `68`  | Delete own contacts                         |
| `160`  | `contact-lists-edit-all-contacts`                            | `contact-lists`                     | `69`  | Edit all contacts                           |
| `161`  | `contact-lists-delete-all-contacts`                          | `contact-lists`                     | `70`  | Delete all contacts                         |
| `204`  | `tasks-show`                                                 | `tasks`                             | `71`  | Tasks                                       |
| `200`  | `tasks-administration-rights`                                | `tasks`                             | `72`  | Administration rights                       |
| `201`  | `tasks-show-administration`                                  | `tasks`                             | `73`  | Show administration                         |
| `205`  | `tasks-show-reports-group-account`                           | `tasks`                             | `74`  | Show reports for group account              |
| `202`  | `tasks-show-reports-users`                                   | `tasks`                             | `75`  | Display reports for users                   |
| `206`  | `tasks-reports-show-all`                                     | `tasks`                             | `76`  | Show all reports                            |
| `203`  | `tasks-show-automated-reports`                               | `tasks`                             | `77`  | Display automated reports                   |
| `250`  | `tasks-2-show`                                               | `tasks-2`                           | `78`  | Tasks 2.0                                   |
| `251`  | `tasks-2-administration-rights`                              | `tasks-2`                           | `79`  | Administration rights                       |
| `277`  | `tasks-2-show-on-menu`                                       | `tasks-2`                           | `80`  | Show in menu                                |
| `252`  | `tasks-2-show-administration`                                | `tasks-2`                           | `81`  | Show administration                         |
| `253`  | `tasks-2-create-templates`                                   | `tasks-2`                           | `82`  | Create templates                            |
| `254`  | `tasks-2-edit-own-templates`                                 | `tasks-2`                           | `83`  | Edit your own templates                     |
| `255`  | `tasks-2-delete-own-templates`                               | `tasks-2`                           | `84`  | Delete your own templates                   |
| `256`  | `tasks-2-edit-permitted-templates`                           | `tasks-2`                           | `85`  | Editing released templates                  |
| `257`  | `tasks-2-delete-permitted-templates`                         | `tasks-2`                           | `86`  | Delete released templates                   |
| `258`  | `tasks-2-edit-all-templates`                                 | `tasks-2`                           | `87`  | Edit all templates                          |
| `259`  | `tasks-2-delete-all-templates`                               | `tasks-2`                           | `88`  | Delete all templates                        |
| `260`  | `tasks-2-create-template-folders`                            | `tasks-2`                           | `89`  | Create template folder                      |
| `261`  | `tasks-2-edit-own-template-folders`                          | `tasks-2`                           | `90`  | Edit your own templates folder              |
| `262`  | `tasks-2-delete-own-template-folders`                        | `tasks-2`                           | `91`  | Delete own templates folder                 |
| `263`  | `tasks-2-edit-all-template-folders`                          | `tasks-2`                           | `92`  | Edit all templates folder                   |
| `264`  | `tasks-2-delete-all-template-folders`                        | `tasks-2`                           | `93`  | Delete all template folders                 |
| `265`  | `tasks-2-create-assignments`                                 | `tasks-2`                           | `94`  | Create assignments                          |
| `266`  | `tasks-2-edit-own-assignments`                               | `tasks-2`                           | `95`  | Edit your own assignments                   |
| `267`  | `tasks-2-delete-own-assignments`                             | `tasks-2`                           | `96`  | Delete your own assignments                 |
| `268`  | `tasks-2-edit-permitted-assignments`                         | `tasks-2`                           | `97`  | Edit released assignments                   |
| `269`  | `tasks-2-delete-permitted-assignments`                       | `tasks-2`                           | `98`  | Delete released assignments                 |
| `278`  | `tasks-2-create-quick-assignments`                           | `tasks-2`                           | `99`  | Create quick tasks                          |
| `270`  | `tasks-2-edit-all-assignments`                               | `tasks-2`                           | `100` | Edit all assignments                        |
| `271`  | `tasks-2-delete-all-assignments`                             | `tasks-2`                           | `101` | Delete all assignments                      |
| `272`  | `tasks-2-create-assignment-folders`                          | `tasks-2`                           | `102` | Create assignment folder                    |
| `273`  | `tasks-2-edit-own-assignment-folders`                        | `tasks-2`                           | `103` | Edit your own assignment folders            |
| `274`  | `tasks-2-delete-own-assignment-folders`                      | `tasks-2`                           | `104` | Delete own assignment folders               |
| `275`  | `tasks-2-edit-all-assignment-folders`                        | `tasks-2`                           | `105` | Edit all assignment folders                 |
| `276`  | `tasks-2-delete-all-assignment-folders`                      | `tasks-2`                           | `106` | Delete all assignment folders               |
| `306`  | `calendar-show`                                              | `calendar`                          | `107` | Calendar                                    |
| `300`  | `calendar-administration-rights`                             | `calendar`                          | `108` | Administration rights                       |
| `301`  | `calendar-create-events`                                     | `calendar`                          | `109` | Create Events                               |
| `302`  | `calendar-edit-own-events`                                   | `calendar`                          | `110` | Edit your own Events                        |
| `303`  | `calendar-delete-own-events`                                 | `calendar`                          | `111` | Delete your own Events                      |
| `304`  | `calendar-edit-all-events`                                   | `calendar`                          | `112` | Edit all Events                             |
| `305`  | `calendar-delete-all-events`                                 | `calendar`                          | `113` | Delete all Events                           |
| `411`  | `filemanager-show`                                           | `filemanager`                       | `114` | File manager                                |
| `400`  | `filemanager-administration-rights`                          | `filemanager`                       | `115` | Administration rights                       |
| `412`  | `filemanager-show-on-menu`                                   | `filemanager`                       | `116` | Show in menu                                |
| `401`  | `filemanager-upload-files`                                   | `filemanager`                       | `117` | Upload files                                |
| `402`  | `filemanager-edit-own-files`                                 | `filemanager`                       | `118` | Edit own files                              |
| `403`  | `filemanager-delete-own-files`                               | `filemanager`                       | `119` | Delete own files                            |
| `409`  | `filemanager-edit-all-files`                                 | `filemanager`                       | `120` | Edit all files                              |
| `410`  | `filemanager-delete-all-files`                               | `filemanager`                       | `121` | Delete all files                            |
| `404`  | `filemanager-create-directories`                             | `filemanager`                       | `122` | Create folder                               |
| `405`  | `filemanager-edit-own-directories`                           | `filemanager`                       | `123` | Edit your own folders                       |
| `406`  | `filemanager-delete-own-directories`                         | `filemanager`                       | `124` | Delete own folders                          |
| `407`  | `filemanager-edit-all-directories`                           | `filemanager`                       | `125` | Edit all folders                            |
| `408`  | `filemanager-delete-all-directories`                         | `filemanager`                       | `126` | Delete all folders                          |
| `450`  | `forms-show`                                                 | `forms`                             | `127` | Forms                                       |
| `451`  | `forms-administration-rights`                                | `forms`                             | `128` | Administration rights                       |
| `453`  | `forms-show-on-menu`                                         | `forms`                             | `129` | Show in menu                                |
| `452`  | `forms-show-administration`                                  | `forms`                             | `130` | Show administration                         |
| `454`  | `forms-messages-show-all`                                    | `forms`                             | `131` | View all sent forms                         |
| `455`  | `forms-create-forms`                                         | `forms`                             | `132` | Create forms                                |
| `456`  | `forms-edit-own-forms`                                       | `forms`                             | `133` | Edit your own forms                         |
| `457`  | `forms-delete-own-forms`                                     | `forms`                             | `134` | Delete own forms                            |
| `458`  | `forms-edit-permitted-forms`                                 | `forms`                             | `135` | Edit released forms                         |
| `459`  | `forms-delete-permitted-forms`                               | `forms`                             | `136` | Delete released forms                       |
| `460`  | `forms-edit-all-forms`                                       | `forms`                             | `137` | Edit all forms                              |
| `461`  | `forms-delete-all-forms`                                     | `forms`                             | `138` | Delete all forms                            |
| `462`  | `forms-create-folders`                                       | `forms`                             | `139` | Create folder                               |
| `463`  | `forms-edit-own-folders`                                     | `forms`                             | `140` | Edit own folders                            |
| `464`  | `forms-delete-own-folders`                                   | `forms`                             | `141` | Delete own folders                          |
| `465`  | `forms-edit-all-folders`                                     | `forms`                             | `142` | Edit all folders                            |
| `466`  | `forms-delete-all-folders`                                   | `forms`                             | `143` | Delete all folders                          |
| `467`  | `forms-messages-delete`                                      | `forms`                             | `144` | Delete sent forms                           |
| `468`  | `forms-message-confirmations-reset`                          | `forms`                             | `145` | Request confirmation of receipt again       |
| `1000` | `reports-show`                                               | `reports`                           | `146` | Reports                                     |
| `1001` | `reports-administration-rights`                              | `reports`                           | `147` | Administration rights                       |
| `1002` | `reports-show-administration`                                | `reports`                           | `148` | Show administration                         |
| `1200` | `chat-show`                                                  | `chat`                              | `149` | Messages                                    |
| `1201` | `chat-administration-rights`                                 | `chat`                              | `150` | Administration rights                       |
| `1202` | `chat-show-on-menu`                                          | `chat`                              | `151` | Show in menu                                |
| `1203` | `chat-show-administration`                                   | `chat`                              | `152` | Show administration                         |
| `1204` | `chat-create-group-conversation`                             | `chat`                              | `153` | Create group chat                           |
| `1205` | `chat-create-unrelated-conversation`                         | `chat`                              | `154` | Create chat without topic                   |
| `1206` | `chat-create-infoboard-post-related-conversation`            | `chat`                              | `155` | Create chat for infoboard entry             |
| `1207` | `chat-create-infoboard-comment-related-conversation`         | `chat`                              | `156` | Create chat for infoboard comment           |
| `1208` | `chat-create-form-related-conversation`                      | `chat`                              | `157` | Create chat to form                         |
| `1209` | `chat-create-form-message-related-conversation`              | `chat`                              | `158` | Create chat for sent form                   |
| `1210` | `chat-create-manual-chapter-related-conversation`            | `chat`                              | `159` | Chat to create manual chapter               |
| `1211` | `chat-create-manual-entry-related-conversation`              | `chat`                              | `160` | Create chat for manual entry                |
| `1212` | `chat-create-task-execution-related-conversation`            | `chat`                              | `161` | Create chat for task                        |
| `1213` | `chat-create-task-template-related-conversation`             | `chat`                              | `162` | Create chat for task template               |
| `1214` | `chat-create-task-template-composition-related-conversation` | `chat`                              | `163` | Chat to create task list                    |
| `1215` | `chat-create-task-assignment-related-conversation`           | `chat`                              | `164` | Create chat for task assignment             |
| `1216` | `chat-create-filemanager-directory-related-conversation`     | `chat`                              | `165` | Create chat for file manager folder         |
| `1217` | `chat-create-filemanager-file-related-conversation`          | `chat`                              | `166` | Create chat for file manager file           |
| `1218` | `chat-create-report-related-conversation`                    | `chat`                              | `167` | Chat to create report                       |
| `1219` | `chat-create-voice-message`                                  | `chat`                              | `168` | Create voice messages                       |
| `475`  | `dashboard-show`                                             | `dashboard`                         | `169` | Dashboard                                   |
| `476`  | `dashboard-administration-rights`                            | `dashboard`                         | `170` | Administration rights                       |
| `477`  | `dashboard-infoboard-item`                                   | `dashboard`                         | `171` | Show infoboard element                      |
| `478`  | `dashboard-tasks-item`                                       | `dashboard`                         | `172` | Show task element                           |
| `479`  | `dashboard-filemanager-item`                                 | `dashboard`                         | `173` | Show file manager element                   |
| `501`  | `settings-show`                                              | `settings`                          | `174` | Settings                                    |
| `500`  | `settings-administration-rights`                             | `settings`                          | `175` | Administration rights                       |
| `1600` | `translations-show`                                          | `translations`                      | `176` | Language                                    |
| `1601` | `translations-set-own-language`                              | `translations`                      | `177` | Set your own language                       |
| `1602` | `translations-request-automatic-translations`                | `translations`                      | `178` | Request automatic translations              |
| `701`  | `departments-show`                                           | `departments`                       | `179` | User groups                                 |
| `700`  | `departments-administration-rights`                          | `departments`                       | `180` | Administration rights                       |
| `801`  | `roles-show`                                                 | `roles`                             | `181` | Roles                                       |
| `800`  | `roles-administration-rights`                                | `roles`                             | `182` | Administration rights                       |
| `604`  | `users-show`                                                 | `users`                             | `183` | Users                                       |
| `600`  | `users-administration-rights`                                | `users`                             | `184` | Administration rights                       |
| `601`  | `users-create-users`                                         | `users`                             | `185` | Create user                                 |
| `602`  | `users-edit-users`                                           | `users`                             | `186` | Edit user                                   |
| `605`  | `users-create-login-tokens`                                  | `users`                             | `187` | Create auto-login URLs for users            |
| `603`  | `users-delete-users`                                         | `users`                             | `188` | Delete user                                 |
| `1500` | `surveys-show`                                               | `surveys`                           | `189` | Surveys                                     |
| `1501` | `surveys-administration-rights`                              | `surveys`                           | `190` | Administration rights                       |
| `1502` | `surveys-show-on-menu`                                       | `surveys`                           | `191` | Show in menu                                |
| `1503` | `surveys-show-administration`                                | `surveys`                           | `192` | Show administration                         |
| `1504` | `surveys-create-survey`                                      | `surveys`                           | `193` | Create survey                               |
| `1505` | `surveys-edit-own-survey`                                    | `surveys`                           | `194` | Edit your own surveys                       |
| `1506` | `surveys-delete-own-survey`                                  | `surveys`                           | `195` | Delete your own surveys                     |
| `1507` | `surveys-edit-permitted-survey`                              | `surveys`                           | `196` | Edit released surveys                       |
| `1508` | `surveys-delete-permitted-survey`                            | `surveys`                           | `197` | Delete released surveys                     |
| `1509` | `surveys-edit-all-survey`                                    | `surveys`                           | `198` | Edit all surveys                            |
| `1510` | `surveys-delete-all-survey`                                  | `surveys`                           | `199` | Delete all surveys                          |
| `1511` | `surveys-create-folders`                                     | `surveys`                           | `200` | Create folder                               |
| `1512` | `surveys-edit-own-folders`                                   | `surveys`                           | `201` | Edit your own folders                       |
| `1513` | `surveys-delete-own-folders`                                 | `surveys`                           | `202` | Delete own folders                          |
| `1514` | `surveys-edit-all-folders`                                   | `surveys`                           | `203` | Edit all folders                            |
| `1515` | `surveys-delete-all-folders`                                 | `surveys`                           | `204` | Delete all folders                          |
| `1400` | `shared-items-show`                                          | `shared-items`                      | `205` | Shared items                                |
| `1401` | `shared-items-administration-rights`                         | `shared-items`                      | `206` | Administration rights                       |
| `1402` | `shared-items-show-administration`                           | `shared-items`                      | `207` | Show administration                         |
| `13`   | `shared-items-create-infoboard-post-items`                   | `shared-items`                      | `208` | Share infoboard entries                     |
| `1404` | `shared-items-create-infoboard-comment-items`                | `shared-items`                      | `209` | Share infoboard comments                    |
| `113`  | `shared-items-create-manual-chapter-items`                   | `shared-items`                      | `210` | Share manual chapter                        |
| `107`  | `shared-items-create-manual-entry-items`                     | `shared-items`                      | `211` | Share manual entries                        |
| `307`  | `shared-items-create-calendar-event-items`                   | `shared-items`                      | `212` | Share calendar events                       |
| `414`  | `shared-items-create-filemanager-directory-items`            | `shared-items`                      | `213` | Share file manager folder                   |
| `413`  | `shared-items-create-filemanager-file-items`                 | `shared-items`                      | `214` | Share file manager files                    |
| `1405` | `shared-items-create-form-items`                             | `shared-items`                      | `215` | Share forms                                 |
| `1411` | `shared-items-create-form-message-items`                     | `shared-items`                      | `216` | Share form submissions                      |
| `1410` | `shared-items-create-task-execution-items`                   | `shared-items`                      | `217` | Share task executions                       |
| `1406` | `shared-items-edit-own-items`                                | `shared-items`                      | `218` | Edit your own shared items                  |
| `1407` | `shared-items-delete-own-items`                              | `shared-items`                      | `219` | Delete your own shared items                |
| `1408` | `shared-items-edit-all-items`                                | `shared-items`                      | `220` | Edit all shared items                       |
| `1409` | `shared-items-delete-all-items`                              | `shared-items`                      | `221` | Delete all shared items                     |
| `1700` | `additional-attributes-show`                                 | `additional-attributes`             | `222` | Additional attributes                       |
| `1701` | `additional-attributes-administration-rights`                | `additional-attributes`             | `223` | Administration rights                       |
| `900`  | `tags-show`                                                  | `tags`                              | `224` | Tags                                        |
| `901`  | `tags-administration-rights`                                 | `tags`                              | `225` | Administration rights                       |
| `650`  | `profile-edit-personal-information-show`                     | `profile-edit-personal-information` | `226` | Edit personal profile details               |
| `1100` | `code-scanner-show`                                          | `code-scanner`                      | `227` | Code scanner                                |
| `1101` | `code-scanner-show-on-header`                                | `code-scanner`                      | `228` | Show in header                              |
| `1102` | `code-scanner-show-on-menu`                                  | `code-scanner`                      | `229` | Show in menu                                |
| `1103` | `code-scanner-allow-external-redirects`                      | `code-scanner`                      | `230` | Allow external redirects                    |

## List

List available permissions.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/permissions`

**Request Keys**

| Key       | Type      | Default          | Description                           |
| --------- | --------- | ---------------- | ------------------------------------- |
| `selects` | `string`  | All fields       | Comma-separated fields to return.     |
| `limit`   | `integer` | No limit         | Maximum number of permissions.        |
| `filter`  | `object`  | No filters       | Value filters applied to permissions. |
| `sort`    | `string`  | Repository order | Sort expression.                      |

**Behavior**

The authenticated user requires the `roles-show` module permission.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/permissions', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'query' => [
        'filter' => [
            'id' => ['in' => '12,101']
        ],
        'sort' => 'sort_number',
        'limit' => 2
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 12,
    "type": "infoboard",
    "name": "infoboard-show",
    "sort_number": 1
  },
  {
    "id": 101,
    "type": "manual",
    "name": "manual-show",
    "sort_number": 36
  }
]
```

## Show

Show one permission.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/permissions/{permission}`

**Route Parameters**

| Parameter    | Type      | Description    |
| ------------ | --------- | -------------- |
| `permission` | `integer` | Permission ID. |

**Request Keys**

| Key       | Type     | Default    | Description                       |
| --------- | -------- | ---------- | --------------------------------- |
| `selects` | `string` | All fields | Comma-separated fields to return. |

**Behavior**

The authenticated user requires the `roles-show` module permission.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/permissions/101', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 101,
  "type": "manual",
  "name": "manual-show",
  "sort_number": 36
}
```


# Users

## Introduction

`Users` are personal or group accounts. They carry a role, a language, profile data, authentication settings, and the identity used by many other resources.

## Model Definition

**Alias**

`user`

**Required Profile Keys**

* `profile_picture` - Requires a profile picture.
* `street` - Requires a street address.
* `zipcode` - Requires a postal code.
* `city` - Requires a city.
* `email` - Requires an email address.
* `phone` - Requires a phone number.
* `birthdate` - Requires a date of birth.
* `gender` - Requires a gender value.

**Capabilities**

* [Targetables](/introduction/resource-capabilities/targetables) - Users can be selected as explicit targets for supported resources.
* [URL Context](/introduction/resource-capabilities/url-context) - User URLs resolve to access-checked context.
* [Notifications](/introduction/resource-capabilities/notifications) - Personal users can receive in-app, push, and optionally email notifications.

**Relations**

| Key                 | Relation                                               | Type            | Relation Field(s)             |
| ------------------- | ------------------------------------------------------ | --------------- | ----------------------------- |
| `language`          | [Language](/api-reference/languages)                   | Belongs to      | `lang_id`                     |
| `role`              | [Role](/api-reference/roles)                           | Belongs to      | `role_id`                     |
| `acceptedPolicies`  | TermsOfUse                                             | Belongs to many | User and terms-of-use IDs     |
| `entityPermissions` | [EntityPermissions](/api-reference/entity-permissions) | Morph many      | Permission entity type and ID |

**Computed Properties**

* `full_name` - The concatenated first and last name.
* `profile_picture_url` - The authenticated content URL when a profile picture exists.
* `static_url`, `url`, and `url_context` - Available through the [URL Context](/introduction/resource-capabilities/url-context) capability when selected.

Personal and administrative fields are only returned when the authenticated user has the corresponding permissions.

## List

List visible `Users`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/users`

**Request Keys**

| Key         | Type      | Default            | Description                          |
| ----------- | --------- | ------------------ | ------------------------------------ |
| `selects`   | `string`  | All visible fields | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations  | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit           | Maximum number of users.             |
| `filter`    | `object`  | No filters         | Filters, including `filter[text]`.   |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 7,
    "username": "alex.rivera",
    "first_name": "Alex",
    "last_name": "Rivera",
    "active": true,
    "group_account": false,
    "role_id": 6,
    "lang_id": "en-US",
    "profile_picture": "alex-rivera.jpg",
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 09:10:00",
    "deleted_at": null,
    "blacked_out_at": null,
    "full_name": "Alex Rivera"
  },
  {
    "id": 8,
    "username": "support.desk",
    "first_name": "",
    "last_name": "",
    "active": false,
    "group_account": true,
    "role_id": 9,
    "lang_id": "en-US",
    "profile_picture": null,
    "created_at": "2026-08-06 09:05:00",
    "updated_at": "2026-08-06 09:15:00",
    "deleted_at": null,
    "blacked_out_at": null,
    "full_name": " "
  }
]
```

## List Deleted Users

List soft-deleted `Users`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/users/deleted`

**Request Keys**

| Key         | Type      | Default            | Description                          |
| ----------- | --------- | ------------------ | ------------------------------------ |
| `selects`   | `string`  | All visible fields | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations  | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit           | Maximum number of deleted users.     |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/users/deleted', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 17,
    "username": "sam.lee",
    "first_name": "Sam",
    "last_name": "Lee",
    "active": false,
    "group_account": false,
    "role_id": 6,
    "lang_id": "en-US",
    "profile_picture": null,
    "created_at": "2025-11-12 08:30:00",
    "updated_at": "2026-08-01 16:20:00",
    "deleted_at": "2026-08-01 16:20:00",
    "blacked_out_at": null,
    "full_name": "Sam Lee"
  },
  {
    "id": 18,
    "username": "former.reception",
    "first_name": "",
    "last_name": "",
    "active": false,
    "group_account": true,
    "role_id": 11,
    "lang_id": "en-US",
    "profile_picture": null,
    "created_at": "2025-09-02 07:45:00",
    "updated_at": "2026-07-20 12:00:00",
    "deleted_at": "2026-07-20 12:00:00",
    "blacked_out_at": null,
    "full_name": " "
  }
]
```

## Show Current User

Show the current authenticated `User`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/users/current`

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/users/current', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 7,
  "username": "alex.rivera",
  "first_name": "Alex",
  "last_name": "Rivera",
  "active": true,
  "group_account": false,
  "role_id": 6,
  "lang_id": "en-US",
  "profile_picture": "alex-rivera.jpg",
  "created_at": "2026-08-06 09:00:00",
  "updated_at": "2026-08-06 09:10:00",
  "deleted_at": null,
  "blacked_out_at": null,
  "full_name": "Alex Rivera"
}
```

## Show

Show one visible `User`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/users/{userWithTrashed}`

**Route Parameters**

| Parameter         | Type      | Description                          |
| ----------------- | --------- | ------------------------------------ |
| `userWithTrashed` | `integer` | User ID; deleted users are included. |

**Request Keys**

| Key         | Type     | Default            | Description                          |
| ----------- | -------- | ------------------ | ------------------------------------ |
| `selects`   | `string` | All visible fields | Comma-separated fields to return.    |
| `relations` | `string` | Default relations  | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/users/7', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 7,
  "username": "alex.rivera",
  "first_name": "Alex",
  "last_name": "Rivera",
  "active": true,
  "group_account": false,
  "role_id": 6,
  "lang_id": "en-US",
  "profile_picture": "alex-rivera.jpg",
  "street": "120 Market Street",
  "zipcode": "94105",
  "city": "San Francisco",
  "email": "alex.rivera@example.com",
  "phone": "+1 415 555 0142",
  "birthdate": "1991-04-18 00:00:00",
  "gender": null,
  "entering_date": "2024-03-01 00:00:00",
  "leaving_date": null,
  "staff_number": "EMP-1042",
  "wants_email_notifications": true,
  "created_at": "2026-08-06 09:00:00",
  "updated_at": "2026-08-06 09:10:00",
  "deleted_at": null,
  "blacked_out_at": null,
  "default_route": "/dashboard",
  "prevent_logout": false,
  "full_name": "Alex Rivera"
}
```

## Show Profile Picture

Return the stored profile picture for one `User`.

**Definition**

<mark style="color:green;">`GET`</mark> `/users/{user}/profile-picture/{fileName?}`

**Route Parameters**

| Parameter  | Type      | Description                                                |
| ---------- | --------- | ---------------------------------------------------------- |
| `user`     | `integer` | User ID.                                                   |
| `fileName` | `string`  | Optional stable filename; defaults to the stored filename. |

**Behavior**

This authenticated content route returns the stored profile-picture bytes with their detected media type. It is not prefixed with `/api`.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/users/7/profile-picture/alex-rivera.jpg', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```http
HTTP/1.1 200 OK
Content-Type: image/jpeg
Content-Disposition: inline; filename="alex-rivera.jpg"

<binary JPEG data>
```

## Download Import Template

Download the CSV import template for `Users`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/users/import-template`

**Request Keys**

| Key                     | Type     | Default   | Description                                          |
| ----------------------- | -------- | --------- | ---------------------------------------------------- |
| CSV-importable User key | `string` | Field key | Optional query value replacing that column's header. |

**Behavior**

The example keeps the default English field keys.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/users/import-template', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```http
HTTP/1.1 200 OK
Content-Type: text/csv; charset=UTF-8
Content-Disposition: attachment; filename=intratool-benutzerdaten-vorlage.csv

username,password,first_name,last_name,active,group_account,role_id,lang_id
```

## Create

Create a new `User`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/users`

**Request Keys**

| Key                                                  | Type               | Default                | Description                                                                                                     |
| ---------------------------------------------------- | ------------------ | ---------------------- | --------------------------------------------------------------------------------------------------------------- |
| `username`\*                                         | `string`           | -                      | Unique username.                                                                                                |
| `password`\*                                         | `string`           | -                      | Password satisfying the configured minimum length.                                                              |
| `first_name`\*                                       | `string`           | -                      | Required for personal accounts.                                                                                 |
| `last_name`\*                                        | `string`           | -                      | Required for personal accounts.                                                                                 |
| `role_id`\*                                          | `integer`          | -                      | Existing [Role](/api-reference/roles) ID.                                                                       |
| `lang_id`                                            | `string` \| `null` | `null`                 | [Language](/api-reference/languages) ID; use `en-US` for US English.                                            |
| `active`                                             | `boolean`          | Date-derived or `true` | Whether the user can log in.                                                                                    |
| `group_account`                                      | `boolean`          | `false`                | Whether this is a shared group account.                                                                         |
| `request_password_change`                            | `boolean`          | `false`                | Require a password change on the next login.                                                                    |
| `profile_picture`                                    | `image` \| `null`  | `null`                 | Uploaded image; use multipart data when provided.                                                               |
| `street`, `zipcode`, `city`, `phone`, `staff_number` | `string` \| `null` | `null`                 | Optional profile fields.                                                                                        |
| `email`                                              | `string` \| `null` | `null`                 | Optional valid email address.                                                                                   |
| `birthdate`, `entering_date`, `leaving_date`         | `date` \| `null`   | `null`                 | Optional dates.                                                                                                 |
| `gender`                                             | `string` \| `null` | `null`                 | Supported gender enum value.                                                                                    |
| `wants_email_notifications`                          | `boolean`          | `false`                | Enable email notification delivery.                                                                             |
| `default_route`                                      | `string` \| `null` | `null`                 | Route opened after login.                                                                                       |
| `prevent_logout`                                     | `boolean`          | `false`                | Protect the account from self-logout and session replacement; only another administrator may change this value. |

Keys with `*` are required.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/users', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'username' => 'morgan.chen',
        'password' => 'A-secure-password-2026',
        'first_name' => 'Morgan',
        'last_name' => 'Chen',
        'role_id' => 6,
        'lang_id' => 'en-US',
        'active' => true,
        'group_account' => false,
        'email' => 'morgan.chen@example.com',
        'entering_date' => '2026-08-15',
        'staff_number' => 'EMP-1088',
        'wants_email_notifications' => true,
        'default_route' => '/dashboard',
        'prevent_logout' => false
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 19,
    "username": "morgan.chen",
    "first_name": "Morgan",
    "last_name": "Chen",
    "active": true,
    "group_account": false,
    "role_id": 6,
    "lang_id": "en-US",
    "profile_picture": null,
    "email": "morgan.chen@example.com",
    "entering_date": "2026-08-15 00:00:00",
    "staff_number": "EMP-1088",
    "wants_email_notifications": true,
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:00:00",
    "deleted_at": null,
    "blacked_out_at": null,
    "default_route": "/dashboard",
    "prevent_logout": false,
    "full_name": "Morgan Chen"
  }
}
```

## Parse CSV Import

Parse a CSV import for `Users`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/users/csv`

**Request Keys**

| Key                  | Type       | Default   | Description                                          |
| -------------------- | ---------- | --------- | ---------------------------------------------------- |
| `user_data_csv`\*    | `CSV file` | -         | CSV with a header row matching importable User keys. |
| `update_users`       | `boolean`  | `false`   | Include matching active users in `update_users`.     |
| `delete_users`       | `boolean`  | `false`   | Include users absent from the CSV in `delete_users`. |
| `whitespace_to_null` | `boolean`  | `false`   | Convert blank nullable fields to `null`.             |
| Importable User keys | `string`   | Field key | Optional multipart values remapping CSV headers.     |

Keys with `*` are required.

**Behavior**

The endpoint parses and classifies rows; it does not persist the proposed changes. The uploaded example CSV contains one new US-English user and one existing user update, which produces the two populated response groups.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/users/csv', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'multipart' => [
        [
            'name' => 'user_data_csv',
            'contents' => fopen('/path/to/users.csv', 'r'),
            'filename' => 'users.csv'
        ],
        ['name' => 'update_users', 'contents' => '1'],
        ['name' => 'delete_users', 'contents' => '0'],
        ['name' => 'whitespace_to_null', 'contents' => '1']
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "new_users": [
      {
        "username": "taylor.nguyen",
        "password": "A-secure-password-2026",
        "first_name": "Taylor",
        "last_name": "Nguyen",
        "role_id": 6,
        "lang_id": "en-US"
      }
    ],
    "update_users": [
      {
        "id": 7,
        "username": "alex.rivera",
        "first_name": "Alex",
        "last_name": "Rivera",
        "role_id": 6,
        "lang_id": "en-US"
      }
    ],
    "delete_users": []
  }
}
```

## Update

Update an existing `User`.

**Definition**

<mark style="color:blue;">`PUT`</mark> `/api/users/{user}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `user`    | `integer` | User ID.    |

**Request Keys**

| Key            | Type     | Default       | Description                                                                               |
| -------------- | -------- | ------------- | ----------------------------------------------------------------------------------------- |
| Any Create key | `mixed`  | Current value | Every Create key is optional during updates.                                              |
| `old_password` | `string` | -             | Current password when changing one's own password without user-administration permission. |

**Behavior**

* Sending `profile_picture` as `null` removes the current image.
* A user cannot change their own `prevent_logout` value. The submitted key is ignored even when that user can administer other users.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('PUT', '/api/users/7', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'role_id' => 9,
        'lang_id' => 'en-US',
        'city' => 'Oakland',
        'wants_email_notifications' => false
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 7,
    "username": "alex.rivera",
    "first_name": "Alex",
    "last_name": "Rivera",
    "active": true,
    "group_account": false,
    "role_id": 9,
    "lang_id": "en-US",
    "profile_picture": "alex-rivera.jpg",
    "city": "Oakland",
    "wants_email_notifications": false,
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 10:20:00",
    "deleted_at": null,
    "blacked_out_at": null,
    "full_name": "Alex Rivera"
  }
}
```

## Log Out

Log out one `User`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/users/logout/{user}`

**Route Parameters**

| Parameter | Type      | Description                 |
| --------- | --------- | --------------------------- |
| `user`    | `integer` | User ID to mark for logout. |

**Behavior**

* The user is marked for logout. Existing sessions are rejected when they next pass the logout check.
* A protected user cannot use this endpoint to mark their own account for logout. Another authorized user can mark a protected account for administrative logout.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/users/logout/7', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 7,
    "username": "alex.rivera",
    "first_name": "Alex",
    "last_name": "Rivera",
    "active": true,
    "group_account": false,
    "role_id": 9,
    "lang_id": "en-US",
    "profile_picture": "alex-rivera.jpg",
    "created_at": "2026-08-06 09:00:00",
    "updated_at": "2026-08-06 10:25:00",
    "deleted_at": null,
    "blacked_out_at": null,
    "full_name": "Alex Rivera"
  }
}
```

## Restore

Restore one soft-deleted `User`.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/users/restore/{userWithTrashed}`

**Route Parameters**

| Parameter         | Type      | Description      |
| ----------------- | --------- | ---------------- |
| `userWithTrashed` | `integer` | Deleted User ID. |

**Request Keys**

| Key       | Type      | Default       | Description                                                              |
| --------- | --------- | ------------- | ------------------------------------------------------------------------ |
| `role_id` | `integer` | Existing role | Optional active [Role](/api-reference/roles) assigned after restoration. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/users/restore/17', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => ['role_id' => 9]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 17,
    "username": "sam.lee",
    "first_name": "Sam",
    "last_name": "Lee",
    "active": false,
    "group_account": false,
    "role_id": 9,
    "lang_id": "en-US",
    "profile_picture": null,
    "created_at": "2025-11-12 08:30:00",
    "updated_at": "2026-08-06 10:30:00",
    "deleted_at": null,
    "blacked_out_at": null,
    "full_name": "Sam Lee"
  }
}
```

## Delete

Soft-delete an existing `User`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/users/{user}`

**Route Parameters**

| Parameter | Type      | Description |
| --------- | --------- | ----------- |
| `user`    | `integer` | User ID.    |

**Behavior**

The user is soft-deleted and can be restored later. The authenticated user cannot delete themselves.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/users/8', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```

## Black Out

Permanently anonymize one `User`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/users/blackout/{userWithTrashed}`

**Route Parameters**

| Parameter         | Type      | Description                |
| ----------------- | --------- | -------------------------- |
| `userWithTrashed` | `integer` | Active or deleted User ID. |

**Behavior**

The user is deleted if necessary and personally identifying fields are irreversibly replaced or cleared while authored content remains associated with the anonymized account.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/users/blackout/18', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "id": 18,
    "username": "__18_1786012800",
    "first_name": "--",
    "last_name": "--",
    "active": false,
    "group_account": true,
    "role_id": 11,
    "lang_id": "en-US",
    "profile_picture": null,
    "created_at": "2025-09-02 07:45:00",
    "updated_at": "2026-08-06 10:35:00",
    "deleted_at": "2026-08-06 10:35:00",
    "blacked_out_at": "2026-08-06 10:35:00",
    "full_name": "-- --"
  }
}
```


# UserLoginTokens

## Introduction

`UserLoginTokens` are short-lived, one-time credentials that let a browser sign in a [User](/api-reference/users) without entering that user's password. See [User Login Token](/introduction/authorization/third-party-login#user-login-token) for the browser login flow.

{% hint style="warning" %}
The signed `token` is returned only when the record is created. Store or forward it securely because list and show responses cannot retrieve it later.
{% endhint %}

## Model Definition

**Relations**

| Key         | Relation                     | Type       | Relation Field(s) |
| ----------- | ---------------------------- | ---------- | ----------------- |
| `user`      | [User](/api-reference/users) | Belongs to | `user_id`         |
| `loginUser` | [User](/api-reference/users) | Belongs to | `login_user_id`   |

Tokens use UUIDs, expire at `expires_at`, and are soft-deleted after use or explicit deletion.

## Admin: List

List all `UserLoginTokens` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/users/login-tokens`

**Request Keys**

| Key         | Type      | Default                           | Description                          |
| ----------- | --------- | --------------------------------- | ------------------------------------ |
| `selects`   | `string`  | All fields except the internal ID | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations                 | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit                          | Maximum number of records.           |

**Behavior**

The administration list includes soft-deleted tokens. The secret `token` is never returned.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/users/login-tokens', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "uuid": "4dcc0220-45b8-4e96-bae1-cb645cc49965",
    "user_id": 7,
    "login_user_id": 8,
    "expires_at": "2026-08-06 11:05:00",
    "created_at": "2026-08-06 11:00:00",
    "updated_at": "2026-08-06 11:00:00",
    "deleted_at": null
  },
  {
    "uuid": "c4d1d6d0-1ea0-4b25-9a26-07951f495c43",
    "user_id": 7,
    "login_user_id": 19,
    "expires_at": "2026-08-06 12:00:00",
    "created_at": "2026-08-06 10:00:00",
    "updated_at": "2026-08-06 10:12:00",
    "deleted_at": "2026-08-06 10:12:00"
  }
]
```

## Admin: Show

Show one `UserLoginToken` in administration scope.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/administration/users/login-tokens/{userLoginToken}`

**Route Parameters**

| Parameter        | Type   | Description          |
| ---------------- | ------ | -------------------- |
| `userLoginToken` | `UUID` | UserLoginToken UUID. |

**Request Keys**

| Key         | Type     | Default                           | Description                          |
| ----------- | -------- | --------------------------------- | ------------------------------------ |
| `selects`   | `string` | All fields except the internal ID | Comma-separated fields to return.    |
| `relations` | `string` | Default relations                 | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/administration/users/login-tokens/4dcc0220-45b8-4e96-bae1-cb645cc49965', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "uuid": "4dcc0220-45b8-4e96-bae1-cb645cc49965",
  "user_id": 7,
  "login_user_id": 8,
  "expires_at": "2026-08-06 11:05:00",
  "created_at": "2026-08-06 11:00:00",
  "updated_at": "2026-08-06 11:00:00",
  "deleted_at": null
}
```

## Admin: Create by User ID

Create a `UserLoginToken` for one user ID.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/users/login-tokens`

**Request Keys**

| Key               | Type      | Default | Description                                                   |
| ----------------- | --------- | ------- | ------------------------------------------------------------- |
| `login_user_id`\* | `integer` | -       | Active, non-internal [User](/api-reference/users) to sign in. |
| `expires_in`\*    | `integer` | -       | Lifetime in seconds, from 1 through 86400.                    |

Keys with `*` are required.

**Behavior**

`user_id` is always taken from the authenticated user and cannot be overridden. The example's 300-second lifetime produces the shown `expires_at` value from the creation time.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/users/login-tokens', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'login_user_id' => 8,
        'expires_in' => 300
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "uuid": "4dcc0220-45b8-4e96-bae1-cb645cc49965",
    "user_id": 7,
    "login_user_id": 8,
    "expires_at": "2026-08-06 11:05:00",
    "created_at": "2026-08-06 11:00:00",
    "updated_at": "2026-08-06 11:00:00",
    "deleted_at": null,
    "token": "eyJhbGciOiJSUzI1NiJ9.eyJsdGkiOiI0ZGNjMDIyMC00NWI4LTRlOTYtYmFlMS1jYjY0NWNjNDk5NjUiLCJzdWIiOjh9.signature"
  }
}
```

## Admin: Create by Email

Create a `UserLoginToken` for one email address.

**Definition**

<mark style="color:yellow;">`POST`</mark> `/api/administration/users/login-tokens/email`

**Request Keys**

| Key                  | Type      | Default | Description                                                           |
| -------------------- | --------- | ------- | --------------------------------------------------------------------- |
| `login_user_email`\* | `string`  | -       | Unique email of an active, non-internal [User](/api-reference/users). |
| `expires_in`\*       | `integer` | -       | Lifetime in seconds, from 1 through 86400.                            |

Keys with `*` are required.

**Behavior**

`user_id` is always taken from the authenticated user. The email is resolved to `login_user_id`; therefore the example request contains the value needed to produce `login_user_id: 19` in the response.

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('POST', '/api/administration/users/login-tokens/email', [
    'headers' => ['Authorization' => "Bearer {accessToken}"],
    'json' => [
        'login_user_email' => 'morgan.chen@example.com',
        'expires_in' => 900
    ]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": {
    "uuid": "9f9771b3-c6fe-442e-81c8-af4d0bde93bc",
    "user_id": 7,
    "login_user_id": 19,
    "expires_at": "2026-08-06 11:30:00",
    "created_at": "2026-08-06 11:15:00",
    "updated_at": "2026-08-06 11:15:00",
    "deleted_at": null,
    "token": "eyJhbGciOiJSUzI1NiJ9.eyJsdGkiOiI5Zjk3NzFiMy1jNmZlLTQ0MmUtODFjOC1hZjRkMGJkZTkzYmMiLCJzdWIiOjE5fQ.signature"
  }
}
```

## Admin: Delete

Delete an existing `UserLoginToken`.

**Definition**

<mark style="color:red;">`DELETE`</mark> `/api/administration/users/login-tokens/{userLoginToken}`

**Route Parameters**

| Parameter        | Type   | Description          |
| ---------------- | ------ | -------------------- |
| `userLoginToken` | `UUID` | UserLoginToken UUID. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('DELETE', '/api/administration/users/login-tokens/4dcc0220-45b8-4e96-bae1-cb645cc49965', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "status": "success",
  "data": null
}
```


# UserActivities

## Introduction

`UserActivities` track certain activities that the related [User](/api-reference/users) initiates and record the [Department](/api-reference/departments) in which the activity took place.

Each `UserActivity` has a concrete `type` which defines the kind of activity that is tracked.

An activity is uniquely identified by the combination of the `type` and the related [User](/api-reference/users). The associated [Department](/api-reference/departments) reflects the user's most recently active location.

The fields `created_at` and `updated_at` indicate the time of the first and most recent occurrence of the activity, respectively.

Activities are only recorded through the use of the intratool application itself and not via the API.

## Model Definition

**Alias**

`userActivity`

**Relations**

| Key          | Relation                                 | Type       | Relation Field(s) |
| ------------ | ---------------------------------------- | ---------- | ----------------- |
| `user`       | [User](/api-reference/users)             | Belongs to | `user_id`         |
| `department` | [Department](/api-reference/departments) | Belongs to | `department_id`   |

**Types**

* `general` - Any activity of the related [User](/api-reference/users).
* `login` - A login by the related [User](/api-reference/users).
* `logout` - Any logout of the related User, recorded together with its reason-specific activity.
* `logoutAdministrative` - A logout caused by an administrative logout marker.
* `logoutPasswordChanged` - A logout caused by a password change.
* `logoutSelf` - A logout initiated by the related User.
* `logoutTokenLogin` - A logout caused when a login token replaces the current user session.
* `logoutUnknown` - A logout for which no more specific reason is available.
* `logoutUserDisabled` - A logout caused when the related User is disabled.

## List

List visible `UserActivities`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/user-activities`

**Request Keys**

| Key         | Type      | Default           | Description                          |
| ----------- | --------- | ----------------- | ------------------------------------ |
| `selects`   | `string`  | All fields        | Comma-separated fields to return.    |
| `relations` | `string`  | Default relations | Pipe-separated relations to include. |
| `limit`     | `integer` | No limit          | Maximum number of activities.        |
| `filter`    | `object`  | No filters        | Supported repository filters.        |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/user-activities', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
[
  {
    "id": 31,
    "user_id": 7,
    "department_id": 6,
    "type": "logout",
    "created_at": "2026-08-06 10:45:00",
    "updated_at": "2026-08-06 10:45:00"
  },
  {
    "id": 32,
    "user_id": 7,
    "department_id": 6,
    "type": "logoutSelf",
    "created_at": "2026-08-06 10:45:00",
    "updated_at": "2026-08-06 10:45:00"
  }
]
```

## Show

Show one visible `UserActivity`.

**Definition**

<mark style="color:green;">`GET`</mark> `/api/user-activities/{userActivity}`

**Route Parameters**

| Parameter      | Type      | Description      |
| -------------- | --------- | ---------------- |
| `userActivity` | `integer` | UserActivity ID. |

**Request Keys**

| Key         | Type     | Default           | Description                          |
| ----------- | -------- | ----------------- | ------------------------------------ |
| `selects`   | `string` | All fields        | Comma-separated fields to return.    |
| `relations` | `string` | Default relations | Pipe-separated relations to include. |

**Example Request**

{% tabs %}
{% tab title="PHP" %}

```php
$client = new GuzzleHttp\Client(['base_uri' => 'https://{tenant}.intratool.de']);
$response = $client->request('GET', '/api/user-activities/31', [
    'headers' => ['Authorization' => "Bearer {accessToken}"]
]);
```

{% endtab %}
{% endtabs %}

**Example Response**

```json
{
  "id": 31,
  "user_id": 7,
  "department_id": 6,
  "type": "login",
  "created_at": "2026-08-06 08:00:00",
  "updated_at": "2026-08-06 08:00:00"
}
```




---

[Next Page](/llms-full.txt/1)

