Для AI-агентов: markdown этой страницы — /docs-content/workday/records.md индекс документации — /llms.txt
История рабочего дня
GET /v1/workday/records
Возвращает историю рабочих дней сотрудника: время начала и завершения, отработанные секунды, длительность перерывов и смещение, вычисленное для каждой записи по текущей IANA-зоне TIME_ZONE из профиля сотрудника. Записи отбираются по обязательному идентификатору сотрудника и периоду, а без периода возвращается окно за последние 7 дней.
Для ручки нужны оба скоупа: timeman и один из user_brief, user_basic или user. Пустая страница возвращается без дополнительного запроса к профилю. Если для непустой страницы нельзя получить текущую IANA-зону профиля или вычислить смещение по её историческим правилам, ручка возвращает 502 BITRIX_UNAVAILABLE без частичных данных. Смену назначенной зоны после создания записи API обнаружить не может, поэтому этот случай не приводит к 502.
Параметры
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
userId (query) |
number | да | Идентификатор сотрудника, целое больше нуля. Свой идентификатор — GET /v1/users/me, список сотрудников — GET /v1/users |
from (query) |
string | нет | Начало периода, ISO-8601 с явным смещением или Z — например 2026-06-01T00:00:00Z. Отбирает по времени начала дня. Дата без времени возвращает 400. Знак плюса в смещении кодируется как %2B, иначе значение приходит с пробелом вместо знака и не распознаётся |
to (query) |
string | нет | Конец периода в том же формате. Значение from позже to возвращает 400 |
limit (query) |
number | нет | Записей на страницу, от 1 до 50. По умолчанию 50 |
offset (query) |
number | нет | Смещение от начала выборки, целое от нуля. По умолчанию 0. Не сочетается с page |
page (query) |
number | нет | Номер страницы от единицы, равнозначен offset со значением (page − 1) × limit. Не сочетается с offset |
order (query) |
string | нет | Порядок по времени начала дня: desc — от новых к старым, asc — от старых к новым. По умолчанию desc |
Оба конца периода необязательны: если ни from, ни to не переданы, выборка ограничивается последними 7 днями от момента запроса. Параметр запроса вне этого списка возвращает 400 INVALID_PARAMS с именем переданного ключа и перечнем принимаемых.
Примеры
curl — личный ключ
curl -H "X-Api-Key: YOUR_API_KEY" \
"https://vibecode.bitrix24.tech/v1/workday/records?userId=1&from=2020-01-01T00:00:00Z&limit=3&order=asc"
curl — записи другого сотрудника
curl -H "X-Api-Key: YOUR_API_KEY" \
"https://vibecode.bitrix24.tech/v1/workday/records?userId=503&from=2020-01-01T00:00:00Z&limit=3&order=asc"
curl — OAuth-приложение
curl -H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
"https://vibecode.bitrix24.tech/v1/workday/records?userId=1&from=2020-01-01T00:00:00Z&limit=3&order=asc"
JavaScript — личный ключ
const params = new URLSearchParams({
userId: '1',
from: '2020-01-01T00:00:00Z',
limit: '3',
order: 'asc',
})
const res = await fetch(`https://vibecode.bitrix24.tech/v1/workday/records?${params}`, {
headers: { 'X-Api-Key': 'YOUR_API_KEY' },
})
const { data, meta } = await res.json()
for (const record of data) {
console.log(record.startTime, '— отработано секунд:', record.duration)
}
console.log('Есть ещё записи:', meta.hasMore)
JavaScript — OAuth-приложение
const params = new URLSearchParams({
userId: '1',
from: '2020-01-01T00:00:00Z',
limit: '3',
order: 'asc',
})
const res = await fetch(`https://vibecode.bitrix24.tech/v1/workday/records?${params}`, {
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
},
})
const { data, meta } = await res.json()
Поля ответа
| Поле | Тип | Описание |
|---|---|---|
success |
boolean | true при успешном ответе |
data |
array | Массив записей рабочего дня. Пустой массив, когда под период не попала ни одна запись |
data[].id |
number | Идентификатор записи рабочего дня |
data[].userId |
number | Идентификатор сотрудника, которому принадлежит запись |
data[].startTime |
string | Время начала дня, ISO-8601. Строка сохраняет смещение портала и не переписывается в часовой пояс сотрудника |
data[].endTime |
string | null | Время завершения дня в том же формате. У незакрытого дня поля нет либо приходит null |
data[].duration |
number | null | Отработано за день, в секундах. У незакрытого дня поля нет либо приходит null |
data[].breakLength |
number | null | Суммарная длительность перерывов за день, в секундах. У незакрытого дня поля нет либо приходит null |
data[].state.status |
string | Состояние записи в нижнем регистре. У закрытого дня — closed |
data[].state.recommendedCloseTime |
string | null | Рекомендованное время закрытия дня. На проверенных записях null |
data[].isApproved |
boolean | null | Признак подтверждённой записи рабочего дня. У незакрытого дня поля нет либо приходит null |
data[].tzOffset |
number | Обязательное смещение в секундах восточнее UTC, вычисленное для момента startTime по историческим правилам текущей IANA-зоны TIME_ZONE из профиля сотрудника. Не подтверждает, что эта зона была назначена сотруднику при создании записи |
meta.hasMore |
boolean | true, когда страница заполнена до limit и за ней есть продолжение |
meta.total |
number | Количество записей в выборке — сумма offset и длины страницы. Приходит только когда страница короче limit |
meta.from |
string | Нижняя граница применённого периода. Приходит, когда она задана — переданным from либо подставленным окном за последние 7 дней |
meta.to |
string | Верхняя граница применённого периода. У периода, открытого сверху (передан только from), поля нет |
Пример ответа
Первые три записи от старых к новым, ?userId=1&from=2020-01-01T00:00:00Z&limit=3&order=asc:
{
"success": true,
"data": [
{
"id": 1,
"userId": 1,
"startTime": "2020-04-23T13:42:10+03:00",
"endTime": "2020-04-23T17:53:26+03:00",
"duration": 12999,
"breakLength": 2077,
"state": { "status": "closed", "recommendedCloseTime": null },
"isApproved": true,
"tzOffset": 7200
},
{
"id": 5,
"userId": 1,
"startTime": "2020-04-24T09:16:12+03:00",
"endTime": "2020-04-24T16:30:13+03:00",
"duration": 26041,
"breakLength": 0,
"state": { "status": "closed", "recommendedCloseTime": null },
"isApproved": true,
"tzOffset": 7200
},
{
"id": 7,
"userId": 1,
"startTime": "2020-04-27T09:04:11+03:00",
"endTime": "2020-04-27T18:00:00+03:00",
"duration": 32149,
"breakLength": 0,
"state": { "status": "closed", "recommendedCloseTime": null },
"isApproved": true,
"tzOffset": 7200
}
],
"meta": { "hasMore": true, "from": "2020-01-01T00:00:00Z" }
}
Последняя страница той же выборки, ?userId=1&from=2020-01-01T00:00:00Z&offset=40&order=asc — записей пришло меньше limit, поэтому в meta появляется total:
{
"success": true,
"data": [
{
"id": 163,
"userId": 1,
"startTime": "2024-07-24T17:34:51+03:00",
"endTime": "2024-07-25T02:35:00+03:00",
"duration": 32409,
"breakLength": 0,
"state": { "status": "closed", "recommendedCloseTime": null },
"isApproved": true,
"tzOffset": 7200
},
{
"id": 165,
"userId": 1,
"startTime": "2024-08-26T12:34:47+03:00",
"endTime": "2024-08-26T12:58:15+03:00",
"duration": 1408,
"breakLength": 0,
"state": { "status": "closed", "recommendedCloseTime": null },
"isApproved": true,
"tzOffset": 7200
},
{
"id": 167,
"userId": 1,
"startTime": "2024-09-05T12:07:30+03:00",
"endTime": "2025-03-27T14:53:08+03:00",
"duration": 17548827,
"breakLength": 311,
"state": { "status": "closed", "recommendedCloseTime": null },
"isApproved": true,
"tzOffset": 7200
},
{
"id": 171,
"userId": 1,
"startTime": "2026-05-05T09:52:51+03:00",
"endTime": "2026-05-05T09:53:37+03:00",
"duration": 20,
"breakLength": 26,
"state": { "status": "closed", "recommendedCloseTime": null },
"isApproved": true,
"tzOffset": 7200
},
{
"id": 173,
"userId": 1,
"startTime": "2026-05-05T09:55:38+03:00",
"endTime": "2026-05-05T09:55:39+03:00",
"duration": 1,
"breakLength": 0,
"state": { "status": "closed", "recommendedCloseTime": null },
"isApproved": true,
"tzOffset": 7200
},
{
"id": 175,
"userId": 1,
"startTime": "2026-05-05T10:07:02+03:00",
"endTime": "2026-05-05T10:07:06+03:00",
"duration": 2,
"breakLength": 2,
"state": { "status": "closed", "recommendedCloseTime": null },
"isApproved": true,
"tzOffset": 7200
}
],
"meta": { "hasMore": false, "total": 46, "from": "2020-01-01T00:00:00Z" }
}
Пример ответа при ошибке
400 — не передан обязательный параметр:
{
"success": false,
"error": {
"code": "MISSING_REQUIRED_PARAMS",
"message": "userId is required. Bitrix24 refuses this method without it; get the id from GET /v1/users/me."
}
}
Ошибки
| HTTP | Код | Описание |
|---|---|---|
| 400 | MISSING_REQUIRED_PARAMS |
Не передан userId |
| 400 | INVALID_PARAMS |
Нарушена валидация параметров: некорректный userId, limit вне диапазона от 1 до 50, дата без явного смещения или несуществующая в календаре, from позже to, offset вместе с page, повторённый дважды параметр, смещение больше 999 999 999, неизвестный параметр запроса |
| 401 | MISSING_API_KEY |
Не передан заголовок X-Api-Key |
| 401 | INVALID_API_KEY |
Неверный API-ключ |
| 401 | KEY_EXPIRED |
Срок действия API-ключа истёк |
| 401 | TOKEN_MISSING |
Ключу не настроены OAuth-токены для портала |
| 402 | ACCOUNT_FROZEN |
Баланс портала заморожен |
| 403 | SCOPE_DENIED |
Ключ не удовлетворяет составному требованию: нужны timeman и хотя бы один из user_brief, user_basic или user |
| 403 | BITRIX_ACCESS_DENIED |
Битрикс24 отказал в доступе — в том числе когда у ключа нет права читать табель указанного сотрудника |
| 409 | TIMEMAN_MODULE_NOT_ENABLED |
Битрикс24 не принял метод. Вызовите GET /v1/workday/status: тот же отказ означает, что на портале выключен модуль учёта рабочего времени, ответ 200 — что портал не отдаёт историю рабочего дня, а текущее состояние дня остаётся доступным |
| 422 | BITRIX_ERROR |
Прочие отказы Битрикс24 — текст в message |
| 429 | RATE_LIMITED |
Превышен лимит темпа этого эндпоинта — 30 запросов в минуту на ключ. Для ключа приложения счёт ведётся отдельно по каждому сотруднику |
| 429 | RATE_LIMITED |
Превышен лимит запросов портала Битрикс24 |
| 502 | BITRIX_UNAVAILABLE |
Портал Битрикс24 недоступен или не удалось получить текущую IANA-зону профиля сотрудника и вычислить по ней смещение записи; неизвестная прежняя зона не вызывает эту ошибку |
Полный список общих ошибок API — Ошибки.
Известные особенности
- Ключ не сужает права до личности вызывающего. Личный ключ
vibe_api_…обращается к порталу через входящий вебхук и читает табель правами того сотрудника Битрикс24, под которым этот вебхук выпущен, — то есть правами создателя ключа, а не того, кто ключ применяет. Владелец такого ключа переборомuserIdвыгружает приходы и уходы всей компании. Ключ приложенияvibe_app_…работает иначе — запрос идёт правами сотрудника, открывшего сессию. Кто вправе читать чужой табель, решает Битрикс24, а не платформа: администратор портала или прямой руководитель сотрудника. - Ответ называет применённый период.
meta.fromиmeta.toприходят и тогда, когда период не передавали: иначеmeta.totalсо значением0невозможно отличить от «у сотрудника нет записей вообще», хотя это означает лишь «нет записей за последние 7 дней». У периода, открытого с одной стороны, приходит только заданная граница. - Параметр нельзя передавать дважды.
?userId=1&userId=2возвращает400, а не выбирает одно из значений молча. meta.totalприходит только на короткой странице. На полной странице поля в ответе нет вовсе, поэтому обработчик, читающий его безусловно, получитundefined. Значение верно на момент ответа: приorder=descзапись, добавленная между двумя запросами, сдвигает окно выдачи.- Смещение в
startTimeиendTime— не часовой пояс сотрудника. Обе строки сохраняются с одним и тем же смещением портала и не переписываются.tzOffsetвычисляется для абсолютного моментаstartTimeпо историческим правилам текущей IANA-зоныTIME_ZONEиз профиля сотрудника. Чтобы получить местное время в этой зоне, разберите абсолютный момент изstartTimeи применитеtzOffset. Если после создания записи сотруднику назначили другую IANA-зону, API не знает прежнее назначение: возвращённое значение относится к текущей зоне профиля и не доказывает, какая зона была назначена тогда. - Длительности приходят в секундах.
durationиbreakLength— целые числа секунд. У соседнегоGET /v1/workday/statusдлительности отдаются строкой видаHH:MM:SS, поэтому разбор с одной страницы на другую не переносится. - Отбор ограничен сотрудником и временем начала дня. Других осей у эндпоинта нет: сузить выборку по времени завершения, длительности или признаку подтверждения нельзя, сортировка тоже идёт только по времени начала. Нужный срез собирается из полученных записей на своей стороне.
- Вся история обходится страницами по 50 записей. Больше 50 записей за запрос эндпоинт не отдаёт, глубина берётся через
offsetилиpage. Признак конца выдачи —meta.hasMoreсо значениемfalse. Запрос со смещением за концом выборки возвращает пустой массив, иmeta.totalв таком ответе не приходит.