Для 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
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
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 |
«Тариф аккаунта пока не определён. Попробуйте позже». Кампания ограничена списком тарифов, а тариф аккаунта платформа ещё не прочитала — так бывает на только что подключённом аккаунте. Купон цел, и попытка не засчитывается в лимит. Не показывайте здесь «код не подходит»: верное действие — повторить позже |
Пример ответа
Купон годен, место пользователя свободно:
{
"success": true,
"data": {
"valid": true,
"reason": null,
"grant": {
"tier": "PRO",
"termMonths": 3,
"campaignName": "Практикум партнёров, август"
},
"decision": { "kind": "apply" }
}
}
Купон годен, но место занято старшим оплаченным тарифом — пользователь выбирает:
{
"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 в теле нет:
{
"success": true,
"data": {
"valid": false,
"reason": "COUPON_INVALID",
"grant": null
}
}
Пример ответа при ошибке
400 — тело без поля code:
{
"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 не бывает. Появление такого варианта означает ошибку на нашей стороне, а не сигнал рисовать под него кнопку.