Для AI-агентов: markdown этой страницы — /docs-content/cowork/coupon-preview.md индекс документации — /llms.txt

Проверить купон

POST /v1/cowork/coupon/preview

Проверяет купон Cowork/Code и показывает, какой тариф и на какой срок он даёт. Место пользователя только читается, купон остаётся непогашенным.

Пользователь и аккаунт Битрикс24 берутся из привязки ключа, параметров пути нет.

Поля запроса (body)

Поле Тип Обяз. Описание
code string да Купон в виде ПРЕФИКС-ТЕЛО-СУММА, от 1 до 64 символов. Регистр и пробелы приводятся к канонической форме на нашей стороне, дефисы разных начертаний тоже. Других полей тело не принимает: любое лишнее поле, в том числе action, даёт 400 INVALID_CODE

Примеры

Ось авторизации одна: эндпоинт принимает только ключ десктопа Cowork/Code. Личный ключ и ключ приложения получают 403 INSUFFICIENT_SCOPE либо 403 COWORK_DESKTOP_KEY_REQUIRED.

curl — ключ десктопа Cowork/Code

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

JavaScript — ключ десктопа Cowork/Code

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

if (!res.ok) {
  const { error } = await res.json()
  console.error(error.code)
} else {
  const { data } = await res.json()

  if (!data.valid) {
    // Текст для пользователя выбирают по data.reason — таблица «Причины отказа».
    console.log(data.reason)
  } else {
    console.log(`${data.grant.tier} на ${data.grant.termMonths} мес.`)
    console.log('Что можно сделать:', data.decision.kind)
  }
}

Поля ответа

Поле Тип Описание
success boolean true при успешном ответе. Отказ по самому купону тоже приходит с true — он живёт в data.valid
data.valid boolean Купон годен: код живой, кампания открыта
data.reason string или null Причина отказа при valid: false — одно из значений, перечисленных в разделе «Причины отказа». При valid: true приходит null
data.grant object или null Что даёт купон. При valid: false приходит null
data.grant.tier string Тариф подарка: PRO, MAX или ULTRA
data.grant.termMonths number Срок подарка в месяцах
data.grant.campaignName string Название кампании — его показывают пользователю в окне подтверждения
data.decision object Что произойдёт с местом пользователя и что ему можно предложить. Приходит только при valid: true, и там приходит всегда. При valid: false поля в ответе нет вовсе — проверяйте его через data.decision?.kind, а не сравнением с null
data.decision.kind string Форма решения: apply, extend, choose или refuse. У apply и extend это же значение отправляют в поле action при погашении, у choose действие берут из options. Значения choose и refuse в action не отправляют
data.decision.addsMonths number Только у extend: на сколько месяцев продлится действующий тариф
data.decision.reason string Только у refuse: код отказа, по которому берётся текст для пользователя
data.decision.facts object Только у choose: факты о месте, которые окно обязано показать до выбора
data.decision.options array Только у choose: доступные действия. Каждый элемент несёт kind — его и отправляют в поле action при погашении

Формы решения

Поле data.decision описывает, что случится с местом пользователя, если купон погасить сейчас.

kind Когда приходит Что показать
apply Место свободное, бесплатное, закрытое либо его ещё нет Подтверждение подарка и кнопку «Применить»
extend Тариф подарка совпадает с действующим, место куплено не больше чем на месяц, и кампания разрешает продление срока «Тариф продлится ещё на addsMonths мес.» Тариф не меняется, двигается только срок
choose Место занято, но выбор у пользователя есть Сначала факты из facts, ниже кнопки по options
refuse Предложить нечего Отказ. Текст берётся по коду из reason — это коды COUPON_SEAT_* и COUPON_TIER_DOWNGRADE_BLOCKED, их описания в таблице Погасить купон

Состав facts у решения choose:

Поле Тип Описание
facts.currentTier string Действующий тариф места
facts.paidThroughAt string До какой даты оплачено, ISO 8601
facts.isPaid boolean У места есть живое списание
facts.cancellationScheduled boolean На месте назначена отмена
facts.grantTier string Тариф подарка
facts.grantTermMonths number Срок подарка в месяцах

Состав элемента options:

Поле Тип Описание
options[].kind string Действие: force — применить подарок сейчас вместо действующего тарифа, resume-and-apply — снять назначенную отмену и применить
options[].losesDays number Только у force: сколько целых суток оплаченного срока сгорит. Число приходит отсюда и на клиенте не пересчитывается
options[].losesTier string Только у force: тариф, оплаченный срок которого сгорит

Причины отказа

Значения поля data.reason при valid: false.

Причина Что показать пользователю
COUPON_INVALID «Код не подходит». Под этим значением собраны «кода не существует», «код отозван», «код уже погашен», «срок вышел», «кампания закончилась», «личный лимит выбран» и «лимит аккаунта выбран»
COUPON_TOO_MANY_ATTEMPTS «Слишком много попыток. Попробуйте через полчаса»
COUPON_ALREADY_REDEEMED_BY_YOU «Вы уже активировали этот купон». Не показывайте здесь «код не подходит» — подарок пользователю уже выдан
COUPON_NOT_ASSIGNED_TO_YOU «Купон выписан на другой адрес электронной почты. Войдите под ним и повторите». Купон живой, исправить это пользователь может сам
COUPON_PORTAL_ACCESS_GATED «Доступ к Cowork/Code на аккаунте закрыт — обратитесь к администратору»
COUPON_TARIFF_NOT_ELIGIBLE «Купон действует не на этом тарифе Битрикс24». Кампания ограничена списком тарифов, и тариф аккаунта в него не входит. Отказ терминальный: на этом аккаунте купон не начнёт работать сам, повторять вызов незачем
COUPON_TARIFF_UNKNOWN «Тариф аккаунта пока не определён. Попробуйте позже». Кампания ограничена списком тарифов, а тариф аккаунта платформа ещё не прочитала — так бывает на только что подключённом аккаунте. Купон цел, и попытка не засчитывается в лимит. Не показывайте здесь «код не подходит»: верное действие — повторить позже

