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

Redeem a coupon

POST /v1/cowork/coupon/redeem

Redeems a Cowork/Code coupon and grants the user a complimentary tier for the term set by the coupon at the platform's expense, with no charge to the Bitrix24 account.

The user and the account come from the key binding; there are no path parameters. The operation is irreversible: a redemption cannot be undone.

Request fields (body)

Field Type Required Description
code string yes Coupon in the form PREFIX-BODY-CHECKSUM, from 1 to 64 characters. Case, whitespace and dash variants are normalized to a canonical form on the server
action string no Action selected by the user: apply, extend, force or resume-and-apply. Take the value from decision returned by the coupon check: for kind apply or extend, use that value; for kind: choose, use the kind of the selected item in options. If the field is omitted, it defaults to apply, preserving the behavior from before the choice was introduced

Examples

This endpoint supports only one authorization method: a Cowork/Code desktop key. Personal keys and application keys receive 403 INSUFFICIENT_SCOPE or 403 COWORK_DESKTOP_KEY_REQUIRED.

curl — Cowork/Code desktop key

Terminal
curl -X POST https://vibecode.bitrix24.com/v1/cowork/coupon/redeem \
  -H "X-Api-Key: YOUR_COWORK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"code": "PRAKTIKUM-7HKM4TQZ2RVW-45C80C", "action": "force"}'

JavaScript — Cowork/Code desktop key

javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/cowork/coupon/redeem', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_COWORK_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    code: 'PRAKTIKUM-7HKM4TQZ2RVW-45C80C',
    action: 'force',
  }),
})

if (!res.ok) {
  const { error } = await res.json()
  if (error.code === 'CONCURRENT_REDEMPTION') {
    // The only error code to retry automatically.
  }
  console.error(error.code)
} else {
  const { data } = await res.json()
  if (data.accessGranted) {
    const months = data.grantedTermMonths
    console.log(`${data.grantedTier} for ${months} ${months === 1 ? 'month' : 'months'}`)
  } else {
    // The tier is granted, but there is no access yet — an access request has been sent to the account administrator.
    console.log(`${data.grantedTier} activated, waiting for the administrator's decision`)
  }
}

Response fields

Field Type Description
success boolean true on a successful redemption
data.grantedTier string The granted tier: PRO, MAX or ULTRA
data.grantedTermMonths number Number of months for which the tier is granted
data.accessGranted boolean false means the tier is granted but the Bitrix24 account administrator has not yet granted access to Cowork/Code

Response example

JSON
{
  "success": true,
  "data": {
    "grantedTier": "PRO",
    "grantedTermMonths": 3,
    "accessGranted": true
  }
}

The tier is granted, but Cowork/Code access has not yet been enabled for the Bitrix24 account:

JSON
{
  "success": true,
  "data": {
    "grantedTier": "PRO",
    "grantedTermMonths": 3,
    "accessGranted": false
  }
}

Error response example

409 — this tier is already assigned to the user's seat:

JSON
{
  "success": false,
  "error": {
    "code": "COUPON_SEAT_ALREADY_ON_TIER",
    "message": "The coupon cannot be redeemed"
  }
}

Errors

Coupon-specific refusals return status 409, with error.code identifying the reason. The message text is the same for all such refusals; do not parse it — branch on the code.

