KELVIN TG

API

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

API — это способ делать из своей программы то же, что ты делаешь в кабинете руками: читать цифры и ответы лидов, создавать и запускать рассылки, отвечать в чатах. Ничего сверх кабинета API не умеет, и все защиты аккаунтов работают так же.

API включается для кабинета отдельно. Если в меню кабинета нет раздела «API», напиши в поддержку — включим.

Получи ключ

  1. Открой в кабинете «API» и нажми «Создать ключ».
  2. Впиши название — по нему ты потом поймёшь, где ключ используется.
  3. Выбери право ключа и нажми «Создать ключ».
  4. Скопируй ключ. Он показывается один раз: закрыл окно, не сохранив, — отзови ключ и создай новый.
Право ключаЧто может программа
«Только чтение»Видит рассылки, статистику, аккаунты и чаты. Ничего не меняет и не отправляет.
«Чтение и действия»Плюс создаёт и запускает рассылки, отвечает лидам, меняет настройки.
Галочка «Разрешить опасные действия»Плюс удаляет аккаунты и телефоны, выгружает весь кабинет, снимает блок @SpamBot. Не ставь без нужды.

В кабинете может быть до пяти ключей. Ключ — это пароль: не отправляй его в чаты и не вставляй на сайты. Если ключ мог попасть к чужим, отзови его в разделе «API» — он перестанет работать сразу.

Первый запрос

Вставь свой ключ вместо kelvin_… и выполни в терминале:

export KELVIN_KEY="kelvin_…"
curl "https://app.kelvintg.ru/api/v1/me" \
  -H "Authorization: Bearer $KELVIN_KEY"

В ответе — название кабинета и право ключа. Значит, всё подключено.

Как устроены запросы

  • Адрес. Все запросы начинаются с https://app.kelvintg.ru/api/v1.
  • Ключ. В каждом запросе — заголовок Authorization: Bearer и твой ключ.
  • Данные. Тело запроса и ответ — JSON.
  • Лимит. Один ключ принимает до 120 запросов в минуту. Сверх лимита придёт отказ rate_limited и число секунд, через которое можно повторить. На скорость рассылок лимит не влияет. Статистику и выгрузку кабинета можно запрашивать до 10 раз в минуту: они считаются по всем отправкам.
  • Отправляют телефоны. Ответ «принято» значит, что сообщение встало в очередь телефона. Телефон отправит его в своём темпе и в пределах дневного лимита аккаунта. Итог смотри в состоянии рассылки или диалога.

Состояния

Ответ «принято» — это ещё не «отправлено». Что происходит дальше, видно по состоянию.

ЧтоСостояниеЧто значит
РассылкаpendingЧерновик: создана, не запущена.
runningИдёт: телефоны берут сообщения по очереди.
pausedНа паузе.
cancelledОтменена, вернуть нельзя.
completedВсе сообщения отправлены.
completed_with_issuesЗакончена, но часть сообщений не ушла.
Сообщение и ответ лидуpendingВ очереди телефона.
claimed, authorizedТелефон взял сообщение и отправляет.
sentОтправлено.
failedНе отправлено, причина — в reason.
unknown, needs_reviewТелефон не увидел подтверждения. Не отправляй заново: KELVIN проверит сам.
cancelledОтменено до отправки.
ТелефонreadyНа связи и готов работать.
unhealthyНа связи, но что-то мешает: экран заблокирован, нет сети или выключены «Действия».
offlineНе выходил на связь.

Аккаунт работает, если у него enabled: true, нет deletedAt и blockedReason, а его телефон в состоянии ready.

Повтор запроса

Связь может оборваться, и ты не узнаешь, дошёл ли запрос. Чтобы повтор не создал вторую рассылку и не отправил ответ дважды, передавай в создающих запросах заголовок Idempotency-Key — свой номер этого запроса, от 8 до 64 символов.

  • Повтор с тем же номером ничего не делает второй раз и возвращает тот же ответ.
  • Ответы хранятся неделю. Этого хватает, чтобы повторить запрос после обрыва связи.
  • Один номер — один запрос. Для нового действия бери новый номер.
  • Если первый запрос оборвался на середине, придёт отказ request_in_progress. Проверь результат чтением и только потом решай, отправлять ли заново с новым номером.

В описании каждого запроса сказано, нужен ли ему номер.

Отказы

Отказ приходит с кодом для программы и фразой для человека:

{ "error": { "code": "read_only_key", "message": "Этот ключ только для чтения. Создай в кабинете ключ с правом «чтение и действия»." } }
КодЧто случилосьЧто сделать
invalid_keyКлюч не подошёл или отозван.Проверь заголовок или создай новый ключ.
api_disabledAPI для кабинета выключен.Напиши в поддержку.
access_endedДоступ к кабинету закончился или приостановлен.Продли доступ — ключ заработает снова.
read_only_keyКлюч только для чтения, а запрос что-то меняет.Создай ключ с правом «Чтение и действия».
danger_not_allowedДействие опасное, а у ключа нет этой галочки.Создай ключ с галочкой «Разрешить опасные действия».
rate_limitedБольше 120 запросов в минуту.Повтори через число секунд из ответа.
idempotency_key_requiredЗапросу нужен номер, а его нет.Добавь заголовок Idempotency-Key.
request_in_progressПервый запрос с этим номером не закончился.Проверь результат чтением.
idempotency_key_reusedЭтот номер уже был у другого запроса или с другими данными.Возьми новый номер.
answer_not_keptЗапрос уже выполнен, но ответ был слишком большой и не сохранён.Проверь результат чтением, повторять не нужно.
not_foundТакого адреса или объекта в твоём кабинете нет.Проверь адрес и номер объекта.

У отказов кабинета коды свои, например campaign_not_found или template_empty. Если сервер знает подробности — какое поле, какой аккаунт, какое место в тексте, — они придут рядом в details.

Частые задачи

Отправить одно сообщение одному человеку

Отдельного запроса нет: это рассылка из одного получателя. Сначала создай её, потом запусти.

curl -X POST "https://app.kelvintg.ru/api/v1/campaigns" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Idempotency-Key: order-0001" \
  -H "Content-Type: application/json" \
  -d '{"name": "Заявка 0001", "recipientsText": "synthetic_lead", "template": "Здравствуйте! Это проверка.", "accountIds": ["ID_АККАУНТА"]}'

curl -X POST "https://app.kelvintg.ru/api/v1/campaigns/ID_РАССЫЛКИ/start" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" -d '{}'

ID_АККАУНТА возьми из списка аккаунтов, ID_РАССЫЛКИ — из ответа первого запроса. Повторный запуск уже идущей рассылки безопасен: второй раз ничего не отправится. Человеку, который уже тебе ответил, проще написать через ответ в чате.

Забирать ответы лидов

Запрашивай новые сообщения «после какого»: в первый раз с after=0, дальше — с числом after из прошлого ответа. Так ничего не потеряется и не придёт дважды.

curl "https://app.kelvintg.ru/api/v1/chats/incoming?after=0" \
  -H "Authorization: Bearer $KELVIN_KEY"

after — это номер последнего сообщения, которое ты уже получил. Если новых нет, after в ответе не меняется. Раз в 5–10 секунд достаточно. Если в ответе more: true, запроси ещё раз сразу — пришло больше, чем помещается в один ответ.

Ответить лиду

curl -X POST "https://app.kelvintg.ru/api/v1/chats/ID_ДИАЛОГА/reply" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Idempotency-Key: reply-0001" \
  -H "Content-Type: application/json" \
  -d '{"text": "Спасибо за ответ! Расскажу подробнее."}'

ID_ДИАЛОГА — это chatId из входящего сообщения. Ответ уходит с того же аккаунта, которому писал лид. Запомни свой номер из Idempotency-Key: по нему запрос «Дошёл ли ответ» скажет, что стало с ответом.

Остановить всё

Программа может нажать «Остановить всё» — например, если твоя система заметила проблему. Возобновить работу может только человек в кабинете.

curl -X POST "https://app.kelvintg.ru/api/v1/work/stop-all" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" -d '{}'

Для ИИ-помощника

Полное машинное описание всех запросов лежит по адресу /api/v1/openapi.json. Отдай эту ссылку своему ИИ-помощнику или программисту — по ней видно каждый запрос, его поля и право ключа.

Чего в API нет

  • Оплаты и подписки, смены пароля и логина.
  • Кодов подключения телефонов и всего, что телефон делает сам.
  • Ключа ИИ и настройки раздела «ИИ».
  • Кнопки «Возобновить работу»: это решение человека.
  • Смены аватарки аккаунта и добавления получателей в уже запущенную рассылку — этого нет и в кабинете. Чтобы написать новым людям, создай новую рассылку.

Все запросы

Ниже — каждый запрос: что он делает, пример и ответ. В примерах $KELVIN_KEY — твой ключ, слова вроде ID_РАССЫЛКИ замени на номер из ответа предыдущего запроса.

Проверка ключа

Проверить ключ

GET /me · право ключа: только чтение

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

curl "https://app.kelvintg.ru/api/v1/me" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "workspace": {
    "id": "ID",
    "name": "Демо-кабинет",
    "accessUntil": null
  },
  "key": {
    "id": "ID",
    "name": "Пример",
    "scope": "danger"
  },
  "limitPerMinute": 120
}

Рассылки

Список рассылок

GET /campaigns · право ключа: только чтение

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

curl "https://app.kelvintg.ru/api/v1/campaigns" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "campaigns": [],
  "deletedCampaigns": []
}

Рассылка и её получатели

GET /campaigns/ID_РАССЫЛКИ · право ключа: только чтение

Отдаёт рассылку целиком: настройки, статус и что стало с каждым получателем. Здесь же лежит номер версии revision, он нужен, чтобы менять рассылку. Рассылку из корзины этот запрос не находит.

curl "https://app.kelvintg.ru/api/v1/campaigns/ID_РАССЫЛКИ" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "id": "ID",
  "name": "Проверка",
  "baseLabel": null,
  "status": "pending",
  "createdAt": "2026-10-10T10:00:00.000Z",
  "schedule": null,
  "scheduleStatus": null,
  "editable": true,
  "revision": 1,
  "startedAt": null,
  "secondTouch": {
    "enabled": false,
    "waiting": false,
    "planned": 0,
    "sent": 0
  },
  "post": null,
  "autoReply": {
    "enabled": false,
    "sent": 0,
    "waiting": 0,
    "refused": 0,
    "missed": 0
  },
  "total": 1
}

Скачать получателей таблицей

GET /campaigns/ID_РАССЫЛКИ/export · право ключа: только чтение

Отдаёт получателей рассылки файлом CSV с их статусами, как кнопка выгрузки в кабинете. Ответ — файл, а не JSON: разделитель «;», открывается в Excel.

ПолеГдеЧто это
filterв адресестрока. Кого выгрузить: all — всех (по умолчанию), waiting — кто ещё в очереди, review — с ошибками, unknown — где отправка ещё проверяется, либо точный статус: pending, claimed, authorized, sent, failed, needs_review, cancelled.
qв адресестрока. Поиск по получателю (без @) и по значениям полей из таблицы.
curl "https://app.kelvintg.ru/api/v1/campaigns/ID_РАССЫЛКИ/export?filter=sent" \
  -H "Authorization: Bearer $KELVIN_KEY"

Проверить список получателей

POST /import/preview · право ключа: только чтение

Разбирает список и показывает, кого KELVIN распознал, какие строки не подошли, сколько повторов и кого уберёт «Не писать». Ничего не сохраняет. Удобно вызвать перед созданием рассылки.

ПолеГдеЧто это
text — обязательнов телестрока. Получатели, по одному в строке: username или телефон, как в рассылке. Не больше 20 000.
curl -X POST "https://app.kelvintg.ru/api/v1/import/preview" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "@synthetic_lead\nsynthetic_lead_two"
}'

Ответ:

{
  "recipients": [
    {
      "kind": "username",
      "value": "synthetic_lead"
    },
    "… ещё 1"
  ],
  "invalid": [],
  "duplicates": 0,
  "excluded": []
}

Проверить текст сообщения

POST /messages/preview · право ключа: только чтение

Показывает, какие сообщения получатся из текста с вариантами и полями. Если передать получателей, покажет ещё, у кого не хватает значений и как распозналось имя. Ничего не сохраняет.

ПолеГдеЧто это
template — обязательнов телестрока. Текст с вариантами {а|б} и полями [[поле|запасной текст]]. Вложенные скобки не поддерживаются.
recipientsTextв телестрока. Получатели, как в рассылке. Без них в ответе будут только варианты текста и их число.
recipientFieldsв телеобъект. Поля из таблицы по получателям, как в рассылке.
nameExclusionsв телесписок строк. Кому писать без обращения по имени, как в рассылке.
filterв телестрока. Кого показать в таблице имён: "all" (по умолчанию), "recognized" — имя распознано, "unrecognized" — не распознано.
offsetв телечисло. С какой строки таблицы имён начать, от 0. По умолчанию 0.
limitв телечисло. Сколько строк таблицы имён отдать, 1–200. По умолчанию 20.
curl -X POST "https://app.kelvintg.ru/api/v1/messages/preview" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "template": "{Здравствуйте|Добрый день}! Это проверка."
}'

Ответ:

{
  "variants": [
    "Здравствуйте! Это проверка.",
    "… ещё 1"
  ],
  "count": 2
}
ОтказЧто случилось и что сделать
400В тексте ошибка в фигурных скобках: лишняя, незакрытая, вложенная или с пустым вариантом. В ответе указано место.

