Bot API reference
Every method of Bot API 1.0: 37 methods, their parameters, limits and the errors each one can answer.
POST https://bots.qivuna.com/v1/<method>
Authorization: Bot <token>
Content-Type: application/json
Methods are case-sensitive camelCase. Send parameters as a JSON object (or multipart/form-data for the three file uploads).
Read methods marked GET may also be called with GET and a query string. Every answer is
{"ok": true, "result": …} or {"ok": false, "error": {…}, "request_id": …} — see Errors.
Object types are on Types.
404 not_found, 400 token_in_url, 413 payload_too_large, 503 platform_paused, 401 unauthorized, 401 token_revoked, 403 bot_suspended, 429 rate_limited, 500 internal. Methods marked write can also answer
403 bot_restricted, 409 idempotency_mismatch, 409 idempotency_in_progress.Basics#
getMe#
Who the bot is, and its current state.
No parameters.
Result: BotInfo
id is the bot number as an integer (7000000001–7999999999). Bot accounts are the only ids that are never pseudonymised.
tier is new for 14 days, then standard, or verified with the blue check. Rate limits follow it.
Updates#
getUpdates#
Long-polls for updates. See Updates and long polling.
| Parameter | Type | Required | Notes |
|---|---|---|---|
offset | Integer | No | Acknowledge every update with a lower update_id (they are deleted). Pass the last update_id you handled + 1. Values ≤ 0 are ignored. |
limit | Integer | No | 1–100, default 100. |
timeout | Integer | No | Seconds to wait when nothing is queued: 0–25, default 0 (answer at once). |
allowed_updates | Array of String | No | Update types to receive. Stored on the bot and used by later calls and by webhooks. [] restores the default set (everything except chat_member). |
Result: Array of Update
One getUpdates per bot at a time; a second one gets 409 conflict. While a webhook is set, 409 conflict (“call deleteWebhook first”).
When the platform has no free long-poll slot, the answer is [] at once with the header Retry-After: 2.
Updates are kept 24 hours, at most 10,000 per bot (the oldest are dropped beyond that).
Returned updates are deleted only when a later call acknowledges them with offset.
Errors: 400 bad_request, 409 conflict.
Messages#
sendMessage#
Sends a text message to a person who started the bot, a group or a channel. Encrypted bots send envelopes instead.
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
text | String | One of | 1–4096 characters. Exactly one of text and envelope. |
entities | Array of MessageEntity | No | At most 100. Offsets and lengths in UTF-16 code units. Not together with parse_mode. |
parse_mode | String | No | Only "HTML". See Text and formatting. |
reply_to_message_id | Integer | No | A message of the same chat. |
disable_notification | Boolean | No | Deliver without a push. |
protect_content | Boolean | No | Apps disable forwarding, saving and copying of this message. |
reply_markup | InlineKeyboardMarkup | No | An inline keyboard (object or JSON string). See Keyboards. |
envelope | String | One of | Encrypted bots only: an s2 envelope, at most 20,000 characters. Nothing that is plaintext (text, entities, parse_mode, reply_markup) may travel next to it. |
reply_token | String | No | With envelope, in an encrypted group: the token from the encrypted_message update. Refused in private chats. |
Result: Message. A reply in an encrypted group returns {inbox_id, date, chat}: it goes to the person's inbox, not into the group.
Groups need can_send, channels can_post. A group switched to “only admins can send” refuses bots with 403 forbidden_right (can_send).
Private chats: only people who pressed Start and did not stop the bot. Messages the person did not ask for in the last 24 hours count against the daily unsolicited cap.
Counts against: messages per second per bot, 1 a second per chat (burst 3), the per-room minute limit, and the platform ceiling. See Rate limits.
Errors: 400 bad_request, 404 chat_not_found, 404 message_not_found, 403 user_not_started, 403 user_blocked_bot, 403 unsolicited_limit, 403 forbidden_right, 403 e2ee_required, 403 e2ee_unsupported, 403 reply_window_closed, 409 key_not_active, 410 room_closed, 422 invalid_entities, 422 invalid_markup, 422 content_blocked, 429 rate_limited.
editMessageText#
Replaces the text of one of the bot's own messages.
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
message_id | Integer | Yes | A message id as the bot received it. |
text | String | One of | 1–4096 characters. |
entities | Array of MessageEntity | No | Or parse_mode. |
parse_mode | String | No | "HTML". |
reply_markup | InlineKeyboardMarkup | No | Omitting it removes the keyboard (as in Telegram). Send the keyboard again to keep it. |
envelope | String | One of | Encrypted bots, private chats only: replaces an own end-to-end encrypted message. |
Result: Message
Own messages only, at most 48 hours old (422 message_too_old). Own posts in channels are always editable; can_edit_posts is not needed for them.
In an encrypted bot's private chat, text edits are refused with 403 e2ee_required; edit with envelope.
Errors: 400 bad_request, 404 chat_not_found, 404 message_not_found, 422 message_too_old, 403 user_blocked_bot, 403 e2ee_required, 403 e2ee_unsupported, 409 key_not_active, 410 room_closed, 422 invalid_entities, 422 invalid_markup, 422 content_blocked.
editMessageReplyMarkup#
Replaces (or removes) the inline keyboard of one of the bot's own messages, at any age.
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
message_id | Integer | Yes | A message id as the bot received it. |
reply_markup | InlineKeyboardMarkup | No | The new keyboard. Omitted or {"inline_keyboard": []}: the keyboard is removed. |
Result: Message
Every keyboard write gives the message a new keyboard revision: presses on an older copy of the keyboard are refused in the app, so a stale button can never reach the bot.
Errors: 400 bad_request, 404 chat_not_found, 404 message_not_found, 403 user_blocked_bot, 403 e2ee_required, 403 e2ee_unsupported, 410 room_closed, 422 invalid_markup, 422 content_blocked.
deleteMessage#
Deletes one of the bot's own messages (any age), or someone else's message in a group.
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
message_id | Integer | Yes | A message id as the bot received it. |
Result: true
Others' messages: groups only, the grant needs can_delete_messages, at most 48 hours old, never a message of the owner, an admin or a bot (403 target_protected). It is the app's “delete for everyone”: attachments are removed too.
Every delete of others' messages is written to the bot's audit log; the room sees at most one “deleted a message” line per bot per 10 minutes.
Encrypted bots may delete their own end-to-end encrypted messages in private chats.
Errors: 400 bad_request, 404 chat_not_found, 404 message_not_found, 403 forbidden_right, 403 target_protected, 422 message_too_old, 403 e2ee_unsupported, 410 room_closed, 429 rate_limited.
Chats#
getChat#
Information about a chat the bot can see.
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
Result: Chat, plus description (if set) and member_count for groups and channels.
Works for a grant suspended when the group became encrypted, and for an encrypted group the bot is attached to (is_e2ee: true).
A private chat is visible only after the person started the bot.
Errors: 400 bad_request, 404 chat_not_found.
leaveChat#
Leaves a group or channel.
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
Result: true
The grant ends, the room gets an event line, the bot gets my_chat_member. In an encrypted group this detaches the bot.
A private chat cannot be left (400 chat_id); the person stops the bot.
Errors: 400 bad_request, 404 chat_not_found.
getChatMember#
One member's status in a group (channels: only the owner and admins).
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
user_id | Integer | Yes | A person's id as the bot received it. |
Result: ChatMember
Someone who is not in the group is shown as {id, is_bot: false, first_name: "Qivuna user"} with status left. In channels, anyone but the owner and admins is 404 user_not_found: the subscriber list is never shown to bots.
Allowed while the bot is restricted (a read).
Errors: 400 bad_request, 404 chat_not_found, 404 user_not_found, 403 e2ee_unsupported.
getChatAdministrators#
The owner and admins of a group or channel (people only, at most 100).
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
Result: Array of ChatMember
Admins who left, and accounts that are not active, are left out.
Errors: 400 bad_request, 404 chat_not_found, 403 e2ee_unsupported.
getChatMemberCount#
How many members a group (or subscribers a channel) has.
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
Result: Integer
Errors: 400 bad_request, 404 chat_not_found, 403 e2ee_unsupported.
Keyboards#
answerCallbackQuery#
Answers a button press: a short toast or an alert in the presser's app.
| Parameter | Type | Required | Notes |
|---|---|---|---|
callback_query_id | String | Yes | From the callback_query (or encrypted_message) update. |
text | String | No | At most 200 characters. Empty: the spinner just stops. |
show_alert | Boolean | No | An alert instead of a toast. |
envelope | String | No | Encrypted presses: the answer sealed to the presser. text and show_alert then go inside it. |
Result: true
Once per press, within 15 minutes. The app gives up waiting after 15 seconds, so answer fast; a late answer is dropped quietly.
Errors: 400 bad_request, 404 callback_not_found, 403 e2ee_required, 409 key_not_active.
Commands and profile#
setMyCommands#
Sets the command list of one scope and language. See Commands and scopes.
| Parameter | Type | Required | Notes |
|---|---|---|---|
commands | Array of BotCommand | Yes | At most 100, no duplicates. [] deletes the list of that scope and language. |
scope | BotCommandScope | No | Default: {"type": "default"}. |
language_code | String | No | Empty, or a two-letter ISO 639-1 code (he, en, …). |
Result: true
setMyCommands and deleteMyCommands together: 10 changes a minute per bot.
Errors: 400 bad_request, 404 chat_not_found, 429 rate_limited.
getMyCommands#
The command list of exactly one scope and language (no fallback).
| Parameter | Type | Required | Notes |
|---|---|---|---|
scope | BotCommandScope | No | As a JSON string when called with GET. |
language_code | String | No |
Result: Array of BotCommand
Errors: 400 bad_request, 404 chat_not_found.
deleteMyCommands#
Deletes the command list of one scope and language.
| Parameter | Type | Required | Notes |
|---|---|---|---|
scope | BotCommandScope | No | |
language_code | String | No |
Result: true
Errors: 400 bad_request, 404 chat_not_found, 429 rate_limited.
setMyDescription#
The description on the bot's profile (shown before Start).
| Parameter | Type | Required | Notes |
|---|---|---|---|
description | String | No | At most 512 characters. Empty clears it. |
language_code | String | No | Accepted and ignored in v1: one description is stored for every language. |
Result: true
Publishing a bot (QivBot → Publish) needs a description and at least one command.
Errors: 400 bad_request, 422 content_blocked, 422 content_not_allowed.
getMyDescription#
The description.
No parameters.
Result: {"description": String}
setMyShortDescription#
The short description (search results, profile).
| Parameter | Type | Required | Notes |
|---|---|---|---|
short_description | String | No | At most 120 characters. Empty clears it. |
Result: true
Errors: 400 bad_request, 422 content_blocked, 422 content_not_allowed.
getMyShortDescription#
The short description.
No parameters.
Result: {"short_description": String}
Moderation#
banChatMember#
Bans a member from a group (the app's own ban).
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
user_id | Integer | Yes | The member. |
reason | String | No | At most 200 characters. Kept in the bot's audit log; shown in the room's line only where the bot could post text itself. |
Result: true
Groups only; needs can_ban_members. Never the creator, owners, admins or other bots.
Banning someone already banned answers true and writes nothing.
Errors: 400 bad_request, 404 chat_not_found, 404 user_not_found, 403 forbidden_right, 403 target_protected, 403 e2ee_unsupported, 410 room_closed, 422 content_blocked, 429 rate_limited.
unbanChatMember#
Lifts a ban. Unlike Telegram, the person becomes a member again.
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
user_id | Integer | Yes | The member. |
reason | String | No | At most 200 characters. |
Result: true
Needs can_ban_members. chat_member reports kicked → member.
Errors: 400 bad_request, 404 chat_not_found, 404 user_not_found, 403 forbidden_right, 403 target_protected, 403 e2ee_unsupported, 410 room_closed, 422 content_blocked, 429 rate_limited.
restrictChatMember#
Mutes a member of a group, for good or until a time; or lifts the mute.
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
user_id | Integer | Yes | The member. |
until_date | Integer | No | Unix time. Absent, 0, less than 30 s ahead or more than 366 days ahead: for good. |
permissions | Object | No | Only {"can_send_messages": false} (mute) or {"can_send_messages": true} (lift). Any other key → 400. |
reason | String | No | At most 200 characters. |
Result: true
Groups only; needs can_restrict_members. Qivuna has one switch: muted or not.
A timed mute is lifted by the platform when it ends, unless an admin changed that member since (then the admin has the last word).
Errors: 400 bad_request, 404 chat_not_found, 404 user_not_found, 403 forbidden_right, 403 target_protected, 403 e2ee_unsupported, 410 room_closed, 422 content_blocked, 429 rate_limited.
pinChatMessage#
Pins a message in a group.
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
message_id | Integer | Yes | A message id as the bot received it. |
Result: true
Groups only; needs can_pin. A group holds up to 3 pinned messages (400 message_id beyond that).
Errors: 400 bad_request, 404 chat_not_found, 404 message_not_found, 403 forbidden_right, 403 e2ee_unsupported, 410 room_closed, 429 rate_limited.
unpinChatMessage#
Unpins a message in a group.
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
message_id | Integer | Yes | Required (there is no “unpin the latest”). |
Result: true
Needs can_pin.
Errors: 400 bad_request, 404 chat_not_found, 404 message_not_found, 403 forbidden_right, 403 e2ee_unsupported, 410 room_closed, 429 rate_limited.
createChatInviteLink#
Creates a new invite link of the bot's own for a group or channel.
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
expire_date | Integer | No | Unix time, in the future and at most 366 days ahead. |
member_limit | Integer | No | 1–99,999 joins. |
Result: ChatInviteLink
Needs can_invite. At most 100 live links per bot per room. The room's own master link is never shown to bots, and no method ever adds a person: people join through the app.
Links stop working when revoked, expired or used up, when the bot loses can_invite or leaves, while it is restricted, and on transfer or deletion.
Errors: 400 bad_request, 404 chat_not_found, 403 forbidden_right, 403 e2ee_unsupported, 410 room_closed, 429 rate_limited.
revokeChatInviteLink#
Revokes one of the bot's invite links.
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
invite_link | String | Yes | A link this bot created in this chat. |
Result: ChatInviteLink
Errors: 400 bad_request, 404 chat_not_found, 403 e2ee_unsupported, 429 rate_limited.
setMyDefaultAdministratorRights#
The rights the “add bot” screen suggests. A suggestion only: admins decide.
| Parameter | Type | Required | Notes |
|---|---|---|---|
rights | ChatRights | Yes | Booleans; only rights that can be granted in that kind of room. |
for_channels | Boolean | No | Channels instead of groups. |
Result: true
Errors: 400 bad_request.
getMyDefaultAdministratorRights#
The suggested rights, or the platform defaults.
| Parameter | Type | Required | Notes |
|---|---|---|---|
for_channels | Boolean | No | Channels instead of groups. |
Result: ChatRights
Defaults: groups can_send, can_send_media; channels can_post, can_send_media.
Media#
sendPhoto#
Sends a picture (JPEG, PNG, WebP; HEIC is converted to JPEG).
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
photo | File upload or String | Yes | multipart/form-data file (≤ 20 MB), or a file_id this bot received or sent. |
caption | String | No | At most 1,024 characters. |
caption_entities | Array of MessageEntity | No | Also accepted as entities. Or parse_mode. |
parse_mode | String | No | "HTML". |
reply_to_message_id | Integer | No | |
disable_notification | Boolean | No | |
protect_content | Boolean | No | |
reply_markup | InlineKeyboardMarkup | No |
Result: Message
See Media and getFile for types, sizes, rights and daily upload limits.
Errors: 400 bad_request, 413 payload_too_large, 404 chat_not_found, 403 user_not_started, 403 user_blocked_bot, 403 unsolicited_limit, 403 forbidden_right, 403 e2ee_required, 403 e2ee_unsupported, 410 room_closed, 422 invalid_entities, 422 invalid_markup, 422 content_blocked, 429 rate_limited.
sendDocument#
Sends a file of a type the app accepts in chats.
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
document | File upload or String | Yes | File (≤ 20 MB) or a file_id. |
caption | String | No | At most 1,024 characters. |
caption_entities | Array of MessageEntity | No | |
parse_mode | String | No | |
reply_to_message_id | Integer | No | |
disable_notification | Boolean | No | |
protect_content | Boolean | No | |
reply_markup | InlineKeyboardMarkup | No |
Result: Message
Errors: 400 bad_request, 413 payload_too_large, 404 chat_not_found, 403 user_not_started, 403 user_blocked_bot, 403 unsolicited_limit, 403 forbidden_right, 403 e2ee_required, 403 e2ee_unsupported, 410 room_closed, 422 invalid_entities, 422 invalid_markup, 422 content_blocked, 429 rate_limited.
sendVideo#
Sends a video (MP4, MOV, WebM, 3GP).
| Parameter | Type | Required | Notes |
|---|---|---|---|
chat_id | Integer | Yes | The chat: a person's id (private chat), or a group's or channel's negative id, as the bot received it. |
video | File upload or String | Yes | File (≤ 20 MB) or a file_id. |
caption | String | No | At most 1,024 characters. |
caption_entities | Array of MessageEntity | No | |
parse_mode | String | No | |
reply_to_message_id | Integer | No | |
disable_notification | Boolean | No | |
protect_content | Boolean | No | |
reply_markup | InlineKeyboardMarkup | No |
Result: Message
Errors: 400 bad_request, 413 payload_too_large, 404 chat_not_found, 403 user_not_started, 403 user_blocked_bot, 403 unsolicited_limit, 403 forbidden_right, 403 e2ee_required, 403 e2ee_unsupported, 410 room_closed, 422 invalid_entities, 422 invalid_markup, 422 content_blocked, 429 rate_limited.
getFile#
A one-hour download link for a file the bot received or sent.
| Parameter | Type | Required | Notes |
|---|---|---|---|
file_id | String | Yes | From a Message's photo, video, document or attachments. |
Result: File with file_path. Download: GET https://bots.qivuna.com/v1/file/<file_path> with the same Authorization header.
The link works for this bot only, for 1 hour, and only while the file is still visible to the bot (message not deleted, the bot still in the room or the 1:1 chat). A file_id lives at most 30 days.
Errors: 404 file_not_found.
Webhooks#
setWebhook#
Delivers updates to your HTTPS endpoint instead of getUpdates. See Webhooks.
| Parameter | Type | Required | Notes |
|---|---|---|---|
url | String | Yes | https://, port 443 or 8443, at most 512 characters, a public host (no IP in private or reserved ranges, no *.qivuna.com). |
secret_token | String | Yes | 32–128 characters of A–Z a–z 0–9 _ -. Your HMAC key. Stored encrypted, never returned. |
max_connections | Integer | No | 1–8, default 4: how many chats are delivered in parallel. |
allowed_updates | Array of String | No | As in getUpdates; stored only after the challenge passed. |
encrypt_payload | Boolean | No | Seal every delivery to the bot's active key (qbw1). Needs an active key. A setWebhook without it turns encryption off. |
Result: true
Not available at launch: every call answers 400 webhooks_disabled until webhooks open. Use getUpdates.
The ownership challenge runs synchronously before anything is saved (up to 10 s).
Limits: 20 calls per bot per hour, 40 per owner per hour, and 30 failed challenges per destination host per hour across all bots.
If the bot changed hands or the token was revoked during the challenge, 401 token_revoked; restricted or suspended meanwhile, 403.
Errors: 400 webhooks_disabled, 400 bad_request, 422 webhook_unverified, 503 webhook_unavailable, 409 conflict, 409 key_not_active, 429 rate_limited.
deleteWebhook#
Turns the webhook off; updates wait for getUpdates.
| Parameter | Type | Required | Notes |
|---|---|---|---|
drop_pending_updates | Boolean | No | Also delete every waiting update. |
Result: true
Not a write: a restricted bot may call it. Because it is not a write, Idempotency-Key is ignored here (it is safe to repeat).
getWebhookInfo#
The webhook's state and its last error.
No parameters.
Result: WebhookInfo
With no webhook: url is "" and has_secret is false.
Encryption keys#
setMyEncryptionKey#
Proposes a new bot key. Nothing changes for anybody until the owner confirms it on their phone. See Encrypted bots.
| Parameter | Type | Required | Notes |
|---|---|---|---|
x25519 | String | Yes | The X25519 public key, 32 bytes, standard base64. |
ed25519 | String | Yes | The Ed25519 public key, 32 bytes, standard base64. |
t | Integer | Yes | Now, in unix milliseconds (±10 minutes). |
pop | String | Yes | Proof of possession: Ed25519 over "Qivuna.Bot.v1.pop|" ‖ bot_number ‖ x25519 ‖ ed25519 ‖ uint64_be(t), 64 bytes, base64. |
Result: {"status": "proposed", "fingerprint": String}
404 not_found while encrypted bots are switched off on the platform.
5 proposals per bot per hour (each one messages the owner). A new proposal replaces an older waiting one. A key used by another bot, a small-order key and a reused proof are refused.
Errors: 400 bad_key, 404 not_found, 429 rate_limited.
getMyEncryptionKey#
The active key and any waiting proposal, and the bot's own user id.
No parameters.
Result: EncryptionKeyInfo
bot_id is the bot account's internal user id: the bot value inside signed encrypted messages (QB1). It is not a secret.
Errors: 404 not_found.