## Get the monthly absence report

`GET /v1/workday/time-control/reports`

Returns an employee's monthly time-control report: workdays, time worked, and absences with explanations. Call it to find an absence and explain it, or to check how much an employee worked in a month.

Requires the `timeman` scope. Permissions are checked for the credential owner: for a personal key, that is the key owner; for an app key, the user whose session token is passed in `Authorization`. Access to reports depends on the role and the [time-control settings](/docs/workday/time-control-settings-list): an administrator and an employee with the full report get the report of any employee, a department head gets the reports of employees in their departments, and an employee with the simple report gets only their own. The `reportViewType` field on the [Get time-control report settings](/docs/workday/time-control-report-settings) page shows which reports are available to the credential owner. While time control is switched off, an employee without subordinates gets `403 BITRIX_ACCESS_DENIED` even for their own report. READONLY keys can read the report.

The generic `filter` parameter is not applied by this endpoint. By default, the API preserves the previous HTTP 200 response and data, adds `Warning: 299 - "filter is not supported on this endpoint and was ignored"`, and records unsupported-filter use in the server log.

## Parameters

| Parameter | Type | Required | Default | Description |
|----------|-----|:-----:|-----------|----------|
| `userId` (query) | integer | yes | — | Employee ID, a positive integer. Your own ID is the `userId` field in the [time-control report settings](/docs/workday/time-control-report-settings); IDs of department employees come from [`GET /v1/workday/time-control/users`](/docs/workday/time-control-users) |
| `month` (query) | integer | yes | — | Month, from 1 to 12 |
| `year` (query) | integer | yes | — | Year, a positive integer |
| `idleMinutes` (query) | number | no | `minimumIdleForReport` from the settings | Minimum break length, in minutes, for a break to count as an absence. Honored in a request from a department head or an administrator, ignored in an employee's request |
| `workdayHours` (query) | number | no | `8` | Workday length in hours; undertime and overtime are calculated from it |

## Examples

### curl — personal key

```bash
curl "https://vibecode.bitrix24.com/v1/workday/time-control/reports?userId=503&month=5&year=2026" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth application

```bash
curl "https://vibecode.bitrix24.com/v1/workday/time-control/reports?userId=503&month=5&year=2026" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — personal key

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/workday/time-control/reports?userId=503&month=5&year=2026', {
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
  },
})
const { data } = await res.json()
```

### JavaScript — OAuth application

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/workday/time-control/reports?userId=503&month=5&year=2026', {
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})
const { data } = await res.json()
```

## Response fields

Dates are returned in ISO 8601 format with a time zone, durations in seconds.