Проверить пост

POST /posts/check · право ключа: чтение и действия

Проверяет пост, который ты хочешь разослать вместо текста: код от @PostBot или ссылку на пост публичного канала. Ничего не сохраняет. Для кода проверяется только его вид, для ссылки KELVIN открывает пост на t.me.

ПолеГдеЧто это
post — обязательнов телеобъект. Пост от @PostBot: {via: "postbot", codes: ["код", …]}, код — 6–64 латинских букв, цифр, «_» и «-». Пост из канала: {via: "forward", links: ["t.me/канал/123", …]}, только публичные каналы. В обоих случаях не больше 50 постов.
curl -X POST "https://app.kelvintg.ru/api/v1/posts/check" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "post": {
    "via": "postbot",
    "codes": [
      "synthetic0code1"
    ]
  }
}'

Ответ:

{
  "ok": true,
  "post": {
    "via": "postbot",
    "codes": [
      "synthetic0code1"
    ]
  },
  "line": "Пост · @PostBot synthetic0code1"
}
ОтказЧто случилось и что сделать
400Пост не открывается: канал должен быть публичным, а пост существовать. Ссылки вида t.me/c/… на закрытый канал не подходят.
400Сайт t.me не ответил, пост проверить не удалось. Повтори через минуту.

Проверить рассылку без создания

POST /campaigns/rehearsal · право ключа: только чтение

Принимает те же поля, что «Создать рассылку», и делает те же проверки, но ничего не сохраняет. В ответе: сколько получателей останется после «Не писать», сколько вариантов текста и до пяти примеров готовых сообщений.

ПолеГдеЧто это
name — обязательнов телестрока. Название рассылки, до 200 знаков.
template — обязательнов телестрока. Текст сообщения, до 20 000 знаков со всеми вариантами. Варианты пишутся как {Здравствуйте|Добрый день}, поля из таблицы как [[город|запасной текст]], обращение по имени как [[имя]]. Каждый итоговый вариант не длиннее 4096 знаков. Можно не передавать, только если отправляешь пост без текста.
recipientsText — обязательнов телестрока. Получатели, по одному в строке: username (5–32 знака, с @ или без, можно ссылкой t.me/имя) или телефон (8–15 цифр). Не больше 20 000. Повторы убираются сами, любая нераспознанная строка даёт отказ.
accountIds — обязательнов телесписок строк. С каких аккаунтов отправлять: хотя бы один аккаунт этого кабинета. Получатели раздаются по аккаунтам по кругу.
scheduleв телеобъект. Когда можно отправлять: {timeZone: "Europe/Moscow" или "UTC", startLocal: "ГГГГ-ММ-ДДTЧЧ:ММ" (не раньше текущей минуты) или null, weekdays: дни недели 0–6, где 0 — воскресенье, intervals: от 1 до 4 отрезков {start: "ЧЧ:ММ", end: "ЧЧ:ММ"}}. Если конец раньше начала, отрезок переходит через полночь. Без поля рассылка идёт без расписания.
pacingв телеобъект. Темп. Любое поле можно не передавать, тогда берётся значение из скобок: batchMin и batchMax — сообщений подряд, 1–20 (2 и 5); waitMinSeconds и waitMaxSeconds — пауза между сообщениями, 10–1800 с (55 и 85); readMinSeconds и readMaxSeconds — перерыв между сериями, 30–600 с (90 и 240); readInBreaks — читать каналы в перерывах (да); spreadEnabled — растягивать отправку по времени (да); spreadMinutes — 15–480 (60). Минимум не больше максимума.
recipientFieldsв телеобъект. Поля из таблицы по получателям: {"username:synthetic_lead": {"город": "Казань"}}. Ключ — "username:имя" или "phone:+7…". Имя поля — строчные буквы, цифры и «_», до 32 знаков; значение до 500 знаков; до 30 полей на получателя.
nameExclusionsв телесписок строк. Кому писать без обращения по имени: ключи вида "username:имя" или "phone:+7…", не больше 20 000.
secondTouchв телеобъект. Второе касание: {enabled: да/нет (обязательно), days: 2, 3 или 5 (по умолчанию 3), audience: "read" — тем, кто прочитал, или "all" — всем (по умолчанию "read"), template: текст до 20 000 знаков, не больше 50 вариантов}. Текст должен отличаться от первого сообщения. С постом не сочетается.
contentв телеобъект. Что отправлять. Не передавать или {kind: "text"} — текст из template. {kind: "post", post: {…}, textPlace: "before" | "after" | null} — пост: post как в «Проверить пост», textPlace добавляет текст отдельным сообщением до или после поста. Пересылка поста из канала работает только для получателей с username.
baseLabelв телестрока. «База»: откуда эти получатели, до 60 знаков. Нужна, чтобы сравнивать базы в статистике.
autoReplyв телеобъект. Автоответ на первое сообщение лида: {enabled: да/нет (обязательно), kind: "text" или "post", text: текст до 4096 знаков, варианты {а|б} можно, поля [[…]] нельзя, post: пост как в «Проверить пост», textPlace: "before", "after" или null, pauseFrom и pauseTo: пауза перед ответом, 5–900 с (30 и 165)}.
curl -X POST "https://app.kelvintg.ru/api/v1/campaigns/rehearsal" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Проверка",
  "template": "Здравствуйте! Это проверка.",
  "recipientsText": "@synthetic_lead",
  "accountIds": [
    "ID_АККАУНТА"
  ]
}'

Ответ:

{
  "total": 1,
  "variants": 1,
  "samples": [
    {
      "recipient": "synthetic_lead",
      "text": "Здравствуйте! Это проверка."
    }
  ],
  "excluded": 0,
  "distribution": [
    {
      "accountId": "ID",
      "label": "Анна",
      "count": 1,
      "sampleRecipients": [
        {
          "kind": "username",
          "value": "synthetic_lead"
        }
      ],
      "estimate": {
        "days": 1,
        "remainingToday": 80,
        "perDay": 80
      }
    }
  ],
  "estimate": {
    "days": 1,
    "remainingToday": 80,
    "perDay": 80
  },
  "schedule": {
    "open": true,
    "nextEligibleAt": null,
ОтказЧто случилось и что сделать
400В списке есть строка, которая не похожа ни на username, ни на телефон, или получателей больше 20 000. Проверь список запросом «Проверить список получателей».
400Все получатели уже в списке «Не писать». Отправлять некому.
400В тексте есть поле [[…]], для которого у части получателей нет значения. Добавь запасной текст: [[город|вашем городе]].

Посчитать второе касание

POST /campaigns/ID_РАССЫЛКИ/second-touch/preview · право ключа: только чтение

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

ПолеГдеЧто это
expectedRevision — обязательнов телечисло. Номер версии revision из рассылки.
secondTouchв телеобъект. Второе касание: {enabled: да/нет (обязательно), days: 2, 3 или 5 (по умолчанию 3), audience: "read" — тем, кто прочитал, или "all" — всем (по умолчанию "read"), template: текст до 20 000 знаков, не больше 50 вариантов}. Текст должен отличаться от первого сообщения. С постом не сочетается. Без поля считаются сохранённые настройки.
accountIdsв телесписок строк. Если хочешь прикинуть с другим набором аккаунтов. Без поля берутся аккаунты рассылки.
scheduleв телеобъект. Когда можно отправлять: {timeZone: "Europe/Moscow" или "UTC", startLocal: "ГГГГ-ММ-ДДTЧЧ:ММ" (не раньше текущей минуты) или null, weekdays: дни недели 0–6, где 0 — воскресенье, intervals: от 1 до 4 отрезков {start: "ЧЧ:ММ", end: "ЧЧ:ММ"}}. Если конец раньше начала, отрезок переходит через полночь. Без поля берётся расписание рассылки.
curl -X POST "https://app.kelvintg.ru/api/v1/campaigns/ID_РАССЫЛКИ/second-touch/preview" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "expectedRevision": 1,
  "secondTouch": {
    "enabled": true,
    "days": 3,
    "audience": "read",
    "template": "Добрый день! Напоминаю о себе."
  }
}'

Ответ:

{
  "revision": 1,
  "upperBound": 1,
  "eligibleNow": 0,
  "scheduled": 0,
  "awaitingFirst": 1,
  "readCheckPending": 1,
  "notReadByChecks": {},
  "notReadFinal": 0,
  "nextEligibleAt": null,
  "sent": 0,
  "unknown": 0,
  "excludedByReason": {},
  "accounts": [
    {
      "accountId": "ID",
      "label": "Анна",
      "upperBound": 1,
      "eligibleNow": 0,
      "scheduled": 0,
      "sent": 0,
      "unknown": 0,
      "todayMax": 0,
      "later": 0,
      "nextEligibleAt": null,
      "excludedByReason": {}
    }
  ],
  "todayMax": 0
}
ОтказЧто случилось и что сделать
409Рассылку изменили после того, как ты её прочитал. Запроси её заново и повтори с новым revision.
400Текст второго касания совпадает с первым сообщением. Напиши другой.

Создать рассылку или одно сообщение

POST /campaigns · право ключа: чтение и действия · нужен номер запроса

Создаёт рассылку-черновик: список получателей, текст и аккаунты. Отправка не начинается, пока ты не вызовешь «Запустить рассылку». Отдельного запроса «написать одному человеку» нет: это рассылка с одним получателем, как в примере, и затем запуск.

ПолеГдеЧто это
name — обязательнов телестрока. Название рассылки, до 200 знаков.
template — обязательнов телестрока. Текст сообщения, до 20 000 знаков со всеми вариантами. Варианты пишутся как {Здравствуйте|Добрый день}, поля из таблицы как [[город|запасной текст]], обращение по имени как [[имя]]. Каждый итоговый вариант не длиннее 4096 знаков. Можно не передавать, только если отправляешь пост без текста.
recipientsText — обязательнов телестрока. Получатели, по одному в строке: username (5–32 знака, с @ или без, можно ссылкой t.me/имя) или телефон (8–15 цифр). Не больше 20 000. Повторы убираются сами, любая нераспознанная строка даёт отказ.
accountIds — обязательнов телесписок строк. С каких аккаунтов отправлять: хотя бы один аккаунт этого кабинета. Получатели раздаются по аккаунтам по кругу.
scheduleв телеобъект. Когда можно отправлять: {timeZone: "Europe/Moscow" или "UTC", startLocal: "ГГГГ-ММ-ДДTЧЧ:ММ" (не раньше текущей минуты) или null, weekdays: дни недели 0–6, где 0 — воскресенье, intervals: от 1 до 4 отрезков {start: "ЧЧ:ММ", end: "ЧЧ:ММ"}}. Если конец раньше начала, отрезок переходит через полночь. Без поля рассылка идёт без расписания.
pacingв телеобъект. Темп. Любое поле можно не передавать, тогда берётся значение из скобок: batchMin и batchMax — сообщений подряд, 1–20 (2 и 5); waitMinSeconds и waitMaxSeconds — пауза между сообщениями, 10–1800 с (55 и 85); readMinSeconds и readMaxSeconds — перерыв между сериями, 30–600 с (90 и 240); readInBreaks — читать каналы в перерывах (да); spreadEnabled — растягивать отправку по времени (да); spreadMinutes — 15–480 (60). Минимум не больше максимума.
recipientFieldsв телеобъект. Поля из таблицы по получателям: {"username:synthetic_lead": {"город": "Казань"}}. Ключ — "username:имя" или "phone:+7…". Имя поля — строчные буквы, цифры и «_», до 32 знаков; значение до 500 знаков; до 30 полей на получателя.
nameExclusionsв телесписок строк. Кому писать без обращения по имени: ключи вида "username:имя" или "phone:+7…", не больше 20 000.
secondTouchв телеобъект. Второе касание: {enabled: да/нет (обязательно), days: 2, 3 или 5 (по умолчанию 3), audience: "read" — тем, кто прочитал, или "all" — всем (по умолчанию "read"), template: текст до 20 000 знаков, не больше 50 вариантов}. Текст должен отличаться от первого сообщения. С постом не сочетается.
contentв телеобъект. Что отправлять. Не передавать или {kind: "text"} — текст из template. {kind: "post", post: {…}, textPlace: "before" | "after" | null} — пост: post как в «Проверить пост», textPlace добавляет текст отдельным сообщением до или после поста. Пересылка поста из канала работает только для получателей с username.
baseLabelв телестрока. «База»: откуда эти получатели, до 60 знаков. Нужна, чтобы сравнивать базы в статистике.
autoReplyв телеобъект. Автоответ на первое сообщение лида: {enabled: да/нет (обязательно), kind: "text" или "post", text: текст до 4096 знаков, варианты {а|б} можно, поля [[…]] нельзя, post: пост как в «Проверить пост», textPlace: "before", "after" или null, pauseFrom и pauseTo: пауза перед ответом, 5–900 с (30 и 165)}.
stopWarmupв теледа/нет. Передай true, если выбранный аккаунт сейчас на прогреве и ты согласен прогрев выключить. Без этого придёт отказ.
curl -X POST "https://app.kelvintg.ru/api/v1/campaigns" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Idempotency-Key: order-0001" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Проверка",
  "template": "Здравствуйте! Это проверка.",
  "recipientsText": "@synthetic_lead",
  "accountIds": [
    "ID_АККАУНТА"
  ]
}'

Ответ:

