For AI agents: markdown of this page — /docs-content-en/cowork/off-peak.md documentation index — /llms.txt

Off-peak hours in the Cowork/Code subscription

During certain hours of the week the subscription quota is consumed more slowly — the same request takes a smaller share of the monthly limit. The subscription responses show whether the discount applies right now and when it starts.

The discount applies on its own, there is nothing to switch on. The offPeak block reports what has already been taken into account when the quota was consumed, so an app can offer to move a bulk job into a cheap hour. Off-peak hours are not in effect on this platform yet, so today the block does not arrive here at all.

Examples

curl — personal key

Terminal
curl https://vibecode.bitrix24.com/v1/cowork/state \
  -H "X-Api-Key: YOUR_API_KEY"

curl — OAuth application

Terminal
curl https://vibecode.bitrix24.com/v1/cowork/state \
  -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/cowork/state', {
  headers: { 'X-Api-Key': 'YOUR_API_KEY' },
})

const state = await res.json()
const off = state.offPeak

if (!off || !off.enabled) {
  console.log('Off-peak hours are not in effect for this subscription')
} else if (off.visible === 'now') {
  const discount = Math.round((1 - off.currentMultiplier) * 100)
  console.log(`Quota is consumed ${discount}% more slowly for another ${off.currentWindowEndsInHours} h`)
} else if (off.visible === 'next' && off.nextAtOrBelow) {
  console.log(`An off-peak hour starts in ${off.nextAtOrBelow.inHours} h`)
}

JavaScript — OAuth application

javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/cowork/state', {
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})

const state = await res.json()

Response fields

The block arrives in two responses and differs in composition.

Response Block composition
GET /v1/cowork/state The whole block — together with the grid and the touSavedPct field
GET /v1/cowork/me The block without the grid and without touSavedPct

Both keys are optional. The capability is switched on account by account, and until it is on, the offPeak and touSavedPct keys are absent from the response body entirely.

Field Type Description
offPeak object Off-peak hours block
offPeak.enabled boolean Whether off-peak hours are in effect for this subscription. When false, the remaining fields are empty and every hour is at full price
offPeak.visible string What to show the user: now — the discount applies right now, next — a meaningful discount starts within the next few hours, none — nothing to show
offPeak.timezone string or null Schedule timezone — an identifier from the IANA time zone database, for example UTC. The day of week and hour are computed against it
offPeak.currentMultiplier number Quota-consumption multiplier for the current hour, greater than 0 and no more than 1. A value of 0.5 means the quota is consumed twice as slowly, a value of 1 means no discount
offPeak.currentWindowEndsInHours number or null In how many hours consumption stops being this favourable. null when it never gets more expensive within a week ahead
offPeak.nextAtOrBelow object or null The nearest hour whose discount is deep enough to be worth showing. null when there is no such hour within a week ahead
offPeak.nextAtOrBelow.inHours number How many hours until that hour arrives
offPeak.nextAtOrBelow.multiplier number Quota-consumption multiplier in that hour
offPeak.perModel boolean true when the schedule was picked for a single model rather than shared by every model of the subscription
offPeak.modelName string or null Name of the model the schedule was picked for. null when the schedule is shared
offPeak.grid array or null Grid of multipliers grid[day][hour]. Seven rows of 24 values. Day 0 is Sunday, day 6 is Saturday. The hour is 0 to 23 in the timezone time zone. Arrives only in GET /v1/cowork/state
offPeak.nowCell object or null The grid cell that the current moment maps to. Computed on the server, so it matches currentMultiplier
offPeak.nowCell.dow number Day of week, from 0 (Sunday) to 6 (Saturday)
offPeak.nowCell.hour number Hour, from 0 to 23
touSavedPct number or null What share of the subscription MONTHLY ALLOWANCE the off-peak hours gave back over the current billing period, in percent with one decimal place, capped at 100. Arrives only in GET /v1/cowork/state

Response example

