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.
/api/v1/updatesGet 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.0means the project’s whole history from the very beginning; pass it only if you want to replay everythingtimeout: how many seconds to wait for new events (0–30, default 0: answer at once)types: a comma-separated filter, for exampleticket.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": []}.
{
"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.created | a ticket was created |
| ticket.status_change | the status changed through PATCH or in the dashboard |
| ticket.assigned | the ticket was assigned to an operator |
| ticket.transfer | the ticket was reassigned to another operator |
| ticket.force_take | an operator took the ticket over from another |
| ticket.department_transfer | the ticket moved to another department |
| ticket.closed | the ticket was closed |
| ticket.reopened | the ticket was reopened |
| ticket.rated | a rating was left |
| ticket.snoozed | the ticket was snoozed |
| ticket.resumed | a 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).
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
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
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;
}
}offset in a database or a file so that after a restart you neither get old events again nor miss new ones.
