# Magic Subs API v1

Документация для интеграции активации CDK-ключей в сайт, бот или CRM.

**Версия:** `v1`  
**Обновлено:** `2026-05-23`  
**Онлайн-документация:** `https://cdk.sale/api`  
**Base URL:** `https://cdk.sale/api/v1`

---

## 1. Что умеет API

Magic Subs API позволяет:

- проверить доступность одного CDK-ключа;
- проверить до 100 ключей за один запрос;
- создать активацию подписки для выбранного аккаунта;
- получить `task_id` и асинхронно отслеживать итоговый статус;
- запускать несколько разных задач параллельно.

Поддерживаемые продукты:

| `product_type` | Продукт | Значение `account_id` |
| --- | --- | --- |
| `claude_pro` | Claude Pro | Claude ID / organization ID |
| `grok_pro` | SuperGrok | Grok User ID |
| `x_premium` | X Premium | X Account ID / obfuscated ID |

---

## 2. Авторизация

Защищенные методы требуют персональный API-ключ.

Рекомендуемый заголовок:

```http
Authorization: Bearer cdk_live_your_api_key
```

Также поддерживается:

```http
X-API-Key: cdk_live_your_api_key
```

### Важно для безопасности

Интегрируйте API со своего backend, сервера или бота. Не помещайте рабочий API-ключ в frontend JavaScript, публичный репозиторий или код, который видит конечный пользователь.

---

## 3. Формат запросов и ответов

Для `POST`-методов передавайте JSON:

```http
Content-Type: application/json
```

Язык сообщений можно выбрать заголовком:

```http
Accept-Language: ru
Accept-Language: en
```

Успешный ответ:

```json
{
  "success": true,
  "data": {
    "...": "..."
  }
}
```

Ответ с ошибкой:

```json
{
  "success": false,
  "error": {
    "code": "card_used",
    "message": "Этот ключ уже был использован."
  }
}
```

Для бизнес-логики используйте `error.code`, а для отображения клиенту - `error.message`.

---

## 4. Быстрый старт

Базовый сценарий интеграции:

```text
1. POST /cards/check
   Проверить, что CDK доступен и подходит выбранному продукту.

2. POST /activations
   Передать CDK, account_id и product_type.
   Получить task_id.

3. GET /activations/{task_id}?product_type=...
   Проверять статус задачи до completed или failed.
```

Для одной активации достаточно одного запроса `POST /activations`: этот метод самостоятельно выполняет проверку ключа, закрепление ID и запуск пополнения. Предварительный вызов `/cards/check` полезен, если вам нужно показать пользователю доступность ключа до финального подтверждения.

---

## 5. Асинхронная обработка и polling

Активация выполняется асинхронно. После создания задачи API возвращает `task_id`.

```json
{
  "success": true,
  "data": {
    "task_id": "task_xxxxx",
    "status": "processing",
    "product_type": "claude_pro"
  }
}
```

После этого сохраняйте `task_id` в своем заказе и запрашивайте статус задачи.

Рекомендуемый polling:

- интервал: один запрос каждые `5` секунд;
- остановка polling: при статусе `completed`, `failed` или `unknown`;
- не создавайте повторную активацию для того же заказа, пока текущая задача обрабатывается.

### Webhook

Webhook/callback-уведомления в текущей версии не поддерживаются. Для получения результата используйте polling по `task_id`.

### Параллельные задачи

Можно запускать несколько отдельных активаций параллельно. Каждый запрос `POST /activations` возвращает собственный `task_id`.

Пример:

```text
POST /activations -> task_id_1
POST /activations -> task_id_2
POST /activations -> task_id_3

GET /activations/task_id_1?product_type=claude_pro
GET /activations/task_id_2?product_type=grok_pro
GET /activations/task_id_3?product_type=x_premium
```

Не используйте один CDK-ключ для нескольких одновременных активаций.

---

## 6. Лимиты