HTTP Code Description
400 INVALID_CODE The body has no string field code; the field is empty or longer than 64 characters; or the body contains a field the endpoint does not accept
400 INVALID_ACTION The action field is present, but its value is not one of apply, extend, force, resume-and-apply
400 INVALID_JSON_BODY A body was sent, but it does not parse as JSON
401 MISSING_API_KEY The X-Api-Key header is missing
401 INVALID_API_KEY The authorization key is invalid
403 INSUFFICIENT_SCOPE The key lacks the vibe:cowork scope
403 COWORK_DESKTOP_KEY_REQUIRED The key belongs to another class: personal key, application key, agent seat key or project deploy key
403 COUPON_FEATURE_DISABLED Coupons are not enabled on the account
403 KEY_NOT_BOUND_TO_USER The key is not bound to an account user
403 WRITE_BLOCKED_READONLY_KEY The key was issued as read-only
409 COUPON_INVALID The coupon does not exist, is revoked, already redeemed or expired, the campaign has ended, its overall redemption cap is exhausted, or the per-user or per-account limit is exhausted. Show a single message — "This code cannot be used"
409 COUPON_TOO_MANY_ATTEMPTS Ten failures within an hour. Further attempts are blocked for thirty minutes
409 COUPON_ALREADY_REDEEMED_BY_YOU This user has already activated this coupon
409 COUPON_NOT_ASSIGNED_TO_YOU The coupon was issued to a different email address
409 COUPON_PORTAL_ACCESS_GATED Cowork/Code access is disabled for the account
409 COUPON_TARIFF_NOT_ELIGIBLE The campaign is limited to a list of Bitrix24 plans, and the account plan is not on it. The refusal is terminal: a repeated call returns the same result
409 COUPON_TARIFF_UNKNOWN The campaign is limited to a list of Bitrix24 plans, and the platform has not read the account plan yet. The coupon is intact and the attempt is not counted against the limit — repeat the call later
409 COUPON_SEAT_PAUSED The seat is paused because of insufficient funds. The gift does not resolve the cause of the pause — top up the balance first
409 COUPON_SEAT_CANCELLATION_SCHEDULED The seat is scheduled for cancellation, and the redemption request omitted action: resume-and-apply. When the gifted tier is not below the current tier, the check includes this action in options — send it to resume the seat and apply the gift. When the gifted tier is lower, the action is unavailable and the refusal is terminal
409 COUPON_TIER_DOWNGRADE_BLOCKED The coupon grants a tier below the current tier, and the user did not select force
409 COUPON_SEAT_ALREADY_ON_TIER This tier is already assigned to the seat, and the redemption request omitted action: extend. If the campaign does not allow extending the term or the purchased term is longer than one month, the check returns the decision refuse rather than extend
409 COUPON_SEAT_PAID_TERM_ACTIVE The seat has an active paid term with no recurring charge. The gift would overwrite that term, so the refusal is terminal — direct the user to support
409 COUPON_SEAT_ALREADY_PAID_LONGER The seat's paid term extends beyond the term offered by the coupon, and the user did not select force
409 COUPON_SELF_PORTAL The portal is backed by a platform employee holding the coupon-issuing right — redemption on such a portal is closed
409 COUPON_SEAT_IS_PAID The seat has an active recurring charge, and the user did not select force
409 COUPON_ACTION_NOT_AVAILABLE The submitted action is stale: the seat state changed between the check and redemption. The coupon remains valid and unspent
409 CONCURRENT_REDEMPTION Another operation is competing for the same seat
429 RATE_LIMITED The platform-wide limit is 10 requests per minute. The effective value for your key is returned in the x-ratelimit-limit header. It is lower than the platform-wide limit because that limit is divided across replicas

See Errors for the full list of common API errors.

Known specifics

Using a generic success screen for accessGranted: false creates the costliest category of support requests. The operation looks successful to the user, yet they cannot launch Cowork/Code, which looks like a malfunction. This state needs a dedicated screen — "The tier is activated, and an access request has been sent to the administrator" — rather than a generic "Done".

The submitted action expresses intent, not permission. The seat may have changed between the check and redemption: the user may have paid for a tier in another tab, the administrator may have downgraded the seat, or a scheduled cancellation may have taken effect. The decision is recomputed from the current state. If the selected action is no longer available, the endpoint returns COUPON_ACTION_NOT_AVAILABLE — repeat the check and show the actions it currently returns.

Retry CONCURRENT_REDEMPTION; never retry any other coupon refusal. The coupon remains valid, the rules were satisfied, and the gift was not consumed: by the time the response arrives, the coupon is available again. Retry up to three times with a 300–800 ms delay and random jitter — without jitter, dozens of clients retry in lockstep and reproduce the same contention. Show the user a waiting state during that time, not an error. Automatically retrying any other coupon refusal exhausts the failure counter in seconds. 429 RATE_LIMITED is separate: it is not a coupon refusal, does not affect the failure counter, and may be retried after a delay.

The force action forfeits the remainder of the paid term with no refund. The coupon check returns the exact number of forfeited days in losesDays; do not recompute it on the client. The button label must include that number before the user clicks it — after redemption, the term cannot be restored.

A coupon is single-use per user, but not necessarily overall. Some coupons allow several activations by different people. The number of users who share a coupon is not disclosed and must not be mentioned in user-facing text.

The failed-attempt counter is shared with the coupon check. The coupon check describes the pair used as the counter key and its reset condition.

The platform-wide limit is 10 requests per minute for redemption and 20 requests per minute for the check. The effective values for your key are returned in the x-ratelimit-limit response header. They are lower than the platform-wide limits because those limits are divided across replicas.

Coupon issuance is intentionally unavailable to this key. The desktop key is stored on the user's machine, so granting it issuance rights would make the campaign cap ineffective. Programmatic issuance requires a different key class and is performed by the partner's server, not the application.

See also