A GET /v1/cowork/state response. It shows the off-peak block and the saved share, the remaining fields are in Cowork/Code subscription state.

JSON
{
  "offPeak": {
    "enabled": true,
    "visible": "now",
    "timezone": "UTC",
    "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
}

The same moment in a GET /v1/cowork/me response — without the grid and without the saved share:

JSON
{
  "offPeak": {
    "enabled": true,
    "visible": "now",
    "timezone": "UTC",
    "currentMultiplier": 0.5,
    "currentWindowEndsInHours": 6,
    "nextAtOrBelow": { "inHours": 1, "multiplier": 0.5 },
    "perModel": false,
    "modelName": null,
    "nowCell": { "dow": 1, "hour": 3 }
  }
}

Error response example

404 — subscription not activated:

JSON
{
  "success": false,
  "error": {
    "code": "COWORK_NOT_ACTIVATED",
    "message": "No active Cowork/Code subscription for this user+portal"
  }
}

Errors

HTTP Code Description
401 MISSING_API_KEY The X-Api-Key header was not passed
401 INVALID_API_KEY Invalid API key
403 INSUFFICIENT_SCOPE The key lacks the vibe:cowork scope
404 COWORK_NOT_ACTIVATED No Cowork/Code subscription found for this user and Bitrix24 account
500 INVALID_TIER_CONFIGURATION The tier configuration on the platform is invalid
503 COWORK_FEATURE_DISABLED Cowork/Code is disabled at the platform level

The full list of common API errors — Errors.

The hint in the exhausted-quota refusal

When a subscription quota window is exhausted, POST /v1/chat/completions answers 402 with the code cowork_quota_exhausted. The body of that refusal carries an optional error.offPeakHint field — in how many hours the block is lifted and which consumption multiplier will be in effect at that moment. An app reads it to decide whether simply waiting is enough.

Field Type Description
error.offPeakHint object Hint about the off-peak hour at the moment the block is lifted
error.offPeakHint.inHours number In how many hours the block is lifted. Rounded up — the same source as the Retry-After header
error.offPeakHint.multiplier number The quota-consumption multiplier that will be in effect at that moment
JSON
{
  "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 }
  }
}

The hint arrives only when the capability is enabled for the Bitrix24 account and the unblock moment falls into a discounted hour. Otherwise the offPeakHint key is absent from the body entirely — it never arrives as null, exactly like the other off-peak fields. Off-peak hours are not in effect on this platform yet, so today the hint does not arrive here at all.

The multiplier is computed exactly at the unblock moment; the nearest off-peak window is not searched for. If that hour carries no discount, no hint arrives either — even when a cheap hour starts a little later.

Known specifics

Check for the presence of the offPeak key, not for its value. Until the capability is on for the account, the key is absent from the response body entirely — it does not arrive as null. A client that reads state.offPeak.enabled without checking offPeak itself will throw on such a response.

Branch the interface on visible, not on a threshold of your own. The threshold above which a discount is worth showing is decided on the platform side and is not exposed. Comparing currentMultiplier against a number baked into the client will diverge from the platform the next time that threshold is tuned.

The multiplier is a consumption coefficient, not the discount size. A value of 0.62 means the call takes 62% of the quota share it would take without a discount, that is, consumption is reduced by 38%. The discount size is computed as 1 - currentMultiplier.

nextAtOrBelow looks for a meaningful discount, not for any price drop. That is how it differs from the nextWindow field in the Off-peak schedule: there it is the nearest strictly cheaper hour, here it is the nearest hour whose discount is deep enough to show.

The block stays silent while the subscription is not active. A paused or cancelled subscription gets enabled: false — the schedule no longer applies to it.

touSavedPct is measured against the monthly allowance, not against consumption. The denominator is the same one the month quota bar is a share of, so the two read side by side: «N% used» and «off-peak hours gave back M%». The value is null only when nothing has been saved yet in the current period.

See also