Документация

LeadShield REST API

Базовый URL: https://lead-shield.ru/api/v1. Формат — JSON, авторизация — Bearer-токен. Ключ выдаётся после регистрации в личном кабинете.

Авторизация

Получите access-токен через POST /auth/login или POST /auth/register, затем передавайте его в заголовке:

Authorization: Bearer YOUR_ACCESS_TOKEN

Access-токен живёт ограниченное время; обновляется через POST /auth/refresh (refresh-токен — в httpOnly cookie). Выход — POST /auth/logout.

Верификация номеров

Основной сценарий из трёх шагов: инициализация → повторная отправка (при необходимости) → проверка кода.

POST/verify/init

Запускает верификацию: звонок Flash Call или SMS с кодом на указанный номер. Код живёт 5 минут.

Параметры: site_key (обязательный), phone в формате +7..., опционально mode (flash_sms, flash_telegram, sms, sms_flash_wcall, telegram_sms_wcall, vk_sms_wcall) и UTM-метки utm_source, utm_medium, utm_campaign, client_id, page_url.

curl -X POST https://lead-shield.ru/api/v1/verify/init \
  -H 'Content-Type: application/json' \
  -d '{
    "site_key": "ls_pk_abc123",
    "phone": "+79123456789"
  }'
# 200 { "session_id": "...", "status": "code_sent", "channel": "flash_call" }
POST/verify/resend

Повторная отправка кода. Не чаще 1 раза в 60 секунд на сессию. Параметр channel = sms или telegram — fallback, если Flash Call не дошёл.

curl -X POST https://lead-shield.ru/api/v1/verify/resend \
  -H 'Content-Type: application/json' \
  -d '{ "session_id": "SESSION_ID", "channel": "sms" }'
POST/verify/check

Проверка 4-значного кода, введённого пользователем. При успехе возвращает JWT-токен верификации (TTL 1 час).

curl -X POST https://lead-shield.ru/api/v1/verify/check \
  -H 'Content-Type: application/json' \
  -d '{ "session_id": "SESSION_ID", "code": "1234" }'
# 200 { "verified": true, "token": "..." } — либо { "verified": false, "attempts_left": 2 }
Виджет для сайта: готовый JS-виджет (CORS *) доступен в кабинете — базовая установка занимает две строки кода, без серверной интеграции.

Сайты

МетодОписание
POST /sitesСоздать сайт. Возвращает site_key и site_secret.
GET /sitesСписок сайтов пользователя.
GET /sites/{id}Детали сайта, включая site_secret.
PATCH /sites/{id}Обновить сайт (название, домен, режим, webhook_url, is_active).
DELETE /sites/{id}Удалить сайт.

Подпись webhook

Каждый webhook (события verification.success и verification.failed на ваш webhook_url) подписан HMAC-SHA256 по секрету сайта site_secret (из личного кабинета). Всегда проверяйте подпись и freshness — иначе перехваченный запрос можно переотправить (replay).

ЗаголовокЗначение
X-LeadShield-TimestampВремя отправки, unix-seconds (UTC).
X-LeadShield-Signaturesha256=<hex> — HMAC-SHA256 от site_secret.

Актуальный формат подписи

Подписывается строка f"{timestamp}.{raw_body}", где raw_body — тело запроса байт-в-байт как получено (без парсинга и пересериализации). Приёмник обязан отклонять запрос, если abs(now - timestamp) > 300 секунд (допуск ±5 минут против replay).

Переходный период (legacy-формат, 30 дней)

Запросы без X-LeadShield-Timestamp принимаются со старой подписью — HMAC-SHA256 от чистого тела: HMAC(site_secret, raw_body). Это обратная совместимость для уже развёрнутых приёмников: принимайте, но пишите deprecation-warning в лог и обновите проверку до актуального формата. Ориентировочное отключение legacy-формата — 21.11.2026 (30 дней с релиза, дату подтвердит владелец сервиса).

