Документация · для разработчиков

Публичный API

Подключите КПейдж к своей CRM: создавайте предложения из сделок, публикуйте ссылки и получайте события о том, как клиент их читает. Доступно на тарифе Business и старше.

Основы

База
https://kpage.ru/api/v1/ext

Все адреса ниже — относительно неё.

Аутентификация
Authorization: Bearer $KPAGE_API_KEY

Ключ создаётся в кабинете: Настройки → «API · вебхуки».

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

Считается на ключ, а не на IP. При превышении — 429.

Тариф
Business и старше

На младших тарифах ключ отвечает 402 — это «нужен апгрейд», а не отказ в доступе.

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

# Ключ создаётся в кабинете: Настройки → «API · вебхуки».
# Показывается один раз — сохраните его в переменную окружения, а не в код.
export KPAGE_API_KEY="kp_live_вставьте_сюда_свой_ключ"

Быстрый старт

Полный путь «сделка в CRM → ссылка клиенту → событие о прочтении» — пять запросов. Примеры переключаются на ваш язык одной кнопкой.

  1. 1
    Проверяем ключ
    Заодно видно тариф и остатки лимитов.
    curl "https://kpage.ru/api/v1/ext/me" \
      -H "Authorization: Bearer $KPAGE_API_KEY"
  2. 2
    Находим КП-шаблон
    Шаблон — обычное КП, собранное в кабинете один раз на услугу.
    curl "https://kpage.ru/api/v1/ext/proposals?q=шаблон" \
      -H "Authorization: Bearer $KPAGE_API_KEY"
  3. 3
    Копируем его под сделку
    В ответе — id новой копии.
    curl -X POST "https://kpage.ru/api/v1/ext/proposals/p_shablon/duplicate" \
      -H "Authorization: Bearer $KPAGE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "title": "КП для Ромашки",
        "client": "ООО Ромашка"
      }'
  4. 4
    Просим клиента представиться и публикуем
    После публикации в ответе появится url — это и есть ссылка для клиента.
    curl -X POST "https://kpage.ru/api/v1/ext/proposals/p_7f3a2b/publish" \
      -H "Authorization: Bearer $KPAGE_API_KEY"
  5. 5
    Подписываемся на события
    Дальше API опрашивать не нужно — КПейдж сам придёт с новостями.
    curl -X POST "https://kpage.ru/api/v1/ext/webhooks" \
      -H "Authorization: Bearer $KPAGE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "url": "https://hooks.company.ru/kpage",
        "events": [
          "proposal.lead",
          "proposal.opened"
        ]
      }'

Тело предложения можно собрать двумя путями. Копия шаблона — одно собранное КП на услугу, копия на каждую сделку, без расхода ИИ-квоты. Или бриф: отправьте описание сделки в /generate, ИИ соберёт черновик за несколько минут и сообщит вебхуком proposal.generated. Проверьте черновик в кабинете, прежде чем публиковать: КП уходит клиенту.

Аккаунт

С этого начинается любая интеграция: проверить ключ и узнать, что доступно аккаунту, не дожидаясь отказа на боевом запросе.

Аккаунт, тариф и лимиты

GET/me

Действующий тариф (с учётом места в команде), доступные возможности, остатки лимитов и база публичных ссылок — личная и командная.

curl "https://kpage.ru/api/v1/ext/me" \
  -H "Authorization: Bearer $KPAGE_API_KEY"
Ответ
{
  "account": { "id": "u_a12f", "email": "you@company.ru", "plan": "business", "org": null },
  "features": { "api": true, "customDomain": true, "team": true },
  "limits": {
    "proposals": { "used": 12, "limit": null },
    "ai": { "used": 34, "limit": 400, "resetAt": "2026-08-19T10:00:00.000Z" },
    "storage": { "usedBytes": 10485760, "limitBytes": 5368709120 }
  },
  "brand": {
    "publicDomain": "kpgo.ru",
    "personal": { "domain": "kp.company.ru", "subdomain": null },
    "team": null
  },
  "key": { "id": "ak_31c8", "name": "amoCRM", "lastUsedAt": null }
}

Команда

Сводка по всей команде — те же цифры, что видит руководитель в кабинете. Доступно владельцу команды и руководителю.

