<!-- /docs/reference (ru) -->

# FrankLab Partner API — справочник

Партнёрский программный интерфейс FrankLab. Аутентификация — партнёрский API-ключ (заголовок X-API-Key или Authorization: Bearer); исключение — выпуск и отзыв самих ключей, они требуют сессию партнёра. Проекция сгенерирована из исходного кода; при расхождении истина — код.

Base URL: https://apergrex.ru/franklab/api

## GET /v1/billing/api-keys

Список API-ключей

Ключи партнёра: префикс, метка, активность, даты. Сырые ключи никогда не возвращаются.

Ответы:

- `200` — Список ключей
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## POST /v1/billing/api-keys

Создать API-ключ

Создаёт новый ключ fl_live_… (аутентификация — сессия партнёра, не API-ключ: ключ не может выпускать ключи). Сырое значение возвращается ровно один раз — сохраните его сразу.

Ответы:

- `201` — Ключ создан; сырое значение — один раз
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## DELETE /v1/billing/api-keys/{id}

Отозвать API-ключ

Деактивирует ключ партнёра по его id (аутентификация — сессия партнёра). Идемпотентно: для чужого id совпадений нет, ошибка не возвращается.

Параметры:

- `id` (path, обяз.) `{"type":"string"}` — Идентификатор ключа

Ответы:

- `200` — Ключ деактивирован (или уже был)
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## GET /v1/billing/balance

Баланс партнёра

Возвращает total (всего зачислено), used (подтверждённый lifetime-расход), reserved (в открытых резервах задач), available (доступно = total − used − reserved, не ниже 0) и флаг isLow. Суммы — в франках (₣), внутренней учётной единице платформы.

Ответы:

- `200` — Баланс получен
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## GET /v1/billing/legal-invoices/{id}/pdf

PDF счёта

PDF-файл счёта (inline; иногда — редирект на файл банка). Лимит: 12 запросов/мин.

Параметры:

- `id` (path, обяз.) `{"type":"string"}` — Идентификатор счёта

Ответы:

- `200` — PDF-файл
- `302` — Редирект на PDF-файл банка
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## POST /v1/billing/legal-invoices/{id}/refresh

Обновить статус счёта

Запрашивает актуальный статус счёта у банка. Лимит: 6 запросов/мин.

Параметры:

- `id` (path, обяз.) `{"type":"string"}` — Идентификатор счёта

Ответы:

- `200` — Актуальный статус
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## POST /v1/billing/legal-invoices/{id}/sbp-link

СБП B2B-ссылка

Создаёт QR-ссылку оплаты счёта через СБП B2B. Лимит: 6 запросов/мин.

Параметры:

- `id` (path, обяз.) `{"type":"string"}` — Идентификатор счёта

Ответы:

- `200` — Ссылка СБП
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## POST /v1/billing/legal-invoices/create

Выставить счёт юрлицу

Создаёт счёт через T-Bank Business; при оплате баланс зачисляется автоматически (₽→₣ 1:1). Лимит: 3 запроса/мин.

Ответы:

- `201` — Счёт создан
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## GET /v1/billing/legal-invoices/history

История счетов

Счета юрлица со статусами, суммами и датами зачисления.

Параметры:

- `page` (query) `{"type":"integer","minimum":1,"default":1}` — Номер страницы
- `limit` (query) `{"type":"integer","minimum":1,"maximum":100,"default":20}` — Размер страницы

Ответы:

- `200` — Список счетов
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## GET /v1/billing/legal-invoices/profile

Реквизиты юрлица

Профиль компании для выставления счетов (ИНН, название, адрес и т.д.).

Ответы:

- `200` — Профиль
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## PUT /v1/billing/legal-invoices/profile

Сохранить реквизиты

Создаёт или обновляет профиль компании для счетов.

Ответы:

- `200` — Профиль сохранён
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## GET /v1/billing/payment/history

История пополнений

Список платёжных операций пополнения с их статусами (pending/authorized/confirmed/canceled/rejected/failed). Возвращает последние 50 операций.

Ответы:

- `200` — История пополнений
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## POST /v1/billing/payment/link

Ссылка на пополнение

Создаёт платёжную ссылку T-Bank для пополнения баланса (₽→₣ 1:1). Заголовок Idempotency-Key обязателен: повтор с тем же ключом возвращает тот же платёж, а не создаёт новый. Сохранение карты не доступно программно. Ссылка открывается плательщиком в браузере. Лимит: 3 запроса/мин.

Параметры:

- `Idempotency-Key` (header, обяз.) `{"type":"string"}` — Ключ идемпотентности (до 128 символов; повтор с тем же ключом и той же суммой возвращает тот же платёж, иная сумма — 409)

Ответы:

- `201` — Платёж создан (или возвращён существующий): paymentUrl для оплаты
- `400` — Нет/некорректный Idempotency-Key или сумма вне диапазона
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## GET /v1/billing/pricing/catalog

Витрина цен

Базовая витрина цен по семействам (видео/изображения/аудио/текст/обработка) с единицами тарификации. Ответ не кэшируется (no-store); перечитывать достаточно раз в 24 часа (refreshAfterSeconds).

Ответы:

- `200` — Каталог цен
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## POST /v1/billing/pricing/estimate

Оценка стоимости запуска

Оценка стоимости операции без побочных эффектов, по вашему тарифу (тот же механизм, что резервирует средства при запуске). Передайте модель и параметры; URL-медиа не нужно — только счётчики референсов.

Ответы:

- `200` — Квота стоимости по действующему тарифу
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## GET /v1/billing/tariff