Стандартный лимит персонального API-ключа:

```text
120 запросов в минуту
```

В лимит входят проверки CDK, создание активаций и polling статусов.

Пример расчета: если одновременно обрабатываются `10` задач и каждая проверяется раз в `5` секунд, это примерно `120` запросов статуса в минуту.

При превышении лимита API вернет:

```json
{
  "success": false,
  "error": {
    "code": "rate_limited",
    "message": "Слишком много запросов. Попробуйте позже."
  }
}
```

---

## 7. Методы API

### 7.1 Проверка доступности API

```http
GET /health
```

Авторизация не требуется.

```bash
curl https://cdk.sale/api/v1/health
```

Ответ:

```json
{
  "success": true,
  "data": {
    "service": "Magic Subs API",
    "version": "1.0.0",
    "status": "ok"
  }
}
```

---

### 7.2 Список продуктов

```http
GET /products
```

Авторизация не требуется.

```bash
curl https://cdk.sale/api/v1/products
```

Ответ:

```json
{
  "success": true,
  "data": [
    { "product_type": "claude_pro", "name": "Claude Pro" },
    { "product_type": "grok_pro", "name": "SuperGrok" },
    { "product_type": "x_premium", "name": "X Premium" }
  ]
}
```

---

### 7.3 Проверить один CDK-ключ

```http
POST /cards/check
```

Проверяет доступность ключа. Метод не запускает активацию и не списывает ключ.

Параметры JSON:

| Поле | Обязательное | Описание |
| --- | --- | --- |
| `code` | Да | CDK-ключ. Также принимается алиас `card_key`. |
| `product_type` | Нет | Рекомендуется передавать для проверки соответствия продукта. |

Пример:

```bash
curl -X POST https://cdk.sale/api/v1/cards/check \
  -H "Authorization: Bearer cdk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -H "Accept-Language: ru" \
  -d '{"code":"EEE-XXXXXXXXXXXXXX","product_type":"claude_pro"}'
```

Успешный ответ:

```json
{
  "success": true,
  "data": {
    "available": true,
    "status": "unused",
    "product_type": "claude_pro",
    "message": "Ключ доступен."
  }
}
```

Если ключ уже использован:

```json
{
  "success": false,
  "error": {
    "code": "card_used",
    "message": "Этот ключ уже был использован."
  }
}
```

---

### 7.4 Массовая проверка CDK-ключей

```http
POST /cards/batch-check
```

Проверяет до `100` ключей за один запрос.

Параметры JSON:

| Поле | Обязательное | Описание |
| --- | --- | --- |
| `codes` | Да | Массив CDK-ключей. Также принимается алиас `card_keys`. |
| `product_type` | Нет | Необязательная проверка соответствия продукта. |

Пример:

```bash
curl -X POST https://cdk.sale/api/v1/cards/batch-check \
  -H "Authorization: Bearer cdk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"codes":["EEE-XXXXXXXXXXXXXX","GGG-XXXXXXXXXXXXXX"],"product_type":"claude_pro"}'
```

Ответ:

```json
{
  "success": true,
  "data": [
    {
      "code": "EEE-XXXXXXXXXXXXXX",
      "available": true,
      "status": "unused",
      "product_type": "claude_pro"
    },
    {
      "code": "GGG-XXXXXXXXXXXXXX",
      "available": false,
      "status": "product_mismatch",
      "product_type": "grok_pro",
      "error": {
        "code": "product_mismatch",
        "message": "Ключ не подходит для выбранного продукта."
      }
    }
  ]
}
```

---

### 7.5 Создать активацию

```http
POST /activations
```

Метод:

1. проверяет доступность CDK;
2. проверяет соответствие ключа выбранному продукту;
3. закрепляет `account_id` за CDK;
4. запускает пополнение;
5. возвращает `task_id`.

Параметры JSON:

| Поле | Обязательное | Описание |
| --- | --- | --- |
| `code` | Да | CDK-ключ. Также принимается `card_key`. |
| `account_id` | Да | ID аккаунта для пополнения. Также принимаются `user_id` и `claude_user_id`. |
| `product_type` | Да | `claude_pro`, `grok_pro` или `x_premium`. |

Пример для Claude Pro:

```bash
curl -X POST https://cdk.sale/api/v1/activations \
  -H "Authorization: Bearer cdk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -H "Accept-Language: ru" \
  -d '{"code":"EEE-XXXXXXXXXXXXXX","account_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","product_type":"claude_pro"}'
```

Ответ:

```json
{
  "success": true,
  "data": {
    "task_id": "task_xxxxx",
    "status": "processing",
    "product_type": "claude_pro"
  }
}
```

#### Важно для Claude Pro

Перед созданием активации убедитесь, что:

- аккаунт находится на `Free`-плане;
- на аккаунте нет активной платной подписки;
- на аккаунте нет просроченных счетов или неоплаченных инвойсов;
- передан корректный Claude ID / organization ID.

После запуска активации неверно переданный `account_id` нельзя исправить повторным запросом.

---

### 7.6 Получить статус активации

Основной вариант:

```http
GET /activations/{task_id}?product_type=claude_pro
```

Альтернативный вариант:

```http
GET /activations/status?task_id=task_xxxxx&product_type=claude_pro
```

Пример:

```bash
curl "https://cdk.sale/api/v1/activations/task_xxxxx?product_type=claude_pro" \
  -H "Authorization: Bearer cdk_live_your_api_key" \
  -H "Accept-Language: ru"
```

Статусы:

| Статус | Описание | Действие |
| --- | --- | --- |
| `processing` | Активация выполняется. | Продолжить polling. |
| `completed` | Активация успешно завершена. | Остановить polling, показать успех. |
| `failed` | Активация завершилась ошибкой. | Остановить polling, обработать `error`. |
| `unknown` | Задача не найдена или статус недоступен. | Проверить `task_id` и продукт. |

Успешное завершение:

```json
{
  "success": true,
  "data": {
    "task_id": "task_xxxxx",
    "status": "completed",
    "product_type": "claude_pro",
    "message": "Активация успешно завершена."
  }
}
```

Ошибка:

```json
{
  "success": false,
  "data": {
    "task_id": "task_xxxxx",
    "status": "failed",
    "product_type": "claude_pro"
  },
  "error": {
    "code": "activation_failed",
    "message": "Не удалось выполнить активацию. Проверьте данные и попробуйте еще раз."
  }
}
```

---

## 8. Ошибки

| `error.code` | Описание |
| --- | --- |
| `unauthorized` | API-ключ отсутствует, неверен или отключен. |
| `rate_limited` | Превышен лимит запросов. |
| `invalid_json` | Некорректный JSON в теле запроса. |
| `not_found` | Метод API не найден. |
| `method_not_allowed` | Неподдерживаемый HTTP-метод. |
| `missing_code` | Не передан CDK-ключ. |
| `missing_codes` | Не передан массив ключей для массовой проверки. |
| `too_many_codes` | Передано более 100 ключей. |
| `missing_account_id` | Не передан ID аккаунта. |
| `missing_task_id` | Не передан `task_id`. |
| `invalid_product` | Передан неверный `product_type`. |
| `card_not_available` | Ключ недоступен для активации. |
| `card_used` | Ключ уже использован. |
| `card_disabled` | Ключ отключен. |
| `product_mismatch` | Ключ выпущен для другого продукта. |
| `queue_busy` | Очередь занята, повторите попытку через 1-5 минут. |
| `no_stock` | Временно отсутствует доступный склад. |
| `task_in_progress` | По ключу уже идет задача. |
| `account_not_eligible` | Аккаунт не подходит для пополнения. |
| `activation_failed` | Не удалось выполнить активацию. |
| `upstream_unavailable` | Сервис временно недоступен. |

