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

Cowork/Code subscription state

GET /v1/cowork/state

Returns a full snapshot of the Cowork/Code subscription state: the tier, the subscription state, quota usage across three windows in percent, a recommendation to wait or move to a higher tier, and the tier catalog for a comparison table.

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' },
})

if (!res.ok) {
  const { error } = await res.json()
  console.error(error.code, error.message)
} else {
  const state = await res.json()
  const tight = state.windows[state.bottleneck]
  console.log(`Window ${state.bottleneck}: ${tight.pctUsed}%`, tight.exhausted ? 'exhausted' : 'ok')
}

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

Field Type Description
subscription.tier string Current tier: FREE, PRO, MAX, ULTRA
subscription.state string Subscription state: ACTIVE, PAUSED, CANCELLED
subscription.currentPeriodStart string Start of the billing period (ISO 8601)
subscription.currentPeriodEnd string End of the billing period (ISO 8601)
subscription.nextChargeAt string or null Date of the next charge, null for the FREE tier
subscription.cancelAtPeriodEnd boolean true if cancellation is scheduled at the end of the period
subscription.pendingTier string or null A tier below the current one that the seat will move to at the end of the paid term: FREE, PRO or MAX. The transition date is subscription.paidThroughAt. A move to a higher tier applies immediately and never appears in this field. null when no downgrade is scheduled, and also for a free seat, for a subscription in the PAUSED or CANCELLED state and for a subscription with cancelAtPeriodEnd: true
subscription.termMonths number How many months the seat is paid for in advance: 1, 3, 6 or 12. 1 for a monthly subscription
subscription.paidThroughAt string The date the seat is paid through (ISO 8601). Different from currentPeriodEnd: the billing period is the quota window and rolls every 30 days regardless of the term. paidThroughAt is when the move to pendingTier happens, and access after a cancellation also runs through that date. The field is only meaningful while nextChargeAt is not null. On a free seat it is written once when the seat is created and never moves again, so it drifts into the past — there is nothing to pay for there and nothing to renew
windows.fiveHour object Sliding 5-hour window
windows.fiveHour.pctUsed number Usage in percent, integer 0–100
windows.fiveHour.resetAt string Window reset time (ISO 8601)
windows.fiveHour.exhausted boolean true if the window quota is exhausted
windows.week object Sliding weekly window, the same fields as fiveHour
windows.month object Monthly window, matches the billing period, the same fields as fiveHour
bottleneck string The busiest window: fiveHour, week, or month
recommendation.reason string Reason for the recommendation: none, approaching (≥75% and a higher tier exists), window_exhausted (the 5-hour or weekly window is exhausted, the monthly one is not yet), fully_exhausted (the monthly window is exhausted)
recommendation.triggerWindow string or null The window that triggered the recommendation, null when reason: none
recommendation.wait object or null When the block lifts: { window, resetAt }, null if no window is exhausted
recommendation.upgrade.available boolean Whether moving to a higher tier is available
recommendation.upgrade.nextTier string or null The recommended next tier, null on the ULTRA tier
tiers array All four tiers for the comparison table
tiers[].tier string Tier name: FREE, PRO, MAX, ULTRA
tiers[].multiplier string or null Tier multiplier: ×1, ×5, ×20, null for FREE
tiers[].feeVibes number Catalog price of the tier in Vibe credits per month. What will actually be debited for a move to this tier comes from the tier change preview
tiers[].current boolean true if the tier is active for the key owner
tiers[].isNext boolean true if the tier matches recommendation.upgrade.nextTier
serverTime string Server time at the moment of the response (ISO 8601)
boostPct integer Temporary limit increase in percent granted by platform support (100 = double limits). 0 when none is active. Every pctUsed share already accounts for it.
boostExpiresAt string|null When the temporary increase ends. null when none is active.
offPeak object Off-peak hours: whether the discount applies now, when the next one starts, and the grid of multipliers by hour of the week. The contents of the block — Off-peak hours in the Cowork/Code subscription
touSavedPct number or null The share of the subscription MONTHLY ALLOWANCE that off-peak hours gave back over the current billing period, in percent, capped at 100. The denominator is the same as for the monthly share in quotaPct — this is NOT a share of usage. Details: Off-peak hours
activation object How this account is activated for work with Bitrix24: the access model of its region, the link to the plan terms, and whether the Marketplace trial can still be offered
activation.model string Access model of the account region: subscription — the Marketplace path applies, trial included; tariff — access comes through a commercial Bitrix24 plan, which is how every international account works
activation.tariffInfoUrl string Page with Bitrix24 plan terms. Present only when model is tariff and the account is not self-hosted
activation.marketTrial.available boolean Whether the trial can be offered right now
activation.marketTrial.unavailableReason string or null Why it should not be offered; null when available: true. Values: trial_already_activated, subscription_active, demo_used, region_not_supported, not_cloud, portal_state, not_supported
activation.marketTrial.status string Log of our own attempts: not_attempted — never tried, activated — the trial was switched on through us, failed — an attempt was made and has not succeeded yet (an activation still in flight reads the same way)
activation.marketTrial.endsAt string or null When the trial ends, as reported by Bitrix24 (ISO 8601)
activation.marketTrial.activatedAt string or null When we switched the trial on (ISO 8601)
relief object Quota relief: when platform support last reset the usage counters and when it last granted a temporary limit increase. Both fields inside the block are always present
relief.resetGrantedAt string or null When the usage counters were reset, to the hour (ISO 8601). null when no reset was granted, and also when the stamp is more than 7 days old
infraState object Whether infrastructure is stopped over an unpaid balance. The block is shared with the self-description, and the strings inside it are machine-readable
infraState.frozen boolean Whether servers, deploys and storage are stopped over an unpaid balance. This call itself is not paid for from the balance, so on a frozen account it goes through and returns true. Until the narrowed refusal reaches your Bitrix24 account, the call answers 402 ACCOUNT_FROZEN for an account in debt, and the stopped state is read from the self-description. What the refusal covers — Error codes
infraState.reason string or null The stop reason code: DEBT, or null when nothing is stopped
infraState.topupUrl string or null The top-up link for your Vibecode account, or null when there is nothing to top up
relief.boostGrantedAt string or null When a temporary limit increase was granted, to the hour (ISO 8601). null when none was granted, when the stamp is more than 7 days old, and when the increase has already ended. An empty value does not mean there is no increase — read boostPct