Итоги и разбивка по участникам

GET/team

Показатели каждого менеджера и сумма по команде. Числа — за всё время. userId участника подставляется в /proposals?authorId=, чтобы посмотреть, из каких предложений сложилась цифра.

curl "https://kpage.ru/api/v1/ext/team" \
  -H "Authorization: Bearer $KPAGE_API_KEY"
Ответ
{
  "org": { "id": "org_5b31", "name": "Агентство" },
  "totals": { "proposals": 48, "published": 31, "views": 214, "opened": 22 },
  "members": [
    {
      "userId": "u_c74e",
      "email": "manager@company.ru",
      "name": "Пётр",
      "role": "manager",
      "status": "active",
      "proposals": 12,
      "published": 9,
      "views": 63,
      "opened": 7
    }
  ]
}

Коммерческие предложения

Жизненный цикл КП: черновик → правка полей и гейтов → публикация → архив или удаление. Тело КП (текст, структура, дизайн) собирается в кабинете в диалоге с ИИ — API его не генерирует. Рабочий путь для интеграции: держите готовое КП-шаблон и копируйте его. Права те же, что в кабинете: свои предложения плюс командные предложения участников, если ключ принадлежит владельцу команды или руководителю.

Список КП

GET/proposals

Свежие сверху. Удалённые (корзина) не отдаются.

ПолеТипОписание
statusdraft | published | archivedПо умолчанию — все, кроме удалённых
scopeteam | personalТолько командные или только личные КП
qstringПоиск по названию и клиенту
authorIdstringКП участника команды — для владельца и руководителя; только командные предложения этого автора
limitint 1…100Размер страницы, по умолчанию 20
offsetintСмещение для постраничного обхода
curl "https://kpage.ru/api/v1/ext/proposals?status=published&limit=50" \
  -H "Authorization: Bearer $KPAGE_API_KEY"
Ответ
{
  "data": [ { "id": "p_7f3a2b", "title": "КП для Ромашки", "status": "published" } ],
  "meta": { "total": 137, "limit": 50, "offset": 0 }
}

Создать черновик

POST/proposals

Создаёт пустой черновик и занимает слот тарифа. Тело наполняется в кабинете. Чтобы получить готовое КП сразу — копируйте шаблон.

ПолеТипОписание
titlestringНазвание КП
clientstringКлиент, для кого КП
scopeteam | personalКому принадлежит КП; по умолчанию команде
curl -X POST "https://kpage.ru/api/v1/ext/proposals" \
  -H "Authorization: Bearer $KPAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "КП для Ромашки",
    "client": "ООО Ромашка"
  }'

Копировать КП

POST/proposals/{id}/duplicate

Копия готового КП в новый черновик: содержимое и настройки переносятся, ссылка и код доступа — новые. Это и есть «собрать КП из сделки»: один шаблон на услугу, копия на сделку.

ПолеТипОписание
titlestringНазвание копии
clientstringКлиент копии
curl -X POST "https://kpage.ru/api/v1/ext/proposals/p_shablon/duplicate" \
  -H "Authorization: Bearer $KPAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "КП для Ромашки",
    "client": "ООО Ромашка"
  }'

Собрать КП по брифу (ИИ)

POST/proposals/{id}/generate

Асинхронно: ответ 202 с jobId, сборка занимает несколько минут. О готовности сообщит вебхук proposal.generated, или опрашивайте GET /jobs/{id}. Только для пустого черновика. Тратит ИИ-квоту тарифа, через API — не больше 10 запросов в час. Проверьте черновик в кабинете (editorUrl), прежде чем публиковать.

ПолеТипОписание
briefstringЧто продаём, кому и на каких условиях — до 8000 символов
urlsstring[]До 5 сайтов: ИИ прочитает их как материалы
curl -X POST "https://kpage.ru/api/v1/ext/proposals/p_7f3a2b/generate" \
  -H "Authorization: Bearer $KPAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "brief": "Внедрение CRM для отдела продаж из 8 человек, срок 6 недель, бюджет до 600 000 ₽",
    "urls": [
      "https://romashka.ru"
    ]
  }'
