> ## Documentation Index
> Fetch the complete documentation index at: https://docs.filexpress.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Schedules

> Create work schedules with their shifts and assign them to employees in bulk

| Operation | Permission |
| - | - |
| View schedules and assignments | `hr.schedules` |
| Create, edit, and delete schedules; assign and remove assignments | `hr.schedules.write` |

See [Schedules](https://docs.filexpress.app/en/docs/rrhh/horarios.md) in the manual.

## List schedules

```http theme={null}
GET /hr/work-schedules
```

**Required permission:** `hr.schedules`

```json theme={null}
{
  "status": true,
  "data": [
    {
      "id": "uuid",
      "business_id": "uuid",
      "name": "Administrativo",
      "code": "ADM",
      "description": "Lunes a viernes",
      "color": "#1890ff",
      "is_active": true,
      "shifts": [
        { "id": "uuid", "weekday": 1, "start_time": "08:00", "end_time": "17:00", "break_minutes": 60, "break_paid": null }
      ],
      "weekly_minutes": 2400,
      "days": [
        { "weekday": 1, "minutes": 480, "shift_kind": "day", "warnings": [] },
        { "weekday": 7, "minutes": 0, "shift_kind": null, "warnings": [] }
      ],
      "warnings": []
    }
  ]
}
```

| Field | Description |
| - | - |
| `shifts[].weekday` | ISO day: 1 = Monday … 7 = Sunday. A day without shifts is a rest day. |
| `shifts[].break_paid` | `true`/`false` or `null` (uses what is defined in the labor rules). |
| `weekly_minutes` | Scheduled weekly minutes (excluding unpaid breaks). |
| `days` | Summary of the 7 days: minutes, shift class (`day`, `night`, `mixed`, or `null` if rest day), and warnings. |
| `warnings` | Legal warnings: `daily_max_exceeded` (in `days`), `weekly_max_exceeded`. Each warning is `{ "code", "message" }`. |

Deleted schedules are not listed.

## Get a schedule

```http theme={null}
GET /hr/work-schedules/{id}
```

**Required permission:** `hr.schedules`

Returns the same object.

## Create a schedule

```http theme={null}
POST /hr/work-schedules
```

**Required permission:** `hr.schedules.write`

```json theme={null}
{
  "name": "Nocturno",
  "code": "NOCHE",
  "description": "Vigilancia",
  "color": "#2f54eb",
  "shifts": [
    { "weekday": 1, "start_time": "22:00", "end_time": "05:00", "break_minutes": 0 },
    { "weekday": 2, "start_time": "22:00", "end_time": "05:00", "break_minutes": 0 },
    { "weekday": 3, "start_time": "22:00", "end_time": "05:00", "break_minutes": 0 }
  ]
}
```

| Field | Required | Description |
| - | - | - |
| `name` | Yes | Name (max. 255). |
| `code` | Yes | Unique code within the company (max. 50). |
| `description` | No | Description. |
| `color` | No | Color for the interface (max. 20). |
| `is_active` | No | Defaults to `true`. |
| `shifts` | Yes | List of shifts (can be empty; max. 50). |
| `shifts[].weekday` | Yes | 1 = Monday … 7 = Sunday. |
| `shifts[].start_time`, `shifts[].end_time` | Yes | `HH:MM`. If the end time is less than or equal to the start time, the shift ends **the next day**. |
| `shifts[].break_minutes` | No | Break minutes; must be less than the shift duration. |
| `shifts[].break_paid` | No | If omitted, the labor rules apply. |

**Behavior**

* A day can have several shifts, but they cannot overlap.
* Legal warnings (`warnings`) do not prevent saving.

**`201` response**: the created schedule.

| Code | Cause |
| - | - |
| `422` | Validation, overlapping shifts, break longer than the shift, or duplicate code. |

## Edit a schedule

```http theme={null}
PUT /hr/work-schedules/{id}
```

**Required permission:** `hr.schedules.write`

Send only the fields that change. If you send `shifts`, it **replaces** all shifts.

**`200` response**: the schedule with `recalculation_queued` (`true` if attendance recalculation was queued for the employees who have it assigned).

## Delete a schedule

```http theme={null}
DELETE /hr/work-schedules/{id}
```

**Required permission:** `hr.schedules.write`

```json theme={null}
{ "status": true, "data": { "id": "uuid", "deleted": true } }
```

Responds `409` if the schedule has current assignments (no end date, or an end date of today or later): end them or change them first.

## Get assignments

```http theme={null}
GET /hr/schedule-assignments?employee_id=uuid&date=2026-10-01
```

**Required permission:** `hr.schedules`

| Parameter | Required | Description |
| - | - | - |
| `employee_id` | No | Filters by employee (full history). |
| `date` | No | Only the assignments in effect on that date. |

```json theme={null}
{
  "status": true,
  "data": [
    {
      "id": "uuid",
      "business_id": "uuid",
      "employee_id": "uuid",
      "employee_name": "María López",
      "work_schedule_id": "uuid",
      "work_schedule_name": "Administrativo",
      "work_schedule_code": "ADM",
      "start_date": "2026-10-01",
      "end_date": null,
      "notes": null
    }
  ]
}
```

`end_date: null` = open-ended assignment.

## Assign a schedule in bulk

```http theme={null}
POST /hr/schedule-assignments
```

**Required permission:** `hr.schedules.write`

Assigns a schedule to several employees with the same validity period.

```json theme={null}
{
  "work_schedule_id": "uuid",
  "document_numbers": ["01234567-8", "98765432-1"],
  "start_date": "2026-11-01",
  "end_date": null,
  "close_previous": true,
  "notes": "Cambio de turno"
}
```

| Field | Required | Description |
| - | - | - |
| `work_schedule_id` | Yes | Company schedule. |
| `employee_ids` | One of the two | Employee IDs (max. 500). |
| `document_numbers` | One of the two | Document numbers (max. 500; hyphens and spaces are ignored). Can be combined with `employee_ids`. |
| `start_date` | Yes | Start (`YYYY-MM-DD`). |
| `end_date` | No | End (equal to or later than the start). `null` = open-ended. |
| `close_previous` | No | If `true`, the employee's previous current assignment ends the day before `start_date`. |
| `notes` | No | Notes (max. 1000). |

**Behavior**

* **Idempotent**: if the employee already has an assignment that starts on `start_date`, it is updated instead of duplicated.
* An assignment that overlaps another one of the employee is rejected (unless `close_previous` closes it).
* Each employee is processed separately: an error does not stop the rest.

**`200` response**

```json theme={null}
{
  "status": true,
  "data": {
    "created": 1,
    "updated": 0,
    "errors": [
      { "employee_id": null, "document_number": "98765432-1", "message": "Empleado no encontrado." }
    ],
    "recalculation_queued": true
  }
}
```

If **no** employee was assigned and there are errors, the response has code `422`. If the schedule does not exist, `404`.

> [!NOTE]
>
> Error messages for already-resolved employees (for example, overlaps) come in Spanish, with `employee_id` populated.

## Delete an assignment

```http theme={null}
DELETE /hr/schedule-assignments/{id}
```

**Required permission:** `hr.schedules.write`

```json theme={null}
{ "status": true, "data": { "id": "uuid", "deleted": true, "recalculation_queued": true } }
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.