999cdk

Публичный API v1

JSON на входе и выходе. API-ключ не нужен — сам код активации служит авторизацией. Кликните на эндпоинт чтобы раскрыть.

POST /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, чтобы продолжить или перезапустить активацию.

Соответствие 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 как обычно.

Пример — 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"
}

Ответ — 200 OK

{
  "ok": true,
  "status": "processing",
  "session": "a1b2c3d4e5f6a7b8c9d0...",
  "message": "Активация начата."
}

Пример — type: "full"

Когда у вас на руках полный 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\":{...}}"
}

Ответ — 200 OK

{
  "ok": true,
  "status": "processing",
  "session": "a1b2c3d4e5f6a7b8c9d0...",
  "message": "Активация начата."
}

Сохраните session — по нему опрашивается статус и возобновляется активация. Страница статуса в вебе — это /?s=<session>.

Коды ответа HTTP

кодсообщениесмысл
200processingКлюч зарезервирован, активация запущена. Опрашивайте статус.
200completedЭтот ключ уже был активирован ранее — вернётся та же session.
400разныеНекорректный запрос: битый JSON, отсутствует поле, неверный формат ключа или несоответствие type/value.
404CDK not foundТакого ключа нет в базе.
409CDK already usedКлюч уже использован ранее. В сообщении может быть email активировавшего.
409CDK is disabledКлюч отключён администратором.
409другой пользователь уже активируетЭтот же ключ сейчас активируется на другой app_user_id. Подождите 2-3 минуты до завершения.
410revokedПродавец пометил эту партию ключей устаревшей (отозвана). В теле: error:"revoked". Ключ не потрачен. Попросите у продавца свежий ключ.
413payload too largeТело запроса больше 128 KB. Подрежьте session-токен.
429too many requests, slow downRate-limit или авто-бан. Подождите и повторите.
503no stockВременно нет в наличии — сейчас нет свободных ключей/аккаунтов для выдачи. В теле: status:"error", error:"no_stock". Ключ НЕ тратится. Клиенту: повторить через несколько минут.

cURL

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"
      }'

Python

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')
GET /api/v1/chatgpt/status

Опрашивает статус ChatGPT-активации по session-токену.

Параметры запроса

полетипописание
session string Session-токен, который вернул /activate.

Запрос

GET /api/v1/chatgpt/status?session=a1b2c3d4e5f6... HTTP/1.1

Ответ — 200 OK (processing)

{
  "ok": true,
  "status": "processing",
  "message": "Активация выполняется…",
  "cdk": "PLUSX-XXXXX-XXXXX-XXXXX",
  "expires_date": null,
  "success_message": null,
  "error": null,
  "created_at": 1781167306
}

Ответ — 200 OK (completed)

{
  "ok": true,
  "status": "completed",
  "message": "Подписка активирована.",
  "cdk": "PLUSX-XXXXX-XXXXX-XXXXX",
  "expires_date": "2026-07-11T11:42:01Z",
  "success_message": "Спасибо за покупку!",
  "error": null,
  "created_at": 1781167306
}

Ответ — 200 OK (failed)

{
  "ok": true,
  "status": "failed",
  "message": "Не удалось активировать: страница оплаты недоступна на этом аккаунте ChatGPT либо платёж отклонён. Ключ освобождён (не потрачен), снова доступен. Повторите позже или используйте другой аккаунт.",
  "cdk": "PLUSX-XXXXX-XXXXX-XXXXX",
  "expires_date": null,
  "success_message": null,
  "error": "task_failed",
  "created_at": 1781167306
}

Ответ — 200 OK (review)

{
  "ok": true,
  "status": "review",
  "message": "Активация на ручной проверке…",
  "cdk": "PLUSC-XXXXX-XXXXX-XXXXX",
  "expires_date": null,
  "success_message": null,
  "error": null,
  "created_at": 1781167306
}

Значения status

статуссмысл
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

curl 'https://999cdk.store/api/v1/chatgpt/status?session=a1b2c3d4e5f6...'
POST /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"
}

Ответ — 200 OK

{
  "ok": true,
  "status": "processing",
  "session": "a1b2c3d4e5f6a7b8c9d0...",
  "message": "Активация начата."
}

Сохраните session — по нему опрашивается статус и возобновляется активация. Страница статуса в вебе — это /claude/?session=<session>.

Коды ответа HTTP

кодсообщениесмысл
200processingКлюч зарезервирован, активация запущена. Опрашивайте статус.
200completedЭтот ключ уже был активирован ранее — вернётся та же session.
400разныеНет cdk/org_id, неверный формат ключа, некорректный Organization ID, либо ключ не для Claude.
403CDK is disabledКлюч отключён администратором.
404CDK not foundТакого Claude-ключа нет в базе.
409CDK is in useКлюч сейчас активируется другим запросом. Повторите чуть позже.
410revokedПродавец пометил эту партию ключей устаревшей (отозвана). В теле: error:"revoked". Ключ не потрачен. Попросите у продавца свежий ключ.
429too many requests…Rate-limit или авто-бан. Подождите и повторите.
503no stockВременно нет в наличии — сейчас нет свободных ключей/аккаунтов для выдачи. В теле: status:"error", error:"no_stock". Ключ НЕ тратится. Клиенту: повторить через несколько минут.