Ответ
{
  "jobId": "j_4kQ9",
  "proposalId": "p_7f3a2b",
  "status": "pending",
  "statusUrl": "https://kpage.ru/api/v1/ext/jobs/j_4kQ9",
  "editorUrl": "https://kpage.ru/cp/proposals/p_7f3a2b"
}

Править КП инструкцией (ИИ)

POST/proposals/{id}/revise

Как generate, но для собранного КП: одна инструкция — одна правка. Тоже 202 и jobId.

ПолеТипОписание
instructionstringЧто изменить — до 4000 символов
curl -X POST "https://kpage.ru/api/v1/ext/proposals/p_7f3a2b/revise" \
  -H "Authorization: Bearer $KPAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "instruction": "Добавь этап обучения сотрудников и пересчитай итог"
  }'

Состояние ИИ-задачи

GET/jobs/{id}

status: pending → processing → succeeded | failed | cancelled.

curl "https://kpage.ru/api/v1/ext/jobs/p_7f3a2b" \
  -H "Authorization: Bearer $KPAGE_API_KEY"

Одно КП

GET/proposals/{id}

Полная карточка КП во внешнем формате.

curl "https://kpage.ru/api/v1/ext/proposals/p_7f3a2b" \
  -H "Authorization: Bearer $KPAGE_API_KEY"
Ответ
{
  "id": "p_7f3a2b",
  "number": "417",
  "title": "КП для Ромашки",
  "client": "ООО Ромашка",
  "status": "published",
  "scope": "personal",
  "url": "https://kp.company.ru/aB3xY9",
  "link": "aB3xY9",
  "guards": ["email"],
  "views": 4,
  "expiresAt": "2026-08-03T10:00:00.000Z",
  "createdAt": "2026-07-27T09:00:00.000Z",
  "updatedAt": "2026-07-27T10:12:00.000Z",
  "publishedAt": "2026-07-27T10:00:00.000Z"
}

Изменить поля и гейты доступа

PATCH/proposals/{id}

Передавайте только то, что меняете: вложенные объекты достраиваются текущими значениями. {"accessCode":{"enabled":true}} включит гейт, не обнулив сам код.

ПолеТипОписание
titlestringНазвание КП
clientstringКлиент
expiry{ enabled, preset, date }Срок жизни ссылки: 7d, 1m или custom с датой. Отсчёт — от публикации
accessCode{ enabled, value }Код доступа. Значение только записывается: наружу код не отдаётся
emailId{ enabled }В кабинете — «Идентификация по email»: посетитель указывает адрес до открытия КП
otp{ enabled, whitelist }В кабинете — «Авторизация email + OTP»: код приходит на адрес. Пустой whitelist — любой
nda{ enabled, text }NDA: посетитель принимает текст соглашения
pdf{ enabled, watermark }Выгрузка в PDF на публичной странице
curl -X PATCH "https://kpage.ru/api/v1/ext/proposals/p_7f3a2b" \
  -H "Authorization: Bearer $KPAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "emailId": {
      "enabled": true
    },
    "expiry": {
      "enabled": true,
      "preset": "7d"
    }
  }'

Удалить

DELETE/proposals/{id}

Переносит в корзину и освобождает слот. Повторный вызов вернёт 404.

curl -X DELETE "https://kpage.ru/api/v1/ext/proposals/p_7f3a2b" \
  -H "Authorization: Bearer $KPAGE_API_KEY"

Опубликовать

POST/proposals/{id}/publish

В ответе поле url — готовая ссылка, которую можно отдавать клиенту. Пустой черновик опубликовать нельзя (400): у КП должно быть готовое тело, иначе клиент получил бы рабочую ссылку на пустую страницу.

curl -X POST "https://kpage.ru/api/v1/ext/proposals/p_7f3a2b/publish" \
  -H "Authorization: Bearer $KPAGE_API_KEY"

Снять с публикации

POST/proposals/{id}/unpublish

Ссылка перестаёт открываться, КП возвращается в черновики.

curl -X POST "https://kpage.ru/api/v1/ext/proposals/p_7f3a2b/unpublish" \
  -H "Authorization: Bearer $KPAGE_API_KEY"

В архив

POST/proposals/{id}/archive

Освобождает слот тарифа, история и аналитика сохраняются.

curl -X POST "https://kpage.ru/api/v1/ext/proposals/p_7f3a2b/archive" \
  -H "Authorization: Bearer $KPAGE_API_KEY"

