JSON на входе и выходе. API-ключ не нужен — сам код активации служит авторизацией. Кликните на эндпоинт чтобы раскрыть.
/api/v1/activate
Запускает асинхронную активацию ChatGPT-ключа (PLUS / GO). Возвращает session-токен для опроса статуса.
Активация ChatGPT — асинхронная: этот вызов резервирует ключ и запускает выдачу, дальше опрашивайте /api/v1/chatgpt/status пока не вернётся completed или failed. Может пройти через review (в основном PLUSC) — продолжайте опрашивать, не перезапуская. Обычно несколько секунд.
| поле | тип | описание |
|---|---|---|
cdk |
string | Код 4×5 символов через дефис, например PLUSX-XXXXX-XXXXX-XXXXX. Регистр не важен. |
type |
"full" | "acc_id" |
Что именно лежит в поле value. |
value |
string | UUID пользователя или JSON session-токен — см. примеры ниже. |
session |
string | Опционально. Передайте существующий session-токен вместо cdk+type+value, чтобы продолжить или перезапустить активацию. |
| тип | значение должно быть | пример |
|---|---|---|
acc_id |
UUID в нижнем регистре, 8-4-4-4-12. | c7dadb6d-47d1-4d31-956d-ed73b29b5051 |
full |
JSON-строка с полем account.id на верхнем уровне (UUID). |
{"account":{"id":"c7dadb6d-..."},"user":{...}} |
Несоответствие (например type=full с голым UUID или type=acc_id с JSON) → 400, без побочных эффектов.
Ключи web-оплаты требуют ПОЛНЫЙ токен. Ключи ChatGPT Plus, оплачиваемые через веб-чекаут (префиксы PLUSC / PLUSW), активируются только с type=full (весь JSON со страницы chatgpt.com/api/auth/session). Отправка type=acc_id (только UUID аккаунта) по такому ключу → отказ, клиент должен прислать полный токен. Ключ не тратится. Остальные ключи ChatGPT Plus принимают type=acc_id как обычно.
POST /api/v1/activate HTTP/1.1
Content-Type: application/json
{
"cdk": "PLUSX-XXXXX-XXXXX-XXXXX",
"type": "acc_id",
"value": "c7dadb6d-47d1-4d31-956d-ed73b29b5051"
}
{
"ok": true,
"status": "processing",
"session": "a1b2c3d4e5f6a7b8c9d0...",
"message": "Активация начата."
}
Когда у вас на руках полный JSON session-токен прямо из iOS-приложения. Сервер сам вытащит account.id — вам не нужно его парсить.
POST /api/v1/activate HTTP/1.1
Content-Type: application/json
{
"cdk": "PLUSX-XXXXX-XXXXX-XXXXX",
"type": "full",
"value": "{\"WARNING_BANNER\":\"!!...\",\"account\":{\"id\":\"c7dadb6d-47d1-4d31-956d-ed73b29b5051\"},\"user\":{...}}"
}
{
"ok": true,
"status": "processing",
"session": "a1b2c3d4e5f6a7b8c9d0...",
"message": "Активация начата."
}
Сохраните session — по нему опрашивается статус и возобновляется активация. Страница статуса в вебе — это /?s=<session>.
| код | сообщение | смысл |
|---|---|---|
| 200 | processing | Ключ зарезервирован, активация запущена. Опрашивайте статус. |
| 200 | completed | Этот ключ уже был активирован ранее — вернётся та же session. |
| 400 | разные | Некорректный запрос: битый JSON, отсутствует поле, неверный формат ключа или несоответствие type/value. |
| 404 | CDK not found | Такого ключа нет в базе. |
| 409 | CDK already used | Ключ уже использован ранее. В сообщении может быть email активировавшего. |
| 409 | CDK is disabled | Ключ отключён администратором. |
| 409 | другой пользователь уже активирует | Этот же ключ сейчас активируется на другой app_user_id. Подождите 2-3 минуты до завершения. |
| 410 | revoked | Продавец пометил эту партию ключей устаревшей (отозвана). В теле: error:"revoked". Ключ не потрачен. Попросите у продавца свежий ключ. |
| 413 | payload too large | Тело запроса больше 128 KB. Подрежьте session-токен. |
| 429 | too many requests, slow down | Rate-limit или авто-бан. Подождите и повторите. |
| 503 | no stock | Временно нет в наличии — сейчас нет свободных ключей/аккаунтов для выдачи. В теле: status:"error", error:"no_stock". Ключ НЕ тратится. Клиенту: повторить через несколько минут. |
curl -X POST https://999cdk.store/api/v1/activate \
-H 'Content-Type: application/json' \
-d '{
"cdk": "PLUSX-XXXXX-XXXXX-XXXXX",
"type": "acc_id",
"value": "c7dadb6d-47d1-4d31-956d-ed73b29b5051"
}'
import requests, time
r = requests.post('https://999cdk.store/api/v1/activate', json={
'cdk': 'PLUSX-XXXXX-XXXXX-XXXXX',
'type': 'acc_id',
'value': 'c7dadb6d-47d1-4d31-956d-ed73b29b5051',
}, timeout=15)
data = r.json()
if not data['ok']:
raise SystemExit(f"start failed: {data.get('message')}")
session = data['session']
print('started', session)
# Poll until terminal status
deadline = time.time() + 300 # 5 min cap
while time.time() < deadline:
time.sleep(2)
s = requests.get(f'https://999cdk.store/api/v1/chatgpt/status?session={session}', timeout=10).json()
if s.get('status') == 'completed':
print('activated, expires:', s.get('expires_date'))
break
if s.get('status') == 'failed':
print('failed:', s.get('error'))
break
# 'review' (встречается на PLUSC): не выходим и НЕ перезапускаем — просто
# продолжаем опрашивать (можно реже), статус сам станет completed/failed.
else:
print('timeout, still processing — keep polling later')
/api/v1/chatgpt/status
Опрашивает статус ChatGPT-активации по session-токену.
| поле | тип | описание |
|---|---|---|
session |
string | Session-токен, который вернул /activate. |
GET /api/v1/chatgpt/status?session=a1b2c3d4e5f6... HTTP/1.1
{
"ok": true,
"status": "processing",
"message": "Активация выполняется…",
"cdk": "PLUSX-XXXXX-XXXXX-XXXXX",
"expires_date": null,
"success_message": null,
"error": null,
"created_at": 1781167306
}
{
"ok": true,
"status": "completed",
"message": "Подписка активирована.",
"cdk": "PLUSX-XXXXX-XXXXX-XXXXX",
"expires_date": "2026-07-11T11:42:01Z",
"success_message": "Спасибо за покупку!",
"error": null,
"created_at": 1781167306
}
{
"ok": true,
"status": "failed",
"message": "Не удалось активировать: страница оплаты недоступна на этом аккаунте ChatGPT либо платёж отклонён. Ключ освобождён (не потрачен), снова доступен. Повторите позже или используйте другой аккаунт.",
"cdk": "PLUSX-XXXXX-XXXXX-XXXXX",
"expires_date": null,
"success_message": null,
"error": "task_failed",
"created_at": 1781167306
}
{
"ok": true,
"status": "review",
"message": "Активация на ручной проверке…",
"cdk": "PLUSC-XXXXX-XXXXX-XXXXX",
"expires_date": null,
"success_message": null,
"error": null,
"created_at": 1781167306
}
| статус | смысл |
|---|---|
processing | Активация идёт — продолжайте опрашивать (каждые 2-3 сек). |
review | Промежуточный статус (может появиться на ключах PLUSC): активация проверяется вручную. Продолжайте опрашивать, но реже — каждые 10–15 сек — и не перезапускайте session; статус сам перейдёт в completed или failed. |
completed | Готово — подписка привязана. Ключ помечен использованным. Заполнены expires_date и опциональное success_message. |
failed | Активация не удалась; причина в error. Ключ освобождён — клиент может перезапустить через POST {session} на /activate. |
failed)
При status=failed поле error содержит короткий машинный код (по нему ветвитесь в интеграции), а message — готовый человеческий текст для показа клиенту. Коды:
| причина | смысл / что делать |
|---|---|
session_expired | Токен ChatGPT-аккаунта истёк к моменту активации. Ключ не потрачен, освобождён. Клиенту: открыть chatgpt.com, выполнить любое действие (обновить сессию), скопировать свежий токен со страницы chatgpt.com/api/auth/session и отправить заново. |
token_bad | Отправленный токен невалиден/неполный (не весь JSON со страницы /api/auth/session). Ключ не потрачен. Клиенту: скопировать ВЕСЬ текст токена заново и отправить ещё раз. |
already_paid | На аккаунте уже есть активный платный тариф ChatGPT. Ключ не потрачен. Аккаунт временно блокируется на ~20 мин (снимается автоматически — когда текущая подписка кончится, клиент сможет активировать). Клиенту: использовать аккаунт без подписки (free) либо дождаться окончания текущего тарифа. |
account_blocked | Оплата на этом аккаунте временно ограничена платёжной системой. Ключ не потрачен. Клиенту: попробовать позже или другой аккаунт. |
task_failed | Не удалось активировать: страница оплаты недоступна на этом аккаунте ChatGPT либо платёж отклонён. Ключ освобождён (не потрачен), снова доступен. Клиенту: повторить позже (отправить session заново) либо использовать другой аккаунт. |
rate_limited | Слишком много попыток оформления на этом аккаунте ChatGPT (лимит OpenAI, 429 too many checkout attempts). Ключ не потрачен. Клиенту: подождать 10–15 мин и повторить (отправить session заново) — лимит спадёт сам. |
review_failed | Активация прошла ручную проверку и была отклонена. Ключ освобождён, снова доступен. Клиенту: повторить либо использовать другой аккаунт. |
payment_failed | Оплата картой не прошла. Ключ не потрачен. Клиенту: повторить — будет другая попытка/карта. |
temporary | Временный сбой инфраструктуры на нашей стороне. Ключ не потрачен. Клиенту: повторить через пару минут. |
activation_failed | Прочий сбой активации. Ключ освобождён (не потрачен). Клиенту: повторить, при повторении — к продавцу. |
Примечание: в интеграции ветвитесь по error (стабильный код) и/или status; клиенту показывайте message как есть. Фейл session_expired/token_bad = проблема токена клиента, не поставщика — ключ остаётся рабочим. review — это промежуточный status (см. выше), а не код error.
404 — неизвестный session (неверный токен или истёк после 15-мин TTL).
curl 'https://999cdk.store/api/v1/chatgpt/status?session=a1b2c3d4e5f6...'
/api/v1/claude/activate
Запускает асинхронную активацию Claude Pro. Возвращает session-токен для опроса статуса.
Активация Claude — асинхронная: этот вызов резервирует ключ и запускает выдачу, дальше опрашивайте /api/v1/claude/status пока не вернётся completed или failed. Обычно 1–2 минуты.
| поле | тип | описание |
|---|---|---|
cdk |
string | Ключ Claude, например CLAUD-XXXXX-XXXXX-XXXXX. Регистр не важен. |
org_id |
string | Organization ID покупателя — UUID со страницы claude.ai → Settings → Account. К этой организации привяжется Claude Pro. |
session |
string | Опционально. Передайте существующий session-токен вместо cdk+org_id, чтобы продолжить или перезапустить активацию. |
POST /api/v1/claude/activate HTTP/1.1
Content-Type: application/json
{
"cdk": "CLAUD-XXXXX-XXXXX-XXXXX",
"org_id": "c7dadb6d-47d1-4d31-956d-ed73b29b5051"
}
{
"ok": true,
"status": "processing",
"session": "a1b2c3d4e5f6a7b8c9d0...",
"message": "Активация начата."
}
Сохраните session — по нему опрашивается статус и возобновляется активация. Страница статуса в вебе — это /claude/?session=<session>.
| код | сообщение | смысл |
|---|---|---|
| 200 | processing | Ключ зарезервирован, активация запущена. Опрашивайте статус. |
| 200 | completed | Этот ключ уже был активирован ранее — вернётся та же session. |
| 400 | разные | Нет cdk/org_id, неверный формат ключа, некорректный Organization ID, либо ключ не для Claude. |
| 403 | CDK is disabled | Ключ отключён администратором. |
| 404 | CDK not found | Такого Claude-ключа нет в базе. |
| 409 | CDK is in use | Ключ сейчас активируется другим запросом. Повторите чуть позже. |
| 410 | revoked | Продавец пометил эту партию ключей устаревшей (отозвана). В теле: error:"revoked". Ключ не потрачен. Попросите у продавца свежий ключ. |
| 429 | too many requests… | Rate-limit или авто-бан. Подождите и повторите. |
| 503 | no stock | Временно нет в наличии — сейчас нет свободных ключей/аккаунтов для выдачи. В теле: status:"error", error:"no_stock". Ключ НЕ тратится. Клиенту: повторить через несколько минут. |
curl -X POST https://999cdk.store/api/v1/claude/activate \
-H 'Content-Type: application/json' \
-d '{
"cdk": "CLAUD-XXXXX-XXXXX-XXXXX",
"org_id": "c7dadb6d-47d1-4d31-956d-ed73b29b5051"
}'
/api/v1/claude/status
Опрашивает статус активации Claude по session-токену.
| поле | тип | описание |
|---|---|---|
session |
string | Session-токен, который вернул /claude/activate. |
GET /api/v1/claude/status?session=a1b2c3d4e5f6... HTTP/1.1
{
"ok": true,
"status": "processing",
"message": "Активация выполняется…",
"cdk": "CLAUD-XXXXX-XXXXX-XXXXX",
"created_at": 1769841121
}
| статус | смысл |
|---|---|
processing | Активация идёт — продолжайте опрашивать (например каждые 3–5 с). |
completed | Готово — Claude Pro привязан к организации покупателя. Ключ помечен использованным. |
failed | Активация не удалась; ключ не был использован. |
404 — неизвестный session (неверный токен или истёк).
/api/v1/claude/reactivate
Повторно привязывает уже купленную подписку к аккаунту Claude — для «зависшего» ключа, который показывает «использован», но Pro так и не появился.
Используйте, только если ключ уже used, но Claude Pro нет. Аккаунт должен быть на бесплатном плане. Мы повторно отправляем сохранённый чек Apple в claude.ai с sessionKey аккаунта. Синхронно — один вызов, без опроса статуса.
| поле | тип | описание |
|---|---|---|
cdk |
string | Тот же ключ Claude, например CLAUD-XXXXX-XXXXX-XXXXX. Должен быть уже used. |
session_key |
string | Куки sessionKey аккаунта с claude.ai (DevTools → Application → Cookies → claude.ai → sessionKey). Начинается с sk-ant-sid. |
POST /api/v1/claude/reactivate HTTP/1.1
Content-Type: application/json
{
"cdk": "CLAUD-XXXXX-XXXXX-XXXXX",
"session_key": "sk-ant-sid02-..."
}
{
"ok": true,
"state": "ok",
"code": null,
"claude_http": 200,
"title": "Готово — Claude принял чек",
"message": "Обновите страницу Claude через 1–2 минуты.",
"kyc_url": "",
"claude_body": "{\"success\":true}"
}
Важно: вызов синхронный — он дожидается реального ответа claude.ai. HTTP 200 означает лишь, что запрос обработан; вердикт лежит в ok и state. Отказ Claude тоже приходит как HTTP 200 с ok:false. Ветвитесь по state/code, а не по HTTP-статусу. Повтор для одного ключа — не чаще раза в 2 минуты.
| поле | тип | описание |
|---|---|---|
ok | bool |
true только когда Claude принял чек (state:"ok"). |
state | string |
ok — готово; err — Claude отказал; warn — до Claude не дошло (транспорт), повторите позже. |
code | string|null |
Машиночитаемая причина. null при успехе. См. таблицу ниже. |
claude_http | int |
Сырой HTTP-статус от claude.ai (0 = ответа не было вовсе). |
title / title_en | string | Короткий заголовок, готов к показу клиенту. |
message / message_en | string | Развёрнутое объяснение с указанием, что клиенту делать дальше. |
kyc_url | string |
Только для must_upgrade: прямая ссылка на верификацию личности (Persona). Пустая строка, если получить не удалось — тогда показывайте просто message. |
claude_body | string | Сырое тело ответа claude.ai, обрезано до 600 символов — для ваших логов. |
| code | state | смысл / что делать клиенту |
|---|---|---|
null | ok | Чек принят. Pro появится в Claude за 1–2 минуты. |
must_upgrade | err |
Claude держит аккаунт за верификацией личности (KYC). Дайте клиенту kyc_url (или отправьте на claude.ai), после проверки — повторить реактивацию тем же ключом, сохранённый чек никуда не делся. |
apple_receipt_cross_account_token_rejected | err | Чек уже привязан к ДРУГОМУ аккаунту Claude. Клиенту нужно войти именно в тот аккаунт, на который активировали ключ. |
account_session_invalid | err |
session_key недействителен или истёк — заново и целиком скопировать cookie с claude.ai. |
apple_receipt_org_not_consumer | err | sessionKey от Team/Workspace. Подписка привязывается только к личному (consumer) аккаунту. |
apple_receipt_verification_failed | err | Обычно на аккаунте уже есть другая подписка (Pro/Max/Team) либо он в бане. Нужен бесплатный план. |
apple_receipt_already_used | err | Чек уже использован. Если Pro всё равно нет — к продавцу. |
org_banned | err | Claude принял чек, но организация аккаунта заблокирована — подписка не встанет. Нужен другой аккаунт. |
transport | warn |
Запрос не дошёл до claude.ai (сеть/мост). Claude ничего не видел, повтор безопасен — но кулдаун на 2 минуты уже взведён, придётся его выждать. claude_http равен 0. |
| любая другая строка | err |
Код ошибки Claude, для которого у нас пока нет расшифровки — покажите message как есть, смотрите claude_body. |
{
"ok": false,
"state": "err",
"code": "must_upgrade",
"claude_http": 400,
"title": "Аккаунту нужна верификация",
"message": "Claude требует пройти верификацию личности (KYC)… Скопируйте ссылку…",
"kyc_url": "https://withpersona.com/verify?inquiry-id=inq_…&session-token=…",
"claude_body": "{\"type\":\"error\",\"error\":{\"message\":\"must_upgrade\"}}"
}
| код | сообщение | смысл |
|---|---|---|
| 200 | ok:true|false | Запрос обработан — вердикт в state/code. Отказ Claude тоже приходит сюда. |
| 400 | разные | Нет cdk/session_key либо session_key неверного формата. |
| 404 | CDK not found | Такого Claude-ключа нет в базе. |
| 409 | непригоден | Ключ ещё не used, либо по нему нет сохранённого чека. |
| 429 | cooldown | Тот же ключ переотправляли менее 2 минут назад. Подождите и повторите. |
Ошибки с HTTP 400/404/409/429 содержат только {ok:false, message} — это наша валидация: до Claude запрос не идёт и кулдаун не взводится. Любой ответ с HTTP 200 означает, что вызов к claude.ai уже ушёл, то есть кулдаун пошёл.
curl -X POST https://999cdk.store/api/v1/claude/reactivate \
-H 'Content-Type: application/json' \
-d '{
"cdk": "CLAUD-XXXXX-XXXXX-XXXXX",
"session_key": "sk-ant-sid02-..."
}'
/api/v1/check
Read-only проверка одного ключа или ссылки активации. Возвращает good / used / invalid.
codes — JSON-массив с одним элементом: либо голый код, либо полная ссылка активации ?cdk=….
POST /api/v1/check HTTP/1.1
Content-Type: application/json
{
"codes": [ "PLUSX-XXXXX-XXXXX-XXXXX" ]
}
{
"ok": true,
"results": [
{ "input": "PLUSX-XXXXX-XXXXX-XXXXX",
"code": "PLUSX-XXXXX-XXXXX-XXXXX",
"status": "good", "reason": null,
"plan": "plus",
"email": null, "used_at": null,
"acc_id": null, "org_id": null }
]
}
| поле | описание |
|---|---|
status |
good — существует, не использован. used — уже активирован. invalid — некорректный формат, не найден или отключён.
|
reason |
Только для invalid: bad_format, not_found или disabled. Иначе null.
|
email |
Для used-кодов, активированных полным токеном — email активировавшего, если был сохранён. Иначе null.
|
used_at |
Для used-кодов — время активации (YYYY-MM-DD HH:MM). Иначе null.
|
plan |
План этого ключа: plus, go или claude.
|
org_id |
Для used-кодов claude — Organization ID покупателя, к которому привязали Claude Pro. Иначе null.
|
acc_id |
Для used-кодов ChatGPT — UUID аккаунта, на который активировали, если выводится. Иначе null.
|
Использованный claude-код возвращает org_id покупателя — удобно посмотреть, кому он активирован.
{
"ok": true,
"results": [
{ "input": "CLAUD-XXXXX-XXXXX-XXXXX",
"code": "CLAUD-XXXXX-XXXXX-XXXXX",
"status": "used", "reason": null,
"plan": "claude",
"email": null, "used_at": "2026-05-30 04:32",
"acc_id": null,
"org_id": "c7dadb6d-47d1-4d31-956d-ed73b29b5051" }
]
}
/api/v1/refund/submit
Ставит в очередь заявку на возврат Apple для использованного CDK с завершённой iOS-покупкой.
Запрос асинхронный. HTTP 201 означает только, что заявка сохранена в очереди обработки, а не то, что Apple уже одобрила возврат. Рассмотрение Apple обычно занимает 24–48 часов. API-ключ не нужен — покупка определяется по CDK.
Принимается только использованный CDK, связанный с завершённой iOS-покупкой. Каждый CDK/покупку можно отправить только один раз. Активные, неверные, отключённые, удалённые, не-iOS ключи и покупки без сохранённого чека возвращают одинаковый публичный ответ 404.
| поле | тип | описание |
|---|---|---|
cdk |
string |
Обязательное поле. Использованный CDK формата XXXXX-XXXXX-XXXXX-XXXXX. Пробелы удаляются, регистр букв не важен.
|
POST /api/v1/refund/submit HTTP/1.1
Content-Type: application/json
Accept: application/json
{
"cdk": "CLAUD-XXXXX-XXXXX-XXXXX"
}
{
"ok": true,
"id": 42,
"status": "queued",
"message": "Заявка принята и поставлена в очередь на обработку."
}
| поле | описание |
|---|---|
id | Внутренний идентификатор заявки. |
status | При принятии всегда queued: обработка ещё не завершена. |
| код | сообщение | смысл |
|---|---|---|
| 201 | status: queued | Заявка сохранена и поставлена в очередь асинхронной обработки. |
| 400 | Некорректный запрос | Тело запроса не является JSON-объектом. |
| 404 | CDK не найден или не был использован | Неверный формат, ключ не найден/не использован/не подходит либо нет завершённого iOS-чека. |
| 409 | Этот CDK уже был отправлен на возврат | Этот CDK или покупка уже были отправлены. Повторять запрос не нужно. |
| 405 | method not allowed | Используйте POST. |
| 413 | Слишком большой запрос | Тело JSON превышает 4096 байт. |
| 500 | Не удалось сохранить заявку | Временная серверная ошибка; повторите позже. |
curl -X POST https://999cdk.store/api/v1/refund/submit \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{"cdk":"CLAUD-XXXXX-XXXXX-XXXXX"}'