SMeet Bot API
Operations
What to watch and what to do when something goes wrong. The two situations that look alike but are not, a technical pause of the platform and an organisation switching bots off, have a section each.
Keeping secrets#
A bot has two secrets: the token its program uses to call the Bot API, and in webhook mode the webhook secret SMeet uses to sign deliveries.
- Keep them in environment variables or a secret store. A
.envfile must be readable only by the service user and must never reach git. - Send the token only in the
Authorizationheader. Never put it into a URL, a log, a chat, a screenshot or a support ticket. The examples log update ids, chat ids and error codes, never the token, the secret or message text. - SMeet shows each secret once and keeps only a hash of the token and an encrypted copy of the webhook secret. The API never returns either of them, and the token is never sent to your webhook.
- Rotating the token: issue a new one on the protected screen of My bots; the old one stops at once, so put the new one into the program and restart it right away.
- Rotating the webhook secret: save the webhook again on the Connection screen; every time webhook mode is set, a new secret is created and shown once. Until your program has it, deliveries signed with the new secret fail your check, your
401pauses delivery and the updates wait (A paused endpoint). Put the new secret into the program, restart it and press Resume delivery; nothing is lost. - If a secret may have leaked, use the emergency reset: a new token, the long polling lease taken away and, in webhook mode, a new webhook secret, in one step.
Health checks#
- getMe answers even while the bot is paused and during a technical pause of the platform. Call it at start: it confirms the token and shows
status(active,paused_by_owner,suspended_by_admin) anddelivery_mode. - getDeliveryInfo is the one call for monitoring (Queue diagnostics). It never returns a secret, and it also answers during a technical pause, with
"maintenance": true. - My bots and the card of the bot in BotFather show one of four statuses:
| Status | Meaning |
|---|---|
| Working | The program is connected and nothing is stuck. |
| Not connected | Long polling: no token yet, or the program has never called. Webhook: nothing delivered yet and nothing waiting. |
| Delivery error | Long polling: updates are waiting and the program has not called for 2 minutes. Webhook: delivery is paused until you resume it, the circuit breaker is open, or the platform switched the webhook off. |
| Paused | The owner paused the bot, the platform suspended it, its owner left the organisation, or the organisation switched bots off. |
"Working" means only that the program is connected. It says nothing about whether its logic is right, and a webhook that refuses updates with 400, 413 or 422 gets no error status for that: watch the FAILED list and failed_update_count. In webhook mode SMeet BotFather also writes to you when delivery pauses or an update goes to FAILED (Notices to the owner).
Queue diagnostics#
{
"ok": true,
"result": {
"mode": "polling",
"config_version": "3",
"pending_update_count": 12,
"failed_update_count": 1,
"oldest_pending_date": "2026-09-26T09:58:10Z",
"confirmed_offset": "1840",
"lease": {"consumer_id": "poller-host1", "epoch": "7", "expires_at": "2026-09-26T10:01:00Z"},
"webhook": {"url": "", "state": "none", "pending_update_count": 12, "max_connections": 1},
"maintenance": false,
"retention_seconds": 604800
}
}| Field | What to watch |
|---|---|
pending_update_count | Updates waiting for confirmation. It grows when the program is down or does not confirm. At 10,000 updates or 50 MiB the queue is full, and people cannot write to the bot. |
oldest_pending_date | The oldest waiting update. When it is minutes old, the program is behind; after 7 days updates expire. |
failed_update_count | The size of the FAILED list; review it in My bots. |
confirmed_offset | Long polling: the server's position. Compare it with what your program stored. |
lease | Long polling: who holds the lease now. Missing means nobody is polling. A consumer_id you do not expect means another copy is running. |
webhook | Webhook mode: state, next_attempt_date and last_error (Webhook problems). |
maintenance | true during a technical pause of the platform. |
If the bot is silent#
- Is the program running? Its log should show
Running as @<address>and no errors. - Does the token work? getMe with the token.
401 INVALID_TOKENmeans it was revoked or mistyped: issue a new one in My bots. - Is the bot running? getMe
status:paused_by_ownermeans/resumein BotFather;suspended_by_adminmeans the platform suspended the bot or its owner left the organisation: contact support. - Is the mode right? getMe
delivery_mode. A polling program against a bot in webhook mode gets409 DELIVERY_MODE_CONFLICT: switch the bot to long polling, or run your webhook receiver. - Is another copy receiving? getDeliveryInfo
lease.consumer_id.CONSUMER_CONFLICTthat does not go away means a second copy holds the lease (A stuck lease). - Did the person press Start? Nothing reaches the bot before Start or after Stop. In an organisation, the person must still be a member.
- Is it the platform or the organisation?
503 BOTS_MAINTENANCEis a technical pause: wait.403 SPACE_BOTS_DISABLEDmeans the organisation switched bots off. - Webhook: getWebhookInfo
stateandlast_error(Webhook problems), and the FAILED list.pausedmeans your endpoint gave an answer only you can fix; fix it and press Resume delivery in My bots. - Replies fail:
403 BOT_STOPPED_OR_BLOCKED(the person stopped the bot),400 INVALID_REQUESTwitherror.field,429 RATE_LIMITED. Keep therequest_idof a failing call.
Errors by HTTP status#
| Status | Code | What it means | What to do |
|---|---|---|---|
| 401 | INVALID_TOKEN | The token is missing, mistyped or revoked, or the bot was deleted. | Issue a new token in My bots. Do not retry with the old one. |
| 403 | BOT_SUSPENDED | The owner paused the bot, the platform suspended it, or its owner left the organisation. | /resume, or contact support. Retrying does not help. |
| 403 | SPACE_BOTS_DISABLED | The organisation switched bots off. | Stop; ask the organisation's administrators. |
| 403 | BOT_STOPPED_OR_BLOCKED | The person stopped or blocked the bot, or left the space. | Stop writing to that chat and delete its subscriptions. |
| 404 | BOT_NOT_FOUND | The bot was deleted while the request was on its way. | Stop: the token no longer works. |
| 409 | CONSUMER_CONFLICT | Another copy holds the lease, or a long poll is still waiting. | Wait retry_after and ask again with the same consumer_id, epoch and offset. |
| 409 | DELIVERY_MODE_CONFLICT | getUpdates while the bot is in webhook mode. | Switch the mode in My bots, or receive by webhook. |
| 409 | CURSOR_BEHIND | offset is below the confirmed position. | Continue from confirmed_offset in the error. |
| 409 | IDEMPOTENCY_CONFLICT | The key was used with other parameters. | A new operation needs a new key. |
| 409 | FILE_NOT_READY | The file is still being checked. | Repeat after retry_after with the same key. |
| 413 | PAYLOAD_TOO_LARGE | The request body or the file is larger than allowed. limit gives the largest accepted size in bytes; a 413 from the proxy in front of the API may come without it. | Send less; repeating the same body does not help. |
| 429 | RATE_LIMITED | Too many requests, also from the proxy for one IP address. | Wait retry_after, repeat with the same key. |
| 429 | QUOTA_EXCEEDED | The daily upload volume is used up. | Wait retry_after, until midnight UTC. |
| 503 | TEMPORARILY_UNAVAILABLE | A transient failure. | Repeat after retry_after with the same key, with a growing pause. |
| 503 | BOTS_MAINTENANCE | A technical pause of the platform. | Repeat after retry_after with the same key and the same offset (below). |
The full list is in the table of error codes.
Webhook problems#
getWebhookInfo shows the state of your endpoint:
state | Meaning |
|---|---|
active | Deliveries run. |
paused | Your endpoint gave an answer only you can fix, and delivery waits for you; last_error names the cause. Fix it and press Resume delivery in My bots (A paused endpoint). |
circuit_open | Three updates in a row used up their error budget; SMeet waits and then probes. next_attempt_date is the next probe (Circuit breaker). |
disabled_by_platform | The platform switched this bot's webhook off. The queue waits; switch to long polling or contact support. |
none | The bot is in long polling mode. |
next_attempt_date also shows a wait after a failed connection or a 429. last_error.code tells what happened last:
last_error.code | What happened | What SMeet does | What to check |
|---|---|---|---|
HTTP_401, HTTP_403 | Your endpoint refused the delivery. | Pauses until you resume. | The secret your program checks the signature with. |
HTTP_404, HTTP_410 | There is nothing at the address. | Pauses until you resume. | The webhook address and your routing. |
REDIRECT_NOT_FOLLOWED | Your endpoint answered with a redirect. | Pauses until you resume. | Save the final address in My bots. |
TLS_CERTIFICATE_ERROR | The certificate is not valid for this address. | Pauses until you resume. | Expiry, the name on the certificate, the chain. |
ADDRESS_FORBIDDEN | The host now resolves only to private or reserved addresses. | Pauses until you resume. | Public DNS records of the host. |
HTTP_<status> of another status, for example HTTP_405 | An answer a webhook does not accept. | Pauses until you resume. | Answer 2xx, or 400, 413, 422 for an update you refuse. |
HTTP_400, HTTP_413, HTTP_422 | Your program refused this update. | Moves it to FAILED and goes on. | Your program's log; the FAILED list. |
HTTP_429 | Your endpoint asked to slow down. | Waits Retry-After. | Your own rate limits. |
HTTP_408, HTTP_5xx such as HTTP_503, TIMEOUT, NO_RESPONSE, IO_ERROR | An error, no answer within 10 seconds, or a connection broken after the request was sent. | Retries within the update's budget. | Answer faster: store, reply 200, work afterwards. |
DNS_ERROR, CONNECT_ERROR, CONNECT_TIMEOUT, TLS_ERROR | The request never reached your program. | Waits and retries, 5 seconds up to 5 minutes. | That the host resolves, the port is open, TLS works. |
DISABLED_BY_PLATFORM | The platform switched the webhook off. | Nothing until the platform allows it. | Contact support. |
New codes may appear; treat an unknown one by the state that comes with it.
Technical pause: BOTS_MAINTENANCE#
The operators of SMeet can pause the whole bot platform, for example while a release is rolled back or during an incident. It is a pause, not a revocation: everything is kept.
During the pause:
- data methods of the Bot API answer
503 BOTS_MAINTENANCEwithretry_after(30 seconds) and the sameRetry-Afterheader; waiting long polls end with this error; - getMe, getMyCommands, getDeliveryInfo and getWebhookInfo still answer, and getDeliveryInfo shows
"maintenance": true; - a webhook delivery already under way finishes and counts; no new deliveries or file checks start;
- people's messages to bots are not sent: their app says that bots are paused for maintenance and keeps the text in the input field;
- the queue, FAILED updates, Idempotency-Keys, offsets, the long polling lease and Start permissions are kept;
- Stop, Block, leaving a space and deletions still take effect and still cancel what they cancel;
- the retention clock keeps running: an update whose 7 days end during the pause expires.
What your program does:
- wait
retry_after, then repeat the same request with the same key and the same offset; never drop or reset the saved position because of this error; - after the pause, getUpdates with the same
consumer_id,epochandoffsetcontinues under the same lease. If the pause outlasted the lease, the call gets a new one, and the saved offset still confirms, because nobody took the lease in between. Nothing is lost; - a webhook receiver does nothing: deliveries resume by themselves, in order.
The client of the examples does exactly this: BOTS_MAINTENANCE is one of the codes smeet.py retries with the same body and key.
An organisation revokes its permission#
An owner or administrator of an organisation can switch Running bots off (Organisations and permissions). This is a decision about the organisation's data, not a technical pause, and it cancels:
- every bot of the organisation gets
403 SPACE_BOTS_DISABLED, and people see that bots are turned off in this organisation; - every undelivered update of these bots is cancelled, FAILED updates included; they cannot be replayed and do not come back when running is switched on again;
- chats and Start permissions stay.
What your program does: stop calling in a loop (the examples exit with code 2 and a message), and talk to the organisation's administrators. When running is switched on again, start the program: it receives only what happens from then on, and the cancelled range is reported as skipped with the reason cancelled.
| Technical pause | Organisation revokes its permission | |
|---|---|---|
| Who | SMeet operators | The organisation's owners or administrators |
| Bot API answer | 503 BOTS_MAINTENANCE with retry_after | 403 SPACE_BOTS_DISABLED |
| Waiting updates | Kept, delivered afterwards | Cancelled for good |
| FAILED updates | Kept, can be replayed afterwards | Cancelled, cannot be replayed |
| Idempotency-Keys and offsets | Kept | Kept, but nothing is left to deliver |
| Your program | Retries the same request | Stops |
Pause and suspension#
| Situation | Bot API | Waiting updates | How it ends |
|---|---|---|---|
| The owner paused the bot | 403 BOT_SUSPENDED; getMe shows paused_by_owner | Kept, delivered after /resume within the 7 days | /resume in BotFather |
| The platform suspended the bot | 403 BOT_SUSPENDED; getMe shows suspended_by_admin | Cancelled; FAILED kept | The platform lifts the suspension |
| The owner left the organisation or their account was disabled | 403 BOT_SUSPENDED; getMe shows suspended_by_admin | Cancelled; FAILED kept | Platform support gives the bot back to the owner |
A stuck lease#
When a long polling program dies, its lease lives on for up to 60 seconds after the server's last answer to it, and a long poll it left open counts until its timeout. A restarted program that kept its epoch continues at once; one that lost it gets CONSUMER_CONFLICT until the lease expires. getDeliveryInfo shows the holder in lease.
If you are sure no other copy runs, press Reset the connection on the Connection screen of My bots. The old epoch then becomes foreign: a copy that still sends it confirms nothing with it, and the updates it had not confirmed come again. The next call gets a new lease from the confirmed position.
Getting help#
Write to support@scrile.com with the bot's address, the time, the method and the request_id of a failing call (it is in every error body and in the X-Request-Id header). Never send the token or the webhook secret.