| Field | Type | Description |
|------|-----|----------|
| `success` | boolean | `true` when the request succeeds |
| `data.report` | object | Report for the month |
| `data.report.monthTitle` | string | Month name |
| `data.report.dateStart` | string | Start of the month |
| `data.report.dateFinish` | string | End of the month |
| `data.report.days` | array | Days on which the employee opened a workday. For an existing employee, an empty array means there is no time-tracking data for the month: the employee did not open a workday, or time control is not set up in the Bitrix24 account. To check, see the `active` field in the [time-control settings](/docs/workday/time-control-settings-list) |
| `data.report.days[].index` | string | Date in `YYYYMMDD` format |
| `data.report.days[].dayTitle` | string | Date in the Bitrix24 account's date format |
| `data.report.days[].workdayDateStart` | string | Workday start |
| `data.report.days[].workdayDateFinish` | string | Workday end. If the day is still open, this is the time the report was built |
| `data.report.days[].workdayComplete` | boolean | Whether the workday is closed |
| `data.report.days[].workdayDuration` | integer | Workday duration including breaks |
| `data.report.days[].workdayDurationFinal` | integer | Time worked, excluding breaks, personal absences, and unexplained absences |
| `data.report.days[].workdayDurationConfig` | integer | Workday duration according to `workdayHours` |
| `data.report.days[].workdayTimeLeaksUser` | integer | Breaks the employee recorded themselves |
| `data.report.days[].workdayTimeLeaksReal` | integer | Breaks according to automatic tracking, including personal and unexplained absences |
| `data.report.days[].workdayTimeLeaksFinal` | integer | Undertime as a positive number, or overtime as a negative number |
| `data.report.days[].reports` | array | Absences and events of the workday |
| `data.report.days[].reports[].id` | string | Record ID. For an absence, it can be used as `absenceId` when [explaining an absence](/docs/workday/time-control-explain) |
| `data.report.days[].reports[].userId` | string | Employee ID |
| `data.report.days[].reports[].type` | string | Record type: `IDLE` — the employee stepped away; `OFFLINE` — offline; `TM_START`, `TM_PAUSE`, `TM_CONTINUE`, `TM_END` — start, pause, resumption, and end of the workday; `DESKTOP_ONLINE`, `DESKTOP_OFFLINE`, `DESKTOP_START` — activity of the Bitrix24 desktop app. Only a department head sees the last three types |
| `data.report.days[].reports[].dateStart` | string | Record start |
| `data.report.days[].reports[].dateFinish` | string | Record end. If the record is active, this is the time the report was built |
| `data.report.days[].reports[].duration` | integer | Record duration |
| `data.report.days[].reports[].active` | boolean | Whether the record is still ongoing |
| `data.report.days[].reports[].entryId` | string | ID of the workday the record belongs to |
| `data.report.days[].reports[].reportType` | string | Explanation type: `WORK` — work-related, `PRIVATE` — personal. Any other value means there is no explanation, and the absence counts as personal |
| `data.report.days[].reports[].reportText` | string | Explanation text |
| `data.report.days[].reports[].systemText` | string \| null | Reason recorded by Bitrix24. Only a department head sees it |
| `data.report.days[].reports[].sourceStart` | string | Source of the record start: `TM_EVENT` — a workday action; `IDLE_EVENT` — the "Away" status; `ONLINE_EVENT` — signing in to the Bitrix24 account; `OFFLINE_AGENT` — switching to the "Offline" status; `DESKTOP_ONLINE_EVENT`, `DESKTOP_START_EVENT`, `DESKTOP_OFFLINE_AGENT` — the desktop app being turned on and off |
| `data.report.days[].reports[].sourceFinish` | string | Source of the record end; values as in `sourceStart` |
| `data.report.days[].reports[].ipStart` | string | IP address at the record start. Only a department head sees it |
| `data.report.days[].reports[].ipFinish` | string | IP address at the record end. Only a department head sees it |
| `data.report.days[].reports[].ipStartNetwork` | boolean \| object | `false` if the IP address at the record start belongs to the office network; otherwise an object with the `ip`, `range`, and `name` fields — the address, the range, and the range name. Only a department head sees it |
| `data.report.days[].reports[].ipFinishNetwork` | boolean \| object | The same for the record end |
| `data.user` | object \| false | Employee the report is built for, or `false` if the employee does not exist. The `data.user.*` fields below are returned only for an employee object |
| `data.user.id` | integer | Employee ID |
| `data.user.active` | boolean | Whether the employee is active |
| `data.user.name` | string | First and last name |
| `data.user.firstName` | string | First name |
| `data.user.lastName` | string | Last name |
| `data.user.workPosition` | string | Position |
| `data.user.avatar` | string | Profile photo URL. An empty string if there is no photo |
| `data.user.personalGender` | string | Gender: `M` or `F` |
| `data.user.lastActivityDate` | string | Last activity time in the Bitrix24 account |

## Response example

Response to a department head, one workday:

