Updates (long polling)
For Developers

Updates (Long Polling)

/api/v1/updates works like the Telegram Bot API getUpdates: you pass offset, the id of the last event you processed, and the server returns newer events. If there are none and timeout is set, the request waits until an event appears or the timeout runs out. Events are read from the database: the tickets’ event log and messages.

GET/api/v1/updates

Get new events

  • offset: the id of the last event you processed. Make the first request with the current time in microseconds to get events from that moment on. 0 means the project’s whole history from the very beginning; pass it only if you want to replay everything
  • timeout: how many seconds to wait for new events (0–30, default 0: answer at once)
  • types: a comma-separated filter, for example ticket.created,message.created

Events come oldest first. An event’s id is its time in microseconds (UTC, an integer). Events written at the same moment (say, a message and the assignment entry from one action) get consecutive ids, so ids are unique and only grow. An event has the same id whatever types you pass. A response holds up to 100 events, sometimes fewer even when more are waiting: just repeat the request with the new offset until a response comes back empty. If nothing arrives while waiting, the response is {"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": [], ... }
    }
  ]
}

Event types

message.created: every new message, including internal notes and system messages. data is the message object, the same as in the ticket’s message list, attachments in media included.

ticket.*: entries of the ticket’s event log, ticket.<entry type>. data holds ticket_id, actor_type, actor_id, the entry’s payload and the current ticket object. The main types:

ticket.createda ticket was created
ticket.status_changethe status changed through PATCH or in the dashboard
ticket.assignedthe ticket was assigned to an operator
ticket.transferthe ticket was reassigned to another operator
ticket.force_takean operator took the ticket over from another
ticket.department_transferthe ticket moved to another department
ticket.closedthe ticket was closed
ticket.reopenedthe ticket was reopened
ticket.rateda rating was left
ticket.snoozedthe ticket was snoozed
ticket.resumeda snoozed ticket came back

There are service entries too (for example ticket.system.operator_joined, ticket.tg_delivery_failed); set types if you don’t need them. The names differ from webhook events: a reassignment is ticket.transfer here and ticket.assigned in webhooks.

Only in webhooks, not in long polling: ticket.updated, message.edited, message.reaction.*, contact.*, operator.status.changed, visitor.reply_waiting, billing.* (see Webhooks).

Follow offset and you get every event exactly once, however many piled up. An event is handed out only once everything written before it is saved: while an earlier operation is still running on the server, an event can wait a few seconds (10 at most). A wait with timeout ends on any change in the project, but a few service entries aren’t announced right away; those come with the next request, up to timeout seconds late.

A Python client

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)  # start from now

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"])

A Node.js client

poll.mjs
const API = "https://api.support.forestsnet.com/api/v1";
const HEAD = { Authorization: "Bearer sk_xxx" };
let offset = Date.now() * 1000; // start from now

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;
  }
}
Store the last offset in a database or a file so that after a restart you neither get old events again nor miss new ones.
Was this page helpful?