# Voxbi Cockpit API
v0.1.0 · OpenAPI 3.2.0

REST API for the Voxbi Cockpit PBX platform. Use it to manage users, extensions, SIP devices, phone numbers, call flows, IVRs, queues, schedules, and related telephony resources.

All resources are scoped to a PBX tenant. The authenticated identity determines which PBX(es) you can access.

Every request must be authenticated. See **Authentication** for the available schemes.

## Principal-conditional fields

Most resources are reachable by two kinds of token: a **user-owned** token
(the cockpit/mobile Bearer token) and a **PBX-owned API key** (created on the
Integrations page). A few response fields are *per-user* and therefore depend
on which kind of token you use: they are returned for user-owned tokens and
**omitted entirely** for PBX API-key tokens (a PBX has no per-user state).
Such fields are flagged **PRINCIPAL-CONDITIONAL** in their description -
examples: `is_favorite` on extensions and contacts, and `editable` on contacts.

## Embedding related resources

List and detail endpoints accept an `?include=` query parameter: a
comma-separated list of related resources to embed. Each endpoint documents
its own allowed values; anything not in that allow-list is rejected, and
nothing is embedded unless you ask for it.

Some relations are **dot-nested**, which lets you choose how deep to go. For
example, on an extension you can request `user` to embed just the user, or
`user.groups` to embed the user **and** its groups (you do not have to load
the parent separately). Combine several with commas, e.g.
`?include=user.groups,sipDevice`.

## Rate limiting

Requests are rate limited per API key (per PBX). The default budget is 120
requests per minute; it may be higher depending on your plan. Every response
reports your current budget so you do not have to hard-code the number:

- `X-RateLimit-Limit` - the limit applied to this request
- `X-RateLimit-Remaining` - requests left in the current window

When you exceed the limit the API responds with `429 Too Many Requests` and a
`Retry-After` header (seconds to wait before retrying). Always read these
headers rather than assuming a fixed rate: the budget can change per plan, and
a few endpoints have their own stricter limit. The clearest example is
**Push the PBX configuration**, which is capped at 1 request per minute - that
endpoint documents its own limit and `429` response.

## Servers

- `https://cockpit.voxbi.com/api/v1` — The production API server
- `https://staging-cockpit.voxbi.com/api/v1` — The staging API server
- `https://dev-cockpit.voxbi.com/api/v1` — The development API server

## Authentication

- `bearerAuth` — HTTP Bearer
- `ApiKeyAuth` — API key in header "Authorization"
- `PbxServerAuth` — HTTP Bearer
- `PbxApiKey` — HTTP Bearer
- `IntegrationApiKey` — HTTP Bearer

## Endpoints

### Authentication

Every endpoint uses **bearer token** authentication: send your token in the `Authorization` header on every request:

```
Authorization: Bearer <token>
```

There are two kinds of token:

- **User token** - obtained by calling `POST /login` with a user's credentials. It inherits that user's permissions and is scoped to their PBX(es). There is no separate "global" token; broader access comes from logging in as a user with broader role assignments (e.g. a super-admin).
- **PBX API key** - created by the customer on the Cockpit **Integrations** page (an "API key" integration). It is owned by the PBX and limited to the scopes selected when the key was created.

Both kinds authenticate against the same endpoints. **Which fields a response contains can depend on the token type:** a few *per-user* fields are returned for user tokens and omitted for PBX API keys (see **Principal-conditional fields** above, and the `PRINCIPAL-CONDITIONAL` note on the affected fields).

Per-operation auth requirements are listed under each endpoint's **Security** section.


#### POST /login

Log in with email and password (public)

Authenticates a user with email and password and returns a personal access token used as the Bearer credential on every subsequent request.

`operationId: postAuthLogin`

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "email": "string<email>",
    "password": "string<password>"
}
```

**Responses**:
- `200` — OK
  ```json
  {
      "message": "string",
      "data": {
          "token": "string"
      }
  }
  ```
- `401` — Invalid email or password.
- `403` — The user account is disabled.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /me

Get the authenticated user (user)

Returns the currently authenticated user's profile data including extensions, SIP devices, groups, twinnings, and integrations. Requires a user-owned bearer token.

`operationId: getAuthMe`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "message": "string",
      "data": {
          "id": "string<uuid>",
          "pbx_id": "string",
          "full_name_with_email": "string",
          "first_name": "string",
          "last_name": "string",
          "email": "string<email>",
          "language": "string",
          "access_token": "string?",
          "email_verified_at": "string?<date-time>",
          "status": "string",
          "allow_telephony": "boolean",
          "allow_dnd": "boolean",
          "allow_change_twinning_dest": "boolean",
          "allow_call_forwarding": "boolean",
          "allow_manage_global_contacts": "boolean",
          "call_recordings_visibility": "string?",
          "extension_roaming": "boolean",
          "call_information_visibility": "string?",
          "allow_status_changer": "boolean",
          "allow_manage_voicemail_parameters": "boolean",
          "show_in_address_book": "boolean",
          "voicemail_type": "string?",
          "voicemail_destination": "string?",
          "concurrent_calls": "integer?",
          "vip": "boolean",
          "sync_source": "string?",
          "allow_anonymous_calls": "boolean",
          "identity_provider_uid": "string?",
          "is_shadow": "boolean",
          "created_at": "string<date-time>",
          "updated_at": "string<date-time>",
          "webrtc_configuration": "object?",
          "extensions": [
              []
          ],
          "sipDevices": [
              []
          ],
          "groups": [
              []
          ],
          "twinnings": [
              []
          ],
          "caller_identities": [
              []
          ],
          "integrations": [
              []
          ],
          "pbx": [],
          "microsoft": {
              "fetch_contacts": "boolean"
          },
          "odoo": {
              "fetch_contacts": "boolean"
          },
          "employee_id": "string?<uuid>",
          "tempus": "string?"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### GET /user

Get the authenticated user record (user)

Returns the raw authenticated user model (the owner of the Sanctum token). This is a
thin endpoint that serialises the user record directly; for the assembled profile with
extensions, SIP devices, groups, twinnings and integrations use `GET /me` instead.
Hidden attributes (password, tokens) are never serialised.

`operationId: getAuthUserRecord`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "string<uuid>",
      "pbx_id": "string<uuid>",
      "first_name": "string",
      "last_name": "string",
      "email": "string<email>",
      "status": "string",
      "created_at": "string?<date-time>",
      "updated_at": "string?<date-time>"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### GET /magic-login/{token}

Log in with a magic link token

Authenticates a user via a magic link token. Returns a redirect or JSON response on success.

`operationId: getAuthMagicLogin`

**Parameters**:
- `Accept` (header, string, optional)
- `token` (path, string, required) — The magic link token sent to the user's email.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "message": "string",
      "data": {
          "token": "string"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### POST /passwordless/login

Request Passwordless Login Code

Sends a one-time login code to the provided email address. The code expires after a configured timeout.

`operationId: postAuthPasswordlessLogin`

**Parameters**:
- `Accept` (header, string, optional)

**Request body** (`application/json`):
```json
{
    "email": "string<email>"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "success": "boolean",
      "expires_in": "integer"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /passwordless/verify

Verify Passwordless Login Code

Verifies a one-time login code for the given email and returns an auth token on success.

`operationId: postAuthPasswordlessVerify`

**Parameters**:
- `Accept` (header, string, optional)

**Request body** (`application/json`):
```json
{
    "email": "string<email>",
    "code": "string"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "message": "string",
      "data": {
          "token": "string"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /passwordless/resend

Resend Passwordless Login Code

Resends a one-time login code to the provided email address, subject to a cooldown period.

`operationId: postAuthPasswordlessResend`

**Parameters**:
- `Accept` (header, string, optional)

**Request body** (`application/json`):
```json
{
    "email": "string<email>"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "success": "boolean",
      "expires_in": "integer"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /request-password-reset

Request a password reset email

Sends a password reset email to the provided address. Requires HPP API key authentication.

`operationId: postAuthRequestPasswordReset`

**Auth**: ApiKeyAuth

**Parameters**:
- `Accept` (header, string, optional)

**Request body** (`application/json`):
```json
{
    "email": "string<email>"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "message": "string"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /reset-password

Reset Password

Resets the user's password using a valid reset token. Requires HPP API key authentication.

`operationId: postAuthResetPassword`

**Auth**: ApiKeyAuth

**Parameters**:
- `Accept` (header, string, optional)

**Request body** (`application/json`):
```json
{
    "token": "string",
    "password": "string<password>"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "message": "string"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /edit-password

Set a new password

Changes the authenticated user's password by verifying the old password and setting a new one. Requires HPP API key authentication.

`operationId: postAuthEditPassword`

**Auth**: ApiKeyAuth

**Parameters**:
- `Accept` (header, string, optional)

**Request body** (`application/json`):
```json
{
    "old_password": "string<password>",
    "new_password": "string<password>"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "message": "string"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

### AI configuration

Read-only aggregated AI setup for a PBX (global prompt, labels, keyword tree, per-extension and per-group prompts, AI profile and feature flags). Read with a PBX API key carrying the `ai-configuration` scope.

#### GET /ai-configuration

Get AI configuration (pbx)

Returns the PBX's aggregated, read-only AI configuration: the account-wide
global prompt and labels, the classification keyword tree, per-extension and
per-group prompts, and the active AI profile and feature flags.

Requires a PBX "API key" carrying the `ai-configuration` scope. The PBX is
resolved from the token.

`operationId: getAiConfiguration`

**Auth**: PbxApiKey

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `200` — OK
  ```json
  {
      "pbx_id": "550e8400-e29b-41d4-a716-446655440000",
      "ai_profile": "premium",
      "ai_features": {
          "summary": true,
          "transcription": true,
          "translations": false,
          "keyword_mapping_to_call": false,
          "tagging_calls": false,
          "sentiment_analysis": false
      },
      "global_prompt": "You are the AI assistant for Example Corp. Be concise and polite.",
      "labels": [
          "Sales",
          "Support",
          "Billing"
      ],
      "keywords": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440010",
              "name": "Billing",
              "prompt": "Apply when the caller asks about invoices or payments.",
              "children": [
                  []
              ]
          }
      ],
      "extension_prompts": [
          {
              "extension_id": "550e8400-e29b-41d4-a716-446655440020",
              "extension": "101",
              "prompt": "Greet callers as the reception desk and offer to take a message."
          }
      ],
      "group_prompts": [
          {
              "group_id": "550e8400-e29b-41d4-a716-446655440030",
              "group": "Support",
              "prompt": "Prioritise urgent tickets and confirm the caller's contract number."
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The API key does not carry the `ai-configuration` scope.
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.

### Caller Identities

Caller identities represent the outbound phone numbers (and optional suffixes) available to the authenticated user when placing calls. Each identity is linked to a phone number and a target entity (User, PBX, Group, or SIP device).

#### GET /caller-identities

List Caller Identities for the authenticated user

Returns a paginated list of caller identities. Use the standard `search`, `sort`, `page`,
and `perPage` query parameters; use `filter[key]=value` to narrow the result set.
Multi-tenant scoping is enforced.

`operationId: listCallerIdentities`

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "phone_number_id": "550e8400-e29b-41d4-a716-446655440002",
              "suffix": "23",
              "label": "Sales line",
              "caller_identity_target_type": "App\\Models\\User",
              "caller_identity_target_id": "550e8400-e29b-41d4-a716-446655440003",
              "order": 1
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### GET /caller-identities/{id}

Get a caller Identity

Returns the caller identity identified by the path parameter. Multi-tenant scoping is
enforced: the caller can only fetch resources within their own PBX.

`operationId: getCallerIdentity`

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — UUID of the caller identity

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "phone_number_id": "550e8400-e29b-41d4-a716-446655440002",
          "suffix": "23",
          "label": "Sales line",
          "caller_identity_target_type": "App\\Models\\User",
          "caller_identity_target_id": "550e8400-e29b-41d4-a716-446655440003",
          "order": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

### Calls

Endpoints for initiating and managing outbound calls.

#### POST /calls/new

Initiate a new call

Places an outbound call on behalf of the authenticated user.

## Minimal request

`destination` is the only required field. Everything else is optional:

```json
{ "destination": "+12125550199" }
```

With nothing else supplied, the call is placed using the user's own configuration: their
default caller identity and their default ringing targets as set up in the cockpit.

## Choosing target devices

Send `targets` when you want to override which of the user's endpoints should ring. Each
flag is independent, so you can combine them (for example ring the desk phone and the
twinned mobile at the same time):

```json
{
  "destination": "+12125550199",
  "targets": {
    "sip_devices": [],
    "webrtc": false,
    "twinning": true,
    "teams": false
  }
}
```

- `sip_devices`: list of SIP device identifiers to ring. Omit or send `null` to let the PBX
  pick the user's SIP devices.
- `webrtc`: ring the browser softphone. Defaults to `false`.
- `twinning`: ring the twinned (mobile) number. Defaults to `false`.
- `teams`: ring the Microsoft Teams endpoint. Defaults to `false`.

Every boolean flag defaults to `false` when it is not passed, so a `targets` object is only
useful if at least one flag is `true` or `sip_devices` is populated.

Enabling a target only has an effect if the matching configuration or integration exists for
the user: WebRTC requires a WebRTC-enabled account, `twinning` requires a twinning number,
and `teams` requires the Teams integration. If the underlying feature is not configured, the
PBX simply skips that target and rings whatever remains.

## Caller identity

`caller_identity` is optional. Pass the UUID of a caller identity available to the user to
present a specific outbound number. When omitted, the user's default caller identity is
used.

`operationId: postCallNew`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `debugAmiTrace` (query, boolean, optional) — Enable AMI trace for debugging
- `autoAnswer` (query, boolean, optional) — Auto-answer the call
- `forceDialBack` (query, boolean, optional) — Force dial-back mode
- `anonymous` (query, boolean, optional) — Make the call anonymous

**Request body** (`application/json`) (required):
```json
{
    "destination": "+12125550199",
    "caller_identity": "550e8400-e29b-41d4-a716-446655440000",
    "targets": {
        "sip_devices": "array?",
        "webrtc": false,
        "twinning": false,
        "teams": false
    }
}
```

**Responses**:
- `200` — OK
  ```json
  {
      "status": "string",
      "message": "string",
      "data": []
  }
  ```
- `400` — Bad Request
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.
- `500` — Internal Server Error

### Call Flows

Define how inbound calls are routed through your PBX. A call flow chains ordered steps (transfer, IVR, voicemail, queue, schedule) that the system evaluates per call.

#### GET /call-flows

List Call flow

Returns a paginated list of call flows. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listCallFlows`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — Filter the list by one or more fields. Allowed keys: `name`, `enabled`.
Apply several at once with filter[key1]=value1&filter[key2]=value2.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Main inbound flow",
              "description": "Routes incoming calls to support, then voicemail.",
              "enabled": true,
              "version": 7,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /call-flows

Create a call flow

Create a call flow. Only `name`, `description` and `enabled` are accepted; the PBX is
taken from the authenticated token. Call flow steps are not part of this request: create
them separately against `/call-flow-steps` once the call flow exists.

`operationId: createCallFlow`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "name": "string",
    "description": "string?",
    "enabled": "boolean"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "name": "Main inbound flow",
      "description": "Routes incoming calls to support, then voicemail.",
      "enabled": true,
      "version": 7,
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /call-flows/{call_flow}

Get a Call flow

Returns the call flow identified by the path parameter. Multi-tenant scoping is enforced:
the caller can only fetch resources within their own PBX.

`operationId: getCallFlow`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "name": "Main inbound flow",
      "description": "Routes incoming calls to support, then voicemail.",
      "enabled": true,
      "version": 7,
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### Call Flow Steps

Individual steps belonging to a call flow. Each step has a type and a target (extension, queue, sound file, IVR, etc.) and is executed in order.

#### GET /call-flow-steps

List Call flow step

Returns a paginated list of call flow steps. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listCallFlowSteps`

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — The filter key can be any of the following keys: id, pbx_id, call_flow_id, type, options, is_active and order.

The filter value can be any string or number.

You can also apply multiple filters using `filter[key1]=value1&filter[key2]=value2`.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "ring_user",
                  "is_active": true,
                  "order": 0,
                  "options": {
                      "duration": 20,
                      "also_ring_dialed_extension": false,
                      "also_ring_everyone_in_the_user_group": false,
                      "dial_mobile_twinnings": false,
                      "missed_calls": true,
                      "music_on_hold": "550e8400-e29b-41d4-a716-446655440010",
                      "users": [
                          "550e8400-e29b-41d4-a716-446655440011"
                      ],
                      "sip_devices": [
                          "550e8400-e29b-41d4-a716-446655440012"
                      ],
                      "groups": [
                          "550e8400-e29b-41d4-a716-446655440013"
                      ],
                      "tags": [
                          "550e8400-e29b-41d4-a716-446655440014"
                      ],
                      "locations": [
                          "550e8400-e29b-41d4-a716-446655440015"
                      ],
                      "languages": [
                          "550e8400-e29b-41d4-a716-446655440016"
                      ],
                      "pools": [
                          "550e8400-e29b-41d4-a716-446655440017"
                      ],
                      "statuses": [
                          "550e8400-e29b-41d4-a716-446655440018"
                      ],
                      "pickup_lines": [
                          "550e8400-e29b-41d4-a716-446655440019"
                      ],
                      "sub_statuses": {
                          "550e8400-e29b-41d4-a716-446655440018": [
                              "550e8400-e29b-41d4-a716-446655440020"
                          ]
                      }
                  }
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "ring_external_number",
                  "is_active": true,
                  "order": 1,
                  "options": {
                      "duration": 30,
                      "number": "+12125550100",
                      "music_on_hold": "550e8400-e29b-41d4-a716-446655440010"
                  }
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "call_flow",
                  "is_active": true,
                  "order": 2,
                  "options": {
                      "call_flow_or_extension": "call_flow",
                      "extension": "550e8400-e29b-41d4-a716-446655440030",
                      "call_flow": "550e8400-e29b-41d4-a716-446655440031",
                      "schedule": "550e8400-e29b-41d4-a716-446655440032"
                  }
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "go_to_ivr",
                  "is_active": true,
                  "order": 0,
                  "options": {
                      "ivr": "550e8400-e29b-41d4-a716-446655440040"
                  }
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "enter_queue",
                  "is_active": true,
                  "order": 1,
                  "options": {
                      "duration": 60,
                      "queue": "550e8400-e29b-41d4-a716-446655440050"
                  }
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "text_to_speech",
                  "is_active": true,
                  "order": 0,
                  "options": {
                      "announcement_type": "tts_message",
                      "sound_file": "550e8400-e29b-41d4-a716-446655440060",
                      "language": "fr",
                      "tts_tag": "greeting_morning",
                      "tts_message": "Welcome to Customer Support."
                  }
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "change_ringtone_and_label",
                  "is_active": true,
                  "order": 0,
                  "options": {
                      "ringtone": "alert-ring1",
                      "label": "VIP",
                      "music_on_hold": "550e8400-e29b-41d4-a716-446655440010"
                  }
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "webhook",
                  "is_active": true,
                  "order": 2,
                  "options": {
                      "url": "https://cockpit.voxbi.com/hooks/call"
                  }
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "send_to_email",
                  "is_active": true,
                  "order": 3,
                  "options": {
                      "sound_file": "550e8400-e29b-41d4-a716-446655440060",
                      "language": "fr",
                      "email": "support@example.com",
                      "email_user": [
                          "550e8400-e29b-41d4-a716-446655440011"
                      ],
                      "email_to_called_user": false
                  }
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "conference_room",
                  "is_active": true,
                  "order": 0,
                  "options": {
                      "extension": "550e8400-e29b-41d4-a716-446655440030"
                  }
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "ivr_extension",
                  "is_active": true,
                  "order": 0,
                  "options": {
                      "sound_file": "550e8400-e29b-41d4-a716-446655440060",
                      "language": "fr"
                  }
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "multicast",
                  "is_active": true,
                  "order": 0,
                  "options": {
                      "multicast_group": "paging-floor-1",
                      "multicast_pin": "1234"
                  }
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "ai_assistant",
                  "is_active": true,
                  "order": 0,
                  "options": {
                      "ai_provider": "famulor",
                      "famulor_agent": "550e8400-e29b-41d4-a716-446655440070"
                  }
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "hang_up",
                  "is_active": true,
                  "order": 4,
                  "options": []
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "start_recording",
                  "is_active": true,
                  "order": 0,
                  "options": []
              },
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "type": "answer",
                  "is_active": true,
                  "order": 0,
                  "options": []
              }
          ]
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /call-flow-steps/{id}

Get a Call flow step

Returns the call flow step identified by the path parameter. Multi-tenant scoping is
enforced: the caller can only fetch resources within their own PBX.

`operationId: getCallFlowStep`

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  [
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "ring_user",
          "is_active": true,
          "order": 0,
          "options": {
              "duration": 20,
              "also_ring_dialed_extension": false,
              "also_ring_everyone_in_the_user_group": false,
              "dial_mobile_twinnings": false,
              "missed_calls": true,
              "music_on_hold": "550e8400-e29b-41d4-a716-446655440010",
              "users": [
                  "550e8400-e29b-41d4-a716-446655440011"
              ],
              "sip_devices": [
                  "550e8400-e29b-41d4-a716-446655440012"
              ],
              "groups": [
                  "550e8400-e29b-41d4-a716-446655440013"
              ],
              "tags": [
                  "550e8400-e29b-41d4-a716-446655440014"
              ],
              "locations": [
                  "550e8400-e29b-41d4-a716-446655440015"
              ],
              "languages": [
                  "550e8400-e29b-41d4-a716-446655440016"
              ],
              "pools": [
                  "550e8400-e29b-41d4-a716-446655440017"
              ],
              "statuses": [
                  "550e8400-e29b-41d4-a716-446655440018"
              ],
              "pickup_lines": [
                  "550e8400-e29b-41d4-a716-446655440019"
              ],
              "sub_statuses": {
                  "550e8400-e29b-41d4-a716-446655440018": [
                      "550e8400-e29b-41d4-a716-446655440020"
                  ]
              }
          }
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "ring_external_number",
          "is_active": true,
          "order": 1,
          "options": {
              "duration": 30,
              "number": "+12125550100",
              "music_on_hold": "550e8400-e29b-41d4-a716-446655440010"
          }
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "call_flow",
          "is_active": true,
          "order": 2,
          "options": {
              "call_flow_or_extension": "call_flow",
              "extension": "550e8400-e29b-41d4-a716-446655440030",
              "call_flow": "550e8400-e29b-41d4-a716-446655440031",
              "schedule": "550e8400-e29b-41d4-a716-446655440032"
          }
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "go_to_ivr",
          "is_active": true,
          "order": 0,
          "options": {
              "ivr": "550e8400-e29b-41d4-a716-446655440040"
          }
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "enter_queue",
          "is_active": true,
          "order": 1,
          "options": {
              "duration": 60,
              "queue": "550e8400-e29b-41d4-a716-446655440050"
          }
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "text_to_speech",
          "is_active": true,
          "order": 0,
          "options": {
              "announcement_type": "tts_message",
              "sound_file": "550e8400-e29b-41d4-a716-446655440060",
              "language": "fr",
              "tts_tag": "greeting_morning",
              "tts_message": "Welcome to Customer Support."
          }
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "change_ringtone_and_label",
          "is_active": true,
          "order": 0,
          "options": {
              "ringtone": "alert-ring1",
              "label": "VIP",
              "music_on_hold": "550e8400-e29b-41d4-a716-446655440010"
          }
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "webhook",
          "is_active": true,
          "order": 2,
          "options": {
              "url": "https://cockpit.voxbi.com/hooks/call"
          }
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "send_to_email",
          "is_active": true,
          "order": 3,
          "options": {
              "sound_file": "550e8400-e29b-41d4-a716-446655440060",
              "language": "fr",
              "email": "support@example.com",
              "email_user": [
                  "550e8400-e29b-41d4-a716-446655440011"
              ],
              "email_to_called_user": false
          }
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "conference_room",
          "is_active": true,
          "order": 0,
          "options": {
              "extension": "550e8400-e29b-41d4-a716-446655440030"
          }
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "ivr_extension",
          "is_active": true,
          "order": 0,
          "options": {
              "sound_file": "550e8400-e29b-41d4-a716-446655440060",
              "language": "fr"
          }
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "multicast",
          "is_active": true,
          "order": 0,
          "options": {
              "multicast_group": "paging-floor-1",
              "multicast_pin": "1234"
          }
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "ai_assistant",
          "is_active": true,
          "order": 0,
          "options": {
              "ai_provider": "famulor",
              "famulor_agent": "550e8400-e29b-41d4-a716-446655440070"
          }
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "hang_up",
          "is_active": true,
          "order": 4,
          "options": []
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "start_recording",
          "is_active": true,
          "order": 0,
          "options": []
      },
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "answer",
          "is_active": true,
          "order": 0,
          "options": []
      }
  ]
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### Contacts

Shared address book entries available across the PBX.

#### GET /contacts

List contact

Returns a paginated list of contacts. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listContacts`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — Filter the list by one or more fields. Allowed keys: `id`, `name1`, `name2`, `email`, `company_name`, `department`, `job_title`, `is_private`, `is_blocked_globally`, `private_owner_id`.
Apply several at once with filter[key1]=value1&filter[key2]=value2.
- `include` (query, string, optional) — Comma-separated related resources to embed (dot-nested where
shown). Allowed: `numbers`, `groups`, `blockedByUsers`.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "company_name": "Acme Corp",
              "department": "Engineering",
              "job_title": "Head of Product",
              "name1": "Jane",
              "name2": "Smith",
              "email": "jane.smith@example.com",
              "notes": "Calls every Monday morning",
              "vip": false,
              "numbers": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440010",
                      "phone_number": "+12125550100",
                      "label": "Direct line",
                      "type": "business",
                      "speed_dial": "201",
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "is_private": false,
              "private_owner_id": "550e8400-e29b-41d4-a716-446655440002",
              "editable": true,
              "is_favorite": false,
              "groups": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                      "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
                      "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
                      "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
                      "external_uid": "grp-support",
                      "allow_call_pickups": true,
                      "name": "Support team",
                      "description": "First-line support",
                      "allow_default_pbxes_outgoing_phone_numbers": false,
                      "display_tab_to_everyone_inside_voxbi": false,
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z",
                      "users": [
                          {
                              "id": "\u2026",
                              "first_name": "\u2026",
                              "last_name": "\u2026",
                              "email": "\u2026"
                          }
                      ]
                  }
              ],
              "is_blocked_globally": false,
              "blocked_by_users": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440006",
                      "first_name": "Jane",
                      "last_name": "Smith",
                      "email": "jane.smith@example.com"
                  }
              ],
              "handling_strategy": {
                  "type": "call_flow",
                  "call_flow_id": "string?<uuid>",
                  "extension_id": "string?<uuid>",
                  "user_id": "string?<uuid>",
                  "sip_device_id": "string?<uuid>"
              },
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /contacts

Create a contact

Create an address-book contact. A contact is either private (owned by
`private_owner_id`) or shared with a set of `groups` (the two are
mutually exclusive). Phone numbers are sent in the nested
`contactPhoneNumbers[]` array. `is_private` is derived from
`private_owner_id` server-side and must not be sent.

The optional `handling_strategy` defines per-contact inbound routing.

`operationId: createContact`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "name1": "string",
    "name2": "string?",
    "email": "string?<email>",
    "company_name": "string?",
    "department": "string?",
    "job_title": "string?",
    "notes": "string?",
    "private_owner_id": "string?<uuid>",
    "groups": [
        "string<uuid>"
    ],
    "contactPhoneNumbers": [
        {
            "phone_number": "string",
            "label": "string?",
            "type": "string",
            "speed_dial": "string?"
        }
    ],
    "handling_strategy": "object?"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "company_name": "Acme Corp",
          "department": "Engineering",
          "job_title": "Head of Product",
          "name1": "Jane",
          "name2": "Smith",
          "email": "jane.smith@example.com",
          "notes": "Calls every Monday morning",
          "vip": false,
          "numbers": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440010",
                  "phone_number": "+12125550100",
                  "label": "Direct line",
                  "type": "business",
                  "speed_dial": "201",
                  "created_at": "2024-03-01T08:29:07Z",
                  "updated_at": "2024-03-01T08:29:07Z"
              }
          ],
          "is_private": false,
          "private_owner_id": "550e8400-e29b-41d4-a716-446655440002",
          "editable": true,
          "is_favorite": false,
          "groups": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
                  "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
                  "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
                  "external_uid": "grp-support",
                  "allow_call_pickups": true,
                  "name": "Support team",
                  "description": "First-line support",
                  "allow_default_pbxes_outgoing_phone_numbers": false,
                  "display_tab_to_everyone_inside_voxbi": false,
                  "created_at": "2024-03-01T08:29:07Z",
                  "updated_at": "2024-03-01T08:29:07Z",
                  "users": [
                      {
                          "id": "550e8400-e29b-41d4-a716-446655440006",
                          "first_name": "Jane",
                          "last_name": "Smith",
                          "email": "jane.smith@example.com"
                      }
                  ]
              }
          ],
          "is_blocked_globally": false,
          "blocked_by_users": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440006",
                  "first_name": "Jane",
                  "last_name": "Smith",
                  "email": "jane.smith@example.com"
              }
          ],
          "handling_strategy": {
              "type": "call_flow",
              "call_flow_id": "string?<uuid>",
              "extension_id": "string?<uuid>",
              "user_id": "string?<uuid>",
              "sip_device_id": "string?<uuid>"
          },
          "created_at": "2024-03-01T08:29:07Z",
          "updated_at": "2024-03-01T08:29:07Z"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /contacts/{contact}

Get a Contact

Returns the contact identified by the path parameter. Multi-tenant scoping is enforced: the
caller can only fetch resources within their own PBX.

`operationId: getContact`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "company_name": "Acme Corp",
      "department": "Engineering",
      "job_title": "Head of Product",
      "name1": "Jane",
      "name2": "Smith",
      "email": "jane.smith@example.com",
      "notes": "Calls every Monday morning",
      "vip": false,
      "numbers": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440010",
              "phone_number": "+12125550100",
              "label": "Direct line",
              "type": "business",
              "speed_dial": "201",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "is_private": false,
      "private_owner_id": "550e8400-e29b-41d4-a716-446655440002",
      "editable": true,
      "is_favorite": false,
      "groups": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
              "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
              "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
              "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
              "external_uid": "grp-support",
              "allow_call_pickups": true,
              "name": "Support team",
              "description": "First-line support",
              "allow_default_pbxes_outgoing_phone_numbers": false,
              "display_tab_to_everyone_inside_voxbi": false,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z",
              "users": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440006",
                      "first_name": "Jane",
                      "last_name": "Smith",
                      "email": "jane.smith@example.com"
                  }
              ]
          }
      ],
      "is_blocked_globally": false,
      "blocked_by_users": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440006",
              "first_name": "Jane",
              "last_name": "Smith",
              "email": "jane.smith@example.com"
          }
      ],
      "handling_strategy": {
          "type": "call_flow",
          "call_flow_id": "string?<uuid>",
          "extension_id": "string?<uuid>",
          "user_id": "string?<uuid>",
          "sip_device_id": "string?<uuid>"
      },
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### PUT /contacts/{contact}

Update a contact

Update an address-book contact. Same request shape as creation, with
`name1` still required. `is_private` is derived from `private_owner_id`
server-side.

`operationId: updateContact`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — Contact identifier

**Request body** (`application/json`) (required):
```json
{
    "name1": "string",
    "name2": "string?",
    "email": "string?<email>",
    "company_name": "string?",
    "department": "string?",
    "job_title": "string?",
    "notes": "string?",
    "private_owner_id": "string?<uuid>",
    "groups": [
        "string<uuid>"
    ],
    "contactPhoneNumbers": [
        {
            "phone_number": "string",
            "label": "string?",
            "type": "string",
            "speed_dial": "string?"
        }
    ],
    "handling_strategy": "object?"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "company_name": "Acme Corp",
          "department": "Engineering",
          "job_title": "Head of Product",
          "name1": "Jane",
          "name2": "Smith",
          "email": "jane.smith@example.com",
          "notes": "Calls every Monday morning",
          "vip": false,
          "numbers": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440010",
                  "phone_number": "+12125550100",
                  "label": "Direct line",
                  "type": "business",
                  "speed_dial": "201",
                  "created_at": "2024-03-01T08:29:07Z",
                  "updated_at": "2024-03-01T08:29:07Z"
              }
          ],
          "is_private": false,
          "private_owner_id": "550e8400-e29b-41d4-a716-446655440002",
          "editable": true,
          "is_favorite": false,
          "groups": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                  "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
                  "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
                  "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
                  "external_uid": "grp-support",
                  "allow_call_pickups": true,
                  "name": "Support team",
                  "description": "First-line support",
                  "allow_default_pbxes_outgoing_phone_numbers": false,
                  "display_tab_to_everyone_inside_voxbi": false,
                  "created_at": "2024-03-01T08:29:07Z",
                  "updated_at": "2024-03-01T08:29:07Z",
                  "users": [
                      {
                          "id": "550e8400-e29b-41d4-a716-446655440006",
                          "first_name": "Jane",
                          "last_name": "Smith",
                          "email": "jane.smith@example.com"
                      }
                  ]
              }
          ],
          "is_blocked_globally": false,
          "blocked_by_users": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440006",
                  "first_name": "Jane",
                  "last_name": "Smith",
                  "email": "jane.smith@example.com"
              }
          ],
          "handling_strategy": {
              "type": "call_flow",
              "call_flow_id": "string?<uuid>",
              "extension_id": "string?<uuid>",
              "user_id": "string?<uuid>",
              "sip_device_id": "string?<uuid>"
          },
          "created_at": "2024-03-01T08:29:07Z",
          "updated_at": "2024-03-01T08:29:07Z"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### DELETE /contacts/{contact}

Delete a contact

Soft-delete a contact. The contact is removed from list responses but can be restored by an administrator.

`operationId: deleteContact`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — Contact identifier

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `204` — Deleted (no content)
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### POST /contacts/{id}/block

Block a contact

Triggers the action on the targeted contact block.

`operationId: postContactBlock`

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, integer, required) — Contact ID

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "status": "string",
      "message": "string"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### POST /contacts/{id}/unblock

Unblock a contact

Triggers the action on the targeted contact unblock.

`operationId: postContactUnblock`

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, integer, required) — Contact ID

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "status": "string",
      "message": "string"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### POST /contacts/favorite

Toggle a contact as favorite

Creates a new contact favorite. Validation rules and required fields are defined in the
request body schema below.

`operationId: postContactFavorite`

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "contact_id": 1
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "status": "string",
      "is_favorite": "boolean"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /source/{source}/contacts

Get contacts from a specific source

Returns the contact source contacts identified by the path parameter. Multi-tenant scoping
is enforced: the caller can only fetch resources within their own PBX.

`operationId: getContactSourceContacts`

**Parameters**:
- `accept` (header, string, optional)
- `source` (path, string, required) — Source identifier
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "company_name": "Acme Corp",
              "department": "Engineering",
              "job_title": "Head of Product",
              "name1": "Jane",
              "name2": "Smith",
              "email": "jane.smith@example.com",
              "notes": "Calls every Monday morning",
              "vip": false,
              "numbers": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440010",
                      "phone_number": "+12125550100",
                      "label": "Direct line",
                      "type": "business",
                      "speed_dial": "201",
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "is_private": false,
              "private_owner_id": "550e8400-e29b-41d4-a716-446655440002",
              "editable": true,
              "is_favorite": false,
              "groups": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                      "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
                      "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
                      "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
                      "external_uid": "grp-support",
                      "allow_call_pickups": true,
                      "name": "Support team",
                      "description": "First-line support",
                      "allow_default_pbxes_outgoing_phone_numbers": false,
                      "display_tab_to_everyone_inside_voxbi": false,
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z",
                      "users": [
                          {
                              "id": "\u2026",
                              "first_name": "\u2026",
                              "last_name": "\u2026",
                              "email": "\u2026"
                          }
                      ]
                  }
              ],
              "is_blocked_globally": false,
              "blocked_by_users": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440006",
                      "first_name": "Jane",
                      "last_name": "Smith",
                      "email": "jane.smith@example.com"
                  }
              ],
              "handling_strategy": {
                  "type": "call_flow",
                  "call_flow_id": "string?<uuid>",
                  "extension_id": "string?<uuid>",
                  "user_id": "string?<uuid>",
                  "sip_device_id": "string?<uuid>"
              },
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

### Contact Imports

Import personal contacts from a CSV or Excel file via a guided upload, map, and confirm flow. Each import and every contact it creates is private to the authenticated user.

#### GET /contacts/imports

List your contact imports (user)

Returns a paginated list of the authenticated user's contact imports, newest
first. This endpoint accepts a USER token only (not a PBX API key): a user
only ever sees their own imports.

Use `filter[status]=incomplete` to narrow by lifecycle stage, and
`?include=records,errors` to embed row-level detail.

`operationId: listContactImports`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Sort field. Prefix with `-` for descending. Allowed: `created_at`,
`completed_at`, `status`. Defaults to `-created_at`.
- `filter[status]` (query, string, optional) — Filter by exact lifecycle status (e.g. `incomplete`, `completed`).
- `include` (query, string, optional) — Comma-separated related detail to embed. Allowed: `records`, `errors`.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "status": "incomplete",
              "file_type": "csv",
              "records_count": 42,
              "errors_count": 3,
              "can_confirm": true,
              "header_row_position": 1,
              "sheet_index": "integer?",
              "delimiter": "string?",
              "enclosure": "\"",
              "input_encoding": "UTF-8",
              "skip_on_error": true,
              "notify_owner_by_email": false,
              "webhook_url": "string?<uri>",
              "mappings": [
                  {
                      "field": "name1",
                      "column": 0,
                      "subfields": [
                          {
                              "name": "\u2026",
                              "value": "\u2026"
                          }
                      ]
                  }
              ],
              "created_at": "2026-01-15T09:30:00Z",
              "started_at": "2026-01-15T09:31:00Z",
              "completed_at": "2026-01-15T09:31:45Z",
              "records": [
                  {
                      "model_id": "550e8400-e29b-41d4-a716-446655440010",
                      "is_new": true,
                      "was_trashed": false
                  }
              ],
              "errors": [
                  {
                      "row": 7,
                      "attribute": "email",
                      "messages": [
                          "The email must be a valid email address."
                      ],
                      "row_data": {
                          "name1": "Jane",
                          "email": "not-an-email"
                      }
                  }
              ]
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### POST /contacts/imports

Upload a contact import file (user)

Uploads a CSV or Excel file and creates an import in the `incomplete` state.
The file is only stored at this step: no contacts are created yet. Next,
configure parsing via the settings endpoint, map columns via the mapping
endpoints, then confirm.

Accepts a USER token only. The created import (and every contact it will
create) is private to the authenticated user. Send the file as
`multipart/form-data` under the `file` key. Accepted types: CSV, XLS, XLSX.

Rate limited: at most one import may be created per minute per user. Only
successful creates count, so a rejected request (422) never blocks a valid
retry. Configuring an existing import is not limited.

Notification preferences may be set here or via the settings endpoint.

`operationId: createContactImport`

**Auth**: bearerAuth

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created. The response is the import resource itself (unwrapped, not under
a `data` key). Fetch the detected columns next via the mapping-options
endpoint.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "status": "incomplete",
      "file_type": "csv",
      "records_count": 42,
      "errors_count": 3,
      "can_confirm": true,
      "header_row_position": 1,
      "sheet_index": "integer?",
      "delimiter": "string?",
      "enclosure": "\"",
      "input_encoding": "UTF-8",
      "skip_on_error": true,
      "notify_owner_by_email": false,
      "webhook_url": "string?<uri>",
      "mappings": [
          {
              "field": "name1",
              "column": 0,
              "subfields": [
                  {
                      "name": "type",
                      "value": "mobile"
                  }
              ]
          }
      ],
      "created_at": "2026-01-15T09:30:00Z",
      "started_at": "2026-01-15T09:31:00Z",
      "completed_at": "2026-01-15T09:31:45Z",
      "records": [
          {
              "model_id": "550e8400-e29b-41d4-a716-446655440010",
              "is_new": true,
              "was_trashed": false
          }
      ],
      "errors": [
          {
              "row": 7,
              "attribute": "email",
              "messages": [
                  "The email must be a valid email address."
              ],
              "row_data": {
                  "name1": "Jane",
                  "email": "not-an-email"
              }
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.
- `429` — Too Many Requests. The rate limit for this action has been exceeded. Retry
after the window indicated by the Retry-After header.

#### GET /contacts/imports/sample

Download a sample import file (user)

Downloads a headers-only sample CSV showing the contact fields the import
accepts (the same file the Cockpit import wizard offers). Column headers are
illustrative only: the import mapping is free-form, so you map your own
file's columns onto the target fields regardless of their headers.

`operationId: getContactImportSample`

**Auth**: bearerAuth

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK. A CSV file download.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### GET /contacts/imports/{id}

Get one of your contact imports (user)

Returns a single import owned by the authenticated user. Use
`?include=records,errors` to embed the imported contacts and any row-level
failures. Accepts a USER token only; another user's import returns 404.

`operationId: getContactImport`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — Import identifier
- `include` (query, string, optional) — Comma-separated related detail to embed. Allowed: `records`, `errors`.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK. The import resource itself (unwrapped, not under a `data` key).
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "status": "incomplete",
      "file_type": "csv",
      "records_count": 42,
      "errors_count": 3,
      "can_confirm": true,
      "header_row_position": 1,
      "sheet_index": "integer?",
      "delimiter": "string?",
      "enclosure": "\"",
      "input_encoding": "UTF-8",
      "skip_on_error": true,
      "notify_owner_by_email": false,
      "webhook_url": "string?<uri>",
      "mappings": [
          {
              "field": "name1",
              "column": 0,
              "subfields": [
                  {
                      "name": "type",
                      "value": "mobile"
                  }
              ]
          }
      ],
      "created_at": "2026-01-15T09:30:00Z",
      "started_at": "2026-01-15T09:31:00Z",
      "completed_at": "2026-01-15T09:31:45Z",
      "records": [
          {
              "model_id": "550e8400-e29b-41d4-a716-446655440010",
              "is_new": true,
              "was_trashed": false
          }
      ],
      "errors": [
          {
              "row": 7,
              "attribute": "email",
              "messages": [
                  "The email must be a valid email address."
              ],
              "row_data": {
                  "name1": "Jane",
                  "email": "not-an-email"
              }
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### DELETE /contacts/imports/{id}

Delete one of your contact imports (user)

Permanently deletes one of the authenticated user's imports, along with its
row-level records, errors, and the stored source file. An import in any
status may be deleted; the contacts it already created are NOT removed.
Accepts a USER token only; another user's import returns 404.

`operationId: deleteContactImport`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — Import identifier

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `204` — No Content
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### GET /contacts/imports/{id}/settings

Get parsing settings for an import (user)

Returns the parsing settings that apply to this import's file type, each with
its type, default, and current value. The client renders exactly these; CSV
imports expose `delimiter`, `enclosure`, and `input_encoding`, while Excel
imports expose `sheet_index`. Only an `incomplete` import can be read here
(otherwise 409).

`operationId: getContactImportSettings`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — Import identifier

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  [
      {
          "key": "input_encoding",
          "type": "string",
          "default": "UTF-8",
          "current": "string",
          "allowed_values": [
              "UTF-8",
              "ISO-8859-1"
          ]
      }
  ]
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `409` — Conflict. The import is no longer in a state that allows this action (for
example, it has already been submitted for processing). Only an `incomplete`
import can be configured or confirmed.

#### PUT /contacts/imports/{id}/settings

Update parsing settings for an import (user)

Updates the parsing settings for an `incomplete` import. Send only the keys
you want to change. Only an `incomplete` import can be updated (otherwise
409).

`operationId: updateContactImportSettings`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — Import identifier

**Request body** (`application/json`) (required):
```json
{
    "header_row_position": "integer?",
    "sheet_index": "integer?",
    "delimiter": "string?",
    "enclosure": "string?",
    "input_encoding": "string?",
    "skip_on_error": "boolean",
    "notify_owner_by_email": "boolean",
    "webhook_url": "string?<uri>"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK. The updated import resource (unwrapped). It carries the setting
values just set (header_row_position, notify_owner_by_email, ...).
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "status": "incomplete",
      "file_type": "csv",
      "records_count": 42,
      "errors_count": 3,
      "can_confirm": true,
      "header_row_position": 1,
      "sheet_index": "integer?",
      "delimiter": "string?",
      "enclosure": "\"",
      "input_encoding": "UTF-8",
      "skip_on_error": true,
      "notify_owner_by_email": false,
      "webhook_url": "string?<uri>",
      "mappings": [
          {
              "field": "name1",
              "column": 0,
              "subfields": [
                  {
                      "name": "type",
                      "value": "mobile"
                  }
              ]
          }
      ],
      "created_at": "2026-01-15T09:30:00Z",
      "started_at": "2026-01-15T09:31:00Z",
      "completed_at": "2026-01-15T09:31:45Z",
      "records": [
          {
              "model_id": "550e8400-e29b-41d4-a716-446655440010",
              "is_new": true,
              "was_trashed": false
          }
      ],
      "errors": [
          {
              "row": 7,
              "attribute": "email",
              "messages": [
                  "The email must be a valid email address."
              ],
              "row_data": {
                  "name1": "Jane",
                  "email": "not-an-email"
              }
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `409` — Conflict. The import is no longer in a state that allows this action (for
example, it has already been submitted for processing). Only an `incomplete`
import can be configured or confirmed.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /contacts/imports/{id}/mapping-options

Get column mapping options for an import (user)

Returns everything the client needs to render the mapping step: the detected
file columns (each with a stable `id` used as `column` in the mapping
payload) and the allow-listed target fields a column may be mapped to. Only
these target fields are accepted when saving a mapping: no other model
attribute (e.g. `private_owner_id`, `pbx_id`, groups) can be targeted, and
nothing is auto-mapped. Only an `incomplete` import can be read here
(otherwise 409).

`operationId: getContactImportMappingOptions`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — Import identifier

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "file_columns": [
          {
              "id": 0,
              "name": "First name"
          }
      ],
      "target_fields": [
          {
              "field": "name1",
              "label": "First name",
              "type": "text",
              "multiple": true,
              "subfields": [
                  {
                      "name": "type",
                      "type": "enum",
                      "options": [
                          "business",
                          "mobile",
                          "other",
                          "fax"
                      ]
                  }
              ]
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `409` — Conflict. The import is no longer in a state that allows this action (for
example, it has already been submitted for processing). Only an `incomplete`
import can be configured or confirmed.

#### PUT /contacts/imports/{id}/mappings

Save the column mapping for an import (user)

Saves how the file's columns map onto contact fields. Each entry maps one
column (`column` = a `file_columns[].id` from mapping-options) onto one
`field`. `field` must be one of the allow-listed target fields; any other
value (including `private_owner_id`, `pbx_id`, `is_private`, or a groups
column) is rejected with 422, so a client cannot forge a mapping to assign
contacts to another user or tenant. A `contactPhoneNumbers` mapping must
carry a `type` sub-field. A single-value field may be mapped to at most one
column; only fields flagged `multiple: true` (currently
`contactPhoneNumbers`) may take several columns, otherwise a duplicate
mapping is rejected with 422. Only an `incomplete` import can be mapped
(otherwise 409).

`operationId: updateContactImportMappings`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — Import identifier

**Request body** (`application/json`) (required):
```json
{
    "mappings": [
        {
            "field": "name1",
            "column": 0,
            "subfields": [
                {
                    "name": "type",
                    "value": "mobile"
                }
            ]
        }
    ]
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK. The updated import resource (unwrapped).
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "status": "incomplete",
      "file_type": "csv",
      "records_count": 42,
      "errors_count": 3,
      "can_confirm": true,
      "header_row_position": 1,
      "sheet_index": "integer?",
      "delimiter": "string?",
      "enclosure": "\"",
      "input_encoding": "UTF-8",
      "skip_on_error": true,
      "notify_owner_by_email": false,
      "webhook_url": "string?<uri>",
      "mappings": [
          {
              "field": "name1",
              "column": 0,
              "subfields": [
                  {
                      "name": "type",
                      "value": "mobile"
                  }
              ]
          }
      ],
      "created_at": "2026-01-15T09:30:00Z",
      "started_at": "2026-01-15T09:31:00Z",
      "completed_at": "2026-01-15T09:31:45Z",
      "records": [
          {
              "model_id": "550e8400-e29b-41d4-a716-446655440010",
              "is_new": true,
              "was_trashed": false
          }
      ],
      "errors": [
          {
              "row": 7,
              "attribute": "email",
              "messages": [
                  "The email must be a valid email address."
              ],
              "row_data": {
                  "name1": "Jane",
                  "email": "not-an-email"
              }
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `409` — Conflict. The import is no longer in a state that allows this action (for
example, it has already been submitted for processing). Only an `incomplete`
import can be configured or confirmed.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /contacts/imports/{id}/confirm

Confirm and start an import (user)

Confirms an `incomplete` import and queues it for background processing. The
import must have a saved mapping (`can_confirm: true`) or a 422 is returned.
On success the import moves to `pending` and is processed asynchronously;
poll the get-import endpoint for the final status and counts. Only an
`incomplete` import can be confirmed (otherwise 409).

Imports created in the Cockpit admin are not drivable here, and an import
started via this API cannot be completed from the Cockpit admin: the two
flows are kept separate.

`operationId: postContactImportConfirm`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — Import identifier

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK. The import resource (unwrapped), now `pending`.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "status": "incomplete",
      "file_type": "csv",
      "records_count": 42,
      "errors_count": 3,
      "can_confirm": true,
      "header_row_position": 1,
      "sheet_index": "integer?",
      "delimiter": "string?",
      "enclosure": "\"",
      "input_encoding": "UTF-8",
      "skip_on_error": true,
      "notify_owner_by_email": false,
      "webhook_url": "string?<uri>",
      "mappings": [
          {
              "field": "name1",
              "column": 0,
              "subfields": [
                  {
                      "name": "type",
                      "value": "mobile"
                  }
              ]
          }
      ],
      "created_at": "2026-01-15T09:30:00Z",
      "started_at": "2026-01-15T09:31:00Z",
      "completed_at": "2026-01-15T09:31:45Z",
      "records": [
          {
              "model_id": "550e8400-e29b-41d4-a716-446655440010",
              "is_new": true,
              "was_trashed": false
          }
      ],
      "errors": [
          {
              "row": 7,
              "attribute": "email",
              "messages": [
                  "The email must be a valid email address."
              ],
              "row_data": {
                  "name1": "Jane",
                  "email": "not-an-email"
              }
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `409` — Conflict. The import is no longer in a state that allows this action (for
example, it has already been submitted for processing). Only an `incomplete`
import can be configured or confirmed.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

### Extensions

Internal dial numbers assigned to users, devices, queues, and other PBX entities.

#### GET /extensions

List extension

Returns a paginated list of extensions. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listExtensions`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — Filter the list by one or more fields. Allowed keys: `number`, `description`, `assigned_to_type`, `user.show_in_address_book`.
Apply several at once with filter[key1]=value1&filter[key2]=value2.
- `include` (query, string, optional) — Comma-separated related resources to embed (dot-nested where
shown). Allowed: `famulorAgent`, `sipDevice`, `user`, `user.employee`, `user.groups`, `user.callerIdentities`, `user.callerIdentities.phoneNumber`, `user.sipDevices`, `user.twinnings`, `user.queues`, `user.queues.statuses`, `user.locations`.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "assigned_to_type": "user",
              "assigned_to_id": "550e8400-e29b-41d4-a716-446655440002",
              "external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
              "internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440004",
              "number": "1337",
              "name": "Support line",
              "description": "Front-line support line",
              "record_external_calls": true,
              "record_internal_calls": false,
              "sync_source": "manual",
              "full_name_with_extension_number": "Jane Smith (1337)",
              "assigned_to": [],
              "employee_id": "550e8400-e29b-41d4-a716-446655440005",
              "department_id": "550e8400-e29b-41d4-a716-446655440006",
              "is_favorite": false,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /extensions/{id}

Get an Extension

Returns the extension identified by the path parameter. Multi-tenant scoping is enforced:
the caller can only fetch resources within their own PBX.

`operationId: getExtension`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "assigned_to_type": "user",
      "assigned_to_id": "550e8400-e29b-41d4-a716-446655440002",
      "external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
      "internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440004",
      "number": "1337",
      "name": "Support line",
      "description": "Front-line support line",
      "record_external_calls": true,
      "record_internal_calls": false,
      "sync_source": "manual",
      "full_name_with_extension_number": "Jane Smith (1337)",
      "assigned_to": [],
      "employee_id": "550e8400-e29b-41d4-a716-446655440005",
      "department_id": "550e8400-e29b-41d4-a716-446655440006",
      "is_favorite": false,
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### GET /favorite-extensions

Get favorite extensions for the authenticated user

Returns a paginated list of extension favorites. Use the standard `search`, `sort`, `page`,
and `perPage` query parameters; use `filter[key]=value` to narrow the result set.
Multi-tenant scoping is enforced.

`operationId: getExtensionFavorites`

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "integer",
              "pbx_id": "integer",
              "extension": "string",
              "user_id": "integer?",
              "created_at": "string<date-time>",
              "updated_at": "string<date-time>"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

### Groups

Ring groups that dial multiple destinations simultaneously or in sequence.

#### GET /groups

List group

Returns a paginated list of groups. Use the standard `search`, `sort`, `page`, and `perPage`
query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant scoping is
enforced.

`operationId: listGroups`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — Filter the list by one or more fields. Allowed keys: `name`, `description`.
Apply several at once with filter[key1]=value1&filter[key2]=value2.
- `include` (query, string, optional) — Comma-separated related resources to embed (dot-nested where
shown). Allowed: `users`.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
              "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
              "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
              "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
              "external_uid": "grp-support",
              "allow_call_pickups": true,
              "name": "Support team",
              "description": "First-line support",
              "allow_default_pbxes_outgoing_phone_numbers": false,
              "display_tab_to_everyone_inside_voxbi": false,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z",
              "users": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440006",
                      "first_name": "Jane",
                      "last_name": "Smith",
                      "email": "jane.smith@example.com"
                  }
              ]
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /groups

Create a group

Create a group and optionally attach users, group permissions, and
pickup-lines / phone numbers. `pbx_id` is forced from the authenticated user.

`operationId: createGroup`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "pbx_id": "string<uuid>",
    "name": "string",
    "description": "string?",
    "defaultInternalCallFlow": "string?<uuid>",
    "defaultExternalCallFlow": "string?<uuid>",
    "users": [
        "string<uuid>"
    ],
    "pickupUsers": [
        "string<uuid>"
    ],
    "allowManageGroupContacts": [
        "string<uuid>"
    ],
    "allowAccessToGroupContacts": [
        "string<uuid>"
    ],
    "phoneNumberAssignations": [
        "string<uuid>"
    ],
    "allow_call_pickups": "boolean",
    "allow_default_pbxes_outgoing_phone_numbers": "boolean",
    "display_tab_to_everyone_inside_voxbi": "boolean",
    "callerIdentities": [
        {
            "phoneNumber": "string<uuid>",
            "suffix": "string?",
            "label": "string?"
        }
    ]
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
          "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
          "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
          "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
          "external_uid": "grp-support",
          "allow_call_pickups": true,
          "name": "Support team",
          "description": "First-line support",
          "allow_default_pbxes_outgoing_phone_numbers": false,
          "display_tab_to_everyone_inside_voxbi": false,
          "created_at": "2024-03-01T08:29:07Z",
          "updated_at": "2024-03-01T08:29:07Z",
          "users": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440006",
                  "first_name": "Jane",
                  "last_name": "Smith",
                  "email": "jane.smith@example.com"
              }
          ]
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /groups/{id}

Get a Group

Returns the group identified by the path parameter. Multi-tenant scoping is enforced: the
caller can only fetch resources within their own PBX.

`operationId: getGroup`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
      "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
      "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
      "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
      "external_uid": "grp-support",
      "allow_call_pickups": true,
      "name": "Support team",
      "description": "First-line support",
      "allow_default_pbxes_outgoing_phone_numbers": false,
      "display_tab_to_everyone_inside_voxbi": false,
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z",
      "users": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440006",
              "first_name": "Jane",
              "last_name": "Smith",
              "email": "jane.smith@example.com"
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### IVR

Interactive Voice Response menus that play a prompt and route the caller based on the key they press.

#### GET /ivrs

List ivr

Returns a paginated list of ivrs. Use the standard `search`, `sort`, `page`, and `perPage`
query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant scoping is
enforced.

`operationId: listIvrs`

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — The filter key can be any of the following keys: id, pbx_id, name and comment.

The filter value can be any string or number.

You can also apply multiple filters using `filter[key1]=value1&filter[key2]=value2`.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Customer Support Menu",
              "comment": "After-hours routing for the support line",
              "announcement_id": "550e8400-e29b-41d4-a716-446655440002",
              "announcement_language": "fr",
              "steps": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "ivr_id": "550e8400-e29b-41d4-a716-446655440002",
                      "target_type": "App\\Models\\CallFlow",
                      "target_id": "550e8400-e29b-41d4-a716-446655440003",
                      "name": "Sales department",
                      "option": "1",
                      "language": "fr",
                      "voip_ivr_btn_id": 42,
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z",
                      "deleted_at": "string?<date-time>"
                  }
              ]
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /ivrs

Create an IVR

Create an IVR menu. `announcement` is the SoundFile of type `announcement`
played when entering the menu; `musicOnHold` is an optional SoundFile of
type `music`. `default_action` controls what happens on caller timeout.

`operationId: createIvr`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "pbx_id": "string<uuid>",
    "name": "string",
    "comment": "string?",
    "announcement": "string<uuid>",
    "musicOnHold": "string?<uuid>",
    "default_action": "enum"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "name": "Customer Support Menu",
          "comment": "After-hours routing for the support line",
          "announcement_id": "550e8400-e29b-41d4-a716-446655440002",
          "announcement_language": "fr",
          "steps": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "ivr_id": "550e8400-e29b-41d4-a716-446655440002",
                  "target_type": "App\\Models\\CallFlow",
                  "target_id": "550e8400-e29b-41d4-a716-446655440003",
                  "name": "Sales department",
                  "option": "1",
                  "language": "fr",
                  "voip_ivr_btn_id": 42,
                  "created_at": "2024-03-01T08:29:07Z",
                  "updated_at": "2024-03-01T08:29:07Z",
                  "deleted_at": "string?<date-time>"
              }
          ]
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /ivrs/{id}

Get an IVR

Returns the ivr identified by the path parameter. Multi-tenant scoping is enforced: the
caller can only fetch resources within their own PBX.

`operationId: getIvr`

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "name": "Customer Support Menu",
      "comment": "After-hours routing for the support line",
      "announcement_id": "550e8400-e29b-41d4-a716-446655440002",
      "announcement_language": "fr",
      "steps": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "ivr_id": "550e8400-e29b-41d4-a716-446655440002",
              "target_type": "App\\Models\\CallFlow",
              "target_id": "550e8400-e29b-41d4-a716-446655440003",
              "name": "Sales department",
              "option": "1",
              "language": "fr",
              "voip_ivr_btn_id": 42,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z",
              "deleted_at": "string?<date-time>"
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### IVR Steps

Individual menu options inside an IVR. Each step maps a digit (or timeout) to a destination.

#### GET /ivr-steps

List IVR step

Returns a paginated list of ivr steps. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listIvrSteps`

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — The filter key can be any of the following keys: id, parent_id, ivr_id, sound_file_id, name, option, delay, action_type and action_value.

The filter value can be any string or number.

You can also apply multiple filters using `filter[key1]=value1&filter[key2]=value2`.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "ivr_id": "550e8400-e29b-41d4-a716-446655440002",
              "target_type": "App\\Models\\CallFlow",
              "target_id": "550e8400-e29b-41d4-a716-446655440003",
              "name": "Sales department",
              "option": "1",
              "language": "fr",
              "voip_ivr_btn_id": 42,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z",
              "deleted_at": "string?<date-time>"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /ivr-steps/{id}

Get an IVR step

Returns the ivr step identified by the path parameter. Multi-tenant scoping is enforced: the
caller can only fetch resources within their own PBX.

`operationId: getIvrStep`

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "ivr_id": "550e8400-e29b-41d4-a716-446655440002",
      "target_type": "App\\Models\\CallFlow",
      "target_id": "550e8400-e29b-41d4-a716-446655440003",
      "name": "Sales department",
      "option": "1",
      "language": "fr",
      "voip_ivr_btn_id": 42,
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z",
      "deleted_at": "string?<date-time>"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### Languages

Languages available for sound files, voice prompts, and user preferences.

#### GET /languages

List language

Returns a paginated list of languages. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listLanguages`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — Filter the list by one or more fields. Allowed keys: `id`, `name`.
Apply several at once with filter[key1]=value1&filter[key2]=value2.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "English",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /languages

Create a language

Enable a language for the PBX. `name` must be one of the supported locale
display names and unique within the PBX. `pbx_id` is forced from the
authenticated principal (a user's PBX, or the Pbx-owned API key itself) and
is never read from the request body.

`operationId: createLanguage`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "name": "string"
}
```

**Responses**:
- `201` — Created
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "name": "English",
          "created_at": "2024-03-01T08:29:07Z",
          "updated_at": "2024-03-01T08:29:07Z"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /languages/{id}

Get a Language

Returns the language identified by the path parameter. Multi-tenant scoping is enforced: the
caller can only fetch resources within their own PBX.

`operationId: getLanguage`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "name": "English",
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### Locations

Physical offices or sites associated with the PBX, used for emergency call routing and reporting.

#### GET /locations

List location

Returns a paginated list of locations. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listLocations`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — Filter the list by one or more fields. Allowed keys: `name`, `description`.
Apply several at once with filter[key1]=value1&filter[key2]=value2.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "company_id": "550e8400-e29b-41d4-a716-446655440004",
              "parent_id": "550e8400-e29b-41d4-a716-446655440002",
              "name": "Luxembourg HQ",
              "description": "Main office, 5th floor",
              "street_no": "12",
              "street": "Main Street",
              "city": "Luxembourg",
              "postal": "1234",
              "country_id": "550e8400-e29b-41d4-a716-446655440003",
              "lft": 1,
              "rgt": 10,
              "depth": 0,
              "enable_location_as_presence_status": false,
              "style": {
                  "icon_class": "la-map-pin",
                  "bg_color": "#3490dc",
                  "font_color": "#ffffff"
              },
              "is_fixed_location": true,
              "latitude": 49.6116,
              "longitude": 6.1319,
              "geofence_radius_m": 100,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /locations/{id}

Get a Location

Returns the location identified by the path parameter. Multi-tenant scoping is enforced: the
caller can only fetch resources within their own PBX.

`operationId: getLocation`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "company_id": "550e8400-e29b-41d4-a716-446655440004",
      "parent_id": "550e8400-e29b-41d4-a716-446655440002",
      "name": "Luxembourg HQ",
      "description": "Main office, 5th floor",
      "street_no": "12",
      "street": "Main Street",
      "city": "Luxembourg",
      "postal": "1234",
      "country_id": "550e8400-e29b-41d4-a716-446655440003",
      "lft": 1,
      "rgt": 10,
      "depth": 0,
      "enable_location_as_presence_status": false,
      "style": {
          "icon_class": "la-map-pin",
          "bg_color": "#3490dc",
          "font_color": "#ffffff"
      },
      "is_fixed_location": true,
      "latitude": 49.6116,
      "longitude": 6.1319,
      "geofence_radius_m": 100,
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### Phone Number Orders

Customer-facing endpoints for the Nexus number-order proxy. Use these to
check whether a phone number can be provisioned at a given address, place
an order (with KYC documents attached), retrieve an order's status, edit
and retry an order that failed submission, list the phone numbers already
owned by the PBX, and attach a call flow to a specific number.

Authentication is the PBX API key (one token per PBX); every response is
scoped to that PBX.


#### GET /numbers

List phone numbers on the PBX

List the PhoneNumbers attached to the caller's PBX (resolved from the bearer
token). Each entry includes the currently-attached call flow (id + summary)
when one is configured.

The list is **paginated** (`data` + `meta` + `links`) and supports filtering
and sorting.

`operationId: listPbxNumbers`

**Auth**: IntegrationApiKey

**Parameters**:
- `filter[number]` (query, string, optional) — Filter by phone number (substring match). A leading `+` is ignored, so
`filter[number]=+4922` and `filter[number]=4922` both match.
- `filter[name]` (query, string, optional) — Filter by the number's name (substring match).
- `sort` (query, enum, optional) — Sort field; prefix with `-` for descending. Allowed: `number`, `name`,
`created_at`. Defaults to `number` ascending. · enum: number, -number, name, -name, created_at, -created_at
- `page` (query, integer, optional) — Page number (1-based).
- `per_page` (query, integer, optional) — Items per page. Defaults to and is capped at 100 (the API page-length
limit); larger values are clamped to 100.

**Responses**:
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "number": "+12125550100",
              "name": "Customer Support",
              "is_active": true,
              "call_flow_id": "550e8400-e29b-41d4-a716-446655440100",
              "call_flow": {
                  "id": "string<uuid>",
                  "name": "string",
                  "enabled": "boolean"
              }
          }
      ],
      "links": [],
      "meta": []
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### PUT /numbers/{id}/call-flow

Attach a call flow to a phone number

Set the handling strategy of a PhoneNumber to route incoming calls through
a specific call flow. Both the PhoneNumber and the CallFlow must belong to
the caller's PBX.

`operationId: updatePbxNumberCallFlow`

**Auth**: IntegrationApiKey

**Parameters**:
- `id` (path, string<uuid>, required) — PhoneNumber UUID.

**Request body** (`application/json`) (required):
```json
{
    "call_flow_id": "550e8400-e29b-41d4-a716-446655440100"
}
```

**Responses**:
- `200` — Updated PhoneNumber with the new call flow attached.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "number": "+12125550100",
      "name": "Customer Support",
      "is_active": true,
      "call_flow_id": "550e8400-e29b-41d4-a716-446655440100",
      "call_flow": {
          "id": "string<uuid>",
          "name": "string",
          "enabled": "boolean"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — PhoneNumber not found in the caller's PBX.
- `422` — Validation error - call_flow_id missing, malformed, or doesn't belong
to the caller's PBX.

#### POST /numbers/orders/check

Check whether a phone number can be ordered at a given address

Pre-flight check that validates an address with the upstream registry and
returns (a) the dial `prefix` for the resolved location, (b) the geocoder's
`resolvedAddress`, and (c) the `kycRequirements` that must accompany the
future order. Call this before `POST /numbers/orders`.

### Comparing the address

The address is geocoded, so `resolvedAddress` may differ from what you sent
(`address` echoes your input). Compare the two and, if they differ, confirm
the corrected values with the end user before placing the order - the order
endpoint persists the geocoder-resolved values. Structured fields in
`resolvedAddress` are nullable (the geocoder may fail to pin a component such
as the street number); `formattedAddress` is always present.

### Working with `kycRequirements`

`kycRequirements` lists the KYC documents that must be supplied on
`POST /numbers/orders`. Each **top-level entry** is either:

  * an **object** - that exact document is required, or
  * an **array of objects** - you must provide **any one** of the documents
    listed inside (an "OR group").

So the example below requires **two** documents in total:

  * either a `business_registration_certificate` **or** a `utility_bill`
    (the OR group), **and**
  * an `id_document` (the standalone entry).

Each `documentType` is the code you reference when uploading on
`POST /numbers/orders`: send file documents as a file part
`kyc[<documentType>]` (e.g. `kyc[utility_bill]` carrying `bill.pdf`;
accepted MIME types `application/pdf`, `image/jpeg`, `image/png`), and
identifier-only documents as a form field
`kyc[<documentType>]=<value>` (e.g. `kyc[business_registration_number]=BE-12345`).

The optional `notes` field carries country-specific context (the local name
of the document, or a freshness constraint such as "dated within 3 months")
and can be surfaced verbatim to the end user.

`operationId: checkPhoneNumberOrder`

**Auth**: IntegrationApiKey

**Request body** (`application/json`) (required):
```json
{
    "street": "Adenauerallee",
    "street_number": "1",
    "postal_code": "53113",
    "city": "Bonn",
    "area_code": "string?",
    "country": "DE"
}
```

**Responses**:
- `200` — Address resolved successfully; KYC requirements returned.
  ```json
  {
      "address": {
          "street": "Adenauerallee",
          "street_number": "1",
          "postal_code": "53113",
          "city": "Bonn",
          "country": "DE"
      },
      "resolvedAddress": {
          "street": "Adenauerallee",
          "streetNumber": "1",
          "postalCode": "53113",
          "city": "Bonn",
          "country": "DE",
          "formattedAddress": "Adenauerallee 1, 53113 Bonn, Germany"
      },
      "prefix": "+49228",
      "kycRequirements": [
          [
              {
                  "documentType": "utility_bill",
                  "notes": "Dated within 3 months"
              },
              [
                  {
                      "documentType": "utility_bill",
                      "notes": "Dated within 3 months"
                  }
              ]
          ]
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — The address failed validation - locally or upstream (e.g. it could not be
resolved, or your account may not order numbers for that country). Same
`{ message, errors }` shape as every other API validation 422, with
`errors` mapping each rejected field to its messages. No order is created
at this stage, so there is no `data`.

When an upstream error has no field to attach to, `errors` is an empty
object `{}` and `message` carries the reason.

#### POST /numbers/orders

Place a phone number order

Submit a phone number order. Must be called as `multipart/form-data` because
KYC document files are part of the request. Call `POST /numbers/orders/check`
first to obtain the address-specific `kycRequirements`.

You may attach your own `order_id` (any short string, max 64 chars) - it
will be echoed back to you in the response, in `GET /numbers/orders/{id}`,
and in the asynchronous webhook payload below. If you omit it, the upstream
provider will generate one and we'll backfill it on your row.

Order fulfilment is asynchronous. This endpoint returns `201` as soon as the
order is accepted upstream. The final outcome (success or rejection) will be
delivered to your `webhook` URL (if provided) and is queryable via
`GET /numbers/orders/{id}`.

### Webhook payload

When the order reaches its final state we POST to your `webhook` URL the
**exact same body** as `GET /numbers/orders/{id}` (the schema below). Read
the `status` field to tell success from rejection:

  * `status: "success"` - `numbers` and `phone_numbers` are populated.
  * `status: "error"` - `error_message` carries the rejection reason.

`operationId: createPhoneNumberOrder`

**Auth**: IntegrationApiKey

**Responses**:
- `201` — Order accepted upstream and persisted on our side.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "order_id": "po-2026-00042",
      "customer_name": "ACME GmbH",
      "status": "requested",
      "address": {
          "street": "Adenauerallee",
          "street_number": "1",
          "postal_code": "53113",
          "city": "Bonn",
          "area_code": "string?",
          "country": "DE"
      },
      "formatted_address": "Adenauerallee 1, 53113 Bonn, DE",
      "quantity": 1,
      "prefix": "+49228",
      "numbers": [
          "+4922812345678"
      ],
      "phone_numbers": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "number": "+12125550100",
              "name": "Customer Support",
              "is_active": true,
              "call_flow_id": "550e8400-e29b-41d4-a716-446655440100",
              "call_flow": {
                  "id": "string<uuid>",
                  "name": "string",
                  "enabled": "boolean"
              }
          }
      ],
      "error_message": "string?",
      "webhook_url": "string?",
      "webhook_status": "enum",
      "webhook_next_retry_at": "string?<date-time>",
      "created_at": "string<date-time>",
      "updated_at": "string<date-time>"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Validation failed. `errors` maps each rejected field to its messages.

If the order reached the carrier and was rejected there, it is persisted
as a retryable `submission_failed` order and returned in `data` (the full
order, same shape as `GET /numbers/orders/{id}`); use `data.id` to edit +
retry it via `POST /numbers/orders/{id}`. When `data` is absent, no order
was created.

#### GET /numbers/orders/{id}

Get a phone number order

Retrieve the current state of a phone number order. The order outcome
(assigned numbers on success, or the rejection reason) can be fetched here
if you cannot accept the webhook callback, and it also lets you inspect a
past order's audit trail.

`operationId: getPhoneNumberOrder`

**Auth**: IntegrationApiKey

**Parameters**:
- `id` (path, string<uuid>, required) — Order UUID returned by `POST /numbers/orders`.

**Responses**:
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "order_id": "po-2026-00042",
      "customer_name": "ACME GmbH",
      "status": "requested",
      "address": {
          "street": "Adenauerallee",
          "street_number": "1",
          "postal_code": "53113",
          "city": "Bonn",
          "area_code": "string?",
          "country": "DE"
      },
      "formatted_address": "Adenauerallee 1, 53113 Bonn, DE",
      "quantity": 1,
      "prefix": "+49228",
      "numbers": [
          "+4922812345678"
      ],
      "phone_numbers": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "number": "+12125550100",
              "name": "Customer Support",
              "is_active": true,
              "call_flow_id": "550e8400-e29b-41d4-a716-446655440100",
              "call_flow": {
                  "id": "string<uuid>",
                  "name": "string",
                  "enabled": "boolean"
              }
          }
      ],
      "error_message": "string?",
      "webhook_url": "string?",
      "webhook_status": "enum",
      "webhook_next_retry_at": "string?<date-time>",
      "created_at": "string<date-time>",
      "updated_at": "string<date-time>"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Order not found in the caller's PBX.

#### POST /numbers/orders/{id}

Edit and retry a failed phone number order

Edit selected fields of a `submission_failed` order and re-submit it
upstream. Same multipart contract as `POST /numbers/orders`.

Uses `POST` (not `PUT`) on purpose: the body is `multipart/form-data`, and
many server runtimes only parse a multipart body when the method is `POST` -
on `PUT` the fields and files arrive empty. Send this edit as a plain `POST`.

Constraints:
- Only orders with status `submission_failed` can be edited.
- `country` is frozen. You may re-send it with the same value (handy when
  you echo back the whole order), but *changing* it returns `422` - order in
  a different country by creating a new order. Omitting it is also fine.

`customer_name` and `order_id` are editable here: send new values to change
them, or send an empty `order_id` to clear your reference.

If the edited address yields different KYC requirements upstream, the
response will be `422` carrying the new requirements; resubmit again with
the matching KYC fields.

`operationId: updatePhoneNumberOrder`

**Auth**: IntegrationApiKey

**Parameters**:
- `id` (path, string<uuid>, required)

**Responses**:
- `200` — Order re-submitted upstream successfully.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "order_id": "po-2026-00042",
      "customer_name": "ACME GmbH",
      "status": "requested",
      "address": {
          "street": "Adenauerallee",
          "street_number": "1",
          "postal_code": "53113",
          "city": "Bonn",
          "area_code": "string?",
          "country": "DE"
      },
      "formatted_address": "Adenauerallee 1, 53113 Bonn, DE",
      "quantity": 1,
      "prefix": "+49228",
      "numbers": [
          "+4922812345678"
      ],
      "phone_numbers": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "number": "+12125550100",
              "name": "Customer Support",
              "is_active": true,
              "call_flow_id": "550e8400-e29b-41d4-a716-446655440100",
              "call_flow": {
                  "id": "string<uuid>",
                  "name": "string",
                  "enabled": "boolean"
              }
          }
      ],
      "error_message": "string?",
      "webhook_url": "string?",
      "webhook_status": "enum",
      "webhook_next_retry_at": "string?<date-time>",
      "created_at": "string<date-time>",
      "updated_at": "string<date-time>"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The order was created from a different source (e.g. the Cockpit UI) and
cannot be retried via the API. Not field-specific, so the body is just a
`message`.
- `404` — Order not found in the caller's PBX.
- `409` — The order is not in `submission_failed` state, so it cannot be edited or
retried. Not field-specific, so the body is just a `message` (the current
status is included in it).
- `422` — Validation failed. `errors` maps each rejected field to its messages
(including an attempt to *change* the locked `country`).

If the carrier rejected the retried order, it stays as a retryable
`submission_failed` order and is returned in `data` (the full order, same
shape as `GET /numbers/orders/{id}`); use `data.id` to edit + retry it via
`POST /numbers/orders/{id}`. When `data` is absent, the failure was local
validation only.

### Push configuration

Push the PBX configuration so pending changes take effect - the programmatic equivalent of the "Push configuration"
button in Cockpit. PBX API key auth; rate limited to 1 request/minute per PBX.


#### POST /push-configuration

Push the PBX configuration (pbx)

Push the authenticated PBX's configuration so that pending changes take
effect. This is the programmatic equivalent of the "Push configuration"
button in Cockpit, typically called after a number's call flow has been
changed.

The target PBX is derived from the API key; there is no request body.

The presented PBX API key must carry the `push_configuration`
ability/scope.

**Rate limit:** 1 request per minute per PBX. Exceeding the limit returns
`429` with a `Retry-After` header.

`operationId: postPushConfiguration`

**Auth**: PbxApiKey

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `200` — Configuration push recorded.
  ```json
  {
      "status": "pushed",
      "pushed_at": "2026-06-15 09:12:04"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The presented PBX API key does not carry the required
`push_configuration` ability/scope.
- `409` — Configuration pushes are currently locked for this PBX.
- `429` — Rate limit exceeded (max 1 push per minute per PBX). Wait `Retry-After`
seconds before retrying.
- `500` — The push could not be recorded; retry later.

### Odoo Installer

Endpoints consumed by the Voxbi Installer module running inside a customer's Odoo.
Register the Odoo integration, poll the install status, read it back, and update or
re-run it. Authenticated with a customer API key carrying the `odoo-installer`
ability; the PBX is resolved from the key.


#### POST /odoo-installer/register-install

Register an Odoo installation (pbx)

Registers (upserts) the Odoo integration for the authenticated PBX and queues the
installation job. Consumed by the Voxbi Installer module running inside a customer's
Odoo. The PBX is resolved from the API key, so it is never part of the request body.
Requires an API key carrying the `odoo-installer` ability.

`operationId: postOdooInstallerRegisterInstall`

**Auth**: IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "odoo_url": "https://erp.example.com",
    "odoo_db": "example_prod",
    "odoo_version": "17.0",
    "service_user": "svc_voxbi",
    "service_key": "s3cr3t-service-key",
    "service_uid": 7,
    "sync_sip_configurations": true,
    "is_active": true,
    "installer_module_version": "1.2.0"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created. The integration is registered and the install job is queued.
  ```json
  {
      "integration_id": "string<uuid>",
      "status_url": "string<uri>"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /odoo-installer/install-status

Get the Odoo installation status (pbx)

Returns the current install job status for the authenticated PBX's Odoo integration.
Requires an API key carrying the `odoo-installer` ability.

`operationId: getOdooInstallerInstallStatus`

**Auth**: IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "status": "enum",
      "results": [
          []
      ],
      "updated_at": "string?<date-time>"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — No Odoo integration exists for the authenticated PBX.

#### GET /odoo-installer/get-integration

Get the Odoo integration (pbx)

Returns the Odoo integration record for the authenticated PBX. The service account
API key is never returned. Requires an API key carrying the `odoo-installer` ability.

`operationId: getOdooInstallerGetIntegration`

**Auth**: IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "integration_id": "string<uuid>",
      "pbx_id": "string<uuid>",
      "type": "odoo",
      "is_active": "boolean",
      "job_status": "enum",
      "odoo_url": "string?",
      "odoo_db": "string?",
      "service_user": "string?",
      "odoo_version": "string?",
      "sync_sip_configurations": "boolean",
      "updated_at": "string?<date-time>"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — No Odoo integration exists for the authenticated PBX.

#### POST /odoo-installer/update-and-fix-integration

Update and re-run the Odoo integration (pbx)

Patches the supplied fields on the authenticated PBX's Odoo integration and re-queues
the install job (job status is reset to `pending` and previous results are cleared).
Every field is optional; only the fields you send are changed. The PBX is resolved
from the API key. Requires an API key carrying the `odoo-installer` ability.

`operationId: postOdooInstallerUpdateAndFixIntegration`

**Auth**: IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`):
```json
{
    "odoo_url": "https://erp.example.com",
    "odoo_db": "example_prod",
    "odoo_version": "17.0",
    "service_user": "svc_voxbi",
    "service_key": "s3cr3t-service-key",
    "service_uid": 7,
    "sync_sip_configurations": true,
    "is_active": true,
    "installer_module_version": "1.2.0"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK. The integration was updated and the install job re-queued.
  ```json
  {
      "integration_id": "string<uuid>",
      "job_status": "enum",
      "status_url": "string<uri>"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — No Odoo integration exists for the authenticated PBX.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

### Pbxes

The tenant container. Every other resource is scoped to a PBX.

#### GET /pbxes

List pbxe

Returns a paginated list of pbxes. Use the standard `search`, `sort`, `page`, and `perPage`
query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant scoping is
enforced.

`operationId: listPbxes`

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — The filter key can be any of the following keys: id, default_carrier_id, fallback_carrier_id, profile_id, name, comment, reserved_extensions, emergency, automatic_firmware, default_call_pickups_for_new_extensions, phones_default_vlan_pc, phones_default_vlan_phone, phones_web_admin_password, lldp, show_users_first_name_first, allow_changing_record_calls, recordings_destination, oidc_enabled, oidc_allow_sign_up, oidc_email_domain, oidc_client_id, oidc_client_secret, oidc_discover_url, oidc_authorization_url, oidc_token_url, oidc_userinfo_url, oidc_logout_url, oidc_issuer and oidc_identity_fields_mapping.

The filter value can be any string or number.

You can also apply multiple filters using `filter[key1]=value1&filter[key2]=value2`.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "partner_id": "550e8400-e29b-41d4-a716-446655440001",
              "customer_id": "550e8400-e29b-41d4-a716-446655440002",
              "default_carrier_id": "550e8400-e29b-41d4-a716-446655440003",
              "fallback_carrier_id": "550e8400-e29b-41d4-a716-446655440004",
              "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440005",
              "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440006",
              "no_ext_match_callflow_id": "550e8400-e29b-41d4-a716-446655440007",
              "profile_id": 42,
              "name": "Acme Corp",
              "instance_name": "pbx16dev",
              "trunk_username": "trunk_acme",
              "comment": "Migrated from legacy platform in March",
              "reserved_extensions": "112, 113, 911",
              "emergency": false,
              "automatic_firmware": true,
              "default_call_pickups_for_new_extensions": true,
              "allow_roaming": true,
              "phones_default_vlan_pc": 200,
              "phones_default_vlan_phone": 100,
              "phones_web_admin_password": "string",
              "lldp": true,
              "show_users_first_name_first": true,
              "capitalize_users_last_name": false,
              "allow_call_recording": false,
              "allow_changing_record_calls": false,
              "recordings_destination": "mixvoip",
              "recording_start_at": "on_start",
              "user_provisioning_key": "string",
              "user_provisioning_ip_addresses": [
                  "string"
              ],
              "it_admin_notification_email": "it@example.com",
              "impersonation_token": "string",
              "force_provisioning_sync": "2024-03-01T08:29:07Z",
              "push_configuration": "2024-03-01T08:29:07Z",
              "push_contact": "2024-03-01T08:29:07Z",
              "push_required": false,
              "push_check_pending": false,
              "push_check_requested_at": "2024-03-01T08:29:07Z",
              "push_required_count": 0,
              "push_other_count": 0,
              "server_push_date": "2024-03-01T08:29:07Z",
              "contact_push_date": "2024-03-01T08:29:07Z",
              "last_config_update": "2024-03-01T08:29:07Z",
              "tenant_id": "550e8400-e29b-41d4-a716-446655440099",
              "sso_enabled": false,
              "sso_type": "openid_connect",
              "allow_sign_up_with_sso": false,
              "force_sign_in_with_sso": false,
              "max_incoming_channels": 50,
              "max_incoming_channels_sound_file_id": "550e8400-e29b-41d4-a716-446655440008",
              "music_on_hold_id": "550e8400-e29b-41d4-a716-446655440009",
              "api_key": "string",
              "sso_identity_fields_mapping": {
                  "first_name": "given_name",
                  "last_name": "family_name",
                  "email": "email"
              },
              "default_call_ring_duration": 30,
              "enable_soop_status": true,
              "ai_profile": "premium",
              "ai_engine_status": false,
              "transcript_retention_days": 30,
              "last_ai_engine_status_activity_check": "2024-03-01T08:29:07Z",
              "ring_tone": "lu",
              "show_all_users_in_voxbi": true,
              "split_internal_external_calls": false,
              "call_block_logged_out_sip_devices": true,
              "show_via_in_missed_call": false,
              "enable_tempus_menu": true,
              "language": "en",
              "estimated_users": 25,
              "estimated_sip_devices": 30,
              "multicast_sites_ip_addresses": [
                  "10.10.0.1",
                  "10.10.0.2"
              ],
              "flags": {
                  "beta_dashboard": true
              },
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z",
              "deleted_at": "string?<date-time>"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /pbxes/{id}

Get a Pbx

Returns the pbx identified by the path parameter. Multi-tenant scoping is enforced: the
caller can only fetch resources within their own PBX.

`operationId: getPbx`

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "partner_id": "550e8400-e29b-41d4-a716-446655440001",
      "customer_id": "550e8400-e29b-41d4-a716-446655440002",
      "default_carrier_id": "550e8400-e29b-41d4-a716-446655440003",
      "fallback_carrier_id": "550e8400-e29b-41d4-a716-446655440004",
      "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440005",
      "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440006",
      "no_ext_match_callflow_id": "550e8400-e29b-41d4-a716-446655440007",
      "profile_id": 42,
      "name": "Acme Corp",
      "instance_name": "pbx16dev",
      "trunk_username": "trunk_acme",
      "comment": "Migrated from legacy platform in March",
      "reserved_extensions": "112, 113, 911",
      "emergency": false,
      "automatic_firmware": true,
      "default_call_pickups_for_new_extensions": true,
      "allow_roaming": true,
      "phones_default_vlan_pc": 200,
      "phones_default_vlan_phone": 100,
      "phones_web_admin_password": "string",
      "lldp": true,
      "show_users_first_name_first": true,
      "capitalize_users_last_name": false,
      "allow_call_recording": false,
      "allow_changing_record_calls": false,
      "recordings_destination": "mixvoip",
      "recording_start_at": "on_start",
      "user_provisioning_key": "string",
      "user_provisioning_ip_addresses": [
          "string"
      ],
      "it_admin_notification_email": "it@example.com",
      "impersonation_token": "string",
      "force_provisioning_sync": "2024-03-01T08:29:07Z",
      "push_configuration": "2024-03-01T08:29:07Z",
      "push_contact": "2024-03-01T08:29:07Z",
      "push_required": false,
      "push_check_pending": false,
      "push_check_requested_at": "2024-03-01T08:29:07Z",
      "push_required_count": 0,
      "push_other_count": 0,
      "server_push_date": "2024-03-01T08:29:07Z",
      "contact_push_date": "2024-03-01T08:29:07Z",
      "last_config_update": "2024-03-01T08:29:07Z",
      "tenant_id": "550e8400-e29b-41d4-a716-446655440099",
      "sso_enabled": false,
      "sso_type": "openid_connect",
      "allow_sign_up_with_sso": false,
      "force_sign_in_with_sso": false,
      "max_incoming_channels": 50,
      "max_incoming_channels_sound_file_id": "550e8400-e29b-41d4-a716-446655440008",
      "music_on_hold_id": "550e8400-e29b-41d4-a716-446655440009",
      "api_key": "string",
      "sso_identity_fields_mapping": {
          "first_name": "given_name",
          "last_name": "family_name",
          "email": "email"
      },
      "default_call_ring_duration": 30,
      "enable_soop_status": true,
      "ai_profile": "premium",
      "ai_engine_status": false,
      "transcript_retention_days": 30,
      "last_ai_engine_status_activity_check": "2024-03-01T08:29:07Z",
      "ring_tone": "lu",
      "show_all_users_in_voxbi": true,
      "split_internal_external_calls": false,
      "call_block_logged_out_sip_devices": true,
      "show_via_in_missed_call": false,
      "enable_tempus_menu": true,
      "language": "en",
      "estimated_users": 25,
      "estimated_sip_devices": 30,
      "multicast_sites_ip_addresses": [
          "10.10.0.1",
          "10.10.0.2"
      ],
      "flags": {
          "beta_dashboard": true
      },
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z",
      "deleted_at": "string?<date-time>"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### Phone Numbers

External DIDs owned by the PBX. Numbers are linked to call flows, users, or queues for inbound routing.

#### GET /phone-numbers

List Phone number

Returns a paginated list of phone numbers. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listPhoneNumbers`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — Filter the list by one or more fields. Allowed keys: `name`, `number`, `is_active`.
Apply several at once with filter[key1]=value1&filter[key2]=value2.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "default_carrier_id": "550e8400-e29b-41d4-a716-446655440002",
              "fallback_carrier_id": "550e8400-e29b-41d4-a716-446655440003",
              "name": "Support line",
              "language": "fr",
              "number": "12125550100",
              "international_prefix": "1",
              "is_root": true,
              "is_active": true,
              "sync_source": "manual",
              "handling_strategy": {
                  "type": "call_flow",
                  "default_callflow_id": "string?<uuid>",
                  "no_ext_match_callflow_id": "string?<uuid>",
                  "prefix_to_remove": "string?",
                  "prefix_to_add": "string?",
                  "call_flow_id": "string?<uuid>",
                  "extension_id": "string?<uuid>",
                  "user_id": "string?<uuid>",
                  "sip_device_id": "string?<uuid>"
              },
              "number_and_name": "12125550100 (Support line)",
              "number_without_international_prefix": "2125550100",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /phone-numbers/{id}

Find Phone number by ID

Returns the phone number identified by the path parameter. Multi-tenant scoping is enforced:
the caller can only fetch resources within their own PBX.

`operationId: getPhoneNumber`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "default_carrier_id": "550e8400-e29b-41d4-a716-446655440002",
      "fallback_carrier_id": "550e8400-e29b-41d4-a716-446655440003",
      "name": "Support line",
      "language": "fr",
      "number": "12125550100",
      "international_prefix": "1",
      "is_root": true,
      "is_active": true,
      "sync_source": "manual",
      "handling_strategy": {
          "type": "call_flow",
          "default_callflow_id": "string?<uuid>",
          "no_ext_match_callflow_id": "string?<uuid>",
          "prefix_to_remove": "string?",
          "prefix_to_add": "string?",
          "call_flow_id": "string?<uuid>",
          "extension_id": "string?<uuid>",
          "user_id": "string?<uuid>",
          "sip_device_id": "string?<uuid>"
      },
      "number_and_name": "12125550100 (Support line)",
      "number_without_international_prefix": "2125550100",
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### Queues

Call queues that hold callers until an agent is available, with configurable strategies and music on hold.

#### GET /queues

List queue

Returns a paginated list of queues. Use the standard `search`, `sort`, `page`, and `perPage`
query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant scoping is
enforced.

`operationId: listQueues`

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — The filter key can be any of the following keys: id, pbx_id, pool_id, name, ring_strategy, music_on_hold_id, ring_busy_members, ring_twinnings, ring_duration, interval, wrap_up_time, exit_key, ring_empty, weight, mapping_id, voip_queue_id and call_flow_id.

The filter value can be any string or number.

You can also apply multiple filters using `filter[key1]=value1&filter[key2]=value2`.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Customer Support",
              "ring_strategy": "ring_all",
              "ring_busy_members": false,
              "ring_duration": 20,
              "interval": 5,
              "wrap_up_time": 30,
              "exit_key": "0",
              "ring_empty": false,
              "enable_caller_position": false,
              "caller_position_announce_frequency": 10,
              "caller_position_language": "en",
              "music_on_hold": "550e8400-e29b-41d4-a716-446655440004",
              "users": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "first_name": "Jane",
                      "last_name": "Smith",
                      "name": "Jane Smith",
                      "full_name_with_email": "Jane Smith <jane.smith@example.com>",
                      "email": "jane.smith@example.com",
                      "status": "enabled",
                      "type": "user",
                      "language": "en",
                      "sync_source": "manual",
                      "vip": false,
                      "show_in_address_book": true,
                      "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
                      "caller_identity_internal_extension_id": "550e8400-e29b-41d4-a716-446655440002",
                      "caller_identity_external_extension_id": "550e8400-e29b-41d4-a716-446655440003",
                      "allow_telephony": true,
                      "allow_call_forwarding": true,
                      "allow_change_twinning_dest": true,
                      "allow_dnd": true,
                      "allow_status_changer": false,
                      "allow_anonymous_calls": false,
                      "allow_international_calls": false,
                      "allow_eu_calls": true,
                      "allow_national_calls": true,
                      "allow_premium_numbers": false,
                      "allow_sms": false,
                      "allow_manage_global_contacts": false,
                      "allow_manage_voicemail_parameters": true,
                      "allow_in_call_recording": false,
                      "allow_default_pbxes_outgoing_phone_numbers": true,
                      "allow_grafana_access": false,
                      "allow_right_to_disconnect": true,
                      "right_to_disconnect": false,
                      "concurrent_calls": true,
                      "extension_roaming": true,
                      "record_your_calls": false,
                      "record_your_internal_calls": 2,
                      "access_all_call_recordings": false,
                      "call_recordings_visibility": "own_calls_only",
                      "call_information_visibility": "own_calls",
                      "send_missed_call_via_email": true,
                      "limit_email_missed_call_to_direct": false,
                      "show_caller_id_for_twinning_call": 2,
                      "voicemail_type": "internal",
                      "voicemail_destination": "email_only",
                      "invited_at": "string?<date-time>",
                      "accepted_invitation_at": "string?<date-time>",
                      "email_verified_at": "2024-02-15T10:00:00Z",
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z",
                      "extensions": [
                          {
                              "id": "\u2026",
                              "pbx_id": "\u2026",
                              "assigned_to_type": "\u2026",
                              "assigned_to_id": "\u2026",
                              "external_call_flow_id": "\u2026",
                              "internal_call_flow_id": "\u2026",
                              "number": "\u2026",
                              "name": "\u2026",
                              "description": "\u2026",
                              "record_external_calls": "\u2026",
                              "record_internal_calls": "\u2026",
                              "sync_source": "\u2026",
                              "full_name_with_extension_number": "\u2026",
                              "assigned_to": "\u2026",
                              "employee_id": "\u2026",
                              "department_id": "\u2026",
                              "is_favorite": "\u2026",
                              "created_at": "\u2026",
                              "updated_at": "\u2026"
                          }
                      ],
                      "groups": [
                          {
                              "id": "\u2026",
                              "pbx_id": "\u2026",
                              "default_internal_call_flow_id": "\u2026",
                              "default_external_call_flow_id": "\u2026",
                              "extension_id_shown_outside_the_group": "\u2026",
                              "default_caller_identity_id": "\u2026",
                              "external_uid": "\u2026",
                              "allow_call_pickups": "\u2026",
                              "name": "\u2026",
                              "description": "\u2026",
                              "allow_default_pbxes_outgoing_phone_numbers": "\u2026",
                              "display_tab_to_everyone_inside_voxbi": "\u2026",
                              "created_at": "\u2026",
                              "updated_at": "\u2026",
                              "users": "\u2026"
                          }
                      ],
                      "allow_manage_group_contacts": [
                          {
                              "id": "\u2026",
                              "pbx_id": "\u2026",
                              "default_internal_call_flow_id": "\u2026",
                              "default_external_call_flow_id": "\u2026",
                              "extension_id_shown_outside_the_group": "\u2026",
                              "default_caller_identity_id": "\u2026",
                              "external_uid": "\u2026",
                              "allow_call_pickups": "\u2026",
                              "name": "\u2026",
                              "description": "\u2026",
                              "allow_default_pbxes_outgoing_phone_numbers": "\u2026",
                              "display_tab_to_everyone_inside_voxbi": "\u2026",
                              "created_at": "\u2026",
                              "updated_at": "\u2026",
                              "users": "\u2026"
                          }
                      ],
                      "allow_access_to_group_contacts": [
                          {
                              "id": "\u2026",
                              "pbx_id": "\u2026",
                              "default_internal_call_flow_id": "\u2026",
                              "default_external_call_flow_id": "\u2026",
                              "extension_id_shown_outside_the_group": "\u2026",
                              "default_caller_identity_id": "\u2026",
                              "external_uid": "\u2026",
                              "allow_call_pickups": "\u2026",
                              "name": "\u2026",
                              "description": "\u2026",
                              "allow_default_pbxes_outgoing_phone_numbers": "\u2026",
                              "display_tab_to_everyone_inside_voxbi": "\u2026",
                              "created_at": "\u2026",
                              "updated_at": "\u2026",
                              "users": "\u2026"
                          }
                      ],
                      "sip_devices": [
                          {
                              "id": "\u2026",
                              "pbx_id": "\u2026",
                              "target_type": "\u2026",
                              "target_id": "\u2026",
                              "extension_id_shown_for_internal_calls": "\u2026",
                              "roaming_user_id": "\u2026",
                              "location_id": "\u2026",
                              "parent_sip_device_id": "\u2026",
                              "name": "\u2026",
                              "description": "\u2026",
                              "is_enabled": "\u2026",
                              "ip_internal": "\u2026",
                              "ip_external": "\u2026",
                              "user_agent": "\u2026",
                              "mac": "\u2026",
                              "vlan_phone": "\u2026",
                              "vlan_pc": "\u2026",
                              "sip_username": "\u2026",
                              "alt_username": "\u2026",
                              "transport": "\u2026",
                              "type": "\u2026",
                              "allow_roaming": "\u2026",
                              "language": "\u2026",
                              "easy_deploy": "\u2026",
                              "notify": "\u2026",
                              "allow_default_pbxes_outgoing_phone_numbers": "\u2026",
                              "sync_source": "\u2026",
                              "custom_provisioning": "\u2026",
                              "last_seen": "\u2026",
                              "created_at": "\u2026",
                              "updated_at": "\u2026",
                              "target": "\u2026",
                              "tags": "\u2026",
                              "caller_identities": "\u2026",
                              "location": "\u2026"
                          }
                      ],
                      "twinnings": [
                          {
                              "id": "\u2026",
                              "user_id": "\u2026",
                              "status": "\u2026",
                              "phone_number": "\u2026",
                              "delay": "\u2026",
                              "sync_source": "\u2026",
                              "type": "\u2026",
                              "dial_strategy": "\u2026",
                              "fmc_enabled": "\u2026",
                              "backup_phone_number": "\u2026",
                              "created_at": "\u2026",
                              "updated_at": "\u2026"
                          }
                      ],
                      "caller_identities": [
                          []
                      ],
                      "locations": [
                          {
                              "id": "\u2026",
                              "pbx_id": "\u2026",
                              "company_id": "\u2026",
                              "parent_id": "\u2026",
                              "name": "\u2026",
                              "description": "\u2026",
                              "street_no": "\u2026",
                              "street": "\u2026",
                              "city": "\u2026",
                              "postal": "\u2026",
                              "country_id": "\u2026",
                              "lft": "\u2026",
                              "rgt": "\u2026",
                              "depth": "\u2026",
                              "enable_location_as_presence_status": "\u2026",
                              "style": "\u2026",
                              "is_fixed_location": "\u2026",
                              "latitude": "\u2026",
                              "longitude": "\u2026",
                              "geofence_radius_m": "\u2026",
                              "created_at": "\u2026",
                              "updated_at": "\u2026"
                          }
                      ],
                      "languages": [
                          {
                              "id": "\u2026",
                              "pbx_id": "\u2026",
                              "name": "\u2026",
                              "created_at": "\u2026",
                              "updated_at": "\u2026"
                          }
                      ],
                      "tags": [
                          {
                              "id": "\u2026",
                              "pbx_id": "\u2026",
                              "name": "\u2026",
                              "description": "\u2026",
                              "created_at": "\u2026",
                              "updated_at": "\u2026"
                          }
                      ],
                      "queues": [
                          []
                      ]
                  }
              ]
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /queues/{id}

Get a Queue

Returns the queue identified by the path parameter. Multi-tenant scoping is enforced: the
caller can only fetch resources within their own PBX.

`operationId: getQueue`

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "name": "Customer Support",
      "ring_strategy": "ring_all",
      "ring_busy_members": false,
      "ring_duration": 20,
      "interval": 5,
      "wrap_up_time": 30,
      "exit_key": "0",
      "ring_empty": false,
      "enable_caller_position": false,
      "caller_position_announce_frequency": 10,
      "caller_position_language": "en",
      "music_on_hold": "550e8400-e29b-41d4-a716-446655440004",
      "users": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "first_name": "Jane",
              "last_name": "Smith",
              "name": "Jane Smith",
              "full_name_with_email": "Jane Smith <jane.smith@example.com>",
              "email": "jane.smith@example.com",
              "status": "enabled",
              "type": "user",
              "language": "en",
              "sync_source": "manual",
              "vip": false,
              "show_in_address_book": true,
              "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
              "caller_identity_internal_extension_id": "550e8400-e29b-41d4-a716-446655440002",
              "caller_identity_external_extension_id": "550e8400-e29b-41d4-a716-446655440003",
              "allow_telephony": true,
              "allow_call_forwarding": true,
              "allow_change_twinning_dest": true,
              "allow_dnd": true,
              "allow_status_changer": false,
              "allow_anonymous_calls": false,
              "allow_international_calls": false,
              "allow_eu_calls": true,
              "allow_national_calls": true,
              "allow_premium_numbers": false,
              "allow_sms": false,
              "allow_manage_global_contacts": false,
              "allow_manage_voicemail_parameters": true,
              "allow_in_call_recording": false,
              "allow_default_pbxes_outgoing_phone_numbers": true,
              "allow_grafana_access": false,
              "allow_right_to_disconnect": true,
              "right_to_disconnect": false,
              "concurrent_calls": true,
              "extension_roaming": true,
              "record_your_calls": false,
              "record_your_internal_calls": 2,
              "access_all_call_recordings": false,
              "call_recordings_visibility": "own_calls_only",
              "call_information_visibility": "own_calls",
              "send_missed_call_via_email": true,
              "limit_email_missed_call_to_direct": false,
              "show_caller_id_for_twinning_call": 2,
              "voicemail_type": "internal",
              "voicemail_destination": "email_only",
              "invited_at": "string?<date-time>",
              "accepted_invitation_at": "string?<date-time>",
              "email_verified_at": "2024-02-15T10:00:00Z",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z",
              "extensions": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "assigned_to_type": "user",
                      "assigned_to_id": "550e8400-e29b-41d4-a716-446655440002",
                      "external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
                      "internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440004",
                      "number": "1337",
                      "name": "Support line",
                      "description": "Front-line support line",
                      "record_external_calls": true,
                      "record_internal_calls": false,
                      "sync_source": "manual",
                      "full_name_with_extension_number": "Jane Smith (1337)",
                      "assigned_to": [],
                      "employee_id": "550e8400-e29b-41d4-a716-446655440005",
                      "department_id": "550e8400-e29b-41d4-a716-446655440006",
                      "is_favorite": false,
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "groups": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                      "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
                      "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
                      "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
                      "external_uid": "grp-support",
                      "allow_call_pickups": true,
                      "name": "Support team",
                      "description": "First-line support",
                      "allow_default_pbxes_outgoing_phone_numbers": false,
                      "display_tab_to_everyone_inside_voxbi": false,
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z",
                      "users": [
                          {
                              "id": "\u2026",
                              "first_name": "\u2026",
                              "last_name": "\u2026",
                              "email": "\u2026"
                          }
                      ]
                  }
              ],
              "allow_manage_group_contacts": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                      "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
                      "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
                      "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
                      "external_uid": "grp-support",
                      "allow_call_pickups": true,
                      "name": "Support team",
                      "description": "First-line support",
                      "allow_default_pbxes_outgoing_phone_numbers": false,
                      "display_tab_to_everyone_inside_voxbi": false,
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z",
                      "users": [
                          {
                              "id": "\u2026",
                              "first_name": "\u2026",
                              "last_name": "\u2026",
                              "email": "\u2026"
                          }
                      ]
                  }
              ],
              "allow_access_to_group_contacts": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                      "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
                      "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
                      "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
                      "external_uid": "grp-support",
                      "allow_call_pickups": true,
                      "name": "Support team",
                      "description": "First-line support",
                      "allow_default_pbxes_outgoing_phone_numbers": false,
                      "display_tab_to_everyone_inside_voxbi": false,
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z",
                      "users": [
                          {
                              "id": "\u2026",
                              "first_name": "\u2026",
                              "last_name": "\u2026",
                              "email": "\u2026"
                          }
                      ]
                  }
              ],
              "sip_devices": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "target_type": "user",
                      "target_id": "550e8400-e29b-41d4-a716-446655440003",
                      "extension_id_shown_for_internal_calls": "string?<uuid>",
                      "roaming_user_id": "string?<uuid>",
                      "location_id": "550e8400-e29b-41d4-a716-446655440002",
                      "parent_sip_device_id": "string?<uuid>",
                      "name": "Reception desk phone",
                      "description": "Front-desk Yealink T46U",
                      "is_enabled": true,
                      "ip_internal": "192.168.1.42",
                      "ip_external": "203.0.113.10",
                      "user_agent": "Yealink SIP-T46U 108.86.0.10",
                      "mac": "00:0a:95:9d:68:16",
                      "vlan_phone": 100,
                      "vlan_pc": 200,
                      "sip_username": "device_001",
                      "alt_username": "string?",
                      "transport": "tls",
                      "type": "auto",
                      "allow_roaming": false,
                      "language": "en",
                      "easy_deploy": "string?<date-time>",
                      "notify": "string?<date-time>",
                      "allow_default_pbxes_outgoing_phone_numbers": false,
                      "sync_source": "provision",
                      "custom_provisioning": "string?",
                      "last_seen": "2024-03-01T08:29:07Z",
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z",
                      "target": [],
                      "tags": [
                          {
                              "id": "\u2026",
                              "pbx_id": "\u2026",
                              "name": "\u2026",
                              "description": "\u2026",
                              "created_at": "\u2026",
                              "updated_at": "\u2026"
                          }
                      ],
                      "caller_identities": [
                          []
                      ],
                      "location": {
                          "id": "\u2026",
                          "pbx_id": "\u2026",
                          "company_id": "\u2026",
                          "parent_id": "\u2026",
                          "name": "\u2026",
                          "description": "\u2026",
                          "street_no": "\u2026",
                          "street": "\u2026",
                          "city": "\u2026",
                          "postal": "\u2026",
                          "country_id": "\u2026",
                          "lft": "\u2026",
                          "rgt": "\u2026",
                          "depth": "\u2026",
                          "enable_location_as_presence_status": "\u2026",
                          "style": "\u2026",
                          "is_fixed_location": "\u2026",
                          "latitude": "\u2026",
                          "longitude": "\u2026",
                          "geofence_radius_m": "\u2026",
                          "created_at": "\u2026",
                          "updated_at": "\u2026"
                      }
                  }
              ],
              "twinnings": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "user_id": "550e8400-e29b-41d4-a716-446655440001",
                      "status": "enabled",
                      "phone_number": "+12125550100",
                      "delay": 5,
                      "sync_source": "manual",
                      "type": "mobile",
                      "dial_strategy": "voip_and_gsm_fallback",
                      "fmc_enabled": false,
                      "backup_phone_number": "+12125550101",
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "caller_identities": [
                  []
              ],
              "locations": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "company_id": "550e8400-e29b-41d4-a716-446655440004",
                      "parent_id": "550e8400-e29b-41d4-a716-446655440002",
                      "name": "Luxembourg HQ",
                      "description": "Main office, 5th floor",
                      "street_no": "12",
                      "street": "Main Street",
                      "city": "Luxembourg",
                      "postal": "1234",
                      "country_id": "550e8400-e29b-41d4-a716-446655440003",
                      "lft": 1,
                      "rgt": 10,
                      "depth": 0,
                      "enable_location_as_presence_status": false,
                      "style": {
                          "icon_class": "la-map-pin",
                          "bg_color": "#3490dc",
                          "font_color": "#ffffff"
                      },
                      "is_fixed_location": true,
                      "latitude": 49.6116,
                      "longitude": 6.1319,
                      "geofence_radius_m": 100,
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "languages": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "name": "English",
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "tags": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "name": "VIP Customers",
                      "description": "Customers with priority routing and dedicated agents.",
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "queues": [
                  []
              ]
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### SIP Carriers

Upstream SIP trunks used for outbound calls and number termination.

#### GET /sip-carriers

List SIP carrier

Returns a paginated list of sip carriers. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listSipCarriers`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — Filter the list by one or more fields. Allowed keys: `name`, `sip_user`, `sip_registrar`.
Apply several at once with filter[key1]=value1&filter[key2]=value2.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Primary carrier",
              "sip_user": "trunk_user",
              "sip_registrar": "sip.carrier.example.com",
              "register": true,
              "destination_header": "default",
              "incoming_header": "request-uri",
              "dtmf_mode": "rfc4733",
              "display_name": "Primary carrier (sip.carrier.example.com)",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /sip-carriers/{id}

Get a sIP Carrier

Returns the sip carrier identified by the path parameter. Multi-tenant scoping is enforced:
the caller can only fetch resources within their own PBX.

`operationId: getSipCarrier`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "name": "Primary carrier",
      "sip_user": "trunk_user",
      "sip_registrar": "sip.carrier.example.com",
      "register": true,
      "destination_header": "default",
      "incoming_header": "request-uri",
      "dtmf_mode": "rfc4733",
      "display_name": "Primary carrier (sip.carrier.example.com)",
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### SIP Devices

Physical or soft SIP endpoints (deskphones, softphones, ATAs) registered against the PBX.

#### GET /sip-devices

List SIP device

Returns a paginated list of sip devices. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listSipDevices`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — Filter the list by one or more fields. Allowed keys: `name`, `mac`, `sip_username`, `is_enabled`, `user_id`.
Apply several at once with filter[key1]=value1&filter[key2]=value2.
- `include` (query, string, optional) — Comma-separated related resources to embed (dot-nested where
shown). Allowed: `target`, `tags`, `callerIdentities`, `location`.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "target_type": "user",
              "target_id": "550e8400-e29b-41d4-a716-446655440003",
              "extension_id_shown_for_internal_calls": "string?<uuid>",
              "roaming_user_id": "string?<uuid>",
              "location_id": "550e8400-e29b-41d4-a716-446655440002",
              "parent_sip_device_id": "string?<uuid>",
              "name": "Reception desk phone",
              "description": "Front-desk Yealink T46U",
              "is_enabled": true,
              "ip_internal": "192.168.1.42",
              "ip_external": "203.0.113.10",
              "user_agent": "Yealink SIP-T46U 108.86.0.10",
              "mac": "00:0a:95:9d:68:16",
              "vlan_phone": 100,
              "vlan_pc": 200,
              "sip_username": "device_001",
              "alt_username": "string?",
              "transport": "tls",
              "type": "auto",
              "allow_roaming": false,
              "language": "en",
              "easy_deploy": "string?<date-time>",
              "notify": "string?<date-time>",
              "allow_default_pbxes_outgoing_phone_numbers": false,
              "sync_source": "provision",
              "custom_provisioning": "string?",
              "last_seen": "2024-03-01T08:29:07Z",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z",
              "target": [],
              "tags": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "name": "VIP Customers",
                      "description": "Customers with priority routing and dedicated agents.",
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "caller_identities": [
                  []
              ],
              "location": {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "company_id": "550e8400-e29b-41d4-a716-446655440004",
                  "parent_id": "550e8400-e29b-41d4-a716-446655440002",
                  "name": "Luxembourg HQ",
                  "description": "Main office, 5th floor",
                  "street_no": "12",
                  "street": "Main Street",
                  "city": "Luxembourg",
                  "postal": "1234",
                  "country_id": "550e8400-e29b-41d4-a716-446655440003",
                  "lft": 1,
                  "rgt": 10,
                  "depth": 0,
                  "enable_location_as_presence_status": false,
                  "style": {
                      "icon_class": "la-map-pin",
                      "bg_color": "#3490dc",
                      "font_color": "#ffffff"
                  },
                  "is_fixed_location": true,
                  "latitude": 49.6116,
                  "longitude": 6.1319,
                  "geofence_radius_m": 100,
                  "created_at": "2024-03-01T08:29:07Z",
                  "updated_at": "2024-03-01T08:29:07Z"
              }
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /sip-devices/{sip_device}

Get a sIP device

Returns the sip device identified by the path parameter. Multi-tenant scoping is enforced:
the caller can only fetch resources within their own PBX.

`operationId: getSipDevice`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "target_type": "user",
      "target_id": "550e8400-e29b-41d4-a716-446655440003",
      "extension_id_shown_for_internal_calls": "string?<uuid>",
      "roaming_user_id": "string?<uuid>",
      "location_id": "550e8400-e29b-41d4-a716-446655440002",
      "parent_sip_device_id": "string?<uuid>",
      "name": "Reception desk phone",
      "description": "Front-desk Yealink T46U",
      "is_enabled": true,
      "ip_internal": "192.168.1.42",
      "ip_external": "203.0.113.10",
      "user_agent": "Yealink SIP-T46U 108.86.0.10",
      "mac": "00:0a:95:9d:68:16",
      "vlan_phone": 100,
      "vlan_pc": 200,
      "sip_username": "device_001",
      "alt_username": "string?",
      "transport": "tls",
      "type": "auto",
      "allow_roaming": false,
      "language": "en",
      "easy_deploy": "string?<date-time>",
      "notify": "string?<date-time>",
      "allow_default_pbxes_outgoing_phone_numbers": false,
      "sync_source": "provision",
      "custom_provisioning": "string?",
      "last_seen": "2024-03-01T08:29:07Z",
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z",
      "target": [],
      "tags": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "VIP Customers",
              "description": "Customers with priority routing and dedicated agents.",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "caller_identities": [
          []
      ],
      "location": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "company_id": "550e8400-e29b-41d4-a716-446655440004",
          "parent_id": "550e8400-e29b-41d4-a716-446655440002",
          "name": "Luxembourg HQ",
          "description": "Main office, 5th floor",
          "street_no": "12",
          "street": "Main Street",
          "city": "Luxembourg",
          "postal": "1234",
          "country_id": "550e8400-e29b-41d4-a716-446655440003",
          "lft": 1,
          "rgt": 10,
          "depth": 0,
          "enable_location_as_presence_status": false,
          "style": {
              "icon_class": "la-map-pin",
              "bg_color": "#3490dc",
              "font_color": "#ffffff"
          },
          "is_fixed_location": true,
          "latitude": 49.6116,
          "longitude": 6.1319,
          "geofence_radius_m": 100,
          "created_at": "2024-03-01T08:29:07Z",
          "updated_at": "2024-03-01T08:29:07Z"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### GET /sip-devices/inventory

Get SIP device inventory

Returns a paginated list of sip device inventory. Use the standard `search`, `sort`, `page`,
and `perPage` query parameters; use `filter[key]=value` to narrow the result set.
Multi-tenant scoping is enforced.

`operationId: getSipDeviceInventory`

**Parameters**:
- `accept` (header, string, optional)
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "integer",
              "pbx_id": "integer",
              "mac_address": "string",
              "model": "string?",
              "vendor": "string?",
              "status": "string",
              "created_at": "string<date-time>",
              "updated_at": "string<date-time>"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

### Sound Files

Audio files used for greetings, prompts, music on hold, and voicemail.

#### GET /sound-files

List Sound file

Returns a paginated list of sound files. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listSoundFiles`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — Filter the list by one or more fields. Allowed keys: `name`, `comment`, `type`, `source`.
Apply several at once with filter[key1]=value1&filter[key2]=value2.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "parent_id": "550e8400-e29b-41d4-a716-446655440002",
              "name": "Welcome announcement",
              "comment": "Played at the start of the main IVR",
              "message": "Welcome to Customer Support. Press 1 for sales, 2 for support.",
              "ai_voice_id": "voice_en_us_female_1",
              "voice_settings": {
                  "stability": 0.75,
                  "similarity_boost": 0.5
              },
              "source": "uploaded_file",
              "type": "announcement",
              "language": "en",
              "audio_url": "https://api.example.com/sound-files/550e8400-e29b-41d4-a716-446655440000/audio?signature=abc123&expires=1709280000",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /sound-files/{soundFile}

Get a sound File

Returns the sound file identified by the path parameter. Multi-tenant scoping is enforced:
the caller can only fetch resources within their own PBX.

`operationId: getSoundFile`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "parent_id": "550e8400-e29b-41d4-a716-446655440002",
      "name": "Welcome announcement",
      "comment": "Played at the start of the main IVR",
      "message": "Welcome to Customer Support. Press 1 for sales, 2 for support.",
      "ai_voice_id": "voice_en_us_female_1",
      "voice_settings": {
          "stability": 0.75,
          "similarity_boost": 0.5
      },
      "source": "uploaded_file",
      "type": "announcement",
      "language": "en",
      "audio_url": "https://api.example.com/sound-files/550e8400-e29b-41d4-a716-446655440000/audio?signature=abc123&expires=1709280000",
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### GET /sound-files/{soundFile}/audio

Stream a sound file's audio (user, pbx)

Stream the raw audio bytes of a sound file. The response is sent inline (not as
an attachment) so it can be played directly, for example as the `src` of an
HTML `<audio>` element. The `Content-Type` reflects the stored format
(commonly `audio/mpeg`, but `audio/wav`, `audio/flac` or `audio/aac` may also
be returned), falling back to `audio/mpeg`.

This endpoint accepts EITHER of two authentication methods:

- A temporary signed URL. This is the `audio_url` returned by the sound file
  resource (it embeds `expires` and `signature` query parameters and expires
  roughly one hour after it is minted). No `Authorization` header is required,
  which lets it be used directly in an `<audio src>`.
- A bearer token carrying the `sound-files` ability. Used for durable or
  programmatic access; the stream is scoped to the token principal's PBX.

When neither a valid signature nor a token is present the request is rejected
with 401. A token that lacks the `sound-files` ability is rejected with 403.

`operationId: getSoundFileAudio`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `soundFile` (path, string<uuid>, required) — Sound file identifier.
- `expires` (query, integer, optional) — Expiry timestamp from the signed `audio_url`. Present only when authenticating with a signed URL.
- `signature` (query, string, optional) — HMAC signature from the signed `audio_url`. Present only when authenticating with a signed URL.

**Responses**:
- `200` — Raw audio stream, sent inline.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The bearer token does not carry the `sound-files` ability.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### GET /sound-files/{soundFileId}/download

Download a Sound File by ID

Returns the sound file download identified by the path parameter. Multi-tenant scoping is
enforced: the caller can only fetch resources within their own PBX.

`operationId: getSoundFileDownload`

**Auth**: PbxServerAuth

**Parameters**:
- `accept` (header, string, optional)
- `soundFileId` (path, integer, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### Statuses

Top-level presence values (e.g. Available, Busy, Away) that users can broadcast.

#### GET /statuses

List statuse

Returns a paginated list of statuses. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listStatuses`

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — The filter key can be any of the following keys: id, pbx_id, mxvp_id, name, icon, font_color, background_color, css, comment, paid, action and sync_tempus.

The filter value can be any string or number.

You can also apply multiple filters using `filter[key1]=value1&filter[key2]=value2`.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "name": "Working",
              "description": "Standard work activity",
              "style": {
                  "icon_class": "briefcase",
                  "font_color": "#FFFFFF",
                  "bg_color": "#1F8F4E"
              },
              "availability": "available",
              "meta_data": {
                  "flags": [
                      "pbx_reachable",
                      "payable"
                  ]
              },
              "allow_multiselect": false,
              "sub_status_selection_mandatory": false,
              "sub_statuses": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440010",
                      "name": "Customer Support",
                      "description": "Handling inbound support calls",
                      "style": {
                          "icon_class": "headset",
                          "font_color": "#FFFFFF",
                          "bg_color": "#2D6CDF"
                      }
                  }
              ],
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /statuses/{id}

Get a Statu

Returns the status identified by the path parameter. Multi-tenant scoping is enforced: the
caller can only fetch resources within their own PBX.

`operationId: getStatus`

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Working",
      "description": "Standard work activity",
      "style": {
          "icon_class": "briefcase",
          "font_color": "#FFFFFF",
          "bg_color": "#1F8F4E"
      },
      "availability": "available",
      "meta_data": {
          "flags": [
              "pbx_reachable",
              "payable"
          ]
      },
      "allow_multiselect": false,
      "sub_status_selection_mandatory": false,
      "sub_statuses": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440010",
              "name": "Customer Support",
              "description": "Handling inbound support calls",
              "style": {
                  "icon_class": "headset",
                  "font_color": "#FFFFFF",
                  "bg_color": "#2D6CDF"
              }
          }
      ],
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### Substatuses

Refinements under a parent status (e.g. Away > Lunch, Away > Meeting).

#### GET /sub-statuses

List substatuse

Returns a paginated list of sub statuses. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listSubStatuses`

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — The filter key can be any of the following keys: id, pbx_id, mxvp_user_id, name, icon, font_color, background_color, css, comment, action and sync_tempus.

The filter value can be any string or number.

You can also apply multiple filters using `filter[key1]=value1&filter[key2]=value2`.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "name": "Lunch",
              "description": "Midday lunch break",
              "style": {
                  "icon_class": "utensils",
                  "font_color": "#FFFFFF",
                  "bg_color": "#E5A100"
              },
              "usage": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440010",
                      "parent_id": "550e8400-e29b-41d4-a716-446655440011",
                      "name": "Away",
                      "description": "Agent is away from the desk",
                      "style": {
                          "icon_class": "pause",
                          "font_color": "#FFFFFF",
                          "bg_color": "#6C757D"
                      },
                      "availability": "away",
                      "meta_data": {
                          "flags": [
                              "billable"
                          ]
                      },
                      "allow_multiselect": false,
                      "sub_status_selection_mandatory": false,
                      "payable": true,
                      "mapping_id": "PAYROLL-LUNCH-01",
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

### Tags

Free-form labels attached to users, devices, or numbers for filtering and reporting.

#### GET /tags

List tag

Returns a paginated list of tags. Use the standard `search`, `sort`, `page`, and `perPage`
query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant scoping is
enforced.

`operationId: listTags`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — Filter the list by one or more fields. Allowed keys: `name`, `description`.
Apply several at once with filter[key1]=value1&filter[key2]=value2.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "VIP Customers",
              "description": "Customers with priority routing and dedicated agents.",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /tags

Create a tag

Create a new tag and optionally attach it to a set of users and SIP devices.
The `pbx_id` is forced from the authenticated user and must not be sent in
the request body.

`operationId: createTag`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "pbx_id": "string<uuid>",
    "name": "string",
    "description": "string?",
    "users": [
        "string<uuid>"
    ],
    "sipDevices": [
        "string<uuid>"
    ]
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "name": "VIP Customers",
          "description": "Customers with priority routing and dedicated agents.",
          "created_at": "2024-03-01T08:29:07Z",
          "updated_at": "2024-03-01T08:29:07Z"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /tags/{id}

Get a Tag

Returns the tag identified by the path parameter. Multi-tenant scoping is enforced: the
caller can only fetch resources within their own PBX.

`operationId: getTag`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "name": "VIP Customers",
      "description": "Customers with priority routing and dedicated agents.",
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### Schedules

Time-of-day rule sets used by call flows to route differently inside vs outside business hours.

#### GET /schedules

List schedule

Returns a paginated list of schedules. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listSchedules`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — Filter the list by one or more fields. Allowed keys: `name`, `description`.
Apply several at once with filter[key1]=value1&filter[key2]=value2.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Business hours",
              "description": "Monday to Friday, 09:00 to 18:00",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /schedules/{id}

Get a Schedule

Returns the schedule identified by the path parameter. Multi-tenant scoping is enforced: the
caller can only fetch resources within their own PBX.

`operationId: getSchedule`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "name": "Business hours",
      "description": "Monday to Friday, 09:00 to 18:00",
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### Twinning

Ring an external number in parallel with the user's extension.

#### GET /twinnings

List twinning

Returns a paginated list of twinnings. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listTwinnings`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — Filter the list by one or more fields. Allowed keys: `status`, `type`, `phone_number`.
Apply several at once with filter[key1]=value1&filter[key2]=value2.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "user_id": "550e8400-e29b-41d4-a716-446655440001",
              "status": "enabled",
              "phone_number": "+12125550100",
              "delay": 5,
              "sync_source": "manual",
              "type": "mobile",
              "dial_strategy": "voip_and_gsm_fallback",
              "fmc_enabled": false,
              "backup_phone_number": "+12125550101",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /twinnings/{id}

Get a Twinning

Returns the twinning identified by the path parameter. Multi-tenant scoping is enforced: the
caller can only fetch resources within their own PBX.

`operationId: getTwinning`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "user_id": "550e8400-e29b-41d4-a716-446655440001",
      "status": "enabled",
      "phone_number": "+12125550100",
      "delay": 5,
      "sync_source": "manual",
      "type": "mobile",
      "dial_strategy": "voip_and_gsm_fallback",
      "fmc_enabled": false,
      "backup_phone_number": "+12125550101",
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### GET /users/{user}/twinnings

List a user's twinnings (user)

List the twinnings that belong to the given user. Results are paginated.

The following Spatie QueryBuilder filters are supported (exact match):
`filter[status]`, `filter[phone_number]`, `filter[delay]`,
`filter[sync_source]` and `filter[type]`. Sorting is allowed on the same
fields; prefix a field with `-` for descending order, for example
`sort=-delay`.

When a twinning has Fixed-Mobile Convergence (FMC) enabled together with a
backup phone number, the response also includes a derived read-only entry
for that backup number. The derived entry carries a generated `id` and is
not a separately stored record.

`operationId: listUserTwinnings`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — User identifier
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Allowed values are `status`, `phone_number`, `delay`,
`sync_source` and `type`. Prefix with `-` for descending order.
- `filter[key]` (query, string, optional) — </br>The filter key can be one of: `status`, `phone_number`, `delay`,
`sync_source` or `type` (exact match). </br>The filter value can be any
string. </br></br> It is also possible to apply multiple filters using
the following format: filter[status]=enabled&filter[type]=mobile

**Responses**:
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "user_id": "550e8400-e29b-41d4-a716-446655440001",
              "status": "enabled",
              "phone_number": "+12125550100",
              "delay": 5,
              "sync_source": "manual",
              "type": "mobile",
              "dial_strategy": "voip_and_gsm_fallback",
              "fmc_enabled": false,
              "backup_phone_number": "+12125550101",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### POST /users/{user}/twinnings

Create a twinning for a user (user)

Create a twinning for the given user. The `user_id` is taken from the path
and the `sync_source` is always set to `manual` server-side; neither may be
supplied in the request body.

Whether `phone_number` is required depends on the combination of `type` and
`dial_strategy` (for example, a `voip_only` mobile twinning does not require
a phone number). A user may hold at most one `teams`-type twinning and at
most one `voip_only` twinning. `backup_phone_number` is only accepted when
`fmc_enabled` is true and the twinning is FMC-eligible (mobile type).

`operationId: createUserTwinning`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — User identifier

**Request body** (`application/json`) (required):
```json
{
    "status": "enabled",
    "phone_number": "+12125550100",
    "delay": 5,
    "type": "mobile",
    "dial_strategy": "voip_and_gsm_fallback",
    "fmc_enabled": false,
    "backup_phone_number": "string?"
}
```

**Responses**:
- `201` — Created
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "status": "enabled",
          "phone_number": "+12125550100",
          "delay": 5,
          "sync_source": "manual",
          "type": "mobile",
          "dial_strategy": "voip_and_gsm_fallback",
          "fmc_enabled": false,
          "backup_phone_number": "+12125550101",
          "created_at": "2024-03-01T08:29:07Z",
          "updated_at": "2024-03-01T08:29:07Z"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /users/{user}/twinnings/{twinning}

Get a user's twinning (user)

Retrieve a single twinning belonging to the given user. A twinning that does
not exist, or that belongs to a different user, responds with 404.

`operationId: getUserTwinning`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — User identifier
- `twinning` (path, string<uuid>, required) — Twinning identifier

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "status": "enabled",
          "phone_number": "+12125550100",
          "delay": 5,
          "sync_source": "manual",
          "type": "mobile",
          "dial_strategy": "voip_and_gsm_fallback",
          "fmc_enabled": false,
          "backup_phone_number": "+12125550101",
          "created_at": "2024-03-01T08:29:07Z",
          "updated_at": "2024-03-01T08:29:07Z"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### PUT /users/{user}/twinnings/{twinning}

Update a user's twinning (user)

Update a twinning belonging to the given user. Every field is optional; only
the fields present in the body are changed. `user_id` and `sync_source` are
server-managed and must not be sent.

For twinnings whose `sync_source` is `provision`, the `phone_number`, `type`
and `dial_strategy` fields are locked: provisioned values are preserved and
any conflicting values are rejected.

`operationId: updateUserTwinning`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — User identifier
- `twinning` (path, string<uuid>, required) — Twinning identifier

**Request body** (`application/json`) (required):
```json
{
    "status": "enum",
    "phone_number": "string",
    "delay": "integer",
    "type": "enum",
    "dial_strategy": "enum",
    "fmc_enabled": "boolean",
    "backup_phone_number": "string?"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "status": "enabled",
          "phone_number": "+12125550100",
          "delay": 5,
          "sync_source": "manual",
          "type": "mobile",
          "dial_strategy": "voip_and_gsm_fallback",
          "fmc_enabled": false,
          "backup_phone_number": "+12125550101",
          "created_at": "2024-03-01T08:29:07Z",
          "updated_at": "2024-03-01T08:29:07Z"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### PATCH /users/{user}/twinnings/{twinning}

Update a user's twinning (user)

Alias of the `PUT` update with identical request shape and validation rules.
Only the fields present in the body are changed; `user_id` and `sync_source`
are server-managed. Provisioned twinnings keep their locked
`phone_number`, `type` and `dial_strategy` values.

`operationId: patchUserTwinning`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — User identifier
- `twinning` (path, string<uuid>, required) — Twinning identifier

**Request body** (`application/json`) (required):
```json
{
    "status": "enum",
    "phone_number": "string",
    "delay": "integer",
    "type": "enum",
    "dial_strategy": "enum",
    "fmc_enabled": "boolean",
    "backup_phone_number": "string?"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "status": "enabled",
          "phone_number": "+12125550100",
          "delay": 5,
          "sync_source": "manual",
          "type": "mobile",
          "dial_strategy": "voip_and_gsm_fallback",
          "fmc_enabled": false,
          "backup_phone_number": "+12125550101",
          "created_at": "2024-03-01T08:29:07Z",
          "updated_at": "2024-03-01T08:29:07Z"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### DELETE /users/{user}/twinnings/{twinning}

Delete a user's twinning (user)

Delete a twinning belonging to the given user. A twinning whose
`sync_source` is `provision` cannot be removed through this endpoint and
responds with 422.

`operationId: deleteUserTwinning`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — User identifier
- `twinning` (path, string<uuid>, required) — Twinning identifier

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `204` — Deleted (no content)
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

### Do Not Disturb

Manage Do Not Disturb rules for users and extensions.

#### GET /dnds

List DND rules for the authenticated user

Returns a paginated list of dnds. Use the standard `search`, `sort`, `page`, and `perPage`
query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant scoping is
enforced.

`operationId: listDnds`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440002",
              "user_id": "550e8400-e29b-41d4-a716-446655440001",
              "caller_identity_id": "550e8400-e29b-41d4-a716-446655440003",
              "type": "external",
              "status": false,
              "created_at": "string?<date-time>",
              "updated_at": "string?<date-time>"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### GET /users/{user}/dnds

List DNDs for a user

Returns the dnd user dnds index identified by the path parameter. Multi-tenant scoping is
enforced: the caller can only fetch resources within their own PBX.

`operationId: getDndUserDndsIndex`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "user_id": "550e8400-e29b-41d4-a716-446655440001",
              "caller_identity_id": 1,
              "extension_id": 1,
              "status": false
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### POST /users/{user}/dnds

Create a DND for a user

Creates a new dnd user dnds index. Validation rules and required fields are defined in the
request body schema below.

`operationId: postDndUserDndsIndex`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Request body** (`application/json`) (required):
```json
{
    "type": "external",
    "caller_identity_id": "550e8400-e29b-41d4-a716-446655440003",
    "status": true
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "caller_identity_id": 1,
          "extension_id": 1,
          "status": false
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### PUT /users/{user}/dnds/{userDnd}

Update a DND for a user

Replaces the dnd user dnds specific with the fields supplied in the request body. Fields
omitted from the body are reset to their defaults.

`operationId: updateDndUserDndsSpecific`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user
- `dnd` (path, string<uuid>, required) — UUID of the DND

**Request body** (`application/json`) (required):
```json
{
    "status": true
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "caller_identity_id": 1,
          "extension_id": 1,
          "status": true
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /users/{user}/dnds/bulk

Bulk-update DND rules for a user

Creates a new dnd user dnds bulk. Validation rules and required fields are defined in the
request body schema below.

`operationId: postDndUserDndsBulk`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Request body** (`application/json`) (required):
```json
{
    "dnds": [
        {
            "type": "enum",
            "caller_identity_id": "string?<uuid>",
            "status": "boolean"
        }
    ]
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "status": true
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /users/{user}/dnds/all-external

Toggle DND on every external extension of a user

Creates a new dnd user dnds all external. Validation rules and required fields are defined
in the request body schema below.

`operationId: postDndUserDndsAllExternal`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Request body** (`application/json`):
```json
{
    "status": true
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "updated": 3
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### POST /users/{user}/dnds/all

Toggle DND on every extension of a user

Creates a new dnd user dnds all. Validation rules and required fields are defined in the
request body schema below.

`operationId: postDndUserDndsAll`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Request body** (`application/json`):
```json
{
    "status": true
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "updated": 5
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### Images

Retrieve images (avatars, logos) associated with models.

#### GET /images/{modelType}/{modelId}

List images for a model

Returns the image list identified by the path parameter. Multi-tenant scoping is enforced:
the caller can only fetch resources within their own PBX.

`operationId: getImageList`

**Parameters**:
- `accept` (header, string, optional)
- `modelType` (path, string, required) — The model type (e.g. user, contact)
- `modelId` (path, string<uuid>, required) — The model UUID

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "images": []
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### GET /images/{modelType}/{modelId}/{usage}

Get a specific image for a model by usage

Returns the image get identified by the path parameter. Multi-tenant scoping is enforced:
the caller can only fetch resources within their own PBX.

`operationId: getImageGet`

**Parameters**:
- `accept` (header, string, optional)
- `modelType` (path, string, required) — The model type (e.g. user, contact)
- `modelId` (path, string<uuid>, required) — The model UUID
- `usage` (path, string, required) — Image usage identifier (e.g. avatar, thumbnail)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "integer",
          "model_type": "string",
          "model_id": "string<uuid>",
          "usage": "string",
          "url": "string",
          "created_at": "string<date-time>"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

### User Groups

Manage user groups for organizing users within a PBX.

#### GET /user-groups

List user groups

Returns a paginated list of user groups. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listUserGroups`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440020",
              "name": "Sales Team",
              "user_id": "550e8400-e29b-41d4-a716-446655440001",
              "position": 1,
              "users": [
                  "string<uuid>"
              ]
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### POST /user-groups

Create a user-group

Creates a new user group. Validation rules and required fields are defined in the request
body schema below.

`operationId: createUserGroup`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "name": "Sales Team",
    "user_ids": [
        "550e8400-e29b-41d4-a716-446655440010",
        "550e8400-e29b-41d4-a716-446655440011"
    ]
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "success": "boolean",
      "message": "string",
      "data": {
          "created_entry": {
              "id": "string<uuid>",
              "name": "string",
              "user_id": "string<uuid>",
              "users": [
                  "string<uuid>"
              ]
          },
          "non_added_user_ids": [
              "string<uuid>"
          ]
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /user-groups/update-positions

Update positions of user-groups

Creates a new user group update positions. Validation rules and required fields are defined
in the request body schema below.

`operationId: postUserGroupUpdatePositions`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
[
    {
        "id": "550e8400-e29b-41d4-a716-446655440020",
        "position": 1
    }
]
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "message": "Positions updated successfully"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /user-groups/{userGroupId}

Get an user-group

Returns the user group identified by the path parameter. Multi-tenant scoping is enforced:
the caller can only fetch resources within their own PBX.

`operationId: getUserGroup`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — UUID of the user-group

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440020",
          "name": "Sales Team",
          "position": 1,
          "pbx_id": "550e8400-e29b-41d4-a716-446655440000"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### PATCH /user-groups/{userGroupId}

Partially update a user-group

Partially updates the user group. Only the fields supplied in the request body are changed;
others are left untouched.

`operationId: updateUserGroup`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — UUID of the user-group

**Request body** (`application/json`) (required):
```json
{
    "name": "Sales Team Updated",
    "user_ids": [
        "550e8400-e29b-41d4-a716-446655440010",
        "550e8400-e29b-41d4-a716-446655440011"
    ],
    "position": 2
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440020",
          "name": "Sales Team Updated",
          "position": 2,
          "pbx_id": "550e8400-e29b-41d4-a716-446655440000"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### DELETE /user-groups/{userGroupId}

Delete a user-group

Deletes the user group. Soft-delete behaviour is model-dependent: most resources are
recoverable via the restore endpoint, while a few are hard-deleted.

`operationId: deleteUserGroup`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — UUID of the user-group

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "message": "User-group deleted successfully"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### POST /user-groups/{userGroupId}/add-users

Add users to a user-group

Creates a new user group add users. Validation rules and required fields are defined in the
request body schema below.

`operationId: postUserGroupAddUsers`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — UUID of the user-group

**Request body** (`application/json`) (required):
```json
{
    "user_ids": [
        "550e8400-e29b-41d4-a716-446655440001",
        "550e8400-e29b-41d4-a716-446655440002"
    ]
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "message": "Users added successfully"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /user-groups/{userGroupId}/remove-users

Remove users from a user-group

Creates a new user group remove users. Validation rules and required fields are defined in
the request body schema below.

`operationId: postUserGroupRemoveUsers`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — UUID of the user-group

**Request body** (`application/json`) (required):
```json
{
    "user_ids": [
        "550e8400-e29b-41d4-a716-446655440001",
        "550e8400-e29b-41d4-a716-446655440002"
    ]
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "message": "Users removed successfully"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

### Pools

Extension pools and their user assignments.

#### GET /pools/{id}/users

List users in a pool

Returns the pool users identified by the path parameter. Multi-tenant scoping is enforced:
the caller can only fetch resources within their own PBX.

`operationId: getPoolUsers`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required) — UUID of the pool

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440001",
              "first_name": "John",
              "last_name": "Doe",
              "email": "john.doe@example.com",
              "status": "enabled"
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

### Voxbi Group Tabs

Custom tab configurations for Voxbi group views.

#### GET /voxbi-group-tabs

List all voxbi group tabs

Returns a paginated list of voxbi group tabs. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listVoxbiGroupTabs`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Sales Team",
              "description": "All sales representatives",
              "users": [
                  "550e8400-e29b-41d4-a716-446655440002"
              ]
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

### Users

PBX users. Each user owns an extension, devices, presence status, and forwarding rules.

#### POST /register-device

Register a device for push notifications

Creates a new push notification register. Validation rules and required fields are defined
in the request body schema below.

`operationId: postPushNotificationRegister`

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "token": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
    "platform": "ios"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "status": "string",
      "message": "string"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /unregister-device

Unregister a device from push notifications

Creates a new push notification unregister. Validation rules and required fields are defined
in the request body schema below.

`operationId: postPushNotificationUnregister`

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "token": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "status": "string",
      "message": "string"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /cleanup-old-tokens

Clean up old push notification tokens

Triggers the action on the targeted push notification cleanup.

`operationId: postPushNotificationCleanup`

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "status": "string",
      "message": "string",
      "removed": "integer"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### POST /notify-device

Send a push notification to a device

Creates a new push notification notify device. Validation rules and required fields are
defined in the request body schema below.

`operationId: postPushNotificationNotifyDevice`

**Auth**: PbxServerAuth

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "token": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
    "title": "Incoming Call",
    "body": "John Doe is calling you",
    "data": []
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "status": "string",
      "message": "string"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /layout

Get the active Status Changer layout (user)

Resolve and return the active Tempus Status Changer layout for the
authenticated user. The PBX is derived server-side from the bearer token
and cannot be supplied by the client.

Resolution rules:

- The user's PBX must be set on the token. If it is not, the request fails
  with `400`.
- The active layout (`is_active = true`) for the user's PBX is selected. If
  the user belongs to a group and an active layout targets that group, the
  most recently created group-targeted layout wins; otherwise the first
  active layout for the PBX is returned.
- If no active layout exists for the PBX, the request fails with `404`.

The response is returned in the legacy `tempus` representation, wrapped in a
`data` envelope, and includes a `filters` echo of any `filter` query values.

`operationId: getUserLayout`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `200` — OK
  ```json
  {
      "data": {
          "options": {
              "geoEnable": "boolean",
              "geoForce": "boolean",
              "postAction": "boolean",
              "updateTimer": "integer"
          },
          "layout": {
              "id": "string<uuid>",
              "mxvp_user_id": "string<uuid>",
              "name": "string",
              "displayflags": "string",
              "type": "string",
              "delete_date": "string?",
              "tl_layouts_statuses": [
                  {
                      "tllayout_id": "string<uuid>",
                      "status_id": {
                          "id": "string<uuid>",
                          "mxvp_user_id": "string<uuid>",
                          "name": "string",
                          "icon": "string?",
                          "fontcolor": "string",
                          "backgroundcolor": "string",
                          "css": "string",
                          "comment": "string",
                          "payable": "boolean",
                          "action": "string?",
                          "migration_id": "string?",
                          "mapping_id": "integer",
                          "delete_date": "string?",
                          "sub_status_layouts": [
                              "\u2026"
                          ],
                          "meta_data": [],
                          "allow_multiselect": "boolean",
                          "sub_status_selection_mandatory": "boolean",
                          "sub_statuses": [
                              "\u2026"
                          ]
                      },
                      "position": "integer",
                      "size": "integer",
                      "toggle_status_id": "string?"
                  }
              ]
          },
          "preview_url": "string<uri>"
      },
      "filters": []
  }
  ```
- `400` — The authenticated user has no PBX associated with their token, so no
layout can be resolved.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### GET /users

List user

Returns a paginated list of users. Use the standard `search`, `sort`, `page`, and `perPage`
query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant scoping is
enforced.

`operationId: listUsers`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `search` (query, string, optional) — Free-text search across the resource's searchable fields.
- `page` (query, integer, optional) — Page number to fetch. Defaults to `1`.
- `per_page` (query, integer, optional) — Number of items per page. Defaults to `25`, maximum `100`.
- `sort` (query, string, optional) — Field to sort by. Prefix with `-` for descending order (e.g. `-created_at`).
- `filter[key]` (query, string, optional) — Filter the list by one or more fields. Allowed keys: `first_name`, `last_name`, `email`, `status`, `vip`, `show_in_address_book`.
Apply several at once with filter[key1]=value1&filter[key2]=value2.
- `include` (query, string, optional) — Comma-separated related resources to embed (dot-nested where
shown). Allowed: `extensions`, `groups`, `allowManageGroupContacts`, `allowAccessToGroupContacts`, `sipDevices`, `twinnings`, `callerIdentities`, `locations`, `languages`, `tags`, `queues`.

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "first_name": "Jane",
              "last_name": "Smith",
              "name": "Jane Smith",
              "full_name_with_email": "Jane Smith <jane.smith@example.com>",
              "email": "jane.smith@example.com",
              "status": "enabled",
              "type": "user",
              "language": "en",
              "sync_source": "manual",
              "vip": false,
              "show_in_address_book": true,
              "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
              "caller_identity_internal_extension_id": "550e8400-e29b-41d4-a716-446655440002",
              "caller_identity_external_extension_id": "550e8400-e29b-41d4-a716-446655440003",
              "allow_telephony": true,
              "allow_call_forwarding": true,
              "allow_change_twinning_dest": true,
              "allow_dnd": true,
              "allow_status_changer": false,
              "allow_anonymous_calls": false,
              "allow_international_calls": false,
              "allow_eu_calls": true,
              "allow_national_calls": true,
              "allow_premium_numbers": false,
              "allow_sms": false,
              "allow_manage_global_contacts": false,
              "allow_manage_voicemail_parameters": true,
              "allow_in_call_recording": false,
              "allow_default_pbxes_outgoing_phone_numbers": true,
              "allow_grafana_access": false,
              "allow_right_to_disconnect": true,
              "right_to_disconnect": false,
              "concurrent_calls": true,
              "extension_roaming": true,
              "record_your_calls": false,
              "record_your_internal_calls": 2,
              "access_all_call_recordings": false,
              "call_recordings_visibility": "own_calls_only",
              "call_information_visibility": "own_calls",
              "send_missed_call_via_email": true,
              "limit_email_missed_call_to_direct": false,
              "show_caller_id_for_twinning_call": 2,
              "voicemail_type": "internal",
              "voicemail_destination": "email_only",
              "invited_at": "string?<date-time>",
              "accepted_invitation_at": "string?<date-time>",
              "email_verified_at": "2024-02-15T10:00:00Z",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z",
              "extensions": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "assigned_to_type": "user",
                      "assigned_to_id": "550e8400-e29b-41d4-a716-446655440002",
                      "external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
                      "internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440004",
                      "number": "1337",
                      "name": "Support line",
                      "description": "Front-line support line",
                      "record_external_calls": true,
                      "record_internal_calls": false,
                      "sync_source": "manual",
                      "full_name_with_extension_number": "Jane Smith (1337)",
                      "assigned_to": [],
                      "employee_id": "550e8400-e29b-41d4-a716-446655440005",
                      "department_id": "550e8400-e29b-41d4-a716-446655440006",
                      "is_favorite": false,
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "groups": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                      "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
                      "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
                      "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
                      "external_uid": "grp-support",
                      "allow_call_pickups": true,
                      "name": "Support team",
                      "description": "First-line support",
                      "allow_default_pbxes_outgoing_phone_numbers": false,
                      "display_tab_to_everyone_inside_voxbi": false,
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z",
                      "users": [
                          {
                              "id": "\u2026",
                              "first_name": "\u2026",
                              "last_name": "\u2026",
                              "email": "\u2026"
                          }
                      ]
                  }
              ],
              "allow_manage_group_contacts": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                      "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
                      "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
                      "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
                      "external_uid": "grp-support",
                      "allow_call_pickups": true,
                      "name": "Support team",
                      "description": "First-line support",
                      "allow_default_pbxes_outgoing_phone_numbers": false,
                      "display_tab_to_everyone_inside_voxbi": false,
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z",
                      "users": [
                          {
                              "id": "\u2026",
                              "first_name": "\u2026",
                              "last_name": "\u2026",
                              "email": "\u2026"
                          }
                      ]
                  }
              ],
              "allow_access_to_group_contacts": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
                      "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
                      "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
                      "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
                      "external_uid": "grp-support",
                      "allow_call_pickups": true,
                      "name": "Support team",
                      "description": "First-line support",
                      "allow_default_pbxes_outgoing_phone_numbers": false,
                      "display_tab_to_everyone_inside_voxbi": false,
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z",
                      "users": [
                          {
                              "id": "\u2026",
                              "first_name": "\u2026",
                              "last_name": "\u2026",
                              "email": "\u2026"
                          }
                      ]
                  }
              ],
              "sip_devices": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "target_type": "user",
                      "target_id": "550e8400-e29b-41d4-a716-446655440003",
                      "extension_id_shown_for_internal_calls": "string?<uuid>",
                      "roaming_user_id": "string?<uuid>",
                      "location_id": "550e8400-e29b-41d4-a716-446655440002",
                      "parent_sip_device_id": "string?<uuid>",
                      "name": "Reception desk phone",
                      "description": "Front-desk Yealink T46U",
                      "is_enabled": true,
                      "ip_internal": "192.168.1.42",
                      "ip_external": "203.0.113.10",
                      "user_agent": "Yealink SIP-T46U 108.86.0.10",
                      "mac": "00:0a:95:9d:68:16",
                      "vlan_phone": 100,
                      "vlan_pc": 200,
                      "sip_username": "device_001",
                      "alt_username": "string?",
                      "transport": "tls",
                      "type": "auto",
                      "allow_roaming": false,
                      "language": "en",
                      "easy_deploy": "string?<date-time>",
                      "notify": "string?<date-time>",
                      "allow_default_pbxes_outgoing_phone_numbers": false,
                      "sync_source": "provision",
                      "custom_provisioning": "string?",
                      "last_seen": "2024-03-01T08:29:07Z",
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z",
                      "target": [],
                      "tags": [
                          {
                              "id": "\u2026",
                              "pbx_id": "\u2026",
                              "name": "\u2026",
                              "description": "\u2026",
                              "created_at": "\u2026",
                              "updated_at": "\u2026"
                          }
                      ],
                      "caller_identities": [
                          []
                      ],
                      "location": {
                          "id": "\u2026",
                          "pbx_id": "\u2026",
                          "company_id": "\u2026",
                          "parent_id": "\u2026",
                          "name": "\u2026",
                          "description": "\u2026",
                          "street_no": "\u2026",
                          "street": "\u2026",
                          "city": "\u2026",
                          "postal": "\u2026",
                          "country_id": "\u2026",
                          "lft": "\u2026",
                          "rgt": "\u2026",
                          "depth": "\u2026",
                          "enable_location_as_presence_status": "\u2026",
                          "style": "\u2026",
                          "is_fixed_location": "\u2026",
                          "latitude": "\u2026",
                          "longitude": "\u2026",
                          "geofence_radius_m": "\u2026",
                          "created_at": "\u2026",
                          "updated_at": "\u2026"
                      }
                  }
              ],
              "twinnings": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "user_id": "550e8400-e29b-41d4-a716-446655440001",
                      "status": "enabled",
                      "phone_number": "+12125550100",
                      "delay": 5,
                      "sync_source": "manual",
                      "type": "mobile",
                      "dial_strategy": "voip_and_gsm_fallback",
                      "fmc_enabled": false,
                      "backup_phone_number": "+12125550101",
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "caller_identities": [
                  []
              ],
              "locations": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "company_id": "550e8400-e29b-41d4-a716-446655440004",
                      "parent_id": "550e8400-e29b-41d4-a716-446655440002",
                      "name": "Luxembourg HQ",
                      "description": "Main office, 5th floor",
                      "street_no": "12",
                      "street": "Main Street",
                      "city": "Luxembourg",
                      "postal": "1234",
                      "country_id": "550e8400-e29b-41d4-a716-446655440003",
                      "lft": 1,
                      "rgt": 10,
                      "depth": 0,
                      "enable_location_as_presence_status": false,
                      "style": {
                          "icon_class": "la-map-pin",
                          "bg_color": "#3490dc",
                          "font_color": "#ffffff"
                      },
                      "is_fixed_location": true,
                      "latitude": 49.6116,
                      "longitude": 6.1319,
                      "geofence_radius_m": 100,
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "languages": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "name": "English",
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "tags": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "name": "VIP Customers",
                      "description": "Customers with priority routing and dedicated agents.",
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "queues": [
                  []
              ]
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /users/{id}

Get an User

Returns the user identified by the path parameter. Multi-tenant scoping is enforced: the
caller can only fetch resources within their own PBX.

`operationId: getUser`

**Auth**: bearerAuth, IntegrationApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "first_name": "Jane",
      "last_name": "Smith",
      "name": "Jane Smith",
      "full_name_with_email": "Jane Smith <jane.smith@example.com>",
      "email": "jane.smith@example.com",
      "status": "enabled",
      "type": "user",
      "language": "en",
      "sync_source": "manual",
      "vip": false,
      "show_in_address_book": true,
      "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
      "caller_identity_internal_extension_id": "550e8400-e29b-41d4-a716-446655440002",
      "caller_identity_external_extension_id": "550e8400-e29b-41d4-a716-446655440003",
      "allow_telephony": true,
      "allow_call_forwarding": true,
      "allow_change_twinning_dest": true,
      "allow_dnd": true,
      "allow_status_changer": false,
      "allow_anonymous_calls": false,
      "allow_international_calls": false,
      "allow_eu_calls": true,
      "allow_national_calls": true,
      "allow_premium_numbers": false,
      "allow_sms": false,
      "allow_manage_global_contacts": false,
      "allow_manage_voicemail_parameters": true,
      "allow_in_call_recording": false,
      "allow_default_pbxes_outgoing_phone_numbers": true,
      "allow_grafana_access": false,
      "allow_right_to_disconnect": true,
      "right_to_disconnect": false,
      "concurrent_calls": true,
      "extension_roaming": true,
      "record_your_calls": false,
      "record_your_internal_calls": 2,
      "access_all_call_recordings": false,
      "call_recordings_visibility": "own_calls_only",
      "call_information_visibility": "own_calls",
      "send_missed_call_via_email": true,
      "limit_email_missed_call_to_direct": false,
      "show_caller_id_for_twinning_call": 2,
      "voicemail_type": "internal",
      "voicemail_destination": "email_only",
      "invited_at": "string?<date-time>",
      "accepted_invitation_at": "string?<date-time>",
      "email_verified_at": "2024-02-15T10:00:00Z",
      "created_at": "2024-03-01T08:29:07Z",
      "updated_at": "2024-03-01T08:29:07Z",
      "extensions": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "assigned_to_type": "user",
              "assigned_to_id": "550e8400-e29b-41d4-a716-446655440002",
              "external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
              "internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440004",
              "number": "1337",
              "name": "Support line",
              "description": "Front-line support line",
              "record_external_calls": true,
              "record_internal_calls": false,
              "sync_source": "manual",
              "full_name_with_extension_number": "Jane Smith (1337)",
              "assigned_to": [],
              "employee_id": "550e8400-e29b-41d4-a716-446655440005",
              "department_id": "550e8400-e29b-41d4-a716-446655440006",
              "is_favorite": false,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "groups": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
              "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
              "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
              "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
              "external_uid": "grp-support",
              "allow_call_pickups": true,
              "name": "Support team",
              "description": "First-line support",
              "allow_default_pbxes_outgoing_phone_numbers": false,
              "display_tab_to_everyone_inside_voxbi": false,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z",
              "users": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440006",
                      "first_name": "Jane",
                      "last_name": "Smith",
                      "email": "jane.smith@example.com"
                  }
              ]
          }
      ],
      "allow_manage_group_contacts": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
              "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
              "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
              "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
              "external_uid": "grp-support",
              "allow_call_pickups": true,
              "name": "Support team",
              "description": "First-line support",
              "allow_default_pbxes_outgoing_phone_numbers": false,
              "display_tab_to_everyone_inside_voxbi": false,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z",
              "users": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440006",
                      "first_name": "Jane",
                      "last_name": "Smith",
                      "email": "jane.smith@example.com"
                  }
              ]
          }
      ],
      "allow_access_to_group_contacts": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "default_internal_call_flow_id": "550e8400-e29b-41d4-a716-446655440002",
              "default_external_call_flow_id": "550e8400-e29b-41d4-a716-446655440003",
              "extension_id_shown_outside_the_group": "550e8400-e29b-41d4-a716-446655440005",
              "default_caller_identity_id": "550e8400-e29b-41d4-a716-446655440004",
              "external_uid": "grp-support",
              "allow_call_pickups": true,
              "name": "Support team",
              "description": "First-line support",
              "allow_default_pbxes_outgoing_phone_numbers": false,
              "display_tab_to_everyone_inside_voxbi": false,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z",
              "users": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440006",
                      "first_name": "Jane",
                      "last_name": "Smith",
                      "email": "jane.smith@example.com"
                  }
              ]
          }
      ],
      "sip_devices": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "target_type": "user",
              "target_id": "550e8400-e29b-41d4-a716-446655440003",
              "extension_id_shown_for_internal_calls": "string?<uuid>",
              "roaming_user_id": "string?<uuid>",
              "location_id": "550e8400-e29b-41d4-a716-446655440002",
              "parent_sip_device_id": "string?<uuid>",
              "name": "Reception desk phone",
              "description": "Front-desk Yealink T46U",
              "is_enabled": true,
              "ip_internal": "192.168.1.42",
              "ip_external": "203.0.113.10",
              "user_agent": "Yealink SIP-T46U 108.86.0.10",
              "mac": "00:0a:95:9d:68:16",
              "vlan_phone": 100,
              "vlan_pc": 200,
              "sip_username": "device_001",
              "alt_username": "string?",
              "transport": "tls",
              "type": "auto",
              "allow_roaming": false,
              "language": "en",
              "easy_deploy": "string?<date-time>",
              "notify": "string?<date-time>",
              "allow_default_pbxes_outgoing_phone_numbers": false,
              "sync_source": "provision",
              "custom_provisioning": "string?",
              "last_seen": "2024-03-01T08:29:07Z",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z",
              "target": [],
              "tags": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                      "name": "VIP Customers",
                      "description": "Customers with priority routing and dedicated agents.",
                      "created_at": "2024-03-01T08:29:07Z",
                      "updated_at": "2024-03-01T08:29:07Z"
                  }
              ],
              "caller_identities": [
                  []
              ],
              "location": {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "company_id": "550e8400-e29b-41d4-a716-446655440004",
                  "parent_id": "550e8400-e29b-41d4-a716-446655440002",
                  "name": "Luxembourg HQ",
                  "description": "Main office, 5th floor",
                  "street_no": "12",
                  "street": "Main Street",
                  "city": "Luxembourg",
                  "postal": "1234",
                  "country_id": "550e8400-e29b-41d4-a716-446655440003",
                  "lft": 1,
                  "rgt": 10,
                  "depth": 0,
                  "enable_location_as_presence_status": false,
                  "style": {
                      "icon_class": "la-map-pin",
                      "bg_color": "#3490dc",
                      "font_color": "#ffffff"
                  },
                  "is_fixed_location": true,
                  "latitude": 49.6116,
                  "longitude": 6.1319,
                  "geofence_radius_m": 100,
                  "created_at": "2024-03-01T08:29:07Z",
                  "updated_at": "2024-03-01T08:29:07Z"
              }
          }
      ],
      "twinnings": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "user_id": "550e8400-e29b-41d4-a716-446655440001",
              "status": "enabled",
              "phone_number": "+12125550100",
              "delay": 5,
              "sync_source": "manual",
              "type": "mobile",
              "dial_strategy": "voip_and_gsm_fallback",
              "fmc_enabled": false,
              "backup_phone_number": "+12125550101",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "caller_identities": [
          []
      ],
      "locations": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "company_id": "550e8400-e29b-41d4-a716-446655440004",
              "parent_id": "550e8400-e29b-41d4-a716-446655440002",
              "name": "Luxembourg HQ",
              "description": "Main office, 5th floor",
              "street_no": "12",
              "street": "Main Street",
              "city": "Luxembourg",
              "postal": "1234",
              "country_id": "550e8400-e29b-41d4-a716-446655440003",
              "lft": 1,
              "rgt": 10,
              "depth": 0,
              "enable_location_as_presence_status": false,
              "style": {
                  "icon_class": "la-map-pin",
                  "bg_color": "#3490dc",
                  "font_color": "#ffffff"
              },
              "is_fixed_location": true,
              "latitude": 49.6116,
              "longitude": 6.1319,
              "geofence_radius_m": 100,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "languages": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "English",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "tags": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "VIP Customers",
              "description": "Customers with priority routing and dedicated agents.",
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "queues": [
          []
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### GET /users/{email}/webrtc

Get an user WebRTC configuration by Email

Returns the user webrtc identified by the path parameter. Multi-tenant scoping is enforced:
the caller can only fetch resources within their own PBX.

`operationId: getUserWebrtc`

**Auth**: ApiKeyAuth

**Parameters**:
- `accept` (header, string, optional)
- `email` (path, string, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "hostname": "pbx.mixvoip.com",
      "port": 8089,
      "username": "mixvoip_webrtc_1337",
      "password": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
      "caller_identities": [
          {
              "id": 1,
              "root_phone_nuber_id": 1,
              "external_caller_extension": "1337",
              "external_caller_number": "2033331337"
          }
      ],
      "allow_anonymous_calls": true,
      "allow_call_forwarding": true,
      "allow_change_twinning_dest": true,
      "allow_dnd": true,
      "allow_status_changer": true,
      "call_information_visibility": "own_calls",
      "call_pickpup_restrictions": "yes",
      "voicemail": true,
      "voicemail_destination": "email_only",
      "voicemail_pin": "1234",
      "voicemail_type": "enabled"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### GET /qr-code

Get QR code for authenticated user

Returns a paginated list of user qr code. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: getUserQrCode`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "qr_code": "data:image/png;base64,iVBORw0KGgo="
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### GET /soop-status

Get SOOP status for authenticated user

Returns a paginated list of user soop status. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: getUserSoopStatus`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "status": "online",
          "sub_status": "in_meeting",
          "updated_at": "2023-10-17T14:12:22Z"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### POST /favorites

Toggle favorite user

Creates a new user favorites. Validation rules and required fields are defined in the
request body schema below.

`operationId: postUserFavorites`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "user_id": "550e8400-e29b-41d4-a716-446655440000"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "data": {
          "favorited": true
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /poll

Poll extensions with DND, status and in-call flags (user)

Live board for every extension in the authenticated user's PBX whose extension is
linked to a user: current Tempus status, sub-statuses, DND, the in-call flag and,
for employees currently on leave, when they are next available.

The response is NOT a list and is NOT paginated: it is a single object keyed by
Cockpit user id, so a client can look a user up directly. Responses are cached per
PBX for a few seconds, so a change made through another endpoint (for example
`POST /in-call`) surfaces here within that window rather than instantly.

`in_call` is owned by Cockpit and comes from what the user's own client last reported
to `POST /in-call`. It is independent of whether the user has a running Tempus
timepoint, and a `true` decays to `false` after an hour if the hang-up is never
reported. `updated_at` is the status/timepoint change time, which is a different
thing from `in_call_updated_at`.

`operationId: getUserPoll`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  []
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### GET /in-call

Read the authenticated user's in-call flag (user)

Returns whether the authenticated user is currently reported as being on a call,
plus when that was last reported. The flag is owned by Cockpit: it is whatever the
client last sent to `POST /in-call`.

A flag set to `true` is trusted for a limited window only (`ttl_seconds`, one hour by
default). Past that window it decays back to `false` on its own, so a client that
never gets to report the hang-up (crash, network loss, killed app) cannot leave the
user marked in-call forever. `in_call_updated_at` and `expires_at` are `null` when
nothing has been reported inside the window.

`operationId: getUserInCall`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `200` — OK
  ```json
  {
      "data": {
          "user_id": "550e8400-e29b-41d4-a716-446655440000",
          "in_call": true,
          "in_call_updated_at": "2026-08-03T09:14:22+00:00",
          "expires_at": "2026-08-03T10:14:22+00:00",
          "ttl_seconds": 3600
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.

#### POST /in-call

Set the authenticated user's in-call flag (user)

Reports whether the authenticated user is on a call right now: send `true` when the
call starts and `false` when it ends. The user is always taken from the bearer token,
so this endpoint can never change anyone else's flag.

The call is idempotent: sending the same value again simply refreshes the timestamp
and pushes the expiry window out. Sending `false` clears the flag immediately, and a
`true` that is never cleared expires by itself after `ttl_seconds` (one hour by
default), so it is safe to miss a hang-up report.

The new value is served by `GET /poll` (as `in_call` and `in_call_updated_at`) within
the poll's own short cache window of a few seconds.

`operationId: postUserInCall`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "in_call": true
}
```

**Responses**:
- `200` — OK
  ```json
  {
      "data": {
          "user_id": "550e8400-e29b-41d4-a716-446655440000",
          "in_call": true,
          "in_call_updated_at": "2026-08-03T09:14:22+00:00",
          "expires_at": "2026-08-03T10:14:22+00:00",
          "ttl_seconds": 3600
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Content. `in_call` is missing or is not a boolean.
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.

#### GET /user/devices

List devices for authenticated user

Returns a paginated list of user devices. Use the standard `search`, `sort`, `page`, and
`perPage` query parameters; use `filter[key]=value` to narrow the result set. Multi-tenant
scoping is enforced.

`operationId: listUserDevices`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `type` (query, string, optional) — Filter by device type
- `active` (query, boolean, optional) — Filter by active status

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "message": "Devices retrieved successfully",
      "devices": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440070",
              "name": "My iPhone",
              "user_id": "550e8400-e29b-41d4-a716-446655440001",
              "twinning_id": "string?<uuid>",
              "type": "fcm",
              "token": "abc123xyz...",
              "is_active": true,
              "last_used_at": "2023-10-17T14:12:22Z",
              "created_at": "2023-10-01T10:00:00Z",
              "updated_at": "2023-10-17T14:12:22Z"
          }
      ],
      "total": 1
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### POST /user/devices

Register a device for authenticated user

Creates a new user device. Validation rules and required fields are defined in the request
body schema below.

`operationId: createUserDevice`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "name": "My iPhone",
    "type": "fcm",
    "token": "abc123xyz..."
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — Device updated (token already existed)
  ```json
  {
      "message": "Device updated successfully",
      "device": {
          "id": "550e8400-e29b-41d4-a716-446655440070",
          "name": "My iPhone",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "twinning_id": "string?<uuid>",
          "type": "fcm",
          "token": "abc123xyz...",
          "is_active": true,
          "last_used_at": "2023-10-17T14:12:22Z",
          "created_at": "2023-10-01T10:00:00Z",
          "updated_at": "2023-10-17T14:12:22Z"
      }
  }
  ```
- `201` — Device registered successfully
  ```json
  {
      "message": "Device registered successfully",
      "device": {
          "id": "550e8400-e29b-41d4-a716-446655440070",
          "name": "My iPhone",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "twinning_id": "string?<uuid>",
          "type": "fcm",
          "token": "abc123xyz...",
          "is_active": true,
          "last_used_at": "2023-10-17T14:12:22Z",
          "created_at": "2023-10-17T14:12:22Z",
          "updated_at": "2023-10-17T14:12:22Z"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### PATCH /user/devices/{deviceIdOrToken}

Update a device for authenticated user

Partially updates the user device. Only the fields supplied in the request body are changed;
others are left untouched.

`operationId: updateUserDevice`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `deviceIdOrToken` (path, string, required) — Device UUID or push token

**Request body** (`application/json`) (required):
```json
{
    "name": "My Android",
    "is_active": false
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "message": "Device updated successfully",
      "device": {
          "id": "550e8400-e29b-41d4-a716-446655440070",
          "name": "My Android",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "twinning_id": "string?<uuid>",
          "type": "fcm",
          "token": "abc123xyz...",
          "is_active": false,
          "last_used_at": "2023-10-17T14:12:22Z",
          "created_at": "2023-10-01T10:00:00Z",
          "updated_at": "2023-10-17T14:12:22Z"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### DELETE /user/devices/{deviceIdOrToken}

Delete a device for authenticated user

Deletes the user device. Soft-delete behaviour is model-dependent: most resources are
recoverable via the restore endpoint, while a few are hard-deleted.

`operationId: deleteUserDevice`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `deviceIdOrToken` (path, string, required) — Device UUID or push token

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "message": "Device deleted successfully"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### POST /dnd

Set User DND

Triggers the action on the targeted user dnd.

`operationId: postUserDnd`

**Auth**: PbxServerAuth

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "user_id": 1,
      "caller_identity_id": 1,
      "extension_id": 1,
      "status": false,
      "id": 1
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /users/{user}/forwards

List forwards for a user

Returns the user user forwards index identified by the path parameter. Multi-tenant scoping
is enforced: the caller can only fetch resources within their own PBX.

`operationId: getUserUserForwardsIndex`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440010",
              "user_id": "550e8400-e29b-41d4-a716-446655440001",
              "type": "unconditional",
              "destination": "+32499123456",
              "status": true
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### POST /users/{user}/forwards

Create a forward for a user

Creates a new user user forwards index. Validation rules and required fields are defined in
the request body schema below.

`operationId: postUserUserForwardsIndex`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Request body** (`application/json`) (required):
```json
{
    "enabled": true,
    "name": "Mobile",
    "event": "no_answer",
    "target": "phone_number",
    "target_options": {
        "phone_number": "+12125550100"
    },
    "caller_identity_id": "string?<uuid>"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440010",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "type": "unconditional",
          "destination": "+32499123456",
          "status": true
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### PUT /users/{user}/forwards/{forward}

Update a forward for a user

Replaces the user user forwards specific with the fields supplied in the request body.
Fields omitted from the body are reset to their defaults.

`operationId: updateUserUserForwardsSpecific`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user
- `forward` (path, string<uuid>, required) — UUID of the forward

**Request body** (`application/json`) (required):
```json
{
    "enabled": false,
    "name": "Mobile"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440010",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "enabled": "boolean",
          "event": "enum",
          "target": "enum",
          "target_options": [],
          "caller_identity_id": "string?<uuid>"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### DELETE /users/{user}/forwards/{forward}

Delete a forward for a user

Deletes the user user forwards specific. Soft-delete behaviour is model-dependent: most
resources are recoverable via the restore endpoint, while a few are hard-deleted.

`operationId: deleteUserUserForwardsSpecific`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user
- `forward` (path, string<uuid>, required) — UUID of the forward

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `204` — Deleted (no content)
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### POST /users/{user}/favorite-users

Add a favorite user (user)

Mark another user as a favorite for the given user. The favorited user must
belong to the same PBX as the owning user, and the same favorite cannot be
added twice. The owning user is taken from the `{user}` path segment and the
PBX is derived server-side; neither `user_id` nor `pbx_id` may be sent in the
body.

`operationId: createUserFavoriteUser`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — Identifier of the user who owns the favorites list

**Request body** (`application/json`) (required):
```json
{
    "favorite_user_id": "string<uuid>"
}
```

**Responses**:
- `201` — Created
  ```json
  {
      "id": "string<uuid>",
      "user_id": "string<uuid>",
      "favorite_user_id": "string<uuid>",
      "created_at": "string<date-time>",
      "updated_at": "string<date-time>"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### DELETE /users/{user}/favorite-users/{favorite_user}

Remove a favorite user (user)

Remove a user from the owning user's favorites list. The owning user is
taken from the `{user}` path segment, and the `{favorite_user}` path segment
is the identifier of the favorited user (not the favorite record id). If no
matching favorite exists for the pair, a `404` is returned.

`operationId: deleteUserFavoriteUser`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — Identifier of the user who owns the favorites list
- `favorite_user` (path, string<uuid>, required) — Identifier of the favorited user to remove

**Responses**:
- `204` — Deleted (no content)
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### GET /users/{user}/phone-buttons/resolved

Get resolved phone buttons for a user

Returns the user phone buttons resolved identified by the path parameter. Multi-tenant
scoping is enforced: the caller can only fetch resources within their own PBX.

`operationId: getUserPhoneButtonsResolved`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "position": 1,
              "type": "speed_dial",
              "value": "101",
              "label": "Front Desk"
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### GET /users/{user}/phone-buttons/resolved-detailed

Get resolved detailed phone buttons for a user

Returns the user phone buttons resolved detailed identified by the path parameter.
Multi-tenant scoping is enforced: the caller can only fetch resources within their own PBX.

`operationId: getUserPhoneButtonsResolvedDetailed`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "position": 1,
              "type": "speed_dial",
              "value": "101",
              "label": "Front Desk",
              "details": "object?"
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### GET /users/{user}/phone-buttons/preview-config

Get phone buttons preview config for a user

Returns the user phone buttons preview config identified by the path parameter. Multi-tenant
scoping is enforced: the caller can only fetch resources within their own PBX.

`operationId: getUserPhoneButtonsPreviewConfig`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "model": "Yealink T46S",
          "total_buttons": 27,
          "buttons": [
              {
                  "position": 1,
                  "type": "speed_dial",
                  "value": "101",
                  "label": "Front Desk"
              }
          ]
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### GET /users/{user}/phone-buttons

List phone buttons for a user

Returns the user phone buttons index identified by the path parameter. Multi-tenant scoping
is enforced: the caller can only fetch resources within their own PBX.

`operationId: getUserPhoneButtonsIndex`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440030",
              "user_id": "550e8400-e29b-41d4-a716-446655440001",
              "number": 1,
              "type": "speed_dial",
              "value": "101",
              "label": "Front Desk"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### POST /users/{user}/phone-buttons

Create a phone button for a user

Creates a new user phone buttons index. Validation rules and required fields are defined in
the request body schema below.

`operationId: postUserPhoneButtonsIndex`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Request body** (`application/json`) (required):
```json
{
    "number": 1,
    "label": "Front Desk",
    "type": "speed_dial_extension",
    "target_user_id": "string?<uuid>",
    "target_extension_id": "string?<uuid>",
    "caller_identity_id": "string?<uuid>",
    "phone_number": "string?",
    "multicast_id": "string?<uuid>",
    "status_id": "string?<uuid>",
    "call_flow_id": "string?<uuid>",
    "forward_type": "string?",
    "forward_caller_identity_id": "string?<uuid>",
    "function_action": "string?",
    "contact_phone_number_id": "string?<uuid>",
    "editable": "boolean",
    "sync_source": "enum"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440030",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "number": 1,
          "type": "speed_dial",
          "value": "101",
          "label": "Front Desk"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /users/{user}/phone-buttons/{phoneButtonId}

Get a phone button for a user

Returns the user phone buttons specific identified by the path parameter. Multi-tenant
scoping is enforced: the caller can only fetch resources within their own PBX.

`operationId: getUserPhoneButtonsSpecific`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user
- `phoneButtonId` (path, string<uuid>, required) — UUID of the phone button

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440030",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "number": 1,
          "type": "speed_dial",
          "value": "101",
          "label": "Front Desk"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### PUT /users/{user}/phone-buttons/{phoneButtonId}

Replace a phone button for a user

Replaces the user phone buttons specific with the fields supplied in the request body.
Fields omitted from the body are reset to their defaults.

`operationId: updateUserPhoneButtonsSpecific`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user
- `phoneButtonId` (path, string<uuid>, required) — UUID of the phone button

**Request body** (`application/json`) (required):
```json
{
    "number": 1,
    "label": "Support",
    "type": "speed_dial_extension",
    "target_user_id": "string?<uuid>",
    "target_extension_id": "string?<uuid>",
    "caller_identity_id": "string?<uuid>",
    "phone_number": "string?",
    "function_action": "string?"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440030",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "number": 1,
          "type": "speed_dial",
          "value": "102",
          "label": "Support"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### PATCH /users/{user}/phone-buttons/{phoneButtonId}

Partially update a phone button for a user

Partially updates the user phone buttons specific. Only the fields supplied in the request
body are changed; others are left untouched.

`operationId: patchUserPhoneButtonsSpecific`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user
- `phoneButtonId` (path, string<uuid>, required) — UUID of the phone button

**Request body** (`application/json`) (required):
```json
{
    "label": "Reception"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440030",
          "user_id": "550e8400-e29b-41d4-a716-446655440001",
          "number": 1,
          "type": "speed_dial",
          "value": "101",
          "label": "Reception"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### DELETE /users/{user}/phone-buttons/{phoneButtonId}

Delete a phone button for a user

Deletes the user phone buttons specific. Soft-delete behaviour is model-dependent: most
resources are recoverable via the restore endpoint, while a few are hard-deleted.

`operationId: deleteUserPhoneButtonsSpecific`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user
- `phoneButtonId` (path, string<uuid>, required) — UUID of the phone button

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `204` — Deleted (no content)
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### GET /users/{user}/user-settings

List settings for a user

Returns the user user settings index identified by the path parameter. Multi-tenant scoping
is enforced: the caller can only fetch resources within their own PBX.

`operationId: getUserUserSettingsIndex`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "type": "notifications",
              "settings": "string"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### POST /users/{user}/user-settings

Create a setting for a user

Creates a new user user settings index. Validation rules and required fields are defined in
the request body schema below.

`operationId: postUserUserSettingsIndex`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Request body** (`application/json`) (required):
```json
{
    "type": "notifications",
    "settings": "string"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "data": {
          "type": "notifications",
          "settings": "string"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /users/{user}/user-settings/{type}

Get a setting for a user by type

Returns the user user settings specific identified by the path parameter. Multi-tenant
scoping is enforced: the caller can only fetch resources within their own PBX.

`operationId: getUserUserSettingsSpecific`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user
- `type` (path, string, required) — The setting type key

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "type": "notifications",
          "settings": "string"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### PUT /users/{user}/user-settings/{type}

Replace a setting for a user by type

Replaces the user user settings specific with the fields supplied in the request body.
Fields omitted from the body are reset to their defaults.

`operationId: updateUserUserSettingsSpecific`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user
- `type` (path, string, required) — The setting type key

**Request body** (`application/json`) (required):
```json
{
    "settings": "string"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "type": "notifications",
          "settings": "string"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### PATCH /users/{user}/user-settings/{type}

Partially update a setting for a user by type

Partially updates the user user settings specific. Only the fields supplied in the request
body are changed; others are left untouched.

`operationId: patchUserUserSettingsSpecific`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user
- `type` (path, string, required) — The setting type key

**Request body** (`application/json`) (required):
```json
{
    "settings": "string"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "type": "notifications",
          "settings": "string"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### DELETE /users/{user}/user-settings/{type}

Delete a setting for a user by type

Deletes the user user settings specific. Soft-delete behaviour is model-dependent: most
resources are recoverable via the restore endpoint, while a few are hard-deleted.

`operationId: deleteUserUserSettingsSpecific`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user
- `type` (path, string, required) — The setting type key

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `204` — Deleted (no content)
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### GET /users/{user}/user-groups

List user-groups for a user

Returns the user user user groups index identified by the path parameter. Multi-tenant
scoping is enforced: the caller can only fetch resources within their own PBX.

`operationId: getUserUserUserGroupsIndex`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440020",
              "name": "Sales Team",
              "position": 1
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### POST /users/{user}/user-groups

Add a user-group to a user

Creates a new user user user groups index. Validation rules and required fields are defined
in the request body schema below.

`operationId: postUserUserUserGroupsIndex`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Request body** (`application/json`) (required):
```json
{
    "user_group_id": "550e8400-e29b-41d4-a716-446655440020",
    "position": 1
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440020",
          "name": "Sales Team",
          "position": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /users/{user}/user-groups/{userGroup}

Get a user-group for a user

Returns the user user user groups specific identified by the path parameter. Multi-tenant
scoping is enforced: the caller can only fetch resources within their own PBX.

`operationId: getUserUserUserGroupsSpecific`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user
- `userGroup` (path, string<uuid>, required) — UUID of the user-group

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440020",
          "name": "Sales Team",
          "position": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### PATCH /users/{user}/user-groups/{userGroup}

Partially update a user-group for a user

Partially updates the user user user groups specific. Only the fields supplied in the
request body are changed; others are left untouched.

`operationId: updateUserUserUserGroupsSpecific`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user
- `userGroup` (path, string<uuid>, required) — UUID of the user-group

**Request body** (`application/json`) (required):
```json
{
    "position": 2
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440020",
          "name": "Sales Team",
          "position": 2
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### DELETE /users/{user}/user-groups/{userGroup}

Remove a user-group from a user

Deletes the user user user groups specific. Soft-delete behaviour is model-dependent: most
resources are recoverable via the restore endpoint, while a few are hard-deleted.

`operationId: deleteUserUserUserGroupsSpecific`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user
- `userGroup` (path, string<uuid>, required) — UUID of the user-group

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `204` — Deleted (no content)
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### GET /users/{user}/right-to-disconnect

Get right-to-disconnect settings for a user

Returns the user right to disconnect identified by the path parameter. Multi-tenant scoping
is enforced: the caller can only fetch resources within their own PBX.

`operationId: getUserRightToDisconnect`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "enabled": true,
          "schedule": "object?"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.

#### PUT /users/{user}/right-to-disconnect

Update right-to-disconnect settings for a user

Replaces the user right to disconnect with the fields supplied in the request body. Fields
omitted from the body are reset to their defaults.

`operationId: updateUserRightToDisconnect`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `user` (path, string<uuid>, required) — UUID of the user

**Request body** (`application/json`) (required):
```json
{
    "enabled": true,
    "schedule": "object?"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": {
          "enabled": true,
          "schedule": "object?"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /change-status

Change presence status for the authenticated user (user)

Record a presence status change for the authenticated user. This opens a new
timepoint for the selected status and, when the status is mapped to the
external Tempus system, propagates the change and returns the updated
work-time totals.

`status_id` accepts either a Cockpit status UUID or an external Tempus mapping
identifier, which is resolved to the matching Cockpit status server-side. The
user and PBX are always derived from the authenticated token and must not be
sent in the request body.

`operationId: changeUserStatus`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "status_id": "550e8400-e29b-41d4-a716-446655440000",
    "location_id": "550e8400-e29b-41d4-a716-446655440010",
    "sub_status_ids": [
        "550e8400-e29b-41d4-a716-446655440020"
    ],
    "sources": [
        "mobile"
    ],
    "triggers": [
        "manual"
    ],
    "meta_data": {
        "lat": 49.6116,
        "lng": 6.1319
    },
    "gpsData": {
        "gpsLat": 49.6116,
        "gpsLong": 6.1319,
        "accuracy": 12.5,
        "gpsTimestamp": 1783418859289
    },
    "comment": "Starting morning shift"
}
```

**Responses**:
- `200` — Status change processed without opening a new time point: either the
submit collapsed onto the already-open time point (same status and same
`location_id` inside the same clock minute), or the status carries no
Tempus mapping id so nothing was synced downstream. In the latter case
`ui.data` is an empty array.
  ```json
  {
      "ui": {
          "data": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "user_id": "550e8400-e29b-41d4-a716-446655440002",
                  "status_id": "550e8400-e29b-41d4-a716-446655440003",
                  "location_id": "550e8400-e29b-41d4-a716-446655440010",
                  "location": {
                      "id": "550e8400-e29b-41d4-a716-446655440010",
                      "name": "HQ Luxembourg",
                      "description": "string?",
                      "street_no": "12",
                      "street": "Rue de Bonnevoie",
                      "city": "Luxembourg",
                      "postal": "1260",
                      "country_id": "550e8400-e29b-41d4-a716-446655440011",
                      "latitude": 49.6008,
                      "longitude": 6.133,
                      "geofence_radius_m": 150,
                      "is_fixed_location": true
                  },
                  "start_time": "2024-03-01T08:29:07Z",
                  "end_time": "2024-03-01T12:00:00Z",
                  "sources": {
                      "channel": "cockpit",
                      "device_id": "550e8400-e29b-41d4-a716-446655440010"
                  },
                  "triggers": {
                      "type": "manual_change",
                      "actor_id": "550e8400-e29b-41d4-a716-446655440002"
                  },
                  "meta_data": {
                      "note": "Started shift remotely",
                      "gpsLat": 49.95,
                      "gpsLong": 6.133
                  },
                  "device_gps": {
                      "latitude": 49.95,
                      "longitude": 6.133,
                      "accuracy_m": 12.5,
                      "captured_at": "2026-07-07T10:07:39+00:00",
                      "captured_at_raw": 1783418859289
                  },
                  "location_check": {
                      "location_id": "550e8400-e29b-41d4-a716-446655440010",
                      "location_latitude": 49.6008,
                      "location_longitude": 6.133,
                      "geofence_radius_m": 150,
                      "distance_m": 38957,
                      "within_geofence": false,
                      "verdict": "mismatch",
                      "reason": null
                  },
                  "is_synced": false,
                  "created_at": "2024-03-01T08:29:07Z",
                  "updated_at": "2024-03-01T08:29:07Z"
              },
              [
                  []
              ]
          ]
      },
      "workTimes": [],
      "message": "string?"
  }
  ```
- `201` — A new time point was opened. `ui.data` is the created time point,
including `device_gps` (the coordinates the client sent, stored as sent)
and `location_check` (how they compare with the selected location's
geofence).
  ```json
  {
      "ui": {
          "data": {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "user_id": "550e8400-e29b-41d4-a716-446655440002",
              "status_id": "550e8400-e29b-41d4-a716-446655440003",
              "location_id": "550e8400-e29b-41d4-a716-446655440010",
              "location": {
                  "id": "550e8400-e29b-41d4-a716-446655440010",
                  "name": "HQ Luxembourg",
                  "description": "string?",
                  "street_no": "12",
                  "street": "Rue de Bonnevoie",
                  "city": "Luxembourg",
                  "postal": "1260",
                  "country_id": "550e8400-e29b-41d4-a716-446655440011",
                  "latitude": 49.6008,
                  "longitude": 6.133,
                  "geofence_radius_m": 150,
                  "is_fixed_location": true
              },
              "start_time": "2024-03-01T08:29:07Z",
              "end_time": "2024-03-01T12:00:00Z",
              "sources": {
                  "channel": "cockpit",
                  "device_id": "550e8400-e29b-41d4-a716-446655440010"
              },
              "triggers": {
                  "type": "manual_change",
                  "actor_id": "550e8400-e29b-41d4-a716-446655440002"
              },
              "meta_data": {
                  "note": "Started shift remotely",
                  "gpsLat": 49.95,
                  "gpsLong": 6.133
              },
              "device_gps": {
                  "latitude": 49.95,
                  "longitude": 6.133,
                  "accuracy_m": 12.5,
                  "captured_at": "2026-07-07T10:07:39+00:00",
                  "captured_at_raw": 1783418859289
              },
              "location_check": {
                  "location_id": "550e8400-e29b-41d4-a716-446655440010",
                  "location_latitude": 49.6008,
                  "location_longitude": 6.133,
                  "geofence_radius_m": 150,
                  "distance_m": 38957,
                  "within_geofence": false,
                  "verdict": "mismatch",
                  "reason": null
              },
              "is_synced": false,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      },
      "workTimes": []
  }
  ```
- `409` — Another status change for this employee is already in progress and could
not be resolved. Retry once after a short delay.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /timepoints

List presence timepoints for the authenticated user (user)

Return a paginated list of presence timepoints (status changes recorded over
time) scoped to the authenticated user's PBX. Results are sorted by
`start_time` descending by default.

Filtering and sorting follow the Spatie query-builder convention: filters are
passed as `filter[<field>]` and sorts as a comma-separated `sort` value where
a leading `-` denotes descending order.

`operationId: listUserTimepoints`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `per_page` (query, integer, optional) — Number of items per page. Defaults to 50.
- `page` (query, integer, optional) — Page number to retrieve.
- `filter[user_id]` (query, string<uuid>, optional) — Filter by user identifier.
- `filter[status_id]` (query, string<uuid>, optional) — Filter by status identifier.
- `filter[start_time]` (query, string<date-time>, optional) — Filter by start time.
- `filter[end_time]` (query, string<date-time>, optional) — Filter by end time.
- `filter[is_synced]` (query, boolean, optional) — Filter by sync state with the external Tempus system.
- `sort` (query, string, optional) — Sort field. Allowed values: `start_time`, `end_time`, `created_at`.
Prefix with `-` for descending order. Defaults to `-start_time`.

**Responses**:
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "user_id": "550e8400-e29b-41d4-a716-446655440002",
              "status_id": "550e8400-e29b-41d4-a716-446655440003",
              "location_id": "550e8400-e29b-41d4-a716-446655440010",
              "location": {
                  "id": "550e8400-e29b-41d4-a716-446655440010",
                  "name": "HQ Luxembourg",
                  "description": "string?",
                  "street_no": "12",
                  "street": "Rue de Bonnevoie",
                  "city": "Luxembourg",
                  "postal": "1260",
                  "country_id": "550e8400-e29b-41d4-a716-446655440011",
                  "latitude": 49.6008,
                  "longitude": 6.133,
                  "geofence_radius_m": 150,
                  "is_fixed_location": true
              },
              "start_time": "2024-03-01T08:29:07Z",
              "end_time": "2024-03-01T12:00:00Z",
              "sources": {
                  "channel": "cockpit",
                  "device_id": "550e8400-e29b-41d4-a716-446655440010"
              },
              "triggers": {
                  "type": "manual_change",
                  "actor_id": "550e8400-e29b-41d4-a716-446655440002"
              },
              "meta_data": {
                  "note": "Started shift remotely",
                  "gpsLat": 49.95,
                  "gpsLong": 6.133
              },
              "device_gps": {
                  "latitude": 49.95,
                  "longitude": 6.133,
                  "accuracy_m": 12.5,
                  "captured_at": "2026-07-07T10:07:39+00:00",
                  "captured_at_raw": 1783418859289
              },
              "location_check": {
                  "location_id": "550e8400-e29b-41d4-a716-446655440010",
                  "location_latitude": 49.6008,
                  "location_longitude": 6.133,
                  "geofence_radius_m": 150,
                  "distance_m": 38957,
                  "within_geofence": false,
                  "verdict": "mismatch",
                  "reason": null
              },
              "is_synced": false,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### GET /workTimes

Get accumulated work times for the authenticated user (user)

Return the accumulated work-time totals for the authenticated user. When the
PBX's Tempus app is sourced from Cockpit the totals are calculated locally;
otherwise they are fetched from the external Tempus system. If no data is
available, zeroed totals are returned.

All durations are formatted as `HH:MM:SS` strings. `min*` values represent the
minimum (contractually required) duration for the corresponding period.

`operationId: getUserWorkTimes`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `200` — OK
  ```json
  {
      "workTimes": {
          "daily": "08:00:00",
          "minDaily": "08:00:00",
          "weekly": "40:00:00",
          "minWeekly": "40:00:00",
          "monthly": "160:00:00",
          "minMonthly": "160:00:00",
          "lunch": "01:00:00"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

### Webhooks

Inbound webhooks from third-party services (e.g. Famulor post-call events).

#### POST /webhooks/famulor/post-call

Receive Famulor post-call events

Receives post-call payload from Famulor AI voice assistant after a call completes. Optionally validates the request via the X-Webhook-Secret header.

`operationId: postWebhookFamulor`

**Parameters**:
- `Accept` (header, string, optional)
- `X-Webhook-Secret` (header, string, optional) — Optional webhook secret for request validation.

**Request body** (`application/json`):
```json
{
    "assistant_id": "string",
    "call_id": "string",
    "id": "string",
    "status": "string",
    "type": "string",
    "client_phone_number": "string",
    "assistant_phone_number": "string",
    "answered_by": "string?",
    "created_at": "string<date-time>",
    "duration": "integer",
    "total_cost": "number",
    "carrier_cost": "number",
    "transcript": "string?",
    "variables": "object?",
    "evaluation": "object?",
    "recording_url": "string?"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "success": "boolean"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

### CDR Utility

### CDR Utility Proxy

Forwards authenticated requests to the internal CDRs API. Rate-limited to 120 requests/minute per user.

Base URL: `/api/v1/cdrs-utility/`


#### GET /cdrs

List call history (CDRs) (user, pbx)

Returns the paginated call history (call detail records) for the
authenticated principal, newest first. Results are scoped server-side to the
caller's PBX (and, for a user token, to the caller's own extension), so the
company scope can never be supplied or overridden by the client.

With a PBX-owned API key the request spans the whole PBX by default. Pass
`extension_id` or `extension` to narrow it to the calls made or received by a
single user's extension. The value is validated against the authenticated PBX
before use, and the pair is always re-derived server-side, so a mismatched
`extension` / `extension_id` combination cannot widen the scope. With a user
personal access token both parameters are ignored: the query is always locked
to the caller's own extension.

Authentication accepts either a user personal access token or a PBX-owned
API key. A PBX API key must carry the `call-history` ability/scope.

Rate-limited to 120 requests per minute per principal (user, PBX or IP);
exceeding the limit returns `429 Too Many Requests`.

`operationId: listCdrs`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `extension_id` (query, integer<int64>, optional) — Internal ID of the extension whose calls should be returned (the
extension of the user who made or received the call). PBX API key only,
and validated to belong to the authenticated PBX; a foreign extension
returns `403`. Takes precedence over `extension` when both are supplied.
Omit to span every extension of the PBX. Ignored for user tokens.
- `extension` (query, string, optional) — Extension number of the user whose calls should be returned, as an
alternative to `extension_id`. PBX API key only, and validated to belong
to the authenticated PBX; a foreign extension returns `403`. Omit to span
every extension of the PBX. Ignored for user tokens.
- `extras` (query, boolean, optional) — When `true`, each record includes the detailed `extras` payload (AI transcription availability and call-timeline data). Defaults to `false` for a lighter, faster response.
- `call_type[]` (query, array, optional) — Filter by call type. Repeatable. One or more of the listed values.
- `start_time` (query, string, optional) — Start of the date/time window (inclusive), in `YYYY-MM-DD HH:MM:SS` format.
- `end_time` (query, string, optional) — End of the date/time window (inclusive), in `YYYY-MM-DD HH:MM:SS` format.
- `search` (query, string, optional) — Full-text search across the call records (names, numbers, and similar fields).
- `id` (query, integer<int64>, optional) — When supplied, only records with a `cdr_id` greater than this value are returned. Use it to fetch only records newer than the last one you saw, for cheaper incremental polling.
- `page` (query, integer, optional) — Page number (1-based).
- `page_size` (query, integer, optional) — Number of records per page.
- `remote_extension_number` (query, string, optional) — Filter to calls involving this remote party number or extension.

**Responses**:
- `200` — Paginated call history.
  ```json
  {
      "data": [
          {
              "cdr_id": "24904193",
              "pbx_user_id": "550e8400-e29b-41d4-a716-446655440010",
              "pbx_profile_id": "string?",
              "customer_pid": "string?",
              "type": "INCOMING",
              "is_missed_call": true,
              "is_answered": false,
              "remote_person_id": "550e8400-e29b-41d4-a716-446655440020",
              "remote_person_name": "Customer Support",
              "remote_person_number": "+12125550100",
              "via_id": "550e8400-e29b-41d4-a716-446655440030",
              "via_name": "Main Line",
              "via_number": "+12125550111",
              "date": "2026-01-12",
              "start_time": "2026-01-12 15:50:19",
              "answer_time": "string?",
              "end_time": "2026-01-12 15:50:25",
              "duration": 6,
              "billsec": 0,
              "is_transfer_call": false,
              "is_conference_call": false,
              "is_recorded": false,
              "notes_count": 0,
              "transcriptions_available": false,
              "extras": []
          }
      ],
      "pagination": {
          "current_page": 1,
          "first_page_url": "https://cockpit.voxbi.com/api/v1/cdrs-utility/cdrs?page=1",
          "prev_page_url": "string?",
          "next_page_url": "https://cockpit.voxbi.com/api/v1/cdrs-utility/cdrs?page=2",
          "last_page_url": "https://cockpit.voxbi.com/api/v1/cdrs-utility/cdrs?page=103",
          "last_page": 103,
          "per_page": 5,
          "total": 515,
          "path": "https://cockpit.voxbi.com/api/v1/cdrs-utility/cdrs"
      },
      "cache_hit": false,
      "status": true,
      "message": "CDRs retrieved successfully."
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Either the presented PBX API key does not carry the required
`call-history` ability/scope, or the requested `extension_id` /
`extension` does not belong to the authenticated PBX. A user token whose
account has no extension assigned is rejected here as well.
- `429` — Too many requests. The endpoint is rate-limited to 120 requests per minute per principal. Retry after the window indicated by the `Retry-After` response header.

#### GET /cdrs/detail/{id}

Get call detail by ID (user, pbx)

Returns the full detail of a single call, including each receiving leg, the
call-flow timeline, recording-file references, notes, and AI transcription
results. Scoped server-side to the caller's PBX.

Authentication accepts either a user personal access token or a PBX-owned
API key. A PBX API key must carry the `call-history` ability/scope.

Rate-limited to 120 requests per minute per principal.

`operationId: getCdrDetail`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, integer<int64>, required) — The `cdr_id` of the call, as returned by the call-history list.

**Responses**:
- `200` — The call detail.
  ```json
  {
      "data": {
          "id": 24904193,
          "company_id": "550e8400-e29b-41d4-a716-446655440000",
          "type": "OUTGOING",
          "date": "2026-01-12",
          "start_time": "2026-01-12 15:50:19",
          "answer_time": "string?",
          "end_time": "2026-01-12 15:50:25",
          "duration": 6,
          "billsec": 0,
          "is_transfer_call": false,
          "is_internal": true,
          "is_conference_call": false,
          "conference": [],
          "is_recording_file_exists": false,
          "recording_file": {
              "name": "",
              "path": "string?",
              "download_url": "string?",
              "hint": "This recording file is for the entire call, including both caller and callee recordings. You can only download it using your bearer token."
          },
          "recording_file_path_caller": {
              "name": "",
              "path": "string?",
              "download_url": "string?",
              "hint": "This recording file is for the entire call, including both caller and callee recordings. You can only download it using your bearer token."
          },
          "recording_file_path_callee": {
              "name": "",
              "path": "string?",
              "download_url": "string?",
              "hint": "This recording file is for the entire call, including both caller and callee recordings. You can only download it using your bearer token."
          },
          "caller": {
              "id": "550e8400-e29b-41d4-a716-446655440041",
              "name": "Customer Support",
              "number": "2041"
          },
          "receivers": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440040",
                  "status": "TRIGGERED",
                  "name": "Sales Team",
                  "number": "2039",
                  "start_time": "2026-01-12 15:50:19",
                  "answer_time": "2026-01-12 15:50:19",
                  "end_time": "2026-01-12 15:50:25",
                  "duration": 6,
                  "billsec": 6,
                  "via_id": "550e8400-e29b-41d4-a716-446655440030",
                  "via_name": "Main Line",
                  "via_number": "+12125550111",
                  "via_type": "CALL_FLOW"
              }
          ],
          "call_flow": [
              {
                  "color": "info",
                  "text": "OUTGOING - Call started by Customer Support (2041)",
                  "sub_text": "string?",
                  "time": "2026-01-12 15:50:19"
              }
          ],
          "notes": [],
          "transcriptions": "object?",
          "ai_processing_errors": []
      },
      "status": true,
      "message": "CDRs"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The presented PBX API key does not carry the required `call-history` ability/scope.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `429` — Too many requests. Rate-limited to 120 requests per minute per principal.

#### GET /call-recording

Download a call recording (user, pbx)

Streams the audio recording for a call. By default the recording for the
given call `id` is returned. To fetch a specific recording variant (caller
leg, callee leg, or the combined file), pass its `path` (obtained from the
`recording_file*` references on the call-detail or, in the list, when
`extras=true`).

Authentication accepts either a user personal access token or a PBX-owned
API key. A PBX API key must carry the `call-history` ability/scope.

Rate-limited to 120 requests per minute per principal.

`operationId: getCdrCallRecording`

**Auth**: bearerAuth

**Parameters**:
- `id` (query, integer<int64>, required) — The `cdr_id` of the call whose recording to download.
- `path` (query, string, optional) — Specific recording-file path to download (the `path` value from a
`recording_file`, `recording_file_path_caller`, or
`recording_file_path_callee` reference). Omit to download the default
recording for the call.

**Responses**:
- `200` — The recording audio stream.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The presented PBX API key does not carry the required `call-history` ability/scope.
- `404` — No recording exists for the given call or path.
- `429` — Too many requests. Rate-limited to 120 requests per minute per principal.

### Absences

Employee absence requests, absence records, and validator decisions on them.

#### GET /{pbx_id}/absences

List absences for the tenant (user)

Returns a paginated list of absence transactions for the PBX tenant. Each item is a `TempusTransaction` ledger row of type absence, illness, special, or legal holiday.

Only days actually taken are returned. Ledger rows that ADD days to a balance are excluded, because they are not time off: every credit row (`tr_type: credit`), including credits carrying no `sub_type` at all; the entitlement and carry-over sub-types (`computed_legal`, `computed_ph_on_day_off`, `computed_ph_on_same_day`, `computed_age`, `computed_experience`, `computed_company_day_off`, `computed_company_day_off_extra`, `legacy_custom`, `rollover`, `converted_balance`), which can be stored as debits and so are excluded by sub-type as well; `special_holiday` reversal rows; and `removal` give-backs. Granted days are reported by `GET /{pbx_id}/user/{employee_id}/calendar` instead. Passing one of the excluded values as `filter[sub_type]` therefore returns an empty list.

By default the result covers every employee in the caller's active-contract company (a team holiday overview). `filter[employee_id]` and `filter[department_id]` narrow the set within that company. Filtering, sorting, and includes are powered by Spatie Query Builder.

`operationId: tempusListAbsences`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `filter[status]` (query, string, optional) — Filter by transaction status (exact match).
- `filter[type]` (query, string, optional) — Filter by transaction type (exact match).
- `filter[sub_type]` (query, string, optional) — Filter by absence sub-type code (exact match). Must be a valid `TempusAbsenceType` code or the request is rejected with 422.
- `filter[from]` (query, string<date>, optional) — Return absences whose start date is on or after this date.
- `filter[to]` (query, string<date>, optional) — Return absences whose end date is on or before this date.
- `filter[employee_id]` (query, string, optional) — Comma-separated list of employee UUIDs to restrict results to. Requires permission to view other employees' absences.
- `filter[department_id]` (query, string, optional) — Comma-separated list of Tempus department UUIDs to restrict results to. Requires permission to view other employees' absences.
- `sort` (query, string, optional) — Sort field. Allowed: start_date, end_date, created_at. Prefix with - for descending.
- `include` (query, string, optional) — Comma-separated related resources to include. Allowed: employee, requester.
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page.

**Responses**:
- `200` — Paginated list of absence transactions.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "employee_id": "550e8400-e29b-41d4-a716-446655440002",
              "requester_id": "550e8400-e29b-41d4-a716-446655440002",
              "creator_id": "550e8400-e29b-41d4-a716-446655440002",
              "ext_id": "550e8400-e29b-41d4-a716-446655440009",
              "company_id": "550e8400-e29b-41d4-a716-446655440003",
              "absence_request_id": "550e8400-e29b-41d4-a716-446655440000",
              "holiday_id": null,
              "status": "accepted",
              "type": "legal",
              "sub_type": null,
              "start": "2026-08-03T00:00:00+00:00",
              "end": "2026-08-05T00:00:00+00:00",
              "requested_time": {
                  "2026-08-03": 14400,
                  "2026-08-04": 28800,
                  "2026-08-05": 28800
              },
              "working_days": 2.5,
              "total_seconds": 72000,
              "workshift_seconds_per_day": 28800,
              "is_half_day": true,
              "starts_half_day": true,
              "ends_half_day": false,
              "start_time": "12:00",
              "end_time": null,
              "working_day_details": [
                  {
                      "date": "2026-08-03",
                      "seconds": 14400,
                      "is_half_day": true,
                      "start_time": "12:00",
                      "end_time": null
                  },
                  {
                      "date": "2026-08-04",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  },
                  {
                      "date": "2026-08-05",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  }
              ],
              "comment": "Family event, returning Thursday.",
              "accepted_by": [
                  {
                      "employee_id": "550e8400-e29b-41d4-a716-446655440004",
                      "status": "accepted"
                  }
              ],
              "next_availability": {
                  "if_approved": {
                      "absent_from": "2026-08-03",
                      "next_available_date": "2026-08-06",
                      "next_available_at": null,
                      "available_now": true,
                      "in_past": false,
                      "extends_current_absence": false,
                      "message": "Off from 2026-08-03, available on 2026-08-06"
                  },
                  "current": {
                      "next_available_date": null,
                      "message": null
                  }
              },
              "created": "2026-07-28T10:00:00+00:00",
              "modified": "2026-07-29T09:30:00+00:00"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no associated employee record.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/user/{employee_id}/absences

List absences for a specific employee (user)

Returns a paginated list of absence transactions for a single employee. Each item is a `TempusTransaction` ledger row of type absence, illness, special, or legal holiday.

Only days actually taken are returned. Ledger rows that ADD days to a balance are excluded, because they are not time off: every credit row (`tr_type: credit`), including credits carrying no `sub_type` at all; the entitlement and carry-over sub-types (`computed_legal`, `computed_ph_on_day_off`, `computed_ph_on_same_day`, `computed_age`, `computed_experience`, `computed_company_day_off`, `computed_company_day_off_extra`, `legacy_custom`, `rollover`, `converted_balance`), which can be stored as debits and so are excluded by sub-type as well; `special_holiday` reversal rows; and `removal` give-backs. Granted days are reported by `GET /{pbx_id}/user/{employee_id}/calendar` instead. Passing one of the excluded values as `filter[sub_type]` therefore returns an empty list.

The caller may always view their own absences; viewing another employee's absences requires the appropriate validator scope, otherwise the request is rejected with 403. Filtering and sorting are powered by Spatie Query Builder.

`operationId: tempusListUserAbsences`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee whose absences are requested.
- `filter[status]` (query, string, optional) — Filter by transaction status (exact match).
- `filter[type]` (query, string, optional) — Filter by transaction type (exact match).
- `filter[sub_type]` (query, string, optional) — Filter by absence sub-type code (exact match). Must be a valid `TempusAbsenceType` code or the request is rejected with 422.
- `filter[from]` (query, string<date>, optional) — Return absences whose start date is on or after this date.
- `filter[to]` (query, string<date>, optional) — Return absences whose end date is on or before this date.
- `sort` (query, string, optional) — Sort field. Allowed: start_date, end_date, created_at. Prefix with - for descending.
- `include` (query, string, optional) — Comma-separated related resources to include. Allowed: employee, requester.
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page.

**Responses**:
- `200` — Paginated list of absence transactions for the employee.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "employee_id": "550e8400-e29b-41d4-a716-446655440002",
              "requester_id": "550e8400-e29b-41d4-a716-446655440002",
              "creator_id": "550e8400-e29b-41d4-a716-446655440002",
              "ext_id": "550e8400-e29b-41d4-a716-446655440009",
              "company_id": "550e8400-e29b-41d4-a716-446655440003",
              "absence_request_id": "550e8400-e29b-41d4-a716-446655440000",
              "holiday_id": null,
              "status": "accepted",
              "type": "legal",
              "sub_type": null,
              "start": "2026-08-03T00:00:00+00:00",
              "end": "2026-08-05T00:00:00+00:00",
              "requested_time": {
                  "2026-08-03": 14400,
                  "2026-08-04": 28800,
                  "2026-08-05": 28800
              },
              "working_days": 2.5,
              "total_seconds": 72000,
              "workshift_seconds_per_day": 28800,
              "is_half_day": true,
              "starts_half_day": true,
              "ends_half_day": false,
              "start_time": "12:00",
              "end_time": null,
              "working_day_details": [
                  {
                      "date": "2026-08-03",
                      "seconds": 14400,
                      "is_half_day": true,
                      "start_time": "12:00",
                      "end_time": null
                  },
                  {
                      "date": "2026-08-04",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  },
                  {
                      "date": "2026-08-05",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  }
              ],
              "comment": "Family event, returning Thursday.",
              "accepted_by": [
                  {
                      "employee_id": "550e8400-e29b-41d4-a716-446655440004",
                      "status": "accepted"
                  }
              ],
              "next_availability": {
                  "if_approved": {
                      "absent_from": "2026-08-03",
                      "next_available_date": "2026-08-06",
                      "next_available_at": null,
                      "available_now": true,
                      "in_past": false,
                      "extends_current_absence": false,
                      "message": "Off from 2026-08-03, available on 2026-08-06"
                  },
                  "current": {
                      "next_available_date": null,
                      "message": null
                  }
              },
              "created": "2026-07-28T10:00:00+00:00",
              "modified": "2026-07-29T09:30:00+00:00"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no associated employee record, or lacks permission to view the requested employee's absences.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/absence-requests

List pending absence requests for a PBX (user)

Returns a paginated list of pending absence requests (debit transactions of type absence, legal holiday, illness or special) for the PBX. Callers without a validator scope only see their own requests unless they filter by group or employee. The response is a standard Laravel paginated collection.

`operationId: tempusListAbsenceRequests`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `filter[from]` (query, string<date>, optional) — Lower bound for the absence period.
- `filter[to]` (query, string<date>, optional) — Upper bound for the absence period.
- `filter[status]` (query, string, optional) — Comma-separated list of statuses to filter by.
- `filter[department_id]` (query, string, optional) — Comma-separated list of Tempus department ids; matches employees whose active contract is in one of these departments.
- `filter[employee_id]` (query, string, optional) — Comma-separated list of employee IDs to filter by.
- `sort` (query, string, optional) — Sort field (start, end, start_date, end_date, created_at, status). Prefix with - for descending.
- `page` (query, integer, optional) — Page number.
- `per_page` (query, integer, optional) — Items per page (default 15).

**Responses**:
- `200` — Paginated list of absence requests.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "employee_id": "550e8400-e29b-41d4-a716-446655440002",
              "requester_id": "550e8400-e29b-41d4-a716-446655440002",
              "creator_id": "550e8400-e29b-41d4-a716-446655440002",
              "ext_id": "550e8400-e29b-41d4-a716-446655440009",
              "company_id": "550e8400-e29b-41d4-a716-446655440003",
              "absence_request_id": "550e8400-e29b-41d4-a716-446655440000",
              "holiday_id": null,
              "status": "accepted",
              "type": "legal",
              "sub_type": null,
              "start": "2026-08-03T00:00:00+00:00",
              "end": "2026-08-05T00:00:00+00:00",
              "requested_time": {
                  "2026-08-03": 14400,
                  "2026-08-04": 28800,
                  "2026-08-05": 28800
              },
              "working_days": 2.5,
              "total_seconds": 72000,
              "workshift_seconds_per_day": 28800,
              "is_half_day": true,
              "starts_half_day": true,
              "ends_half_day": false,
              "start_time": "12:00",
              "end_time": null,
              "working_day_details": [
                  {
                      "date": "2026-08-03",
                      "seconds": 14400,
                      "is_half_day": true,
                      "start_time": "12:00",
                      "end_time": null
                  },
                  {
                      "date": "2026-08-04",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  },
                  {
                      "date": "2026-08-05",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  }
              ],
              "comment": "Family event, returning Thursday.",
              "accepted_by": [
                  {
                      "employee_id": "550e8400-e29b-41d4-a716-446655440004",
                      "status": "accepted"
                  }
              ],
              "next_availability": {
                  "if_approved": {
                      "absent_from": "2026-08-03",
                      "next_available_date": "2026-08-06",
                      "next_available_at": null,
                      "available_now": true,
                      "in_past": false,
                      "extends_current_absence": false,
                      "message": "Off from 2026-08-03, available on 2026-08-06"
                  },
                  "current": {
                      "next_available_date": null,
                      "message": null
                  }
              },
              "created": "2026-07-28T10:00:00+00:00",
              "modified": "2026-07-29T09:30:00+00:00"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no linked employee record or lacks permission to view the requested absences.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/user/{employee_id}/absence-requests

List absence requests for a specific employee (user)

Returns a paginated list of absence requests (debit transactions of type absence, legal holiday, illness or special) for the given employee. Callers may only view their own requests unless they hold a validator scope over the employee. The response is a standard Laravel paginated collection.

`operationId: tempusListUserAbsenceRequests`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee.
- `filter[from]` (query, string<date>, optional) — Lower bound for the absence period.
- `filter[to]` (query, string<date>, optional) — Upper bound for the absence period.
- `filter[status]` (query, string, optional) — Comma-separated list of statuses to filter by.
- `sort` (query, string, optional) — Sort field (start, end, start_date, end_date, created_at, status). Prefix with - for descending.
- `page` (query, integer, optional) — Page number.
- `per_page` (query, integer, optional) — Items per page (default 15).

**Responses**:
- `200` — Paginated list of the employee's absence requests.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "employee_id": "550e8400-e29b-41d4-a716-446655440002",
              "requester_id": "550e8400-e29b-41d4-a716-446655440002",
              "creator_id": "550e8400-e29b-41d4-a716-446655440002",
              "ext_id": "550e8400-e29b-41d4-a716-446655440009",
              "company_id": "550e8400-e29b-41d4-a716-446655440003",
              "absence_request_id": "550e8400-e29b-41d4-a716-446655440000",
              "holiday_id": null,
              "status": "accepted",
              "type": "legal",
              "sub_type": null,
              "start": "2026-08-03T00:00:00+00:00",
              "end": "2026-08-05T00:00:00+00:00",
              "requested_time": {
                  "2026-08-03": 14400,
                  "2026-08-04": 28800,
                  "2026-08-05": 28800
              },
              "working_days": 2.5,
              "total_seconds": 72000,
              "workshift_seconds_per_day": 28800,
              "is_half_day": true,
              "starts_half_day": true,
              "ends_half_day": false,
              "start_time": "12:00",
              "end_time": null,
              "working_day_details": [
                  {
                      "date": "2026-08-03",
                      "seconds": 14400,
                      "is_half_day": true,
                      "start_time": "12:00",
                      "end_time": null
                  },
                  {
                      "date": "2026-08-04",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  },
                  {
                      "date": "2026-08-05",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  }
              ],
              "comment": "Family event, returning Thursday.",
              "accepted_by": [
                  {
                      "employee_id": "550e8400-e29b-41d4-a716-446655440004",
                      "status": "accepted"
                  }
              ],
              "next_availability": {
                  "if_approved": {
                      "absent_from": "2026-08-03",
                      "next_available_date": "2026-08-06",
                      "next_available_at": null,
                      "available_now": true,
                      "in_past": false,
                      "extends_current_absence": false,
                      "message": "Off from 2026-08-03, available on 2026-08-06"
                  },
                  "current": {
                      "next_available_date": null,
                      "message": null
                  }
              },
              "created": "2026-07-28T10:00:00+00:00",
              "modified": "2026-07-29T09:30:00+00:00"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no linked employee record or is not allowed to view this employee's absences.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/user/{employee_id}/absence-requests

Create an absence request for a specific employee (user)

Creates a new pending absence request for the employee. Requested time is expressed in seconds for the first and last day; weekends, public holidays, special holidays and dates already covered by other absences are skipped. If no working day remains after skipping, a 422 is returned.

`operationId: tempusCreateAbsenceRequest`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee.

**Request body** (`application/json`) (required):
```json
{
    "company_id": "550e8400-e29b-41d4-a716-446655440020",
    "start": "2026-03-02",
    "end": "2026-03-06",
    "requested_time_first_day": 28800,
    "requested_time_last_day": 28800,
    "comment": "Family trip",
    "type": "legal",
    "sub_type": "planned"
}
```

**Responses**:
- `201` — The created absence request.
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
          "employee_id": "550e8400-e29b-41d4-a716-446655440002",
          "requester_id": "550e8400-e29b-41d4-a716-446655440002",
          "creator_id": "550e8400-e29b-41d4-a716-446655440002",
          "ext_id": "550e8400-e29b-41d4-a716-446655440009",
          "company_id": "550e8400-e29b-41d4-a716-446655440003",
          "absence_request_id": "550e8400-e29b-41d4-a716-446655440000",
          "holiday_id": null,
          "status": "accepted",
          "type": "legal",
          "sub_type": null,
          "start": "2026-08-03T00:00:00+00:00",
          "end": "2026-08-05T00:00:00+00:00",
          "requested_time": {
              "2026-08-03": 14400,
              "2026-08-04": 28800,
              "2026-08-05": 28800
          },
          "working_days": 2.5,
          "total_seconds": 72000,
          "workshift_seconds_per_day": 28800,
          "is_half_day": true,
          "starts_half_day": true,
          "ends_half_day": false,
          "start_time": "12:00",
          "end_time": null,
          "working_day_details": [
              {
                  "date": "2026-08-03",
                  "seconds": 14400,
                  "is_half_day": true,
                  "start_time": "12:00",
                  "end_time": null
              },
              {
                  "date": "2026-08-04",
                  "seconds": 28800,
                  "is_half_day": false,
                  "start_time": null,
                  "end_time": null
              },
              {
                  "date": "2026-08-05",
                  "seconds": 28800,
                  "is_half_day": false,
                  "start_time": null,
                  "end_time": null
              }
          ],
          "comment": "Family event, returning Thursday.",
          "accepted_by": [
              {
                  "employee_id": "550e8400-e29b-41d4-a716-446655440004",
                  "status": "accepted"
              }
          ],
          "next_availability": {
              "if_approved": {
                  "absent_from": "2026-08-03",
                  "next_available_date": "2026-08-06",
                  "next_available_at": null,
                  "available_now": true,
                  "in_past": false,
                  "extends_current_absence": false,
                  "message": "Off from 2026-08-03, available on 2026-08-06"
              },
              "current": {
                  "next_available_date": null,
                  "message": null
              }
          },
          "created": "2026-07-28T10:00:00+00:00",
          "modified": "2026-07-29T09:30:00+00:00"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no linked employee record or is not allowed to create absences for this employee.
- `422` — Validation failed, or the requested period contains nothing bookable. The second case is returned when every date in the range is a weekend, a public holiday, a company special holiday, a day another absence already covers, or a day the employee's work shift schedules no hours for. It carries a bare `message` with no field-keyed `errors` object, so branch on whether `errors` is present.

#### PUT /{pbx_id}/user/{employee_id}/absence-requests/{absence_request_id}

Update a pending absence request for an employee (user)

Updates a pending absence request. Only pending requests can be modified; any other status returns a 422. Dates and requested times default to the existing values when omitted. Sending a cancelling status only updates the status. The requested time is recalculated against weekends, public holidays, special holidays and overlapping absences.

`operationId: tempusUpdateAbsenceRequest`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee.
- `absence_request_id` (path, string<uuid>, required) — UUID of the absence request transaction.

**Request body** (`application/json`):
```json
{
    "company_id": "550e8400-e29b-41d4-a716-446655440020",
    "start": "2026-03-02",
    "end": "2026-03-06",
    "requested_time_first_day": 28800,
    "requested_time_last_day": 28800,
    "comment": "Updated note",
    "type": "legal",
    "sub_type": "planned",
    "status": "cancelled"
}
```

**Responses**:
- `200` — The updated absence request.
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
          "employee_id": "550e8400-e29b-41d4-a716-446655440002",
          "requester_id": "550e8400-e29b-41d4-a716-446655440002",
          "creator_id": "550e8400-e29b-41d4-a716-446655440002",
          "ext_id": "550e8400-e29b-41d4-a716-446655440009",
          "company_id": "550e8400-e29b-41d4-a716-446655440003",
          "absence_request_id": "550e8400-e29b-41d4-a716-446655440000",
          "holiday_id": null,
          "status": "accepted",
          "type": "legal",
          "sub_type": null,
          "start": "2026-08-03T00:00:00+00:00",
          "end": "2026-08-05T00:00:00+00:00",
          "requested_time": {
              "2026-08-03": 14400,
              "2026-08-04": 28800,
              "2026-08-05": 28800
          },
          "working_days": 2.5,
          "total_seconds": 72000,
          "workshift_seconds_per_day": 28800,
          "is_half_day": true,
          "starts_half_day": true,
          "ends_half_day": false,
          "start_time": "12:00",
          "end_time": null,
          "working_day_details": [
              {
                  "date": "2026-08-03",
                  "seconds": 14400,
                  "is_half_day": true,
                  "start_time": "12:00",
                  "end_time": null
              },
              {
                  "date": "2026-08-04",
                  "seconds": 28800,
                  "is_half_day": false,
                  "start_time": null,
                  "end_time": null
              },
              {
                  "date": "2026-08-05",
                  "seconds": 28800,
                  "is_half_day": false,
                  "start_time": null,
                  "end_time": null
              }
          ],
          "comment": "Family event, returning Thursday.",
          "accepted_by": [
              {
                  "employee_id": "550e8400-e29b-41d4-a716-446655440004",
                  "status": "accepted"
              }
          ],
          "next_availability": {
              "if_approved": {
                  "absent_from": "2026-08-03",
                  "next_available_date": "2026-08-06",
                  "next_available_at": null,
                  "available_now": true,
                  "in_past": false,
                  "extends_current_absence": false,
                  "message": "Off from 2026-08-03, available on 2026-08-06"
              },
              "current": {
                  "next_available_date": null,
                  "message": null
              }
          },
          "created": "2026-07-28T10:00:00+00:00",
          "modified": "2026-07-29T09:30:00+00:00"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no linked employee record or is not allowed to update this request.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/user/{employee_id}/absence-requests/{absence_request_id}/cancel

Cancel an absence request, or take back its cancellation (user)

The single endpoint an employee uses to undo an absence, at whatever stage it
has reached. What it does depends on the current status of the request:

| Current status | What happens | Response `status` |
|---|---|---|
| `pending` | Cancelled outright. Pending approvals are deleted and no validator is asked. | `canceled` |
| `accepted` (internally `approved`) | Cannot be deleted directly. A **removal request** (a credit transaction, `sub_type: removal`) is created and the original moves to `pending_removal`; a validator must approve the removal before the days come back. | `pending` (of the new removal row) |
| `pending_removal` | The open removal is **revoked**: it is cancelled and the absence returns to `accepted`. The days stay booked, exactly as if a validator had rejected the removal. | `accepted` |
| anything else (`rejected`, `removed`, `canceled`) | Nothing. `400`. | n/a |

### Taking back a cancellation

The `pending_removal` row is what makes this endpoint reversible. An employee
who asked for an approved holiday to be given back - either through this
endpoint or through `POST /{pbx_id}/user/{employee_id}/absence-removal-requests` -
can change their mind for as long as no validator has ruled, and keep the days.

You may address that revocation with **either id**:

- the **original absence** id (status `pending_removal`) - the row the employee
  sees in their calendar, and the id a client normally holds; or
- the **removal request** id (status `pending`, `sub_type: removal`) - the
  credit row created when the removal was opened.

Both land on the same state and both answer with the **restored original
absence**, never with the withdrawn credit row. Revoking also deletes the
removal's pending approvals, so no validator is left holding a decision that no
longer matters.

Revocation is not one-way: once the absence is back to `accepted`, calling this
endpoint again simply opens a fresh removal request.

### Emails

A withdrawal the employee performs themselves is confirmed by email to the
employee the absence belongs to, and to whoever filed it when that is someone
else. Two different messages are sent:

- **request cancelled** - a `pending` request was withdrawn;
- **cancellation withdrawn** - a `pending_removal` was revoked and the days
  stay booked.

Opening a removal (the `accepted` branch) sends no such confirmation: nothing
has been withdrawn yet, and the validators are notified instead.

### Authorization

Owner-based: only the employee the request belongs to may cancel it, regardless
of who created it on their behalf. The request is resolved by its own id, so
the `{employee_id}` path segment is **not** used for authorization - a request
that genuinely does not exist returns `404`, one that exists but is not the
caller's returns `403`.

`operationId: tempusCancelAbsenceRequest`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee (path segment only; not used for authorization - the request is resolved by its own id and cancel is owner-based).
- `absence_request_id` (path, string<uuid>, required) — UUID of the absence request transaction. To revoke a cancellation this may be either the original absence (status `pending_removal`) or its pending removal request (`sub_type: removal`) - both are accepted.

**Responses**:
- `200` — The affected absence request, serialized by the V1 transaction resource
(unwrapped - the object is the response body, there is no `data` envelope).

Which row you get back depends on the branch taken: the cancelled request,
the newly created pending removal, or the restored original absence. Switch
on `status` and `sub_type` rather than assuming.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
      "employee_id": "550e8400-e29b-41d4-a716-446655440002",
      "requester_id": "550e8400-e29b-41d4-a716-446655440002",
      "creator_id": "550e8400-e29b-41d4-a716-446655440002",
      "ext_id": "550e8400-e29b-41d4-a716-446655440009",
      "company_id": "550e8400-e29b-41d4-a716-446655440003",
      "absence_request_id": "550e8400-e29b-41d4-a716-446655440000",
      "holiday_id": null,
      "status": "accepted",
      "type": "legal",
      "sub_type": null,
      "start": "2026-08-03T00:00:00+00:00",
      "end": "2026-08-05T00:00:00+00:00",
      "requested_time": {
          "2026-08-03": 14400,
          "2026-08-04": 28800,
          "2026-08-05": 28800
      },
      "working_days": 2.5,
      "total_seconds": 72000,
      "workshift_seconds_per_day": 28800,
      "is_half_day": true,
      "starts_half_day": true,
      "ends_half_day": false,
      "start_time": "12:00",
      "end_time": null,
      "working_day_details": [
          {
              "date": "2026-08-03",
              "seconds": 14400,
              "is_half_day": true,
              "start_time": "12:00",
              "end_time": null
          },
          {
              "date": "2026-08-04",
              "seconds": 28800,
              "is_half_day": false,
              "start_time": null,
              "end_time": null
          },
          {
              "date": "2026-08-05",
              "seconds": 28800,
              "is_half_day": false,
              "start_time": null,
              "end_time": null
          }
      ],
      "comment": "Family event, returning Thursday.",
      "accepted_by": [
          {
              "employee_id": "550e8400-e29b-41d4-a716-446655440004",
              "status": "accepted"
          }
      ],
      "next_availability": {
          "if_approved": {
              "absent_from": "2026-08-03",
              "next_available_date": "2026-08-06",
              "next_available_at": null,
              "available_now": true,
              "in_past": false,
              "extends_current_absence": false,
              "message": "Off from 2026-08-03, available on 2026-08-06"
          },
          "current": {
              "next_available_date": null,
              "message": null
          }
      },
      "created": "2026-07-28T10:00:00+00:00",
      "modified": "2026-07-29T09:30:00+00:00"
  }
  ```
- `400` — The request is in a status that cannot be cancelled - `rejected`, `removed` or already `canceled`. Only `pending`, `accepted` and `pending_removal` rows can be acted on.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The request exists but does not belong to the authenticated user (only its owner may cancel it).
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### POST /{pbx_id}/user/{employee_id}/absence-removal-requests

Request removal of an approved legal holiday absence (user)

Creates a pending removal request to credit back an approved legal holiday
absence. The referenced absence must be an approved legal holiday debit
transaction with no pending removal already in flight. The original absence
moves to a pending-removal status and the created credit transaction must be
validated.

**This is reversible.** For as long as no validator has ruled, the employee can
take the removal back and keep the days, via
`POST /{pbx_id}/user/{employee_id}/absence-requests/{absence_request_id}/cancel`.
That endpoint accepts either the original absence's id (now `pending_removal`)
or the `id` returned here, and restores the absence to `accepted`.

The same cancel endpoint is also the shorter way to *open* a removal: calling
it on an approved absence creates exactly the row this endpoint does. Use this
endpoint when you want to attach a `comment` to the removal.

`operationId: tempusCreateAbsenceRemovalRequest`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee.

**Request body** (`application/json`) (required):
```json
{
    "absence_id": "550e8400-e29b-41d4-a716-446655440030",
    "comment": "Cancelling planned leave"
}
```

**Responses**:
- `201` — The created pending removal request.
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
          "employee_id": "550e8400-e29b-41d4-a716-446655440002",
          "requester_id": "550e8400-e29b-41d4-a716-446655440002",
          "creator_id": "550e8400-e29b-41d4-a716-446655440002",
          "ext_id": "550e8400-e29b-41d4-a716-446655440009",
          "company_id": "550e8400-e29b-41d4-a716-446655440003",
          "absence_request_id": "550e8400-e29b-41d4-a716-446655440000",
          "holiday_id": null,
          "status": "accepted",
          "type": "legal",
          "sub_type": null,
          "start": "2026-08-03T00:00:00+00:00",
          "end": "2026-08-05T00:00:00+00:00",
          "requested_time": {
              "2026-08-03": 14400,
              "2026-08-04": 28800,
              "2026-08-05": 28800
          },
          "working_days": 2.5,
          "total_seconds": 72000,
          "workshift_seconds_per_day": 28800,
          "is_half_day": true,
          "starts_half_day": true,
          "ends_half_day": false,
          "start_time": "12:00",
          "end_time": null,
          "working_day_details": [
              {
                  "date": "2026-08-03",
                  "seconds": 14400,
                  "is_half_day": true,
                  "start_time": "12:00",
                  "end_time": null
              },
              {
                  "date": "2026-08-04",
                  "seconds": 28800,
                  "is_half_day": false,
                  "start_time": null,
                  "end_time": null
              },
              {
                  "date": "2026-08-05",
                  "seconds": 28800,
                  "is_half_day": false,
                  "start_time": null,
                  "end_time": null
              }
          ],
          "comment": "Family event, returning Thursday.",
          "accepted_by": [
              {
                  "employee_id": "550e8400-e29b-41d4-a716-446655440004",
                  "status": "accepted"
              }
          ],
          "next_availability": {
              "if_approved": {
                  "absent_from": "2026-08-03",
                  "next_available_date": "2026-08-06",
                  "next_available_at": null,
                  "available_now": true,
                  "in_past": false,
                  "extends_current_absence": false,
                  "message": "Off from 2026-08-03, available on 2026-08-06"
              },
              "current": {
                  "next_available_date": null,
                  "message": null
              }
          },
          "created": "2026-07-28T10:00:00+00:00",
          "modified": "2026-07-29T09:30:00+00:00"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no linked employee record or is not allowed to create removal requests for this employee.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/absence-requests/{absence_request_id}/validations

List validations for an absence request (user)

Returns the paginated list of validation (approval) records for a single
absence request. Each record is one validator's decision on the request:
pending, accepted, or rejected, with an optional comment. The authenticated
user must be allowed to view approvals for the request.

`operationId: tempusListAbsenceRequestValidations`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `absence_request_id` (path, string<uuid>, required) — UUID of the absence request (Tempus transaction).
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of records per page (maximum 100, defaults to 15).

**Responses**:
- `200` — Paginated list of absence request validations.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "absence_request_id": "550e8400-e29b-41d4-a716-446655440002",
              "ext_id": "201",
              "validator": {
                  "id": "550e8400-e29b-41d4-a716-446655440003",
                  "name": "Jane Smith"
              },
              "status": "pending",
              "comment": "Approved, please coordinate handover with the team.",
              "create_time": "2024-03-01T08:29:07+00:00",
              "modified": "2024-03-01T09:15:42+00:00",
              "holiday_id": "string?<uuid>"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is not allowed to view approvals for this absence request.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/absence-requests/{absence_request_id}/validations

Submit a validation decision for an absence request (user)

Records the authenticated validator's decision (accepted or rejected) on an
absence request. The user must have an employee record and be allowed to
create an approval for the request. If the validator already submitted a
final decision, the request is rejected with a conflict.

`operationId: tempusCreateAbsenceRequestValidation`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `absence_request_id` (path, string<uuid>, required) — UUID of the absence request (Tempus transaction).

**Request body** (`application/json`) (required):
```json
{
    "status": "accepted",
    "comment": "Approved, please coordinate handover with the team."
}
```

**Responses**:
- `201` — The validation decision was recorded.
  ```json
  {
      "message": "Absence request validation created."
  }
  ```
- `400` — The absence request is no longer in a pending state and cannot be validated.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no employee record or is not allowed to create
an approval for this absence request.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `409` — A final validation decision has already been submitted by this validator.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

### Home office

Home office requests, home office calendars, and validator decisions on them.

#### GET /{pbx_id}/home-office-requests

List home office requests (tenant-wide, validator-scoped)

Return a paginated list of home office requests (Tempus transactions of type
`home_office`) across the PBX. The acting user must have a linked Tempus
employee record. When the user is not a validator with home office scope and
no `filter[department_id]` or `filter[employee_id]` is supplied, results are
limited to the acting user's own requests. Validators may filter by
`employee_id` or `department_id` within their scope.

For a single employee's requests use
`GET /{pbx_id}/user/{employee_id}/home-office-requests` instead.

Each item is serialized by the V1 transaction resource. The list is wrapped in
the standard Laravel pagination envelope (`data`, `links`, `meta`).

`operationId: tempusListHomeOfficeRequests`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — Identifier of the PBX tenant
- `filter[from]` (query, string<date>, optional) — Start of the date window the request period must overlap.
- `filter[to]` (query, string<date>, optional) — End of the date window the request period must overlap.
- `filter[status]` (query, string, optional) — Comma-separated list of statuses to filter by. Accepts API aliases:
`accepted` maps to the internal `approved`, `canceled` maps to the
internal `cancelled`.
- `filter[department_id]` (query, string, optional) — Comma-separated list of Tempus department ids; matches employees whose active contract is in one of these departments.
- `filter[employee_id]` (query, string, optional) — Comma-separated list of employee identifiers to filter by.
- `sort` (query, string, optional) — Sort field. Supports `start`, `end`, `start_date`, `end_date`,
`created_at`, `status`. Prefix with `-` for descending order.
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page.

**Responses**:
- `200` — Paginated list of home office requests
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "employee_id": "550e8400-e29b-41d4-a716-446655440002",
              "requester_id": "550e8400-e29b-41d4-a716-446655440002",
              "creator_id": "550e8400-e29b-41d4-a716-446655440002",
              "ext_id": "550e8400-e29b-41d4-a716-446655440009",
              "company_id": "550e8400-e29b-41d4-a716-446655440003",
              "absence_request_id": "550e8400-e29b-41d4-a716-446655440000",
              "holiday_id": null,
              "status": "accepted",
              "type": "legal",
              "sub_type": null,
              "start": "2026-08-03T00:00:00+00:00",
              "end": "2026-08-05T00:00:00+00:00",
              "requested_time": {
                  "2026-08-03": 14400,
                  "2026-08-04": 28800,
                  "2026-08-05": 28800
              },
              "working_days": 2.5,
              "total_seconds": 72000,
              "workshift_seconds_per_day": 28800,
              "is_half_day": true,
              "starts_half_day": true,
              "ends_half_day": false,
              "start_time": "12:00",
              "end_time": null,
              "working_day_details": [
                  {
                      "date": "2026-08-03",
                      "seconds": 14400,
                      "is_half_day": true,
                      "start_time": "12:00",
                      "end_time": null
                  },
                  {
                      "date": "2026-08-04",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  },
                  {
                      "date": "2026-08-05",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  }
              ],
              "comment": "Family event, returning Thursday.",
              "accepted_by": [
                  {
                      "employee_id": "550e8400-e29b-41d4-a716-446655440004",
                      "status": "accepted"
                  }
              ],
              "next_availability": {
                  "if_approved": {
                      "absent_from": "2026-08-03",
                      "next_available_date": "2026-08-06",
                      "next_available_at": null,
                      "available_now": true,
                      "in_past": false,
                      "extends_current_absence": false,
                      "message": "Off from 2026-08-03, available on 2026-08-06"
                  },
                  "current": {
                      "next_available_date": null,
                      "message": null
                  }
              },
              "created": "2026-07-28T10:00:00+00:00",
              "modified": "2026-07-29T09:30:00+00:00"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The acting user has no linked Tempus employee record, or the policy
`viewAny` check denied access.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/user/{employee_id}/home-office-requests

List a specific employee's home office requests

Return a paginated list of home office requests (Tempus transactions of type
`home_office`) for the employee identified by the `employee_id` path
parameter. The caller may view their own requests, or - when they are a
validator - another employee's; otherwise `403` is returned. Results are
always scoped to the path employee (there is no `ext_id` filter here; use
`GET /{pbx_id}/home-office-requests` for the tenant-wide, filterable list).

Each item is serialized by the V1 transaction resource. The list is wrapped in
the standard Laravel pagination envelope (`data`, `links`, `meta`).

`operationId: tempusListUserHomeOfficeRequests`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — Identifier of the PBX tenant
- `employee_id` (path, string<uuid>, required) — Identifier of the employee whose home office requests are listed
- `filter[from]` (query, string<date>, optional) — Start of the date window the request period must overlap.
- `filter[to]` (query, string<date>, optional) — End of the date window the request period must overlap.
- `filter[status]` (query, string, optional) — Comma-separated list of statuses to filter by. Accepts API aliases:
`accepted` maps to the internal `approved`, `canceled` maps to the
internal `cancelled`.
- `sort` (query, string, optional) — Sort field. Supports `start`, `end`, `start_date`, `end_date`,
`created_at`, `status`. Prefix with `-` for descending order.
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page.

**Responses**:
- `200` — Paginated list of home office requests
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "employee_id": "550e8400-e29b-41d4-a716-446655440002",
              "requester_id": "550e8400-e29b-41d4-a716-446655440002",
              "creator_id": "550e8400-e29b-41d4-a716-446655440002",
              "ext_id": "550e8400-e29b-41d4-a716-446655440009",
              "company_id": "550e8400-e29b-41d4-a716-446655440003",
              "absence_request_id": "550e8400-e29b-41d4-a716-446655440000",
              "holiday_id": null,
              "status": "accepted",
              "type": "legal",
              "sub_type": null,
              "start": "2026-08-03T00:00:00+00:00",
              "end": "2026-08-05T00:00:00+00:00",
              "requested_time": {
                  "2026-08-03": 14400,
                  "2026-08-04": 28800,
                  "2026-08-05": 28800
              },
              "working_days": 2.5,
              "total_seconds": 72000,
              "workshift_seconds_per_day": 28800,
              "is_half_day": true,
              "starts_half_day": true,
              "ends_half_day": false,
              "start_time": "12:00",
              "end_time": null,
              "working_day_details": [
                  {
                      "date": "2026-08-03",
                      "seconds": 14400,
                      "is_half_day": true,
                      "start_time": "12:00",
                      "end_time": null
                  },
                  {
                      "date": "2026-08-04",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  },
                  {
                      "date": "2026-08-05",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  }
              ],
              "comment": "Family event, returning Thursday.",
              "accepted_by": [
                  {
                      "employee_id": "550e8400-e29b-41d4-a716-446655440004",
                      "status": "accepted"
                  }
              ],
              "next_availability": {
                  "if_approved": {
                      "absent_from": "2026-08-03",
                      "next_available_date": "2026-08-06",
                      "next_available_at": null,
                      "available_now": true,
                      "in_past": false,
                      "extends_current_absence": false,
                      "message": "Off from 2026-08-03, available on 2026-08-06"
                  },
                  "current": {
                      "next_available_date": null,
                      "message": null
                  }
              },
              "created": "2026-07-28T10:00:00+00:00",
              "modified": "2026-07-29T09:30:00+00:00"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The acting user has no linked Tempus employee record, or is neither the
target employee nor a validator, so may not view this employee's requests.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/user/{employee_id}/home-office-requests

Request home office days for an employee (user)

Request one or more home office days for the given employee. Each date in
`dates` becomes its own pending Tempus transaction of type `home_office` (a
per-day ledger row); all rows created by one call share a `request_group_id`.
Each day debits a full working day (28800 seconds). Approval records are
generated automatically.

Tenant (`pbx_id`), owning employee, requester, type, status and amounts are
set server-side and must not be sent in the body. Dates that are public
holidays, already booked as home office, or overlapping an absence are
rejected with `422` and an `invalid_dates` payload.

`operationId: tempusCreateHomeOfficeRequest`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — Identifier of the PBX tenant
- `employee_id` (path, string<uuid>, required) — Identifier of the employee the request is created for

**Request body** (`application/json`) (required):
```json
{
    "dates": [
        "2024-03-04"
    ],
    "comment": "Working from home."
}
```

**Responses**:
- `201` — The created home office day-rows, one per requested date. Wrapped in a `data` array.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "employee_id": "550e8400-e29b-41d4-a716-446655440002",
              "requester_id": "550e8400-e29b-41d4-a716-446655440002",
              "creator_id": "550e8400-e29b-41d4-a716-446655440002",
              "ext_id": "550e8400-e29b-41d4-a716-446655440009",
              "company_id": "550e8400-e29b-41d4-a716-446655440003",
              "absence_request_id": "550e8400-e29b-41d4-a716-446655440000",
              "holiday_id": null,
              "status": "accepted",
              "type": "legal",
              "sub_type": null,
              "start": "2026-08-03T00:00:00+00:00",
              "end": "2026-08-05T00:00:00+00:00",
              "requested_time": {
                  "2026-08-03": 14400,
                  "2026-08-04": 28800,
                  "2026-08-05": 28800
              },
              "working_days": 2.5,
              "total_seconds": 72000,
              "workshift_seconds_per_day": 28800,
              "is_half_day": true,
              "starts_half_day": true,
              "ends_half_day": false,
              "start_time": "12:00",
              "end_time": null,
              "working_day_details": [
                  {
                      "date": "2026-08-03",
                      "seconds": 14400,
                      "is_half_day": true,
                      "start_time": "12:00",
                      "end_time": null
                  },
                  {
                      "date": "2026-08-04",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  },
                  {
                      "date": "2026-08-05",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  }
              ],
              "comment": "Family event, returning Thursday.",
              "accepted_by": [
                  {
                      "employee_id": "550e8400-e29b-41d4-a716-446655440004",
                      "status": "accepted"
                  }
              ],
              "next_availability": {
                  "if_approved": {
                      "absent_from": "2026-08-03",
                      "next_available_date": "2026-08-06",
                      "next_available_at": null,
                      "available_now": true,
                      "in_past": false,
                      "extends_current_absence": false,
                      "message": "Off from 2026-08-03, available on 2026-08-06"
                  },
                  "current": {
                      "next_available_date": null,
                      "message": null
                  }
              },
              "created": "2026-07-28T10:00:00+00:00",
              "modified": "2026-07-29T09:30:00+00:00"
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The acting user has no linked Tempus employee record, or the policy
`create` check denied access.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/user/{employee_id}/home-office-requests/cancel

Withdraw home office days, or take back their cancellation (user)

Withdraw one or more home office day-rows (Tempus transactions of type
`home_office`) by id. Home office is stored one row per day, so this endpoint
is a bulk operation: it is best-effort and decides per row from that row's
current status.

| Current status | What happens | Reported under |
|---|---|---|
| `pending` | Cancelled directly; its pending approvals are deleted. | `cancelled` |
| `accepted` (internally `approved`) | A **removal request** (credit row, `sub_type: removal`) is created and the day moves to `pending_removal`; a validator must approve it. | `cancelled` (the new removal row) |
| `pending_removal` | The open removal is **revoked** and the day returns to `accepted` - the employee taking back their own cancellation. | `cancelled` (the restored day) |
| `removed`, `canceled`, `rejected`, or an id that is not one of this employee's home office days | Nothing. | `skipped` |

The call always returns `200`: valid days are acted on even when others are
skipped, and every id you sent is accounted for in exactly one of the two
lists.

### Taking back a cancellation

A day whose removal is still waiting for validation can be given back to the
employee for as long as no validator has ruled. Pass **either id**:

- the **original day** (status `pending_removal`), or
- its **removal row** (status `pending`, `sub_type: removal`).

Both revoke the removal and both report the **restored original day** in
`cancelled` - so an entry in `cancelled` is not always the row whose id you
sent. Match on the returned objects, not positionally against your input.

Sending both ids of the same pair in one call is safe: the pair is revoked
once, the original appears in `cancelled`, and the removal row - already
cancelled by then - is reported in `skipped` as `already_cancelled`.

### Emails

Withdrawing a `pending` day, and revoking a `pending_removal`, each confirm by
email to the employee (and to whoever filed the day, when that is someone
else). Opening a removal on an `accepted` day sends no confirmation - nothing
has been withdrawn yet, and the validators are notified instead.

`operationId: tempusCancelHomeOfficeRequests`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — Identifier of the PBX tenant
- `employee_id` (path, string<uuid>, required) — Identifier of the employee who owns the requests

**Request body** (`application/json`) (required):
```json
{
    "ids": [
        "550e8400-e29b-41d4-a716-446655440030"
    ]
}
```

**Responses**:
- `200` — The outcome of the call. `cancelled` holds the affected day-rows in their
new state - cancelled outright, moved to `pending_removal`, or restored to
`accepted` after a revocation. `skipped` lists the ids that were not acted
on, each with a machine `reason` and a human `message`.
  ```json
  {
      "cancelled": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "employee_id": "550e8400-e29b-41d4-a716-446655440002",
              "requester_id": "550e8400-e29b-41d4-a716-446655440002",
              "creator_id": "550e8400-e29b-41d4-a716-446655440002",
              "ext_id": "550e8400-e29b-41d4-a716-446655440009",
              "company_id": "550e8400-e29b-41d4-a716-446655440003",
              "absence_request_id": "550e8400-e29b-41d4-a716-446655440000",
              "holiday_id": null,
              "status": "accepted",
              "type": "legal",
              "sub_type": null,
              "start": "2026-08-03T00:00:00+00:00",
              "end": "2026-08-05T00:00:00+00:00",
              "requested_time": {
                  "2026-08-03": 14400,
                  "2026-08-04": 28800,
                  "2026-08-05": 28800
              },
              "working_days": 2.5,
              "total_seconds": 72000,
              "workshift_seconds_per_day": 28800,
              "is_half_day": true,
              "starts_half_day": true,
              "ends_half_day": false,
              "start_time": "12:00",
              "end_time": null,
              "working_day_details": [
                  {
                      "date": "2026-08-03",
                      "seconds": 14400,
                      "is_half_day": true,
                      "start_time": "12:00",
                      "end_time": null
                  },
                  {
                      "date": "2026-08-04",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  },
                  {
                      "date": "2026-08-05",
                      "seconds": 28800,
                      "is_half_day": false,
                      "start_time": null,
                      "end_time": null
                  }
              ],
              "comment": "Family event, returning Thursday.",
              "accepted_by": [
                  {
                      "employee_id": "550e8400-e29b-41d4-a716-446655440004",
                      "status": "accepted"
                  }
              ],
              "next_availability": {
                  "if_approved": {
                      "absent_from": "2026-08-03",
                      "next_available_date": "2026-08-06",
                      "next_available_at": null,
                      "available_now": true,
                      "in_past": false,
                      "extends_current_absence": false,
                      "message": "Off from 2026-08-03, available on 2026-08-06"
                  },
                  "current": {
                      "next_available_date": null,
                      "message": null
                  }
              },
              "created": "2026-07-28T10:00:00+00:00",
              "modified": "2026-07-29T09:30:00+00:00"
          }
      ],
      "skipped": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440050",
              "status": "removed",
              "reason": "already_removed",
              "message": "This date has already been removed."
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The policy `create` check denied access for this employee.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/home-office-requests/{home_office_request_id}/validations

List validations for a home office request (user)

Returns the paginated list of validation (approval) records for a single
home office request. The home office request is resolved from the underlying
Tempus transaction, scoped to the authenticated tenant. Each record
represents one validator's decision on the request.

`operationId: tempusListHomeOfficeRequestValidations`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant
- `home_office_request_id` (path, string<uuid>, required) — UUID of the home office request (Tempus transaction)
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of records per page (defaults to 15).

**Responses**:
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "absence_request_id": "550e8400-e29b-41d4-a716-446655440002",
              "ext_id": "201",
              "validator": {
                  "id": "550e8400-e29b-41d4-a716-446655440003",
                  "name": "Jane Smith"
              },
              "status": "pending",
              "comment": "Approved, please coordinate handover with the team.",
              "create_time": "2024-03-01T08:29:07+00:00",
              "modified": "2024-03-01T09:15:42+00:00",
              "holiday_id": "string?<uuid>"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Forbidden: the authenticated user is not allowed to view validations for this home office request.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/home-office-requests/{home_office_request_id}/validations

Submit a validation decision for a home office request (user)

Records the authenticated validator's decision (accepted or rejected) on a
home office request and processes the approval against the underlying Tempus
transaction. The authenticated user must have an associated employee record.
A validator may only submit one decision per request.

`operationId: tempusCreateHomeOfficeRequestValidation`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant
- `home_office_request_id` (path, string<uuid>, required) — UUID of the home office request (Tempus transaction)

**Request body** (`application/json`) (required):
```json
{
    "status": "accepted",
    "comment": "Approved as requested."
}
```

**Responses**:
- `201` — Created
  ```json
  {
      "message": "string"
  }
  ```
- `400` — Bad Request: the home office request is not in pending status and cannot be validated.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Forbidden: the authenticated user is not allowed to validate this request, or has no associated employee record.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `409` — Conflict: the authenticated validator has already submitted a decision for this request.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/home-office-requests/validations

Validate several home office day-rows at once

Apply a single decision (`accepted` or `rejected`) to several home office
day-rows in one call, for example a whole request group or week. The acting
user must be a validator with home office scope over the affected employees.

Rows that are not `pending`, or that the acting validator has already decided,
are skipped rather than failing the batch. The response reports how many rows
were processed and skipped.

`operationId: tempusBulkValidateHomeOfficeRequests`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — Identifier of the PBX tenant

**Request body** (`application/json`) (required):
```json
{
    "ids": [
        "550e8400-e29b-41d4-a716-446655440030"
    ],
    "status": "accepted",
    "comment": "Approved for the whole week."
}
```

**Responses**:
- `200` — The decision was applied. Reports processed and skipped counts.
  ```json
  {
      "message": "Validation completed.",
      "processed": 2,
      "skipped": 0
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The acting user is not a validator for one of the listed rows.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/home-office-calendars

List home office calendars across employees (user)

Returns virtual home office calendar entries aggregated from `home_office`
transactions across the tenant. Each entry groups the pending, approved, and
removed home-office days for one employee, company, and calendar year.

When the caller has permission to view other employees' home office
(resolved from the validator scope), entries for the caller's company are
returned; without a group filter the result is scoped to the caller's
company. Otherwise only the caller's own entries are returned. The response
is a plain list under `data` and is not paginated, even though `page` and
`per_page` are accepted as filters.

`operationId: tempusListHomeOfficeCalendars`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `filter[year]` (query, integer, optional) — Restrict the calendars to a single calendar year.
- `filter[group_id]` (query, string<uuid>, optional) — Restrict the calendars to employees in a specific department group.
- `include` (query, enum, optional) — When set to `user`, embeds the linked Voxbi user summary on each entry. · enum: user
- `page` (query, integer, optional) — Accepted for compatibility; the response is not paginated.
- `per_page` (query, integer, optional) — Accepted for compatibility; the response is not paginated.

**Responses**:
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "ext_id": "201",
              "company_id": "550e8400-e29b-41d4-a716-446655440002",
              "creator_id": "550e8400-e29b-41d4-a716-446655440003",
              "validator_id": "550e8400-e29b-41d4-a716-446655440004",
              "year": 2026,
              "days_of_week": "mon,tue,wed,thu,fri",
              "requested_days": [
                  "2026-06-15"
              ],
              "approved_days": [
                  "2026-06-16"
              ],
              "removed_days": [
                  "2026-06-17"
              ],
              "comment": "Working from home on project deadline week",
              "created": "2026-06-01T08:29:07+00:00",
              "modified": "2026-06-10T14:12:33+00:00",
              "count_requested_days": 1,
              "count_approved_days": 1,
              "count_removed_days": 1,
              "user": {
                  "id": "550e8400-e29b-41d4-a716-446655440005",
                  "number": "201",
                  "name": "Jane",
                  "name2": "Smith"
              }
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Forbidden. The authenticated user does not belong to the requested PBX
tenant.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/user/{employee_id}/home-office-calendars

List home office calendars for one employee (user)

Returns the virtual home office calendar entries for a single employee,
aggregated from that employee's `home_office` transactions and grouped by
company and calendar year. The response is a plain list under `data` and is
not paginated, even though `page` and `per_page` are accepted as filters.

`operationId: tempusListUserHomeOfficeCalendars`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the Tempus employee whose calendars are returned.
- `filter[year]` (query, integer, optional) — Restrict the calendars to a single calendar year.
- `page` (query, integer, optional) — Accepted for compatibility; the response is not paginated.
- `per_page` (query, integer, optional) — Accepted for compatibility; the response is not paginated.

**Responses**:
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "ext_id": "201",
              "company_id": "550e8400-e29b-41d4-a716-446655440002",
              "creator_id": "550e8400-e29b-41d4-a716-446655440003",
              "validator_id": "550e8400-e29b-41d4-a716-446655440004",
              "year": 2026,
              "days_of_week": "mon,tue,wed,thu,fri",
              "requested_days": [
                  "2026-06-15"
              ],
              "approved_days": [
                  "2026-06-16"
              ],
              "removed_days": [
                  "2026-06-17"
              ],
              "comment": "Working from home on project deadline week",
              "created": "2026-06-01T08:29:07+00:00",
              "modified": "2026-06-10T14:12:33+00:00",
              "count_requested_days": 1,
              "count_approved_days": 1,
              "count_removed_days": 1,
              "user": {
                  "id": "550e8400-e29b-41d4-a716-446655440005",
                  "number": "201",
                  "name": "Jane",
                  "name2": "Smith"
              }
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

### Corrections

Time point correction requests and validator decisions on them.

#### GET /{pbx_id}/corrections

List timepoint corrections for the tenant (user)

Returns a paginated list of Tempus time point corrections for the given
Pbx (tenant). Results can be filtered by correction status and by the
groups the requesting employee belongs to, sorted by creation time, and
expanded with optional related data through the `include` parameter.

Each item is serialized by the `CorrectionListResource`. The response uses
the standard Laravel pagination envelope: a `data` array of corrections plus
`links` and `meta` objects.

`operationId: tempusListCorrections`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — Identifier of the Pbx (tenant).
- `filter[status]` (query, array, optional) — Restrict results to corrections in the given lifecycle states. Accepts a
repeated array parameter or a single comma-separated string.
- `filter[group_id]` (query, array, optional) — Restrict results to corrections whose requesting employee belongs to one
of the given group identifiers. Accepts a repeated array parameter or a
single comma-separated string of UUIDs.
- `sort` (query, enum, optional) — Sort order by creation time. `create_time` is ascending, `-create_time`
is descending. Defaults to `-create_time`. · enum: create_time, -create_time
- `include` (query, string, optional) — Comma-separated list of optional expansions to include on each
correction. Allowed values are `requesterExt`, `newStatus`, and
`oldStatus`. Unknown values are ignored.
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page (1-100). Defaults to 15.

**Responses**:
- `200` — Paginated list of corrections.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "timepoint_id": "550e8400-e29b-41d4-a716-446655440002",
              "requester_ext_id": "550e8400-e29b-41d4-a716-446655440003",
              "new_status_id": "550e8400-e29b-41d4-a716-446655440004",
              "new_sub_status_id": "550e8400-e29b-41d4-a716-446655440005",
              "old_status_id": "550e8400-e29b-41d4-a716-446655440006",
              "old_sub_status_id": "550e8400-e29b-41d4-a716-446655440007",
              "new_location_id": "550e8400-e29b-41d4-a716-446655440010",
              "old_location_id": "550e8400-e29b-41d4-a716-446655440011",
              "new_time": "2024-03-01T08:29:07Z",
              "old_time": "2024-03-01T08:00:00Z",
              "correction_comment": "Forgot to clock in after lunch break.",
              "new_comment": "Corrected start time per manager approval.",
              "status": "pending",
              "create_time": "2024-03-01T08:29:07Z",
              "update_time": "2024-03-01T09:15:42Z",
              "requesterExt": {
                  "id": "550e8400-e29b-41d4-a716-446655440003",
                  "number": "1042",
                  "name": "Jane",
                  "name2": "Smith"
              },
              "newStatus": {
                  "id": "550e8400-e29b-41d4-a716-446655440004",
                  "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Working",
                  "icon": "la la-briefcase",
                  "fontcolor": "#ffffff",
                  "backgroundcolor": "#2e7d32",
                  "css": "status-working",
                  "comment": false,
                  "payable": true,
                  "action": "clock_in",
                  "migration_id": "12345",
                  "delete_date": "string?<date-time>"
              },
              "oldStatus": {
                  "id": "550e8400-e29b-41d4-a716-446655440006",
                  "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Break",
                  "icon": "la la-coffee",
                  "fontcolor": "#000000",
                  "backgroundcolor": "#fbc02d",
                  "css": "status-break",
                  "comment": false,
                  "payable": false,
                  "action": "string?",
                  "migration_id": "string?",
                  "delete_date": "string?<date-time>"
              }
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is not authorized for this tenant (the request
fails the FormRequest authorization check).
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/user/{employee_id}/corrections

List timepoint corrections for an employee (user)

Returns a paginated list of Tempus time point corrections belonging to a
single employee within the given Pbx (tenant), optionally filtered by
correction status and ordered by creation time (newest first).

Each item is serialized by the `CorrectionResource`. The response uses the
standard Laravel pagination envelope: a `data` array of corrections plus
`links` and `meta` objects.

`operationId: tempusListUserCorrections`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — Identifier of the Pbx (tenant).
- `employee_id` (path, string<uuid>, required) — Identifier of the Tempus employee whose corrections are listed.
- `filter[status]` (query, array, optional) — Restrict results to corrections in the given lifecycle states. Accepts a
repeated array parameter or a single comma-separated string.
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page (1-100). Defaults to 15.

**Responses**:
- `200` — Paginated list of the employee's corrections.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "timepoint_id": "550e8400-e29b-41d4-a716-446655440002",
              "requester_ext_id": "550e8400-e29b-41d4-a716-446655440003",
              "new_status_id": "550e8400-e29b-41d4-a716-446655440004",
              "new_sub_status_id": "550e8400-e29b-41d4-a716-446655440005",
              "old_status_id": "550e8400-e29b-41d4-a716-446655440006",
              "old_sub_status_id": "550e8400-e29b-41d4-a716-446655440007",
              "new_location_id": "550e8400-e29b-41d4-a716-446655440010",
              "old_location_id": "550e8400-e29b-41d4-a716-446655440011",
              "old_location": {
                  "id": "550e8400-e29b-41d4-a716-446655440011",
                  "name": "HQ Luxembourg",
                  "latitude": 49.6008,
                  "longitude": 6.133,
                  "geofence_radius_m": 150
              },
              "new_location": {
                  "id": "550e8400-e29b-41d4-a716-446655440010",
                  "name": "Remote office",
                  "latitude": 49.612,
                  "longitude": 6.13,
                  "geofence_radius_m": 200
              },
              "new_time": "2024-03-01T09:00:00.000000Z",
              "old_time": "2024-03-01T08:30:00.000000Z",
              "correction_comment": "Forgot to clock out at the end of the shift.",
              "new_comment": "Manual entry approved by supervisor.",
              "status": "pending",
              "create_time": "2024-03-01T08:29:07.000000Z",
              "update_time": "2024-03-01T08:29:07.000000Z",
              "accepted_validations_count": 1,
              "refused_validations_count": 0,
              "accepted_by": [
                  {
                      "employee_id": "550e8400-e29b-41d4-a716-446655440004",
                      "status": "pending"
                  }
              ],
              "timepoint": {
                  "id": "550e8400-e29b-41d4-a716-446655440002",
                  "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                  "ext_id": "550e8400-e29b-41d4-a716-446655440003",
                  "status_id": "550e8400-e29b-41d4-a716-446655440004",
                  "time": "2024-03-01T08:30:00.000000Z",
                  "migration_id": "legacy-tp-10293",
                  "delete_date": "string?<date-time>",
                  "status": {
                      "id": "550e8400-e29b-41d4-a716-446655440004",
                      "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                      "name": "Working",
                      "icon": "la la-briefcase",
                      "fontcolor": "#ffffff",
                      "backgroundcolor": "#2e7d32",
                      "css": "status-working",
                      "comment": 0,
                      "payable": true,
                      "action": "clock_in",
                      "migration_id": "legacy-status-7",
                      "delete_date": "string?<date-time>"
                  },
                  "substatuses": [
                      {
                          "id": "550e8400-e29b-41d4-a716-446655440005",
                          "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                          "name": "Client meeting",
                          "icon": "la la-users",
                          "fontcolor": "#000000",
                          "backgroundcolor": "#bbdefb",
                          "css": "substatus-meeting",
                          "comment": 0,
                          "action": "string?",
                          "delete_date": "string?<date-time>"
                      }
                  ]
              }
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is not authorized for this employee or tenant
(the request fails the FormRequest authorization check).
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/user/{employee_id}/corrections

Create a timepoint correction for an employee, edit alias (user)

Back-compat alias of `POST .../corrections/edit`. Prefer the explicit
intent endpoints: `/corrections/add` (add a missing timepoint),
`/corrections/edit` (amend an existing one), `/corrections/delete` (remove
one).

Creates a pending Tempus time point correction for the authenticated
employee. A correction always amends an **existing** time point, so
`timepoint_id` is **required** and must reference one of the employee's own
time points in this tenant. On acceptance the referenced time point is
updated in place (no duplicate). A `null` `new_time` requests removal of
that time point.

Any earlier pending correction for the same `timepoint_id` is automatically
canceled before the new one is created. Pending validator approvals are
generated after the correction is persisted.

The created correction is returned as a single object serialized by the
`StoreUserCorrectionResource` (not wrapped in a `data` envelope). Server-set
fields such as `pbx_id`, `employee_id`, the resolved old values, and
`status` are not accepted in the request body.

`operationId: tempusCreateUserCorrection`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — Identifier of the Pbx (tenant).
- `employee_id` (path, string<uuid>, required) — Identifier of the Tempus employee the correction is created for (must match the authenticated user's own employee).

**Request body** (`application/json`) (required):
```json
{
    "timepoint_id": "550e8400-e29b-41d4-a716-446655440002",
    "new_status_id": "550e8400-e29b-41d4-a716-446655440004",
    "new_sub_status_id": "550e8400-e29b-41d4-a716-446655440005",
    "new_location_id": "550e8400-e29b-41d4-a716-446655440010",
    "new_time": "2024-03-01 08:30:00",
    "correction_comment": "Forgot to clock in after lunch break.",
    "new_comment": "Corrected start time per manager approval."
}
```

**Responses**:
- `201` — The created correction.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
      "timepoint_id": "550e8400-e29b-41d4-a716-446655440002",
      "requester_ext_id": "550e8400-e29b-41d4-a716-446655440003",
      "new_status_id": "550e8400-e29b-41d4-a716-446655440004",
      "new_sub_status_id": "550e8400-e29b-41d4-a716-446655440005",
      "old_status_id": "550e8400-e29b-41d4-a716-446655440006",
      "old_sub_status_id": "550e8400-e29b-41d4-a716-446655440007",
      "new_location_id": "550e8400-e29b-41d4-a716-446655440010",
      "old_location_id": "550e8400-e29b-41d4-a716-446655440011",
      "old_location": {
          "id": "550e8400-e29b-41d4-a716-446655440011",
          "name": "HQ Luxembourg",
          "latitude": 49.6008,
          "longitude": 6.133,
          "geofence_radius_m": 150
      },
      "new_location": {
          "id": "550e8400-e29b-41d4-a716-446655440010",
          "name": "Remote office",
          "latitude": 49.612,
          "longitude": 6.13,
          "geofence_radius_m": 200
      },
      "new_time": "2024-03-01T09:00:00.000000Z",
      "old_time": "2024-03-01T08:30:00.000000Z",
      "correction_comment": "Forgot to clock out at the end of the shift.",
      "new_comment": "Manual entry approved by supervisor.",
      "status": "pending",
      "create_time": "2024-03-01T08:29:07.000000Z",
      "update_time": "2024-03-01T08:29:07.000000Z",
      "accepted_validations_count": 1,
      "refused_validations_count": 0,
      "accepted_by": [
          {
              "employee_id": "550e8400-e29b-41d4-a716-446655440004",
              "status": "pending"
          }
      ],
      "timepoint": {
          "id": "550e8400-e29b-41d4-a716-446655440002",
          "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
          "ext_id": "550e8400-e29b-41d4-a716-446655440003",
          "status_id": "550e8400-e29b-41d4-a716-446655440004",
          "time": "2024-03-01T08:30:00.000000Z",
          "migration_id": "legacy-tp-10293",
          "delete_date": "string?<date-time>",
          "status": {
              "id": "550e8400-e29b-41d4-a716-446655440004",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Working",
              "icon": "la la-briefcase",
              "fontcolor": "#ffffff",
              "backgroundcolor": "#2e7d32",
              "css": "status-working",
              "comment": 0,
              "payable": true,
              "action": "clock_in",
              "migration_id": "legacy-status-7",
              "delete_date": "string?<date-time>"
          },
          "substatuses": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440005",
                  "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Client meeting",
                  "icon": "la la-users",
                  "fontcolor": "#000000",
                  "backgroundcolor": "#bbdefb",
                  "css": "substatus-meeting",
                  "comment": 0,
                  "action": "string?",
                  "delete_date": "string?<date-time>"
              }
          ]
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is not authorized for this employee or tenant, or
has no linked employee record (the request fails authorization).
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/user/{employee_id}/corrections/add

Add-a-missing-timepoint correction (user)

Creates a pending correction that requests a BRAND-NEW timepoint: there is
no existing timepoint, so `timepoint_id` must be omitted. On validator
acceptance a timepoint is created (never a duplicate). Self-scoped: the
authenticated user must be the `{employee_id}`. Any prior pending "add"
correction by this employee is auto-canceled first.

`operationId: tempusAddUserCorrection`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required)
- `employee_id` (path, string<uuid>, required)

**Request body** (`application/json`) (required):
```json
{
    "new_status_id": "string<uuid>",
    "new_time": "2026-03-02 08:30:00",
    "new_sub_status_id": "string?<uuid>",
    "new_location_id": "550e8400-e29b-41d4-a716-446655440010",
    "correction_comment": "string?",
    "new_comment": "string?"
}
```

**Responses**:
- `201` — The created (pending) correction.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
      "timepoint_id": "550e8400-e29b-41d4-a716-446655440002",
      "requester_ext_id": "550e8400-e29b-41d4-a716-446655440003",
      "new_status_id": "550e8400-e29b-41d4-a716-446655440004",
      "new_sub_status_id": "550e8400-e29b-41d4-a716-446655440005",
      "old_status_id": "550e8400-e29b-41d4-a716-446655440006",
      "old_sub_status_id": "550e8400-e29b-41d4-a716-446655440007",
      "new_location_id": "550e8400-e29b-41d4-a716-446655440010",
      "old_location_id": "550e8400-e29b-41d4-a716-446655440011",
      "old_location": {
          "id": "550e8400-e29b-41d4-a716-446655440011",
          "name": "HQ Luxembourg",
          "latitude": 49.6008,
          "longitude": 6.133,
          "geofence_radius_m": 150
      },
      "new_location": {
          "id": "550e8400-e29b-41d4-a716-446655440010",
          "name": "Remote office",
          "latitude": 49.612,
          "longitude": 6.13,
          "geofence_radius_m": 200
      },
      "new_time": "2024-03-01T09:00:00.000000Z",
      "old_time": "2024-03-01T08:30:00.000000Z",
      "correction_comment": "Forgot to clock out at the end of the shift.",
      "new_comment": "Manual entry approved by supervisor.",
      "status": "pending",
      "create_time": "2024-03-01T08:29:07.000000Z",
      "update_time": "2024-03-01T08:29:07.000000Z",
      "accepted_validations_count": 1,
      "refused_validations_count": 0,
      "accepted_by": [
          {
              "employee_id": "550e8400-e29b-41d4-a716-446655440004",
              "status": "pending"
          }
      ],
      "timepoint": {
          "id": "550e8400-e29b-41d4-a716-446655440002",
          "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
          "ext_id": "550e8400-e29b-41d4-a716-446655440003",
          "status_id": "550e8400-e29b-41d4-a716-446655440004",
          "time": "2024-03-01T08:30:00.000000Z",
          "migration_id": "legacy-tp-10293",
          "delete_date": "string?<date-time>",
          "status": {
              "id": "550e8400-e29b-41d4-a716-446655440004",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Working",
              "icon": "la la-briefcase",
              "fontcolor": "#ffffff",
              "backgroundcolor": "#2e7d32",
              "css": "status-working",
              "comment": 0,
              "payable": true,
              "action": "clock_in",
              "migration_id": "legacy-status-7",
              "delete_date": "string?<date-time>"
          },
          "substatuses": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440005",
                  "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Client meeting",
                  "icon": "la la-users",
                  "fontcolor": "#000000",
                  "backgroundcolor": "#bbdefb",
                  "css": "substatus-meeting",
                  "comment": 0,
                  "action": "string?",
                  "delete_date": "string?<date-time>"
              }
          ]
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Not authorized for this employee/tenant, or no linked employee record.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/user/{employee_id}/corrections/edit

Edit-an-existing-timepoint correction (user)

Creates a pending correction that amends an EXISTING timepoint.
`timepoint_id` is required and must be one of the requesting employee's own
timepoints. On validator acceptance the timepoint is updated in place, with no
duplicate. `new_time` is optional; if omitted it defaults to the timepoint's
current time (so a status-only edit is never read as a removal). Base
`POST .../corrections` (no suffix) is a back-compat alias of this endpoint.
Any prior pending correction for the same timepoint is auto-canceled first.

`operationId: tempusEditUserCorrection`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required)
- `employee_id` (path, string<uuid>, required)

**Request body** (`application/json`) (required):
```json
{
    "timepoint_id": "string<uuid>",
    "new_time": "2026-03-02 08:00:00",
    "new_status_id": "string?<uuid>",
    "new_sub_status_id": "string?<uuid>",
    "new_location_id": "550e8400-e29b-41d4-a716-446655440010",
    "correction_comment": "string?",
    "new_comment": "string?"
}
```

**Responses**:
- `201` — The created (pending) correction.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
      "timepoint_id": "550e8400-e29b-41d4-a716-446655440002",
      "requester_ext_id": "550e8400-e29b-41d4-a716-446655440003",
      "new_status_id": "550e8400-e29b-41d4-a716-446655440004",
      "new_sub_status_id": "550e8400-e29b-41d4-a716-446655440005",
      "old_status_id": "550e8400-e29b-41d4-a716-446655440006",
      "old_sub_status_id": "550e8400-e29b-41d4-a716-446655440007",
      "new_location_id": "550e8400-e29b-41d4-a716-446655440010",
      "old_location_id": "550e8400-e29b-41d4-a716-446655440011",
      "old_location": {
          "id": "550e8400-e29b-41d4-a716-446655440011",
          "name": "HQ Luxembourg",
          "latitude": 49.6008,
          "longitude": 6.133,
          "geofence_radius_m": 150
      },
      "new_location": {
          "id": "550e8400-e29b-41d4-a716-446655440010",
          "name": "Remote office",
          "latitude": 49.612,
          "longitude": 6.13,
          "geofence_radius_m": 200
      },
      "new_time": "2024-03-01T09:00:00.000000Z",
      "old_time": "2024-03-01T08:30:00.000000Z",
      "correction_comment": "Forgot to clock out at the end of the shift.",
      "new_comment": "Manual entry approved by supervisor.",
      "status": "pending",
      "create_time": "2024-03-01T08:29:07.000000Z",
      "update_time": "2024-03-01T08:29:07.000000Z",
      "accepted_validations_count": 1,
      "refused_validations_count": 0,
      "accepted_by": [
          {
              "employee_id": "550e8400-e29b-41d4-a716-446655440004",
              "status": "pending"
          }
      ],
      "timepoint": {
          "id": "550e8400-e29b-41d4-a716-446655440002",
          "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
          "ext_id": "550e8400-e29b-41d4-a716-446655440003",
          "status_id": "550e8400-e29b-41d4-a716-446655440004",
          "time": "2024-03-01T08:30:00.000000Z",
          "migration_id": "legacy-tp-10293",
          "delete_date": "string?<date-time>",
          "status": {
              "id": "550e8400-e29b-41d4-a716-446655440004",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Working",
              "icon": "la la-briefcase",
              "fontcolor": "#ffffff",
              "backgroundcolor": "#2e7d32",
              "css": "status-working",
              "comment": 0,
              "payable": true,
              "action": "clock_in",
              "migration_id": "legacy-status-7",
              "delete_date": "string?<date-time>"
          },
          "substatuses": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440005",
                  "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Client meeting",
                  "icon": "la la-users",
                  "fontcolor": "#000000",
                  "backgroundcolor": "#bbdefb",
                  "css": "substatus-meeting",
                  "comment": 0,
                  "action": "string?",
                  "delete_date": "string?<date-time>"
              }
          ]
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Not authorized for this employee/tenant, or no linked employee record.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/user/{employee_id}/corrections/delete

Delete-a-timepoint correction (user)

Requests removal of an EXISTING timepoint. `timepoint_id` is required and
must be one of the requesting employee's own timepoints. Two outcomes:

- If there is **no** pending correction for that timepoint, a pending delete
  request is created (`201`); on validator acceptance the timepoint is
  removed.
- If a correction for that timepoint is **already pending**, that pending
  request is canceled directly (`200`, no new request and no approval);
  i.e. delete retracts the outstanding correction.

Self-scoped: the authenticated user must be the `{employee_id}`.

`operationId: tempusDeleteUserCorrection`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required)
- `employee_id` (path, string<uuid>, required)

**Request body** (`application/json`) (required):
```json
{
    "timepoint_id": "string<uuid>",
    "correction_comment": "string?",
    "new_comment": "string?"
}
```

**Responses**:
- `201` — A pending delete correction was created.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
      "timepoint_id": "550e8400-e29b-41d4-a716-446655440002",
      "requester_ext_id": "550e8400-e29b-41d4-a716-446655440003",
      "new_status_id": "550e8400-e29b-41d4-a716-446655440004",
      "new_sub_status_id": "550e8400-e29b-41d4-a716-446655440005",
      "old_status_id": "550e8400-e29b-41d4-a716-446655440006",
      "old_sub_status_id": "550e8400-e29b-41d4-a716-446655440007",
      "new_location_id": "550e8400-e29b-41d4-a716-446655440010",
      "old_location_id": "550e8400-e29b-41d4-a716-446655440011",
      "old_location": {
          "id": "550e8400-e29b-41d4-a716-446655440011",
          "name": "HQ Luxembourg",
          "latitude": 49.6008,
          "longitude": 6.133,
          "geofence_radius_m": 150
      },
      "new_location": {
          "id": "550e8400-e29b-41d4-a716-446655440010",
          "name": "Remote office",
          "latitude": 49.612,
          "longitude": 6.13,
          "geofence_radius_m": 200
      },
      "new_time": "2024-03-01T09:00:00.000000Z",
      "old_time": "2024-03-01T08:30:00.000000Z",
      "correction_comment": "Forgot to clock out at the end of the shift.",
      "new_comment": "Manual entry approved by supervisor.",
      "status": "pending",
      "create_time": "2024-03-01T08:29:07.000000Z",
      "update_time": "2024-03-01T08:29:07.000000Z",
      "accepted_validations_count": 1,
      "refused_validations_count": 0,
      "accepted_by": [
          {
              "employee_id": "550e8400-e29b-41d4-a716-446655440004",
              "status": "pending"
          }
      ],
      "timepoint": {
          "id": "550e8400-e29b-41d4-a716-446655440002",
          "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
          "ext_id": "550e8400-e29b-41d4-a716-446655440003",
          "status_id": "550e8400-e29b-41d4-a716-446655440004",
          "time": "2024-03-01T08:30:00.000000Z",
          "migration_id": "legacy-tp-10293",
          "delete_date": "string?<date-time>",
          "status": {
              "id": "550e8400-e29b-41d4-a716-446655440004",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Working",
              "icon": "la la-briefcase",
              "fontcolor": "#ffffff",
              "backgroundcolor": "#2e7d32",
              "css": "status-working",
              "comment": 0,
              "payable": true,
              "action": "clock_in",
              "migration_id": "legacy-status-7",
              "delete_date": "string?<date-time>"
          },
          "substatuses": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440005",
                  "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Client meeting",
                  "icon": "la la-users",
                  "fontcolor": "#000000",
                  "backgroundcolor": "#bbdefb",
                  "css": "substatus-meeting",
                  "comment": 0,
                  "action": "string?",
                  "delete_date": "string?<date-time>"
              }
          ]
      }
  }
  ```
- `200` — An already-pending correction for the timepoint was canceled instead.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
      "timepoint_id": "550e8400-e29b-41d4-a716-446655440002",
      "requester_ext_id": "550e8400-e29b-41d4-a716-446655440003",
      "new_status_id": "550e8400-e29b-41d4-a716-446655440004",
      "new_sub_status_id": "550e8400-e29b-41d4-a716-446655440005",
      "old_status_id": "550e8400-e29b-41d4-a716-446655440006",
      "old_sub_status_id": "550e8400-e29b-41d4-a716-446655440007",
      "new_location_id": "550e8400-e29b-41d4-a716-446655440010",
      "old_location_id": "550e8400-e29b-41d4-a716-446655440011",
      "old_location": {
          "id": "550e8400-e29b-41d4-a716-446655440011",
          "name": "HQ Luxembourg",
          "latitude": 49.6008,
          "longitude": 6.133,
          "geofence_radius_m": 150
      },
      "new_location": {
          "id": "550e8400-e29b-41d4-a716-446655440010",
          "name": "Remote office",
          "latitude": 49.612,
          "longitude": 6.13,
          "geofence_radius_m": 200
      },
      "new_time": "2024-03-01T09:00:00.000000Z",
      "old_time": "2024-03-01T08:30:00.000000Z",
      "correction_comment": "Forgot to clock out at the end of the shift.",
      "new_comment": "Manual entry approved by supervisor.",
      "status": "pending",
      "create_time": "2024-03-01T08:29:07.000000Z",
      "update_time": "2024-03-01T08:29:07.000000Z",
      "accepted_validations_count": 1,
      "refused_validations_count": 0,
      "accepted_by": [
          {
              "employee_id": "550e8400-e29b-41d4-a716-446655440004",
              "status": "pending"
          }
      ],
      "timepoint": {
          "id": "550e8400-e29b-41d4-a716-446655440002",
          "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
          "ext_id": "550e8400-e29b-41d4-a716-446655440003",
          "status_id": "550e8400-e29b-41d4-a716-446655440004",
          "time": "2024-03-01T08:30:00.000000Z",
          "migration_id": "legacy-tp-10293",
          "delete_date": "string?<date-time>",
          "status": {
              "id": "550e8400-e29b-41d4-a716-446655440004",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Working",
              "icon": "la la-briefcase",
              "fontcolor": "#ffffff",
              "backgroundcolor": "#2e7d32",
              "css": "status-working",
              "comment": 0,
              "payable": true,
              "action": "clock_in",
              "migration_id": "legacy-status-7",
              "delete_date": "string?<date-time>"
          },
          "substatuses": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440005",
                  "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Client meeting",
                  "icon": "la la-users",
                  "fontcolor": "#000000",
                  "backgroundcolor": "#bbdefb",
                  "css": "substatus-meeting",
                  "comment": 0,
                  "action": "string?",
                  "delete_date": "string?<date-time>"
              }
          ]
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Not authorized for this employee/tenant, or no linked employee record.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/user/{employee_id}/corrections/{correction_id}

Get a timepoint correction for an employee (user)

Returns a single Tempus time point correction belonging to the given
employee within the tenant. The authenticated user may only read their own
employee's corrections.

The correction is returned as a single object serialized by the
`CorrectionResource` (not wrapped in a `data` envelope), with the related
time point, statuses, and sub-statuses eager loaded.

`operationId: tempusGetUserCorrection`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — Identifier of the Pbx (tenant).
- `employee_id` (path, string<uuid>, required) — Identifier of the Tempus employee (must match the authenticated user's own employee).
- `correction_id` (path, string<uuid>, required) — Identifier of the correction to retrieve.

**Responses**:
- `200` — The requested correction.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
      "timepoint_id": "550e8400-e29b-41d4-a716-446655440002",
      "requester_ext_id": "550e8400-e29b-41d4-a716-446655440003",
      "new_status_id": "550e8400-e29b-41d4-a716-446655440004",
      "new_sub_status_id": "550e8400-e29b-41d4-a716-446655440005",
      "old_status_id": "550e8400-e29b-41d4-a716-446655440006",
      "old_sub_status_id": "550e8400-e29b-41d4-a716-446655440007",
      "new_location_id": "550e8400-e29b-41d4-a716-446655440010",
      "old_location_id": "550e8400-e29b-41d4-a716-446655440011",
      "old_location": {
          "id": "550e8400-e29b-41d4-a716-446655440011",
          "name": "HQ Luxembourg",
          "latitude": 49.6008,
          "longitude": 6.133,
          "geofence_radius_m": 150
      },
      "new_location": {
          "id": "550e8400-e29b-41d4-a716-446655440010",
          "name": "Remote office",
          "latitude": 49.612,
          "longitude": 6.13,
          "geofence_radius_m": 200
      },
      "new_time": "2024-03-01T09:00:00.000000Z",
      "old_time": "2024-03-01T08:30:00.000000Z",
      "correction_comment": "Forgot to clock out at the end of the shift.",
      "new_comment": "Manual entry approved by supervisor.",
      "status": "pending",
      "create_time": "2024-03-01T08:29:07.000000Z",
      "update_time": "2024-03-01T08:29:07.000000Z",
      "accepted_validations_count": 1,
      "refused_validations_count": 0,
      "accepted_by": [
          {
              "employee_id": "550e8400-e29b-41d4-a716-446655440004",
              "status": "pending"
          }
      ],
      "timepoint": {
          "id": "550e8400-e29b-41d4-a716-446655440002",
          "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
          "ext_id": "550e8400-e29b-41d4-a716-446655440003",
          "status_id": "550e8400-e29b-41d4-a716-446655440004",
          "time": "2024-03-01T08:30:00.000000Z",
          "migration_id": "legacy-tp-10293",
          "delete_date": "string?<date-time>",
          "status": {
              "id": "550e8400-e29b-41d4-a716-446655440004",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Working",
              "icon": "la la-briefcase",
              "fontcolor": "#ffffff",
              "backgroundcolor": "#2e7d32",
              "css": "status-working",
              "comment": 0,
              "payable": true,
              "action": "clock_in",
              "migration_id": "legacy-status-7",
              "delete_date": "string?<date-time>"
          },
          "substatuses": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440005",
                  "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Client meeting",
                  "icon": "la la-users",
                  "fontcolor": "#000000",
                  "backgroundcolor": "#bbdefb",
                  "css": "substatus-meeting",
                  "comment": 0,
                  "action": "string?",
                  "delete_date": "string?<date-time>"
              }
          ]
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no linked employee record, is requesting an
employee other than their own, or does not belong to the tenant.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### PUT /{pbx_id}/user/{employee_id}/corrections/{correction_id}

Cancel a timepoint correction for an employee (user)

Cancels an existing Tempus time point correction for the authenticated
employee. The only accepted `status` value is `canceled`.

Behaviour depends on the correction's current state. A `pending` correction
is marked `canceled` and its pending approvals are removed. An `accepted`
correction cannot be undone directly; instead a new pending reversal
correction (old and new values swapped) is created and returned for
validation. Any other current state results in an error.

The resulting correction is returned as a single object serialized by the
`UpdateUserCorrectionResource` (not wrapped in a `data` envelope).

`operationId: tempusUpdateUserCorrection`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — Identifier of the Pbx (tenant).
- `employee_id` (path, string<uuid>, required) — Identifier of the Tempus employee (must match the authenticated user's own employee).
- `correction_id` (path, string<uuid>, required) — Identifier of the correction to cancel.

**Request body** (`application/json`) (required):
```json
{
    "status": "canceled"
}
```

**Responses**:
- `200` — The canceled correction, or the newly created pending reversal
correction when the original had been accepted.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
      "timepoint_id": "550e8400-e29b-41d4-a716-446655440002",
      "requester_ext_id": "550e8400-e29b-41d4-a716-446655440003",
      "new_status_id": "550e8400-e29b-41d4-a716-446655440004",
      "new_sub_status_id": "550e8400-e29b-41d4-a716-446655440005",
      "old_status_id": "550e8400-e29b-41d4-a716-446655440006",
      "old_sub_status_id": "550e8400-e29b-41d4-a716-446655440007",
      "new_location_id": "550e8400-e29b-41d4-a716-446655440010",
      "old_location_id": "550e8400-e29b-41d4-a716-446655440011",
      "old_location": {
          "id": "550e8400-e29b-41d4-a716-446655440011",
          "name": "HQ Luxembourg",
          "latitude": 49.6008,
          "longitude": 6.133,
          "geofence_radius_m": 150
      },
      "new_location": {
          "id": "550e8400-e29b-41d4-a716-446655440010",
          "name": "Remote office",
          "latitude": 49.612,
          "longitude": 6.13,
          "geofence_radius_m": 200
      },
      "new_time": "2024-03-01T09:00:00.000000Z",
      "old_time": "2024-03-01T08:30:00.000000Z",
      "correction_comment": "Forgot to clock out at the end of the shift.",
      "new_comment": "Manual entry approved by supervisor.",
      "status": "pending",
      "create_time": "2024-03-01T08:29:07.000000Z",
      "update_time": "2024-03-01T08:29:07.000000Z",
      "accepted_validations_count": 1,
      "refused_validations_count": 0,
      "accepted_by": [
          {
              "employee_id": "550e8400-e29b-41d4-a716-446655440004",
              "status": "pending"
          }
      ],
      "timepoint": {
          "id": "550e8400-e29b-41d4-a716-446655440002",
          "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
          "ext_id": "550e8400-e29b-41d4-a716-446655440003",
          "status_id": "550e8400-e29b-41d4-a716-446655440004",
          "time": "2024-03-01T08:30:00.000000Z",
          "migration_id": "legacy-tp-10293",
          "delete_date": "string?<date-time>",
          "status": {
              "id": "550e8400-e29b-41d4-a716-446655440004",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Working",
              "icon": "la la-briefcase",
              "fontcolor": "#ffffff",
              "backgroundcolor": "#2e7d32",
              "css": "status-working",
              "comment": 0,
              "payable": true,
              "action": "clock_in",
              "migration_id": "legacy-status-7",
              "delete_date": "string?<date-time>"
          },
          "substatuses": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440005",
                  "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Client meeting",
                  "icon": "la la-users",
                  "fontcolor": "#000000",
                  "backgroundcolor": "#bbdefb",
                  "css": "substatus-meeting",
                  "comment": 0,
                  "action": "string?",
                  "delete_date": "string?<date-time>"
              }
          ]
      }
  }
  ```
- `400` — The correction is neither pending nor accepted and therefore cannot be
canceled.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is not authorized for this employee or tenant
(the request fails the FormRequest authorization check).
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/corrections/{correction_id}/validations

List validation decisions for a time-point correction (user)

Returns the paginated list of validation (approval) decisions attached to a
single Tempus time-point correction request. Each item is a
`TempusCorrectionApproval` recording one validator's decision. The validator
summary is eager-loaded on every item. Results are wrapped in the standard
Laravel pagination envelope (`data`, `links`, `meta`).

`operationId: tempusListCorrectionValidations`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — Identifier of the Pbx (tenant).
- `correction_id` (path, string<uuid>, required) — Identifier of the Tempus time-point correction request.
- `page` (query, integer, optional) — Page number of the result set.
- `per_page` (query, integer, optional) — Number of records per page (maximum 100).

**Responses**:
- `200` — Paginated list of validation decisions for the correction.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "correction_id": "550e8400-e29b-41d4-a716-446655440002",
              "ext_id": "550e8400-e29b-41d4-a716-446655440003",
              "validator": {
                  "id": "550e8400-e29b-41d4-a716-446655440003",
                  "name": "Jane Smith"
              },
              "status": "pending",
              "comment": "Approved after confirming the corrected clock-in time with the team lead.",
              "create_time": "2024-03-01T08:29:07+00:00"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user does not belong to the requested tenant.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/corrections/{correction_id}/validations

Submit a validation decision for a time-point correction (user)

Records the authenticated validator's decision (accepted or rejected) for a
Tempus time-point correction request, then applies the decision to the
correction. If the validator already has a pending decision on this
correction it is updated in place; a decision that has already been finalised
cannot be resubmitted. The authenticated user must have a linked Tempus
employee record. A requester notification email is dispatched after the
decision is committed.

`operationId: tempusCreateCorrectionValidation`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — Identifier of the Pbx (tenant).
- `correction_id` (path, string<uuid>, required) — Identifier of the Tempus time-point correction request.

**Request body** (`application/json`) (required):
```json
{
    "status": "accepted",
    "comment": "Approved after confirming the corrected clock-in time.",
    "note": "Approved after confirming the corrected clock-in time.",
    "new_time": "2024-03-01T08:00:00+00:00",
    "new_status_id": "550e8400-e29b-41d4-a716-446655440010"
}
```

**Responses**:
- `201` — The validation decision was recorded.
  ```json
  {
      "message": "Correction validation created."
  }
  ```
- `400` — The correction is no longer pending and cannot be validated.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user does not belong to the requested tenant or has no
linked Tempus employee record.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `409` — A validation decision already exists for this correction.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

### Overtime

Overtime calculation periods and per-employee overtime decisions.

#### GET /{pbx_id}/user/{employee_id}/user-overtime-decision/getOvertime

Calculate overtime for an employee over a date range (user)

Calculates the accumulated overtime for a single Tempus employee within a date range and returns it split into `business` (overtime worked on regular business days) and `non_working` (overtime worked on weekends, public holidays and other non-working days). Both figures are expressed in seconds.

The window is taken from the `start` and `end` query parameters. When neither is supplied the endpoint falls back to the employee's current overtime period. The range may not exceed the configured maximum number of months (default 3); a longer range is rejected with 422. The employee must belong to the requested PBX tenant, otherwise the request returns 404.

`operationId: tempusGetUserOvertime`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the Tempus employee.
- `start` (query, string<date>, optional) — Start of the range (any parseable date). Must be on or before `end`. When omitted the employee's current overtime period start is used.
- `end` (query, string<date>, optional) — End of the range (any parseable date). Must be on or after `start` and within the configured maximum span from `start`. When omitted the employee's current overtime period end is used.

**Responses**:
- `200` — Calculated overtime totals in seconds.
  ```json
  {
      "business": 7200,
      "non_working": 1800
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/user/{employee_id}/user-overtime-decision

List a specific employee's overtime decisions (user)

Returns a paginated list of overtime decisions recorded for a single Tempus employee within the PBX tenant. Each item is serialized by the `UserOvertimeDecisionResource`. When no `filter[overtime_calculation_period_id]` is supplied the list is automatically restricted to the employee's current overtime period. The caller must be authorized to view the employee's own decisions, otherwise the request is rejected with 403, and the employee must belong to the requested PBX tenant, otherwise 404 is returned.

`operationId: tempusListUserOvertimeDecisions`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the Tempus employee.
- `filter[overtime_calculation_period_id]` (query, string<uuid>, optional) — Restrict results to a single overtime calculation period. When omitted the current period is used automatically.
- `sort` (query, string, optional) — Sort order. Supports `created_at` and `overtime_calculation_period.start`, each prefixable with `-` for descending. Defaults to `-created_at`.
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page.

**Responses**:
- `200` — Paginated list of the employee's overtime decisions.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "ext_id": "1042",
              "overtime_calculation_period_id": "550e8400-e29b-41d4-a716-446655440002",
              "decision_type": "manual",
              "decision": "pay_out",
              "decision_balance": 36000,
              "decision_overtime": 7200,
              "to_pay": 3600,
              "to_pay_extra": 0,
              "to_increment": 0,
              "to_convert": 1800,
              "to_reset": 0,
              "decision_new_balance": 38400,
              "comment": "Carried forward at the employee's request",
              "locked": false,
              "values_live": false,
              "created": "2024-03-01 08:29:07",
              "overtime_calculation_period": {
                  "id": "550e8400-e29b-41d4-a716-446655440002",
                  "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                  "company_id": "550e8400-e29b-41d4-a716-446655440003",
                  "ext_id": "1042",
                  "start": "2024-03-01T00:00:00Z",
                  "end": "2024-03-31T23:59:59Z",
                  "locked": false,
                  "delete_date": "string?<date-time>"
              }
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The caller is not authorized to view this employee's overtime decisions.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/user/{employee_id}/user-overtime-decision

Create or update an employee's overtime decision (user)

Creates or updates the overtime decision for a single Tempus employee for an overtime calculation period. The endpoint recalculates the period's overtime, derives `to_pay` / `to_increment` from the chosen `decision` (or from an explicit `to_pay`), and persists the decision via an upsert keyed on tenant, employee and period.

The stored row is fully derived server-side, so the response is the authoritative "after" state:

* `decision_balance` — the balance carried into the period, taken from the
  last LOCKED period's closing balance (never from an unlocked draft).
* `decision_overtime` — the period's accrual UP TO TODAY. Days that have not
  happened yet are not counted, so a decision taken mid-period no longer
  reports the remaining workdays as undertime.
* `to_pay` — what is cashed out, capped at
  `decision_balance + decision_overtime` and never negative.
* `to_increment` — `decision_overtime - to_pay`, i.e. what lands on the
  balance. Negative when the payout is drawn from the carried-in balance.
* `decision_new_balance` — what is left afterwards.

When `overtime_calculation_period_id` is omitted the active period is resolved from the employee's current contract; if no active contract or no active decision period can be resolved the request is rejected with 422. If a decision for the period has already been processed (locked) it can no longer be changed and the request returns 403. The employee must belong to the requested PBX tenant, otherwise 404 is returned.

`operationId: tempusStoreUserOvertimeDecision`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the Tempus employee.

**Request body** (`application/json`) (required):
```json
{
    "decision": "add_to_balance",
    "to_pay": 43200,
    "overtime_calculation_period_id": "550e8400-e29b-41d4-a716-446655440060"
}
```

**Responses**:
- `200` — The created or updated overtime decision.
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
          "ext_id": "1042",
          "overtime_calculation_period_id": "550e8400-e29b-41d4-a716-446655440002",
          "decision_type": "manual",
          "decision": "pay_out",
          "decision_balance": 36000,
          "decision_overtime": 7200,
          "to_pay": 3600,
          "to_pay_extra": 0,
          "to_increment": 0,
          "to_convert": 1800,
          "to_reset": 0,
          "decision_new_balance": 38400,
          "comment": "Carried forward at the employee's request",
          "locked": false,
          "values_live": false,
          "created": "2024-03-01 08:29:07",
          "overtime_calculation_period": {
              "id": "550e8400-e29b-41d4-a716-446655440002",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "company_id": "550e8400-e29b-41d4-a716-446655440003",
              "ext_id": "1042",
              "start": "2024-03-01T00:00:00Z",
              "end": "2024-03-31T23:59:59Z",
              "locked": false,
              "delete_date": "string?<date-time>"
          }
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The overtime decision for this period has already been processed and is locked.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Validation failed: an unknown period, a `to_pay` larger than the employee's available time, or no active contract / decision period to resolve.

#### GET /{pbx_id}/user/{employee_id}/user-overtime-decision-preference

Read the employee's overtime decision preference (user)

Returns the employee's standing overtime decision preference, the same row HR edits under "Decision Preferences" on the employee record, plus the options, bounds and defaults needed to render the form.

`data` is null when the employee has never saved a preference; in that case `meta.configured` is false and the company level rule applies. Read the starting values for the form from `meta.defaults`.

Self service only. The request is rejected with 403 when the authenticated user has no access to the tenant or the employee record is not their own, and with 404 when the employee cannot be found.

`operationId: tempusShowUserOvertimeDecisionPreference`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee. Must be the authenticated user's own employee record.

**Responses**:
- `200` — The current preference, or null when none has been saved.
  ```json
  {
      "data": [
          {
              "id": "9b1f0c62-4d0a-4e2f-9f43-6f4a5c8d1e70",
              "scope": "employee",
              "employee_id": "a24c68c0-92c0-4ca0-b749-84c6ddd671ed",
              "auto_decision_policy": "force_auto_decision",
              "auto_decision_policy_label": "Force auto-decision (override manual)",
              "policy_reason": "Prefers time off over payout",
              "preferred_decision": "pay_up_to",
              "preferred_decision_label": "Pay up to X hours",
              "max_seconds": 28800,
              "max_hours": 8,
              "rest_policy": "add_to_balance",
              "rest_policy_label": "Add to balance",
              "is_active": true,
              "created_at": "2026-08-06 09:14:22",
              "updated_at": "2026-08-06 10:02:51"
          },
          "null"
      ],
      "meta": {
          "configured": true,
          "options": {
              "auto_decision_policy": {
                  "skip_if_manual_decision": "Skip if manual decision exists",
                  "force_auto_decision": "Force auto-decision (override manual)",
                  "force_zero_overtime": "Force zero overtime (not eligible)"
              },
              "preferred_decision": {
                  "pay_up_to": "Pay up to X hours (specify max time)",
                  "increment_up_to": "Increment balance up to X hours (specify max time)"
              },
              "rest_policy": {
                  "add_to_balance": "Add to balance",
                  "drop": "Drop (ignore remaining hours)"
              }
          },
          "limits": {
              "min_seconds": 3600,
              "max_seconds": 115200,
              "min_hours": 1,
              "max_hours": 32
          },
          "defaults": {
              "auto_decision_policy": "skip_if_manual_decision",
              "preferred_decision": "pay_up_to",
              "rest_policy": "add_to_balance",
              "max_seconds": 3600,
              "max_hours": 1,
              "is_active": true
          }
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no access to the requested tenant, or the requested employee record does not belong to the authenticated user.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### PUT /{pbx_id}/user/{employee_id}/user-overtime-decision-preference

Save the employee's overtime decision preference (user)

Creates or replaces the employee's overtime decision preference. There is at
most one employee level row, so this is an upsert: 201 the first time, 200
on every later call. It writes the same row HR edits under "Decision
Preferences", so a change made here is visible to HR immediately.

This is a full replace, not a patch. Fields left out are reset: `policy_reason`
becomes null and `is_active` becomes true.

**Which fields are required depends on `auto_decision_policy`:**

| `auto_decision_policy` | `preferred_decision` | cap | `rest_policy` |
|---|---|---|---|
| `skip_if_manual_decision` | required | required | required |
| `force_auto_decision` | required | required | required |
| `force_zero_overtime` | ignored, stored as null | ignored, stored as null | ignored, stored as null |

The cap may be sent as `max_seconds` or as `max_hours`; `max_seconds` wins
when both are present. `max_hours` is multiplied by 3600 and rounded to the
nearest second. One of the two is required unless the policy is
`force_zero_overtime`.

Self service only. The request is rejected with 403 when the authenticated
user has no access to the tenant or the employee record is not their own,
and with 404 when the employee cannot be found.

`operationId: tempusUpdateUserOvertimeDecisionPreference`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee. Must be the authenticated user's own employee record.

**Request body** (`application/json`) (required):
```json
{
    "auto_decision_policy": "force_auto_decision",
    "policy_reason": "Prefers time off over payout",
    "preferred_decision": "increment_up_to",
    "max_hours": 8,
    "max_seconds": 28800,
    "rest_policy": "add_to_balance",
    "is_active": true
}
```

**Responses**:
- `200` — An existing preference was replaced.
  ```json
  {
      "message": "Your overtime decision preference has been saved.",
      "data": {
          "id": "9b1f0c62-4d0a-4e2f-9f43-6f4a5c8d1e70",
          "scope": "employee",
          "employee_id": "a24c68c0-92c0-4ca0-b749-84c6ddd671ed",
          "auto_decision_policy": "force_auto_decision",
          "auto_decision_policy_label": "Force auto-decision (override manual)",
          "policy_reason": "Prefers time off over payout",
          "preferred_decision": "pay_up_to",
          "preferred_decision_label": "Pay up to X hours",
          "max_seconds": 28800,
          "max_hours": 8,
          "rest_policy": "add_to_balance",
          "rest_policy_label": "Add to balance",
          "is_active": true,
          "created_at": "2026-08-06 09:14:22",
          "updated_at": "2026-08-06 10:02:51"
      },
      "meta": {
          "configured": true,
          "options": {
              "auto_decision_policy": {
                  "skip_if_manual_decision": "Skip if manual decision exists",
                  "force_auto_decision": "Force auto-decision (override manual)",
                  "force_zero_overtime": "Force zero overtime (not eligible)"
              },
              "preferred_decision": {
                  "pay_up_to": "Pay up to X hours (specify max time)",
                  "increment_up_to": "Increment balance up to X hours (specify max time)"
              },
              "rest_policy": {
                  "add_to_balance": "Add to balance",
                  "drop": "Drop (ignore remaining hours)"
              }
          },
          "limits": {
              "min_seconds": 3600,
              "max_seconds": 115200,
              "min_hours": 1,
              "max_hours": 32
          },
          "defaults": {
              "auto_decision_policy": "skip_if_manual_decision",
              "preferred_decision": "pay_up_to",
              "rest_policy": "add_to_balance",
              "max_seconds": 3600,
              "max_hours": 1,
              "is_active": true
          }
      }
  }
  ```
- `201` — The employee had no preference before and one was created.
  ```json
  {
      "message": "Your overtime decision preference has been saved.",
      "data": {
          "id": "9b1f0c62-4d0a-4e2f-9f43-6f4a5c8d1e70",
          "scope": "employee",
          "employee_id": "a24c68c0-92c0-4ca0-b749-84c6ddd671ed",
          "auto_decision_policy": "force_auto_decision",
          "auto_decision_policy_label": "Force auto-decision (override manual)",
          "policy_reason": "Prefers time off over payout",
          "preferred_decision": "pay_up_to",
          "preferred_decision_label": "Pay up to X hours",
          "max_seconds": 28800,
          "max_hours": 8,
          "rest_policy": "add_to_balance",
          "rest_policy_label": "Add to balance",
          "is_active": true,
          "created_at": "2026-08-06 09:14:22",
          "updated_at": "2026-08-06 10:02:51"
      },
      "meta": {
          "configured": true,
          "options": {
              "auto_decision_policy": {
                  "skip_if_manual_decision": "Skip if manual decision exists",
                  "force_auto_decision": "Force auto-decision (override manual)",
                  "force_zero_overtime": "Force zero overtime (not eligible)"
              },
              "preferred_decision": {
                  "pay_up_to": "Pay up to X hours (specify max time)",
                  "increment_up_to": "Increment balance up to X hours (specify max time)"
              },
              "rest_policy": {
                  "add_to_balance": "Add to balance",
                  "drop": "Drop (ignore remaining hours)"
              }
          },
          "limits": {
              "min_seconds": 3600,
              "max_seconds": 115200,
              "min_hours": 1,
              "max_hours": 32
          },
          "defaults": {
              "auto_decision_policy": "skip_if_manual_decision",
              "preferred_decision": "pay_up_to",
              "rest_policy": "add_to_balance",
              "max_seconds": 3600,
              "max_hours": 1,
              "is_active": true
          }
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no access to the requested tenant, or the requested employee record does not belong to the authenticated user.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — The body failed validation.

#### DELETE /{pbx_id}/user/{employee_id}/user-overtime-decision-preference

Remove the employee's overtime decision preference (user)

Removes the employee level preference so the company level rule applies again. The row is soft deleted, so a later save creates a fresh one.

Returns 404 when the employee has no preference to remove.

Self service only. The request is rejected with 403 when the authenticated user has no access to the tenant or the employee record is not their own.

`operationId: tempusDeleteUserOvertimeDecisionPreference`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee. Must be the authenticated user's own employee record.

**Responses**:
- `200` — The preference was removed.
  ```json
  {
      "message": "Your overtime decision preference has been removed.",
      "data": "null",
      "meta": {
          "configured": true,
          "options": {
              "auto_decision_policy": {
                  "skip_if_manual_decision": "Skip if manual decision exists",
                  "force_auto_decision": "Force auto-decision (override manual)",
                  "force_zero_overtime": "Force zero overtime (not eligible)"
              },
              "preferred_decision": {
                  "pay_up_to": "Pay up to X hours (specify max time)",
                  "increment_up_to": "Increment balance up to X hours (specify max time)"
              },
              "rest_policy": {
                  "add_to_balance": "Add to balance",
                  "drop": "Drop (ignore remaining hours)"
              }
          },
          "limits": {
              "min_seconds": 3600,
              "max_seconds": 115200,
              "min_hours": 1,
              "max_hours": 32
          },
          "defaults": {
              "auto_decision_policy": "skip_if_manual_decision",
              "preferred_decision": "pay_up_to",
              "rest_policy": "add_to_balance",
              "max_seconds": 3600,
              "max_hours": 1,
              "is_active": true
          }
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no access to the requested tenant, or the requested employee record does not belong to the authenticated user.
- `404` — The employee was not found, or has no overtime decision preference to remove.

#### GET /{pbx_id}/users-overtime-decisions

List overtime decisions for all employees (user)

Returns a paginated list of overtime decisions across all Tempus employees in the PBX tenant (the administrative view). Each item is serialized by the `UserOvertimeDecisionResource`. The caller must be authorized to view all overtime decisions for the tenant, otherwise the request is rejected with 403. Results may be filtered by overtime calculation period, decision type, lock state and company, and are sorted by `-created_at` by default.

`operationId: tempusListUsersOvertimeDecisions`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `filter[overtime_calculation_period_id]` (query, string<uuid>, optional) — Restrict results to a single overtime calculation period.
- `filter[decision_type]` (query, string, optional) — Restrict results to one or more decision types. Accepts a single value or a comma-separated list (for example `manual,automatic`).
- `filter[lock_states]` (query, enum, optional) — Restrict results by lock state. `locked` returns processed (locked) decisions, `unlocked` returns decisions that have not been processed. · enum: locked, unlocked
- `filter[company_id]` (query, string<uuid>, optional) — Restrict results to a single Tempus company.
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page.

**Responses**:
- `200` — Paginated list of overtime decisions for all employees.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "ext_id": "1042",
              "overtime_calculation_period_id": "550e8400-e29b-41d4-a716-446655440002",
              "decision_type": "manual",
              "decision": "pay_out",
              "decision_balance": 36000,
              "decision_overtime": 7200,
              "to_pay": 3600,
              "to_pay_extra": 0,
              "to_increment": 0,
              "to_convert": 1800,
              "to_reset": 0,
              "decision_new_balance": 38400,
              "comment": "Carried forward at the employee's request",
              "locked": false,
              "values_live": false,
              "created": "2024-03-01 08:29:07",
              "overtime_calculation_period": {
                  "id": "550e8400-e29b-41d4-a716-446655440002",
                  "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                  "company_id": "550e8400-e29b-41d4-a716-446655440003",
                  "ext_id": "1042",
                  "start": "2024-03-01T00:00:00Z",
                  "end": "2024-03-31T23:59:59Z",
                  "locked": false,
                  "delete_date": "string?<date-time>"
              }
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The caller is not authorized to view overtime decisions for this tenant.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/overtime-calculation-periods

List overtime calculation periods for a tenant (user)

Returns a paginated list of overtime calculation periods belonging to the PBX tenant, ordered by start date descending. Each item is serialized by the `OvertimeCalculationPeriodResource`. The caller must be authorized to view overtime periods for the tenant, otherwise the request is rejected with 403. Results may be filtered by company, by start/end date bounds and by lock state.

This endpoint is served under the standard `/api/v1` host (not the Tempus host).

`operationId: tempusListOvertimeCalculationPeriods`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `filter[company_id]` (query, string<uuid>, optional) — Restrict results to a single Tempus company.
- `filter[from]` (query, string<date>, optional) — Only include periods whose start date is on or after this date.
- `filter[to]` (query, string<date>, optional) — Only include periods whose end date is on or before this date.
- `filter[locked]` (query, enum, optional) — Filter by lock state. A truthy value returns only locked periods; a falsy value returns all periods that are not locked. · enum: true, false, 1, 0
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page.

**Responses**:
- `200` — Paginated list of overtime calculation periods.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "company_id": "550e8400-e29b-41d4-a716-446655440002",
              "ext_id": "550e8400-e29b-41d4-a716-446655440003",
              "start": "2024-03-01T00:00:00.000000Z",
              "end": "2024-03-31T00:00:00.000000Z",
              "locked": false,
              "delete_date": "string?<date-time>"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The caller is not authorized to view overtime periods for this tenant.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

### Work times

Weekly and required work times and compiled workshifts.

#### GET /{pbx_id}/user/{employee_id}/compiled-user-workshifts

List compiled workshifts for an employee (user)

Returns the paginated list of compiled (precomputed) workshift entries for a
single Tempus employee within the given PBX tenant. Each entry summarizes the
worked time the Tempus engine has rolled up for one calendar day, attributing
it to the employee's effective work shift and work week.

Results are sorted by `date` descending by default and support `from`/`to`
date filtering. The authenticated user may only read their own compiled
workshifts; requesting another employee's data returns 403.

`operationId: tempusListCompiledUserWorkshifts`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the Tempus employee whose compiled workshifts are listed.
- `filter[from]` (query, string<date>, optional) — Include only entries on or after this calendar date (YYYY-MM-DD).
- `filter[to]` (query, string<date>, optional) — Include only entries on or before this calendar date (YYYY-MM-DD).
- `sort` (query, enum, optional) — Sort field. Use `date` for ascending or `-date` for descending. · enum: date, -date
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of records per page (1 to 100).

**Responses**:
- `200` — Paginated collection of compiled workshift entries.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "ext_id": "550e8400-e29b-41d4-a716-446655440002",
              "user_workshift_id": "550e8400-e29b-41d4-a716-446655440003",
              "date": "2024-03-01",
              "workshift_week": 9,
              "worktime": 28800,
              "create_time": "2024-03-01 08:29:07"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user may only view their own compiled workshifts.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/interactive-calendar

Build the interactive calendar for an employee (user)

Builds the interactive home-office and absence calendar for a single Tempus
employee (`ext_id`) over a from/to month-year window within the given PBX
tenant. The window must be valid (the `to` period must be greater than or
equal to `from`) and may not exceed 24 months.

The authenticated user must belong to the same PBX tenant. Viewing another
employee's calendar requires absence-validation or home-office-validation
scope over that employee; otherwise a 403 is returned. The response is a
JSON object whose `data` payload is assembled by the calendar builder and
varies with the requested window.

`operationId: tempusBuildInteractiveCalendar`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.

**Request body** (`application/json`) (required):
```json
{
    "ext_id": "550e8400-e29b-41d4-a716-446655440001",
    "from": {
        "year": 2024,
        "month": 3
    },
    "to": {
        "year": 2024,
        "month": 6
    }
}
```

**Responses**:
- `200` — The assembled interactive calendar for the requested window.
  ```json
  {
      "ext_id": "string<uuid>",
      "month_calendars": [],
      "data": []
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the tenant, or lacks validation scope
over the requested employee's calendar.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/work-times

List weekly work times for employees in the tenant (user)

Returns a paginated map of weekly work-time aggregates for active employees in the PBX tenant. The response uses a hand-rolled Laravel paginator envelope (`data`, `links`, `meta`). The `data` object is keyed by employee UUID; each value is a map keyed by ISO week-start date (`YYYY-MM-DD`) plus a reserved `summary` key holding the totals across the queried range. Time values are formatted strings (`HH:MM:SS`) or `-` when no work shift was compiled for that week, and every one is mirrored by a signed `<field>Seconds` integer. Overtime figures are **balances**: a week below the required time is negative, so the weekly rows always sum to `summary`. Days after today are excluded from the aggregates (they would otherwise book a full requirement against zero worked time), so a still-running overtime period reports the same balance as `GET /{pbx_id}/user/{employee_id}/overtime`. When the authenticated user cannot view other employees (no validator scope) and no `filter[employee_id]` or `user_ids` is supplied, the result is scoped to the caller's own employee record; requesting another employee returns 403. The queried date range defaults to the current month and cannot exceed 100 days.

`operationId: tempusListWorkTimes`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string, required) — UUID of the PBX tenant.
- `filter[from]` (query, string<date>, optional) — Start date of the range (format `Y-m-d`). Defaults to the first day of the current month.
- `filter[to]` (query, string<date>, optional) — End date of the range (format `Y-m-d`). Defaults to today. The range may not exceed 100 days.
- `filter[employee_id]` (query, array, optional) — One or more employee UUIDs to restrict the result to. Accepts a comma-separated string or an array.
- `user_ids` (query, array, optional) — Array of employee UUIDs to restrict the result to.
- `page` (query, integer, optional) — Page number.
- `per_page` (query, integer, optional) — Number of employees per page (1 to 100, default 20).

**Responses**:
- `200` — OK. Paginated envelope. The `data` object is keyed by employee UUID; each value is a weekly map keyed by week-start date plus a `summary` key.
  ```json
  {
      "data": [],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no associated employee record, belongs to a different PBX, or requested work times for an employee they are not allowed to view.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/user/{employee_id}/work-times

List weekly work times for a single employee (user)

Returns the weekly work-time aggregates for one employee. The response is a flat object (NOT wrapped in `data`) keyed by ISO week-start date (`YYYY-MM-DD`) plus a reserved `summary` key holding the totals across the queried range. Time values are formatted strings (`HH:MM:SS`) or `-` when no work shift was compiled for that week, and every one is mirrored by a signed `<field>Seconds` integer. Overtime figures are **balances**: a week below the required time is negative, so the weekly rows always sum to `summary`. Days after today are excluded from the aggregates (they would otherwise book a full requirement against zero worked time), so a still-running overtime period reports the same balance as `GET /{pbx_id}/user/{employee_id}/overtime`. Each entry also includes a `minWorkTimeReached` boolean. The caller may only request their own employee record; requesting another employee returns 403. The queried date range defaults to the current month and cannot exceed 100 days.

`operationId: tempusListUserWorkTimes`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string, required) — UUID of the PBX tenant.
- `employee_id` (path, string, required) — UUID of the employee. Must match the authenticated caller's own employee record.
- `filter[from]` (query, string<date>, optional) — Start date of the range (format `Y-m-d`). Defaults to the first day of the current month.
- `filter[to]` (query, string<date>, optional) — End date of the range (format `Y-m-d`). Defaults to today. The range may not exceed 100 days.

**Responses**:
- `200` — OK. Flat object keyed by week-start date (`YYYY-MM-DD`) plus a reserved `summary` key. NOT wrapped in `data`.
  ```json
  []
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no associated employee record, belongs to a different PBX, or requested an employee other than their own.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/user/{employee_id}/required-work-time

Get daily required work time for a single employee (user)

Returns the contracted (required) work time per calendar day for one employee across the requested range. The response is a flat object (NOT wrapped in `data`) keyed by date (`YYYY-MM-DD`); each value is the required work time for that day in seconds. The figure is derived from the employee's active work-shift assignment and is reduced by approved absences and public holidays (full-day holidays yield 0, half-day holidays halve the requirement). Values are never negative. Both `filter[from]` and `filter[to]` are required and the range may not exceed 100 days. The caller may only request their own employee record; requesting another employee returns 403.

`operationId: tempusGetUserRequiredWorkTime`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string, required) — UUID of the PBX tenant.
- `employee_id` (path, string, required) — UUID of the employee. Must match the authenticated caller's own employee record.
- `filter[from]` (query, string<date>, required) — Start date of the range (format `Y-m-d`).
- `filter[to]` (query, string<date>, required) — End date of the range (format `Y-m-d`). The range may not exceed 100 days.

**Responses**:
- `200` — OK. Flat object keyed by date (`YYYY-MM-DD`); each value is the required work time in seconds for that day. NOT wrapped in `data`.
  ```json
  []
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no associated employee record, belongs to a different PBX, or requested an employee other than their own.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

### Schedules

Managed work schedules, slots, and entry assignments.

#### GET /{pbx_id}/schedules

List schedules for the tenant (user)

Returns a paginated list of active Tempus schedules (planning grids) for the PBX tenant. Only schedules whose owning department belongs to the tenant and whose `is_active` flag is true are returned, ordered alphabetically by name. Each item is a `TempusScheduleSummary` as serialized by the `ScheduleResource`, with its owning department embedded under the `group` key. Results can be narrowed to one or more departments with the `filter[group_id]` query parameter.

`operationId: tempusListSchedules`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `filter[group_id]` (query, string, optional) — Comma-separated list of department (group) UUIDs to restrict results to. Only schedules belonging to these departments are returned.
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page.

**Responses**:
- `200` — Paginated list of active schedules for the tenant.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Customer Support",
              "group_id": "550e8400-e29b-41d4-a716-446655440002",
              "days_to_display": "1111100",
              "slot_min_time": "08:00",
              "slot_max_time": "18:00",
              "slot_duration": "00:30:00",
              "status_id": "550e8400-e29b-41d4-a716-446655440003",
              "sub_status_id": "550e8400-e29b-41d4-a716-446655440004",
              "delete_date": "2024-03-01 08:29:07",
              "group": {
                  "id": "550e8400-e29b-41d4-a716-446655440002",
                  "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Sales Team",
                  "delete_date": "2024-03-01 08:29:07"
              },
              "scope_type": "department",
              "groups": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440002",
                      "name": "Sales Team"
                  }
              ],
              "employees": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440010",
                      "full_name": "Jane Smith",
                      "group_id": "550e8400-e29b-41d4-a716-446655440002",
                      "group_name": "Sales Team"
                  }
              ]
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/schedule-management

List managed schedules for the tenant (user)

Returns a paginated list of the tenant's schedules. Reading is open to any authenticated user of the PBX; write access stays gated by the caller's schedules-management validator scope. Use the `search` parameter to filter by schedule name and `is_active` to filter by active state.

Each item carries its scope: `scopeType` plus the raw `scope` selection, the resolved `departments`, and the `employees` who may be booked into its slots.

`operationId: tempusListManagedSchedules`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `search` (query, string, optional) — Filter schedules whose name contains this string.
- `is_active` (query, boolean, optional) — Filter by active state. Accepts boolean-like values.
- `per_page` (query, integer, optional) — Number of items per page (capped at 100).

**Responses**:
- `200` — Paginated list of managed schedules.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "name": "Sales Team Weekly Plan",
              "department": {
                  "id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Customer Support"
              },
              "scopeType": "department",
              "scope": {
                  "companies": [],
                  "departments": [
                      {
                          "id": "550e8400-e29b-41d4-a716-446655440001",
                          "name": "Customer Support"
                      }
                  ],
                  "employees": []
              },
              "departments": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440001",
                      "name": "Customer Support"
                  }
              ],
              "employees": [
                  {
                      "id": "550e8400-e29b-41d4-a716-446655440010",
                      "fullName": "Jane Smith",
                      "departmentId": "550e8400-e29b-41d4-a716-446655440002",
                      "departmentName": "Customer Support"
                  }
              ],
              "daysOfWeek": [
                  1,
                  2,
                  3,
                  4,
                  5
              ],
              "dayStartTime": "09:00",
              "dayEndTime": "17:30",
              "slotDurationMinutes": 30,
              "status": {
                  "id": "550e8400-e29b-41d4-a716-446655440002",
                  "name": "Available"
              },
              "subStatus": {
                  "id": "550e8400-e29b-41d4-a716-446655440003",
                  "name": "On Break"
              },
              "statusFilters": [
                  "550e8400-e29b-41d4-a716-446655440002",
                  "550e8400-e29b-41d4-a716-446655440003"
              ],
              "isActive": true,
              "createdAt": "2024-03-01T08:29:07+00:00",
              "updatedAt": "2024-03-01T08:29:07+00:00"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the requested tenant, has no associated employee record, or has an empty schedules-management scope.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/schedule-management

Create a schedule (user)

Creates a schedule. Requires a token whose validator holds the schedules-management permission, and every company, department or employee you select must be within that validator scope.

`scope_type` decides who the schedule is for and which selection field is required. It is optional: omit it and the `department_id` you send becomes a single-department scope, which is exactly how this endpoint behaved before scopes existed, so existing integrations need no change.

The schedule's anchor department (`department` in the response, `group_id` on the legacy list endpoint) is derived from the selection. When the selection resolves to no department at all, which happens for a company whose people hold contracts carrying no department, the schedule is saved without one and works normally: its people come from the company. Nothing is refused for lack of a department.

`operationId: tempusCreateManagedSchedule`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.

**Request body** (`application/json`) (required):
```json
{
    "name": "Reception weekdays",
    "scope_type": "department",
    "companies": [
        "550e8400-e29b-41d4-a716-446655440020"
    ],
    "departments": [
        "550e8400-e29b-41d4-a716-446655440001",
        "550e8400-e29b-41d4-a716-446655440002"
    ],
    "employees": [
        "550e8400-e29b-41d4-a716-446655440010",
        "550e8400-e29b-41d4-a716-446655440011"
    ],
    "department_id": "550e8400-e29b-41d4-a716-446655440001",
    "days_of_week": [
        1,
        2,
        3,
        4,
        5
    ],
    "day_start_time": "08:00",
    "day_end_time": "17:00",
    "status_id": "550e8400-e29b-41d4-a716-446655440003",
    "sub_status_id": "string?<uuid>",
    "status_filters": [],
    "is_active": true
}
```

**Responses**:
- `201` — The created schedule.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Sales Team Weekly Plan",
      "department": {
          "id": "550e8400-e29b-41d4-a716-446655440001",
          "name": "Customer Support"
      },
      "scopeType": "department",
      "scope": {
          "companies": [],
          "departments": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Customer Support"
              }
          ],
          "employees": []
      },
      "departments": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Customer Support"
          }
      ],
      "employees": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440010",
              "fullName": "Jane Smith",
              "departmentId": "550e8400-e29b-41d4-a716-446655440002",
              "departmentName": "Customer Support"
          }
      ],
      "daysOfWeek": [
          1,
          2,
          3,
          4,
          5
      ],
      "dayStartTime": "09:00",
      "dayEndTime": "17:30",
      "slotDurationMinutes": 30,
      "status": {
          "id": "550e8400-e29b-41d4-a716-446655440002",
          "name": "Available"
      },
      "subStatus": {
          "id": "550e8400-e29b-41d4-a716-446655440003",
          "name": "On Break"
      },
      "statusFilters": [
          "550e8400-e29b-41d4-a716-446655440002",
          "550e8400-e29b-41d4-a716-446655440003"
      ],
      "isActive": true,
      "createdAt": "2024-03-01T08:29:07+00:00",
      "updatedAt": "2024-03-01T08:29:07+00:00"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The caller has no schedules-management scope, or the selection reaches beyond it. The message is "You do not have permission to manage schedules for this department." for a department (or a department resolved from a selected company) outside the scope, and "You do not have permission to manage this employee." for an employee outside it.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/schedule-management/{schedule}

Get a managed schedule (user)

Returns the detail of a single schedule with its `department`, `status`, and `subStatus` relations resolved, plus its scope: `scopeType`, the raw `scope` selection, the resolved `departments`, and the `employees` who may be booked into its slots. Reading is open to any authenticated user of the PBX; a schedule in another tenant returns 404.

`operationId: tempusGetManagedSchedule`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `schedule` (path, string<uuid>, required) — UUID of the schedule.

**Responses**:
- `200` — The requested schedule.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Sales Team Weekly Plan",
      "department": {
          "id": "550e8400-e29b-41d4-a716-446655440001",
          "name": "Customer Support"
      },
      "scopeType": "department",
      "scope": {
          "companies": [],
          "departments": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Customer Support"
              }
          ],
          "employees": []
      },
      "departments": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Customer Support"
          }
      ],
      "employees": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440010",
              "fullName": "Jane Smith",
              "departmentId": "550e8400-e29b-41d4-a716-446655440002",
              "departmentName": "Customer Support"
          }
      ],
      "daysOfWeek": [
          1,
          2,
          3,
          4,
          5
      ],
      "dayStartTime": "09:00",
      "dayEndTime": "17:30",
      "slotDurationMinutes": 30,
      "status": {
          "id": "550e8400-e29b-41d4-a716-446655440002",
          "name": "Available"
      },
      "subStatus": {
          "id": "550e8400-e29b-41d4-a716-446655440003",
          "name": "On Break"
      },
      "statusFilters": [
          "550e8400-e29b-41d4-a716-446655440002",
          "550e8400-e29b-41d4-a716-446655440003"
      ],
      "isActive": true,
      "createdAt": "2024-03-01T08:29:07+00:00",
      "updatedAt": "2024-03-01T08:29:07+00:00"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the requested tenant.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### PUT /{pbx_id}/schedule-management/{schedule}

Update a schedule (user)

Updates a schedule. Every field is optional: send only what changes. The caller must hold the schedules-management permission and manage at least some of the schedule's people; otherwise a 404 is returned.

Scope: send `scope_type` with the matching selection to change who the schedule is for. The new selection REPLACES the previous one. An update that mentions none of `scope_type`, `companies`, `departments`, `employees` or `department_id` leaves the scope exactly as it was, so a plain rename is safe. `day_start_time` and `day_end_time` must be sent together.

`operationId: tempusUpdateManagedSchedule`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `schedule` (path, string<uuid>, required) — UUID of the schedule.

**Request body** (`application/json`) (required):
```json
{
    "name": "Reception (summer)",
    "scope_type": "employees",
    "companies": [
        "550e8400-e29b-41d4-a716-446655440020"
    ],
    "departments": [
        "550e8400-e29b-41d4-a716-446655440001"
    ],
    "employees": [
        "550e8400-e29b-41d4-a716-446655440010"
    ],
    "department_id": "550e8400-e29b-41d4-a716-446655440001",
    "days_of_week": [
        1,
        2,
        3,
        4,
        5
    ],
    "day_start_time": "08:00",
    "day_end_time": "17:00",
    "status_id": "550e8400-e29b-41d4-a716-446655440003",
    "sub_status_id": "string?<uuid>",
    "status_filters": [],
    "is_active": false
}
```

**Responses**:
- `200` — The updated schedule.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Sales Team Weekly Plan",
      "department": {
          "id": "550e8400-e29b-41d4-a716-446655440001",
          "name": "Customer Support"
      },
      "scopeType": "department",
      "scope": {
          "companies": [],
          "departments": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Customer Support"
              }
          ],
          "employees": []
      },
      "departments": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Customer Support"
          }
      ],
      "employees": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440010",
              "fullName": "Jane Smith",
              "departmentId": "550e8400-e29b-41d4-a716-446655440002",
              "departmentName": "Customer Support"
          }
      ],
      "daysOfWeek": [
          1,
          2,
          3,
          4,
          5
      ],
      "dayStartTime": "09:00",
      "dayEndTime": "17:30",
      "slotDurationMinutes": 30,
      "status": {
          "id": "550e8400-e29b-41d4-a716-446655440002",
          "name": "Available"
      },
      "subStatus": {
          "id": "550e8400-e29b-41d4-a716-446655440003",
          "name": "On Break"
      },
      "statusFilters": [
          "550e8400-e29b-41d4-a716-446655440002",
          "550e8400-e29b-41d4-a716-446655440003"
      ],
      "isActive": true,
      "createdAt": "2024-03-01T08:29:07+00:00",
      "updatedAt": "2024-03-01T08:29:07+00:00"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The caller has no schedules-management scope, or the new selection reaches beyond it. The message is "You do not have permission to manage schedules for this department." for a department (or one resolved from a selected company) outside the scope, and "You do not have permission to manage this employee." for an employee outside it.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### PATCH /{pbx_id}/schedule-management/{schedule}

Partially update a schedule (user)

Updates a schedule. Every field is optional: send only what changes. The caller must hold the schedules-management permission and manage at least some of the schedule's people; otherwise a 404 is returned.

Scope: send `scope_type` with the matching selection to change who the schedule is for. The new selection REPLACES the previous one. An update that mentions none of `scope_type`, `companies`, `departments`, `employees` or `department_id` leaves the scope exactly as it was, so a plain rename is safe. `day_start_time` and `day_end_time` must be sent together.

`operationId: tempusPatchManagedSchedule`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `schedule` (path, string<uuid>, required) — UUID of the schedule.

**Request body** (`application/json`) (required):
```json
{
    "name": "Reception (summer)",
    "scope_type": "employees",
    "companies": [
        "550e8400-e29b-41d4-a716-446655440020"
    ],
    "departments": [
        "550e8400-e29b-41d4-a716-446655440001"
    ],
    "employees": [
        "550e8400-e29b-41d4-a716-446655440010"
    ],
    "department_id": "550e8400-e29b-41d4-a716-446655440001",
    "days_of_week": [
        1,
        2,
        3,
        4,
        5
    ],
    "day_start_time": "08:00",
    "day_end_time": "17:00",
    "status_id": "550e8400-e29b-41d4-a716-446655440003",
    "sub_status_id": "string?<uuid>",
    "status_filters": [],
    "is_active": false
}
```

**Responses**:
- `200` — The updated schedule.
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Sales Team Weekly Plan",
      "department": {
          "id": "550e8400-e29b-41d4-a716-446655440001",
          "name": "Customer Support"
      },
      "scopeType": "department",
      "scope": {
          "companies": [],
          "departments": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Customer Support"
              }
          ],
          "employees": []
      },
      "departments": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Customer Support"
          }
      ],
      "employees": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440010",
              "fullName": "Jane Smith",
              "departmentId": "550e8400-e29b-41d4-a716-446655440002",
              "departmentName": "Customer Support"
          }
      ],
      "daysOfWeek": [
          1,
          2,
          3,
          4,
          5
      ],
      "dayStartTime": "09:00",
      "dayEndTime": "17:30",
      "slotDurationMinutes": 30,
      "status": {
          "id": "550e8400-e29b-41d4-a716-446655440002",
          "name": "Available"
      },
      "subStatus": {
          "id": "550e8400-e29b-41d4-a716-446655440003",
          "name": "On Break"
      },
      "statusFilters": [
          "550e8400-e29b-41d4-a716-446655440002",
          "550e8400-e29b-41d4-a716-446655440003"
      ],
      "isActive": true,
      "createdAt": "2024-03-01T08:29:07+00:00",
      "updatedAt": "2024-03-01T08:29:07+00:00"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The caller has no schedules-management scope, or the new selection reaches beyond it. The message is "You do not have permission to manage schedules for this department." for a department (or one resolved from a selected company) outside the scope, and "You do not have permission to manage this employee." for an employee outside it.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### DELETE /{pbx_id}/schedule-management/{schedule}

Delete a schedule (user)

Soft-deletes a schedule and cascades to its slots. Refused with a 422 while the schedule still has assigned entries: clear those first. The caller must hold the schedules-management permission and manage at least some of the schedule's people.

`operationId: tempusDeleteManagedSchedule`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `schedule` (path, string<uuid>, required) — UUID of the schedule.

**Responses**:
- `200` — The schedule was deleted.
  ```json
  {
      "message": "Schedule deleted successfully."
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The caller has no schedules-management scope.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — The schedule still has assigned entries. Body is `{"message": "This schedule has assigned entries and cannot be deleted. Remove the entries first."}`.

#### GET /{pbx_id}/schedule-management/{schedule}/employees

List the employees a schedule may assign (user)

Returns the people who may be booked into this schedule's slots: its scope resolved for you, whether that scope is whole companies, several departments, or a hand-picked list. Use it to populate the assignment dropdown on the slot create and edit forms. Booking anybody outside this list is refused with a 422.

Only employees holding a contract that is active today are listed, whatever the scope type — a hand-picked selection does not override the contract, so somebody who has left or has not started yet is left out.

Reading only requires a token belonging to the PBX, like the other schedule GET endpoints. The same list is embedded as `employees` on every schedule payload, so a client that already loaded the schedule does not need this call.

This replaces the former approach of calling `GET /{pbx_id}/users?filter[group_id]=<the schedule's group>`, which only ever worked for single-department schedules and under-reports for company and employee scoped ones.

`operationId: tempusGetManagedScheduleEmployees`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `schedule` (path, string<uuid>, required) — UUID of the schedule.

**Responses**:
- `200` — The employees the schedule may assign, ordered by name.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440010",
              "fullName": "Jane Smith",
              "departmentId": "550e8400-e29b-41d4-a716-446655440002",
              "departmentName": "Customer Support"
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the requested tenant.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### GET /{pbx_id}/schedule-management/{schedule}/calendar

Get the weekly calendar for a schedule (user)

Returns a weekly calendar view for the schedule, expanding its recurring slots and booked entries across a single week. The week begins on the Monday of the week containing `week_start`; when omitted it defaults to the current week. The schedule must belong to a department the authenticated user is allowed to manage.

`operationId: tempusGetScheduleCalendar`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `schedule` (path, string<uuid>, required) — UUID of the schedule.
- `week_start` (query, string<date>, optional) — ISO date used to select the week to render.
- `employee_id` (query, string<uuid>, optional) — Optional employee UUID to restrict the calendar to a single employee.

**Responses**:
- `200` — Weekly calendar for the schedule.
  ```json
  {
      "data": {
          "schedule": [],
          "weekStart": "2024-03-04",
          "weekEnd": "2024-03-08",
          "days": [
              {
                  "date": "2024-03-04",
                  "dayLabel": "Mon 04.03.2024",
                  "isToday": false,
                  "slots": [
                      {
                          "slotId": "550e8400-e29b-41d4-a716-446655440000",
                          "renderKey": "550e8400-e29b-41d4-a716-446655440000:550e8400-e29b-41d4-a716-446655440010",
                          "columnIndex": 0,
                          "startTime": "09:00",
                          "endTime": "17:00",
                          "recurrenceType": "weekly",
                          "recurrenceDays": [
                              1,
                              2,
                              3,
                              4,
                              5
                          ],
                          "recurrenceStartDate": "2024-01-01",
                          "recurrenceEndDate": "2024-12-31",
                          "entry": {
                              "entryId": "\u2026",
                              "employeeId": "\u2026",
                              "userName": "\u2026",
                              "color": "\u2026",
                              "comment": "\u2026",
                              "isException": "\u2026"
                          }
                      }
                  ]
              }
          ]
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the requested tenant, has no associated employee record, or has an empty schedules-management scope.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/schedule-management/{schedule}/colors

List employee colors for a schedule (user)

Returns the per-employee hex colors configured for a schedule. These colors are used to tint each employee's entries on the calendar grid. The schedule must belong to a department the authenticated user is allowed to manage.

`operationId: tempusListScheduleColors`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `schedule` (path, string<uuid>, required) — UUID of the schedule.

**Responses**:
- `200` — List of employee colors for the schedule.
  ```json
  {
      "data": [
          {
              "employeeId": "550e8400-e29b-41d4-a716-446655440002",
              "employeeName": "Jane Smith",
              "colorHex": "#4F46E5"
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the requested tenant, has no associated employee record, or has an empty schedules-management scope.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### PUT /{pbx_id}/schedule-management/{schedule}/colors/{employee}

Set an employee color for a schedule (user)

Creates or updates the hex color assigned to an employee within a schedule. The schedule must belong to a department the authenticated user manages, and the employee must be within the caller's schedules-management scope.

`operationId: tempusSetScheduleColor`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `schedule` (path, string<uuid>, required) — UUID of the schedule.
- `employee` (path, string<uuid>, required) — UUID of the Tempus employee.

**Request body** (`application/json`) (required):
```json
{
    "color_hex": "#4F46E5"
}
```

**Responses**:
- `200` — The color was created or updated.
  ```json
  {
      "message": "Color updated."
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the requested tenant or the employee is not within the caller's schedules-management scope.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/schedule-management/{schedule}/slots

Create a slot in a schedule (user)

Creates a recurring time slot within a schedule. The slot's start and end times must fall within the schedule's daily bounds, and the column index is assigned automatically when not supplied. The schedule must belong to a department the authenticated user is allowed to manage.

`operationId: tempusCreateScheduleSlot`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `schedule` (path, string<uuid>, required) — UUID of the schedule.

**Request body** (`application/json`) (required):
```json
{
    "column_index": 0,
    "start_time": "09:00",
    "end_time": "17:00",
    "recurrence_type": "weekly",
    "recurrence_days": [
        1,
        2,
        3,
        4,
        5
    ],
    "recurrence_start_date": "2026-03-02",
    "recurrence_end_date": "2026-12-31",
    "recurrence_count": 60
}
```

**Responses**:
- `201` — The slot was created.
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "scheduleId": "550e8400-e29b-41d4-a716-446655440001",
          "columnIndex": 0,
          "startTime": "09:00",
          "endTime": "17:30",
          "recurrenceType": "weekly",
          "recurrenceDays": [
              1,
              2,
              3,
              4,
              5
          ],
          "recurrenceStartDate": "2024-03-01",
          "recurrenceEndDate": "2024-12-31",
          "recurrenceCount": 12,
          "isActive": true,
          "createdBy": {
              "id": "550e8400-e29b-41d4-a716-446655440002",
              "name": "Customer Support"
          }
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the requested tenant, has no associated employee record, or has an empty schedules-management scope.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### PUT /{pbx_id}/schedule-management/{schedule}/slots/{slot}

Update a schedule slot (user)

Updates an existing slot. Any subset of the slot fields may be supplied. Start and end times, when provided, must remain within the schedule's daily bounds. The schedule must belong to a department the authenticated user manages, and the slot must belong to that schedule.

`operationId: tempusUpdateScheduleSlot`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `schedule` (path, string<uuid>, required) — UUID of the schedule.
- `slot` (path, string<uuid>, required) — UUID of the slot.

**Request body** (`application/json`) (required):
```json
{
    "column_index": 0,
    "start_time": "09:00",
    "end_time": "17:00",
    "recurrence_type": "weekly",
    "recurrence_days": [
        1,
        2,
        3,
        4,
        5
    ],
    "recurrence_start_date": "2026-03-02",
    "recurrence_end_date": "2026-12-31",
    "recurrence_count": 60,
    "is_active": true
}
```

**Responses**:
- `200` — The slot was updated.
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "scheduleId": "550e8400-e29b-41d4-a716-446655440001",
          "columnIndex": 0,
          "startTime": "09:00",
          "endTime": "17:30",
          "recurrenceType": "weekly",
          "recurrenceDays": [
              1,
              2,
              3,
              4,
              5
          ],
          "recurrenceStartDate": "2024-03-01",
          "recurrenceEndDate": "2024-12-31",
          "recurrenceCount": 12,
          "isActive": true,
          "createdBy": {
              "id": "550e8400-e29b-41d4-a716-446655440002",
              "name": "Customer Support"
          }
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the requested tenant, has no associated employee record, or has an empty schedules-management scope.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### DELETE /{pbx_id}/schedule-management/{schedule}/slots/{slot}

Delete a schedule slot (user)

Deletes a slot and all of its entries. The schedule must belong to a department the authenticated user manages, and the slot must belong to that schedule.

`operationId: tempusDeleteScheduleSlot`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `schedule` (path, string<uuid>, required) — UUID of the schedule.
- `slot` (path, string<uuid>, required) — UUID of the slot.

**Responses**:
- `204` — The slot and its entries were deleted.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the requested tenant, has no associated employee record, or has an empty schedules-management scope.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### POST /{pbx_id}/schedule-management/slots/{slot}/entries

Create an entry in a slot (user)

Books an employee into a slot for a given time window. When `apply_to_all` is true the booking is applied across all occurrences of the recurring slot and a collection of entries is returned, optionally with a `warning` describing skipped occurrences; otherwise a single entry is returned. The employee must be within the caller's schedules-management scope, and the slot must belong to a schedule in a department the caller manages.

`operationId: tempusCreateScheduleEntry`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `slot` (path, string<uuid>, required) — UUID of the slot.

**Request body** (`application/json`) (required):
```json
{
    "employee_id": "550e8400-e29b-41d4-a716-446655440002",
    "start_datetime": "2026-03-02T09:00:00",
    "end_datetime": "2026-03-02T17:00:00",
    "apply_to_all": false,
    "comment": "Covering the morning desk"
}
```

**Responses**:
- `201` — The entry was created. Returns a single entry when apply_to_all is false, or a collection (optionally with a warning) when apply_to_all is true.
  ```json
  [
      {
          "data": {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "slotId": "550e8400-e29b-41d4-a716-446655440001",
              "scheduleId": "550e8400-e29b-41d4-a716-446655440002",
              "employee": {
                  "id": "550e8400-e29b-41d4-a716-446655440003",
                  "fullName": "Jane Smith"
              },
              "color": "#3B82F6",
              "startDatetime": "2024-03-01T08:00:00+00:00",
              "endDatetime": "2024-03-01T16:30:00+00:00",
              "comment": "Covering the morning reception shift",
              "isException": false,
              "assignedBy": {
                  "id": "550e8400-e29b-41d4-a716-446655440004",
                  "name": "Customer Support"
              },
              "assignedAt": "2024-03-01T07:45:12+00:00"
          }
      },
      {
          "data": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "slotId": "550e8400-e29b-41d4-a716-446655440001",
                  "scheduleId": "550e8400-e29b-41d4-a716-446655440002",
                  "employee": {
                      "id": "550e8400-e29b-41d4-a716-446655440003",
                      "fullName": "Jane Smith"
                  },
                  "color": "#3B82F6",
                  "startDatetime": "2024-03-01T08:00:00+00:00",
                  "endDatetime": "2024-03-01T16:30:00+00:00",
                  "comment": "Covering the morning reception shift",
                  "isException": false,
                  "assignedBy": {
                      "id": "550e8400-e29b-41d4-a716-446655440004",
                      "name": "Customer Support"
                  },
                  "assignedAt": "2024-03-01T07:45:12+00:00"
              }
          ],
          "warning": "2 occurrences were skipped due to conflicts."
      }
  ]
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the requested tenant, or the employee is not within the caller's schedules-management scope.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Validation failed, or the employee is not part of the schedule. A schedule covers a set of companies, departments or hand-picked people, and only those may be booked into its slots: assigning anybody else returns `{"message": "This employee is not part of the schedule. Pick one of the schedule's employees."}`. Take the assignable list from the schedule's `employees` field or from `GET /{pbx_id}/schedule-management/{schedule}/employees`.

#### POST /{pbx_id}/schedule-management/slots/{slot}/auto-assign

Auto-assign the best employee to a slot (user)

Selects the most eligible employee for a slot over the given time window and books them into it, returning the created entry. The chosen employee must be within the caller's schedules-management scope; if no eligible in-scope employee is found a 404 is returned. The slot must belong to a schedule in a department the caller manages.

`operationId: tempusAutoAssignScheduleEntry`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `slot` (path, string<uuid>, required) — UUID of the slot.

**Request body** (`application/json`) (required):
```json
{
    "start_datetime": "2026-03-02T09:00:00",
    "end_datetime": "2026-03-02T17:00:00"
}
```

**Responses**:
- `201` — The entry was created for the auto-selected employee.
  ```json
  {
      "data": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "slotId": "550e8400-e29b-41d4-a716-446655440001",
          "scheduleId": "550e8400-e29b-41d4-a716-446655440002",
          "employee": {
              "id": "550e8400-e29b-41d4-a716-446655440003",
              "fullName": "Jane Smith"
          },
          "color": "#3B82F6",
          "startDatetime": "2024-03-01T08:00:00+00:00",
          "endDatetime": "2024-03-01T16:30:00+00:00",
          "comment": "Covering the morning reception shift",
          "isException": false,
          "assignedBy": {
              "id": "550e8400-e29b-41d4-a716-446655440004",
              "name": "Customer Support"
          },
          "assignedAt": "2024-03-01T07:45:12+00:00"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the requested tenant, has no associated employee record, or has an empty schedules-management scope.
- `404` — The slot was not found within the caller's scope, or no eligible in-scope employee could be assigned.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### PUT /{pbx_id}/schedule-management/entries/{entry}

Update a schedule entry (user)

Updates an existing entry. Any subset of the entry fields may be supplied. When `apply_to_all` is true the change is propagated across the recurring series and a collection is returned; otherwise a single entry is returned. When changing the employee, the new employee must be within the caller's schedules-management scope. The entry must belong to a schedule in a department the caller manages.

`operationId: tempusUpdateScheduleEntry`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `entry` (path, string<uuid>, required) — UUID of the entry.

**Request body** (`application/json`) (required):
```json
{
    "employee_id": "550e8400-e29b-41d4-a716-446655440002",
    "start_datetime": "2026-03-02T09:00:00",
    "end_datetime": "2026-03-02T17:00:00",
    "apply_to_all": false,
    "comment": "Covering the morning desk"
}
```

**Responses**:
- `200` — The entry was updated. Returns a single entry when apply_to_all is false, or a collection when apply_to_all is true.
  ```json
  [
      {
          "data": {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "slotId": "550e8400-e29b-41d4-a716-446655440001",
              "scheduleId": "550e8400-e29b-41d4-a716-446655440002",
              "employee": {
                  "id": "550e8400-e29b-41d4-a716-446655440003",
                  "fullName": "Jane Smith"
              },
              "color": "#3B82F6",
              "startDatetime": "2024-03-01T08:00:00+00:00",
              "endDatetime": "2024-03-01T16:30:00+00:00",
              "comment": "Covering the morning reception shift",
              "isException": false,
              "assignedBy": {
                  "id": "550e8400-e29b-41d4-a716-446655440004",
                  "name": "Customer Support"
              },
              "assignedAt": "2024-03-01T07:45:12+00:00"
          }
      },
      {
          "data": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "slotId": "550e8400-e29b-41d4-a716-446655440001",
                  "scheduleId": "550e8400-e29b-41d4-a716-446655440002",
                  "employee": {
                      "id": "550e8400-e29b-41d4-a716-446655440003",
                      "fullName": "Jane Smith"
                  },
                  "color": "#3B82F6",
                  "startDatetime": "2024-03-01T08:00:00+00:00",
                  "endDatetime": "2024-03-01T16:30:00+00:00",
                  "comment": "Covering the morning reception shift",
                  "isException": false,
                  "assignedBy": {
                      "id": "550e8400-e29b-41d4-a716-446655440004",
                      "name": "Customer Support"
                  },
                  "assignedAt": "2024-03-01T07:45:12+00:00"
              }
          ]
      }
  ]
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the requested tenant, or the target employee is not within the caller's schedules-management scope.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Validation failed, or the employee is not part of the schedule. A schedule covers a set of companies, departments or hand-picked people, and only those may be booked into its slots: assigning anybody else returns `{"message": "This employee is not part of the schedule. Pick one of the schedule's employees."}`. Take the assignable list from the schedule's `employees` field or from `GET /{pbx_id}/schedule-management/{schedule}/employees`.

#### DELETE /{pbx_id}/schedule-management/entries/{entry}

Delete a schedule entry (user)

Deletes an entry. When `apply_to_all` is true the entire recurring series is removed; otherwise only the single occurrence is deleted. The entry must belong to a schedule in a department the caller manages.

`operationId: tempusDeleteScheduleEntry`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `entry` (path, string<uuid>, required) — UUID of the entry.

**Request body** (`application/json`):
```json
{
    "apply_to_all": false
}
```

**Responses**:
- `204` — The entry was deleted.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the requested tenant, has no associated employee record, or has an empty schedules-management scope.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### PATCH /{pbx_id}/schedule-management/entries/{entry}/move

Move a schedule entry to a new time (user)

Reschedules an entry to a new start and end datetime. The move is rejected with 422 when the new window overlaps another booking for the same employee in the same department. When `apply_to_all` is false the moved occurrence is marked as an exception and a single entry is returned; when true the change is propagated across the recurring series and a collection is returned. The entry must belong to a schedule in a department the caller manages.

`operationId: tempusMoveScheduleEntry`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `entry` (path, string<uuid>, required) — UUID of the entry.

**Request body** (`application/json`) (required):
```json
{
    "start_datetime": "2026-03-03T09:00:00",
    "end_datetime": "2026-03-03T17:00:00",
    "apply_to_all": false
}
```

**Responses**:
- `200` — The entry was moved. Returns a single entry when apply_to_all is false, or a collection when apply_to_all is true.
  ```json
  [
      {
          "data": {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "slotId": "550e8400-e29b-41d4-a716-446655440001",
              "scheduleId": "550e8400-e29b-41d4-a716-446655440002",
              "employee": {
                  "id": "550e8400-e29b-41d4-a716-446655440003",
                  "fullName": "Jane Smith"
              },
              "color": "#3B82F6",
              "startDatetime": "2024-03-01T08:00:00+00:00",
              "endDatetime": "2024-03-01T16:30:00+00:00",
              "comment": "Covering the morning reception shift",
              "isException": false,
              "assignedBy": {
                  "id": "550e8400-e29b-41d4-a716-446655440004",
                  "name": "Customer Support"
              },
              "assignedAt": "2024-03-01T07:45:12+00:00"
          }
      },
      {
          "data": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "slotId": "550e8400-e29b-41d4-a716-446655440001",
                  "scheduleId": "550e8400-e29b-41d4-a716-446655440002",
                  "employee": {
                      "id": "550e8400-e29b-41d4-a716-446655440003",
                      "fullName": "Jane Smith"
                  },
                  "color": "#3B82F6",
                  "startDatetime": "2024-03-01T08:00:00+00:00",
                  "endDatetime": "2024-03-01T16:30:00+00:00",
                  "comment": "Covering the morning reception shift",
                  "isException": false,
                  "assignedBy": {
                      "id": "550e8400-e29b-41d4-a716-446655440004",
                      "name": "Customer Support"
                  },
                  "assignedAt": "2024-03-01T07:45:12+00:00"
              }
          ]
      }
  ]
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is outside the requested tenant, has no associated employee record, or has an empty schedules-management scope.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

### Calendar

Company and per-employee absence, home office, and personal calendars.

#### GET /{pbx_id}/calendar/absence

Company absence calendar

Lightweight absence calendar for the caller's company: every colleague's absence, illness, special and legal-holiday bookings in the `pending`, `approved` and `pending_removal` states. Any employee in the company may read it (a team availability view); this is intentionally not scoped to the caller's own records and carries only the fields a calendar needs - full request detail lives on the `absence-requests` endpoints. The response is a flat array, not wrapped in a `data` envelope and not paginated. Cancellation-in-progress rows appear once (the original, as `pending_removal`); the internal removal credit is omitted.

`operationId: tempusAbsenceCalendar`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `filter[department_id]` (query, string, optional) — Narrow to one or more departments (comma-separated), by the employee's active contract. Omit for the whole company.
- `filter[from]` (query, string<date>, optional) — Only events overlapping on or after this date (`Y-m-d`).
- `filter[to]` (query, string<date>, optional) — Only events overlapping on or before this date (`Y-m-d`).

**Responses**:
- `200` — The company absence calendar. Flat array, not wrapped in data, not paginated.
  ```json
  [
      {
          "id": "550e8400-e29b-41d4-a716-446655440030",
          "employee_id": "550e8400-e29b-41d4-a716-446655440001",
          "employee_name": "Jane Smith",
          "department_id": "550e8400-e29b-41d4-a716-446655440042",
          "type": "absence",
          "sub_type": "string?",
          "status": "approved",
          "start": "2026-07-07",
          "end": "2026-07-09",
          "total_seconds": 86400,
          "days": [
              {
                  "date": "2026-07-07",
                  "seconds": 28800
              }
          ],
          "working_days": 3,
          "workshift_seconds_per_day": 28800,
          "is_half_day": false,
          "starts_half_day": false,
          "ends_half_day": false,
          "start_time": "string?",
          "end_time": "string?",
          "working_day_details": [
              {
                  "date": "2026-07-07",
                  "seconds": 28800,
                  "is_half_day": false,
                  "start_time": "string?",
                  "end_time": "string?"
              }
          ]
      }
  ]
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The caller belongs to another tenant, has no employee record, or has no active company.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/calendar/home-office

Company home office calendar

Lightweight home office calendar for the caller's company: every colleague's home office bookings in the `pending`, `approved` and `pending_removal` states. Any employee in the company may read it (a team availability view); this is intentionally not scoped to the caller's own records and carries only the fields a calendar needs - full request detail lives on the `home-office-requests` endpoints. The response is a flat array, not wrapped in a `data` envelope and not paginated. Cancellation-in-progress rows appear once (the original, as `pending_removal`); the internal removal credit is omitted.

`operationId: tempusHomeOfficeCalendar`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `filter[department_id]` (query, string, optional) — Narrow to one or more departments (comma-separated), by the employee's active contract. Omit for the whole company.
- `filter[from]` (query, string<date>, optional) — Only events overlapping on or after this date (`Y-m-d`).
- `filter[to]` (query, string<date>, optional) — Only events overlapping on or before this date (`Y-m-d`).

**Responses**:
- `200` — The company home office calendar. Flat array, not wrapped in data, not paginated.
  ```json
  [
      {
          "id": "550e8400-e29b-41d4-a716-446655440030",
          "employee_id": "550e8400-e29b-41d4-a716-446655440001",
          "employee_name": "Jane Smith",
          "department_id": "550e8400-e29b-41d4-a716-446655440042",
          "type": "home_office",
          "sub_type": "string?",
          "status": "approved",
          "start": "2026-07-07",
          "end": "2026-07-07",
          "total_seconds": 28800,
          "days": [
              {
                  "date": "2026-07-07",
                  "seconds": 28800
              }
          ],
          "working_days": 1,
          "workshift_seconds_per_day": 28800,
          "is_half_day": false,
          "starts_half_day": false,
          "ends_half_day": false,
          "start_time": "string?",
          "end_time": "string?",
          "working_day_details": [
              {
                  "date": "2026-07-07",
                  "seconds": 28800,
                  "is_half_day": false,
                  "start_time": "string?",
                  "end_time": "string?"
              }
          ]
      }
  ]
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The caller belongs to another tenant, has no employee record, or has no active company.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/user/calendar

Employee calendar for the logged-in user

Returns the data behind the Tempus "Employee Calendar" page for the
**logged-in user**, for a single year: the summary block, the per-company
rights breakdown (`details`), contract periods, and a fully-classified
day-by-day `year_calendar`. Plain data only: no colours, icons or theme;
the client owns presentation.

The employee is resolved from the Sanctum token; there is no employee
parameter and a caller can only ever read their own calendar. The only
input is the `year` (bounded to the employee's contract history through
next year).

Per-day `type` merge priority (highest first): `no_contract` >
`special_holiday` > transaction (non-home-office) > `public_holiday` >
`weekend` > `work`. Home office is an orthogonal flag, never the winning
type.

`operationId: tempusGetUserCalendar`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required) — Identifier of the Pbx (tenant).
- `year` (query, integer, optional) — Calendar year to return. Must fall within the employee's
`[minYear, maxYear]` bounds (earliest contract year … current year + 1).
Defaults to the current year. Out-of-range values return 422.

**Responses**:
- `200` — The employee's calendar for the requested year.
  ```json
  {
      "year": 2026,
      "year_bounds": {
          "min": 2023,
          "max": 2027
      },
      "summary": {
          "totals": {
              "rights": "integer",
              "taken": "integer",
              "remaining": "integer",
              "compensatory": "integer",
              "available": "integer"
          },
          "cards": [
              {
                  "key": "enum",
                  "title": "string?",
                  "value": "string?",
                  "raw_seconds": "integer",
                  "percentage": "number?",
                  "warning": "boolean",
                  "comment": "string?"
              }
          ],
          "handle_compensatory_balance": "boolean",
          "workshift_missing": "boolean"
      },
      "details": {
          "total_seconds": "integer",
          "total_display": "string",
          "companies": [
              {
                  "company_id": "string?<uuid>",
                  "company": "string?",
                  "period_label": "string?",
                  "periods": [
                      {
                          "start": "string<date>",
                          "end": "string<date>"
                      }
                  ],
                  "show_period": "boolean",
                  "switched_on": "string?",
                  "subtotal_seconds": "integer",
                  "subtotal_display": "string?",
                  "rows": [
                      {
                          "type": "string?",
                          "type_key": "string?",
                          "description": "string?",
                          "rights_seconds": "integer",
                          "rights_display": "string?"
                      }
                  ]
              }
          ]
      },
      "contract_periods": [
          {
              "start": "string<date>",
              "end": "string<date>"
          }
      ],
      "daily_expected_seconds": 28800,
      "year_calendar": []
  }
  ```
- `401` — Unauthenticated.
- `403` — The authenticated user has no employee record in this Pbx.
- `422` — The requested year is outside the employee's allowed range.

### Profile

Employee profile, holidays reports, associated companies, and next availability.

#### GET /v2/{pbx}/employee/{employee}/profile

Get your own Tempus profile (user)

The caller's own Tempus profile: their active contract, balances, home office
settings, preferences, and what they validate.

Same payload as `GET /tempus/api/v1/{pbx_id}/user/{employee_id}/profile` with one
difference, which is why it is versioned: the `validator` block is the v2 shape. It
always has the same keys (`is_validator`, `permissions`, `pending`) instead of being
an object or null, and it drops the `companies`, `departments` and `employees` lists,
which approval chains cannot produce cheaply. That roster is on
`GET /tempus/api/v1/{pbx_id}/validator/employees`.

The path says `employee`, not v1's `user`: the subject is a Tempus employee record,
and v1 passed an employee id to a segment named `user`.

422 when the employee has no active contract, since almost every figure in the
payload is derived from one.

`operationId: tempusV2ShowEmployeeProfile`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx` (path, string<uuid>, required) — UUID of the PBX tenant. Must be the tenant the token belongs to; anything else is
refused with 403 rather than answered from another tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the Tempus employee whose queue this is. Must be the caller's own employee
record: a queue is built from the approval rows addressed to one person, so another
employee's id is refused rather than quietly answered with your own.

**Responses**:
- `200` — OK. The employee's profile.
  ```json
  {
      "employee": {
          "full_name": "Jane Smith",
          "email": "jane.smith@example.com"
      },
      "company": {
          "id": "550e8400-e29b-41d4-a716-446655440001",
          "name": "Acme Corp"
      },
      "contract": {
          "start": "2024-01-01",
          "end": "string?<date>",
          "department_id": "550e8400-e29b-41d4-a716-446655440042"
      },
      "absence": {
          "possible": "200:00:00",
          "taken": "80:00:00",
          "remaining": "120:00:00"
      },
      "home_office": {
          "enabled": true,
          "allowed": 20,
          "taken": 5,
          "remaining": 15
      },
      "overtime": {
          "enabled": true,
          "decision_enabled": true,
          "compensatory_balance": "12:30:00",
          "compensatory_balance_seconds": 45000,
          "decision_preference": {
              "level": "employee",
              "preferred_decision": "increment_up_to",
              "auto_decision_policy": "skip_if_manual_decision",
              "max_seconds": 36000,
              "rest_policy": "add_to_balance",
              "policy_reason": "Per HR overtime policy."
          },
          "current_period": {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "start": "2026-01-01",
              "end": "2026-12-31",
              "decision_possible_from": "2026-12-24",
              "decision_possible_to": "2026-12-31"
          },
          "current_accrual": {
              "business": 7200,
              "business_formatted": "02:00:00",
              "non_working": 3600,
              "non_working_formatted": "01:00:00",
              "total": 10800,
              "total_formatted": "03:00:00"
          }
      },
      "travel_time": {
          "enabled": true,
          "minutes_per_request": 30,
          "max_per_day": 1,
          "used_today": 0,
          "remaining_today": 1
      },
      "travel_time_allowed": true,
      "preferences": {
          "date_format": "d.m.Y"
      },
      "validator": {
          "is_validator": true,
          "permissions": {
              "absence_requests": true,
              "home_office_requests": true,
              "time_point_corrections": false,
              "schedule_assignment_requests": false
          },
          "pending": {
              "absence_requests": 3,
              "home_office_requests": 1,
              "time_point_corrections": 0,
              "schedule_assignment_requests": 0
          }
      },
      "validators": [
          {
              "employee_id": "550e8400-e29b-41d4-a716-446655440010",
              "user_id": "550e8400-e29b-41d4-a716-446655440011",
              "name": "Jane Smith",
              "scopes": [
                  "holiday_requests",
                  "time_point_corrections_requests"
              ]
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Forbidden. The caller is outside this PBX, or asked for an employee record that is
not their own.
- `404` — Not found. No such employee in this PBX.
- `422` — Unprocessable. The employee has no active contract, so there is no profile to
assemble.

#### GET /{pbx_id}/user/{employee_id}/next-availability

Get the next availability date for an employee (user)

Searches forward from a start moment for the next time the employee is available and returns it. The response is a flat object and is not wrapped in a `data` envelope. When the employee is already available at that moment, `next_available_date` is `null` and `available_now` is `true` : there is no *future* availability to report, so a date equal to the current/`from` day is never returned for that case.
The search is **time-aware**: a half day off only blocks the part of the day it covers. A morning absence (e.g. 08:00–12:00) that is under way returns **today** together with `next_available_at` = the moment it ends; an afternoon one reads as available *before* it starts and returns the next working day (date only) from the moment it does. `message` is a ready-to-display line for either case.
When no available date is found within the search window (and the employee is not available now), the endpoint responds with 404 and a flat body reporting the searched range. A 404 is also returned when the employee cannot be found for the tenant.

`operationId: tempusGetUserNextAvailability`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee whose next availability is requested.
- `from` (query, string, optional) — Moment the availability search starts from. Defaults to **now** when omitted. A bare date evaluates from that day's 00:00; pass a date-time (`YYYY-MM-DD HH:MM:SS`) to ask "is the employee available at this moment?" — half days are honoured to the clock.
- `max_days` (query, integer, optional) — Maximum number of days to search forward. Defaults to 90 when omitted.

**Responses**:
- `200` — The next available date for the employee. Flat object, not wrapped in data.
  ```json
  {
      "success": "boolean",
      "next_available_date": "string<date>",
      "next_available_at": "string",
      "message": "string",
      "available_now": "boolean",
      "current": {
          "date": "string<date>",
          "available": "boolean",
          "reason": "string",
          "absence": {
              "type": "string",
              "sub_type": "string",
              "status": "string",
              "start": "string<date>",
              "end": "string<date>"
          },
          "absence_window": {
              "start": "string",
              "end": "string",
              "part": "enum"
          }
      },
      "employee_id": "string<uuid>",
      "checked_from": "string<date>",
      "checked_from_at": "string",
      "checked_until": "string<date>"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — No available date was found within the search window, or the employee could not be found for the tenant. The body is a flat object reporting the searched range.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/user/{employee_id}/public-holidays

List public holidays for an employee (user)

Returns a paginated list of public holidays relevant to the employee. The countries are derived from the employee's contracts that overlap the requested date range, and holidays are returned for those countries. When no date filters are supplied the range defaults to the current calendar year. Filtering and sorting are powered by Spatie Query Builder.

`operationId: tempusListUserPublicHolidays`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee whose public holidays are requested.
- `filter[from]` (query, string<date>, optional) — Return holidays on or after this date. Defaults to the start of the current year.
- `filter[to]` (query, string<date>, optional) — Return holidays on or before this date. Defaults to the end of the current year.
- `sort` (query, string, optional) — Sort field. Allowed: date. Prefix with - for descending.
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page.

**Responses**:
- `200` — Paginated list of public holidays for the employee's countries.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "New Year's Day",
              "date": "2024-01-01T00:00:00+00:00",
              "halfday": "fullday",
              "country_cc": "US",
              "delete_date": "string?<date-time>"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/user/{employee_id}/profile

Get the Tempus profile for an employee (user)

Returns the consolidated Tempus profile for a single employee, assembled from the employee's active contract and company: the company, the contract dates, basic employee contact fields, and per-feature summaries (absence, home office allowance, overtime). The caller may only view their own profile: the request is rejected with 403 when the authenticated user has no access to the tenant or the employee record is not their own, with 404 when the employee or its linked user account cannot be found, and with 422 when the employee has no active contract to build a profile from.

`operationId: tempusGetUserProfile`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee whose profile is requested.

**Responses**:
- `200` — The assembled Tempus profile for the employee.
  ```json
  {
      "employee": {
          "full_name": "Jane Smith",
          "email": "jane.smith@example.com"
      },
      "company": {
          "id": "550e8400-e29b-41d4-a716-446655440001",
          "name": "Acme Corp"
      },
      "contract": {
          "start": "2024-01-01",
          "end": "string?<date>",
          "department_id": "550e8400-e29b-41d4-a716-446655440042"
      },
      "absence": {
          "possible": "200:00:00",
          "taken": "80:00:00",
          "remaining": "120:00:00"
      },
      "home_office": {
          "enabled": true,
          "allowed": 20,
          "taken": 5,
          "remaining": 15
      },
      "overtime": {
          "enabled": true,
          "decision_enabled": true,
          "compensatory_balance": "12:30:00",
          "compensatory_balance_seconds": 45000,
          "decision_preference": {
              "level": "employee",
              "preferred_decision": "increment_up_to",
              "auto_decision_policy": "skip_if_manual_decision",
              "max_seconds": 36000,
              "rest_policy": "add_to_balance",
              "policy_reason": "Per HR overtime policy."
          },
          "current_period": {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "start": "2026-01-01",
              "end": "2026-12-31",
              "decision_possible_from": "2026-12-24",
              "decision_possible_to": "2026-12-31"
          },
          "current_accrual": {
              "business": 7200,
              "business_formatted": "02:00:00",
              "non_working": 3600,
              "non_working_formatted": "01:00:00",
              "total": 10800,
              "total_formatted": "03:00:00"
          }
      },
      "travel_time": {
          "enabled": true,
          "minutes_per_request": 30,
          "max_per_day": 1,
          "used_today": 0,
          "remaining_today": 1
      },
      "travel_time_allowed": true,
      "preferences": {
          "date_format": "d.m.Y"
      },
      "validator": {
          "permissions": {
              "holiday_requests": "boolean",
              "home_office_requests": "boolean",
              "time_point_corrections_requests": "boolean",
              "schedules_management": "boolean"
          },
          "pending": {
              "holiday_requests": 3,
              "home_office_requests": 1,
              "time_point_corrections_requests": 0,
              "schedules_management": 0
          },
          "companies": [
              {
                  "id": "string<uuid>",
                  "name": "string"
              }
          ],
          "departments": [
              {
                  "id": "string<uuid>",
                  "name": "string"
              }
          ],
          "employees": [
              {
                  "employee_id": "string<uuid>",
                  "name": "string"
              }
          ]
      },
      "validators": [
          {
              "employee_id": "550e8400-e29b-41d4-a716-446655440010",
              "user_id": "550e8400-e29b-41d4-a716-446655440011",
              "name": "Jane Smith",
              "scopes": [
                  "holiday_requests",
                  "time_point_corrections_requests"
              ]
          }
      ]
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no access to the requested tenant, or the requested employee record does not belong to the authenticated user.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — The employee has no active contract, so no profile can be produced.

#### GET /{pbx_id}/user/{employee_id}/travel-time

List an employee's travel time credits

Returns the travel time credits recorded for the employee in the given date range, newest first, plus the current allowance in `meta`. Defaults to the current month when `from` and `to` are omitted. The caller may only read their own credits: the request is rejected with 403 when the authenticated user has no access to the tenant or the employee record is not their own, and with 404 when the employee cannot be found. When travel time is not configured for the PBX the list is empty and `meta.enabled` reflects the employee's own flag.

`operationId: tempusListUserTravelTime`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee whose credits are requested.
- `from` (query, string<date>, optional) — Start of the range, `Y-m-d`. Defaults to the first day of the current month.
- `to` (query, string<date>, optional) — End of the range, `Y-m-d`. Must not be earlier than `from`. Defaults to today.

**Responses**:
- `200` — The employee's travel time credits and current allowance.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440010",
              "date": "2026-05-11",
              "start_time": "2026-05-11 07:30:00",
              "end_time": "2026-05-11 08:00:00",
              "minutes": 30,
              "status_id": "550e8400-e29b-41d4-a716-446655440011",
              "timepoint_id": "550e8400-e29b-41d4-a716-446655440012",
              "comment": "Travel time requested",
              "created_at": "2026-05-11 12:00:00"
          }
      ],
      "meta": {
          "enabled": true,
          "minutes_per_request": 30,
          "max_per_day": 1,
          "used_today": 1,
          "remaining_today": 0
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no access to the requested tenant, or the requested employee record does not belong to the authenticated user.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — The `from` or `to` query parameter is malformed, or `to` precedes `from`.

#### POST /{pbx_id}/user/{employee_id}/travel-time

Credit travel time to a day

Credits the employee's configured travel time allowance to a date and returns
the time point created.

The minutes are inserted immediately BEFORE the first status of that day,
ending exactly where that first status begins, so the working day grows and
the minutes count toward work time and overtime. When the day has no time
points at all, the credit is anchored at the PBX's configured empty day time
(06:00 by default) and closed by the configured stop status. Repeat requests
on the same day chain backwards from the earlier credit, producing one
contiguous block.

The credit is recorded as an accepted time point correction, so it appears in
the employee's correction history and remains correctable afterwards. No
acceptance email is sent.

The caller may only credit their own travel time: the request is rejected with
403 when the authenticated user has no access to the tenant or the employee
record is not their own, and with 404 when the employee cannot be found.

`operationId: tempusStoreUserTravelTime`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee to credit.

**Request body** (`application/json`):
```json
{
    "date": "2026-05-11",
    "comment": "Client site visit"
}
```

**Responses**:
- `201` — The travel time credit was applied.
  ```json
  {
      "message": "Travel time has been added.",
      "data": {
          "timepoint_id": "550e8400-e29b-41d4-a716-446655440012",
          "status_id": "550e8400-e29b-41d4-a716-446655440011",
          "start_time": "2026-05-11 07:30:00",
          "end_time": "2026-05-11 08:00:00",
          "minutes": 30,
          "comment": "Client site visit"
      },
      "meta": {
          "used_today": 1,
          "remaining_today": 0
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no access to the requested tenant, or the requested employee record does not belong to the authenticated user.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Either the `date` or `comment` field failed validation (standard
validation error body), or a travel time guard refused the request. Guard
refusals carry a stable machine readable `code` alongside the translated
`message`:

| code | meaning |
|---|---|
| `not_allowed` | Travel time is not enabled for this employee. |
| `employee_user_missing` | The employee is not linked to a user account. |
| `status_not_configured` | No status is marked as travel time for the PBX. |
| `status_ambiguous` | More than one status is marked as travel time. |
| `stop_status_not_configured` | The day is empty and no stop status is configured. |
| `stop_status_same_as_travel` | The stop status is misconfigured to equal the travel status. |
| `minutes_not_configured` | The configured duration is zero or negative. |
| `date_in_future` | The requested date is in the future. |
| `date_locked` | The requested date is older than the allowed window. |
| `no_contract` | The employee has no active contract on that date. |
| `absence_day` | The date is covered by an approved absence. |
| `limit_reached` | The per-date allowance is already used up. |
| `no_room_before_first_status` | The day's first status is at midnight, leaving no room. |

#### GET /{pbx_id}/user/{employee_id}/user-companies

List companies associated with an employee (user)

Returns a paginated list of the employee's contracts, each enriched with its company details. When no active filter is supplied, the currently active contract is flagged as the main company. Filtering and sorting are powered by Spatie Query Builder. The caller may only list their own companies: the request is rejected with 403 when the authenticated user has no access to the tenant or the employee record is not their own, and with 404 when the employee cannot be found.

`operationId: tempusListUserCompanies`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee whose companies are requested.
- `filter[from]` (query, string<date>, optional) — Return contracts whose start date is on or after this date.
- `filter[to]` (query, string<date>, optional) — Return contracts whose end date is on or before this date. Must be on or after filter[from].
- `filter[active]` (query, enum, optional) — Restrict to active (true/1) or ended (false/0) contracts. · enum: true, false, 1, 0
- `sort` (query, string, optional) — Sort field. Allowed: from (contract_start), to (contract_end). Prefix with - for descending.
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page.

**Responses**:
- `200` — Paginated list of the employee's companies.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "ext_id": "1001",
              "company_id": "550e8400-e29b-41d4-a716-446655440002",
              "participation": 1,
              "contract_start": "2024-01-01T00:00:00.000000Z",
              "contract_end": "2025-12-31T00:00:00.000000Z",
              "main_company": 1,
              "compensatory_balance": 3600,
              "ho_balance": 8.5,
              "title": "Sales Representative",
              "department_id": "550e8400-e29b-41d4-a716-446655440003",
              "delete_date": "string?<date-time>",
              "company": {
                  "id": "550e8400-e29b-41d4-a716-446655440002",
                  "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
                  "company": "Sales Team",
                  "address": "100 Market Street 10001 New York",
                  "country_cc": "US",
                  "legalHolidays": 26,
                  "full_time": 144000,
                  "salary_lu_id": "null",
                  "overtime_calculation_period_enabled": false,
                  "overtime_decision_enabled": false,
                  "delete_date": "string?<date-time>"
              }
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no access to the requested tenant, or the requested employee record does not belong to the authenticated user.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/user/{employee_id}/holidays-reports

Get the holidays report for an employee (user)

Returns the legal-holiday report for the employee for a given year, derived from the transaction ledger. The report includes the possible, taken, expired and remaining holiday balances, the compensatory balance and extra time, plus a per-year pool details list. The response is a flat object and is not wrapped in a `data` envelope. The request is rejected with 403 when the authenticated user does not belong to the requested tenant, and with 404 when the employee cannot be found.

`operationId: tempusGetUserHolidaysReport`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee whose holidays report is requested.
- `year` (query, string, optional) — Four-digit year for the report. Defaults to the current year when omitted.

**Responses**:
- `200` — The holidays report for the employee. Flat object, not wrapped in data.
  ```json
  {
      "possible_holidays": "number",
      "taken_holidays": "number",
      "expired_holidays": "number",
      "remaining_holidays": "number",
      "pools_times_details_for_year": [
          []
      ],
      "compensatory_balance": "number",
      "extra_time": "number"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user does not belong to the requested tenant.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

### Employees

Tempus employees, departments, and their time points.

#### GET /{pbx_id}/users

List Tempus employees for the tenant (user)

Returns a paginated list of Tempus employees (time-and-attendance records) belonging to the PBX tenant identified by `pbx_id`. Each item is serialized by the `TempusUserResource` and combines the `TempusEmployee` record with data resolved from its linked Voxbi user account and first telephony extension. The authenticated user must have an associated employee record and must belong to the requested PBX, otherwise the request is rejected with 403. Results may be filtered by department via `filter[group_id]` (matched through employee contracts) and are sorted by id by default.

`operationId: tempusListUsers`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `filter[group_id]` (query, string, optional) — Comma-separated list of department (group) UUIDs to restrict results to. Employees are matched through their contracts' department assignment. Each value must be a valid UUID or the request is rejected with 422.
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page.

**Responses**:
- `200` — Paginated list of Tempus employees.
  ```json
  {
      "data": [
          {
              "ext_id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "ext_number": "101",
              "first_name": "Jane",
              "last_name": "Smith",
              "full_name": "Jane Smith",
              "email": "jane.smith@example.com",
              "soop_enable": "integer?<int32>",
              "soop_name": "string?",
              "soop_badge": "string?"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no associated employee record, or does not belong to the requested PBX tenant.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/groups

List departments for the tenant (user)

Returns a paginated list of departments (also called groups) belonging to the PBX tenant. Each item is serialized by the V1 `TempusDepartmentResource`. Departments are a per-tenant catalog of organizational units used across the Tempus time and attendance module. The authenticated user must belong to the PBX identified by the path parameter, otherwise the request is rejected with 403. Results are paginated using standard Laravel pagination.

`operationId: tempusListDepartments`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page.

**Responses**:
- `200` — Paginated list of departments.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "mxvp_user_id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Customer Support",
              "flags": "",
              "absence_validation_amount_required": 0,
              "timestamp": "2024-03-01T08:29:07Z",
              "delete_date": "string?<date-time>"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user does not belong to the requested PBX tenant.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/departments

List departments for the tenant (user)

Returns the full list of departments belonging to the PBX tenant, each serialized by the V1 `DepartmentResource` with clean field names (id, name, description). Departments are a per-tenant catalog of organizational units used across the Tempus time and attendance module. The authenticated user must belong to the PBX identified by the path parameter, otherwise the request is rejected with 403. The result is not paginated: the complete list is returned as a top-level array. This is the properly named replacement for the legacy `groups` endpoint, which returns the same records under legacy key names.

`operationId: listDepartments`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.

**Responses**:
- `200` — List of departments.
  ```json
  [
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "name": "Customer Support",
          "description": "Handles inbound customer requests"
      }
  ]
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user does not belong to the requested PBX tenant.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### GET /{pbx_id}/user/{employee_id}/timepoints

List timepoints for an employee (user)

Returns a paginated list of clock-in/clock-out timepoints recorded for a single employee, newest first. The authenticated user must have access to the PBX tenant and be authorized to view the target employee's records. An optional date window can be applied with `filter[from]` and `filter[to]`; when both are supplied, `filter[from]` must not be after `filter[to]` and the range may not exceed 100 days, otherwise the request is rejected with 422. Each item is returned in the legacy timepoint shape (id, ext_id, time, status, comment, GPS coordinates, and an optional sub-status).

Three independent pieces of location data are returned and must not be conflated: `location` is the configured location record the employee selected; `gpsLat` / `gpsLong` / `gpsTimestamp` and the normalised `device_gps` block are where the device said it was, stored exactly as the client reported it; `location_check` compares the two and carries the geofence verdict (`match` / `mismatch` / `unknown`). The device coordinates are never replaced by the coordinates configured on the location.

`operationId: tempusListUserTimepoints`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee whose timepoints are listed.
- `filter[from]` (query, string<date>, optional) — Return timepoints whose start time is on or after this date (Y-m-d).
- `filter[to]` (query, string<date>, optional) — Return timepoints whose start time is on or before this date (Y-m-d).
- `page` (query, integer, optional) — Page number to retrieve.
- `per_page` (query, integer, optional) — Number of items per page.

**Responses**:
- `200` — Paginated list of employee timepoints.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "ext_id": "EMP-10472",
              "time": "2024-03-01T08:29:07Z",
              "status": {
                  "id": "550e8400-e29b-41d4-a716-446655440001",
                  "name": "Working",
                  "icon": "la la-briefcase",
                  "fontcolor": "#ffffff",
                  "backgroundcolor": "#2e7d32",
                  "css": "status-working",
                  "comment": 0,
                  "payable": 1
              },
              "comment": "Started shift on site",
              "gpsLong": -73.9857,
              "gpsLat": 40.7484,
              "gpsTimestamp": "2024-03-01T08:29:05Z",
              "device_gps": {
                  "latitude": 49.95,
                  "longitude": 6.133,
                  "accuracy_m": 12.5,
                  "captured_at": "2026-07-07T10:07:39+00:00",
                  "captured_at_raw": 1783418859289
              },
              "location_check": {
                  "location_id": "550e8400-e29b-41d4-a716-446655440010",
                  "location_latitude": 49.6008,
                  "location_longitude": 6.133,
                  "geofence_radius_m": 150,
                  "distance_m": 38957,
                  "within_geofence": false,
                  "verdict": "mismatch",
                  "reason": "enum"
              },
              "substatus": {
                  "id": "550e8400-e29b-41d4-a716-446655440002",
                  "name": "Lunch break",
                  "icon": "la la-coffee",
                  "fontcolor": "#000000",
                  "backgroundcolor": "#fff3e0",
                  "css": "substatus-lunch",
                  "comment": 0
              }
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user has no access to this PBX tenant or is not authorized to view the target employee's records.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### DELETE /{pbx_id}/user/{employee_id}/timepoints/{id}

Delete a timepoint for an employee (user)

Permanently deletes a single timepoint belonging to the specified employee within the PBX tenant. The timepoint must exist for the tenant and must belong to the given employee, otherwise a 404 is returned. The authenticated user must be authorized by the timepoint delete policy for this tenant; if not, a 403 is returned.

`operationId: tempusDeleteUserTimepoint`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the employee that owns the timepoint.
- `id` (path, string<uuid>, required) — UUID of the timepoint to delete.

**Responses**:
- `204` — Timepoint deleted. No content is returned.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated user is not authorized to delete this timepoint.
- `404` — Data Not Found. This error is returned when the requested data is not found.

### Validators

Validator scope and validator-facing pending requests and corrections.

#### GET /{pbx_id}/validators

List all validators in the tenant

Every validator in the tenant (an employee named on any validator rule),
with the per-module permissions they hold and the lightweight list of
employees they validate (the union across their rules' company / department /
employees scope). Readable by any authenticated user in the tenant.

`operationId: tempusListValidators`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required)

**Responses**:
- `200` — The validators in the tenant.
  ```json
  {
      "data": [
          {
              "validator": {
                  "employee_id": "string<uuid>",
                  "user_id": "string?<uuid>",
                  "name": "string"
              },
              "permissions": {
                  "holiday_requests": "boolean",
                  "home_office_requests": "boolean",
                  "time_point_corrections_requests": "boolean",
                  "schedules_management": "boolean"
              },
              "employees": [
                  {
                      "employee_id": "string<uuid>",
                      "name": "string"
                  }
              ]
          }
      ]
  }
  ```
- `401` — Unauthenticated.
- `403` — The caller does not belong to this tenant.

#### GET /{pbx_id}/validator/employees

Employees the logged-in validator can validate

Returns the employees the logged-in user (a validator) is allowed to
validate (the union across all validator modules), each with identity, the
active contract, and a per-module capability map. Access is rule-based
(see the "Validators & Managers" rules). Matricule is never returned.

Optional filters narrow the list by the employee's active-contract company or
department, or to a single employee. The dropdown values (the companies /
departments / employees in scope) come from the caller's user-profile
`validator` block.

`operationId: tempusListValidatorEmployees`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required)
- `filter[company_id]` (query, string<uuid>, optional) — Keep only employees whose active contract is in this company.
- `filter[department_id]` (query, string<uuid>, optional) — Keep only employees whose active contract is in this department.
- `filter[employee_id]` (query, string<uuid>, optional) — Keep only this employee (when in scope).

**Responses**:
- `200` — The employees in the validator's scope.
  ```json
  {
      "data": [
          {
              "employee_id": "550e8400-e29b-41d4-a716-446655440010",
              "user_id": "550e8400-e29b-41d4-a716-446655440011",
              "first_name": "John",
              "last_name": "Doe",
              "email": "john.doe@example.com",
              "extension_id": "550e8400-e29b-41d4-a716-446655440020",
              "is_active": true,
              "contract": {
                  "start_date": "string?<date>",
                  "end_date": "string?<date>",
                  "weekly_expected_working_time_seconds": "integer",
                  "weekly_expected_working_time": "string",
                  "participation": "number?",
                  "job_title": "string?",
                  "contract_type": "string?",
                  "company_id": "string?<uuid>",
                  "company": "string?",
                  "department_id": "string?<uuid>",
                  "department": "string?",
                  "compensatory_balance_seconds": "integer",
                  "ho_balance_seconds": "integer"
              },
              "modules": {
                  "holiday_requests": "boolean",
                  "home_office_requests": "boolean",
                  "time_point_corrections_requests": "boolean",
                  "schedules_management": "boolean"
              }
          }
      ]
  }
  ```
- `401` — Unauthenticated.
- `403` — The caller has no employee record in this tenant.
- `422` — A filter value is not a valid UUID.

#### GET /{pbx_id}/validator/{employee_id}

Validator-scoped employee profile

Full profile of one employee as a validator sees it: identity, active
contract, active workshift, work hours (worked vs required, overtime on
business vs off days), legal-holiday balance, and leave / home-office /
correction history for the range. Returns `403` unless the target employee
is within the caller's validator scope. Matricule is never returned.
Transaction/correction `status` values are the raw ledger values.

`operationId: tempusGetValidatorEmployeeProfile`

**Auth**: bearerAuth

**Parameters**:
- `pbx_id` (path, string<uuid>, required)
- `employee_id` (path, string<uuid>, required)
- `filter[from]` (query, string<date>, optional) — Y-m-d. Defaults to the start of the current month. Range max 100 days.
- `filter[to]` (query, string<date>, optional) — Y-m-d. Defaults to today.

**Responses**:
- `200` — The employee's validator-facing profile.
  ```json
  {
      "employee": {
          "id": "string<uuid>",
          "user_id": "string?<uuid>",
          "first_name": "string?",
          "last_name": "string?",
          "email": "string?",
          "is_active": "boolean"
      },
      "contract": {
          "start_date": "string?<date>",
          "end_date": "string?<date>",
          "weekly_expected_working_time_seconds": "integer",
          "weekly_expected_working_time": "string",
          "participation": "number?",
          "job_title": "string?",
          "contract_type": "string?",
          "company_id": "string?<uuid>",
          "company": "string?",
          "department_id": "string?<uuid>",
          "department": "string?",
          "compensatory_balance_seconds": "integer",
          "ho_balance_seconds": "integer"
      },
      "workshift": {
          "id": "string<uuid>",
          "title": "string?",
          "detail": "string?",
          "weekly_seconds": "integer",
          "weekly": "string",
          "working_hours": [],
          "assignment": {
              "start_date": "string?<date>",
              "end_date": "string?<date>",
              "is_default": "boolean"
          }
      },
      "range": {
          "from": "string<date>",
          "to": "string<date>"
      },
      "work_hours": [],
      "holiday_balance": {
          "year": "integer",
          "rights_seconds": "integer",
          "taken_seconds": "integer",
          "remaining_seconds": "integer",
          "compensatory_seconds": "integer",
          "available_seconds": "integer"
      },
      "leave": [
          {
              "id": "string<uuid>",
              "type": "string?",
              "sub_type": "string?",
              "status": "enum",
              "start": "string?<date-time>",
              "end": "string?<date-time>",
              "requested_seconds": "integer",
              "comment": "string?",
              "created": "string?<date-time>"
          }
      ],
      "home_office": [
          {
              "id": "string<uuid>",
              "type": "string?",
              "sub_type": "string?",
              "status": "enum",
              "start": "string?<date-time>",
              "end": "string?<date-time>",
              "requested_seconds": "integer",
              "comment": "string?",
              "created": "string?<date-time>"
          }
      ],
      "corrections": [
          {
              "id": "string<uuid>",
              "status": "string?",
              "timepoint_id": "string?<uuid>",
              "old_status_id": "string?<uuid>",
              "new_status_id": "string?<uuid>",
              "old_time": "string?<date-time>",
              "new_time": "string?<date-time>",
              "correction_comment": "string?",
              "new_comment": "string?",
              "created": "string?<date-time>"
          }
      ],
      "modules": {
          "holiday_requests": "boolean",
          "home_office_requests": "boolean",
          "time_point_corrections_requests": "boolean",
          "schedules_management": "boolean"
      }
  }
  ```
- `401` — Unauthenticated.
- `403` — Caller has no employee record, or the employee is not in the caller's scope.
- `422` — Bad date format or range exceeds 100 days.

#### GET /{pbx_id}/validator/pending-requests

List employees with pending absence and home office requests (user)

Returns the employees, in scope of the authenticated validator, who have
pending home office requests, absence requests, or absence removal requests
awaiting validation. The authenticated user must be a Tempus employee in the
given PBX; otherwise a 403 is returned.

The response is a hand-rolled envelope. The `data` object is keyed by the
employee's telephony extension id. Each value carries the optional
`home_office`, `absence_requests`, and `absence_removals` sections, populated
only when the validator has scope for that module and pending records exist.

The queue is built from the approval rows addressed to the caller, so it
accounts for approval chains: which step holds the request, watchers, and
self-approval grants. Every request entry therefore also reports
`can_decide_now`, `watcher`, `step`, and `awaiting`. Requests at a step below
the caller's are listed with `can_decide_now: false`: visible, not yet theirs
to decide.

`home_office` keeps its three date lists and adds a `requests` array, because
a home office group is one row per day and the date lists cannot say which
days the caller may act on.

Absence entries (`absence_requests`, `absence_removals`) also carry
`next_availability`: when that employee is back **if this request is approved**,
next to what the general next-availability answer says today. Home office does
not — a home office day is governed by a weekly limit, not time off that moves an
availability date.

`operationId: tempusListValidatorPendingRequests`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `page` (query, integer, optional) — Page number of the employee list to return.
- `per_page` (query, integer, optional) — Number of employees to return per page.

**Responses**:
- `200` — OK. Employees with pending requests, keyed by extension id.
  ```json
  {
      "data": [],
      "pagination": {
          "page": 1,
          "total_pages": 3,
          "total": 24,
          "per_page": 10
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Forbidden. The authenticated user is not part of the PBX or is not a
Tempus employee in this tenant.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/validator/pending-corrections

List pending time point corrections in the validator scope (user)

Returns the pending time point correction requests raised by employees who
fall within the authenticated validator's correction scope. The
authenticated user must be a Tempus employee in the given PBX; otherwise a
403 is returned.

The response is a hand-rolled envelope: a `data` array of correction objects
plus a `pagination` block. Each correction is serialized by the V1
`PendingCorrectionResource`.

The queue is built from the approval rows addressed to the caller, so it
accounts for approval chains. Each correction therefore also reports
`can_decide_now`, `watcher`, `step`, and `awaiting`: a correction at a step
below the caller's is listed with `can_decide_now: false`, visible but not yet
theirs to decide.

`operationId: tempusListValidatorPendingCorrections`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `page` (query, integer, optional) — Page number of the corrections list to return.
- `per_page` (query, integer, optional) — Number of corrections to return per page.

**Responses**:
- `200` — OK. Paginated list of pending corrections.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "requested_ext_id": "1042",
              "full_name": "Jane Smith",
              "date": "Mon 2024-03-04",
              "timepoint_id": "550e8400-e29b-41d4-a716-446655440010",
              "old_status": {
                  "id": "550e8400-e29b-41d4-a716-446655440020",
                  "name": "Working",
                  "font_color": "#ffffff",
                  "background_color": "#2e7d32",
                  "icon": "la la-briefcase"
              },
              "new_status": {
                  "id": "550e8400-e29b-41d4-a716-446655440021",
                  "name": "Home Office",
                  "font_color": "#ffffff",
                  "background_color": "#1565c0",
                  "icon": "la la-home"
              },
              "old_sub_status": {
                  "id": "550e8400-e29b-41d4-a716-446655440022",
                  "name": "Meeting",
                  "font_color": "#000000",
                  "background_color": "#fff59d",
                  "icon": "la la-users"
              },
              "new_sub_status": {
                  "id": "550e8400-e29b-41d4-a716-446655440023",
                  "name": "Training",
                  "font_color": "#000000",
                  "background_color": "#b2dfdb",
                  "icon": "la la-graduation-cap"
              },
              "old_location": {
                  "id": "550e8400-e29b-41d4-a716-446655440011",
                  "name": "HQ Luxembourg",
                  "latitude": 49.6008,
                  "longitude": 6.133,
                  "geofence_radius_m": 150
              },
              "new_location": {
                  "id": "550e8400-e29b-41d4-a716-446655440010",
                  "name": "Remote office",
                  "latitude": 49.612,
                  "longitude": 6.13,
                  "geofence_radius_m": 200
              },
              "new_location_id": "550e8400-e29b-41d4-a716-446655440010",
              "old_location_id": "550e8400-e29b-41d4-a716-446655440011",
              "new_datetime": "2024-03-04 09:00:00",
              "old_datetime": "2024-03-04 08:30:00",
              "new_time": "09:00",
              "old_time": "08:30",
              "correction_comment": "Forgot to clock in after lunch",
              "new_status_id": "550e8400-e29b-41d4-a716-446655440021",
              "new_comment": "Client visit on site",
              "can_decide_now": true,
              "watcher": false,
              "step": {
                  "position": 2,
                  "total": 3,
                  "name": "Team lead",
                  "status": "active",
                  "required": 2,
                  "mandatory": false
              },
              "awaiting": [
                  "Muller Jonas"
              ]
          }
      ],
      "pagination": {
          "page": 1,
          "total_pages": 4,
          "total": 38,
          "per_page": 10
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Forbidden. The authenticated user is not part of the PBX or is not a
Tempus employee in this tenant.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/validator/pending-corrections/{correction_id}/details

Get the time points behind a pending correction (user)

Returns the day's time points for the employee tied to a single pending
correction, giving the validator the surrounding clock events for context.
The correction must belong to an employee within the authenticated
validator's correction scope; otherwise a 403 is returned. A 400 is returned
when the correction has neither a new nor an old time, or its requester has
no linked user.

The response is a bare array of time point objects (not wrapped in `data`),
each serialized by the V1 `PendingCorrectionDetailsResource`.

`operationId: tempusGetValidatorPendingCorrectionDetails`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `correction_id` (path, string<uuid>, required) — UUID of the pending correction to inspect.

**Responses**:
- `200` — OK. Time points for the correction's day.
  ```json
  [
      {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "date": "2024-03-01",
          "time": "08:29:07",
          "status": {
              "id": "550e8400-e29b-41d4-a716-446655440001",
              "name": "Working",
              "font_color": "#FFFFFF",
              "background_color": "#2E7D32",
              "icon": "la la-briefcase"
          }
      }
  ]
  ```
- `400` — Bad request. The correction has no usable date, or its requester has no
linked user account.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Forbidden. The authenticated user is not part of the PBX, is not a
Tempus employee in this tenant, or the correction is outside their
validation scope.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/user/{employee_id}/validator-permissions

Get the validator permissions for an employee (user)

Deprecated: these per-module capability flags are now returned inline on the
user profile under the `validator.permissions` block
(`GET /{pbx_id}/user/{employee_id}/profile`). Prefer that; this endpoint is
kept for backward compatibility.

Returns the validation capabilities the given employee holds: whether they
can validate absences, home office, and corrections, whether they manage
schedules, and whether they have calendar access. The employee must belong
to the authenticated user (their `user_id` must match the requester) and to
the PBX; otherwise a 403 is returned. A 404 is returned when the employee
does not exist in the tenant.

The response is a flat object of booleans, not wrapped in `data`.

`operationId: tempusGetValidatorPermissions`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the Tempus employee.

**Responses**:
- `200` — OK. The employee's validation capabilities.
  ```json
  {
      "absence_validation": "boolean",
      "home_office_validation": "boolean",
      "correction_validation": "boolean",
      "schedules_management": "boolean",
      "calendar_access": "boolean"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Forbidden. The authenticated user is not part of the PBX, or the
employee does not belong to the authenticated user.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /{pbx_id}/validator/scope

Get the validator scope (employees the authenticated user can validate)

Deprecated: the caller's validator scope is now returned inline on their user
profile under the `validator` block (`GET /{pbx_id}/user/{employee_id}/profile`),
which carries the same per-module scope plus the consultable companies,
departments and employees. Prefer that; this endpoint is kept for
backward compatibility.

Returns the tempus validator scope identified by the path parameter. Multi-tenant scoping is
enforced: the caller can only fetch resources within their own PBX.

`operationId: getTempusValidatorScope`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK. The response is a flat object keyed by validator module name
(`holiday_requests`, `time_point_corrections_requests`, etc.). Each
module entry says whether the authenticated user is a validator for
that module and lists the employees in their scope.
  ```json
  []
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /{pbx_id}/validator/change-employee-status

Change a scoped employee's current status as a validator (user)

Sets the current Tempus status (and optional sub-statuses) of an employee on
behalf of a validator. The authenticated user must be a Tempus employee in
the PBX and must hold correction scope over the target employee; otherwise a
403 is returned. A 404 is returned when no employee matches the given
`user_id` in the tenant. A validator may assign any status, not only the ones
in the employee's active layout.

`operationId: tempusChangeValidatorEmployeeStatus`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx_id` (path, string<uuid>, required) — UUID of the PBX tenant.

**Request body** (`application/json`) (required):
```json
{
    "user_id": "550e8400-e29b-41d4-a716-446655440030",
    "status_id": "550e8400-e29b-41d4-a716-446655440031",
    "sub_status_ids": [
        "550e8400-e29b-41d4-a716-446655440032"
    ],
    "comment": "Manual status set by validator"
}
```

**Responses**:
- `200` — OK. The status that was applied.
  ```json
  {
      "user_id": "550e8400-e29b-41d4-a716-446655440030",
      "status_id": "550e8400-e29b-41d4-a716-446655440031",
      "sub_status_ids": [
          "550e8400-e29b-41d4-a716-446655440032"
      ]
  }
  ```
- `400` — Bad request. The status change failed; the body carries an `errors`
array describing the reasons.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Forbidden. The authenticated user is not part of the PBX, is not a
Tempus employee in this tenant, or has no correction scope over the
target employee.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /v2/{pbx}/employee/{employee}/validator

Get an employee's validator role and queue counts (user)

Whether this employee holds a validator role, which request types are theirs,
and how much is queued for them.

Replaces the `validator` block of `GET /tempus/api/v1/{pbx_id}/user/{employee_id}/profile`.
That block also carried the companies, departments and employees the validator
covered; with approval chains that roster is not a flat set (a validator's place
is a position in each requester's own chain, resolved through that person's
contract), so it is not repeated here. For the roster use
`GET /tempus/api/v1/{pbx_id}/validator/employees`; for the work itself use the
per-type queues under `employee/{employee}/pending-requests/`: `absences`,
`home-office` and `corrections`.

The same block is on `GET /v2/{pbx}/employee/{employee}/profile`, so a screen that
already fetches the profile needs no second call. This endpoint exists for the
opposite case: deciding whether to offer a validator section is one boolean, and
should not require the whole profile payload.

Readable for your own employee record only: asking for another employee returns
403, since how someone else's validator duties are staffed is not theirs to read.

Note the path says `employee`, not `user` as v1 did: the subject is a Tempus
employee record, and v1 passed an employee id to a segment named `user`.

`operationId: tempusV2ShowEmployeeValidator`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx` (path, string<uuid>, required) — UUID of the PBX tenant. Must be the tenant the token belongs to; anything else is
refused with 403 rather than answered from another tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the Tempus employee whose queue this is. Must be the caller's own employee
record: a queue is built from the approval rows addressed to one person, so another
employee's id is refused rather than quietly answered with your own.

**Responses**:
- `200` — OK. Always this shape, whether or not they are a validator.
  ```json
  {
      "is_validator": true,
      "permissions": {
          "absence_requests": true,
          "home_office_requests": true,
          "time_point_corrections": false,
          "schedule_assignment_requests": false
      },
      "pending": {
          "absence_requests": 3,
          "home_office_requests": 1,
          "time_point_corrections": 0,
          "schedule_assignment_requests": 0
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Forbidden. The caller is outside this PBX, or asked for an employee record that is
not their own.
- `404` — Not found. No such employee in this PBX.

#### GET /v2/{pbx}/employee/{employee}/pending-requests/absences

List the absence requests waiting on you to decide (user)

The absence, illness and special-leave requests this employee is asked to decide,
newest first, one entry per request. Requests to remove already approved days are
included, marked `sub_type: removal`.

Read off the approval rows addressed to them, so approval chains are honoured
exactly as the decision endpoints honour them: which step holds the request,
watchers, self-approval grants, and restarts. Requests at a step below theirs are
included with `can_decide_now: false`, so a validator further up a chain sees what
is coming rather than having it appear from nowhere.

Your own employee record only: another employee's id returns 403, since a queue is
built from rows addressed to one person.

One endpoint per request type, matching how the work is looked at: a screen showing
one type pages that type, instead of over-fetching a mixed feed and filtering it.
The sibling queues are `pending-requests/absences`, `pending-requests/home-office`
and `pending-requests/time-point-corrections`; `GET /v2/{pbx}/employee/{employee}/validator`
reports how many are waiting in each.

`operationId: tempusV2ListPendingAbsenceRequests`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx` (path, string<uuid>, required) — UUID of the PBX tenant. Must be the tenant the token belongs to; anything else is
refused with 403 rather than answered from another tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the Tempus employee whose queue this is. Must be the caller's own employee
record: a queue is built from the approval rows addressed to one person, so another
employee's id is refused rather than quietly answered with your own.
- `page` (query, integer, optional) — Page number to return.
- `per_page` (query, integer, optional) — Number of entries per page.
- `filter[requester_id]` (query, string<uuid>, optional) — Narrow the queue to one person who raised requests. Named `requester_id` rather than
`employee_id` because the employee in the path is the validator: these routes have two
employees in play. A requester you hold no rows on yields an empty page rather than a
403, since nothing was hidden from you.

**Responses**:
- `200` — OK. Paginated absence requests waiting on this employee.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440010",
              "type": "absence",
              "sub_type": "string?",
              "status": "pending",
              "employee_id": "550e8400-e29b-41d4-a716-446655440001",
              "employee": {
                  "employee_id": "string<uuid>",
                  "first_name": "string?",
                  "last_name": "string?",
                  "name": "Muller Anna"
              },
              "requested_by": {
                  "employee_id": "string<uuid>",
                  "first_name": "string?",
                  "last_name": "string?",
                  "name": "string?"
              },
              "company_id": "string?<uuid>",
              "start": "2026-03-04T00:00:00+00:00",
              "end": "2026-03-06T00:00:00+00:00",
              "dates": [
                  "2026-03-04",
                  "2026-03-05"
              ],
              "requested_time": {
                  "2026-03-04": 28800,
                  "2026-03-05": 28800
              },
              "requested_seconds": 57600,
              "next_availability": {
                  "if_approved": {
                      "absent_from": "2026-08-13",
                      "next_available_date": "2026-08-17",
                      "next_available_at": null,
                      "available_now": true,
                      "in_past": false,
                      "extends_current_absence": false,
                      "message": "Off from 2026-08-13, available on 2026-08-17"
                  },
                  "current": {
                      "next_available_date": "2026-08-12",
                      "message": "Back on 2026-08-12"
                  }
              },
              "comment": "Family trip",
              "created": "string?<date-time>",
              "modified": "string?<date-time>",
              "validators": [
                  {
                      "employee_id": "string?<uuid>",
                      "name": "Lemaire Farid",
                      "status": "enum",
                      "comment": "string?",
                      "step_position": 1,
                      "watcher": "boolean",
                      "mandatory": "boolean"
                  }
              ],
              "can_decide_now": true,
              "watcher": false,
              "step": {
                  "position": 2,
                  "total": 3,
                  "name": "Team lead",
                  "status": "active",
                  "required": 2,
                  "mandatory": false
              },
              "awaiting": [
                  "Muller Jonas"
              ]
          }
      ],
      "pagination": {
          "page": 1,
          "total_pages": 2,
          "total": 14,
          "per_page": 10
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Forbidden. The caller is outside this PBX, or asked for an employee record that is
not their own.
- `404` — Not found. No such employee in this PBX.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /v2/{pbx}/employee/{employee}/pending-requests/home-office

List the home office requests waiting on you to decide (user)

The home office requests this employee is asked to decide, newest first.

One entry per day-row: a home office request for a week is stored as a row per day,
each decided on its own, and each carrying its own `id` and place in the chain. Read
`dates` rather than `start`/`end`. Requests to take back already approved days are
included, marked `sub_type: removal`.

Read off the approval rows addressed to them, so approval chains are honoured
exactly as the decision endpoints honour them: which step holds the request,
watchers, self-approval grants, and restarts. Requests at a step below theirs are
included with `can_decide_now: false`, so a validator further up a chain sees what
is coming rather than having it appear from nowhere.

Your own employee record only: another employee's id returns 403, since a queue is
built from rows addressed to one person.

One endpoint per request type, matching how the work is looked at: a screen showing
one type pages that type, instead of over-fetching a mixed feed and filtering it.
The sibling queues are `pending-requests/absences`, `pending-requests/home-office`
and `pending-requests/time-point-corrections`; `GET /v2/{pbx}/employee/{employee}/validator`
reports how many are waiting in each.

`operationId: tempusV2ListPendingHomeOfficeRequests`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx` (path, string<uuid>, required) — UUID of the PBX tenant. Must be the tenant the token belongs to; anything else is
refused with 403 rather than answered from another tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the Tempus employee whose queue this is. Must be the caller's own employee
record: a queue is built from the approval rows addressed to one person, so another
employee's id is refused rather than quietly answered with your own.
- `page` (query, integer, optional) — Page number to return.
- `per_page` (query, integer, optional) — Number of entries per page.
- `filter[requester_id]` (query, string<uuid>, optional) — Narrow the queue to one person who raised requests. Named `requester_id` rather than
`employee_id` because the employee in the path is the validator: these routes have two
employees in play. A requester you hold no rows on yields an empty page rather than a
403, since nothing was hidden from you.

**Responses**:
- `200` — OK. Paginated home office day-rows waiting on this employee.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440010",
              "type": "absence",
              "sub_type": "string?",
              "status": "pending",
              "employee_id": "550e8400-e29b-41d4-a716-446655440001",
              "employee": {
                  "employee_id": "string<uuid>",
                  "first_name": "string?",
                  "last_name": "string?",
                  "name": "Muller Anna"
              },
              "requested_by": {
                  "employee_id": "string<uuid>",
                  "first_name": "string?",
                  "last_name": "string?",
                  "name": "string?"
              },
              "company_id": "string?<uuid>",
              "start": "2026-03-04T00:00:00+00:00",
              "end": "2026-03-06T00:00:00+00:00",
              "dates": [
                  "2026-03-04",
                  "2026-03-05"
              ],
              "requested_time": {
                  "2026-03-04": 28800,
                  "2026-03-05": 28800
              },
              "requested_seconds": 57600,
              "next_availability": {
                  "if_approved": {
                      "absent_from": "2026-08-13",
                      "next_available_date": "2026-08-17",
                      "next_available_at": null,
                      "available_now": true,
                      "in_past": false,
                      "extends_current_absence": false,
                      "message": "Off from 2026-08-13, available on 2026-08-17"
                  },
                  "current": {
                      "next_available_date": "2026-08-12",
                      "message": "Back on 2026-08-12"
                  }
              },
              "comment": "Family trip",
              "created": "string?<date-time>",
              "modified": "string?<date-time>",
              "validators": [
                  {
                      "employee_id": "string?<uuid>",
                      "name": "Lemaire Farid",
                      "status": "enum",
                      "comment": "string?",
                      "step_position": 1,
                      "watcher": "boolean",
                      "mandatory": "boolean"
                  }
              ],
              "can_decide_now": true,
              "watcher": false,
              "step": {
                  "position": 2,
                  "total": 3,
                  "name": "Team lead",
                  "status": "active",
                  "required": 2,
                  "mandatory": false
              },
              "awaiting": [
                  "Muller Jonas"
              ]
          }
      ],
      "pagination": {
          "page": 1,
          "total_pages": 2,
          "total": 14,
          "per_page": 10
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Forbidden. The caller is outside this PBX, or asked for an employee record that is
not their own.
- `404` — Not found. No such employee in this PBX.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /v2/{pbx}/employee/{employee}/pending-requests/time-point-corrections

List the time point corrections waiting on you to decide (user)

The time point correction requests this employee is asked to decide: changes to a
recorded clock event, raised by the employee whose sheet it is.

Read off the approval rows addressed to them, so approval chains are honoured
exactly as the decision endpoints honour them. A correction at a step below theirs is
included with `can_decide_now: false`.

Your own employee record only: another employee's id returns 403.

The payload is the v2 correction shape: people are identified by `employee_id`
rather than v1's `requested_ext_id` (a telephony extension is not how a Tempus
employee is identified), and the timestamps are ISO 8601 rather than several
pre-formatted renderings of the same instant.

Open one with
`GET /tempus/api/v1/{pbx_id}/validator/pending-corrections/{correction_id}/details`,
which returns the day's other time points for context and is unchanged.

`operationId: tempusV2ListPendingTimePointCorrections`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `pbx` (path, string<uuid>, required) — UUID of the PBX tenant. Must be the tenant the token belongs to; anything else is
refused with 403 rather than answered from another tenant.
- `employee_id` (path, string<uuid>, required) — UUID of the Tempus employee whose queue this is. Must be the caller's own employee
record: a queue is built from the approval rows addressed to one person, so another
employee's id is refused rather than quietly answered with your own.
- `page` (query, integer, optional) — Page number to return.
- `per_page` (query, integer, optional) — Number of entries per page.
- `filter[requester_id]` (query, string<uuid>, optional) — Narrow the queue to one person who raised requests. Named `requester_id` rather than
`employee_id` because the employee in the path is the validator: these routes have two
employees in play. A requester you hold no rows on yields an empty page rather than a
403, since nothing was hidden from you.

**Responses**:
- `200` — OK. Paginated corrections waiting on this employee.
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440040",
              "status": "pending",
              "employee_id": "550e8400-e29b-41d4-a716-446655440001",
              "employee": {
                  "employee_id": "string<uuid>",
                  "first_name": "string?",
                  "last_name": "string?",
                  "name": "Muller Anna"
              },
              "timepoint_id": "string?<uuid>",
              "old_status": {
                  "id": "string<uuid>",
                  "name": "Working",
                  "style": {
                      "font_color": "#ffffff",
                      "background_color": "#2f6f4f",
                      "icon": "la la-clock"
                  }
              },
              "new_status": {
                  "id": "string<uuid>",
                  "name": "Break",
                  "style": {
                      "font_color": "#ffffff",
                      "background_color": "#2f6f4f",
                      "icon": "la la-clock"
                  }
              },
              "old_sub_status": {
                  "id": "string<uuid>",
                  "name": "string",
                  "style": {
                      "font_color": "#ffffff",
                      "background_color": "#2f6f4f",
                      "icon": "la la-clock"
                  }
              },
              "new_sub_status": {
                  "id": "string<uuid>",
                  "name": "string",
                  "style": {
                      "font_color": "#ffffff",
                      "background_color": "#2f6f4f",
                      "icon": "la la-clock"
                  }
              },
              "old_location": {
                  "id": "string<uuid>",
                  "name": "string",
                  "latitude": "number?<double>",
                  "longitude": "number?<double>",
                  "geofence_radius_m": "integer?"
              },
              "new_location": {
                  "id": "string<uuid>",
                  "name": "string",
                  "latitude": "number?<double>",
                  "longitude": "number?<double>",
                  "geofence_radius_m": "integer?"
              },
              "old_time": "2026-03-04T09:03:00+00:00",
              "new_time": "2026-03-04T09:00:00+00:00",
              "correction_comment": "Badge reader was down",
              "new_comment": "string?",
              "created": "string?<date-time>",
              "can_decide_now": true,
              "watcher": false,
              "step": {
                  "position": 2,
                  "total": 3,
                  "name": "Team lead",
                  "status": "active",
                  "required": 2,
                  "mandatory": false
              },
              "awaiting": [
                  "Muller Jonas"
              ]
          }
      ],
      "pagination": {
          "page": 1,
          "total_pages": 4,
          "total": 38,
          "per_page": 10
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — Forbidden. The caller is outside this PBX, or asked for an employee record that is
not their own.
- `404` — Not found. No such employee in this PBX.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

### Tempus terminals and recurring transactions

Tempus endpoints for badge readers and NFC terminals (authenticated with a PBX API-key token carrying the `terminals` ability) and for managing recurring transactions (PBX API key).

#### GET /AndroidNfc/getUser

Get user by NFC badge

Returns a paginated list of tempus android nfc. Use the standard `search`, `sort`, `page`,
and `perPage` query parameters; use `filter[key]=value` to narrow the result set.
Multi-tenant scoping is enforced. Requires a PBX API-key token carrying the `terminals` ability.

`operationId: getTempusAndroidNfc`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "currentStatus": "object?",
      "userInfo": {
          "id": "string<uuid>",
          "bearerToken": "string",
          "displayName": "string?"
      },
      "layout": "object?"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### POST /AndroidNfc/getUser

Log a terminal NFC event

Creates a new tempus android nfc. Validation rules and required fields are defined in the
request body schema below. Requires a PBX API-key token carrying the `terminals` ability.

`operationId: postTempusAndroidNfc`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "badge_id": "BADGE123",
    "terminal_id": "TERM01",
    "timestamp": "2024-01-15T08:30:00Z"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "success": "boolean",
      "message": "string",
      "data": {
          "id": "string<uuid>",
          "created": "string"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /terminal/{mac_address}/badge

Get tablet session (grid layout format)

Returns the tablet session for a scanned badge: current status, work-time
summary, layout and terminal location. Requires a PBX API-key token with the
`terminals` ability.

`operationId: postTempusTerminalBadge`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)
- `mac_address` (path, string, required) — The tablet's MAC address (any format; case-insensitive).

**Request body** (`application/json`) (required):
```json
{
    "badge_identifier": "string"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "current_status": {
          "id": "string<uuid>",
          "name": "string",
          "description": "string?",
          "style": [],
          "metadata": {
              "availability": "string?",
              "allow_multiselect": "boolean",
              "substatus_selection_mandatory": "boolean"
          },
          "location_ids": [
              "string<uuid>"
          ],
          "substatus_ids": [
              "string<uuid>"
          ]
      },
      "user_info": {
          "id": "string<uuid>",
          "bearer_token": "string",
          "display_name": "string?"
      },
      "work_times": {
          "daily": "string",
          "min_daily": "string",
          "weekly": "string",
          "min_weekly": "string",
          "monthly": "string",
          "min_monthly": "string",
          "lunch": "string"
      },
      "layout": {
          "id": "string<uuid>",
          "name": "string",
          "grid_size": {
              "rows": "integer",
              "columns": "integer"
          },
          "statuses": [
              []
          ],
          "substatuses": []
      },
      "location": {
          "id": "string<uuid>",
          "name": "string",
          "description": "string?"
      }
  }
  ```
- `400` — Missing required field (badge_identifier).
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The matched terminal exists but is not active. No session is returned.

#### GET /TimeLogger/getBadge

Get badge data (TimeLogger)

Returns a paginated list of tempus time logger. Use the standard `search`, `sort`, `page`,
and `perPage` query parameters; use `filter[key]=value` to narrow the result set.
Multi-tenant scoping is enforced. Requires a PBX API-key token carrying the `terminals` ability.

`operationId: getTempusTimeLogger`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "user": {
          "id": "string<uuid>",
          "full_name": "string?",
          "extension_number": "string?",
          "email": "string?",
          "active": "boolean"
      },
      "auth_info": {
          "bearerToken": "string",
          "pbx_id": "string<uuid>"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### POST /TimeLogger/getBadge

Submit badge data (TimeLogger)

Creates a new tempus time logger. Validation rules and required fields are defined in the
request body schema below. Requires a PBX API-key token carrying the `terminals` ability.

`operationId: postTempusTimeLogger`

**Auth**: bearerAuth

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "badge_id": "BADGE123",
    "terminal_id": "TERM01"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "user": {
          "id": "string<uuid>",
          "full_name": "string?",
          "extension_number": "string?",
          "email": "string?",
          "active": "boolean"
      },
      "auth_info": {
          "bearerToken": "string",
          "pbx_id": "string<uuid>"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### POST /recurring-transactions/preview-next-run

Preview the next run of a recurring transaction

Computes the next materialisation date for a given frequency without creating a
template. Server-to-server only (PBX API key).

`operationId: postTempusRecurringTransactionPreviewNextRun`

**Auth**: PbxApiKey

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "frequency": "enum",
    "run_once": "boolean",
    "from": "string<date>"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "next_run_at": "string<date-time>",
      "next_run_at_human": "string",
      "timezone": "string"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated principal is not allowed to manage recurring transactions.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /recurring-transactions

List recurring transactions

Returns a paginated list of recurring transaction templates for the authenticated PBX,
ordered by creation date descending. Server-to-server only (PBX API key).

`operationId: listTempusRecurringTransactions`

**Auth**: PbxApiKey

**Parameters**:
- `accept` (header, string, optional)
- `employee_id` (query, string<uuid>, optional) — Filter by Tempus employee.
- `status` (query, enum, optional) · enum: active, paused, completed, failed
- `frequency` (query, enum, optional) · enum: monthly_start, yearly_start
- `run_once` (query, boolean, optional)
- `per_page` (query, integer, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "data": [
          {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "employee_id": "550e8400-e29b-41d4-a716-446655440002",
              "type": "bonus",
              "sub_type": "annual",
              "absence_type_id": "string?<uuid>",
              "amount": 8,
              "tr_type": "credit",
              "note": "Monthly meal allowance",
              "frequency": "monthly_start",
              "run_once": false,
              "status": "active",
              "failure_reason": "string?",
              "next_run_at": "2026-07-01T00:00:00+00:00",
              "last_run_at": "string?<date-time>",
              "last_transaction_id": "string?",
              "created_by_user_id": "string?",
              "created_at": "2026-06-01T08:29:07+00:00",
              "updated_at": "2026-06-01T08:29:07+00:00"
          }
      ],
      "links": {
          "first": "http://localhost/api/v1/resources?page=1",
          "last": "http://localhost/api/v1/resources?page=1",
          "prev": "string?",
          "next": "string?"
      },
      "meta": {
          "current_page": 1,
          "from": 1,
          "last_page": 1,
          "links": [
              {
                  "url": "http://localhost/api/v1/resources?page=1",
                  "label": "first",
                  "active": true
              }
          ],
          "path": "http://localhost/api/v1/resources",
          "per_page": 15,
          "to": 1,
          "total": 1
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated principal is not allowed to manage recurring transactions.

#### POST /recurring-transactions

Create a recurring transaction

Creates a recurring transaction template for the authenticated PBX. The template is
activated immediately and its first `next_run_at` is computed from the frequency.
The PBX is resolved from the API key, so `pbx_id` is never part of the request body.
Server-to-server only (PBX API key).

`operationId: createTempusRecurringTransaction`

**Auth**: PbxApiKey

**Parameters**:
- `accept` (header, string, optional)

**Request body** (`application/json`) (required):
```json
{
    "employee_id": "string<uuid>",
    "type": "string",
    "sub_type": "string?",
    "absence_type_id": "string?<uuid>",
    "amount": "number<float>",
    "tr_type": "enum",
    "note": "string?",
    "frequency": "enum",
    "run_once": "boolean"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `201` — Created
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "employee_id": "550e8400-e29b-41d4-a716-446655440002",
      "type": "bonus",
      "sub_type": "annual",
      "absence_type_id": "string?<uuid>",
      "amount": 8,
      "tr_type": "credit",
      "note": "Monthly meal allowance",
      "frequency": "monthly_start",
      "run_once": false,
      "status": "active",
      "failure_reason": "string?",
      "next_run_at": "2026-07-01T00:00:00+00:00",
      "last_run_at": "string?<date-time>",
      "last_transaction_id": "string?",
      "created_by_user_id": "string?",
      "created_at": "2026-06-01T08:29:07+00:00",
      "updated_at": "2026-06-01T08:29:07+00:00"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated principal is not allowed to manage recurring transactions.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /recurring-transactions/{id}

Get a recurring transaction

Returns a single recurring transaction template. Server-to-server only (PBX API key).

`operationId: getTempusRecurringTransaction`

**Auth**: PbxApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "employee_id": "550e8400-e29b-41d4-a716-446655440002",
      "type": "bonus",
      "sub_type": "annual",
      "absence_type_id": "string?<uuid>",
      "amount": 8,
      "tr_type": "credit",
      "note": "Monthly meal allowance",
      "frequency": "monthly_start",
      "run_once": false,
      "status": "active",
      "failure_reason": "string?",
      "next_run_at": "2026-07-01T00:00:00+00:00",
      "last_run_at": "string?<date-time>",
      "last_transaction_id": "string?",
      "created_by_user_id": "string?",
      "created_at": "2026-06-01T08:29:07+00:00",
      "updated_at": "2026-06-01T08:29:07+00:00"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated principal is not allowed to manage recurring transactions.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### PATCH /recurring-transactions/{id}

Update a recurring transaction

Updates the mutable fields of a recurring transaction template. Only `amount`, `tr_type`,
`note`, `frequency` and `run_once` can be changed; `employee_id`, `type`, `sub_type`,
`absence_type_id` and `pbx_id` are fixed at creation. Changing `frequency` recomputes the
next run. Server-to-server only (PBX API key).

`operationId: patchTempusRecurringTransaction`

**Auth**: PbxApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Request body** (`application/json`):
```json
{
    "amount": "number<float>",
    "tr_type": "enum",
    "note": "string?",
    "frequency": "enum",
    "run_once": "boolean"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "employee_id": "550e8400-e29b-41d4-a716-446655440002",
      "type": "bonus",
      "sub_type": "annual",
      "absence_type_id": "string?<uuid>",
      "amount": 8,
      "tr_type": "credit",
      "note": "Monthly meal allowance",
      "frequency": "monthly_start",
      "run_once": false,
      "status": "active",
      "failure_reason": "string?",
      "next_run_at": "2026-07-01T00:00:00+00:00",
      "last_run_at": "string?<date-time>",
      "last_transaction_id": "string?",
      "created_by_user_id": "string?",
      "created_at": "2026-06-01T08:29:07+00:00",
      "updated_at": "2026-06-01T08:29:07+00:00"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated principal is not allowed to manage recurring transactions.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### DELETE /recurring-transactions/{id}

Delete a recurring transaction

Soft-deletes a recurring transaction template. Server-to-server only (PBX API key).

`operationId: deleteTempusRecurringTransaction`

**Auth**: PbxApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `204` — No Content
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated principal is not allowed to manage recurring transactions.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### POST /recurring-transactions/{id}/pause

Pause a recurring transaction

Sets the template status to `paused`. Server-to-server only (PBX API key).

`operationId: postTempusRecurringTransactionPause`

**Auth**: PbxApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "employee_id": "550e8400-e29b-41d4-a716-446655440002",
      "type": "bonus",
      "sub_type": "annual",
      "absence_type_id": "string?<uuid>",
      "amount": 8,
      "tr_type": "credit",
      "note": "Monthly meal allowance",
      "frequency": "monthly_start",
      "run_once": false,
      "status": "active",
      "failure_reason": "string?",
      "next_run_at": "2026-07-01T00:00:00+00:00",
      "last_run_at": "string?<date-time>",
      "last_transaction_id": "string?",
      "created_by_user_id": "string?",
      "created_at": "2026-06-01T08:29:07+00:00",
      "updated_at": "2026-06-01T08:29:07+00:00"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated principal is not allowed to manage recurring transactions.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### POST /recurring-transactions/{id}/resume

Resume a recurring transaction

Sets the template status back to `active`, clears any failure reason, and re-anchors
`next_run_at` to the next valid occurrence when it is null or in the past.
Server-to-server only (PBX API key).

`operationId: postTempusRecurringTransactionResume`

**Auth**: PbxApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
      "employee_id": "550e8400-e29b-41d4-a716-446655440002",
      "type": "bonus",
      "sub_type": "annual",
      "absence_type_id": "string?<uuid>",
      "amount": 8,
      "tr_type": "credit",
      "note": "Monthly meal allowance",
      "frequency": "monthly_start",
      "run_once": false,
      "status": "active",
      "failure_reason": "string?",
      "next_run_at": "2026-07-01T00:00:00+00:00",
      "last_run_at": "string?<date-time>",
      "last_transaction_id": "string?",
      "created_by_user_id": "string?",
      "created_at": "2026-06-01T08:29:07+00:00",
      "updated_at": "2026-06-01T08:29:07+00:00"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated principal is not allowed to manage recurring transactions.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### POST /recurring-transactions/{id}/run-now

Run a recurring transaction now

Materialises the template immediately (out of schedule). The template must be `active`.
The HTTP response is `200` even when the individual run is skipped or fails; read
`run.status` for the per-run outcome. Server-to-server only (PBX API key).

`operationId: postTempusRecurringTransactionRunNow`

**Auth**: PbxApiKey

**Parameters**:
- `accept` (header, string, optional)
- `id` (path, string<uuid>, required)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK. The run was attempted; inspect `run.status` for the outcome.
  ```json
  {
      "run": {
          "id": "string<uuid>",
          "status": "enum",
          "reason": "string?",
          "tempus_transaction_id": "string?",
          "ran_at": "string?<date-time>"
      },
      "template": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
          "employee_id": "550e8400-e29b-41d4-a716-446655440002",
          "type": "bonus",
          "sub_type": "annual",
          "absence_type_id": "string?<uuid>",
          "amount": 8,
          "tr_type": "credit",
          "note": "Monthly meal allowance",
          "frequency": "monthly_start",
          "run_once": false,
          "status": "active",
          "failure_reason": "string?",
          "next_run_at": "2026-07-01T00:00:00+00:00",
          "last_run_at": "string?<date-time>",
          "last_transaction_id": "string?",
          "created_by_user_id": "string?",
          "created_at": "2026-06-01T08:29:07+00:00",
          "updated_at": "2026-06-01T08:29:07+00:00"
      }
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `403` — The authenticated principal is not allowed to manage recurring transactions.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `409` — The recurring transaction is not active and cannot be run.

### API v2

### API v2 Endpoints

Second-generation endpoints with improved response structures.

Base URL: `/api/v2/`


#### GET /v2/test-latency

Test API Latency

A lightweight endpoint with no processing logic, used to measure API round-trip latency.

`operationId: getV2TestLatency`

**Auth**: bearerAuth

**Parameters**:
- `Accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "message": "string",
      "timestamp": "string<date-time>"
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### POST /v2/change-status

Change User Status

Changes the authenticated user's status in the Tempus time-attendance system. Accepts either a Cockpit UUID or a Tempus mapping ID for the status.

`operationId: postV2ChangeStatus`

**Auth**: bearerAuth

**Parameters**:
- `Accept` (header, string, optional)

**Request body** (`application/json`):
```json
{
    "status_id": "string",
    "location_id": "string?<uuid>",
    "sub_status_ids": "array?",
    "sources": "array?",
    "triggers": "array?",
    "meta_data": "object?",
    "gpsData": "object?",
    "comment": "string?"
}
```

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "ui": {
          "data": [
              {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
                  "user_id": "550e8400-e29b-41d4-a716-446655440002",
                  "status_id": "550e8400-e29b-41d4-a716-446655440003",
                  "location_id": "550e8400-e29b-41d4-a716-446655440010",
                  "location": {
                      "id": "550e8400-e29b-41d4-a716-446655440010",
                      "name": "HQ Luxembourg",
                      "description": "string?",
                      "street_no": "12",
                      "street": "Rue de Bonnevoie",
                      "city": "Luxembourg",
                      "postal": "1260",
                      "country_id": "550e8400-e29b-41d4-a716-446655440011",
                      "latitude": 49.6008,
                      "longitude": 6.133,
                      "geofence_radius_m": 150,
                      "is_fixed_location": true
                  },
                  "start_time": "2024-03-01T08:29:07Z",
                  "end_time": "2024-03-01T12:00:00Z",
                  "sources": {
                      "channel": "cockpit",
                      "device_id": "550e8400-e29b-41d4-a716-446655440010"
                  },
                  "triggers": {
                      "type": "manual_change",
                      "actor_id": "550e8400-e29b-41d4-a716-446655440002"
                  },
                  "meta_data": {
                      "note": "Started shift remotely",
                      "gpsLat": 49.95,
                      "gpsLong": 6.133
                  },
                  "device_gps": {
                      "latitude": 49.95,
                      "longitude": 6.133,
                      "accuracy_m": 12.5,
                      "captured_at": "2026-07-07T10:07:39+00:00",
                      "captured_at_raw": 1783418859289
                  },
                  "location_check": {
                      "location_id": "550e8400-e29b-41d4-a716-446655440010",
                      "location_latitude": 49.6008,
                      "location_longitude": 6.133,
                      "geofence_radius_m": 150,
                      "distance_m": 38957,
                      "within_geofence": false,
                      "verdict": "mismatch",
                      "reason": null
                  },
                  "is_synced": false,
                  "created_at": "2024-03-01T08:29:07Z",
                  "updated_at": "2024-03-01T08:29:07Z"
              },
              [
                  []
              ]
          ]
      },
      "workTimes": "object?",
      "message": "string"
  }
  ```
- `201` — A new time point was opened. `ui.data` is the created time point,
including `device_gps` (the coordinates the client sent, stored as sent)
and `location_check` (how they compare with the selected location's
configured coordinates and geofence radius).
  ```json
  {
      "ui": {
          "data": {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "pbx_id": "550e8400-e29b-41d4-a716-446655440001",
              "user_id": "550e8400-e29b-41d4-a716-446655440002",
              "status_id": "550e8400-e29b-41d4-a716-446655440003",
              "location_id": "550e8400-e29b-41d4-a716-446655440010",
              "location": {
                  "id": "550e8400-e29b-41d4-a716-446655440010",
                  "name": "HQ Luxembourg",
                  "description": "string?",
                  "street_no": "12",
                  "street": "Rue de Bonnevoie",
                  "city": "Luxembourg",
                  "postal": "1260",
                  "country_id": "550e8400-e29b-41d4-a716-446655440011",
                  "latitude": 49.6008,
                  "longitude": 6.133,
                  "geofence_radius_m": 150,
                  "is_fixed_location": true
              },
              "start_time": "2024-03-01T08:29:07Z",
              "end_time": "2024-03-01T12:00:00Z",
              "sources": {
                  "channel": "cockpit",
                  "device_id": "550e8400-e29b-41d4-a716-446655440010"
              },
              "triggers": {
                  "type": "manual_change",
                  "actor_id": "550e8400-e29b-41d4-a716-446655440002"
              },
              "meta_data": {
                  "note": "Started shift remotely",
                  "gpsLat": 49.95,
                  "gpsLong": 6.133
              },
              "device_gps": {
                  "latitude": 49.95,
                  "longitude": 6.133,
                  "accuracy_m": 12.5,
                  "captured_at": "2026-07-07T10:07:39+00:00",
                  "captured_at_raw": 1783418859289
              },
              "location_check": {
                  "location_id": "550e8400-e29b-41d4-a716-446655440010",
                  "location_latitude": 49.6008,
                  "location_longitude": 6.133,
                  "geofence_radius_m": 150,
                  "distance_m": 38957,
                  "within_geofence": false,
                  "verdict": "mismatch",
                  "reason": null
              },
              "is_synced": false,
              "created_at": "2024-03-01T08:29:07Z",
              "updated_at": "2024-03-01T08:29:07Z"
          }
      },
      "workTimes": "null"
  }
  ```
- `409` — Another status change for this employee is already in progress and could
not be resolved. Retry once after a short delay.
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.
- `422` — Unprocessable Parameters. This error is returned when a parameter is not valid.

#### GET /v2/layout

Get User Layout

Returns the active status-changer layout for the authenticated user, including grid configuration, statuses, and substatuses.

`operationId: getV2Layout`

**Auth**: bearerAuth

**Parameters**:
- `Accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  {
      "id": "string<uuid>",
      "name": "string",
      "grid_size": {
          "rows": "integer",
          "columns": "integer"
      },
      "statuses": [
          {
              "id": "string<uuid>",
              "name": "string",
              "description": "string?",
              "style": [],
              "metadata": {
                  "availability": "string?",
                  "allow_multiselect": "boolean",
                  "substatus_selection_mandatory": "boolean"
              },
              "location_ids": [
                  "string"
              ],
              "substatus_ids": [
                  "string"
              ],
              "css_grid_area": "string",
              "position": {
                  "row": "integer",
                  "column": "integer",
                  "row_span": "integer",
                  "col_span": "integer"
              }
          }
      ],
      "substatuses": []
  }
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
- `404` — Data Not Found. This error is returned when the requested data is not found.

#### GET /v2/statuses

Get v2 Statuses

Returns all Tempus statuses available for the authenticated user's PBX tenant, including style, metadata, and associated substatus and location IDs.

`operationId: getV2Statuses`

**Auth**: bearerAuth

**Parameters**:
- `Accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  [
      {
          "id": "string<uuid>",
          "name": "string",
          "description": "string?",
          "style": [],
          "metadata": {
              "availability": "string?",
              "allow_multiselect": "boolean",
              "substatus_selection_mandatory": "boolean"
          },
          "location_ids": [
              "string<uuid>"
          ],
          "substatus_ids": [
              "string<uuid>"
          ]
      }
  ]
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.

#### GET /v2/substatuses

Get v2 Substatuses

Returns all Tempus substatuses available for the authenticated user's PBX tenant, including style information.

`operationId: getV2Substatuses`

**Auth**: bearerAuth

**Parameters**:
- `Accept` (header, string, optional)

**Responses**:
- `5XX` — Server error. An unexpected condition was encountered on the server and the
request could not be completed. The body is a generic JSON envelope with a
`message` field. The response is logged on the server side; quote the
request URL + timestamp when reporting an issue.
- `200` — OK
  ```json
  [
      {
          "id": "string<uuid>",
          "name": "string",
          "description": "string?",
          "style": []
      }
  ]
  ```
- `401` — Authorization Token Missing. This error is returned when the authorization token is missing.
