QivunaDevelopers

Updates and long polling

Everything that happens to your bot arrives as an update. You receive updates either by long polling getUpdates or through a webhook — never both. At launch only getUpdates is available; webhooks open later.

Update types#

An Update is {"update_id": …, "<type>": {…}} with exactly one type:

TypeWhenDefault
messageA person writes in a private chat with the bot, or in a group where the privacy rules let the bot see it.yes
edited_messageSuch a message was edited and the bot would receive it now (the privacy rules are applied to the new text).yes
channel_post, edited_channel_postA post in a channel the bot was added to, or its edit.yes
callback_querySomeone pressed one of the bot's buttons.yes
my_chat_memberThe bot was added, its rights changed, it was removed or left, or its grant was suspended because the group became encrypted; in encrypted groups, attached or detached.yes
bot_startedA person pressed Start (with start_param if the link had one).yes
bot_blockedA person stopped or blocked the bot.yes
encrypted_messageEncrypted bots: an end-to-end encrypted message or button press (see Encrypted bots).yes
chat_memberA member of a group joined, left, was banned or unbanned, muted or unmuted.no — opt in

chat_member is delivered only to bots that list it in allowed_updates and either read all messages (privacy off with can_read_all) or hold can_restrict_members or can_ban_members in that group. Groups only.

Set the types you want with allowed_updates on getUpdates or setWebhook. The list is stored on the bot and applies until you change it; [] restores the default set. Handle unknown types gracefully: new ones may be added.

Examples#

A command in a group

{"update_id": 1043,
 "message": {"message_id": 4503599627502817, "date": 1759590000,
   "chat": {"id": -4503600130020245, "type": "group", "title": "Runners TLV", "username": "runnerstlv", "is_e2ee": false},
   "from": {"id": 4503600212045337, "is_bot": false, "first_name": "Dana", "username": "dana"},
   "text": "/poll@pollbot Lunch? Pizza / Sushi",
   "entities": [{"type": "bot_command", "offset": 0, "length": 13}],
   "command": {"name": "poll", "args": "Lunch? Pizza / Sushi", "addressed_to_me": true}}}

The bot was added to a group

{"update_id": 1001,
 "my_chat_member": {"chat": {"id": -4503600130020245, "type": "group", "title": "Runners TLV", "is_e2ee": false},
   "from": {"id": 4503600212045337, "is_bot": false, "first_name": "Dana"},
   "date": 1759589000, "old_status": "left", "new_status": "member",
   "rights": {"can_send": true, "can_send_media": true}, "mode": "server",
   "consent_receipt_id": "01jb2f6x9q0v7m4c8d1e5k3h2n"}}

A person pressed Start

{"update_id": 1002,
 "bot_started": {"from": {"id": 4503600212045337, "is_bot": false, "first_name": "Dana"},
   "chat": {"id": 4503600212045337, "type": "private", "first_name": "Dana", "is_e2ee": false},
   "start_param": "spring-campaign", "date": 1759589100}}

A button press

{"update_id": 1050,
 "callback_query": {"id": "01JB2G0M3K6V1S8T4W2Q9R5X7Y",
   "from": {"id": 4503600212045337, "is_bot": false, "first_name": "Dana"},
   "message": {"message_id": 4503599627502900, "date": 1759590100,
     "chat": {"id": -4503600130020245, "type": "group", "title": "Runners TLV", "is_e2ee": false},
     "from": {"id": 7123456789, "is_bot": true, "first_name": "Poll", "username": "pollbot"},
     "text": "Lunch?",
     "reply_markup": {"inline_keyboard": [[{"text": "Pizza", "callback_data": "v:pizza"}, {"text": "Sushi", "callback_data": "v:sushi"}]]}},
   "data": "v:pizza"}}

Rights in my_chat_member list only the rights that are on. Names never include a phone number; see what a bot learns about people.

Long polling with getUpdates#

POST https://bots.qivuna.com/v1/getUpdates
{"offset": 1044, "limit": 100, "timeout": 25}
  1. If a webhook is set: 409 conflict (“A webhook is active; call deleteWebhook first”).
  2. If another getUpdates of this bot is running: 409 conflict. Run exactly one poller per bot.
  3. offset acknowledges every update with a lower update_id: they are deleted.
  4. Up to limit waiting updates are returned at once, oldest first.
  5. If none are waiting and timeout > 0, the call waits up to timeout seconds (25 at most) for the first one. If the platform has no free long-poll slot, or your bots (all bots of one owner together) already have 4 polls waiting, it answers [] at once with Retry-After: 2: wait two seconds and poll again.

Updates you received are not deleted until a later call acknowledges them. If your process dies before acknowledging, the next call returns them again — that is how nothing gets lost, and also why the same update can reach you twice.

Keep your HTTP client's read timeout above the long-poll timeout (for timeout: 25, at least 30 seconds).

Ordering#

Expiry and limits#

Handle updates at least once#

Delivery is at least once. The same update can arrive again after a crash, a lost response or a webhook retry, with the same update_id. In rare cases (a message with files whose delivery was interrupted part-way) the same message can arrive as two updates with different update_ids; compare message_id if a repeat would matter.

What is never delivered#