```json
{
  "success": true,
  "data": {
    "report": {
      "monthTitle": "May",
      "dateStart": "2026-05-01T00:00:00+00:00",
      "dateFinish": "2026-05-31T23:59:59+00:00",
      "days": [
        {
          "index": "20260526",
          "dayTitle": "26.05.2026",
          "workdayDateStart": "2026-05-26T09:02:11+00:00",
          "workdayDateFinish": "2026-05-26T18:05:40+00:00",
          "workdayComplete": true,
          "workdayTimeLeaksUser": 3600,
          "workdayTimeLeaksFinal": -9,
          "workdayDuration": 32609,
          "workdayDurationFinal": 28809,
          "workdayDurationConfig": 28800,
          "workdayTimeLeaksReal": 3800,
          "reports": [
            {
              "id": "153",
              "userId": "503",
              "type": "IDLE",
              "dateStart": "2026-05-26T14:10:05+00:00",
              "dateFinish": "2026-05-26T14:30:12+00:00",
              "duration": 1207,
              "active": false,
              "entryId": "2237",
              "reportType": "WORK",
              "reportText": "Client meeting",
              "systemText": null,
              "sourceStart": "IDLE_EVENT",
              "sourceFinish": "IDLE_EVENT",
              "ipStart": "203.0.113.24",
              "ipFinish": "203.0.113.24",
              "ipStartNetwork": false,
              "ipFinishNetwork": false
            }
          ]
        }
      ]
    },
    "user": {
      "id": 503,
      "active": true,
      "name": "Anna Smith",
      "firstName": "Anna",
      "lastName": "Smith",
      "workPosition": "Manager",
      "avatar": "",
      "personalGender": "F",
      "lastActivityDate": "2026-05-29T17:15:56+00:00"
    }
  }
}
```

## Error response example

400 — a required parameter is missing or a threshold is not a number:

```json
{
  "success": false,
  "error": {
    "code": "INVALID_PARAMS",
    "message": "userId, month (1..12), year are required; optional thresholds must be numeric"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 200 | `Warning: 299` | The filter is ignored by default; previous data is preserved and the warning is sent in the `Warning` header. |
| 400 (strict mode only) | `UNSUPPORTED_FILTER` | The server mode is disabled by default; until it is enabled, the endpoint preserves HTTP 200 and the previous data. |
| 400 | `INVALID_PARAMS` | `userId`, `month`, or `year` is missing or is not a positive integer, `month` is greater than 12, `idleMinutes` or `workdayHours` is not a number |
| 403 | `BITRIX_ACCESS_DENIED` | The role and time-control settings do not give the credential owner access to this employee's report: an employee with the simple report requested someone else's report, a department head without the full report requested the report of an employee outside their departments, or an employee without subordinates requested their own report while time control is switched off |
| 409 | `TIMEMAN_MODULE_NOT_ENABLED` | Time Management is not available on this portal: the Time Management tool is switched off in the portal settings, or the module is not included in the plan |
| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 is unavailable or returned a response of an unexpected shape |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not passed |
| 401 | `TOKEN_MISSING` | An app key was sent without a session token in `Authorization: Bearer` |
| 401 | `INVALID_SESSION` | The session token is invalid or has expired |
| 403 | `SCOPE_DENIED` | The key lacks the `timeman` scope |
| 429 | `RATE_LIMITED` | The general request limit was exceeded; the retry delay is in the `Retry-After` header |

The full list of common API errors — [Errors](/docs/errors).

## Known specifics

**A nonexistent employee is returned without an error.** For a nonexistent `userId`, the method returns `200`: `success` is `true`, `data.user` is `false`, and `data.report.days` is an empty array. The indicator that the employee does not exist is `data.user === false`, rather than `success`: that field indicates a successful request, not that the employee exists. Check `data.user` before interpreting empty days as "there were no absences".

**Employee IP addresses are personal data.** The `ipStart`, `ipFinish`, `ipStartNetwork`, and `ipFinishNetwork` fields are returned only to a department head and an administrator. Store and display them according to your company's personal data processing rules.

**Thresholds are rounded, and an out-of-range year is replaced with the current one.** Fractional `idleMinutes` and `workdayHours` are converted to integers, and `workdayHours` less than 1 is treated as 1. A year less than 1900 or greater than 3000 is replaced with the current year without an error, so compare the report's `dateStart` with the requested month.

## See also

- [Get time-control report settings](/docs/workday/time-control-report-settings)
- [List department employees](/docs/workday/time-control-users)
- [Explain an absence](/docs/workday/time-control-explain)
- [Time-control settings](/docs/workday/time-control-settings)
- [Workday history](/docs/workday/records)
- [Workday](/docs/workday)