The offPeak and touSavedPct keys arrive only when off-peak hours are enabled for the account. Until then, they are absent from the response body entirely, and the example below does not show them.

The relief key arrives only when quota relief is enabled for the account. Until the capability is on for your platform, it is absent from the response body entirely, and the example below does not show it. Test for the presence of the key, not for its value: null inside the block carries a meaning of its own.

Response example

JSON
{
  "subscription": {
    "tier": "FREE",
    "state": "ACTIVE",
    "currentPeriodStart": "2026-06-01T00:00:00.000Z",
    "currentPeriodEnd": "2026-07-01T00:00:00.000Z",
    "nextChargeAt": null,
    "cancelAtPeriodEnd": false,
    "pendingTier": null,
    "termMonths": 1,
    "paidThroughAt": "2026-05-15T00:00:00.000Z"
  },
  "windows": {
    "fiveHour": { "pctUsed": 40, "resetAt": "2026-06-09T17:30:00.000Z", "exhausted": false },
    "week":     { "pctUsed": 24, "resetAt": "2026-06-12T09:00:00.000Z", "exhausted": false },
    "month":    { "pctUsed": 20, "resetAt": "2026-07-01T00:00:00.000Z", "exhausted": false }
  },
  "bottleneck": "fiveHour",
  "recommendation": {
    "reason": "none",
    "triggerWindow": null,
    "wait": null,
    "upgrade": { "available": true, "nextTier": "PRO" }
  },
  "tiers": [
    { "tier": "FREE",  "multiplier": null,  "feeVibes": 0,   "current": true,  "isNext": false },
    { "tier": "PRO",   "multiplier": "×1",  "feeVibes": 20,  "current": false, "isNext": true  },
    { "tier": "MAX",   "multiplier": "×5",  "feeVibes": 100, "current": false, "isNext": false },
    { "tier": "ULTRA", "multiplier": "×20", "feeVibes": 200, "current": false, "isNext": false }
  ],
  "serverTime": "2026-06-09T14:05:00.000Z",
  "activation": {
    "model": "tariff",
    "tariffInfoUrl": "https://www.bitrix24.com/prices/",
    "marketTrial": {
      "available": false,
      "unavailableReason": "region_not_supported",
      "status": "not_attempted",
      "endsAt": null,
      "activatedAt": null
    }
  }
}

The example shows an account on the tariff model, which is how every international account works. Where the subscription model applies, activation.model is subscription, the tariffInfoUrl key is absent entirely, and marketTrial.available can be true.

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 the user and portal
429 RATE_LIMITED Polling faster than allowed
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.

Known specifics

A successful response (200) is the state object itself, without a success wrapper. Errors arrive in the envelope { success: false, error: { code, message } }. Determine success by the HTTP status (res.ok).

The window-exhaustion flag is the exhausted field, not pctUsed === 100. The pctUsed value is rounded to an integer: 99.6% shows as 100, though the window is not yet exhausted.

Measure the time until reset from serverTime, not from the device clock — clock drift between the client and the server skews the counter.

Windows reset lazily on read — if there have been no requests since the reset time, the reset is applied when you call the endpoint, so the snapshot is always current.

Poll no more than once every 15–30 seconds. The response is not cached (Cache-Control: no-store). The endpoint has a rate limit shared by the account and the key owner, so every device of one person draws on the same budget; the recommended interval stays well inside it, and above it the endpoint answers 429 RATE_LIMITED.

The activation block may be absent from the response entirely — that is the response from a platform that does not know about it yet. This is a normal state: no block means behave as before. When the block is there, available and unavailableReason inside it are always present, so test for the BLOCK and read the VALUE of the field, never the other way round.

activation.marketTrial.available is a forecast, not a guarantee. false is final: do not show the trial step. true means you may offer it, but it does not promise success — the account state can change between the poll and the click, and the final decision stays with Bitrix24, so keep handling a refusal at activation time.

The list of unavailableReason values may grow. Read an unknown value as "do not offer the trial", with no special branch. The same goes for status.

activation.tariffInfoUrl is a shared terms page, not an address specific to your account. The value is constant and may be absent from the response (a subscription-model region, a self-hosted account) — the screen must survive without the link. Do not persist it and do not parse its host.

An empty relief.boostGrantedAt does not mean there is no limit increase. The stamp is there for a one-off notification and stays visible for 7 days, while an increase is granted for up to 30 days. An increase granted more than seven days ago reports an empty stamp and is still in effect. Only boostPct tells you whether an increase is in effect.

The value of the relief block matches what the subscription summary returns. Store the stamp you have already reported to the user once per application, not separately per endpoint, or the same relief will be shown twice. Notify only when a non-empty value differs from the one you stored: the server deliberately turns a stamp into null (the visibility window closed, the increase expired or was revoked, the seat was handed over), so a plain "it changed — show it" rule would fire a false notice on every such transition.

See also