What does this solve?
Any internal tool needs an interface, and the interface is the front end, authorization, layout for the phone and persuading employees to install something. The Telegram bot captures everything at once: people already have the app, they’ve already logged in, notifications have already been configured. The internal report, application acceptance, approval button and alert from monitoring are collected using HTTP requests, without a single line of client code. The same calculation works for external clients: the path from the link to the first action is shorter than that of any site with registration.
How it works
Token and calls
The bot is created in @BotFather, which issues a token like <числовой id>:<строка>. Token - full access to the bot, it is enough to read correspondence and send messages on behalf of the bot; in case of leakage it only saves /revoke in the same BotFather. All methods are triggered by a regular HTTPS request to the address https://api.telegram.org/bot<токен>/<метод>, parameters are passed in a query line, JSON body or multipart when uploading files. The answer is always the same: {"ok": true, "result": ...} or {"ok": false, "error_code": ..., "description": ...} —the error description is textual and readable; it should be logged in its entirety.
Two ways to receive events
Long polling: method getUpdates holds the connection and sends new events. Processing confirmation - parameter offset, equal update_id of the last processed update plus one. While there is no confirmation, the same events will come again. Nothing public is needed outside, so the mode is convenient for development and for internal bots behind NAT.
Webhook: method setWebhook registers the address, and Telegram itself sends a POST there with an update. The requirements are strict - HTTPS with a valid certificate and one of ports 443, 80, 88 or 8443. Parameter secret_token causes Telegram to send the header X-Telegram-Bot-Api-Secret-Token, by which the receiver distinguishes a real call from an extraneous request to an open URL. Parameter allowed_updates limits the types of events and at the same time saves traffic. Two modes do not work at the same time: the enabled webhook disables getUpdates.
Update
The event arrives as an Update object. Inside is one of the fields: message, edited_message, channel_post, callback_query (pressing the inline button), inline_query, my_chat_member i chat_member (change of rights and entry/exit of participants), pre_checkout_query i successful_payment for payments, poll_answer for surveys. Field update_id grows monotonically and serves as a deduplication key: when delivered again, the number is the same.
Sending and editing
sendMessage with parse_mode in HTML or MarkdownV2. MarkdownV2 requires escaping a dozen special characters, and any period or hyphen from the user data causes the request to fail with a parsing error - in practice, HTML is more predictable. Media is sent using methods sendPhoto, sendDocument, sendMediaGroup. Separate force - editMessageText i editMessageReplyMarkup: The interface is redrawn on the spot, instead of cluttering the chat with new messages. After clicking the inline button, it is required answerCallbackQuery, otherwise the person who presses the button will have a waiting indicator spinning for several seconds.
Buttons and dialogue entry
The online keyboard is attached to the message, its buttons carry callback_data —up to 64 bytes. A short action key is placed there, and the state is stored on its side; An attempt to cram the entire payload into a button hits the limit at the very first long name. A regular reply keyboard replaces the user's input of commands. The contact and location request buttons receive phone number and coordinates only with explicit consent. Deep link t.me/<имя_бота>?start=<payload> sends an arbitrary label to the first message - this is how the Telegram user is associated with an entry in the internal system.
Files
Each downloaded file has file_id, through which it can be sent again without re-downloading. Called for downloading getFile, it gives the path, then the file is taken with a regular GET. Bot API restrictions: downloading - up to 20 MB, uploading - up to 50 MB. Both ceilings are removed by its own Bot API server, which is deployed next to its application.
Mini Apps and payments
Mini App - a web page that opens inside the client; she receives a signed block initData. The signature is verified on the server using the bot’s token, and without this verification, anyone will substitute someone else’s user ID. Payments are connected through the provider in BotFather: sendInvoice issues an invoice, pre_checkout_query requires quick confirmation from the bot, successful_payment arrives after payment. For digital goods there is an internal currency called Stars.
Limits
When the frequency is exceeded, error 429 comes with the field retry_after —number of seconds until next attempt. Documentation guidelines: about thirty messages per second in total and about twenty per minute per group. Sending to a large database without a queue with pauses runs into this problem with the first hundred recipients.
Useful scripts
- Monitoring alerts in the working group topic with the “hired to work” button, which edits the message itself.
- Accepting applications using a questionnaire consisting of several steps with recording the result in the database and a deep link to a card in CRM.
- Internal commands instead of access to the admin panel: a short report upon request for those who do not need a full account.
- Coordination: online confirmation and refusal buttons, the decision is written to the system, the message text is replaced with a summary with a name and time.
- Channel plus bot administrator for scheduled publication from a common content database.
Limitations
Bot cannot write first. Until the person clicked /start or did not add the bot to the group, sending is impossible. Mass mailing to phone numbers is not done using the Bot API.
In groups, privacy mode is enabled by default: the bot sees only commands, replies to its messages and mentions. Full access to group messages is given by disabling the mode in BotFather or administrator rights - and this is a deliberate extension of rights, and not a default setting.
The bot does not receive any history. Added to a group - the flow from this moment is visible, the past is inaccessible.
Phone and mail are received only by button pressed by the user. The user ID is permanent, the name and username change at any time - you can link records only by numeric id.
Updates are stored on the Telegram side for no longer than a day. A receiver that has been lying around longer loses events irretrievably, and they cannot be restored by a request.
With a webhook, the order of delivery is not guaranteed, and repetitions are possible. The handler must be idempotent update_id.
Formal framework: message length up to 4096 characters, media caption up to 1024, callback_data up to 64 bytes. A long report is cut into pieces on its side.
The user can block the bot in one click - then the sending returns 403 with a description of the blocking, and such recipients are marked, otherwise the mailing queue endlessly fills up dead chats.
The entire structure hangs on one platform and one token. This does not cancel the backup notification channel.
How to check the result
getMe is the first thing that is called: it responds, which means the token is alive and there is a network to the API.
getWebhookInfo is the main diagnostic method for a silent bot. The response shows the registered address, the number of unsorted updates (pending_update_count), time and text of the last delivery error. The growing queue and repeated error text indicate the reason directly: wrong certificate, wrong port, the receiver does not respond with 200.
The shipment is checked manually: curl with method sendMessage to your own chat. Passed - the problem is in the application logic, if not passed - in the token, network or chat rights.
The raw JSON of the update should be logged entirely before parsing. Half of the oddities are explained by the fact that the field that was not received was not what was expected: a forwarded message, editing an old one, an event about changing rights instead of text.
Deduplication is checked by re-submitting the same update to the endpoint: the result should remain the same in the database.
At the development stage, a webhook is launched through a tunnel to the local machine, and not by editing the combat address. Switching back and forth is done using methods setWebhook i deleteWebhook, and after each switch it is useful to look again getWebhookInfo.
The logs count codes 429 and 403 separately: the first means that a queue with pauses is needed, the second means that part of the recipient database is dead and needs to be cleaned.
The Mini App signature is tested by attacking itself: in initData The user ID is replaced, the request is sent to the server, the response must be a refusal.