Аналитика и лиды

Что произошло со ссылкой после отправки. Тестовые заходы автора и боты (почтовые сканеры, краулеры превью) в цифры не попадают.

Сводка по КП

GET/proposals/{id}/analytics

Визиты, уникальные посетители, среднее время и разделы, на которых задержались.

curl "https://kpage.ru/api/v1/ext/proposals/p_7f3a2b/analytics" \
  -H "Authorization: Bearer $KPAGE_API_KEY"
Ответ
{
  "proposalId": "p_7f3a2b",
  "sessions": 6,
  "visitors": 2,
  "avgTimeSeconds": 214,
  "opened": true,
  "lastActivity": "2026-07-27T09:12:44.000Z",
  "topSections": [
    { "name": "Цена", "seconds": 190 },
    { "name": "Сроки", "seconds": 88 }
  ]
}

Кто смотрел (лиды)

GET/proposals/{id}/visitors

Если у КП включён гейт email — в списке есть адреса, это лиды. Без гейта посетители анонимны (email: null), но время и число визитов считаются.

curl "https://kpage.ru/api/v1/ext/proposals/p_7f3a2b/visitors" \
  -H "Authorization: Bearer $KPAGE_API_KEY"
Ответ
{
  "data": [
    {
      "id": "v_9d21",
      "email": "buyer@romashka.ru",
      "totalSeconds": 421,
      "sessions": 3,
      "firstVisit": "2026-07-26T14:02:11.000Z",
      "lastVisit": "2026-07-27T09:12:44.000Z",
      "online": false
    }
  ],
  "meta": { "total": 1 }
}

Управление вебхуками

Подписки можно вести и из кабинета (Настройки → «API · вебхуки»), и через API — например, чтобы разворачивать интеграцию скриптом.

Реестр событий

GET/webhook-events

Ключи, на которые можно подписаться, с человеческими названиями.

curl "https://kpage.ru/api/v1/ext/webhook-events" \
  -H "Authorization: Bearer $KPAGE_API_KEY"

Список подписок

GET/webhooks

Адреса, события, секреты, статус последней доставки.

curl "https://kpage.ru/api/v1/ext/webhooks" \
  -H "Authorization: Bearer $KPAGE_API_KEY"

Добавить адрес

POST/webhooks

Только https и только публичный адрес: локальные и внутренние цели отклоняются, редиректы не выполняются. В ответе — секрет для проверки подписи.

ПолеТипОписание
url*stringHTTPS-адрес приёмника
events*string[]Ключи событий из реестра
curl -X POST "https://kpage.ru/api/v1/ext/webhooks" \
  -H "Authorization: Bearer $KPAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.company.ru/kpage",
    "events": [
      "proposal.lead",
      "proposal.opened"
    ]
  }'
Ответ
{
  "id": "wh_2c9d",
  "url": "https://hooks.company.ru/kpage",
  "secret": "whsec_4f21c8a9e05b7d3612fa8c40",
  "events": ["proposal.lead", "proposal.opened"],
  "active": true,
  "lastDeliveryAt": null,
  "lastStatus": null,
  "failureCount": 0,
  "createdAt": "2026-07-27T10:00:00.000Z"
}

Изменить подписку

PATCH/webhooks/{id}

Адрес, набор событий или включённость. Включение вручную обнуляет счётчик отказов.

ПолеТипОписание
urlstringНовый адрес
eventsstring[]Новый набор событий
activebooleanВключить или приостановить доставку
curl -X PATCH "https://kpage.ru/api/v1/ext/webhooks/wh_2c9d" \
  -H "Authorization: Bearer $KPAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      "proposal.lead"
    ],
    "active": true
  }'

Удалить подписку

DELETE/webhooks/{id}

Адрес перестаёт получать события.

curl -X DELETE "https://kpage.ru/api/v1/ext/webhooks/wh_2c9d" \
  -H "Authorization: Bearer $KPAGE_API_KEY"

Пробная доставка

POST/webhooks/{id}/test

Отправляет событие ping тем же путём, что и реальные события, и возвращает результат: код ответа, время и причину неуспеха. Счётчик отказов не трогает.

