Updates (Long Polling)
Для разработчиков

Updates (Long Polling)

/api/v1/updates работает в стиле Telegram Bot API getUpdates: вы передаёте offset — id последнего обработанного события, сервер отдаёт события новее. Если новых нет и задан timeout, запрос ждёт, пока событие появится или истечёт таймаут. События читаются из базы: журнал событий тикетов и сообщения.

GET/api/v1/updates

Получить новые события

  • offset — id последнего обработанного события. Первый запрос делайте с текущим временем в микросекундах: так вы получите события с этого момента. 0 — вся история проекта с самого начала; передавайте его, только если вам нужно перечитать всё
  • timeout — сколько секунд ждать новых событий (0–30, по умолчанию 0: ответить сразу)
  • types — фильтр через запятую, например ticket.created,message.created

События идут от старых к новым. id события — его время в микросекундах (UTC, целое число). События, записанные одновременно (например, сообщение и запись о назначении из одной операции), получают идущие подряд id, поэтому id уникальны и только растут. У события один и тот же id при любом types. В ответе до 100 событий, иногда меньше, даже если новые ещё есть: просто повторите запрос с новым offset, пока ответ не придёт пустым. Если за время ожидания ничего не пришло, ответ — {"updates": []}.

200 OK
{
  "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

poll.py
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

poll.mjs
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 в базе или файле, чтобы после перезапуска не получить старые события снова и не потерять новые.
Была ли страница полезной?