> For the complete documentation index, see [llms.txt](https://docs.api.intratool.de/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.api.intratool.de/api-reference/users.md).

# 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.md) - Users can be selected as explicit targets for supported resources.
* [URL Context](/introduction/resource-capabilities/url-context.md) - User URLs resolve to access-checked context.
* [Notifications](/introduction/resource-capabilities/notifications.md) - Personal users can receive in-app, push, and optionally email notifications.

**Relations**

| Key                 | Relation                                                  | Type            | Relation Field(s)             |
| ------------------- | --------------------------------------------------------- | --------------- | ----------------------------- |
| `language`          | [Language](/api-reference/languages.md)                   | Belongs to      | `lang_id`                     |
| `role`              | [Role](/api-reference/roles.md)                           | Belongs to      | `role_id`                     |
| `acceptedPolicies`  | TermsOfUse                                                | Belongs to many | User and terms-of-use IDs     |
| `entityPermissions` | [EntityPermissions](/api-reference/entity-permissions.md) | 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.md) 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.md) ID.                                                                    |
| `lang_id`                                            | `string` \| `null` | `null`                 | [Language](/api-reference/languages.md) 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.md) 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": "-- --"
  }
}
```
