
## 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

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

### curl — OAuth application

```bash
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 |
| `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, fields analogous to `fiveHour` |
| `windows.month` | object | Monthly window, matches the billing period, fields analogous to `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 | Tier price in Vibe credits per month |
| `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) |

## 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
  },
  "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"
}
```

## 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 |
| 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](/docs/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 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.

**Count the time until reset from `serverTime`, not from the device clock** — a drift between the client and server clocks would skew the counter.

**Windows reset lazily on read** — if there were no requests since the reset, it is applied when the endpoint is accessed, so the snapshot is always current.

**Poll no more than once every 15–30 seconds.** The response is not cached (`Cache-Control: no-store`).

## See also

- [Cowork/Code subscription summary](/docs/cowork/me)
- [Cowork/Code](/docs/cowork)
- [Limits and optimization](/docs/optimization)
- [Errors](/docs/errors)