curl -X POST "https://kpage.ru/api/v1/ext/webhooks/wh_2c9d/test" \
  -H "Authorization: Bearer $KPAGE_API_KEY"
Ответ
{ "ok": true, "status": 200, "event": "ping", "durationMs": 143, "error": null }

События (вебхуки)

Не опрашивайте аналитику по расписанию — подпишитесь. КПейдж сам придёт POST-запросом, когда с предложением что-то произошло.

СобытиеКогда приходитПоля data
proposal.created
КП создано
Появился новый черновик — в кабинете, копированием или через API.proposalId, number
proposal.published
КП опубликовано
Ссылка стала рабочей и её можно отправлять клиенту.proposalId, number, title, client
proposal.lead
Клиент назвал себя (лид)
Посетитель прошёл гейт email или подтверждённый email — впервые с этим адресом на этом КП. Повторные заходы того же адреса события не создают, тестовые заходы автора — тоже.proposalId, number, title, client, email, visitorId
proposal.opened
Клиент открыл КП
Посетитель прошёл все включённые гейты и увидел предложение.proposalId, title, client
proposal.generated
ИИ собрал или изменил КП
Закончилась ИИ-задача по КП — сборка по брифу или правка, запрошенная через API, в чате кабинета или подключённым ИИ-ассистентом. via — api, chat или mcp. jobId совпадает с ответом 202 на generate/revise.proposalId, jobId, via, number, title, client
ping
Пробная доставка
Кнопка «Проверить» в кабинете или POST /webhooks/{id}/test. Подписаться нельзя.test, webhookId

Как выглядит доставка

POST https://hooks.company.ru/kpage
Content-Type: application/json
X-KPage-Event: proposal.lead
X-KPage-Signature: sha256=9f86d081884c7d659a2feaa0c55ad015

{
  "id": "evt_5c1a",
  "event": "proposal.lead",
  "createdAt": "2026-07-27T09:12:44.000Z",
  "data": {
    "proposalId": "p_7f3a2b",
    "number": "417",
    "title": "КП для Ромашки",
    "client": "ООО Ромашка",
    "email": "buyer@romashka.ru",
    "visitorId": "v_9d21"
  }
}

Проверка подписи

Тело подписано HMAC-SHA256 по секрету вебхука. Считайте подпись по сырому телу до разбора JSON и сравнивайте постоянным по времени сравнением — иначе приёмник можно обмануть подделкой или подобрать подпись по замерам.

# Подпись проверяется на вашей стороне — вот тот же расчёт в шелле.
# $BODY — сырое тело запроса, $SECRET — секрет вебхука.
printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1
  • Событие получает автор предложения, а если предложение командное — ещё и владелец команды с руководителями. Рядовому участнику чужие события не уходят.
  • Отвечайте 2xx сразу, а тяжёлую работу делайте фоном: на ответ отведено 8 секунд.
  • Неудачная доставка повторяется трижды с нарастающей паузой. После 15 подряд неуспешных адрес отключается — включить обратно можно в кабинете.
  • Доставка может повториться: событие несёт поле id, используйте его для защиты от повторной обработки.
  • Принимаются только адреса https на публичных хостах; переадресации не выполняются.
  • Проверить приёмник, не дожидаясь реального события, — кнопкой «Проверить» в кабинете или запросом POST /webhooks/{id}/test.

Ошибки

Любая ошибка — JSON одного вида. Поле details появляется у ошибок валидации и перечисляет поля, которые не подошли.

{
  "message": "Пустое КП опубликовать нельзя: сначала соберите содержимое.",
  "code": "BAD_REQUEST"
}
КодЧто значитЧто делать
401Ключ не передан, недействителен или отозванПроверьте заголовок и создайте новый ключ в кабинете
402Возможность не входит в текущий тарифНужен тариф Business или старше — это не проблема оплаты запроса
403Действие упирается в права или лимит тарифаНапример, кончились слоты одновременных КП или вы не управляете этой командой
404Объект не найден или принадлежит другому аккаунтуПроверьте id; чужие объекты намеренно неотличимы от несуществующих
422Тело или параметры не прошли валидациюСмотрите details — там перечислены поля
429Превышен лимит запросов на ключПовторите позже, лимит — 120 запросов в минуту