{
  "id": "ID",
  "name": "Проверка",
  "baseLabel": null,
  "status": "pending",
  "createdAt": "2026-10-10T10:00:00.000Z",
  "schedule": null,
  "scheduleStatus": null,
  "editable": true,
  "revision": 1,
  "startedAt": null,
  "secondTouch": {
    "enabled": false,
    "waiting": false,
    "planned": 0,
    "sent": 0
  },
  "post": null,
  "autoReply": {
    "enabled": false,
    "sent": 0,
    "waiting": 0,
    "refused": 0,
    "missed": 0
  },
  "total": 1
}
ОтказЧто случилось и что сделать
400В списке есть строка, которая не похожа ни на username, ни на телефон, или получателей больше 20 000. Проверь список запросом «Проверить список получателей».
400Все получатели уже в списке «Не писать». Отправлять некому.
400В тексте есть поле [[…]], для которого у части получателей нет значения. Добавь запасной текст: [[город|вашем городе]].
409Один из аккаунтов на прогреве. Останови прогрев или повтори запрос с stopWarmup: true (с новым номером запроса).

Изменить черновик рассылки

PATCH /campaigns/ID_РАССЫЛКИ · право ключа: чтение и действия

Заменяет черновик целиком: передавай все поля, как при создании, а не только изменённые. Работает, пока рассылку ни разу не запускали. После запуска меняются только настройки, для этого есть отдельный запрос.

ПолеГдеЧто это
expectedRevision — обязательнов телечисло. Номер версии revision, который ты видел в рассылке. Если рассылку успели изменить, придёт отказ, и ничего не затрётся.
name — обязательнов телестрока. Название рассылки, до 200 знаков.
template — обязательнов телестрока. Текст сообщения, до 20 000 знаков со всеми вариантами. Варианты пишутся как {Здравствуйте|Добрый день}, поля из таблицы как [[город|запасной текст]], обращение по имени как [[имя]]. Каждый итоговый вариант не длиннее 4096 знаков. Можно не передавать, только если отправляешь пост без текста.
recipientsText — обязательнов телестрока. Получатели, по одному в строке: username (5–32 знака, с @ или без, можно ссылкой t.me/имя) или телефон (8–15 цифр). Не больше 20 000. Повторы убираются сами, любая нераспознанная строка даёт отказ.
accountIds — обязательнов телесписок строк. С каких аккаунтов отправлять: хотя бы один аккаунт этого кабинета. Получатели раздаются по аккаунтам по кругу.
scheduleв телеобъект. Когда можно отправлять: {timeZone: "Europe/Moscow" или "UTC", startLocal: "ГГГГ-ММ-ДДTЧЧ:ММ" (не раньше текущей минуты) или null, weekdays: дни недели 0–6, где 0 — воскресенье, intervals: от 1 до 4 отрезков {start: "ЧЧ:ММ", end: "ЧЧ:ММ"}}. Если конец раньше начала, отрезок переходит через полночь. Если не передать, расписание снимется.
pacingв телеобъект. Темп. Любое поле можно не передавать, тогда берётся значение из скобок: batchMin и batchMax — сообщений подряд, 1–20 (2 и 5); waitMinSeconds и waitMaxSeconds — пауза между сообщениями, 10–1800 с (55 и 85); readMinSeconds и readMaxSeconds — перерыв между сериями, 30–600 с (90 и 240); readInBreaks — читать каналы в перерывах (да); spreadEnabled — растягивать отправку по времени (да); spreadMinutes — 15–480 (60). Минимум не больше максимума. Если не передать весь объект, темп вернётся к значениям из скобок.
recipientFieldsв телеобъект. Поля из таблицы по получателям: {"username:synthetic_lead": {"город": "Казань"}}. Ключ — "username:имя" или "phone:+7…". Имя поля — строчные буквы, цифры и «_», до 32 знаков; значение до 500 знаков; до 30 полей на получателя.
nameExclusionsв телесписок строк. Кому писать без обращения по имени: ключи вида "username:имя" или "phone:+7…", не больше 20 000. Если не передать, останется прежний список.
secondTouchв телеобъект. Второе касание: {enabled: да/нет (обязательно), days: 2, 3 или 5 (по умолчанию 3), audience: "read" — тем, кто прочитал, или "all" — всем (по умолчанию "read"), template: текст до 20 000 знаков, не больше 50 вариантов}. Текст должен отличаться от первого сообщения. С постом не сочетается. Если не передать, второе касание выключится.
contentв телеобъект. Что отправлять. Не передавать или {kind: "text"} — текст из template. {kind: "post", post: {…}, textPlace: "before" | "after" | null} — пост: post как в «Проверить пост», textPlace добавляет текст отдельным сообщением до или после поста. Пересылка поста из канала работает только для получателей с username.
baseLabelв телестрока. «База»: откуда эти получатели, до 60 знаков. Нужна, чтобы сравнивать базы в статистике. Если не передать, останется прежняя.
autoReplyв телеобъект. Автоответ на первое сообщение лида: {enabled: да/нет (обязательно), kind: "text" или "post", text: текст до 4096 знаков, варианты {а|б} можно, поля [[…]] нельзя, post: пост как в «Проверить пост», textPlace: "before", "after" или null, pauseFrom и pauseTo: пауза перед ответом, 5–900 с (30 и 165)}. Если не передать, автоответ выключится.
curl -X PATCH "https://app.kelvintg.ru/api/v1/campaigns/ID_РАССЫЛКИ" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "expectedRevision": 1,
  "name": "Проверка, вторая версия",
  "template": "Здравствуйте! Это проверка.",
  "recipientsText": "@synthetic_lead",
  "accountIds": [
    "ID_АККАУНТА"
  ]
}'

Ответ:

{
  "id": "ID",
  "name": "Проверка, вторая версия",
  "baseLabel": null,
  "status": "pending",
  "createdAt": "2026-10-10T10:00:00.000Z",
  "schedule": null,
  "scheduleStatus": null,
  "editable": true,
  "revision": 2,
  "startedAt": null,
  "secondTouch": {
    "enabled": false,
    "waiting": false,
    "planned": 0,
    "sent": 0
  },
  "post": null,
  "autoReply": {
    "enabled": false,
    "sent": 0,
    "waiting": 0,
    "refused": 0,
    "missed": 0
  },
  "total": 1
}
ОтказЧто случилось и что сделать
409Рассылку уже запускали: черновик изменить нельзя. Меняй настройки запросом «Изменить настройки рассылки».
409Рассылку изменили после того, как ты её прочитал. Запроси её заново и повтори с новым revision.
400В списке есть строка, которая не похожа ни на username, ни на телефон, или получателей больше 20 000. Проверь список запросом «Проверить список получателей».
400Все получатели уже в списке «Не писать». Отправлять некому.

Изменить настройки рассылки

PATCH /campaigns/ID_РАССЫЛКИ/settings · право ключа: чтение и действия

Меняет название, аккаунты, расписание, темп, второе касание и автоответ. Работает и для запущенной рассылки, текст и получателей не трогает. Расписание и темп передавай каждый раз: без них они сбросятся.

ПолеГдеЧто это
expectedRevision — обязательнов телечисло. Номер версии revision из рассылки. Если он устарел, придёт отказ.
name — обязательнов телестрока. Название рассылки, до 200 знаков.
accountIds — обязательнов телесписок строк. Аккаунты рассылки, хотя бы один. Добавленный аккаунт начнёт брать получателей из общей очереди, убранный перестанет.
scheduleв телеобъект. Когда можно отправлять: {timeZone: "Europe/Moscow" или "UTC", startLocal: "ГГГГ-ММ-ДДTЧЧ:ММ" (не раньше текущей минуты) или null, weekdays: дни недели 0–6, где 0 — воскресенье, intervals: от 1 до 4 отрезков {start: "ЧЧ:ММ", end: "ЧЧ:ММ"}}. Если конец раньше начала, отрезок переходит через полночь. Если не передать, расписание снимется.
pacingв телеобъект. Темп. Любое поле можно не передавать, тогда берётся значение из скобок: batchMin и batchMax — сообщений подряд, 1–20 (2 и 5); waitMinSeconds и waitMaxSeconds — пауза между сообщениями, 10–1800 с (55 и 85); readMinSeconds и readMaxSeconds — перерыв между сериями, 30–600 с (90 и 240); readInBreaks — читать каналы в перерывах (да); spreadEnabled — растягивать отправку по времени (да); spreadMinutes — 15–480 (60). Минимум не больше максимума. Если не передать весь объект, темп вернётся к значениям из скобок.
secondTouchв телеобъект. Второе касание: {enabled: да/нет (обязательно), days: 2, 3 или 5 (по умолчанию 3), audience: "read" — тем, кто прочитал, или "all" — всем (по умолчанию "read"), template: текст до 20 000 знаков, не больше 50 вариантов}. Текст должен отличаться от первого сообщения. С постом не сочетается. Если не передать, останется как было.
autoReplyв телеобъект. Автоответ на первое сообщение лида: {enabled: да/нет (обязательно), kind: "text" или "post", text: текст до 4096 знаков, варианты {а|б} можно, поля [[…]] нельзя, post: пост как в «Проверить пост», textPlace: "before", "after" или null, pauseFrom и pauseTo: пауза перед ответом, 5–900 с (30 и 165)}. Если не передать, останется как было.
baseLabelв телестрока. «База», до 60 знаков. Если не передать, останется прежняя.
stopWarmupв теледа/нет. Передай true, если добавляешь аккаунт, который сейчас на прогреве, и согласен прогрев выключить.
curl -X PATCH "https://app.kelvintg.ru/api/v1/campaigns/ID_РАССЫЛКИ/settings" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "expectedRevision": 1,
  "name": "Проверка",
  "accountIds": [
    "ID_АККАУНТА"
  ]
}'

Ответ:

{
  "id": "ID",
  "name": "Проверка",
  "baseLabel": null,
  "status": "pending",
  "createdAt": "2026-10-10T10:00:00.000Z",
  "schedule": null,
  "scheduleStatus": null,
  "editable": true,
  "revision": 2,
  "startedAt": null,
  "secondTouch": {
    "enabled": false,
    "waiting": false,
    "planned": 0,
    "sent": 0
  },
  "post": null,
  "autoReply": {
    "enabled": false,
    "sent": 0,
    "waiting": 0,
    "refused": 0,
    "missed": 0
  },
  "total": 1
}
ОтказЧто случилось и что сделать
409Рассылка отменена, изменить её нельзя.
409Рассылку изменили после того, как ты её прочитал. Запроси её заново и повтори с новым revision.
400Начало расписания в прошлом. Поставь время не раньше текущей минуты.

Запустить рассылку

POST /campaigns/ID_РАССЫЛКИ/start · право ключа: чтение и действия

Запускает созданную рассылку или продолжает её после паузы: телефоны начнут отправлять сообщения в своём обычном темпе и в пределах дневных лимитов аккаунтов. Если в кабинете нажато «Остановить всё», запрос примется, но отправка начнётся только после возобновления работы человеком.

ПолеГдеЧто это
stopWarmupв теледа/нет. Передай true, если аккаунт рассылки сейчас на прогреве и ты согласен прогрев выключить.
curl -X POST "https://app.kelvintg.ru/api/v1/campaigns/ID_РАССЫЛКИ/start" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Ответ:

{
  "id": "ID",
  "name": "Проверка",
  "baseLabel": null,
  "status": "running",
  "createdAt": "2026-10-10T10:00:00.000Z",
  "schedule": null,
  "scheduleStatus": null,
  "editable": false,
  "revision": 2,
  "startedAt": "2026-10-10T10:00:00.000Z",
  "secondTouch": {
    "enabled": false,
    "waiting": false,
    "planned": 0,
    "sent": 0
  },
  "post": null,
  "autoReply": {
    "enabled": false,
    "sent": 0,
    "waiting": 0,
    "refused": 0,
    "missed": 0
  },
  "total": 1
}
ОтказЧто случилось и что сделать
409Рассылка отменена. Запустить её снова нельзя, создай новую.
409Отправлять некому: в рассылке не осталось получателей в очереди.
409Один из аккаунтов на прогреве. Останови прогрев или повтори запрос с stopWarmup: true.

Поставить рассылку на паузу

POST /campaigns/ID_РАССЫЛКИ/pause · право ключа: чтение и действия

Останавливает выдачу новых сообщений телефонам. Сообщение, которое телефон уже начал отправлять, он закончит. Продолжить можно запросом «Запустить рассылку».

curl -X POST "https://app.kelvintg.ru/api/v1/campaigns/ID_РАССЫЛКИ/pause" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Ответ:

{
  "id": "ID",
  "name": "Проверка",
  "baseLabel": null,
  "status": "paused",
  "createdAt": "2026-10-10T10:00:00.000Z",
  "schedule": null,
  "scheduleStatus": null,
  "editable": false,
  "revision": 3,
  "startedAt": "2026-10-10T10:00:00.000Z",
  "secondTouch": {
    "enabled": false,
    "waiting": false,
    "planned": 0,
    "sent": 0
  },
  "post": null,
  "autoReply": {
    "enabled": false,
    "sent": 0,
    "waiting": 0,
    "refused": 0,
    "missed": 0
  },
  "total": 1
}
ОтказЧто случилось и что сделать
409Рассылка отменена, ставить её на паузу незачем.

Отменить рассылку

POST /campaigns/ID_РАССЫЛКИ/cancel · право ключа: чтение и действия

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

curl -X POST "https://app.kelvintg.ru/api/v1/campaigns/ID_РАССЫЛКИ/cancel" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Ответ:

{
  "id": "ID",
  "name": "Проверка",
  "baseLabel": null,
  "status": "cancelled",
  "createdAt": "2026-10-10T10:00:00.000Z",
  "schedule": null,
  "scheduleStatus": null,
  "editable": false,
  "revision": 2,
  "startedAt": null,
  "secondTouch": {
    "enabled": false,
    "waiting": false,
    "planned": 0,
    "sent": 0
  },
  "post": null,
  "autoReply": {
    "enabled": false,
    "sent": 0,
    "waiting": 0,
    "refused": 0,
    "missed": 0
  },
  "total": 1
}
ОтказЧто случилось и что сделать
409Рассылка уже отменена.

Убрать в корзину или вернуть

POST /campaigns/trash · право ключа: чтение и действия

Переносит рассылки в корзину или возвращает из неё. Идущая рассылка при этом встаёт на паузу. Уже отправленные сообщения остаются в статистике.

ПолеГдеЧто это
ids — обязательнов телесписок строк. Какие рассылки: от 1 до 500 номеров.
action — обязательнов телестрока. "delete" — в корзину, "restore" — вернуть из корзины.
curl -X POST "https://app.kelvintg.ru/api/v1/campaigns/trash" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "ids": [
    "ID_РАССЫЛКИ"
  ],
  "action": "delete"
}'

Ответ:

{
  "ok": true,
  "ids": [
    "ID"
  ]
}
ОтказЧто случилось и что сделать
409Телефон прямо сейчас отправляет сообщение этой рассылки. Поставь её на паузу, дождись конца отправки и повтори. Ни одна рассылка из запроса не изменилась.

Наборы настроек

Список наборов

GET /campaign-playbooks · право ключа: только чтение

Отдаёт сохранённые наборы настроек рассылки: текст, темп, расписание и аккаунты. Получателей в наборах нет.

curl "https://app.kelvintg.ru/api/v1/campaign-playbooks" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "items": []
}

Один набор

GET /campaign-playbooks/ID_НАБОРА · право ключа: только чтение

Отдаёт набор целиком вместе с номером версии version. Этот номер пригодится, чтобы изменить набор и не затереть чужую правку.

curl "https://app.kelvintg.ru/api/v1/campaign-playbooks/ID_НАБОРА" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "id": "ID",
  "name": "Первое касание",
  "version": 1,
  "template": "{Здравствуйте|Добрый день}! Это проверка.",
  "accountIds": [
    "ID"
  ],
  "pacing": {
    "batchMin": 2,
    "batchMax": 5,
    "waitMinSeconds": 55,
    "waitMaxSeconds": 85,
    "readMinSeconds": 90,
    "readMaxSeconds": 240,
    "readInBreaks": true,
    "spreadEnabled": true,
    "spreadMinutes": 60
  },
  "schedule": null,
  "recipientFieldSchema": [],
  "requiredChecks": [
    "Проверить список получателей"
  ],
  "createdAt": "2026-10-10T10:00:00.000Z",
  "updatedAt": "2026-10-10T10:00:00.000Z"
}

Что даст набор

GET /campaign-playbooks/ID_НАБОРА/apply-preview · право ключа: только чтение

Показывает, что подставится в новую рассылку из набора, и каких аккаунтов из него уже нет в кабинете. Рассылку не создаёт: для этого возьми данные набора и вызови «Создать рассылку».

curl "https://app.kelvintg.ru/api/v1/campaign-playbooks/ID_НАБОРА/apply-preview" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "id": "ID",
  "name": "Первое касание",
  "version": 1,
  "template": "{Здравствуйте|Добрый день}! Это проверка.",
  "accountIds": [
    "ID"
  ],
  "pacing": {
    "batchMin": 2,
    "batchMax": 5,
    "waitMinSeconds": 55,
    "waitMaxSeconds": 85,
    "readMinSeconds": 90,
    "readMaxSeconds": 240,
    "readInBreaks": true,
    "spreadEnabled": true,
    "spreadMinutes": 60
  },
  "schedule": null,
  "recipientFieldSchema": [],
  "requiredChecks": [
    "Проверить список получателей"
  ],
  "createdAt": "2026-10-10T10:00:00.000Z",
  "updatedAt": "2026-10-10T10:00:00.000Z",
  "accounts": [
    {
      "id": "ID",
      "label": "Анна",
      "aiStatus": "off"
    }
  ],
  "missingAccountIds": []
}

Сохранить набор

POST /campaign-playbooks · право ключа: чтение и действия · нужен номер запроса

Сохраняет настройки рассылки, чтобы не вводить их каждый раз. Получатели в набор не входят.

ПолеГдеЧто это
name — обязательнов телестрока. Название набора, до 200 знаков.
template — обязательнов телестрока. Текст сообщения с вариантами {а|б} и полями [[поле|запасной текст]]. Проверяется так же, как в рассылке.
accountIds — обязательнов телесписок строк. Аккаунты, с которых обычно идёт такая рассылка: хотя бы один аккаунт кабинета.
scheduleв телеобъект. Когда можно отправлять: {timeZone: "Europe/Moscow" или "UTC", startLocal: "ГГГГ-ММ-ДДTЧЧ:ММ" (не раньше текущей минуты) или null, weekdays: дни недели 0–6, где 0 — воскресенье, intervals: от 1 до 4 отрезков {start: "ЧЧ:ММ", end: "ЧЧ:ММ"}}. Если конец раньше начала, отрезок переходит через полночь.
pacingв телеобъект. Темп. Любое поле можно не передавать, тогда берётся значение из скобок: batchMin и batchMax — сообщений подряд, 1–20 (2 и 5); waitMinSeconds и waitMaxSeconds — пауза между сообщениями, 10–1800 с (55 и 85); readMinSeconds и readMaxSeconds — перерыв между сериями, 30–600 с (90 и 240); readInBreaks — читать каналы в перерывах (да); spreadEnabled — растягивать отправку по времени (да); spreadMinutes — 15–480 (60). Минимум не больше максимума.
requiredChecksв телесписок строк. Что проверить перед запуском: до 20 пунктов, каждый до 200 знаков.
curl -X POST "https://app.kelvintg.ru/api/v1/campaign-playbooks" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Idempotency-Key: order-0001" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Первое касание",
  "template": "{Здравствуйте|Добрый день}! Это проверка.",
  "accountIds": [
    "ID_АККАУНТА"
  ],
  "requiredChecks": [
    "Проверить список получателей"
  ]
}'

Ответ:

{
  "id": "ID",
  "name": "Первое касание",
  "version": 1,
  "template": "{Здравствуйте|Добрый день}! Это проверка.",
  "accountIds": [
    "ID"
  ],
  "pacing": {
    "batchMin": 2,
    "batchMax": 5,
    "waitMinSeconds": 55,
    "waitMaxSeconds": 85,
    "readMinSeconds": 90,
    "readMaxSeconds": 240,
    "readInBreaks": true,
    "spreadEnabled": true,
    "spreadMinutes": 60
  },
  "schedule": null,
  "recipientFieldSchema": [],
  "requiredChecks": [
    "Проверить список получателей"
  ],
  "createdAt": "2026-10-10T10:00:00.000Z",
  "updatedAt": "2026-10-10T10:00:00.000Z"
}

Изменить набор

PUT /campaign-playbooks/ID_НАБОРА · право ключа: чтение и действия

Заменяет набор целиком: передавай все поля, а не только изменённые. Расписание, темп и список проверок без своих полей сбросятся. Уже созданные рассылки не меняются.

ПолеГдеЧто это
name — обязательнов телестрока. Название набора, до 200 знаков.
template — обязательнов телестрока. Текст сообщения с вариантами {а|б} и полями [[поле|запасной текст]]. Проверяется так же, как в рассылке.
accountIds — обязательнов телесписок строк. Аккаунты, с которых обычно идёт такая рассылка: хотя бы один аккаунт кабинета.
scheduleв телеобъект. Когда можно отправлять: {timeZone: "Europe/Moscow" или "UTC", startLocal: "ГГГГ-ММ-ДДTЧЧ:ММ" (не раньше текущей минуты) или null, weekdays: дни недели 0–6, где 0 — воскресенье, intervals: от 1 до 4 отрезков {start: "ЧЧ:ММ", end: "ЧЧ:ММ"}}. Если конец раньше начала, отрезок переходит через полночь.
pacingв телеобъект. Темп. Любое поле можно не передавать, тогда берётся значение из скобок: batchMin и batchMax — сообщений подряд, 1–20 (2 и 5); waitMinSeconds и waitMaxSeconds — пауза между сообщениями, 10–1800 с (55 и 85); readMinSeconds и readMaxSeconds — перерыв между сериями, 30–600 с (90 и 240); readInBreaks — читать каналы в перерывах (да); spreadEnabled — растягивать отправку по времени (да); spreadMinutes — 15–480 (60). Минимум не больше максимума.
requiredChecksв телесписок строк. Что проверить перед запуском: до 20 пунктов, каждый до 200 знаков.
versionв телечисло. Номер версии, который ты видел. Если набор успели изменить, придёт отказ. Без поля набор перезапишется без проверки.
curl -X PUT "https://app.kelvintg.ru/api/v1/campaign-playbooks/ID_НАБОРА" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Первое касание, вечер",
  "template": "{Здравствуйте|Добрый день}! Это проверка.",
  "accountIds": [
    "ID_АККАУНТА"
  ],
  "requiredChecks": [
    "Проверить список получателей"
  ]
}'

Ответ:

{
  "id": "ID",
  "name": "Первое касание, вечер",
  "version": 2,
  "template": "{Здравствуйте|Добрый день}! Это проверка.",
  "accountIds": [
    "ID"
  ],
  "pacing": {
    "batchMin": 2,
    "batchMax": 5,
    "waitMinSeconds": 55,
    "waitMaxSeconds": 85,
    "readMinSeconds": 90,
    "readMaxSeconds": 240,
    "readInBreaks": true,
    "spreadEnabled": true,
    "spreadMinutes": 60
  },
  "schedule": null,
  "recipientFieldSchema": [],
  "requiredChecks": [
    "Проверить список получателей"
  ],
  "createdAt": "2026-10-10T10:00:00.000Z",
  "updatedAt": "2026-10-10T10:00:00.000Z"
}
ОтказЧто случилось и что сделать
409Набор изменили после того, как ты его прочитал. Запроси его заново и повтори с новым version.

Удалить набор

DELETE /campaign-playbooks/ID_НАБОРА · право ключа: чтение и действия

Удаляет набор насовсем, вернуть его нельзя. Рассылки, созданные по этому набору, остаются как были.

curl -X DELETE "https://app.kelvintg.ru/api/v1/campaign-playbooks/ID_НАБОРА" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "ok": true,
  "id": "ID"
}

«Не писать»

Список «Не писать»

GET /blocklist · право ключа: только чтение

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

curl "https://app.kelvintg.ru/api/v1/blocklist" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "entries": []
}

Добавить в «Не писать»

POST /blocklist · право ключа: чтение и действия

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

ПолеГдеЧто это
text — обязательнов телестрока. Кого добавить, по одному в строке: username или телефон, как в списке получателей.
reasonв телестрока. Причина, до 200 знаков. Видна только в кабинете.
curl -X POST "https://app.kelvintg.ru/api/v1/blocklist" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "@synthetic_blocked",
  "reason": "Попросил не писать"
}'

Ответ:

{
  "added": [
    {
      "line": 1,
      "kind": "username",
      "value": "synthetic_blocked"
    }
  ],
  "duplicates": [],
  "invalid": [],
  "cancelledTasks": 0
}

Убрать из «Не писать»

DELETE /blocklist · право ключа: чтение и действия

Убирает одного человека из списка. Сообщения, отменённые из-за списка раньше, не возвращаются: человек попадёт только в новые рассылки. Поля передаются в теле запроса.

ПолеГдеЧто это
kind — обязательнов телестрока. "username" или "phone". Должен совпасть с тем, что написано в value.
value — обязательнов телестрока. Один получатель: @имя, ссылка t.me/имя или телефон.
curl -X DELETE "https://app.kelvintg.ru/api/v1/blocklist" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "kind": "username",
  "value": "@synthetic_blocked"
}'

Ответ:

{
  "ok": true,
  "removed": true
}

Чаты и ответы лидам

Что телефоны прочитали в Telegram

GET /dialog-list · право ключа: только чтение

Отдаёт сводку по каждому аккаунту: сколько чатов телефон увидел в папке «Лиды», сколько из них ответили, прочитали и не прочитали. В строках есть названия чатов и начало сообщений, если они не нужны, запроси без строк.

ПолеГдеЧто это
rowsв адресестрока. "0" — только сводка, без строк по чатам. Без параметра строки приходят, до 3000 на аккаунт.
curl "https://app.kelvintg.ru/api/v1/dialog-list?rows=0" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "accounts": [],
  "firstRead": []
}

Новые сообщения лидов

GET /chats/incoming · право ключа: только чтение

Отдаёт входящие сообщения лидов по всему кабинету по порядку. Запомни after из ответа и передай его в следующий раз: так ты получишь только новое, без пропусков и повторов. Наших сообщений здесь нет.

ПолеГдеЧто это
afterв адресечисло. Значение after из прошлого ответа. По умолчанию 0, то есть с самого начала.
limitв адресечисло. Сколько сообщений отдать, 1–500. По умолчанию 100.
curl "https://app.kelvintg.ru/api/v1/chats/incoming?after=0&limit=100" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "messages": [
    {
      "id": 1,
      "chatId": "chat-1",
      "accountId": "ID",
      "name": "Мария",
      "username": "synthetic_lead",
      "text": "Здравствуйте!",
      "at": "2026-10-10T09:30:00.000Z",
      "receivedAt": "2026-10-10T09:30:00.000Z",
      "voice": false
    },
    "… ещё 1"
  ],
  "after": 2,
  "more": false
}

