Updates (Long Polling)
/api/v1/updates работает в стиле Telegram Bot API getUpdates: вы передаёте offset — id последнего обработанного события, сервер отдаёт события новее. Если новых нет и задан timeout, запрос ждёт, пока событие появится или истечёт таймаут. События читаются из базы: журнал событий тикетов и сообщения.
/api/v1/updatesПолучить новые события
offset— id последнего обработанного события. Первый запрос делайте с текущим временем в микросекундах: так вы получите события с этого момента.0— вся история проекта с самого начала; передавайте его, только если вам нужно перечитать всёtimeout— сколько секунд ждать новых событий (0–30, по умолчанию 0: ответить сразу)types— фильтр через запятую, напримерticket.created,message.created
События идут от старых к новым. id события — его время в микросекундах (UTC, целое число). События, записанные одновременно (например, сообщение и запись о назначении из одной операции), получают идущие подряд id, поэтому id уникальны и только растут. У события один и тот же id при любом types. В ответе до 100 событий, иногда меньше, даже если новые ещё есть: просто повторите запрос с новым offset, пока ответ не придёт пустым. Если за время ожидания ничего не пришло, ответ — {"updates": []}.
{
"updates": [
{
"id": 1775470353123456,
"type": "ticket.created",
"timestamp": "2026-04-06T10:12:33.123456",
"data": {
"ticket_id": "8a3f...",
"actor_type": "system",
"actor_id": null,
"payload": { "status": "new", "priority": "normal", "contact_id": "44e1..." },
"ticket": { "id": "8a3f...", "status": "new", ... }
}
},
{
"id": 1775470353999000,
"type": "message.created",
"timestamp": "2026-04-06T10:12:33.999000",
"data": { "id": "f1...", "ticket_id": "8a3f...", "sender_type": "contact", "content": "...", "media": [], ... }
}
]
}Типы событий
message.created — каждое новое сообщение, включая внутренние заметки и системные сообщения. data — объект сообщения, как в списке сообщений тикета, вместе с вложениями в media.
ticket.* — записи журнала тикета: ticket.<тип записи>. data содержит ticket_id, actor_type, actor_id, payload записи и текущий объект ticket. Основные типы:
| ticket.created | тикет создан |
| ticket.status_change | статус изменён через PATCH или в дашборде |
| ticket.assigned | тикет назначен оператору |
| ticket.transfer | тикет переназначен другому оператору |
| ticket.force_take | оператор забрал тикет у другого |
| ticket.department_transfer | тикет передан в другой отдел |
| ticket.closed | тикет закрыт |
| ticket.reopened | тикет переоткрыт |
| ticket.rated | поставлена оценка |
| ticket.snoozed | тикет отложен |
| ticket.resumed | отложенный тикет вернулся в работу |
Бывают и служебные записи (например, ticket.system.operator_joined, ticket.tg_delivery_failed) — если они не нужны, задайте types. Названия отличаются от webhook-событий: переназначение здесь — ticket.transfer, в webhooks — ticket.assigned.
Только в webhooks, не в long polling: ticket.updated, message.edited, message.reaction.*, contact.*, operator.status.changed, visitor.reply_waiting, billing.* (см. Webhooks).
offset, каждое событие приходит ровно один раз, сколько бы их ни накопилось. Событие отдаётся, только когда всё, что было записано раньше него, уже сохранено: пока на сервере не завершилась более ранняя операция, событие может задержаться на несколько секунд (не больше 10). Ожидание с timeout прерывается любым изменением в проекте, но о нескольких служебных записях сервер не сообщает сразу — такие придут со следующим запросом, то есть с задержкой до timeout секунд.Клиент на Python
import time
import requests
API = "https://api.support.forestsnet.com/api/v1"
HEAD = {"Authorization": "Bearer sk_xxx"}
offset = int(time.time() * 1_000_000) # начать с текущего момента
while True:
r = requests.get(
f"{API}/updates",
headers=HEAD,
params={"offset": offset, "timeout": 25},
timeout=35,
)
if r.status_code != 200:
time.sleep(5)
continue
for ev in r.json()["updates"]:
print(ev["type"], ev["data"])
offset = max(offset, ev["id"])Клиент на Node.js
const API = "https://api.support.forestsnet.com/api/v1";
const HEAD = { Authorization: "Bearer sk_xxx" };
let offset = Date.now() * 1000; // начать с текущего момента
while (true) {
const url = new URL(API + "/updates");
url.searchParams.set("offset", String(offset));
url.searchParams.set("timeout", "25");
const r = await fetch(url, { headers: HEAD });
if (!r.ok) {
await new Promise((resolve) => setTimeout(resolve, 5000));
continue;
}
const { updates } = await r.json();
for (const ev of updates) {
console.log(ev.type, ev.data);
if (ev.id > offset) offset = ev.id;
}
}offset в базе или файле, чтобы после перезапуска не получить старые события снова и не потерять новые.