cURL

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"
      }'
GET /api/v1/claude/status

Опрашивает статус активации Claude по session-токену.

Параметры запроса

полетипописание
session string Session-токен, который вернул /claude/activate.

Запрос

GET /api/v1/claude/status?session=a1b2c3d4e5f6... HTTP/1.1

Ответ — 200 OK

{
  "ok": true,
  "status": "processing",
  "message": "Активация выполняется…",
  "cdk": "CLAUD-XXXXX-XXXXX-XXXXX",
  "created_at": 1769841121
}

Значения status

статуссмысл
processingАктивация идёт — продолжайте опрашивать (например каждые 3–5 с).
completedГотово — Claude Pro привязан к организации покупателя. Ключ помечен использованным.
failedАктивация не удалась; ключ не был использован.

404 — неизвестный session (неверный токен или истёк).

POST /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 минуты.

Поля ответа

полетипописание
okbool true только когда Claude принял чек (state:"ok").
statestring ok — готово; err — Claude отказал; warn — до Claude не дошло (транспорт), повторите позже.
codestring|null Машиночитаемая причина. null при успехе. См. таблицу ниже.
claude_httpint Сырой HTTP-статус от claude.ai (0 = ответа не было вовсе).
title / title_enstringКороткий заголовок, готов к показу клиенту.
message / message_enstringРазвёрнутое объяснение с указанием, что клиенту делать дальше.
kyc_urlstring Только для must_upgrade: прямая ссылка на верификацию личности (Persona). Пустая строка, если получить не удалось — тогда показывайте просто message.
claude_bodystringСырое тело ответа claude.ai, обрезано до 600 символов — для ваших логов.

Значения code

codestateсмысл / что делать клиенту
nullokЧек принят. Pro появится в Claude за 1–2 минуты.
must_upgradeerr Claude держит аккаунт за верификацией личности (KYC). Дайте клиенту kyc_url (или отправьте на claude.ai), после проверки — повторить реактивацию тем же ключом, сохранённый чек никуда не делся.
apple_receipt_cross_account_token_rejectederr Чек уже привязан к ДРУГОМУ аккаунту Claude. Клиенту нужно войти именно в тот аккаунт, на который активировали ключ.
account_session_invaliderr session_key недействителен или истёк — заново и целиком скопировать cookie с claude.ai.
apple_receipt_org_not_consumererr sessionKey от Team/Workspace. Подписка привязывается только к личному (consumer) аккаунту.
apple_receipt_verification_failederr Обычно на аккаунте уже есть другая подписка (Pro/Max/Team) либо он в бане. Нужен бесплатный план.
apple_receipt_already_usederr Чек уже использован. Если Pro всё равно нет — к продавцу.
org_bannederr Claude принял чек, но организация аккаунта заблокирована — подписка не встанет. Нужен другой аккаунт.
transportwarn Запрос не дошёл до claude.ai (сеть/мост). Claude ничего не видел, повтор безопасен — но кулдаун на 2 минуты уже взведён, придётся его выждать. claude_http равен 0.
любая другая строкаerr Код ошибки Claude, для которого у нас пока нет расшифровки — покажите message как есть, смотрите claude_body.

Ответ — must_upgrade (KYC)

{
  "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\"}}"
}

Коды ответа HTTP

кодсообщениесмысл
200ok:true|falseЗапрос обработан — вердикт в state/code. Отказ Claude тоже приходит сюда.
400разныеНет cdk/session_key либо session_key неверного формата.
404CDK not foundТакого Claude-ключа нет в базе.
409непригоденКлюч ещё не used, либо по нему нет сохранённого чека.
429cooldownТот же ключ переотправляли менее 2 минут назад. Подождите и повторите.

Ошибки с HTTP 400/404/409/429 содержат только {ok:false, message} — это наша валидация: до Claude запрос не идёт и кулдаун не взводится. Любой ответ с HTTP 200 означает, что вызов к claude.ai уже ушёл, то есть кулдаун пошёл.

cURL

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-..."
      }'
POST /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" ]
}

Ответ — 200 OK

{
  "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-код

Использованный 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" }
  ]
}
POST /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"
}

Ответ — 201 Created

{
  "ok": true,
  "id": 42,
  "status": "queued",
  "message": "Заявка принята и поставлена в очередь на обработку."
}
полеописание
idВнутренний идентификатор заявки.
statusПри принятии всегда queued: обработка ещё не завершена.

Коды ответа HTTP

кодсообщениесмысл
201status: queuedЗаявка сохранена и поставлена в очередь асинхронной обработки.
400Некорректный запросТело запроса не является JSON-объектом.
404CDK не найден или не был использованНеверный формат, ключ не найден/не использован/не подходит либо нет завершённого iOS-чека.
409Этот CDK уже был отправлен на возвратЭтот CDK или покупка уже были отправлены. Повторять запрос не нужно.
405method not allowedИспользуйте POST.
413Слишком большой запросТело JSON превышает 4096 байт.
500Не удалось сохранить заявкуВременная серверная ошибка; повторите позже.

cURL

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"}'