Список чатов

GET /chats · право ключа: только чтение

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

ПолеГдеЧто это
offsetв адресечисло. Сколько чатов пропустить, 0–1 000 000. По умолчанию 0.
limitв адресечисло. Сколько чатов отдать, 50–500. По умолчанию 50, меньше 50 не бывает.
accountв адресестрока. Чаты одного аккаунта: его номер из списка аккаунтов. "none" — чаты, у которых аккаунт ещё не определён.
unreadв адресестрока. "1" — только чаты, которые ждут ответа. Сильнее, чем filter.
filterв адресестрока. "replied" — лид написал хотя бы раз, "waiting" — последнее сообщение наше.
qв адресестрока. Поиск по имени или username, первые 100 знаков.
curl "https://app.kelvintg.ru/api/v1/chats?limit=50&unread=1" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "chats": [
    {
      "id": "chat-1",
      "accountId": "ID",
      "name": "Мария",
      "username": "synthetic_lead",
      "needsReply": true,
      "updatedAt": "2026-10-10T09:30:00.000Z",
      "lastText": "Сколько стоит консультация?",
      "lastFrom": "lead",
      "unread": 2,
      "queuedReply": null,
      "snoozedUntil": null
    }
  ],
  "total": 1
}

Переписка в чате

GET /chats/ID_ДИАЛОГА · право ключа: только чтение

Отдаёт чат, до 100 последних сообщений и ответы, которые ты поставил в очередь, с их состоянием. В переписке то, что телефон успел передать в кабинет: до ответа лиду телефон в чат не заходит.

ПолеГдеЧто это
beforeв адресечисло. Номер самого старого сообщения, которое у тебя уже есть: придут до 100 сообщений старше него.
curl "https://app.kelvintg.ru/api/v1/chats/ID_ДИАЛОГА" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "chat": {
    "id": "chat-1",
    "accountId": "ID",
    "name": "Мария",
    "username": "synthetic_lead",
    "needsReply": true,
    "updatedAt": "2026-10-10T09:30:00.000Z"
  },
  "read": {
    "readAt": null,
    "state": "never",
    "phone": "offline",
    "blocked": null,
    "failure": null
  },
  "messages": [
    {
      "id": 1,
      "from": "lead",
      "text": "Здравствуйте!",
      "at": "2026-10-10T09:30:00.000Z",
      "receivedAt": "2026-10-10T09:30:00.000Z",
      "voice": null,
      "readByLead": false
    },
    "… ещё 1"
  ],
  "replies": [
    {
      "id": "ID",
      "text": "Здравствуйте! Это проверка.",
      "state": "pending",
      "reason": null,
      "createdAt": "2026-10-10T10:00:00.000Z",
      "updatedAt": "2026-10-10T10:00:00.000Z"

Узнать судьбу ответа

GET /chats/reply-requests/НОМЕР_ЗАПРОСА · право ключа: только чтение

Показывает, что стало с ответом, по номеру из «Idempotency-Key», с которым ты его отправлял. Нужен, когда ответ сервера потерялся: сначала спроси здесь, а не отправляй заново. Состояние unknown значит «вероятно отправлено», повторять такой ответ не надо.

curl "https://app.kelvintg.ru/api/v1/chats/reply-requests/НОМЕР_ЗАПРОСА" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "taskId": "ID",
  "state": "pending"
}
ОтказЧто случилось и что сделать
404Ответа с таким номером запроса нет: он не был принят. Можно отправить снова.

Ответить лиду

POST /chats/ID_ДИАЛОГА/reply · право ключа: чтение и действия · нужен номер запроса

Ставит ответ в очередь телефона: он отправит его с того аккаунта, который ведёт чат, когда освободится. Если этот чат вёл ИИ, дальше его ведёшь ты. Повтор запроса с тем же номером в заголовке «Idempotency-Key» второе сообщение не создаст.

ПолеГдеЧто это
text — обязательнов телестрока. Текст ответа, от 1 до 4096 знаков.
curl -X POST "https://app.kelvintg.ru/api/v1/chats/ID_ДИАЛОГА/reply" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Idempotency-Key: order-0001" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Здравствуйте! Это проверка."
}'

Ответ:

{
  "ok": true,
  "taskId": "ID",
  "taskState": "pending"
}
ОтказЧто случилось и что сделать
409Собеседник в списке «Не писать». Убери его оттуда, если хочешь ответить.
409Лид ещё не написал, а его username неизвестен. Ответить получится после его первого сообщения.
409Аккаунт этого чата удалён или не определён. Ответить с него нельзя.
410Ответы из кабинета сейчас выключены. Напиши в поддержку KELVIN TG.

Отменить ответ

POST /chats/replies/ID_ОТВЕТА/cancel · право ключа: чтение и действия

Отменяет ответ, пока он ждёт в очереди. Номер ответа — taskId из ответа на «Ответить лиду». Если телефон уже взял ответ в работу, отменить его нельзя.

curl -X POST "https://app.kelvintg.ru/api/v1/chats/replies/ID_ОТВЕТА/cancel" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Ответ:

{
  "ok": true
}
ОтказЧто случилось и что сделать
409Телефон уже отправляет этот ответ или закончил. Отменить его нельзя.

Сверить чат с телефоном

POST /chats/ID_ДИАЛОГА/read · право ключа: чтение и действия

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

ПолеГдеЧто это
reasonв телестрока. "opened" — мягкая сверка, не чаще раза в 2 минуты на чат. Без поля сверка запрашивается всегда.
curl -X POST "https://app.kelvintg.ru/api/v1/chats/ID_ДИАЛОГА/read" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Ответ:

{
  "readAt": null,
  "state": "queued",
  "phone": "offline",
  "blocked": null
}
ОтказЧто случилось и что сделать
409Сверить можно после первого ответа лида: пока KELVIN не знает, как найти этот чат на телефоне.
409Телефон этого чата отключён от кабинета или на нём старая версия KELVIN TG. Подключи или обнови телефон.

Отметить прочитанным в кабинете

POST /chats/ID_ДИАЛОГА/seen · право ключа: чтение и действия

Убирает счётчик непрочитанных у чата в кабинете. В Telegram ничего не меняется: лид не увидит, что сообщение прочитано.

ПолеГдеЧто это
upTo — обязательнов телестрока. Дата и время самого свежего сообщения лида, которое ты показал, в виде 2026-10-10T09:00:00.000Z. Всё, что раньше, считается прочитанным.
curl -X POST "https://app.kelvintg.ru/api/v1/chats/ID_ДИАЛОГА/seen" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "upTo": "2026-10-10T09:00:00.000Z"
}'

Ответ:

{
  "ok": true
}

Снять «ждёт ответа»

POST /chats/ID_ДИАЛОГА/review · право ключа: чтение и действия

Отмечает, что отвечать на этот чат не нужно, и убирает его из тех, что ждут ответа. Новое сообщение лида вернёт отметку.

curl -X POST "https://app.kelvintg.ru/api/v1/chats/ID_ДИАЛОГА/review" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Ответ:

{
  "ok": true
}

Обновить список чатов

POST /dialog-list/refresh · право ключа: чтение и действия

Просит телефоны заново прочитать список чатов в Telegram. Это задание в очереди: телефон выполнит его, когда будет свободен и на связи. В ответе queued — сколько заданий поставлено.

ПолеГдеЧто это
accountIdв телестрока. Обновить только этот аккаунт. Без поля — все включённые аккаунты кабинета.
rowsв телечисло. Сколько строк списка прочитать, 1–3000. Без поля KELVIN считает сам по числу отправок аккаунта.
curl -X POST "https://app.kelvintg.ru/api/v1/dialog-list/refresh" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "accountId": "ID_АККАУНТА"
}'

Ответ:

{
  "queued": 1,
  "accounts": 1,
  "stopped": true
}

ИИ-ответы

Чаты, которые ждут человека

GET /ai/queue · право ключа: чтение и действия

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

curl "https://app.kelvintg.ru/api/v1/ai/queue" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "items": []
}

Статистика ИИ-ответов

GET /ai/stats · право ключа: только чтение

Отдаёт, сколько ответов ИИ подготовил за период и сколько из них приняты. Пустое значение вместо числа значит «данных пока нет», а не ноль. Текстов переписки в ответе нет.

ПолеГдеЧто это
daysв адресечисло. За сколько дней считать, 1–30. По умолчанию 7.
curl "https://app.kelvintg.ru/api/v1/ai/stats?days=7" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "days": 7,
  "provider": "none",
  "external": true,
  "dailyRequests": 0,
  "today": {
    "requests": 0,
    "errors": 0,
    "reserved": 0
  },
  "drafts": 0,
  "proposed": null,
  "accepted": null,
  "confirmed": null,
  "handoff": null,
  "rejected": null,
  "stale": null,
  "unknown": null,
  "byReason": []
}

Аккаунты и телефоны

Список аккаунтов

GET /accounts · право ключа: только чтение

Отдаёт все аккаунты Telegram в кабинете: лимиты, сколько отправлено сегодня, идёт ли прогрев и почему аккаунт сейчас не пишет, если не пишет. Удалённые аккаунты тоже в списке, у них заполнено deletedAt. Счётчики идут с начала суток по Москве.

curl "https://app.kelvintg.ru/api/v1/accounts" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "accounts": [
    {
      "id": "ID",
      "deviceId": "ID",
      "label": "Анна",
      "phone": "+12025550401",
      "enabled": true,
      "deletedAt": null,
      "messageLimit": 80,
      "effectiveMessageLimit": 80,
      "phoneSearchLimit": 15,
      "taskDelayMin": 6,
      "taskDelayMax": 6,
      "accountDelayMin": 0,
      "accountDelayMax": 0,
      "searchDelayMin": 0
    }
  ]
}

Список телефонов

GET /devices · право ключа: только чтение

Отдаёт телефоны кабинета: на связи ли телефон, чем занят, версии Android, Telegram и KELVIN TG. Телефон считается не на связи, если молчит дольше 45 секунд. Отключённые телефоны в списке есть, удалённых нет.

curl "https://app.kelvintg.ru/api/v1/devices" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "devices": [
    {
      "id": "ID",
      "name": "Телефон 1",
      "lastSeen": null,
      "status": "offline",
      "androidVersion": null,
      "telegramVersion": null,
      "accessibilityEnabled": false,
      "keyboardEnabled": null,
      "power": null,
      "screenUnlocked": false,
      "networkConnected": false,
      "liveEnabled": false,
      "manualScreen": false,
      "networkGroup": null
    }
  ]
}

Лента аккаунта

GET /accounts/ID_АККАУНТА/timeline · право ключа: только чтение

Отдаёт всё, что происходило с аккаунтом за последние дни, свежее сверху: отправки, прогрев, проверки, остановки. В ленте только статусы, без текстов и номеров. Не больше 3000 записей.

ПолеГдеЧто это
daysв адресечисло. За сколько дней, 1–30. По умолчанию 7.
curl "https://app.kelvintg.ru/api/v1/accounts/ID_АККАУНТА/timeline?days=7" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "accountId": "ID",
  "days": 7,
  "since": "2026-10-03T10:00:00.000Z",
  "truncated": false,
  "items": [
    {
      "at": "2026-10-10T10:00:00.000Z",
      "type": "account",
      "event": "account_created",
      "changes": null
    }
  ]
}

Добавить аккаунт

POST /accounts · право ключа: чтение и действия · нужен номер запроса

Добавляет аккаунт Telegram к телефону в кабинете. Войти в этот аккаунт в Telegram на телефоне нужно руками, запрос этого не делает. На одном телефоне в одном приложении Telegram не больше трёх аккаунтов.

ПолеГдеЧто это
deviceId — обязательнов телестрока. Телефон из списка телефонов.
label — обязательнов телестрока. Название аккаунта в кабинете, 1–100 знаков.
phone — обязательнов телестрока. Номер аккаунта: 8–15 цифр, можно с «+», пробелами, скобками и дефисами.
enabledв теледа/нет. Включён ли аккаунт. По умолчанию да.
clientIdв телестрока. Какое приложение Telegram на телефоне: основное или второе. Без поля — основное.
replaceSettingsв теледа/нет. Только если возвращаешь удалённый аккаунт с тем же номером: true — взять лимиты из этого запроса, иначе вернутся прежние.
messageLimitв телечисло. Сколько сообщений в сутки отправляет аккаунт, 0–100. По умолчанию 80.
phoneSearchLimitв телечисло. Сколько получателей в сутки искать по номеру телефона, 0–15. По умолчанию 15.
taskDelayMinв телечисло. Пауза между сообщениями, от: 0–86 400 с. По умолчанию 6. Не больше taskDelayMax.
taskDelayMaxв телечисло. Пауза между сообщениями, до: 0–86 400 с. По умолчанию 6.
accountDelayMinв телечисло. Пауза при смене аккаунта, от: 0–86 400 с. По умолчанию 0. Не больше accountDelayMax.
accountDelayMaxв телечисло. Пауза при смене аккаунта, до: 0–86 400 с. По умолчанию 0.
searchDelayMinв телечисло. Пауза перед поиском по номеру, от: 0–86 400 с. По умолчанию 0. Не больше searchDelayMax.
searchDelayMaxв телечисло. Пауза перед поиском по номеру, до: 0–86 400 с. По умолчанию 0.
curl -X POST "https://app.kelvintg.ru/api/v1/accounts" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Idempotency-Key: order-0001" \
  -H "Content-Type: application/json" \
  -d '{
  "deviceId": "ID_ТЕЛЕФОНА",
  "label": "Аккаунт 2",
  "phone": "+70000000002"
}'