Пример ответа

Купон годен, место пользователя свободно:

JSON
{
  "success": true,
  "data": {
    "valid": true,
    "reason": null,
    "grant": {
      "tier": "PRO",
      "termMonths": 3,
      "campaignName": "Практикум партнёров, август"
    },
    "decision": { "kind": "apply" }
  }
}

Купон годен, но место занято старшим оплаченным тарифом — пользователь выбирает:

JSON
{
  "success": true,
  "data": {
    "valid": true,
    "reason": null,
    "grant": {
      "tier": "PRO",
      "termMonths": 3,
      "campaignName": "Практикум партнёров, август"
    },
    "decision": {
      "kind": "choose",
      "facts": {
        "currentTier": "MAX",
        "paidThroughAt": "2026-09-14T10:00:00.000Z",
        "isPaid": true,
        "cancellationScheduled": false,
        "grantTier": "PRO",
        "grantTermMonths": 3
      },
      "options": [
        { "kind": "force", "losesDays": 24, "losesTier": "MAX" }
      ]
    }
  }
}

Купон не годен — статус ответа тот же 200, поля decision в теле нет:

JSON
{
  "success": true,
  "data": {
    "valid": false,
    "reason": "COUPON_INVALID",
    "grant": null
  }
}

Пример ответа при ошибке

400 — тело без поля code:

JSON
{
  "success": false,
  "error": {
    "code": "INVALID_CODE",
    "message": "Field `code` is required"
  }
}

Ошибки

HTTP Код Описание
400 INVALID_CODE В теле нет строкового поля code, оно пустое, длиннее 64 символов либо рядом лежит лишнее поле. Поле action принимает только погашение, здесь оно даёт этот же код ошибки
400 INVALID_JSON_BODY Тело передано, но не читается как JSON
401 MISSING_API_KEY Не передан заголовок X-Api-Key
401 INVALID_API_KEY Ключ не опознан: такой строки на платформе нет
403 INSUFFICIENT_SCOPE У ключа нет скоупа vibe:cowork
403 COWORK_DESKTOP_KEY_REQUIRED Ключ другого класса: личный ключ, ключ приложения, ключ места агента или проектный ключ деплоя
403 COUPON_FEATURE_DISABLED Купоны на аккаунте не включены. Поле ввода стоит скрыть целиком
403 KEY_NOT_BOUND_TO_USER Ключ не привязан к пользователю аккаунта
403 WRITE_BLOCKED_READONLY_KEY Ключ выпущен только для чтения
429 RATE_LIMITED Суммарный лимит платформы — 20 запросов в минуту. Действующее для вашего ключа значение приходит в заголовке x-ratelimit-limit — оно ниже суммарного, поскольку лимит делится между репликами

Полный список общих ошибок API — Ошибки.

Отказ по самому купону ошибкой не считается и приходит статусом 200 с data.valid: false.

Известные особенности

Проверка не отвечает на вопрос «погашение пройдёт». Поле data.valid смотрит только на купон и кампанию: код живой, срок не вышел, кампания открыта, личный лимит и лимит аккаунта не выбраны. Условия самого места отвечают отдельным полем data.decision, но и оно не гарантия: штучный потолок кампании проверяется только при погашении, а место могло измениться между двумя вызовами. Готовьте экран отказа на шаге погашения даже после зелёной проверки.

Место читается, но не создаётся. Погашение перед проверкой правил заводит пользователю бесплатное место, проверка — нет: иначе просмотр купона менял бы состояние. Поэтому у пользователя без места решение вырождается в apply, а не в пустое значение.

Неудачные попытки жгут тот же счётчик, что и погашение. Счёт ведётся по паре «аккаунт плюс пользователь», коллеги по аккаунту чужими попытками не страдают. Десять неудач за час закрывают попытки на тридцать минут. Успешное погашение счётчик обнуляет.

Опечатка счётчик не тратит. Хвост купона — контрольная сумма над префиксом и телом. Ввод, который её не прошёл, до базы не доходит, поэтому пользователь, переписывающий код с раздатки или диктующий его по телефону, ничего не теряет.

Не вызывайте проверку на каждое нажатие клавиши. Она дешевле погашения и повторяема, поэтому для подбора опаснее него. Вешайте вызов на кнопку либо на задержку от 500 мс после последнего нажатия.

Семь разных отказов схлопнуты в COUPON_INVALID намеренно, и различать их не понадобится. Отдельная причина на каждый случай подсказывала бы подбирающему, какие префиксы живые: чтобы увидеть ответ «лимит выбран», надо предъявить настоящий код этой кампании. Ветки под «код отозван» и «лимит выбран» заводить не нужно — таких значений в ответе не появится.

Отказ занимает не меньше четырёхсот миллисекунд. Время ответа выровнено по нижней границе, чтобы секундомер не отличал «такого кода нет» от «код есть, но отозван». Ответ, который сам по себе занял дольше, не укорачивается.

Отложенного применения не существует. Действия «применить подарок в конце оплаченного срока» в options не бывает. Появление такого варианта означает ошибку на нашей стороне, а не сигнал рисовать под него кнопку.

Смотрите также