POSTпример: проверка подписи на приёмнике (Python)
import hashlib, hmac, time

SECRET = b'ls_sk_0123456789abcdef0123456789abcdef'  # site_secret из кабинета
MAX_SKEW_SEC = 300

def verify_signature(headers: dict, raw_body: bytes) -> bool:
    ts = headers.get('X-LeadShield-Timestamp')
    received = headers.get('X-LeadShield-Signature', '')
    if not received.startswith('sha256='):
        return False
    if ts is not None:
        # Актуальный формат: подпись от "{ts}.{body}", свежесть ±300 сек
        if abs(time.time() - int(ts)) > MAX_SKEW_SEC:
            return False
        signed = f'{ts}.'.encode() + raw_body
    else:
        # Legacy-формат (переходный период): подпись от чистого тела.
        # Принимаем, но логируем deprecation-warning.
        signed = raw_body
    expected = hmac.new(SECRET, signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, received.removeprefix('sha256='))

# Flask: raw_body = request.get_data()
POSTпример: проверка подписи на приёмнике (Node.js)
const crypto = require('crypto');

const SECRET = 'ls_sk_0123456789abcdef0123456789abcdef'; // site_secret из кабинета
const MAX_SKEW_SEC = 300;

// rawBody — тело запроса байт-в-байт (express.raw({ type: 'application/json' }))
function verifySignature(headers, rawBody) {
  const ts = headers['x-leadshield-timestamp'];
  const received = headers['x-leadshield-signature'] || '';
  if (!received.startsWith('sha256=')) return false;
  let signed;
  if (ts !== undefined) {
    // Актуальный формат: "{ts}.{body}", свежесть ±300 сек
    if (Math.abs(Math.floor(Date.now() / 1000) - Number(ts)) > MAX_SKEW_SEC) return false;
    signed = Buffer.concat([Buffer.from(`${ts}.`), rawBody]);
  } else {
    // Legacy-формат (переходный период): подпись от чистого тела + deprecation-warning в лог
    console.warn('[leadshield-webhook] legacy signature format used — update verifier');
    signed = rawBody;
  }
  const expected = crypto.createHmac('sha256', SECRET).update(signed).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received.slice(7)));
}
Важно: подписывается именно сырое тело запроса. Не пересериализуйте JSON (порядок ключей и пробелы меняют подпись), читайте тело как буфер/строку до парсинга.

Логи и статистика

МетодОписание
GET /sites/{id}/logsЛоги верификаций сайта: пагинация, фильтры. Номера маскируются (+7***89).
GET /sites/{id}/exportЭкспорт подтверждённых лидов в CSV.
GET /sites/{id}/statsСтатистика сайта по дням.
GET /stats/overviewСводная статистика по всем сайтам пользователя.

Тарифы и оплата

МетодОписание
GET /billing/plansСписок тарифов с ценами и лимитами (публичный, без авторизации).
GET /billing/currentТекущий тариф пользователя.
POST /billing/checkoutСоздать сессию оплаты выбранного тарифа.

Актуальные тарифы: Trial — 0₽ (5 верификаций), Start — 1690₽/мес (30 верификаций), Pro — 3990₽/мес (100 верификаций), Business — 8990₽/мес (300 верификаций).

Коды ошибок

КодЗначение
400Ошибка валидации полей (неверный формат телефона, короткий пароль и т.п.).
401Не авторизован или неверные учётные данные.
403Нет доступа (например, неверный текущий пароль при смене).
404Сайт или ресурс не найден.
409Конфликт (например, email уже занят при регистрации).
410Сессия верификации завершена или код истёк.
429Превышен лимит запросов. Тело содержит retry_after — пауза в секундах до повтора.
502Провайдер связи недоступен (provider_unavailable). Повторите запрос позже.

Тело ошибки всегда в формате: { "error": "код_ошибки", "message": "Человекочитаемое описание" }.

Лимиты и правила