Ответ:

{
  "id": "ID",
  "deviceId": "ID",
  "label": "Аккаунт 2",
  "phone": "+70000000002",
  "enabled": true,
  "deletedAt": null,
  "messageLimit": 80,
  "effectiveMessageLimit": 80,
  "phoneSearchLimit": 15,
  "taskDelayMin": 6,
  "taskDelayMax": 6,
  "accountDelayMin": 0,
  "accountDelayMax": 0,
  "searchDelayMin": 0
}
ОтказЧто случилось и что сделать
409Этот номер уже есть в кабинете или занят в другом. Свой аккаунт найди в списке, про чужой напиши в поддержку.
409На этом телефоне уже три аккаунта в этом приложении Telegram. Добавь аккаунт на другой телефон.
409Телефон отключён от кабинета. Подключи его заново и повтори.

Изменить аккаунт

PATCH /accounts/ID_АККАУНТА · право ключа: чтение и действия

Меняет название, лимиты и паузы аккаунта или выключает его. Передавай только то, что меняешь. Новые лимиты действуют сразу для сообщений, которые телефон ещё не взял.

ПолеГдеЧто это
labelв телестрока. Название аккаунта, 1–100 знаков.
enabledв теледа/нет. false выключает аккаунт: он перестаёт отправлять.
messageLimitв телечисло. Сколько сообщений в сутки отправляет аккаунт, 0–100.
phoneSearchLimitв телечисло. Сколько получателей в сутки искать по номеру телефона, 0–15.
taskDelayMinв телечисло. Пауза между сообщениями, от: 0–86 400 с. Не больше taskDelayMax.
taskDelayMaxв телечисло. Пауза между сообщениями, до: 0–86 400 с.
accountDelayMinв телечисло. Пауза при смене аккаунта, от: 0–86 400 с. Не больше accountDelayMax.
accountDelayMaxв телечисло. Пауза при смене аккаунта, до: 0–86 400 с.
searchDelayMinв телечисло. Пауза перед поиском по номеру, от: 0–86 400 с. Не больше searchDelayMax.
searchDelayMaxв телечисло. Пауза перед поиском по номеру, до: 0–86 400 с.
curl -X PATCH "https://app.kelvintg.ru/api/v1/accounts/ID_АККАУНТА" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "messageLimit": 40
}'

Ответ:

{
  "id": "ID",
  "deviceId": "ID",
  "label": "Анна",
  "phone": "+12025550401",
  "enabled": true,
  "deletedAt": null,
  "messageLimit": 40,
  "effectiveMessageLimit": 40,
  "phoneSearchLimit": 15,
  "taskDelayMin": 6,
  "taskDelayMax": 6,
  "accountDelayMin": 0,
  "accountDelayMax": 0,
  "searchDelayMin": 0
}
ОтказЧто случилось и что сделать
409Номер аккаунта изменить нельзя. Добавь новый аккаунт с другим номером.

Прогрев и группы

Прогрев: план и факт на сегодня

GET /warmup/today · право ключа: только чтение

Отдаёт по каждому телефону и аккаунту, сколько заходов в Telegram запланировано на сегодня и сколько уже сделано. Сутки считаются по Москве.

curl "https://app.kelvintg.ru/api/v1/warmup/today" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "generatedAt": "2026-10-10T10:00:00.000Z",
  "day": {
    "from": "2026-10-09T21:00:00.000Z",
    "to": "2026-10-10T21:00:00.000Z",
    "timeZone": "Europe/Moscow"
  },
  "activePrograms": 0,
  "workStopped": false,
  "phones": [],
  "summary": {
    "planned": 0,
    "completed": 0,
    "running": 0,
    "waiting": 0,
    "interrupted": 0,
    "needsReview": 0,
    "skipped": 0,
    "skippedReasons": [],
    "manualInterventions": {
      "decisions": 0,
      "workStops": 0,
      "workResumes": 0,
      "warmupStops": 0,
      "warmupResumes": 0,
      "total": 0
    },
    "screenConflicts": null
  }
}

Прогрев: каналы и подписки

GET /warmup/sources · право ключа: только чтение

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

curl "https://app.kelvintg.ru/api/v1/warmup/sources" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "generatedAt": "2026-10-10T10:00:00.000Z",
  "day": {
    "from": "2026-10-09T21:00:00.000Z",
    "to": "2026-10-10T21:00:00.000Z",
    "timeZone": "Europe/Moscow"
  },
  "catalog": {
    "approved": 621,
    "pending": 0,
    "rejected": 0,
    "approvedByCategory": {
      "large": 140,
      "lending": 14,
      "microblog": 223,
      "hobby": 127,
      "daily_life": 117
    }
  },
  "joinsToday": {
    "joined": 0,
    "alreadyMember": 0,
    "unavailable": 0,
    "unknown": 0,
    "inProgress": 0,
    "total": 0
  },
  "unknownToday": [],
  "noJoinProfiles": [
    "read_only",
    "… ещё 1"
  ]
}

Список программ прогрева

GET /warmup-runs · право ключа: только чтение

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

curl "https://app.kelvintg.ru/api/v1/warmup-runs" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "runs": []
}

Журнал программы прогрева

GET /warmup-runs/ID_ПРОГРЕВА/journal · право ключа: только чтение

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

ПолеГдеЧто это
accountIdв адресестрока. Только записи этого аккаунта программы.
limitв адресечисло. Сколько записей отдать, 1–500. По умолчанию 100.
curl "https://app.kelvintg.ru/api/v1/warmup-runs/ID_ПРОГРЕВА/journal?limit=50" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "items": []
}

Журнал активности в группах

GET /group-activity/journal · право ключа: только чтение

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

ПолеГдеЧто это
accountId — обязательнов адресестрока. Аккаунт, чей журнал нужен.
limitв адресечисло. Сколько записей отдать, 1–500. По умолчанию 100.
curl "https://app.kelvintg.ru/api/v1/group-activity/journal?accountId=$account&limit=50" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "reports": []
}

Включить или выключить прогрев аккаунта

POST /accounts/ID_АККАУНТА/warmup · право ключа: чтение и действия

Включает или выключает простой прогрев одного аккаунта. Пока прогрев идёт, рассылка с этого аккаунта запустится только с подтверждением stopWarmup.

ПолеГдеЧто это
action — обязательнов телестрока. "start" — включить, "stop" — выключить.
curl -X POST "https://app.kelvintg.ru/api/v1/accounts/ID_АККАУНТА/warmup" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "action": "stop"
}'

Ответ:

{
  "id": "ID",
  "deviceId": "ID",
  "client": {
    "clientId": "tc_54b5fd93b04f5adf10d891521ec615bb",
    "deviceId": "ID",
    "kind": "default",
    "userSerial": null,
    "package": null,
    "profileEpoch": null,
    "installEpoch": null,
    "bindingId": "tb_2285326d919db59459a0b11ebb35de15",
    "bindingRevision": 1,
    "legacyUnknown": true
  },
  "label": "Анна",
  "phone": "+12025550401",
  "enabled": true,
  "deletedAt": null,
  "messageLimit": 40,
  "effectiveMessageLimit": 40,
  "phoneSearchLimit": 15,
  "taskDelayMin": 6,
  "taskDelayMax": 6,
  "accountDelayMin": 0,
  "accountDelayMax": 0
}
ОтказЧто случилось и что сделать
409Аккаунт прямо сейчас отправляет сообщение. Включи прогрев, когда отправка закончится.

Настроить активность в группах

PATCH /accounts/ID_АККАУНТА/group-activity · право ключа: чтение и действия

Настраивает, как аккаунт ведёт себя в группах и каналах: заходит, листает, ставит реакции, вступает. Передавай только то, что меняешь. Если включить, аккаунт начнёт делать это в Telegram по-настоящему.

ПолеГдеЧто это
enabledв теледа/нет. Включить активность. Не включится, пока нет ни одной группы и выключен каталог.
joinAllowedв теледа/нет. Можно ли вступать в группы.
reactionsEnabledв теледа/нет. Ставить ли реакции.
catalogEnabledв теледа/нет. Брать ли каналы из общего каталога KELVIN.
groupsв телесписок строк. Разрешённые группы: @username или ссылка https://t.me/…, до 50.
scheduleв телеобъект. Когда можно отправлять: {timeZone: "Europe/Moscow" или "UTC", startLocal: "ГГГГ-ММ-ДДTЧЧ:ММ" (не раньше текущей минуты) или null, weekdays: дни недели 0–6, где 0 — воскресенье, intervals: от 1 до 4 отрезков {start: "ЧЧ:ММ", end: "ЧЧ:ММ"}}. Если конец раньше начала, отрезок переходит через полночь.
joinLimit24hв телечисло. Вступлений в сутки, 0–50. По умолчанию 3.
reactionLimit24hв телечисло. Реакций в сутки, 0–300. По умолчанию 40.
scrollMinв телечисло. Сколько раз листать за заход, от: 0–100. По умолчанию 6.
scrollMaxв телечисло. Сколько раз листать за заход, до: 0–100, не меньше scrollMin. По умолчанию 12.
sessionMinSecondsв телечисло. Длина захода, от: 30–3600 с. По умолчанию 90.
sessionMaxSecondsв телечисло. Длина захода, до: 30–3600 с, не меньше sessionMinSeconds. По умолчанию 180.
pauseMinSecondsв телечисло. Пауза между заходами, от: 0–86 400 с. По умолчанию 600.
pauseMaxSecondsв телечисло. Пауза между заходами, до: 0–86 400 с, не меньше pauseMinSeconds. По умолчанию 1800.
maxActionsPerSessionв телечисло. Действий за заход, 1–20. По умолчанию 2.
reactionEmojisв телесписок строк. Какие реакции ставить: до 12 обычных бесплатных реакций Telegram.
reactionProbabilityв телечисло. Как часто ставить реакцию, от 0 до 1. По умолчанию 0.5.
catalogPoolSizeв телечисло. Сколько каналов брать из каталога, 1–200. По умолчанию 40.
sessionsLimitв телечисло. Сколько заходов сделать всего, 0–1000. 0 — без ограничения. Новое значение обнуляет счётчик сделанных.
channelsPerSessionв телечисло. Каналов за заход, 1–20. По умолчанию 1.
curl -X PATCH "https://app.kelvintg.ru/api/v1/accounts/ID_АККАУНТА/group-activity" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "joinLimit24h": 2,
  "reactionsEnabled": false
}'

Ответ:

