> ## Documentation Index
> Fetch the complete documentation index at: https://wiki.lumiweb.cc/llms.txt
> Use this file to discover all available pages before exploring further.

# API — обзор

> Публичный REST API Lumi: авторизация по ключам, права, лимиты, идемпотентность и подпись запросов

Публичный REST API Lumi для автоматизации: данные аккаунта, проверка и покупка ресурсов, пополнение баланса. У каждого продукта — свой API и свой ключ.

| Продукт       | Базовый URL                                        | Документация            |
| ------------- | -------------------------------------------------- | ----------------------- |
| Домены        | `https://scamprojecttest.xyz/api/v1`               | [Домены](/api/domains)  |
| Хостинг (VPS) | `https://panel.scamprojecttest.xyz/hosting/api/v1` | [Хостинг](/api/hosting) |
| Прокси        | `https://panel.scamprojecttest.xyz/proxy/api/v1`   | [Прокси](/api/proxy)    |

<Note>
  Ключ одного продукта работает только с этим продуктом. Для доменов, хостинга и прокси выпускаются разные ключи. Ключ выдаёт оператор — напиши в [@lumisup\_robot](https://t.me/lumisup_robot).
</Note>

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

Каждый запрос передаёт ключ в заголовке:

```http theme={"system"}
Authorization: Bearer lumi_domains_XXXXXXXXXXXXXXXXXXXX
```

Ключ показывается один раз — храни его в секрете. В базе лежит только хеш, восстановить ключ нельзя — только выпустить новый.

### Типы ключей

| Тип        | Доступ                                | Привязка                          |
| ---------- | ------------------------------------- | --------------------------------- |
| `reseller` | Только данные **одного** пользователя | Жёстко привязан к одному аккаунту |
| `operator` | Все пользователи                      | Не привязан; полный доступ        |

* **Reseller**: все запросы автоматически ограничены его аккаунтом. Чужой `user_id` вернёт `404` (существование чужих аккаунтов не раскрывается).
* **Operator**: указывает `user_id` явно (в теле или `?user_id=`); для денежных операций это обязательно.

### Права (scopes)

Reseller-ключу выдаётся набор прав. Operator-ключ имеет все права неявно.

| Scope                                         | Что разрешает                                         |
| --------------------------------------------- | ----------------------------------------------------- |
| `domains:read` / `vps:read` / `proxy:read`    | Чтение: списки, статусы, цены, каталог                |
| `domains:write` / `vps:write` / `proxy:write` | Управление своими объектами                           |
| `domains:buy` / `vps:buy` / `proxy:buy`       | Покупка и продление (списывает баланс)                |
| `deposits:write`                              | Создание счетов на пополнение                         |
| `admin:*`                                     | Только operator-ключ; кросс-пользовательские операции |

Недостающее право → `403 forbidden_scope`.

## Денежные операции

Эндпоинты, которые двигают деньги (создание счёта, покупка, продление), подчиняются дополнительным правилам.

<Warning>
  Денежные операции работают, только когда на деплое включён флаг `API_MONEY_ENABLED`. Пока он выключен — такие запросы возвращают `403 money_disabled`, даже если у ключа есть нужный scope.
</Warning>

### Идемпотентность

Каждый денежный `POST` обязан нести заголовок `Idempotency-Key` (8–200 символов):

```http theme={"system"}
Idempotency-Key: a1b2c3d4-orders-2026-06-09
```

* Повтор запроса с тем же ключом вернёт **исходный** ответ, не выполняя операцию заново.
* Два одновременных одинаковых запроса не выполнятся дважды: проигравший получит `409 in_progress`.
* Без заголовка → `400 idempotency_key_required`.

### Дневной лимит трат

У ключа может быть дневной лимит в USD (или общий по умолчанию). По его достижении денежные операции возвращают `402 daily_cap_exceeded` до начала следующих суток (UTC). Operator-ключи лимиту не подчиняются.

### Подпись запросов (опционально)

Если ключ выпущен с требованием подписи, каждый денежный запрос должен нести заголовок:

```text theme={"system"}
X-Signature: HMAC-SHA256(signing_secret, raw_request_body)
```

Подпись считается по точным байтам тела запроса. Так утечка одного Bearer-ключа не даёт двигать деньги без второго секрета. Без корректной подписи → `401 invalid_signature`.

<CodeGroup>
  ```python Python theme={"system"}
  import hmac, hashlib, requests

  KEY = "lumi_domains_XXX"
  SIGNING = "your_signing_secret"  # выдаётся вместе с ключом, если включена подпись
  body = b'{"amount_usd":"5","provider":"cryptobot"}'
  sig = hmac.new(SIGNING.encode(), body, hashlib.sha256).hexdigest()

  requests.post(
      "https://scamprojecttest.xyz/api/v1/deposits",
      headers={
          "Authorization": f"Bearer {KEY}",
          "Idempotency-Key": "dep-2026-06-09-001",
          "X-Signature": sig,
          "Content-Type": "application/json",
      },
      data=body,
  )
  ```
</CodeGroup>

## Лимиты запросов

| Класс            | Лимит по умолчанию          |
| ---------------- | --------------------------- |
| Обычные запросы  | 120 запросов / 60 с на ключ |
| Денежные запросы | 20 запросов / 60 с на ключ  |

Bulk-запрос расходует столько денежных «токенов», сколько в нём позиций. При превышении → `429 rate_limited` с заголовком `Retry-After` (секунды).

## Пагинация

Списочные эндпоинты используют курсор:

```text theme={"system"}
GET /v1/domains?limit=50&cursor=eyJ...
```

* `limit`: 1–200 (по умолчанию 50).
* Ответ: `{ "items": [...], "next_cursor": "...", "has_more": true }`. Передай `next_cursor` в `cursor` для следующей страницы. `next_cursor: null` — страниц больше нет.

## Массовые операции (bulk)

Bulk-эндпоинты принимают массив и возвращают **по-позиционный** результат — операции **не атомарны**: ошибка одной позиции не откатывает остальные.

```json theme={"system"}
{
  "total": 2,
  "ok": 1,
  "failed": 1,
  "items": [
    { "ref": "a.com", "status": "ok", "result": { "...": "..." } },
    { "ref": "b.com", "status": "failed", "error": { "code": "...", "message": "..." } }
  ]
}
```

Лимит позиций в одном запросе — 500, иначе `413 batch_too_large`. Долгие bulk-покупки выполняются асинхронно: эндпоинт возвращает `202` с `batch_id`, статус опрашивается через `GET /v1/batches/{batch_id}`.

## Формат ошибок

Все ошибки — единый JSON:

```json theme={"system"}
{ "error": { "code": "forbidden_scope", "message": "Key lacks the required scope 'domains:buy'." } }
```

| HTTP | code                 | Когда                                     |
| ---- | -------------------- | ----------------------------------------- |
| 401  | `missing_bearer`     | Нет заголовка Authorization               |
| 401  | `invalid_key`        | Ключ неизвестен, отозван или истёк        |
| 401  | `invalid_signature`  | Требуется подпись, а её нет/неверна       |
| 402  | `daily_cap_exceeded` | Достигнут дневной лимит трат              |
| 403  | `forbidden_scope`    | У ключа нет нужного права                 |
| 403  | `money_disabled`     | Денежные операции выключены на деплое     |
| 404  | `not_found`          | Объект не найден (или чужой — у reseller) |
| 409  | `in_progress`        | Идентичный запрос ещё выполняется         |
| 413  | `batch_too_large`    | Слишком много позиций в bulk              |
| 422  | `validation_error`   | Некорректное тело/параметры               |
| 429  | `rate_limited`       | Превышен лимит запросов                   |
| 503  | `api_disabled`       | API выключен на деплое                    |

## Деньги в ответах

Денежные суммы всегда приходят строками (`"5.15"`), а не числами с плавающей точкой — без потерь точности. Передавай суммы в запросах тоже строками.

## Здоровье сервиса

```http theme={"system"}
GET /healthz   →  { "status": "ok", "service": "api" }
```

Без авторизации; только проверка живости.
