История переписки WhatsApp через API: журнал, авторство и выгрузка в CSV
wapi.kz ведёт журнал всех сообщений ваших номеров: входящих, отправленных через API и MCP и набранных менеджером на телефоне. Через API его можно читать постранично, искать по собеседнику и периоду, проверять статус доставки, скачивать фото, голосовые и документы и выгружать переписку файлом или архивом.
Гайд для тех, кто строит отчёты, синхронизирует переписку с CRM или пишет ИИ-агента, которому важно знать, кто и что уже ответил клиенту.
Что хранится в журнале
- Входящие от клиентов, в том числе из групп, где состоит ваш номер.
- Исходящие через API, MCP и тестовую кнопку кабинета.
- Сообщения, которые человек написал с телефона или в WhatsApp Web: номер остаётся у вас, и эти ответы тоже попадают в журнал.
- Файлы: фото, видео, голосовые, документы и стикеры — входящие и исходящие, в том числе с телефона и в группах.
Журнал ведётся с момента подключения номера и хранится в Казахстане: текст — без срока и без ограничения объёма, файлы — в хранилище, 5 ГБ на аккаунт бесплатно (подробнее — в разделе о хранении). У каждого сообщения есть тип: text, image, video, audio, voice (голосовое), document, sticker, location, contact, media или other.
Журнал: GET /messages
curl -G https://wapi.kz/api/v1/messages \
-H "Authorization: Bearer $WAPI_KEY" \
-d from=1 -d chat=77011234567 -d limit=20| Параметр | Что значит |
|---|---|
from | Ваш номер: id или телефон. Без него — все номера аккаунта |
chat | Собеседник: телефон 77011234567 или chat_id (…@c.us, …@g.us, …@lid) |
since | Не раньше этого момента |
until | Раньше этого момента (сама граница не входит) |
direction | in — входящие, out — исходящие |
limit | Сколько вернуть: по умолчанию 50, максимум 500 |
before | Курсор следующей страницы — значение next_before |
В ответе — массив messages, новые сверху:
{
"messages": [
{
"id": 1842,
"from": 1,
"direction": "out",
"source": "api",
"chat_id": "77011234567@c.us",
"peer": "77011234567",
"peer_lid": null,
"name": "Айгерим",
"is_group": false,
"group_subject": null,
"author": null,
"wa_message_id": "3EB0A1F2C4D5E6F7",
"type": "text",
"text": "Ваш заказ №1042 готов к выдаче",
"status": "read",
"error": null,
"created_at": "2026-10-16T09:04:11.482Z",
"updated_at": "2026-10-16T09:05:02.117Z",
"media": null,
"media_skipped": false,
"media_skip_reason": null,
"media_pending": false
}
],
"next_before": null
}from здесь — id вашего номера, peer — телефон собеседника, name — его имя из WhatsApp или название группы, created_at — время в UTC. Для скрытого номера (…@lid) в peer телефон, если WhatsApp его сообщил, иначе null; сам идентификатор — в peer_lid, и его цифры — не телефон. У групп peer равен null, а у входящих в группе есть author — кто написал: { name, phone, lid }.
Формат времени
since и until принимают дату 2026-10-16, дату со временем 2026-10-16T09:00:00Z или unix-время в секундах. Дата без времени — это полночь по UTC. Чтобы считать сутки по Алматы, укажите смещение: 2026-10-16T00:00:00+05:00. В адресе плюс нужно закодировать как %2B — requests в Python и curl --data-urlencode делают это сами. Неверный формат — ответ 400 с кодом invalid_param.
Пагинация: next_before
Если страница заполнена до limit, в ответе есть next_before — передайте его в параметр before и получите следующую. Следующая страница может оказаться пустой — значит, сообщения кончились. Неполная страница приходит с next_before: null.
Генератор на Python, который проходит все страницы:
import os
import requests
API = "https://wapi.kz/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['WAPI_KEY']}"}
def iter_messages(filters):
"""Все сообщения по фильтру, от новых к старым."""
params = {"limit": 500, **filters}
while True:
r = requests.get(f"{API}/messages", headers=HEADERS, params=params, timeout=30)
r.raise_for_status()
data = r.json()
yield from data["messages"]
if not data["next_before"]:
break
params["before"] = data["next_before"]
for m in iter_messages({"from": 1, "direction": "in", "since": "2026-10-01T00:00:00+05:00"}):
print(m["created_at"], m["peer"], m["text"])Одно сообщение и статус доставки
GET /messages/{id} возвращает одно сообщение — например, чтобы проверить, дошло ли отправленное. id приходит в ответе на POST /messages/send.
| status | Что значит |
|---|---|
queued | Принято, ждёт отправки |
sent | Отправлено в WhatsApp (одна галочка) |
delivered | Доставлено на телефон (две галочки) |
read | Прочитано |
failed | Не доставлено — причина в поле error |
В error бывает send_failed, capacity или number_paused — сообщение не ушло, или not delivered — WhatsApp сообщил, что не доставил его. Статус только повышается: прочитанное не станет снова «доставленным». Входящие сохраняются со статусом delivered. Сообщения нет в вашем аккаунте — ответ 404 с кодом not_found.
Опрашивать статус в цикле не нужно: изменения сразу приходят в вебхук событием message.ack.
Файлы: поле media
Если в сообщении было фото, видео, голосовое, документ или стикер, файл лежит в поле media:
"media": {
"id": 512, "kind": "photo", "mimetype": "image/jpeg", "filename": null, "size": 184320,
"url": "https://storage.yandexcloud.kz/wapi-media/…?X-Amz-Signature=…"
}kind — photo, video, voice, document или sticker. url — подписанная ссылка, она живёт 15 минут. Нужна свежая — GET /media/{id} ответит переадресацией 302 на новую ссылку, а с ?download=1 отдаст файл с исходным именем:
curl -L "https://wapi.kz/api/v1/media/512?download=1" -H "Authorization: Bearer $WAPI_KEY" -o check.jpgФайл был, но не сохранён — media_skipped: true, причина в media_skip_reason: history_off — хранение переписки выключено, ceiling — достигнут ваш потолок объёма, no_balance — сверх 5 ГБ не хватает баланса, failed — WhatsApp не отдал файл. media_pending: true значит, что файл ещё сохраняется — обычно это секунды, запросите сообщение чуть позже. Ключ одного номера видит только файлы своего номера. Подробнее — в документации по файлам.
Чаты: GET /chats
GET /chats?from=&limit= — собеседники с последним сообщением и счётчиками incoming, outgoing, total, свежие сверху. Удобно для своей панели «входящих» или чтобы найти клиентов, которые написали и не дождались ответа. Пример на Node.js (18+, ES-модуль):
const API = "https://wapi.kz/api/v1";
const headers = { Authorization: `Bearer ${process.env.WAPI_KEY}` };
const { chats } = await fetch(`${API}/chats?from=1&limit=200`, { headers }).then((r) => r.json());
const waiting = chats.filter((c) => c.peer && c.last.direction === "in");
for (const c of waiting) console.log(`${c.peer} ждёт ответа с ${c.last_at}: ${c.last.text ?? "[без текста]"}`);Поле source: кто написал
Номер остаётся на телефоне, поэтому в одном чате могут писать программа, ИИ-агент и живой менеджер. Поле source различает их:
| source | Кто написал |
|---|---|
api | Ваша программа через REST API |
mcp | ИИ-агент через MCP-сервер |
test | Тестовая отправка из кабинета |
phone | Человек с телефона или в WhatsApp Web, Desktop |
contact | Собеседник |
system | Служебное сообщение сервиса |
Главное применение — чтобы ИИ-агент не перебивал менеджера. Перед ответом агент смотрит последние сообщения чата: если недавно там есть source: "phone", разговор ведёт человек, и агенту лучше промолчать. Продолжение примера выше:
async function agentMayReply(from, client) {
const url = `${API}/messages?from=${from}&chat=${client}&limit=10`;
const { messages } = await fetch(url, { headers }).then((r) => r.json());
const hourAgo = Date.now() - 60 * 60 * 1000;
return !messages.some((m) => m.source === "phone" && Date.parse(m.created_at) > hourAgo);
}Агент на MCP получает те же поля через инструмент get_messages — подробнее в гайде про ИИ-агентов и MCP и в документации.
Выгрузка в CSV и JSON
GET /export отдаёт переписку файлом: для отчётов, архива или переноса в свою базу. До 100 000 сообщений за раз, старые сверху. from, since, until — как в GET /messages, format — csv (по умолчанию) или json.
curl -G https://wapi.kz/api/v1/export \
-H "Authorization: Bearer $WAPI_KEY" \
-d from=1 -d since=2026-10-01 -d until=2026-11-01 -d format=csv \
-o whatsapp-october.csvКолонки CSV: id, created_at, number_id, number_phone, direction, source, peer_phone, chat_id, type, text, status, wa_message_id, error. Файл в UTF-8 с меткой BOM, поэтому кириллица в Excel открывается нормально. Если Excel сложил всё в одну колонку, откройте файл через «Данные → Из текста/CSV» и выберите разделитель «запятая».
Если текст начинается с =, +, - или @, в CSV перед ним ставится апостроф — чтобы Excel не принял сообщение клиента за формулу. Обрабатывая CSV программой, учитывайте это или берите JSON: там текст как есть.
JSON-выгрузка — сообщения в том же виде, что в GET /messages, плюс from_phone (телефон вашего номера) и общее число count. Месячный отчёт «сколько сообщений и от кого» на Python — API и HEADERS из генератора выше:
from collections import Counter
r = requests.get(f"{API}/export", headers=HEADERS, timeout=120, params={
"since": "2026-10-01T00:00:00+05:00",
"until": "2026-11-01T00:00:00+05:00",
"format": "json",
})
r.raise_for_status()
stats = Counter((m["from_phone"] or str(m["from"]), m["source"]) for m in r.json()["messages"])
for (number, source), count in sorted(stats.items()):
print(number, source, count)Больше 100 000 сообщений — выгружайте частями, по неделям или месяцам. Подробности — в документации по выгрузке.
Архив вместе с файлами
GET /export выгружает только сообщения. Переписку вместе с файлами собирает POST /exports — ZIP-архив с папкой файлов, messages.json и messages.csv для Excel:
curl -X POST https://wapi.kz/api/v1/exports \
-H "Authorization: Bearer $WAPI_KEY" -H "Content-Type: application/json" \
-d '{"from":1,"since":"2026-10-01","until":"2026-11-01","include_media":true}'Все поля необязательные: без from — все номера, без chat — все чаты, без дат — с момента подключения. Архив собирается в фоне, обычно за минуты. Статус и ссылку показывает GET /exports/{id}: queued → running → done (или failed, expired), а когда архив готов, на вебхук приходит событие export.ready. Ссылка живёт 7 дней. Одновременно — не больше 2 выгрузок, в сутки — до 10. Подробнее — в документации по архивам.
Догон пропущенных вебхуков
Вебхуки не отправляются повторно: если ваш сервер был недоступен, события за это время потеряны для вебхука, но не для журнала. Схема догона простая: запоминайте wapi.message_id обработанных событий — это id в журнале — и после простоя запрашивайте входящие с запасом по времени:
def catch_up(since_iso, seen_ids):
"""Входящие с момента since_iso, которые не пришли вебхуком, от старых к новым."""
missed = [m for m in iter_messages({"since": since_iso, "direction": "in"}) if m["id"] not in seen_ids]
return list(reversed(missed))Хранение: без срока, файлы и тексты
Тексты хранятся без срока и без ограничения объёма — автоматически ничего не удаляется. Файлы лежат в хранилище в Казахстане (Yandex Cloud), зашифрованы, есть резервная копия. 5 ГБ на аккаунт — сумма по всем номерам — бесплатно, сверх — 1 000 ₸ в месяц за каждые 5 ГБ; пакеты крупнее дешевле: 50 ГБ — 7 900 ₸, 200 ГБ — 19 900 ₸, самый выгодный набор выбираем сами. Списываем с баланса по дням, по фактическому объёму. Сколько занято, видно в кабинете и в GET /storage — см. документацию.
В кабинете, в разделе «Хранилище», можно поставить потолок «не хранить больше N ГБ»: сверх него новые файлы просто не сохраняются, доплат нет. Там же включается автоудаление файлов старше 30, 90, 180 или 365 дней — по умолчанию оно выключено. Через API файлы удаляются по одному — DELETE /media/{id} — или пачкой — POST /media/delete. Удалённый файл сразу пропадает из хранилища, резервная копия — через 30 дней. Если удалить номер, его чаты и файлы хранятся ещё 30 дней, потом удаляются.
Если место сверх 5 ГБ не оплачено, новые файлы сверх 5 ГБ не сохраняются, а читать и выгружать сохранённое можно. Мы предупреждаем в кабинете, в WhatsApp и событием storage.warning на вебхук, а через 30 дней неуплаты удаляем самые старые файлы сверх 5 ГБ.
Хранение переписки можно выключить в настройках аккаунта. Тогда у сообщений остаются время, направление, собеседник и статус, а text равен null, и новые файлы не сохраняются; уже сохранённые тексты при выключении стираются. Учтите: без текстов догон пропущенных вебхуков и выгрузка покажут только факт сообщения. Как это связано с требованиями к персональным данным — в гайде про локализацию данных в РК (это не юридическая консультация).
История, чаты и выгрузки входят в стоимость номера — 9 900 ₸ в месяц, файлы — 5 ГБ на аккаунт бесплатно, см. цены. Начать можно с регистрации.
Частые вопросы
Можно ли получить переписку, которая была до подключения к wapi.kz?
Нет. Журнал ведётся с момента подключения номера; старые чаты остаются в WhatsApp на телефоне.
Сколько хранится история сообщений?
Без срока. Тексты хранятся без ограничений, файлы — тоже без срока, пока помещаются в бесплатные 5 ГБ на аккаунт или оплачено место сверх них. Старые файлы можно удалять автоматически — это включается в кабинете.
Попадают ли в историю сообщения, которые менеджер пишет с телефона?
Да, с пометкой source: "phone". Это же событие приходит в вебхук как message.out.
Как выгрузить больше 100 000 сообщений?
Частями: разбейте период на недели или месяцы и сделайте несколько запросов GET /export с since и until.
Почему в CSV перед некоторыми сообщениями стоит апостроф?
Это защита от формул: текст, который начинается с =, +, - или @, Excel мог бы выполнить как формулу. В JSON-выгрузке текст приходит без изменений.
Подключите WhatsApp к своим системам
9 900 ₸ в месяц за номер, без платы за сообщения. Сервер в Казахстане, оплата Kaspi или счётом на ТОО/ИП. Подключение — около 5 минут.