
## Список записей по всему порталу

`GET /v1/task-time`

Возвращает записи учёта времени со всех задач портала одним запросом, с отбором по сотруднику, периоду и задаче. Применяется, когда список задач заранее неизвестен — например, для отчёта о времени сотрудника за месяц.

## Параметры

| Параметр | Тип | По умолч. | Описание |
|----------|-----|-----------|---------|
| `userId` (query) | number | — | Идентификатор сотрудника. Список: `GET /v1/users`. Без параметра возвращаются записи всех сотрудников, доступных владельцу ключа |
| `from` (query) | string | — | Нижняя граница периода включительно по полю `createdDate`. Принимает ISO 8601 `2026-05-01T00:00:00+03:00` и дату `2026-05-01` |
| `to` (query) | string | — | Верхняя граница периода включительно по полю `createdDate`. Формат — как у `from`. Дата без времени включает весь день целиком |
| `taskId` (query) | number | — | Сузить выборку до одной задачи, поверх `userId`, `from` и `to`. Идентификатор: `GET /v1/tasks` |
| `limit` (query) | number | `50` | Размер страницы, от 1 до 500. Значение больше 500 приводится к 500 |
| `offset` (query) | number | `0` | Смещение в выборке. Должно быть кратно `limit` |

**Пагинация.** За один вызов возвращается до 500 записей. При `limit` больше 50 Вайбкод собирает окно на своей стороне и отдаёт его целиком. Значение `offset` кратно `limit` — `0`, `limit`, `2 × limit` и так далее, иначе приходит `400 INVALID_OFFSET`. Признак того, что за окном есть ещё записи, — `meta.hasMore`.

**Формат фильтра.** Generic-envelope `filter` не поддерживается. Используйте именованные query-параметры `userId`, `taskId`, `from` и `to`. Любой bracket-ключ `filter[...]`, непустое значение `filter`, JSON-вариант, включая `filter={}`, или повтор параметра `filter` возвращает `400 UNSUPPORTED_FILTER`. Единственный пустой параметр `filter=` означает отсутствие фильтра и сохраняет статус `200`.

## Примеры

### curl — личный ключ

```bash
curl "https://vibecode.bitrix24.tech/v1/task-time?userId=1&from=2026-05-01&to=2026-05-31" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth-приложение

```bash
curl "https://vibecode.bitrix24.tech/v1/task-time?userId=1&from=2026-05-01&to=2026-05-31" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — личный ключ

```javascript
const url = 'https://vibecode.bitrix24.tech/v1/task-time?userId=1&from=2026-05-01&to=2026-05-31'

const res = await fetch(url, {
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
  },
})

const { success, data, meta } = await res.json()
const totalSeconds = data.reduce((sum, r) => sum + r.seconds, 0)
console.log(`За период: ${totalSeconds / 3600} ч, записей ${meta.total}`)
```

### JavaScript — OAuth-приложение

```javascript
const url = 'https://vibecode.bitrix24.tech/v1/task-time?userId=1&from=2026-05-01&to=2026-05-31'

const res = await fetch(url, {
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})

const { success, data, meta } = await res.json()
```

## Поля ответа

| Поле | Тип | Описание |
|------|-----|---------|
| `success` | boolean | Всегда `true` при успехе |
| `data` | array | Массив записей учёта времени |
| `data[].id` | number | Идентификатор записи |
| `data[].taskId` | number | Идентификатор родительской задачи. Карточка: `GET /v1/tasks/:id` |
| `data[].userId` | number | Автор записи. Профиль: `GET /v1/users/:userId` |
| `data[].seconds` | number | Длительность в секундах |
| `data[].minutes` | number | Длительность в минутах, производное от `seconds` |
| `data[].commentText` | string | Комментарий к записи. Если комментарий пуст, приходит `""` |
| `data[].source` | string | Источник: `2` — REST API |
| `data[].createdDate` | datetime | Когда запись была создана |
| `data[].dateStart` | datetime | Начало учтённого интервала, заполняется автоматически |
| `data[].dateStop` | datetime | Окончание учтённого интервала, заполняется автоматически |
| `meta.total` | number | Общее число записей, соответствующих отбору |
| `meta.limit` | number | Применённый размер страницы |
| `meta.offset` | number | Применённое смещение |
| `meta.hasMore` | boolean | Есть ли ещё записи за пределами текущего окна |

## Пример ответа

```json
{
  "success": true,
  "data": [
    {
      "id": 161,
      "taskId": 3881,
      "userId": 1,
      "commentText": "Код-ревью",
      "seconds": 900,
      "minutes": 15,
      "source": "2",
      "createdDate": "2026-05-13T16:15:41+03:00",
      "dateStart": "2026-05-13T17:15:41+03:00",
      "dateStop": "2026-05-13T17:15:41+03:00"
    },
    {
      "id": 159,
      "taskId": 3867,
      "userId": 1,
      "commentText": "Проверка сборки",
      "seconds": 600,
      "minutes": 10,
      "source": "2",
      "createdDate": "2026-05-06T10:46:12+03:00",
      "dateStart": "2026-05-06T11:46:12+03:00",
      "dateStop": "2026-05-06T11:46:12+03:00"
    },
    {
      "id": 157,
      "taskId": 289,
      "userId": 1,
      "commentText": "Подготовка черновика",
      "seconds": 1800,
      "minutes": 30,
      "source": "2",
      "createdDate": "2026-05-06T10:45:47+03:00",
      "dateStart": "2026-05-06T11:45:47+03:00",
      "dateStop": "2026-05-06T11:45:47+03:00"
    }
  ],
  "meta": {
    "total": 3,
    "limit": 50,
    "offset": 0,
    "hasMore": false
  }
}
```

## Пример ответа при ошибке

400 — `userId` не является положительным целым числом:

```json
{
  "success": false,
  "error": {
    "code": "INVALID_USER_ID",
    "message": "userId must be a positive integer"
  }
}
```

## Ошибки

| HTTP | Код | Описание |
|------|-----|---------|
| 400 | `INVALID_USER_ID` | `userId` не положительное целое число |
| 400 | `INVALID_TASK_ID` | `taskId` не положительное целое число |
| 400 | `INVALID_OFFSET` | `offset` не кратен `limit` — постраничная выборка требует значений `0`, `limit`, `2 × limit` и так далее |
| 400 | `UNSUPPORTED_FILTER` | Передан неподдерживаемый `filter`; используйте `userId`, `taskId`, `from` и `to` |
| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `task` |
| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |

Полный список общих ошибок API — [Ошибки](/docs/errors).

## Известные особенности

**Сортировка от новых к старым.** Первыми возвращаются последние добавленные записи. `limit` и `offset` работают поверх этой сортировки.

**Некорректная дата не вызывает ошибку.** Значение `from` или `to`, которое не разбирается как дата, даёт пустую выборку и статус `200`. Формат проверяйте на своей стороне.

**Смещение за пределы выборки.** Запрос с `offset` больше числа записей возвращает пустой массив, а `meta.total` в таком ответе повторяет `offset`. Конец выдачи определяйте по `meta.hasMore` со значением `false`, а не сравнением `offset` с `meta.total`.

## Смотрите также

- [Список записей задачи](./list.md)
- [Добавить запись](./create.md)
- [Учёт времени задач](/docs/entities/tasks/time)
- [Задачи](/docs/entities/tasks)
- [Сотрудники](/docs/entities/users)
- [Лимиты и оптимизация](/docs/optimization)
