Skip to main content

Receiving Bot messages and events: polling and webhooks

Two mutually exclusive ways to receive MPChat updates: getUpdates long polling for development and setWebhook for production, with secret_token validation.

They are mutually exclusive. While a webhook is enabled, getUpdates returns 409. Use polling for development, webhook for production.


Overview

Pick one delivery mode. Polling actively pulls; webhook gets pushed. setWebhook and deleteWebhook are high-risk controlled operations — do not run them casually against a production bot.

getUpdates parameters

  • offset — when using a cursor, pass the highest update_id from the previous batch plus one.

  • limit — 1 to 100, default 100.

  • timeout — 0 to 50 seconds of long polling.

  • allowed_updates — type filter, e.g. ["message", "callback_query"].

A successful getUpdates consumes only the batch it returned. Persist the returned updates before further processing. Undelivered ordinary updates are kept for up to 24 hours. A callback_query can be delivered for up to 150 seconds, and the separate window for answerCallbackQuery is 300 seconds.

curl --silent --show-error "https://call.mp.net/bot/getUpdates" -X POST \
-H "Authorization: Bearer ${MPCHAT_BOT_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"offset":0,"timeout":30,"limit":50,"allowed_updates":["message","callback_query"]}'

Configuring a webhook

curl --silent --show-error "https://call.mp.net/bot/setWebhook" -X POST \
-H "Authorization: Bearer ${MPCHAT_BOT_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"url":"https://example.com/mp-bot/webhook","secret_token":"YOUR_WEBHOOK_SECRET","allowed_updates":["message","callback_query"]}'
  • url — required, must be HTTPS. An empty string is equivalent to deleteWebhook.

  • secret_token — optional; when set, requests carry the X-MpChat-Bot-Api-Secret-Token header.

  • max_connections, allowed_updates — optional tuning and type filter.

Webhook URL changes preserve pending updates, retry state and deadlines. Use drop_pending_updates: true only to discard updates accepted before the switch. max_connections defaults to 40 (range 1–100) across service instances.

How allowed_updates works

  • Omit it to keep the previous filter.

  • Pass [] to use the default set, which excludes chat_member, message_reaction, and message_reaction_count.

  • Pass a non-empty list to receive the specified types, for example ["message", "callback_query"].

This applies to polling and webhook registration. Changes apply to new updates and do not retroactively filter the existing queue.

Webhook probes and delivery

The registration probe can be an empty JSON object ({}). When you configure secret_token, both the probe and real updates carry X-MpChat-Bot-Api-Secret-Token.

Verify the header first and return 2xx for an authenticated probe. For an update, persist it or enqueue it durably before returning 2xx. Failed deliveries can be retried. Deduplicate with update_id per bot so a repeat delivery does not repeat the business action.

Use the same secret value in registration and on the receiver. The handler below assumes a JSON parser and a durable queue with a unique key per bot and update ID.

async function handleWebhook(req, res) {
const secret = process.env.WEBHOOK_SECRET;
const incoming = req.headers["x-mpchat-bot-api-secret-token"];
if (!secret || incoming !== secret) return res.status(401).end();

const update = req.body;
if (!update || typeof update !== "object" || Array.isArray(update)) {
return res.status(400).end();
}
if (Object.keys(update).length === 0) return res.status(204).end();
if (!Number.isSafeInteger(update.update_id)) return res.status(400).end();

try {
await enqueueUpdateOnce(update);
return res.status(204).end();
} catch {
return res.status(503).end();
}
}

Choosing a mode

  • Local development: getUpdates; confirm no webhook with getWebhookInfo.

  • Production: webhook + secret_token validation.

  • Switching: verify in a test environment before running setWebhook against the target bot.

Related

When supported group events are enabled, joins are represented as message.new_chat_members, leaves as message.left_chat_member, and pins as message.pinned_message when visible to the bot. Kafka-sourced group events and join-request actions are pre-release.

Did this answer your question?