Для AI-агентов: markdown этой страницы — /docs-content/cowork/off-peak.md индекс документации — /llms.txt
Выгодные часы в подписке Cowork/Code
В отдельные часы недели квота подписки расходуется медленнее — один и тот же запрос забирает меньшую долю месячного лимита. Ответы подписки показывают, действует ли скидка прямо сейчас и когда она начнётся.
Скидка применяется сама, включать её не нужно. Блок offPeak показывает то, что уже учтено при расходе квоты, чтобы приложение могло предложить перенести объёмную задачу на выгодный час.
Примеры
curl — личный ключ
curl https://vibecode.bitrix24.tech/v1/cowork/state \
-H "X-Api-Key: YOUR_API_KEY"
curl — OAuth-приложение
curl https://vibecode.bitrix24.tech/v1/cowork/state \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN"
JavaScript — личный ключ
const res = await fetch('https://vibecode.bitrix24.tech/v1/cowork/state', {
headers: { 'X-Api-Key': 'YOUR_API_KEY' },
})
const state = await res.json()
const off = state.offPeak
if (!off || !off.enabled) {
console.log('Выгодные часы для этой подписки не действуют')
} else if (off.visible === 'now') {
const discount = Math.round((1 - off.currentMultiplier) * 100)
console.log(`Квота расходуется на ${discount}% медленнее ещё ${off.currentWindowEndsInHours} ч`)
} else if (off.visible === 'next' && off.nextAtOrBelow) {
console.log(`Выгодный час начнётся через ${off.nextAtOrBelow.inHours} ч`)
}
JavaScript — OAuth-приложение
const res = await fetch('https://vibecode.bitrix24.tech/v1/cowork/state', {
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
},
})
const state = await res.json()
Поля ответа
Блок приезжает в двух ответах и различается составом.
| Ответ | Состав блока |
|---|---|
ОтветGET /v1/cowork/state |
Состав блокаБлок целиком — вместе с сеткой grid и полем touSavedPct |
ОтветGET /v1/cowork/me |
Состав блокаБлок без сетки и без touSavedPct |
offPeak приходит в каждом успешном ответе обеих ручек, touSavedPct — в каждом успешном ответе GET /v1/cowork/state.
| Поле | Тип | Описание |
|---|---|---|
ПолеoffPeak |
Типobject | ОписаниеБлок выгодных часов |
ПолеoffPeak.enabled |
Типboolean | ОписаниеДействуют ли выгодные часы для этой подписки. При false остальные поля пустые, все часы идут по полной цене |
ПолеoffPeak.visible |
Типstring | ОписаниеЧто показать пользователю: now — скидка действует прямо сейчас, next — заметная скидка начнётся в ближайшие часы, none — показывать нечего |
ПолеoffPeak.timezone |
Типstring или null | ОписаниеЧасовой пояс расписания — идентификатор из базы часовых поясов IANA, например Europe/Moscow. По нему считаются день недели и час |
ПолеoffPeak.currentMultiplier |
Типnumber | ОписаниеМножитель расхода квоты в текущий час, больше 0 и не больше 1. Значение 0.5 означает, что квота расходуется вдвое медленнее, значение 1 — без скидки |
ПолеoffPeak.currentWindowEndsInHours |
Типnumber или null | ОписаниеЧерез сколько часов расход перестанет быть таким же выгодным. null, если в пределах недели вперёд дороже не станет |
ПолеoffPeak.nextAtOrBelow |
Типobject или null | ОписаниеБлижайший час, скидка в котором достаточна, чтобы её показывать. null, когда такого часа нет в пределах недели вперёд |
ПолеoffPeak.nextAtOrBelow.inHours |
Типnumber | ОписаниеЧерез сколько часов наступит этот час |
ПолеoffPeak.nextAtOrBelow.multiplier |
Типnumber | ОписаниеМножитель расхода квоты в этом часе |
ПолеoffPeak.perModel |
Типboolean | Описаниеtrue, если расписание подобрано под одну модель, а не общее для всех моделей подписки |
ПолеoffPeak.modelName |
Типstring или null | ОписаниеНазвание модели, под которую подобрано расписание. null, когда расписание общее |
ПолеoffPeak.grid |
Типarray или null | ОписаниеСетка множителей grid[день][час]. Семь строк по 24 значения. День 0 — воскресенье, день 6 — суббота. Час — от 0 до 23 в часовом поясе timezone. Приходит только в GET /v1/cowork/state |
ПолеoffPeak.nowCell |
Типobject или null | ОписаниеЯчейка сетки, которой соответствует текущий момент. Считается на сервере, поэтому совпадает с currentMultiplier |
ПолеoffPeak.nowCell.dow |
Типnumber | ОписаниеДень недели, от 0 (воскресенье) до 6 (суббота) |
ПолеoffPeak.nowCell.hour |
Типnumber | ОписаниеЧас, от 0 до 23 |
ПолеtouSavedPct |
Типnumber или null | ОписаниеКакую долю МЕСЯЧНОГО ОБЪЁМА подписки вернули выгодные часы за текущий расчётный период, в процентах с одним знаком после запятой, не больше 100. Приходит только в GET /v1/cowork/state |
Пример ответа
Ответ GET /v1/cowork/state. Показаны блок выгодных часов и доля сэкономленного, остальные поля — в Состоянии подписки Cowork/Code.
{
"offPeak": {
"enabled": true,
"visible": "now",
"timezone": "Europe/Moscow",
"currentMultiplier": 0.5,
"currentWindowEndsInHours": 6,
"nextAtOrBelow": { "inHours": 1, "multiplier": 0.5 },
"perModel": false,
"modelName": null,
"grid": [
[0.75, 0.87, 0.87, 0.87, 0.87, 0.87, 0.87, 0.87, 0.75, 0.75, 0.75, 0.75, 0.62, 0.5, 0.75, 0.87, 1, 1, 0.87, 0.87, 0.87, 0.75, 0.75, 0.75],
[0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.62, 0.62, 0.62, 0.62, 0.75, 0.75, 0.87, 0.87, 0.75, 0.75, 0.75, 0.75, 0.62, 0.5, 0.5],
[0.5, 0.5, 0.62, 0.62, 0.75, 0.62, 0.62, 0.62, 0.75, 0.75, 0.87, 1, 1, 0.87, 0.75, 0.87, 0.87, 0.75, 0.75, 0.62, 0.62, 0.62, 0.62, 0.62],
[0.75, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.62, 0.62, 0.62, 0.75, 0.75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.87],
[1, 1, 1, 1, 1, 0.87, 0.87, 0.87, 0.87, 1, 1, 1, 1, 1, 1, 0.87, 0.75, 0.75, 0.75, 0.87, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 0.87, 0.87, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.87, 0.87, 0.75, 0.87],
[0.75, 0.62, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.62, 0.75, 0.75, 0.62, 0.62, 0.75, 0.75, 0.87, 0.87, 1, 1, 1, 1, 0.87]
],
"nowCell": { "dow": 1, "hour": 3 }
},
"touSavedPct": 12.4
}
Тот же момент в ответе GET /v1/cowork/me — без сетки и без доли сэкономленного:
{
"offPeak": {
"enabled": true,
"visible": "now",
"timezone": "Europe/Moscow",
"currentMultiplier": 0.5,
"currentWindowEndsInHours": 6,
"nextAtOrBelow": { "inHours": 1, "multiplier": 0.5 },
"perModel": false,
"modelName": null,
"nowCell": { "dow": 1, "hour": 3 }
}
}
Пример ответа при ошибке
404 — подписка не активирована:
{
"success": false,
"error": {
"code": "COWORK_NOT_ACTIVATED",
"message": "No active Cowork/Code subscription for this user+portal"
}
}
Ошибки
| HTTP | Код | Описание |
|---|---|---|
| HTTP401 | КодMISSING_API_KEY |
ОписаниеНе передан заголовок X-Api-Key |
| HTTP401 | КодINVALID_API_KEY |
ОписаниеКлюч не опознан: такой строки на платформе нет |
| HTTP403 | КодINSUFFICIENT_SCOPE |
ОписаниеУ ключа нет скоупа vibe:cowork |
| HTTP404 | КодCOWORK_NOT_ACTIVATED |
ОписаниеПодписка Cowork/Code не найдена для пары пользователь и портал |
| HTTP500 | КодINVALID_TIER_CONFIGURATION |
ОписаниеКонфигурация тарифов на платформе некорректна |
| HTTP503 | КодCOWORK_FEATURE_DISABLED |
ОписаниеCowork/Code отключён на уровне платформы |
Полный список общих ошибок API — Ошибки.
Подсказка в отказе по исчерпанной квоте
Когда окно квоты подписки исчерпано, POST /v1/chat/completions отвечает 402 с кодом cowork_quota_exhausted. В теле такого отказа приезжает необязательное поле error.offPeakHint — через сколько часов блокировка снимется и какой множитель расхода будет действовать в этот момент. По нему приложение решает, достаточно ли просто подождать.
| Поле | Тип | Описание |
|---|---|---|
Полеerror.offPeakHint |
Типobject | ОписаниеПодсказка о выгодном часе на момент разблокировки |
Полеerror.offPeakHint.inHours |
Типnumber | ОписаниеЧерез сколько часов снимется блокировка. Округляется вверх — тот же источник, что и у заголовка Retry-After |
Полеerror.offPeakHint.multiplier |
Типnumber | ОписаниеМножитель расхода квоты, который будет действовать в этот момент |
{
"error": {
"message": "Cowork/Code quota exhausted for the 5h window. Wait until 2026-08-10T15:00:00.000Z or upgrade your tier.",
"type": "insufficient_quota",
"code": "cowork_quota_exhausted",
"window": "5h",
"resetAt": "2026-08-10T15:00:00.000Z",
"nextTier": "PRO",
"offPeakHint": { "inHours": 3, "multiplier": 0.5 }
}
}
Подсказка приходит, только когда момент разблокировки попадает в час со скидкой. В остальных случаях ключа offPeakHint в теле нет вовсе — со значением null он не приходит.
Множитель считается ровно на момент разблокировки, ближайшее выгодное окно при этом не ищется. Если в этот час скидки не будет, подсказки не будет тоже — даже когда выгодный час наступит немного позже.
Известные особенности
Ветвите интерфейс по visible, а не по собственному порогу. Порог, начиная с которого скидку стоит показывать, платформа решает на своей стороне и наружу не отдаёт. Сравнение currentMultiplier с числом, вписанным в клиент, разойдётся с платформой при следующей настройке порога.
Множитель — коэффициент расхода, а не размер скидки. Значение 0.62 означает, что вызов забирает 62% от той доли квоты, которую он забрал бы без скидки, то есть расход снижен на 38%. Размер скидки считается как 1 - currentMultiplier.
nextAtOrBelow ищет заметную скидку, а не любое удешевление. Этим он отличается от поля nextWindow в Расписании выгодных часов: там ближайший строго более дешёвый час, здесь — ближайший час, скидка в котором достаточна для показа.
Блок молчит, пока подписка не активна. Приостановленная и отменённая подписка получает enabled: false — расписание к ней уже не применяется.
touSavedPct считается от месячного объёма подписки, а не от расхода. Знаменатель тот же, что у полосы месячной квоты, поэтому две доли читаются рядом: «израсходовано N%» и «выгодные часы вернули M%». Значение null приходит, только когда за текущий период ещё ничего не сэкономлено.