{
  "settings": {
    "configured": true,
    "enabled": false,
    "joinAllowed": false,
    "reactionsEnabled": false,
    "groups": [],
    "schedule": null,
    "joinLimit24h": 2,
    "reactionLimit24h": 40,
    "scrollMin": 6,
    "scrollMax": 12,
    "sessionMinSeconds": 90,
    "sessionMaxSeconds": 180,
    "pauseMinSeconds": 600,
    "pauseMaxSeconds": 1800
  },
  "counters": {
    "joins24h": 0,
    "joinAttempts24h": 0,
    "reactions24h": 0
  },
  "catalog": {
    "enabled": false,
    "poolSize": 40,
    "size": 385,
    "tracked": 0,
    "actionable": 0,
    "categories": {
      "news": {
        "size": 52,
        "available": 0
      },
      "finance": {
        "size": 20,
        "available": 0
ОтказЧто случилось и что сделать
409Телефон прямо сейчас выполняет заход по группам. Дождись отчёта и повтори.
400Включить нельзя: добавь хотя бы одну группу или включи каталог.

Отметить, состоит ли аккаунт в группе

POST /accounts/ID_АККАУНТА/group-activity/group-state · право ключа: чтение и действия

Записывает твоё решение по группе из списка аккаунта: состоит он в ней, не состоит или группа недоступна. Нужен, когда телефон сам этого не понял. Заходы в эту группу, которые ждали в очереди, снимаются.

ПолеГдеЧто это
group — обязательнов телестрока. Группа из списка этого аккаунта: @username или ссылка https://t.me/….
membership — обязательнов телестрока. "member" — состоит, "not_member" — не состоит, "unavailable" — группа недоступна, "unknown" — неизвестно.
curl -X POST "https://app.kelvintg.ru/api/v1/accounts/ID_АККАУНТА/group-activity/group-state" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "group": "@synthetic_group",
  "membership": "member"
}'

Ответ:

{
  "settings": {
    "configured": true,
    "enabled": false,
    "joinAllowed": false,
    "reactionsEnabled": false,
    "groups": [
      {
        "kind": "username",
        "value": "synthetic_group"
      }
    ],
    "schedule": null,
    "joinLimit24h": 2,
    "reactionLimit24h": 40,
    "scrollMin": 6,
    "scrollMax": 12,
    "sessionMinSeconds": 90,
    "sessionMaxSeconds": 180,
    "pauseMinSeconds": 600,
    "pauseMaxSeconds": 1800
  },
  "counters": {
    "joins24h": 0,
    "joinAttempts24h": 0,
    "reactions24h": 0
  },
  "catalog": {
    "enabled": false,
    "poolSize": 40,
    "size": 385,
    "tracked": 0,
    "actionable": 0,
    "categories": {
      "news": {
        "size": 52,
ОтказЧто случилось и что сделать
404Этой группы нет в списке аккаунта. Сначала добавь её в настройках активности в группах.

Создать программу прогрева

POST /warmup-runs · право ключа: чтение и действия · нужен номер запроса

Создаёт черновик программы: какие аккаунты, сколько дней и в какие часы телефон читает каналы и подписывается на них. Прогрев не начинается, пока ты не вызовешь «Запустить программу прогрева».

ПолеГдеЧто это
window — обязательнов телеобъект. Часы, в которые телефон заходит в Telegram, каждый день: {timeZone: "Europe/Moscow" или "UTC", start: "ЧЧ:ММ", end: "ЧЧ:ММ"}. Если конец раньше начала, окно переходит через полночь. Окно должно быть длиннее одного захода.
accounts — обязательнов телесписок объектов. От 1 до 50 аккаунтов без повторов, каждый как {accountId, profile, durationDays, settings}. profile: "read_only" — только чтение, 1 заход в день; "work_reading" — чтение, 3–7 заходов; "safe" — 1–3 захода и 2–3 подписки; "standard" — 3–6 заходов и 5 подписок; "intensive" — 5–8 заходов и 7–8 подписок; "pilot_one" — одна пробная подписка. durationDays: 1–365. settings (только для read_only и work_reading): {visits: [от, до] в пределах 1–12, visitSeconds: [от, до] в пределах 60–600}.
pilotHandleв телестрока. Только для profile "pilot_one": username канала из каталога, 5–32 знака. Тогда в программе один аккаунт и один день.
startв теледа/нет. true — сразу проверить, что аккаунты свободны и телефоны умеют прогрев. Запуск всё равно отдельным запросом.
curl -X POST "https://app.kelvintg.ru/api/v1/warmup-runs" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Idempotency-Key: order-0001" \
  -H "Content-Type: application/json" \
  -d '{
  "window": {
    "timeZone": "Europe/Moscow",
    "start": "10:00",
    "end": "20:00"
  },
  "accounts": [
    {
      "accountId": "ID_АККАУНТА",
      "profile": "read_only",
      "durationDays": 3
    }
  ]
}'

Ответ:

{
  "outcome": "draft",
  "id": "ID",
  "status": "draft",
  "window": {
    "timeZone": "Europe/Moscow",
    "start": "10:00",
    "end": "20:00"
  },
  "accounts": [
    {
      "accountId": "ID",
      "label": "Анна",
      "profile": "read_only",
      "durationDays": 3,
      "newSubscriptionsConfirmed": 0,
      "status": "draft",
      "pilotHandle": null,
      "settings": null,
      "sessions": {
        "total": 0,
        "pending": 0,
        "claimed": 0,
        "completed": 0,
        "skipped": 0,
        "interrupted": 0,
        "needsReview": 0,
        "stopRequested": 0,
        "nextScheduledAt": null,
        "plannedDays": 0,
        "nextReason": null,
        "nextMaxSeconds": null
      },
      "categories": {},
      "reading": {
        "scrolls": 0,
ОтказЧто случилось и что сделать
400Окно слишком короткое для выбранного режима. Раздвинь часы работы.

Запустить программу прогрева

POST /warmup-runs/ID_ПРОГРЕВА/start · право ключа: чтение и действия

Запускает черновик программы: телефоны начнут заходить в Telegram в заданные часы. Если в кабинете нажато «Остановить всё», заходы начнутся после возобновления работы человеком.

curl -X POST "https://app.kelvintg.ru/api/v1/warmup-runs/ID_ПРОГРЕВА/start" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Ответ:

{
  "outcome": "running",
  "id": "ID",
  "status": "running",
  "window": {
    "timeZone": "Europe/Moscow",
    "start": "10:00",
    "end": "20:00"
  },
  "accounts": [
    {
      "accountId": "ID",
      "label": "Анна",
      "profile": "read_only",
      "durationDays": 3,
      "newSubscriptionsConfirmed": 0,
      "status": "running",
      "pilotHandle": null,
      "settings": null,
      "sessions": {
        "total": 1,
        "pending": 1,
        "claimed": 0,
        "completed": 0,
        "skipped": 0,
        "interrupted": 0,
        "needsReview": 0,
        "stopRequested": 0,
        "nextScheduledAt": "2026-10-10T10:00:00.000Z",
        "plannedDays": 1,
        "nextReason": null,
        "nextMaxSeconds": 120
      },
      "categories": {},
      "reading": {
        "scrolls": 0,
ОтказЧто случилось и что сделать
409На телефоне старая версия KELVIN TG, она не умеет прогрев. Обнови приложение на каждом телефоне программы.
409Аккаунт уже занят в другой запущенной программе. Останови её или убери аккаунт.
409Программа остановлена или завершена. Остановленную продолжают запросом «Продолжить программу прогрева».

Продолжить программу прогрева

POST /warmup-runs/ID_ПРОГРЕВА/resume · право ключа: чтение и действия

Продолжает остановленную программу или один остановленный аккаунт в ней. Оставшиеся заходы переносятся в новое окно. К кнопке «Возобновить работу» после «Остановить всё» этот запрос отношения не имеет.

ПолеГдеЧто это
durationDays — обязательнов телечисло. Сколько дней программа длится всего, 1–365. Не меньше, чем уже запланировано.
windowв телеобъект. Часы, в которые телефон заходит в Telegram, каждый день: {timeZone: "Europe/Moscow" или "UTC", start: "ЧЧ:ММ", end: "ЧЧ:ММ"}. Если конец раньше начала, окно переходит через полночь. Обязательно, если продолжаешь всю остановленную программу.
accountIdв телестрока. Продолжить только этот аккаунт. Часы работы тогда остаются прежними.
curl -X POST "https://app.kelvintg.ru/api/v1/warmup-runs/ID_ПРОГРЕВА/resume" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "durationDays": 3,
  "window": {
    "timeZone": "Europe/Moscow",
    "start": "10:00",
    "end": "20:00"
  }
}'

Ответ:

{
  "outcome": "running",
  "id": "ID",
  "status": "running",
  "window": {
    "timeZone": "Europe/Moscow",
    "start": "10:00",
    "end": "20:00"
  },
  "accounts": [
    {
      "accountId": "ID",
      "label": "Анна",
      "profile": "read_only",
      "durationDays": 3,
      "newSubscriptionsConfirmed": 0,
      "status": "running",
      "pilotHandle": null,
      "settings": null,
      "sessions": {
        "total": 1,
        "pending": 1,
        "claimed": 0,
        "completed": 0,
        "skipped": 0,
        "interrupted": 0,
        "needsReview": 0,
        "stopRequested": 0,
        "nextScheduledAt": "2026-10-10T10:00:00.000Z",
        "plannedDays": 1,
        "nextReason": null,
        "nextMaxSeconds": 120
      },
      "categories": {},
      "reading": {
        "scrolls": 0,
ОтказЧто случилось и что сделать
409Телефон ещё не отчитался о заходе, который взял. Подожди отчёта и повтори.
409Продолжать нечего: все заходы программы уже сделаны.
400Срок меньше, чем дней уже запланировано. Увеличь durationDays.

Остановить программу прогрева

POST /warmup-runs/ID_ПРОГРЕВА/stop · право ключа: чтение и действия

Останавливает программу для всех её аккаунтов. Заход, который телефон уже начал, он закончит и отчитается. Продолжить можно запросом «Продолжить программу прогрева».

curl -X POST "https://app.kelvintg.ru/api/v1/warmup-runs/ID_ПРОГРЕВА/stop" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Ответ:

{
  "outcome": "stopped",
  "id": "ID",
  "status": "stopped",
  "window": {
    "timeZone": "Europe/Moscow",
    "start": "10:00",
    "end": "20:00"
  },
  "accounts": [
    {
      "accountId": "ID",
      "label": "Анна",
      "profile": "read_only",
      "durationDays": 3,
      "newSubscriptionsConfirmed": 0,
      "status": "stopped",
      "pilotHandle": null,
      "settings": null,
      "sessions": {
        "total": 1,
        "pending": 0,
        "claimed": 0,
        "completed": 0,
        "skipped": 1,
        "interrupted": 0,
        "needsReview": 0,
        "stopRequested": 0,
        "nextScheduledAt": null,
        "plannedDays": 1,
        "nextReason": null,
        "nextMaxSeconds": null
      },
      "categories": {},
      "reading": {
        "scrolls": 0,

Остановить прогрев одного аккаунта

POST /warmup-runs/ID_ПРОГРЕВА/accounts/ID_АККАУНТА/stop · право ключа: чтение и действия

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

curl -X POST "https://app.kelvintg.ru/api/v1/warmup-runs/ID_ПРОГРЕВА/accounts/ID_АККАУНТА/stop" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Ответ:

{
  "outcome": "stopped",
  "id": "ID",
  "status": "stopped",
  "window": {
    "timeZone": "Europe/Moscow",
    "start": "10:00",
    "end": "20:00"
  },
  "accounts": [
    {
      "accountId": "ID",
      "label": "Анна",
      "profile": "read_only",
      "durationDays": 3,
      "newSubscriptionsConfirmed": 0,
      "status": "stopped",
      "pilotHandle": null,
      "settings": null,
      "sessions": {
        "total": 1,
        "pending": 0,
        "claimed": 0,
        "completed": 0,
        "skipped": 1,
        "interrupted": 0,
        "needsReview": 0,
        "stopRequested": 0,
        "nextScheduledAt": null,
        "plannedDays": 1,
        "nextReason": null,
        "nextMaxSeconds": null
      },
      "categories": {},
      "reading": {
        "scrolls": 0,

Удалить черновик прогрева

DELETE /warmup-runs/ID_ПРОГРЕВА · право ключа: чтение и действия

Удаляет программу, которую ещё не запускали. Вернуть её нельзя. Запущенную программу удалить нельзя, её можно только остановить: она остаётся в истории.

curl -X DELETE "https://app.kelvintg.ru/api/v1/warmup-runs/ID_ПРОГРЕВА" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "ok": true,
  "id": "ID"
}
ОтказЧто случилось и что сделать
409Программу уже запускали. Её можно остановить, но не удалить.

Статистика и журнал

Статистика

GET /stats · право ключа: только чтение

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

ПолеГдеЧто это
daysв адресестрока. Период: "7", "30" (по умолчанию) или "all" — за всё время. Сутки считаются по Москве.
curl "https://app.kelvintg.ru/api/v1/stats?days=7" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "generatedAt": "2026-10-10T10:00:00.000Z",
  "days": "7",
  "since": "2026-10-03T21:00:00.000Z",
  "accountPrice": null,
  "lowDataBelow": 30,
  "summary": {
    "sent": 0,
    "unknown": 0,
    "read": null,
    "replied": null,
    "replyRate": null,
    "repliesKnown": false,
    "tracked": 0,
    "lowData": true,
    "accounts": 0,
    "spent": null,
    "costPerMessage": null,
    "costPerReply": null,
    "visualEnabled": false,
    "visualEligibleChats": 0
  },
  "campaigns": [],
  "bases": [],
  "accounts": [
    {
      "id": "ID",
      "label": "Анна",
      "deviceName": "Телефон 1",
      "status": "working",
      "days": 1,
      "firstSentAt": null,
      "sent": 0,
      "unknown": 0,
      "read": null,
      "replied": null,

Отправки по аккаунтам

GET /stats/accounts · право ключа: только чтение

Отдаёт по каждому аккаунту, сколько сообщений рассылок он отправил всего, за 7 дней и сегодня, сколько лидов ответили и когда была последняя отправка.

curl "https://app.kelvintg.ru/api/v1/stats/accounts" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "generatedAt": "2026-10-10T10:00:00.000Z",
  "accounts": [
    {
      "id": "ID",
      "label": "Анна",
      "deviceName": "Телефон 1",
      "deleted": false,
      "total": 0,
      "week": 0,
      "today": 0,
      "replies": 0,
      "lastAt": null
    }
  ]
}

Итог за 7 дней

GET /today/week · право ключа: только чтение

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

curl "https://app.kelvintg.ru/api/v1/today/week" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "generatedAt": "2026-10-10T10:00:00.000Z",
  "timeZone": "Europe/Moscow",
  "days": [
    {
      "date": "2026-10-04",
      "total": null,
      "sent": null,
      "unknown": null,
      "needsReview": null,
      "decisions": null,
      "restrictedAccounts": null,
      "workStops": null
    },
    "… ещё 6"
  ],
  "current": {
    "from": "2026-10-04",
    "to": "2026-10-10",
    "total": null,
    "sent": null,
    "unknown": null,
    "needsReview": null,
    "decisions": null,
    "restrictedAccounts": null,
    "workStops": null
  },
  "previous": {
    "from": "2026-09-27",
    "to": "2026-10-03",
    "total": null,
    "sent": null,
    "unknown": null,
    "needsReview": null,
    "decisions": null,
    "restrictedAccounts": null,

Сколько ещё уйдёт сегодня

GET /day-plan · право ключа: только чтение

Оценивает по каждому телефону, сколько сообщений он ещё может отправить до конца суток по Москве, и называет причины, если отправка стоит. Это оценка «от и до» по лимитам и времени, а не обещание.

curl "https://app.kelvintg.ru/api/v1/day-plan" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "generatedAt": "2026-10-10T10:00:00.000Z",
  "moscowDayEndsAt": "2026-10-10T21:00:00.000Z",
  "stopActive": false,
  "phones": [
    {
      "deviceId": "ID",
      "deviceName": "Телефон 1",
      "noData": true,
      "range": null,
      "reasons": [
        {
          "code": "device_offline",
          "title": "Телефон не на связи"
        },
        "… ещё 1"
      ],
      "accounts": [
        {
          "accountId": "ID",
          "label": "Анна",
          "remainingToday": 80,
          "reasons": []
        }
      ],
      "campaigns": []
    }
  ]
}

Журнал событий

GET /events · право ключа: только чтение

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

ПолеГдеЧто это
fromв адресестрока. С какого момента, включительно. Дата и время в виде 2026-10-10T09:00:00.000Z.
toв адресестрока. До какого момента, не включая его. В том же виде.
accountIdв адресестрока. Только события этого аккаунта.
deviceIdв адресестрока. Только события этого телефона и его аккаунтов.
typeв адресестрока. Типы событий через запятую, до 50. Например campaign_created,work_stopped.
beforeв адресестрока. Значение nextBefore из прошлого ответа: придёт следующая, более старая страница.
limitв адресечисло. Сколько записей отдать, 1–200. По умолчанию 50.
curl "https://app.kelvintg.ru/api/v1/events?limit=20" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "items": [
    {
      "id": "ID",
      "at": "2026-10-10T10:00:00.000Z",
      "type": "campaign_playbook_created",
      "message": "Набор «Первое касание» сохранён",
      "accountId": null,
      "deviceId": null,
      "objectId": "ID",
      "outcome": "ok",
      "actor": "operator",
      "by": "demo",
      "apiKey": "Пример",
      "before": null,
      "after": null
    },
    "… ещё 11"
  ],
  "nextBefore": null
}

«Остановить всё»

Нажато ли «Остановить всё»

GET /work/stop-all · право ключа: только чтение

Отвечает, остановлена ли работа кабинета, кто и когда её остановил и как каждый телефон это подтвердил. Пока работа остановлена, телефоны новых заданий не получают.

curl "https://app.kelvintg.ru/api/v1/work/stop-all" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "stopped": false,
  "stoppedAt": null,
  "stoppedBy": null,
  "devices": [],
  "resumable": null
}

Остановить всё

POST /work/stop-all · право ключа: чтение и действия

Сразу останавливает всю работу кабинета: рассылки, прогрев и активность в группах. Сообщение, которое телефон уже начал отправлять, он закончит. Программа может только остановить: возобновить работу может только человек в кабинете, кнопкой «Возобновить работу».

curl -X POST "https://app.kelvintg.ru/api/v1/work/stop-all" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Ответ:

{
  "stopped": true,
  "stoppedAt": "2026-10-10T10:00:00.000Z",
  "stoppedBy": "demo",
  "devices": [
    {
      "id": "ID",
      "name": "Телефон 1",
      "state": "offline",
      "reason": "no_heartbeat_since_stop",
      "why": "Телефон не выходил на связь после остановки",
      "activeWork": null,
      "lastSeen": null
    }
  ],
  "resumable": {
    "runningCampaigns": [
      {
        "id": "ID",
        "name": "Проверка",
        "pending": 1
      }
    ],
    "activeWarmupRuns": [],
    "stoppedWarmupRuns": [],
    "groupActivityAccounts": []
  },
  "alreadyStopped": false
}

Опасные действия

Удалить аккаунт

DELETE /accounts/ID_АККАУНТА · право ключа: опасные действия

Убирает аккаунт из кабинета: его сообщения в очереди отменяются, и это не отменить. Из рассылок он выходит, а общую очередь получателей доделают другие аккаунты. Сам аккаунт в Telegram не трогается, история в статистике остаётся.

curl -X DELETE "https://app.kelvintg.ru/api/v1/accounts/ID_АККАУНТА" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "ok": true,
  "alreadyDeleted": false
}
ОтказЧто случилось и что сделать
409Аккаунт прямо сейчас выполняет задание или проверку. Дождись конца и повтори.

Отключить телефон

POST /devices/ID_ТЕЛЕФОНА/revoke · право ключа: опасные действия

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

ПолеГдеЧто это
confirmed — обязательнов теледа/нет. Должно быть true: так ты подтверждаешь отключение.
curl -X POST "https://app.kelvintg.ru/api/v1/devices/ID_ТЕЛЕФОНА/revoke" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "confirmed": true
}'

Ответ:

{
  "id": "ID",
  "name": "Телефон 1",
  "lastSeen": null,
  "status": "offline",
  "androidVersion": null,
  "telegramVersion": null,
  "accessibilityEnabled": false,
  "keyboardEnabled": null,
  "power": null,
  "screenUnlocked": false,
  "networkConnected": false,
  "liveEnabled": false,
  "manualScreen": false,
  "networkGroup": null
}

Удалить телефон

POST /devices/ID_ТЕЛЕФОНА/delete · право ключа: опасные действия

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

ПолеГдеЧто это
confirmed — обязательнов теледа/нет. Должно быть true: так ты подтверждаешь удаление.
curl -X POST "https://app.kelvintg.ru/api/v1/devices/ID_ТЕЛЕФОНА/delete" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "confirmed": true
}'

Ответ:

{
  "ok": true,
  "alreadyDeleted": false,
  "archivedAccounts": 1
}
ОтказЧто случилось и что сделать
409Телефон прямо сейчас выполняет задание. Дождись отчёта и повтори.

Подтвердить, что сообщение ушло

POST /tasks/ID_ОТПРАВКИ/confirm-delivery · право ключа: опасные действия

Для сообщений в состоянии «вероятно отправлено»: ты сам посмотрел в Telegram и подтверждаешь, что оно доставлено. Сообщение навсегда записывается как отправленное и попадает в статистику, отменить это нельзя. Повторно KELVIN такое сообщение не отправляет в любом случае.

ПолеГдеЧто это
confirmed — обязательнов теледа/нет. Должно быть true.
manualSendв теледа/нет. true — если сообщение остановила пауза рассылки и ты отправил его руками в Telegram.
curl -X POST "https://app.kelvintg.ru/api/v1/tasks/ID_ОТПРАВКИ/confirm-delivery" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "confirmed": true
}'

Ответ:

{
  "ok": true,
  "alreadyConfirmed": false
}
ОтказЧто случилось и что сделать
409Подтвердить можно только сообщение с неясным итогом отправки. У этого итог уже известен.
409Телефон этого аккаунта сейчас выполняет задание. Дождись конца и повтори.

Оставить сообщение на проверке

POST /tasks/ID_ОТПРАВКИ/keep-review · право ключа: опасные действия

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

ПолеГдеЧто это
noteв телестрока. Заметка для себя, до 200 знаков.
curl -X POST "https://app.kelvintg.ru/api/v1/tasks/ID_ОТПРАВКИ/keep-review" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "note": "Проверю позже"
}'

Ответ:

{
  "ok": true,
  "alreadyRecorded": false
}
ОтказЧто случилось и что сделать
409У этого сообщения итог уже известен или доставка подтверждена. Оставлять на проверке нечего.

Проверка @SpamBot: включить или выключить

POST /accounts/ID_АККАУНТА/health/gate · право ключа: опасные действия

Включает или выключает для аккаунта проверку у @SpamBot. Если включить, аккаунт перед следующей серией сообщений спросит @SpamBot и будет отправлять только при ответе «ограничений нет». Если выключить, аккаунт пишет без этой проверки, и ограничение от Telegram ты заметишь позже.

ПолеГдеЧто это
enabled — обязательнов теледа/нет. true — включить проверку, false — выключить.
curl -X POST "https://app.kelvintg.ru/api/v1/accounts/ID_АККАУНТА/health/gate" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": true
}'

Ответ:

{
  "id": "ID",
  "deviceId": "ID",
  "client": {
    "clientId": "tc_54b5fd93b04f5adf10d891521ec615bb",
    "deviceId": "ID",
    "kind": "default",
    "userSerial": null,
    "package": null,
    "profileEpoch": null,
    "installEpoch": null,
    "bindingId": "tb_2285326d919db59459a0b11ebb35de15",
    "bindingRevision": 1,
    "legacyUnknown": true
  },
  "label": "Анна",
  "phone": "+12025550401",
  "enabled": true,
  "deletedAt": null,
  "messageLimit": 40,
  "effectiveMessageLimit": 40,
  "phoneSearchLimit": 15,
  "taskDelayMin": 6,
  "taskDelayMax": 6,
  "accountDelayMin": 0,
  "accountDelayMax": 0
}

Снять остановку после @SpamBot

POST /accounts/ID_АККАУНТА/health/clear · право ключа: опасные действия

Вручную возвращает аккаунт в работу, не спрашивая @SpamBot: отправка с него продолжится. Если ограничение в Telegram на самом деле не снято, сообщения уйдут с ограниченного аккаунта, и вернуть их нельзя. Пока идут 12 часов после ответа @SpamBot об ограничении, запрос откажет.

ПолеГдеЧто это
confirmed — обязательнов теледа/нет. Должно быть true.
curl -X POST "https://app.kelvintg.ru/api/v1/accounts/ID_АККАУНТА/health/clear" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "confirmed": true
}'
ОтказЧто случилось и что сделать
409Аккаунт отложен на 12 часов после ограничения от @SpamBot. Раньше этого срока остановку снять нельзя, срок указан в ответе.
409Снимать нечего: аккаунт не ждёт проверки и не остановлен.

Проверить аккаунт у @SpamBot снова

POST /accounts/ID_АККАУНТА/health/recheck · право ключа: опасные действия

Просит телефон заново спросить @SpamBot. Аккаунт вернётся к отправке, только если придёт ответ «ограничений нет». Проверку делает телефон, когда до неё дойдёт очередь, и отозвать её нельзя.

curl -X POST "https://app.kelvintg.ru/api/v1/accounts/ID_АККАУНТА/health/recheck" \
  -H "Authorization: Bearer $KELVIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
ОтказЧто случилось и что сделать
409Проверить снова можно, только когда проверка @SpamBot включена и отправка с аккаунта остановлена после неё.
409Аккаунт отложен на 12 часов после ограничения от @SpamBot. Повтори после срока из ответа.

Скачать данные кабинета

GET /workspace/export · право ключа: опасные действия

Отдаёт одним файлом рассылки, получателей, аккаунты, телефоны и журнал всего кабинета. Паролей, ключей и текстов переписки в файле нет, но списки получателей есть: файл, который ушёл наружу, обратно не вернуть. Скачивание записывается в журнал. Файл может быть большим.

curl "https://app.kelvintg.ru/api/v1/workspace/export" \
  -H "Authorization: Bearer $KELVIN_KEY"

Ответ:

{
  "exportedAt": "2026-10-10T10:00:00.000Z",
  "workspaceId": "ID",
  "campaigns": [
    {
      "id": "ID",
      "name": "Проверка",
      "baseLabel": null,
      "status": "pending",
      "createdAt": "2026-10-10T10:00:00.000Z",
      "schedule": null,
      "scheduleStatus": null,
      "editable": true,
      "revision": 1,
      "startedAt": null,
      "secondTouch": {
        "enabled": false,
        "waiting": false,
        "planned": 0,
        "sent": 0
      },
      "post": null,
      "autoReply": {
        "enabled": false,
        "sent": 0,
        "waiting": 0,
        "refused": 0,
        "missed": 0
      },
      "total": 1
    },
    "… ещё 13"
  ],
  "tasks": [
    {
      "id": "ID",

На этой странице

Получи ключПервый запросКак устроены запросыСостоянияПовтор запросаОтказыЧастые задачиОтправить одно сообщение одному человекуЗабирать ответы лидовОтветить лидуОстановить всёДля ИИ-помощникаЧего в API нетВсе запросыПроверка ключаПроверить ключРассылкиСписок рассылокРассылка и её получателиСкачать получателей таблицейПроверить список получателейПроверить текст сообщенияПроверить постПроверить рассылку без созданияПосчитать второе касаниеСоздать рассылку или одно сообщениеИзменить черновик рассылкиИзменить настройки рассылкиЗапустить рассылкуПоставить рассылку на паузуОтменить рассылкуУбрать в корзину или вернутьНаборы настроекСписок наборовОдин наборЧто даст наборСохранить наборИзменить наборУдалить набор«Не писать»Список «Не писать»Добавить в «Не писать»Убрать из «Не писать»Чаты и ответы лидамЧто телефоны прочитали в TelegramНовые сообщения лидовСписок чатовПереписка в чатеУзнать судьбу ответаОтветить лидуОтменить ответСверить чат с телефономОтметить прочитанным в кабинетеСнять «ждёт ответа»Обновить список чатовИИ-ответыЧаты, которые ждут человекаСтатистика ИИ-ответовАккаунты и телефоныСписок аккаунтовСписок телефоновЛента аккаунтаДобавить аккаунтИзменить аккаунтПрогрев и группыПрогрев: план и факт на сегодняПрогрев: каналы и подпискиСписок программ прогреваЖурнал программы прогреваЖурнал активности в группахВключить или выключить прогрев аккаунтаНастроить активность в группахОтметить, состоит ли аккаунт в группеСоздать программу прогреваЗапустить программу прогреваПродолжить программу прогреваОстановить программу прогреваОстановить прогрев одного аккаунтаУдалить черновик прогреваСтатистика и журналСтатистикаОтправки по аккаунтамИтог за 7 днейСколько ещё уйдёт сегодняЖурнал событий«Остановить всё»Нажато ли «Остановить всё»Остановить всёОпасные действияУдалить аккаунтОтключить телефонУдалить телефонПодтвердить, что сообщение ушлоОставить сообщение на проверкеПроверка @SpamBot: включить или выключитьСнять остановку после @SpamBotПроверить аккаунт у @SpamBot сноваСкачать данные кабинета