Тарифный план

Действующий тариф (base/pro/max/ultra), назначенный и заработанный планы, lifetime-расход (₣), следующий уровень и сколько осталось до него, процент скидки от базовой цены.

Ответы:

- `200` — Тариф получен
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## GET /v1/billing/transactions

История транзакций

Постраничный журнал операций: тип (topup/reservation/confirm/refund; debit — редкие легаси-строки), сумма ₣, цена за единицу, задача, дата, атрибуция. Пагинация page/limit (limit ≤ 100).

Параметры:

- `type` (query) `{"type":"string","enum":["topup","reservation","confirm","refund","debit"]}` — Фильтр по типу транзакции (debit — легаси-строки)
- `attribution` (query) `{"type":"string","enum":["all","company","personal"]}` — Чьи пополнения показывать: все / компании / личные
- `dateFrom` (query) `{"type":"string"}` — Начало периода (ISO 8601)
- `dateTo` (query) `{"type":"string"}` — Конец периода (ISO 8601)
- `page` (query) `{"type":"integer","minimum":1,"default":1}` — Номер страницы
- `limit` (query) `{"type":"integer","minimum":1,"maximum":100,"default":20}` — Размер страницы

Ответы:

- `200` — Список транзакций с пагинацией
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## GET /v1/billing/transactions/export

Экспорт транзакций в CSV

Выгрузка журнала в CSV (attachment). Те же фильтры, что у списка; не более 10 000 строк за выгрузку — при превышении в конец добавляется строка-комментарий `# truncated: …` с полным количеством.

Параметры:

- `type` (query) `{"type":"string","enum":["topup","reservation","confirm","refund","debit"]}` — Фильтр по типу транзакции (debit — легаси-строки)
- `dateFrom` (query) `{"type":"string"}` — Начало периода (ISO 8601)
- `dateTo` (query) `{"type":"string"}` — Конец периода (ISO 8601)
- `format` (query) `{"type":"string","enum":["csv"]}` — Формат выгрузки; единственный вариант — csv
- `attribution` (query) `{"type":"string","enum":["all","company","personal"]}` — Чьи пополнения включать: все / компании / личные

Ответы:

- `200` — CSV-файл
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## GET /v1/billing/usage/daily

Расход по дням

Подтверждённый расход по дням UTC: confirm минус возвраты, не привязанные к задачам.

Параметры:

- `dateFrom` (query) `{"type":"string"}` — Начало периода (ISO 8601)
- `dateTo` (query) `{"type":"string"}` — Конец периода (ISO 8601)

Ответы:

- `200` — Агрегация по дням
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## GET /v1/webhooks/deliveries

История доставок

Последние 50 доставок партнёра со статусами (pending/delivering/delivered/uncertain/dead_lettered), попытками и кодами ответов.

Параметры:

- `endpointId` (query) `{"type":"string"}` — Фильтр по эндпоинту
- `status` (query) `{"type":"string","enum":["pending","delivering","delivered","uncertain","dead_lettered"]}` — Фильтр по статусу доставки

Ответы:

- `200` — Список доставок
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## POST /v1/webhooks/deliveries/{id}/replay

Повторить доставку

Ставит доставку (uncertain, dead_lettered или delivered) обратно в очередь с тем же eventId. Лимит: 30 запросов/мин.

Параметры:

- `id` (path, обяз.) `{"type":"string"}` — Идентификатор доставки

Ответы:

- `200` — Доставка пере-в-очереди
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## GET /v1/webhooks/endpoints

Список вебхук-эндпоинтов

Эндпоинты партнёра: URL, подписки, активность. Секреты никогда не возвращаются.

Ответы:

- `200` — Список эндпоинтов
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## POST /v1/webhooks/endpoints

Зарегистрировать эндпоинт

Создаёт эндпоинт; секрет whsec_… возвращается ровно один раз. URL — публичный HTTPS; внутренние адреса отклоняются. До 10 эндпоинтов на партнёра. Лимит: 10 запросов/мин.

Ответы:

- `201` — Эндпоинт создан; секрет — один раз
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## DELETE /v1/webhooks/endpoints/{id}

Удалить эндпоинт

Удаляет эндпоинт партнёра; его ожидающие доставки исчезают вместе с ним.

Параметры:

- `id` (path, обяз.) `{"type":"string"}` — Идентификатор эндпоинта

Ответы:

- `200` — Эндпоинт удалён
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## POST /v1/webhooks/endpoints/{id}/rotate-secret

Ротация секрета

Генерирует новый подписывающий секрет; старый перестаёт работать немедленно. Новое значение — один раз. Периода перекрытия нет: доставки в полёте немедленно подписываются новым секретом. Лимит: 10 запросов/мин.

Параметры:

- `id` (path, обяз.) `{"type":"string"}` — Идентификатор эндпоинта

Ответы:

- `200` — Новый секрет
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна

## POST /v1/webhooks/endpoints/{id}/test

Тестовое событие

Ставит в очередь событие webhook.test ровно для ЭТОГО эндпоинта — проверьте приём и подпись. Лимит: 6 запросов/мин.

Параметры:

- `id` (path, обяз.) `{"type":"string"}` — Идентификатор эндпоинта

Ответы:

- `200` — Событие поставлено в очередь
- `401` — Ключ отсутствует или недействителен
- `403` — Legacy service-ключи не принимаются: нужен партнёрский API-ключ
- `429` — Превышен лимит запросов (по умолчанию 60/мин на партнёра); заголовок Retry-After даёт секунды до сброса окна