---

## 9. Полные примеры интеграции

### JavaScript / Node.js

Храните `API_KEY` только на стороне сервера.

```js
const BASE_URL = "https://cdk.sale/api/v1";
const API_KEY = process.env.MAGIC_SUBS_API_KEY;

async function request(path, options = {}) {
  const response = await fetch(`${BASE_URL}${path}`, {
    ...options,
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
      "Accept-Language": "ru",
      ...(options.headers || {})
    }
  });

  return response.json();
}

async function createActivation(code, accountId, productType) {
  return request("/activations", {
    method: "POST",
    body: JSON.stringify({
      code,
      account_id: accountId,
      product_type: productType
    })
  });
}

async function waitForCompletion(taskId, productType) {
  for (let attempt = 0; attempt < 120; attempt += 1) {
    const result = await request(
      `/activations/${encodeURIComponent(taskId)}?product_type=${encodeURIComponent(productType)}`
    );
    const status = result.data?.status;

    if (status === "completed" || status === "failed" || status === "unknown") {
      return result;
    }

    await new Promise((resolve) => setTimeout(resolve, 5000));
  }

  throw new Error("Activation status timeout");
}
```

### Python

```python
import os
import time
import requests

BASE_URL = "https://cdk.sale/api/v1"
API_KEY = os.environ["MAGIC_SUBS_API_KEY"]

HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
    "Accept-Language": "ru",
}

def create_activation(code: str, account_id: str, product_type: str) -> dict:
    response = requests.post(
        f"{BASE_URL}/activations",
        headers=HEADERS,
        json={
            "code": code,
            "account_id": account_id,
            "product_type": product_type,
        },
        timeout=30,
    )
    return response.json()

def wait_for_completion(task_id: str, product_type: str) -> dict:
    for _ in range(120):
        response = requests.get(
            f"{BASE_URL}/activations/{task_id}",
            headers=HEADERS,
            params={"product_type": product_type},
            timeout=30,
        )
        payload = response.json()
        status = payload.get("data", {}).get("status")

        if status in ("completed", "failed", "unknown"):
            return payload

        time.sleep(5)

    raise TimeoutError("Activation status timeout")
```

### PHP

```php
<?php

$baseUrl = 'https://cdk.sale/api/v1';
$apiKey = getenv('MAGIC_SUBS_API_KEY');

function apiRequest(string $method, string $url, ?array $body, string $apiKey): array
{
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CUSTOMREQUEST => $method,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $apiKey,
            'Content-Type: application/json',
            'Accept-Language: ru',
        ],
    ]);

    if ($body !== null) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body, JSON_UNESCAPED_UNICODE));
    }

    $response = curl_exec($ch);
    curl_close($ch);

    return json_decode((string) $response, true);
}

$activation = apiRequest('POST', $baseUrl . '/activations', [
    'code' => 'EEE-XXXXXXXXXXXXXX',
    'account_id' => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
    'product_type' => 'claude_pro',
], $apiKey);

print_r($activation);
```

---

## 10. Правила надежной интеграции

1. Храните API-ключ на сервере и никогда не отдавайте его в браузер конечного пользователя.
2. Сохраняйте `task_id` в записи заказа сразу после создания активации.
3. Для одного заказа создавайте только одну активацию.
4. Передавайте правильный `product_type`; ключи разных продуктов не взаимозаменяемы.
5. Для Claude Pro заранее предупреждайте пользователя о требованиях к Free-плану и отсутствию неоплаченных счетов.
6. Показывайте пользователю локализованное `error.message`, а в своей логике ориентируйтесь на `error.code`.
7. Учитывайте лимит при параллельном polling большого количества задач.

---

## 11. Получение доступа

Для получения персонального API-ключа или согласования увеличенного лимита обратитесь к менеджеру:

```text
https://t.me/Rewy_dm
```

