Skip to content

Methods

94 methods across 15 categories. Generated from the API schema — see how this page is built.

Nothing here is live yet

The gateway is not deployed. Every method below returns 404 today — verified 2026-07-19. These pages are the specification being built to, not a description of a running service. Do not schedule work against them without checking the status page.

Index

BasicgetMe · logOut · close

Getting updatesgetUpdates · setWebhook · deleteWebhook · getWebhookInfo

Sending messagessendMessage · sendPhoto · sendVideo · sendAudio · sendDocument · sendVoice · sendVideoNote · sendAnimation · sendSticker · sendMediaGroup · sendLocation · sendVenue · sendContact · sendPoll · sendDice · sendChatAction

Editing and deletingeditMessageText · editMessageCaption · editMessageMedia · editMessageReplyMarkup · stopPoll · deleteMessage · deleteMessages

Forwarding and copyingforwardMessage · forwardMessages · copyMessage · copyMessages

FilesgetFile

Chat managementgetChat · getChatAdministrators · getChatMemberCount · getChatMember · banChatMember · unbanChatMember · restrictChatMember · promoteChatMember · setChatTitle · setChatDescription · setChatPhoto · pinChatMessage · unpinChatMessage · leaveChat

Invite linkscreateChatInviteLink · editChatInviteLink · revokeChatInviteLink · approveChatJoinRequest · declineChatJoinRequest

Forum topicscreateForumTopic · editForumTopic · closeForumTopic · reopenForumTopic · deleteForumTopic · unpinAllForumTopicMessages

Callbacks and inlineanswerCallbackQuery · answerInlineQuery · answerWebAppQuery · savePreparedInlineMessage

Bot settingssetMyCommands · getMyCommands · deleteMyCommands · setChatMenuButton · getChatMenuButton · setMyDefaultAdministratorRights · getMyDefaultAdministratorRights · setMyName · getMyName · setMyDescription · getMyDescription · setMyShortDescription · getMyShortDescription

ReactionssetMessageReaction

PaymentssendInvoice · createInvoiceLink · answerShippingQuery · answerPreCheckoutQuery · refundStarPayment

GamessendGame · setGameScore · getGameHighScores

StickersgetStickerSet · uploadStickerFile · createNewStickerSet · addStickerToSet · setStickerPositionInSet · deleteStickerFromSet · setStickerSetThumbnail · deleteStickerSet

Basic

Identify the bot and release its connection.

getMe planned

Returns basic information about the bot behind the token.

Use it as a health check for your token: it takes no parameters and touches no chat.

Takes no parameters.

Returns — User

Common errors401 Unauthorized

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getMe
python
result = await bot.get_me()
php
$result = Http::get("https://api.casinoplaneta.info/bot{$token}/getMe")->json();

Response

json
{
  "ok": true,
  "result": {
    "id": 4210,
    "is_bot": true,
    "first_name": "Weather",
    "username": "weather_bot",
    "can_join_groups": true,
    "can_read_all_group_messages": false,
    "supports_inline_queries": true
  }
}

logOut planned

Closes the bot instance on the server so the token can be used from a different machine.

After a successful call you cannot make requests for 10 minutes. Intended for moving a bot between hosts, not for routine shutdown.

Takes no parameters.

Returns — True on success

Common errors401 Unauthorized

bash
curl -X POST https://api.casinoplaneta.info/bot$TOKEN/logOut
python
result = await bot.log_out()
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/logOut")->json();

Response

json
{
  "ok": true,
  "result": true
}

close planned

Releases the bot's server-side resources before restarting it locally.

Unlike logOut, the token stays bound to this server. The call fails for the first 10 minutes after the bot is launched.

Takes no parameters.

Returns — True on success

Common errors401 Unauthorized

bash
curl -X POST https://api.casinoplaneta.info/bot$TOKEN/close
python
result = await bot.close()
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/close")->json();

Response

json
{
  "ok": true,
  "result": true
}

Getting updates

Long polling and webhooks. A bot uses one or the other, never both at once.

getUpdates planned

Receives incoming updates by long polling.

Each call blocks until an update is available or timeout seconds elapse, so an idle bot costs one open request rather than a spin loop.

Only one getUpdates may be open per token. A second concurrent call kills the first and returns 409 Conflict — in practice this means you deployed without stopping the previous process, or you left a webhook registered. Polling and webhooks are mutually exclusive: call deleteWebhook before you start polling.

The server keeps an update until you acknowledge it by passing a higher offset. Nothing else acknowledges — returning from your handler does not.

ParameterTypeRequiredDescription
offsetIntegerOptionalAcknowledges every update below this value and returns the rest. Pass the last update_id you finished handling plus one. Omitting the increment is the classic bug: the same batch is redelivered forever because nothing was ever acknowledged. A negative value returns that many of the most recent updates instead.
limitIntegerOptionalMaximum number of updates in one response, 1–100, default 100. Lower it only if your handler is slow enough that a full batch would outlive the poll cycle.
timeoutIntegerOptionalSeconds to hold the connection open when there is nothing to return, default 0. Always set this in production — 0 means short polling, which hammers the gateway and burns your rate limit. Keep it below your HTTP client's own read timeout or the client aborts a healthy request.
allowed_updatesArray of StringOptionalWhitelist of update types to receive, e.g. ["message","callback_query"]. Omit to keep the previous setting; the types message_reaction, message_reaction_count and chat_member are never delivered unless named here explicitly. Passing an empty list restores the default set. The setting is remembered server-side between calls, so it is not per-request state.

Returns — Array of Update

Common errors409 Conflict: terminated by other getUpdates request · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error: temporarily unavailable · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getUpdates \
  -H 'Content-Type: application/json' \
  -d '{"offset":851230,"limit":100,"timeout":50,"allowed_updates":["message","callback_query"]}'
python
result = await bot.get_updates(
    offset=851230,
    limit=100,
    timeout=50,
    allowed_updates=[
        "message",
        "callback_query"
    ],
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/getUpdates", [
    'offset' => 851230,
    'limit' => 100,
    'timeout' => 50,
    'allowed_updates' => [
        'message',
        'callback_query'
    ]
])->json();

Response

json
{
  "ok": true,
  "result": [
    {
      "update_id": 851230,
      "message": {
        "message_id": 4471,
        "from": {
          "id": 918273645,
          "is_bot": false,
          "first_name": "Aziz",
          "username": "aziz"
        },
        "chat": {
          "id": 918273645,
          "first_name": "Aziz",
          "username": "aziz",
          "type": "private"
        },
        "date": 1784500123,
        "text": "/start"
      }
    }
  ]
}

setWebhook planned

Registers an HTTPS URL that receives updates as POSTed JSON.

Registering a webhook disables getUpdates for that token — the two transports are mutually exclusive. Call deleteWebhook to go back to polling.

The URL must be HTTPS on port 443, 80, 88 or 8443. Other ports are rejected outright, and a certificate the gateway cannot verify fails the same way — pass certificate if you are terminating TLS with a self-signed cert.

Set secret_token and verify the X-Grom-Bot-Api-Secret-Token header on every incoming request. Without that check anyone who learns or guesses your URL can post forged updates to it.

Delivery is at-least-once, retried at 1s, 5s, 30s, 2m and 10m before being dropped after 24 hours. Your handler will eventually see the same update_id twice, so make it idempotent — store handled ids, or keep the side effect naturally repeatable. Return 200 immediately and do the work afterwards; the retry schedule keys off your response time, so a slow handler manufactures duplicates.

ParameterTypeRequiredDescription
urlStringYesHTTPS endpoint that receives updates, on port 443, 80, 88 or 8443. Pass an empty string to remove the webhook, though deleteWebhook is the clearer way to do that. Putting a random component in the path is worth doing even with secret_token set.
certificateInputFileOptionalYour public key certificate, uploaded so the gateway can verify a self-signed chain. Unnecessary when the endpoint presents a publicly trusted certificate.
ip_addressStringOptionalFixed IP to connect to instead of resolving the URL's hostname through DNS. Use it when the hostname resolves differently from outside your network.
max_connectionsIntegerOptionalUpper bound on simultaneous HTTPS connections used for delivery, 1–100, default 40. Lower it to protect a modest server; raising it only helps if your handler is genuinely concurrent, and it increases the chance of updates being processed out of order.
allowed_updatesArray of StringOptionalWhitelist of update types to deliver. Omit to keep the previous setting. message_reaction, message_reaction_count and chat_member are only delivered when named here explicitly.
drop_pending_updatesBooleanOptionalDiscards everything queued before the webhook was set. Useful after downtime, when replaying a backlog of stale commands would do more harm than skipping them.
secret_tokenStringOptional1–256 characters of A-Z a-z 0-9 _ -, sent back in the X-Grom-Bot-Api-Secret-Token header on every delivery. Compare it in constant time and reject the request on mismatch. This is the only thing that proves a POST came from the gateway.

Returns — True on success

Common errors429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error: temporarily unavailable · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/setWebhook \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://bot.example.com/hook/a7f3c1e9","max_connections":40,"allowed_updates":["message","callback_query","my_chat_member"],"drop_pending_updates":true,"secret_token":"9f2c4b7ae1d84f0396a5c7d2e8b1f640"}'
python
result = await bot.set_webhook(
    url="https://bot.example.com/hook/a7f3c1e9",
    max_connections=40,
    allowed_updates=[
        "message",
        "callback_query",
        "my_chat_member"
    ],
    drop_pending_updates=True,
    secret_token="9f2c4b7ae1d84f0396a5c7d2e8b1f640",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/setWebhook", [
    'url' => 'https://bot.example.com/hook/a7f3c1e9',
    'max_connections' => 40,
    'allowed_updates' => [
        'message',
        'callback_query',
        'my_chat_member'
    ],
    'drop_pending_updates' => true,
    'secret_token' => '9f2c4b7ae1d84f0396a5c7d2e8b1f640'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

deleteWebhook planned

Removes the registered webhook and re-enables long polling.

Succeeds even when no webhook is set, so it is safe to call unconditionally at startup — that is the standard fix for a poller that keeps hitting 409 Conflict.

Without drop_pending_updates, everything queued while the webhook was active is handed to your first getUpdates call.

ParameterTypeRequiredDescription
drop_pending_updatesBooleanOptionalDiscards the queued backlog instead of delivering it to the next poll. Leave it off if the queued updates still matter; turn it on when the backlog is stale enough that acting on it would be wrong.

Returns — True on success

Common errors429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error: temporarily unavailable · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/deleteWebhook \
  -H 'Content-Type: application/json' \
  -d '{"drop_pending_updates":false}'
python
result = await bot.delete_webhook(
    drop_pending_updates=False,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/deleteWebhook", [
    'drop_pending_updates' => false
])->json();

Response

json
{
  "ok": true,
  "result": true
}

getWebhookInfo planned

Returns the current webhook registration and its delivery health.

The first thing to check when a bot goes quiet. A climbing pending_update_count with a populated last_error_message means the gateway is delivering and your endpoint is failing; a climbing count with no error usually means your handler is too slow and deliveries are timing out.

If url comes back empty the bot is in polling mode, and nothing will arrive until something calls getUpdates.

Takes no parameters.

Returns — WebhookInfo

Common errors401 Unauthorized · 500 Internal Server Error: temporarily unavailable · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getWebhookInfo
python
result = await bot.get_webhook_info()
php
$result = Http::get("https://api.casinoplaneta.info/bot{$token}/getWebhookInfo")->json();

Response

json
{
  "ok": true,
  "result": {
    "url": "https://bot.example.com/hook/a7f3c1e9",
    "has_custom_certificate": false,
    "pending_update_count": 0,
    "max_connections": 40,
    "ip_address": "203.0.113.17",
    "allowed_updates": [
      "message",
      "callback_query",
      "my_chat_member"
    ],
    "last_error_date": 1784496500,
    "last_error_message": "Wrong response from the webhook: 502 Bad Gateway"
  }
}

Sending messages

Every send method returns the Message it created.

sendMessage planned

Sends a text message and returns the Message that was created.

Text is limited to 4096 UTF-16 code units and captions to 1024. Over the limit the call is rejected, not truncated, so split long output yourself — on a character boundary, since a split through a surrogate pair produces text no client can render.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup. A @username string resolves only for public channels. Getting the sign wrong points at a different chat entirely and surfaces as chat_not_found.
textStringYesThe message body, 1-4096 UTF-16 code units. Empty strings are rejected — if your template can render nothing, guard it before the call rather than sending a space.
parse_modeStringOptionalMarkdownV2 or HTML. Leave it unset when interpolating user input you have not escaped: one stray _ or < fails the entire call with cant_parse_entities rather than degrading to plain text.
entitiesArray of MessageEntityOptionalFormatting given as explicit offsets instead of markup. Mutually exclusive with parse_mode. Offsets are UTF-16 code units, so most emoji advance the offset by 2 — computing them over a UTF-8 byte string is the usual source of misplaced bold.
link_preview_optionsLinkPreviewOptionsOptionalControls the preview built from the first URL in the text. is_disabled suppresses it; prefer_small_media keeps the card but shrinks the image. Without this the preview can dominate a one-line message.
message_thread_idIntegerOptionalTarget topic in a forum supergroup. Omitting it in a forum does not fail — the message lands in General, which is rarely what you wanted.
disable_notificationBooleanOptionalDelivers silently. The message still marks the chat unread; this only suppresses sound and vibration.
protect_contentBooleanOptionalBlocks forwarding and saving of this message. It does not prevent screenshots, so do not treat it as confidentiality.
reply_parametersReplyParametersOptionalMakes the message a reply. If the target was deleted the call fails with reply_not_found unless allow_sending_without_reply is set inside this object.
reply_markupInlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReplyOptionalInline keyboards attach to this message and produce callback_query updates; reply keyboards replace the user's own keyboard and produce ordinary messages. Only inline keyboards work in channels.

Returns — Message

Common errors400 chat not found · 403 bot was blocked by the user · 400 message is too long · 400 can't parse entities

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendMessage \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":123456789,"text":"Rain expected in *Tashkent* after 18:00\\.","parse_mode":"MarkdownV2"}'
python
result = await bot.send_message(
    chat_id=123456789,
    text="Rain expected in *Tashkent* after 18:00\\.",
    parse_mode="MarkdownV2",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendMessage", [
    'chat_id' => 123456789,
    'text' => 'Rain expected in *Tashkent* after 18:00\\.',
    'parse_mode' => 'MarkdownV2'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4821,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": 123456789,
      "first_name": "Aziz",
      "username": "aziz",
      "type": "private"
    },
    "date": 1784462400,
    "text": "Rain expected in Tashkent after 18:00.",
    "entities": [
      {
        "type": "bold",
        "offset": 17,
        "length": 8
      }
    ]
  }
}

sendPhoto planned

Sends an image that clients display inline and compress.

A file_id returned by any send method can be passed back to re-send the same image with no upload. It is bound to the bot that received it — a file_id from another bot will not resolve.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
photoInputFile or StringYesA file_id to re-send something already on the server, an HTTPS URL for us to fetch, or multipart bytes. The server re-encodes and strips EXIF, so the returned file_id never refers to your original bytes.
captionStringOptionalUp to 1024 UTF-16 code units — a quarter of the text limit, which is what usually breaks code that shares one formatter with sendMessage.
parse_modeStringOptionalMarkdownV2 or HTML, applied to the caption only.
caption_entitiesArray of MessageEntityOptionalCaption formatting as explicit UTF-16 offsets. Mutually exclusive with parse_mode.
has_spoilerBooleanOptionalCovers the image until the recipient taps it. The bytes are still delivered — this is a display treatment, not access control.
message_thread_idIntegerOptionalTarget topic in a forum supergroup.
disable_notificationBooleanOptionalDelivers silently.
protect_contentBooleanOptionalBlocks forwarding and saving of this message.
reply_parametersReplyParametersOptionalMakes the message a reply.
reply_markupInlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReplyOptionalKeyboard attached below the photo.

Returns — Message

Common errors400 chat not found · 413 Request Entity Too Large · 400 wrong file type · 400 message is too long

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendPhoto \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"photo":"https://example.com/radar/tashkent-1740.jpg","caption":"Radar sweep, 17:40"}'
python
result = await bot.send_photo(
    chat_id=-1001234567890,
    photo="https://example.com/radar/tashkent-1740.jpg",
    caption="Radar sweep, 17:40",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendPhoto", [
    'chat_id' => -1001234567890,
    'photo' => 'https://example.com/radar/tashkent-1740.jpg',
    'caption' => 'Radar sweep, 17:40'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 118,
    "sender_chat": {
      "id": -1001234567890,
      "title": "Tashkent Weather",
      "username": "tashkent_weather",
      "type": "channel"
    },
    "chat": {
      "id": -1001234567890,
      "title": "Tashkent Weather",
      "username": "tashkent_weather",
      "type": "channel"
    },
    "date": 1784462520,
    "photo": [
      {
        "file_id": "AgACAgIAAxkBAAIEc2ZQr1n4",
        "file_unique_id": "AQADbroxG0k9sEt4",
        "width": 320,
        "height": 180,
        "file_size": 12043
      },
      {
        "file_id": "AgACAgIAAxkBAAIEc2ZQr1n4-m",
        "file_unique_id": "AQADbroxG0k9sEty",
        "width": 800,
        "height": 450,
        "file_size": 48210
      },
      {
        "file_id": "AgACAgIAAxkBAAIEc2ZQr1n4-x",
        "file_unique_id": "AQADbroxG0k9sEt9",
        "width": 1280,
        "height": 720,
        "file_size": 121874
      }
    ],
    "caption": "Radar sweep, 17:40"
  }
}

sendVideo planned

Sends an MP4 video that plays inside the chat.

Only MP4 plays inline. Other containers are delivered as documents even though the call succeeds, so check the returned Message if inline playback matters.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
videoInputFile or StringYesA file_id, an HTTPS URL, or multipart bytes. Fetching by URL is subject to our own timeout — for anything large or slow to serve, upload it.
durationIntegerOptionalLength in seconds. Clients show a placeholder duration until the file is processed, so passing it makes the bubble correct immediately.
widthIntegerOptionalWidth in pixels. Sent with height it lets the client reserve the right aspect ratio instead of reflowing the message once metadata arrives.
heightIntegerOptionalHeight in pixels.
thumbnailInputFileOptionalJPEG under 200 kB, at most 320x320. Only applies to a fresh upload — a thumbnail cannot be attached to a file re-sent by file_id.
captionStringOptionalUp to 1024 UTF-16 code units.
parse_modeStringOptionalMarkdownV2 or HTML, applied to the caption only.
has_spoilerBooleanOptionalCovers the video until the recipient taps it.
supports_streamingBooleanOptionalLets clients start playback before the download finishes. It only has an effect if the moov atom sits at the front of the file — ffmpeg -movflags +faststart. Setting it on a file that is not laid out that way silently does nothing.
disable_notificationBooleanOptionalDelivers silently.
reply_parametersReplyParametersOptionalMakes the message a reply.
reply_markupInlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReplyOptionalKeyboard attached below the video.

Returns — Message

Common errors400 chat not found · 413 Request Entity Too Large · 400 wrong file type · 400 file is not ready

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendVideo \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":123456789,"video":"https://example.com/clips/storm-timelapse.mp4","duration":34,"width":1280,"height":720,"supports_streaming":true,"caption":"Timelapse, 16:00-18:00"}'
python
result = await bot.send_video(
    chat_id=123456789,
    video="https://example.com/clips/storm-timelapse.mp4",
    duration=34,
    width=1280,
    height=720,
    supports_streaming=True,
    caption="Timelapse, 16:00-18:00",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendVideo", [
    'chat_id' => 123456789,
    'video' => 'https://example.com/clips/storm-timelapse.mp4',
    'duration' => 34,
    'width' => 1280,
    'height' => 720,
    'supports_streaming' => true,
    'caption' => 'Timelapse, 16:00-18:00'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4822,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": 123456789,
      "first_name": "Aziz",
      "username": "aziz",
      "type": "private"
    },
    "date": 1784462610,
    "video": {
      "file_id": "BAACAgIAAxkBAAIEdGZQr2Xy",
      "file_unique_id": "AgAD3wADk9sES",
      "width": 1280,
      "height": 720,
      "duration": 34,
      "mime_type": "video/mp4",
      "file_size": 3841022
    },
    "caption": "Timelapse, 16:00-18:00"
  }
}

sendAudio planned

Sends an audio file for playback in the client's music player.

MP3 and M4A appear in the music player. Anything else is delivered as a document instead, without an error.

performer and title override the file's own tags in the player. If you omit them the client falls back to whatever ID3 data the file carries, which for user-supplied files is often blank or wrong.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
audioInputFile or StringYesA file_id, an HTTPS URL, or multipart bytes.
captionStringOptionalUp to 1024 UTF-16 code units, shown beneath the player.
parse_modeStringOptionalMarkdownV2 or HTML, applied to the caption only.
durationIntegerOptionalLength in seconds. Without it the player shows no total until the file has been probed.
performerStringOptionalArtist line in the player.
titleStringOptionalTrack name in the player. Falls back to the file name when unset, so uploads named tmp_1.mp3 show exactly that.
thumbnailInputFileOptionalCover art as JPEG under 200 kB, at most 320x320. Fresh uploads only.
disable_notificationBooleanOptionalDelivers silently.
reply_parametersReplyParametersOptionalMakes the message a reply.
reply_markupInlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReplyOptionalKeyboard attached below the player.

Returns — Message

Common errors400 chat not found · 413 Request Entity Too Large · 400 wrong file type

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendAudio \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":123456789,"audio":"https://example.com/podcast/ep-14.mp3","title":"Forecast weekly, episode 14","performer":"Tashkent Weather","duration":1284}'
python
result = await bot.send_audio(
    chat_id=123456789,
    audio="https://example.com/podcast/ep-14.mp3",
    title="Forecast weekly, episode 14",
    performer="Tashkent Weather",
    duration=1284,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendAudio", [
    'chat_id' => 123456789,
    'audio' => 'https://example.com/podcast/ep-14.mp3',
    'title' => 'Forecast weekly, episode 14',
    'performer' => 'Tashkent Weather',
    'duration' => 1284
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4823,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": 123456789,
      "first_name": "Aziz",
      "username": "aziz",
      "type": "private"
    },
    "date": 1784462700,
    "audio": {
      "file_id": "CQACAgIAAxkBAAIEdWZQr3Ab",
      "file_unique_id": "AgADqQADk9sESw",
      "duration": 1284,
      "performer": "Tashkent Weather",
      "title": "Forecast weekly, episode 14",
      "mime_type": "audio/mpeg",
      "file_size": 20548112
    }
  }
}

sendDocument planned

Sends a file of any type as a document, with no re-encoding.

The fallback for formats the typed methods reject, and the only method that delivers bytes untouched — sendPhoto recompresses, sendVideo may transcode. Send anything you need to arrive byte-identical this way.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
documentInputFile or StringYesA file_id, an HTTPS URL, or multipart bytes. The file name shown to the recipient comes from the multipart part name, not from the URL path.
thumbnailInputFileOptionalJPEG under 200 kB, at most 320x320, shown as the file icon. Fresh uploads only.
captionStringOptionalUp to 1024 UTF-16 code units.
parse_modeStringOptionalMarkdownV2 or HTML, applied to the caption only.
disable_content_type_detectionBooleanOptionalStops the server sniffing the bytes and re-classifying the upload. Without it a JPEG sent here can come back as a photo — set it when you need the recipient to get a file, not a picture.
message_thread_idIntegerOptionalTarget topic in a forum supergroup.
disable_notificationBooleanOptionalDelivers silently.
protect_contentBooleanOptionalBlocks forwarding and saving of this message.
reply_parametersReplyParametersOptionalMakes the message a reply.
reply_markupInlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReplyOptionalKeyboard attached below the file.

Returns — Message

Common errors400 chat not found · 413 Request Entity Too Large · 400 not enough rights

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendDocument \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"document":"https://example.com/reports/july-2026.pdf","caption":"Monthly summary","disable_content_type_detection":true}'
python
result = await bot.send_document(
    chat_id=-1001234567890,
    document="https://example.com/reports/july-2026.pdf",
    caption="Monthly summary",
    disable_content_type_detection=True,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendDocument", [
    'chat_id' => -1001234567890,
    'document' => 'https://example.com/reports/july-2026.pdf',
    'caption' => 'Monthly summary',
    'disable_content_type_detection' => true
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 119,
    "sender_chat": {
      "id": -1001234567890,
      "title": "Tashkent Weather",
      "username": "tashkent_weather",
      "type": "channel"
    },
    "chat": {
      "id": -1001234567890,
      "title": "Tashkent Weather",
      "username": "tashkent_weather",
      "type": "channel"
    },
    "date": 1784462790,
    "document": {
      "file_id": "BQACAgIAAxkBAAIEdmZQr4Jd",
      "file_unique_id": "AgADsQADk9sESx",
      "file_name": "july-2026.pdf",
      "mime_type": "application/pdf",
      "file_size": 884213
    },
    "caption": "Monthly summary"
  }
}

sendVoice planned

Sends a voice message with a waveform and a playback bar.

Only OGG encoded with OPUS renders as a voice message. Other formats — including the M4A most Android recorders produce by default — arrive as ordinary audio files, so transcode before sending if you want the voice bubble.

Voice messages have no width, height or thumbnail, and a recipient can restrict who may send them to it in privacy settings; that restriction surfaces as not_enough_rights.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
voiceInputFile or StringYesOGG/OPUS as a file_id, an HTTPS URL, or multipart bytes.
captionStringOptionalUp to 1024 UTF-16 code units, shown under the waveform.
parse_modeStringOptionalMarkdownV2 or HTML, applied to the caption only.
durationIntegerOptionalLength in seconds. Worth passing — the waveform is drawn against it, and without it the bar can jump once the server finishes probing the file.
disable_notificationBooleanOptionalDelivers silently.
reply_parametersReplyParametersOptionalMakes the message a reply.
reply_markupInlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReplyOptionalKeyboard attached below the voice message.

Returns — Message

Common errors400 chat not found · 400 wrong file type · 413 Request Entity Too Large · 400 file is not ready

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendVoice \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":123456789,"voice":"https://example.com/alerts/storm-warning.ogg","duration":12}'
python
result = await bot.send_voice(
    chat_id=123456789,
    voice="https://example.com/alerts/storm-warning.ogg",
    duration=12,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendVoice", [
    'chat_id' => 123456789,
    'voice' => 'https://example.com/alerts/storm-warning.ogg',
    'duration' => 12
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4824,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": 123456789,
      "first_name": "Aziz",
      "username": "aziz",
      "type": "private"
    },
    "date": 1784462880,
    "voice": {
      "file_id": "AwACAgIAAxkBAAIEd2ZQr5Kt",
      "file_unique_id": "AgADuQADk9sESy",
      "duration": 12,
      "mime_type": "audio/ogg",
      "file_size": 41203
    }
  }
}

sendVideoNote planned

Sends a round video note, up to one minute long.

The client crops to a circle, so a non-square source loses its edges — record or letterbox to a square before sending.

URLs are not accepted for video notes: upload the bytes or re-send by file_id. Captions are not supported either, so send any accompanying text as a separate message.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
video_noteInputFile or StringYesA file_id or multipart bytes. Unlike the other media methods this one will not fetch an HTTPS URL.
durationIntegerOptionalLength in seconds, at most 60. Longer files are rejected rather than trimmed.
lengthIntegerOptionalSide length of the square in pixels — one number, since width and height are equal by definition.
thumbnailInputFileOptionalJPEG under 200 kB, at most 320x320. Fresh uploads only.
disable_notificationBooleanOptionalDelivers silently.
reply_parametersReplyParametersOptionalMakes the message a reply.
reply_markupInlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReplyOptionalKeyboard attached below the video note.

Returns — Message

Common errors400 chat not found · 400 wrong file type · 413 Request Entity Too Large

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendVideoNote \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":123456789,"video_note":"DQACAgIAAxkBAAIEeGZQr6Lm","duration":18,"length":384}'
python
result = await bot.send_video_note(
    chat_id=123456789,
    video_note="DQACAgIAAxkBAAIEeGZQr6Lm",
    duration=18,
    length=384,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendVideoNote", [
    'chat_id' => 123456789,
    'video_note' => 'DQACAgIAAxkBAAIEeGZQr6Lm',
    'duration' => 18,
    'length' => 384
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4825,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": 123456789,
      "first_name": "Aziz",
      "username": "aziz",
      "type": "private"
    },
    "date": 1784462970,
    "video_note": {
      "file_id": "DQACAgIAAxkBAAIEeGZQr6Lm",
      "file_unique_id": "AgAD0QADk9sESz",
      "length": 384,
      "duration": 18,
      "file_size": 1204884
    }
  }
}

sendAnimation planned

Sends a GIF or a silent MP4 that loops in place.

A GIF is transcoded to MP4 on upload, so the file_id you get back describes a video file even though you sent a GIF. Audio tracks are dropped rather than rejected — an MP4 with sound sends fine and plays silently.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
animationInputFile or StringYesGIF or H.264 MP4 as a file_id, an HTTPS URL, or multipart bytes.
durationIntegerOptionalLength in seconds.
widthIntegerOptionalWidth in pixels; sent with height it prevents the message reflowing once metadata arrives.
heightIntegerOptionalHeight in pixels.
thumbnailInputFileOptionalJPEG under 200 kB, at most 320x320. Fresh uploads only.
captionStringOptionalUp to 1024 UTF-16 code units.
parse_modeStringOptionalMarkdownV2 or HTML, applied to the caption only.
has_spoilerBooleanOptionalCovers the animation until the recipient taps it.
disable_notificationBooleanOptionalDelivers silently.
reply_parametersReplyParametersOptionalMakes the message a reply.
reply_markupInlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReplyOptionalKeyboard attached below the animation.

Returns — Message

Common errors400 chat not found · 400 wrong file type · 413 Request Entity Too Large

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendAnimation \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":123456789,"animation":"https://example.com/loops/cloud-cover.mp4","width":480,"height":480,"duration":6,"caption":"Cloud cover, last 6 hours"}'
python
result = await bot.send_animation(
    chat_id=123456789,
    animation="https://example.com/loops/cloud-cover.mp4",
    width=480,
    height=480,
    duration=6,
    caption="Cloud cover, last 6 hours",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendAnimation", [
    'chat_id' => 123456789,
    'animation' => 'https://example.com/loops/cloud-cover.mp4',
    'width' => 480,
    'height' => 480,
    'duration' => 6,
    'caption' => 'Cloud cover, last 6 hours'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4826,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": 123456789,
      "first_name": "Aziz",
      "username": "aziz",
      "type": "private"
    },
    "date": 1784463060,
    "animation": {
      "file_id": "CgACAgIAAxkBAAIEeWZQr7Rp",
      "file_unique_id": "AgAD2QADk9sES0",
      "width": 480,
      "height": 480,
      "duration": 6,
      "mime_type": "video/mp4",
      "file_size": 742110
    },
    "caption": "Cloud cover, last 6 hours"
  }
}

sendSticker planned

Sends a static, animated or video sticker.

WEBP is static, TGS is animated and WEBM is video. Uploading raw bytes sends a one-off sticker that belongs to no set — it will not appear in anyone's sticker panel.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
stickerInputFile or StringYesA file_id from a sticker set, an HTTPS URL, or multipart bytes. Re-sending by file_id is the normal path — that is how you send a sticker from an existing set.
emojiStringOptionalThe emoji this sticker corresponds to. Applies only to newly uploaded stickers and is ignored when re-sending by file_id, where the set's own mapping wins.
message_thread_idIntegerOptionalTarget topic in a forum supergroup.
disable_notificationBooleanOptionalDelivers silently.
protect_contentBooleanOptionalBlocks forwarding and saving of this message.
reply_parametersReplyParametersOptionalMakes the message a reply.
reply_markupInlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReplyOptionalKeyboard attached below the sticker.

Returns — Message

Common errors400 chat not found · 400 wrong file type · 400 not enough rights

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendSticker \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":123456789,"sticker":"CAACAgIAAxkBAAIEempQr8Tz"}'
python
result = await bot.send_sticker(
    chat_id=123456789,
    sticker="CAACAgIAAxkBAAIEempQr8Tz",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendSticker", [
    'chat_id' => 123456789,
    'sticker' => 'CAACAgIAAxkBAAIEempQr8Tz'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4827,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": 123456789,
      "first_name": "Aziz",
      "username": "aziz",
      "type": "private"
    },
    "date": 1784463150,
    "sticker": {
      "file_id": "CAACAgIAAxkBAAIEempQr8Tz",
      "file_unique_id": "AgAD4QADk9sES1",
      "type": "regular",
      "width": 512,
      "height": 512,
      "is_animated": false,
      "is_video": false,
      "emoji": "☔",
      "set_name": "GromWeather",
      "file_size": 24118
    }
  }
}

sendMediaGroup planned

Sends 2-10 items as a single album and returns the Messages it created.

Photos and videos may be mixed freely. Documents may only be grouped with documents, and audio only with audio — mixing across those families is rejected for the whole call.

Captions live on the individual items, not the group. An album shows one caption only when exactly one item carries it; put it on the first item, since that is where clients render it. reply_markup is not supported at all — attach a keyboard with a follow-up message if you need one.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
mediaArray of InputMediaAudio, InputMediaDocument, InputMediaPhoto and InputMediaVideoYes2 to 10 items, each with its own type and media. One item fails validation rather than degrading to a single send, and 11 is rejected outright — chunk longer sets yourself.
message_thread_idIntegerOptionalTarget topic in a forum supergroup.
disable_notificationBooleanOptionalDelivers the whole album silently.
protect_contentBooleanOptionalBlocks forwarding and saving for every message in the album.
reply_parametersReplyParametersOptionalMakes the album a reply. The reply is anchored to the first message of the group.

Returns — Array of Message

Common errors400 chat not found · 413 Request Entity Too Large · 400 wrong file type · 400 message is too long

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendMediaGroup \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"media":[{"type":"photo","media":"https://example.com/radar/1600.jpg","caption":"Radar, 16:00 to 18:00"},{"type":"photo","media":"https://example.com/radar/1700.jpg"},{"type":"photo","media":"https://example.com/radar/1800.jpg"}]}'
python
result = await bot.send_media_group(
    chat_id=-1001234567890,
    media=[
        {
            "type": "photo",
            "media": "https://example.com/radar/1600.jpg",
            "caption": "Radar, 16:00 to 18:00"
        },
        {
            "type": "photo",
            "media": "https://example.com/radar/1700.jpg"
        },
        {
            "type": "photo",
            "media": "https://example.com/radar/1800.jpg"
        }
    ],
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendMediaGroup", [
    'chat_id' => -1001234567890,
    'media' => [
        [
            'type' => 'photo',
            'media' => 'https://example.com/radar/1600.jpg',
            'caption' => 'Radar, 16:00 to 18:00'
        ],
        [
            'type' => 'photo',
            'media' => 'https://example.com/radar/1700.jpg'
        ],
        [
            'type' => 'photo',
            'media' => 'https://example.com/radar/1800.jpg'
        ]
    ]
])->json();

Response

json
{
  "ok": true,
  "result": [
    {
      "message_id": 120,
      "media_group_id": "13284957120458821",
      "chat": {
        "id": -1001234567890,
        "title": "Tashkent Weather",
        "username": "tashkent_weather",
        "type": "channel"
      },
      "date": 1784463240,
      "photo": [
        {
          "file_id": "AgACAgIAAxkBAAIEe2ZQr9Aa",
          "file_unique_id": "AQADbroxG0k9sEuA",
          "width": 1280,
          "height": 720,
          "file_size": 118402
        }
      ],
      "caption": "Radar, 16:00 to 18:00"
    },
    {
      "message_id": 121,
      "media_group_id": "13284957120458821",
      "chat": {
        "id": -1001234567890,
        "title": "Tashkent Weather",
        "username": "tashkent_weather",
        "type": "channel"
      },
      "date": 1784463240,
      "photo": [
        {
          "file_id": "AgACAgIAAxkBAAIEfGZQr9Bb",
          "file_unique_id": "AQADbroxG0k9sEuB",
          "width": 1280,
          "height": 720,
          "file_size": 121003
        }
      ]
    },
    {
      "message_id": 122,
      "media_group_id": "13284957120458821",
      "chat": {
        "id": -1001234567890,
        "title": "Tashkent Weather",
        "username": "tashkent_weather",
        "type": "channel"
      },
      "date": 1784463240,
      "photo": [
        {
          "file_id": "AgACAgIAAxkBAAIEfWZQr9Cc",
          "file_unique_id": "AQADbroxG0k9sEuC",
          "width": 1280,
          "height": 720,
          "file_size": 119558
        }
      ]
    }
  ]
}

sendLocation planned

Sends a point on the map, optionally as a live location that keeps updating.

Without live_period this is a static pin and nothing further is needed. With it, the message only moves when you call editMessageLiveLocation — the server does not track anything on its own, and the pin freezes where you last put it when the period expires.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
latitudeFloatYesLatitude in degrees, -90 to 90.
longitudeFloatYesLongitude in degrees, -180 to 180. Swapping this with latitude is accepted silently whenever both are in range and simply puts the pin somewhere else.
horizontal_accuracyFloatOptionalRadius of uncertainty in metres, 0-1500. Drawn as a circle around the pin.
live_periodIntegerOptionalSeconds the location stays live, 60 to 86400. Omit it for a static pin.
headingIntegerOptionalDirection of travel in degrees, 1-360. Only rendered for live locations.
proximity_alert_radiusIntegerOptionalDistance in metres at which the other party is notified of approach. Live locations only; ignored on a static pin.
disable_notificationBooleanOptionalDelivers silently.
reply_parametersReplyParametersOptionalMakes the message a reply.
reply_markupInlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReplyOptionalKeyboard attached below the map.

Returns — Message

Common errors400 chat not found · 403 bot was blocked by the user · 400 not enough rights

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendLocation \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":123456789,"latitude":41.311081,"longitude":69.240562,"horizontal_accuracy":65}'
python
result = await bot.send_location(
    chat_id=123456789,
    latitude=41.311081,
    longitude=69.240562,
    horizontal_accuracy=65,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendLocation", [
    'chat_id' => 123456789,
    'latitude' => 41.311081,
    'longitude' => 69.240562,
    'horizontal_accuracy' => 65
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4828,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": 123456789,
      "first_name": "Aziz",
      "username": "aziz",
      "type": "private"
    },
    "date": 1784463330,
    "location": {
      "latitude": 41.311081,
      "longitude": 69.240562,
      "horizontal_accuracy": 65
    }
  }
}

sendVenue planned

Sends a named place: a pin plus a title and street address.

A venue is a location with a label, not a database lookup — we do not verify that the coordinates and the address agree. Whatever you pass is what the recipient sees.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
latitudeFloatYesLatitude in degrees, -90 to 90.
longitudeFloatYesLongitude in degrees, -180 to 180.
titleStringYesName of the place, shown in bold above the address.
addressStringYesStreet address as one line. Clients do not reformat it, so include the city if the recipient may not be local.
foursquare_idStringOptionalFoursquare identifier for the venue, if you have one.
foursquare_typeStringOptionalFoursquare category, such as arts_entertainment/aquarium. Only meaningful alongside foursquare_id.
google_place_idStringOptionalGoogle Places identifier for the venue.
google_place_typeStringOptionalGoogle Places category, such as restaurant. Only meaningful alongside google_place_id.
disable_notificationBooleanOptionalDelivers silently.
reply_parametersReplyParametersOptionalMakes the message a reply.
reply_markupInlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReplyOptionalKeyboard attached below the venue card.

Returns — Message

Common errors400 chat not found · 403 bot was blocked by the user · 400 not enough rights

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendVenue \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":123456789,"latitude":41.316667,"longitude":69.249722,"title":"Hydrometeorology Centre","address":"72 Amir Temur Avenue, Tashkent"}'
python
result = await bot.send_venue(
    chat_id=123456789,
    latitude=41.316667,
    longitude=69.249722,
    title="Hydrometeorology Centre",
    address="72 Amir Temur Avenue, Tashkent",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendVenue", [
    'chat_id' => 123456789,
    'latitude' => 41.316667,
    'longitude' => 69.249722,
    'title' => 'Hydrometeorology Centre',
    'address' => '72 Amir Temur Avenue, Tashkent'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4829,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": 123456789,
      "first_name": "Aziz",
      "username": "aziz",
      "type": "private"
    },
    "date": 1784463420,
    "venue": {
      "location": {
        "latitude": 41.316667,
        "longitude": 69.249722
      },
      "title": "Hydrometeorology Centre",
      "address": "72 Amir Temur Avenue, Tashkent"
    },
    "location": {
      "latitude": 41.316667,
      "longitude": 69.249722
    }
  }
}

sendContact planned

Sends a phone contact as a tappable card.

The number does not have to belong to a GROM account — this sends a contact card, not an invitation. If the number is registered, the card links to that account.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
phone_numberStringYesThe number in E.164 form, for example +998901234567. Local formats are accepted but the card then shows exactly what you sent, which the recipient may not be able to dial.
first_nameStringYesGiven name shown on the card.
last_nameStringOptionalFamily name shown after the given name.
vcardStringOptionalExtra fields as a vCard, up to 2048 bytes, shown when the card is opened. phone_number and first_name still take precedence over anything the vCard repeats.
disable_notificationBooleanOptionalDelivers silently.
reply_parametersReplyParametersOptionalMakes the message a reply.
reply_markupInlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReplyOptionalKeyboard attached below the contact card.

Returns — Message

Common errors400 chat not found · 403 bot was blocked by the user · 400 not enough rights

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendContact \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":123456789,"phone_number":"+998901234567","first_name":"Dispatch","last_name":"Desk"}'
python
result = await bot.send_contact(
    chat_id=123456789,
    phone_number="+998901234567",
    first_name="Dispatch",
    last_name="Desk",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendContact", [
    'chat_id' => 123456789,
    'phone_number' => '+998901234567',
    'first_name' => 'Dispatch',
    'last_name' => 'Desk'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4830,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": 123456789,
      "first_name": "Aziz",
      "username": "aziz",
      "type": "private"
    },
    "date": 1784463510,
    "contact": {
      "phone_number": "+998901234567",
      "first_name": "Dispatch",
      "last_name": "Desk"
    }
  }
}

sendPoll planned

Sends a native poll or a quiz with one correct answer.

With the default is_anonymous: true you receive aggregate poll updates and never learn who voted. Set it to false if you need poll_answer updates identifying voters — that choice cannot be changed after sending.

Both poll and poll_answer must be listed in allowed_updates to arrive at all; they are excluded from the default set.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
questionStringYesThe question, 1-300 characters.
optionsArray of InputPollOptionYes2 to 10 answers, each up to 100 characters. Duplicate option text is accepted and becomes impossible to tell apart in the results.
is_anonymousBooleanOptionalTrue by default. Anonymous polls hide who voted from everyone, including the bot.
typeStringOptionalregular (default) or quiz. A quiz has exactly one correct answer and reveals it once a user votes.
allows_multiple_answersBooleanOptionalLets a voter pick several options. Regular polls only — a quiz always takes a single answer.
correct_option_idIntegerOptionalZero-based index of the right answer. Required when type is quiz and rejected otherwise. Off-by-one here silently marks the wrong option correct.
explanationStringOptionalUp to 200 characters shown after a quiz answer, typically the reasoning. Quiz polls only.
open_periodIntegerOptionalSeconds the poll accepts votes, 5-600. Mutually exclusive with close_date.
close_dateIntegerOptionalUnix time at which voting stops, between 5 and 600 seconds from now. Mutually exclusive with open_period.
message_thread_idIntegerOptionalTarget topic in a forum supergroup.
disable_notificationBooleanOptionalDelivers silently.
reply_parametersReplyParametersOptionalMakes the message a reply.
reply_markupInlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReplyOptionalKeyboard attached below the poll.

Returns — Message

Common errors400 chat not found · 400 not enough rights · 400 message is too long

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendPoll \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"question":"Which forecast window is most useful to you?","options":[{"text":"Next 6 hours"},{"text":"Today"},{"text":"Three days"}],"is_anonymous":false}'
python
result = await bot.send_poll(
    chat_id=-1001234567890,
    question="Which forecast window is most useful to you?",
    options=[
        {
            "text": "Next 6 hours"
        },
        {
            "text": "Today"
        },
        {
            "text": "Three days"
        }
    ],
    is_anonymous=False,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendPoll", [
    'chat_id' => -1001234567890,
    'question' => 'Which forecast window is most useful to you?',
    'options' => [
        [
            'text' => 'Next 6 hours'
        ],
        [
            'text' => 'Today'
        ],
        [
            'text' => 'Three days'
        ]
    ],
    'is_anonymous' => false
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 123,
    "sender_chat": {
      "id": -1001234567890,
      "title": "Tashkent Weather",
      "username": "tashkent_weather",
      "type": "channel"
    },
    "chat": {
      "id": -1001234567890,
      "title": "Tashkent Weather",
      "username": "tashkent_weather",
      "type": "channel"
    },
    "date": 1784463600,
    "poll": {
      "id": "5842019374018273",
      "question": "Which forecast window is most useful to you?",
      "options": [
        {
          "text": "Next 6 hours",
          "voter_count": 0
        },
        {
          "text": "Today",
          "voter_count": 0
        },
        {
          "text": "Three days",
          "voter_count": 0
        }
      ],
      "total_voter_count": 0,
      "is_closed": false,
      "is_anonymous": false,
      "type": "regular",
      "allows_multiple_answers": false
    }
  }
}

sendDice planned

Sends an animated emoji whose landing value the server decides.

The value is returned in message.dice.value straight away, before the animation finishes on the recipient's screen. If you build a game on this, do not announce the outcome in a follow-up message immediately — you will spoil the roll.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
emojiStringOptionalWhich animation to roll, defaulting to the die. The value range depends on it: 1-6 for dice, darts and bowling, 1-5 for basketball and football, 1-64 for the slot machine. Any other emoji is rejected.
message_thread_idIntegerOptionalTarget topic in a forum supergroup.
disable_notificationBooleanOptionalDelivers silently.
protect_contentBooleanOptionalBlocks forwarding and saving of this message.
reply_parametersReplyParametersOptionalMakes the message a reply.
reply_markupInlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReplyOptionalKeyboard attached below the animation.

Returns — Message

Common errors400 chat not found · 403 bot was blocked by the user · 400 not enough rights

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendDice \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":123456789,"emoji":"🎯"}'
python
result = await bot.send_dice(
    chat_id=123456789,
    emoji="🎯",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendDice", [
    'chat_id' => 123456789,
    'emoji' => '🎯'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4831,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": 123456789,
      "first_name": "Aziz",
      "username": "aziz",
      "type": "private"
    },
    "date": 1784463690,
    "dice": {
      "emoji": "🎯",
      "value": 4
    }
  }
}

sendChatAction planned

Shows a transient status such as "typing..." at the top of the chat.

The indicator clears after 5 seconds, or the moment the bot sends a message — whichever comes first. For work that runs longer, repeat the call every 4-5 seconds instead of once at the start.

Only worth sending when the reply will take a noticeable time to produce. Ahead of an instant response it renders as a flicker, and it counts against your rate limit like any other call.

ParameterTypeRequiredDescription
chat_idInteger or StringYesPositive for a private chat, negative for a group, -100-prefixed for a channel or supergroup.
actionStringYesOne of typing, upload_photo, record_video, upload_video, record_voice, upload_voice, upload_document, choose_sticker, find_location, record_video_note, upload_video_note. Pick the one matching what you are actually about to send — the client renders different wording for each.
message_thread_idIntegerOptionalTarget topic in a forum supergroup. Without it the indicator appears in General rather than the topic the user is reading.

Returns — True on success

Common errors400 chat not found · 403 bot is not a member of the chat · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendChatAction \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":123456789,"action":"typing"}'
python
result = await bot.send_chat_action(
    chat_id=123456789,
    action="typing",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendChatAction", [
    'chat_id' => 123456789,
    'action' => 'typing'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

Editing and deleting

Change or remove a message the bot can act on.

editMessageText planned

Replaces the text of a message the bot sent earlier.

Address the message either by chat_id + message_id or by inline_message_id — never both, never neither. Mixing the two forms is the most common failure with every edit* method.

A bot can only edit its own messages, and only within 48 hours of sending. After that the call fails with edit_time_expired; delete and re-send instead. Editing to text identical to the current text is rejected as well, so guard your update loop with a comparison before calling.

ParameterTypeRequiredDescription
chat_idInteger or StringOptionalTarget chat when editing a normal message. Required unless inline_message_id is given. Positive for private chats, negative for groups, -100-prefixed for channels; a channel @username also works.
message_idIntegerOptionalId of the message to edit inside chat_id. Required unless inline_message_id is given. Message ids are unique per chat, so an id from another chat resolves to the wrong message or to nothing.
inline_message_idStringOptionalIdentifier of a message the bot sent through inline mode. Required if chat_id and message_id are not given. Such messages have no chat to address, which is why the result is True rather than a Message.
textStringYesNew text, 1-4096 characters counted in UTF-16 code units. Empty strings are rejected — use deleteMessage to remove a message.
parse_modeStringOptionalMarkdownV2 or HTML. Interpolating user-supplied text without escaping the reserved characters is what usually produces cant_parse_entities.
entitiesArray of MessageEntityOptionalExplicit formatting ranges instead of markup. Ignored when parse_mode is set. Offsets and lengths are UTF-16 code units, so one emoji advances the offset by 2.
link_preview_optionsLinkPreviewOptionsOptionalControls the preview generated for links in the new text. Omitting it does not keep the original message's preview settings — the edit re-evaluates them from scratch.
reply_markupInlineKeyboardMarkupOptionalNew inline keyboard. Omitting it removes the existing keyboard; pass the old markup again to keep the buttons while changing only the text.

Returns — Message on success, or True when editing an inline message

Common errors400 message to edit not found · 400 message can't be edited · 400 message can't be edited · 400 can't parse entities

bash
curl https://api.casinoplaneta.info/bot$TOKEN/editMessageText \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_id":4187,"text":"Order #8812 — <b>shipped</b>","parse_mode":"HTML"}'
python
result = await bot.edit_message_text(
    chat_id=-1001234567890,
    message_id=4187,
    text="Order #8812 — <b>shipped</b>",
    parse_mode="HTML",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/editMessageText", [
    'chat_id' => -1001234567890,
    'message_id' => 4187,
    'text' => 'Order #8812 — <b>shipped</b>',
    'parse_mode' => 'HTML'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4187,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": -1001234567890,
      "title": "Grom Status",
      "type": "channel"
    },
    "date": 1784500000,
    "edit_date": 1784500420,
    "text": "Order #8812 — shipped",
    "entities": [
      {
        "type": "bold",
        "offset": 14,
        "length": 7
      }
    ]
  }
}

editMessageCaption planned

Replaces the caption of a photo, video, audio, animation or document the bot sent.

Same either/or addressing as editMessageText: chat_id + message_id for a normal message, inline_message_id for an inline one.

This method never touches the media itself — use editMessageMedia for that. Calling it on a plain text message fails, since text messages have no caption.

ParameterTypeRequiredDescription
chat_idInteger or StringOptionalTarget chat. Required unless inline_message_id is given. Positive for private chats, negative for groups, -100-prefixed for channels.
message_idIntegerOptionalId of the media message whose caption changes. Required unless inline_message_id is given.
inline_message_idStringOptionalIdentifier of an inline message. Required if chat_id and message_id are not given.
captionStringOptionalNew caption, 0-1024 characters in UTF-16 code units. Omit it or send an empty string to strip the caption entirely while leaving the media in place.
parse_modeStringOptionalMarkdownV2 or HTML for the caption markup.
caption_entitiesArray of MessageEntityOptionalExplicit formatting ranges for the caption. Ignored when parse_mode is set.
show_caption_above_mediaBooleanOptionalRenders the caption above the media instead of below it. Ignored for audio and document messages, which always caption below.
reply_markupInlineKeyboardMarkupOptionalNew inline keyboard. Omitting it drops the existing buttons.

Returns — Message on success, or True when editing an inline message

Common errors400 message to edit not found · 400 message can't be edited · 400 message can't be edited · 400 message is too long

bash
curl https://api.casinoplaneta.info/bot$TOKEN/editMessageCaption \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_id":4190,"caption":"Floor plan, rev 3 — approved","parse_mode":"HTML"}'
python
result = await bot.edit_message_caption(
    chat_id=-1001234567890,
    message_id=4190,
    caption="Floor plan, rev 3 — approved",
    parse_mode="HTML",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/editMessageCaption", [
    'chat_id' => -1001234567890,
    'message_id' => 4190,
    'caption' => 'Floor plan, rev 3 — approved',
    'parse_mode' => 'HTML'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4190,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": -1001234567890,
      "title": "Grom Status",
      "type": "channel"
    },
    "date": 1784499000,
    "edit_date": 1784500500,
    "photo": [
      {
        "file_id": "AgACAgIAAxkBAAIEh2ZdT3mQ",
        "file_unique_id": "AQADr8cxG7f2",
        "width": 320,
        "height": 240,
        "file_size": 12043
      }
    ],
    "caption": "Floor plan, rev 3 — approved"
  }
}

editMessageMedia planned

Replaces the media of an existing message, optionally changing its caption at the same time.

Addressed the same either/or way: chat_id + message_id, or inline_message_id.

A message can only swap media for a compatible kind — audio stays audio, a document stays a document, and a message inside an album can only become a photo or a video. To attach a freshly uploaded file, post the request as multipart/form-data and reference the part as attach://<name> in the media field; otherwise pass a file_id or an HTTP URL.

ParameterTypeRequiredDescription
chat_idInteger or StringOptionalTarget chat. Required unless inline_message_id is given.
message_idIntegerOptionalId of the message whose media is replaced. Required unless inline_message_id is given.
inline_message_idStringOptionalIdentifier of an inline message. Required if chat_id and message_id are not given.
mediaInputMediaYesJSON-serialized description of the new media: its type, the file to use, and any caption. The caption fields here replace the old caption — leaving them out clears it rather than preserving it.
reply_markupInlineKeyboardMarkupOptionalNew inline keyboard. Omitting it removes the existing buttons.

Returns — Message on success, or True when editing an inline message

Common errors400 message to edit not found · 400 message can't be edited · 400 wrong file type · 400 message can't be edited

bash
curl https://api.casinoplaneta.info/bot$TOKEN/editMessageMedia \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_id":4190,"media":{"type":"photo","media":"AgACAgIAAxkBAAIEi2ZdT4KrN9wQ","caption":"Floor plan, rev 4"}}'
python
result = await bot.edit_message_media(
    chat_id=-1001234567890,
    message_id=4190,
    media={
        "type": "photo",
        "media": "AgACAgIAAxkBAAIEi2ZdT4KrN9wQ",
        "caption": "Floor plan, rev 4"
    },
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/editMessageMedia", [
    'chat_id' => -1001234567890,
    'message_id' => 4190,
    'media' => [
        'type' => 'photo',
        'media' => 'AgACAgIAAxkBAAIEi2ZdT4KrN9wQ',
        'caption' => 'Floor plan, rev 4'
    ]
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4190,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": -1001234567890,
      "title": "Grom Status",
      "type": "channel"
    },
    "date": 1784499000,
    "edit_date": 1784500900,
    "photo": [
      {
        "file_id": "AgACAgIAAxkBAAIEi2ZdT4KrN9wQ",
        "file_unique_id": "AQADs9AxG7f9",
        "width": 1280,
        "height": 960,
        "file_size": 184320
      }
    ],
    "caption": "Floor plan, rev 4"
  }
}

editMessageReplyMarkup planned

Changes only the inline keyboard attached to a message, leaving its content untouched.

Addressed either by chat_id + message_id or by inline_message_id.

This is the method to call from a callback_query handler when a button press should change the buttons — updating a vote count or disabling a used action — without re-sending the text. Passing an empty inline_keyboard array removes the keyboard entirely.

ParameterTypeRequiredDescription
chat_idInteger or StringOptionalTarget chat. Required unless inline_message_id is given.
message_idIntegerOptionalId of the message whose keyboard changes. Required unless inline_message_id is given.
inline_message_idStringOptionalIdentifier of an inline message. Required if chat_id and message_id are not given.
reply_markupInlineKeyboardMarkupOptionalThe keyboard to show. Omit it, or send {"inline_keyboard": []}, to strip the buttons.

Returns — Message on success, or True when editing an inline message

Common errors400 message to edit not found · 400 message can't be edited · 400 message can't be edited

bash
curl https://api.casinoplaneta.info/bot$TOKEN/editMessageReplyMarkup \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_id":4187,"reply_markup":{"inline_keyboard":[[{"text":"Track parcel","url":"https://example.com/t/8812"}]]}}'
python
result = await bot.edit_message_reply_markup(
    chat_id=-1001234567890,
    message_id=4187,
    reply_markup={
        "inline_keyboard": [
            [
                {
                    "text": "Track parcel",
                    "url": "https://example.com/t/8812"
                }
            ]
        ]
    },
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/editMessageReplyMarkup", [
    'chat_id' => -1001234567890,
    'message_id' => 4187,
    'reply_markup' => [
        'inline_keyboard' => [
            [
                [
                    'text' => 'Track parcel',
                    'url' => 'https://example.com/t/8812'
                ]
            ]
        ]
    ]
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4187,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": -1001234567890,
      "title": "Grom Status",
      "type": "channel"
    },
    "date": 1784500000,
    "edit_date": 1784501000,
    "text": "Order #8812 — shipped",
    "reply_markup": {
      "inline_keyboard": [
        [
          {
            "text": "Track parcel",
            "url": "https://example.com/t/8812"
          }
        ]
      ]
    }
  }
}

stopPoll planned

Closes a poll the bot sent and returns its final state.

There is no inline_message_id form here — polls cannot be sent through inline mode, so the message is always addressed by chat_id + message_id.

Closing is permanent: a stopped poll cannot be reopened and accepts no further votes. The returned Poll carries the frozen counts, which is the only reliable way to read final results for an anonymous poll.

ParameterTypeRequiredDescription
chat_idInteger or StringYesChat holding the poll. Positive for private chats, negative for groups, -100-prefixed for channels.
message_idIntegerYesId of the message containing the poll — not the poll's own id from the Poll object. Passing the poll id here yields message_not_found.
reply_markupInlineKeyboardMarkupOptionalInline keyboard to leave on the closed poll, typically a link to results or a follow-up action.

Returns — Poll — the final state of the stopped poll

Common errors400 message to edit not found · 400 message can't be edited · 400 not enough rights · 400 chat not found

bash
curl https://api.casinoplaneta.info/bot$TOKEN/stopPoll \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_id":4201}'
python
result = await bot.stop_poll(
    chat_id=-1001234567890,
    message_id=4201,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/stopPoll", [
    'chat_id' => -1001234567890,
    'message_id' => 4201
])->json();

Response

json
{
  "ok": true,
  "result": {
    "id": "5182946103772",
    "question": "Ship on Friday?",
    "options": [
      {
        "text": "Yes",
        "voter_count": 31
      },
      {
        "text": "No",
        "voter_count": 4
      }
    ],
    "total_voter_count": 35,
    "is_closed": true,
    "is_anonymous": true,
    "type": "regular",
    "allows_multiple_answers": false
  }
}

deleteMessage planned

Deletes a single message for everyone in the chat.

The bot can delete its own outgoing messages in private chats, groups and supergroups, and incoming messages in private chats. To delete anyone else's message in a group or channel it needs the can_delete_messages administrator right.

A message can only be deleted within 48 hours of being sent. Service messages announcing the creation of a supergroup, channel or forum topic cannot be deleted at all.

ParameterTypeRequiredDescription
chat_idInteger or StringYesChat containing the message. Positive for private chats, negative for groups, -100-prefixed for channels.
message_idIntegerYesId of the message to delete. Store it together with its chat_id — the id alone is ambiguous across chats and will delete nothing, or resolve elsewhere.

Returns — True on success

Common errors400 message to edit not found · 400 not enough rights · 400 chat not found

bash
curl https://api.casinoplaneta.info/bot$TOKEN/deleteMessage \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_id":4187}'
python
result = await bot.delete_message(
    chat_id=-1001234567890,
    message_id=4187,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/deleteMessage", [
    'chat_id' => -1001234567890,
    'message_id' => 4187
])->json();

Response

json
{
  "ok": true,
  "result": true
}

deleteMessages planned

Deletes several messages in one chat with a single request.

Takes 1-100 ids per call and applies the same per-message rules as deleteMessage: the 48-hour window, ownership, and the can_delete_messages right for other people's messages.

Messages that cannot be deleted are skipped silently. A true result therefore does not prove every id was removed — if you need certainty, verify individually rather than trusting the return value.

ParameterTypeRequiredDescription
chat_idInteger or StringYesChat containing the messages. All ids must belong to this one chat; there is no cross-chat form.
message_idsArray of IntegerYes1-100 message ids to delete. Anything outside that range is rejected outright, so batch larger clean-ups yourself and pace them to avoid flood_wait.

Returns — True on success

Common errors400 chat not found · 400 not enough rights · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/deleteMessages \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_ids":[4187,4188,4189]}'
python
result = await bot.delete_messages(
    chat_id=-1001234567890,
    message_ids=[
        4187,
        4188,
        4189
    ],
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/deleteMessages", [
    'chat_id' => -1001234567890,
    'message_ids' => [
        4187,
        4188,
        4189
    ]
])->json();

Response

json
{
  "ok": true,
  "result": true
}

Forwarding and copying

Forward keeps the attribution header; copy does not.

forwardMessage planned

Forwards a message to another chat, keeping the attribution header.

The forwarded copy shows where it came from, and the recipient can tap through to the original. If the original author has forwarding privacy enabled, the header names them without a link. Use copyMessage when you do not want any attribution.

The bot must be able to read from_chat_id and to post in chat_id. Service messages, and messages from chats that have content protection enabled, cannot be forwarded at all.

ParameterTypeRequiredDescription
chat_idInteger or StringYesDestination chat. Positive for private chats, negative for groups, -100-prefixed for channels; a channel @username also works.
message_thread_idIntegerOptionalTarget topic inside a forum-enabled supergroup. Ignored elsewhere; omitting it in a forum drops the message into the General topic.
from_chat_idInteger or StringYesChat the original message lives in, using the same sign convention. The bot has to still be a member — leaving the source chat makes its message ids unusable.
message_idIntegerYesId of the message inside from_chat_id. Ids are scoped per chat, so pairing an id with the wrong from_chat_id gives message_not_found rather than a wrong forward.
disable_notificationBooleanOptionalDelivers silently: the message still arrives, recipients simply get no sound or vibration.
protect_contentBooleanOptionalBlocks saving and further forwarding of the copy in the destination chat. It does not retroactively protect the original.

Returns — Message — the forwarded message as it exists in the destination chat

Common errors400 chat not found · 400 message to edit not found · 403 bot is not a member of the chat · 400 not enough rights

bash
curl https://api.casinoplaneta.info/bot$TOKEN/forwardMessage \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":194837452,"from_chat_id":-1001234567890,"message_id":4187}'
python
result = await bot.forward_message(
    chat_id=194837452,
    from_chat_id=-1001234567890,
    message_id=4187,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/forwardMessage", [
    'chat_id' => 194837452,
    'from_chat_id' => -1001234567890,
    'message_id' => 4187
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 902,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": 194837452,
      "first_name": "Dilnoza",
      "type": "private"
    },
    "date": 1784501100,
    "forward_origin": {
      "type": "channel",
      "chat": {
        "id": -1001234567890,
        "title": "Grom Status",
        "type": "channel"
      },
      "message_id": 4187,
      "date": 1784500000
    },
    "text": "Order #8812 — shipped"
  }
}

forwardMessages planned

Forwards up to 100 messages from one chat to another in a single request.

message_ids must be listed in increasing order and all come from the same from_chat_id. Messages that belong to one album stay grouped in the destination.

Messages that cannot be forwarded are skipped without an error, so the returned array can be shorter than the ids you sent — compare lengths if that matters. If none can be forwarded, the call fails instead.

ParameterTypeRequiredDescription
chat_idInteger or StringYesDestination chat. Positive for private chats, negative for groups, -100-prefixed for channels.
message_thread_idIntegerOptionalTarget topic inside a forum-enabled supergroup. Ignored in non-forum chats.
from_chat_idInteger or StringYesSource chat for every id in message_ids. There is no way to mix sources in one call.
message_idsArray of IntegerYes1-100 ids to forward, in increasing order. An unsorted array is rejected rather than silently sorted.
disable_notificationBooleanOptionalDelivers the whole batch silently.
protect_contentBooleanOptionalBlocks saving and further forwarding of the forwarded copies.

Returns — Array of MessageId — one entry per message actually forwarded

Common errors400 chat not found · 403 bot is not a member of the chat · 400 not enough rights · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/forwardMessages \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":194837452,"from_chat_id":-1001234567890,"message_ids":[4187,4188,4189]}'
python
result = await bot.forward_messages(
    chat_id=194837452,
    from_chat_id=-1001234567890,
    message_ids=[
        4187,
        4188,
        4189
    ],
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/forwardMessages", [
    'chat_id' => 194837452,
    'from_chat_id' => -1001234567890,
    'message_ids' => [
        4187,
        4188,
        4189
    ]
])->json();

Response

json
{
  "ok": true,
  "result": [
    {
      "message_id": 903
    },
    {
      "message_id": 904
    },
    {
      "message_id": 905
    }
  ]
}

copyMessage planned

Sends a copy of a message without any link back to the original.

A copy behaves exactly like a freshly sent message: no attribution header, and no relationship to the source afterwards. Editing or deleting the original leaves the copy untouched.

Because of that, the result is a MessageId, not a full Message — you get the new message_id and nothing else. Poll messages and service messages cannot be copied, and a quiz can only be copied if the bot knows its correct_option_id.

ParameterTypeRequiredDescription
chat_idInteger or StringYesDestination chat. Positive for private chats, negative for groups, -100-prefixed for channels.
message_thread_idIntegerOptionalTarget topic inside a forum-enabled supergroup. Ignored elsewhere.
from_chat_idInteger or StringYesChat the original message lives in. The bot must be able to read it at the time of the call.
message_idIntegerYesId of the message to copy inside from_chat_id.
captionStringOptionalReplaces the original caption, 0-1024 characters in UTF-16 code units. Only applies to media; on a text message it is ignored. Omit it to keep the original caption.
parse_modeStringOptionalMarkdownV2 or HTML for the new caption. Has no effect unless caption is set.
caption_entitiesArray of MessageEntityOptionalExplicit formatting ranges for the new caption. Ignored when parse_mode is set.
disable_notificationBooleanOptionalDelivers silently, without sound or vibration.
protect_contentBooleanOptionalBlocks saving and forwarding of the copy.
reply_parametersReplyParametersOptionalMakes the copy a reply to a message in the destination chat. The id must exist there, not in from_chat_id — replying to the original by its source id is the usual mistake here.
reply_markupInlineKeyboardMarkupOptionalInline keyboard for the copy. The original message's buttons are never carried over — attach them again here if you need them.

Returns — MessageId — the id of the new message, not a full Message

Common errors400 chat not found · 400 message to edit not found · 403 bot was blocked by the user · 400 message to reply not found

bash
curl https://api.casinoplaneta.info/bot$TOKEN/copyMessage \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":194837452,"from_chat_id":-1001234567890,"message_id":4190,"caption":"Floor plan for your unit","disable_notification":true}'
python
result = await bot.copy_message(
    chat_id=194837452,
    from_chat_id=-1001234567890,
    message_id=4190,
    caption="Floor plan for your unit",
    disable_notification=True,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/copyMessage", [
    'chat_id' => 194837452,
    'from_chat_id' => -1001234567890,
    'message_id' => 4190,
    'caption' => 'Floor plan for your unit',
    'disable_notification' => true
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 906
  }
}

copyMessages planned

Copies up to 100 messages into another chat without attribution.

Same ordering rules as forwardMessages: 1-100 ids, increasing, all from one source chat, with albums kept together. Messages that cannot be copied are skipped silently.

Captions cannot be rewritten in bulk — remove_caption is the only caption control, and it strips every caption in the batch. Copy one at a time with copyMessage if you need to change the text.

ParameterTypeRequiredDescription
chat_idInteger or StringYesDestination chat. Positive for private chats, negative for groups, -100-prefixed for channels.
message_thread_idIntegerOptionalTarget topic inside a forum-enabled supergroup. Ignored in non-forum chats.
from_chat_idInteger or StringYesSource chat for every id in message_ids.
message_idsArray of IntegerYes1-100 ids to copy, in increasing order. An unsorted array is rejected.
disable_notificationBooleanOptionalDelivers the whole batch silently.
protect_contentBooleanOptionalBlocks saving and forwarding of the copies.
remove_captionBooleanOptionalDrops the captions of all copied media. There is no per-message form — it is all captions or none.

Returns — Array of MessageId — one entry per message actually copied

Common errors400 chat not found · 403 bot is not a member of the chat · 400 not enough rights · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/copyMessages \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":194837452,"from_chat_id":-1001234567890,"message_ids":[4190,4191,4192],"remove_caption":true}'
python
result = await bot.copy_messages(
    chat_id=194837452,
    from_chat_id=-1001234567890,
    message_ids=[
        4190,
        4191,
        4192
    ],
    remove_caption=True,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/copyMessages", [
    'chat_id' => 194837452,
    'from_chat_id' => -1001234567890,
    'message_ids' => [
        4190,
        4191,
        4192
    ],
    'remove_caption' => true
])->json();

Response

json
{
  "ok": true,
  "result": [
    {
      "message_id": 907
    },
    {
      "message_id": 908
    },
    {
      "message_id": 909
    }
  ]
}

Files

Upload by multipart, re-send by file_id, download by path.

getFile planned

Resolves a file_id into a temporary path you can download from.

Two steps, two different hosts. getFile is a normal API call against the API base; the bytes are then fetched from the file path, which is a different URL shape:

GET https://api.casinoplaneta.info/file/bot<TOKEN>/<file_path>

Concatenating file_path onto the API base instead is the usual mistake and produces a 404.

file_path is valid for a limited time and then stops resolving. Do not store it — store the file_id and call getFile again when you next need the bytes. The file_id stays valid indefinitely for the bot that received it.

Media that has just been uploaded may still be in post-processing (thumbnails, waveform, transcode). That returns file_not_ready, which is genuinely transient — retry after a short delay rather than treating it as a failure.

ParameterTypeRequiredDescription
file_idStringYesIdentifier taken from an incoming message's photo, document, voice or video object. It is scoped to the bot that received it — a file_id from another bot, or one a user pasted in, will not resolve.

Returns — File

Common errors400 file is not ready · 413 Request Entity Too Large · 401 Unauthorized · 429 Too Many Requests: retry after N · 500 Internal Server Error: temporarily unavailable · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getFile \
  -H 'Content-Type: application/json' \
  -d '{"file_id":"BQACAgIAAxkBAAIEd2ZKq1nT3rXfPmLdAAHc9Q"}'
python
result = await bot.get_file(
    file_id="BQACAgIAAxkBAAIEd2ZKq1nT3rXfPmLdAAHc9Q",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/getFile", [
    'file_id' => 'BQACAgIAAxkBAAIEd2ZKq1nT3rXfPmLdAAHc9Q'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "file_id": "BQACAgIAAxkBAAIEd2ZKq1nT3rXfPmLdAAHc9Q",
    "file_unique_id": "AgAD9wADr2xZSw",
    "file_size": 184320,
    "file_path": "documents/file_204.pdf"
  }
}

Chat management

Read chat metadata and administer members. A bot can never exceed the rights a member would have.

getChat planned

Returns up-to-date information about a single chat.

This is the only way to read a chat's current configuration rather than the snapshot embedded in an update. Slow-mode interval, the allowed-reactions whitelist, the default member permissions and the pinned message all come from here.

The object is heavier than the chat field inside a Message — it carries description, photo, permissions and pinned_message, which updates never include. Cache it; do not call it before every send.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget chat: a positive id for a private chat, a negative id for a group, a -100-prefixed id for a channel, or @username for a public channel or supergroup.

Returns — Chat

Common errors400 chat not found · 403 bot is not a member of the chat · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getChat \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890}'
python
result = await bot.get_chat(
    chat_id=-1001234567890,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/getChat", [
    'chat_id' => -1001234567890
])->json();

Response

json
{
  "ok": true,
  "result": {
    "id": -1001234567890,
    "type": "supergroup",
    "title": "GROM Developers",
    "username": "gromdev",
    "description": "Bot API questions and release notes.",
    "invite_link": "https://code.casinoplaneta.info/+kL9xQ2mVr4A",
    "slow_mode_delay": 30,
    "permissions": {
      "can_send_messages": true,
      "can_send_media_messages": true,
      "can_send_polls": false,
      "can_send_other_messages": true,
      "can_add_web_page_previews": true,
      "can_change_info": false,
      "can_invite_users": true,
      "can_pin_messages": false
    }
  }
}

getChatAdministrators planned

Lists the administrators of a group, supergroup or channel.

Other bots are omitted from the result, including the calling bot itself. To read your own rights, call getChatMember with the bot's own id.

In a group where every member is an administrator the list contains everyone, which can be large. Prefer getChatMember when you only need to check one person.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget group, supergroup or channel. Private chats have no administrators and return chat_not_found.

Returns — Array of ChatMember

Common errors400 chat not found · 403 bot is not a member of the chat · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getChatAdministrators \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890}'
python
result = await bot.get_chat_administrators(
    chat_id=-1001234567890,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/getChatAdministrators", [
    'chat_id' => -1001234567890
])->json();

Response

json
{
  "ok": true,
  "result": [
    {
      "status": "creator",
      "user": {
        "id": 4210871,
        "is_bot": false,
        "first_name": "Aziz",
        "username": "aziz"
      },
      "is_anonymous": false
    },
    {
      "status": "administrator",
      "user": {
        "id": 5590124,
        "is_bot": false,
        "first_name": "Dilnoza",
        "username": "dilnoza"
      },
      "can_be_edited": false,
      "is_anonymous": false,
      "can_manage_chat": true,
      "can_delete_messages": true,
      "can_restrict_members": true,
      "can_promote_members": false,
      "can_change_info": true,
      "can_invite_users": true,
      "can_pin_messages": true
    }
  ]
}

getChatMemberCount planned

Returns the number of members in a chat.

The count includes bots and, in channels, counts subscribers. It is served from a cached counter and can lag a few seconds behind a burst of joins or leaves — do not use it to detect a specific person entering.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget group, supergroup or channel.

Returns — Integer

Common errors400 chat not found · 403 bot is not a member of the chat · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getChatMemberCount \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890}'
python
result = await bot.get_chat_member_count(
    chat_id=-1001234567890,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/getChatMemberCount", [
    'chat_id' => -1001234567890
])->json();

Response

json
{
  "ok": true,
  "result": 1842
}

getChatMember planned

Returns information about one member of a chat, including their status and rights.

Call it with the bot's own id to discover exactly which administrator rights the bot currently holds. That is the fastest way to diagnose a not_enough_rights failure, because the returned ChatMember names the missing right directly.

status is one of creator, administrator, member, restricted, left or kicked. A user who never joined and a user who left both report left, so the two cases are indistinguishable.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget group, supergroup or channel.
user_idIntegerYesAccount to look up. Passing an id the bot has never seen returns chat_not_found rather than an empty member object.

Returns — ChatMember

Common errors400 chat not found · 403 bot is not a member of the chat · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getChatMember \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"user_id":5590124}'
python
result = await bot.get_chat_member(
    chat_id=-1001234567890,
    user_id=5590124,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/getChatMember", [
    'chat_id' => -1001234567890,
    'user_id' => 5590124
])->json();

Response

json
{
  "ok": true,
  "result": {
    "status": "restricted",
    "user": {
      "id": 5590124,
      "is_bot": false,
      "first_name": "Dilnoza",
      "username": "dilnoza"
    },
    "is_member": true,
    "until_date": 1789600000,
    "can_send_messages": false,
    "can_send_media_messages": false,
    "can_send_polls": false,
    "can_send_other_messages": false,
    "can_add_web_page_previews": false,
    "can_change_info": false,
    "can_invite_users": false,
    "can_pin_messages": false
  }
}

banChatMember planned

Removes a user from a group, supergroup or channel and prevents them from returning.

until_date is an absolute Unix timestamp, not a duration. Values less than 30 seconds or more than 366 days from now are treated as a permanent ban, and omitting the field bans forever. A ban that is permanent can only be lifted with unbanChatMember; a timed ban lifts itself but the user must still rejoin on their own.

The bot needs can_restrict_members, and it can never ban someone a human administrator with the same rights could not — the chat owner and administrators promoted by other administrators are out of reach, which surfaces as not_enough_rights.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget group, supergroup or channel.
user_idIntegerYesAccount to ban.
until_dateIntegerOptionalUnix time when the ban is lifted. Omit for a permanent ban. Applies to supergroups and channels only — in basic groups every ban is permanent.
revoke_messagesBooleanOptionalPass true to delete every message the user ever sent in the chat. Irreversible, and always true in channels regardless of what you send.

Returns — True on success

Common errors400 not enough rights · 400 chat not found · 403 bot is not a member of the chat · 403 user is banned · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/banChatMember \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"user_id":5590124,"until_date":1789000000,"revoke_messages":false}'
python
result = await bot.ban_chat_member(
    chat_id=-1001234567890,
    user_id=5590124,
    until_date=1789000000,
    revoke_messages=False,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/banChatMember", [
    'chat_id' => -1001234567890,
    'user_id' => 5590124,
    'until_date' => 1789000000,
    'revoke_messages' => false
])->json();

Response

json
{
  "ok": true,
  "result": true
}

unbanChatMember planned

Lifts a ban so the user is able to join the chat again.

Unbanning does not put the user back in the chat. It only clears the ban record; the person has to rejoin themselves, through an invite link or by searching for a public chat.

Without only_if_banned, calling this on a current member removes them — the call is defined as "make this user a non-member who is not banned", so it kicks first. Always pass only_if_banned: true unless you specifically want that behaviour.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget group, supergroup or channel.
user_idIntegerYesAccount to unban.
only_if_bannedBooleanOptionalPass true to make the call a no-op when the user is not banned. Defaults to false, which will remove a user who is currently a member.

Returns — True on success

Common errors400 not enough rights · 400 chat not found · 403 bot is not a member of the chat · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/unbanChatMember \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"user_id":5590124,"only_if_banned":true}'
python
result = await bot.unban_chat_member(
    chat_id=-1001234567890,
    user_id=5590124,
    only_if_banned=True,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/unbanChatMember", [
    'chat_id' => -1001234567890,
    'user_id' => 5590124,
    'only_if_banned' => true
])->json();

Response

json
{
  "ok": true,
  "result": true
}

restrictChatMember planned

Limits what a member of a supergroup may do, optionally for a fixed period.

permissions is a complete ChatPermissions object, not a patch. Any field you omit is read as false, so sending only can_send_messages: false silently strips every other right the member had. Read the current state with getChatMember, change the fields you care about, and send the whole object back.

until_date follows the same rule as banChatMember: under 30 seconds or over 366 days means forever. To lift a restriction early, send the object again with the rights set back to true — there is no separate unrestrict method. Requires can_restrict_members, and the bot cannot restrict anyone it could not restrict as a human administrator.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget supergroup. Basic groups have no per-member permissions and return chat_not_found.
user_idIntegerYesAccount to restrict. Administrators cannot be restricted — demote them first.
permissionsChatPermissionsYesThe full set of rights the member should end up with. Omitted fields become false.
use_independent_chat_permissionsBooleanOptionalPass true to set each media right on its own. By default the media rights are linked, so can_send_polls and the other content rights follow can_send_media_messages.
until_dateIntegerOptionalUnix time when the restriction expires. Omit to restrict indefinitely.

Returns — True on success

Common errors400 not enough rights · 400 chat not found · 403 bot is not a member of the chat · 403 user is banned · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/restrictChatMember \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"user_id":5590124,"permissions":{"can_send_messages":true,"can_send_media_messages":false,"can_send_polls":false,"can_send_other_messages":false,"can_add_web_page_previews":false,"can_change_info":false,"can_invite_users":false,"can_pin_messages":false},"until_date":1789600000}'
python
result = await bot.restrict_chat_member(
    chat_id=-1001234567890,
    user_id=5590124,
    permissions={
        "can_send_messages": True,
        "can_send_media_messages": False,
        "can_send_polls": False,
        "can_send_other_messages": False,
        "can_add_web_page_previews": False,
        "can_change_info": False,
        "can_invite_users": False,
        "can_pin_messages": False
    },
    until_date=1789600000,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/restrictChatMember", [
    'chat_id' => -1001234567890,
    'user_id' => 5590124,
    'permissions' => [
        'can_send_messages' => true,
        'can_send_media_messages' => false,
        'can_send_polls' => false,
        'can_send_other_messages' => false,
        'can_add_web_page_previews' => false,
        'can_change_info' => false,
        'can_invite_users' => false,
        'can_pin_messages' => false
    ],
    'until_date' => 1789600000
])->json();

Response

json
{
  "ok": true,
  "result": true
}

promoteChatMember planned

Promotes or demotes a user by setting their administrator rights.

Every right is a separate boolean and the call is absolute, not incremental: passing all of them as false demotes the user back to an ordinary member. That is the only way to demote — there is no demoteChatMember.

A bot can only grant rights it holds itself, and can only edit administrators it promoted. Granting a right the bot does not have fails with not_enough_rights; the bot always needs can_promote_members to call this at all.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget group, supergroup or channel.
user_idIntegerYesAccount to promote or demote.
is_anonymousBooleanOptionalHide the administrator's account in the member list, and post under the chat name instead of their own.
can_manage_chatBooleanOptionalBaseline administrator access: see the admin log, statistics and members. Implied by every other right, so an administrator with any right has this one.
can_delete_messagesBooleanOptionalDelete messages sent by anyone, not only their own.
can_manage_video_chatsBooleanOptionalStart, join and end voice and video chats in the group.
can_restrict_membersBooleanOptionalRestrict, ban and unban members. Required before the account can call banChatMember or restrictChatMember.
can_promote_membersBooleanOptionalPromote further administrators, limited to the rights this account already holds.
can_change_infoBooleanOptionalEdit the title, description and photo of the chat.
can_invite_usersBooleanOptionalAdd members and create invite links.
can_post_messagesBooleanOptionalPublish posts. Channels only; ignored in groups.
can_edit_messagesBooleanOptionalEdit posts written by others and pin them. Channels only; ignored in groups.
can_pin_messagesBooleanOptionalPin and unpin messages. Groups and supergroups only; in channels pinning follows can_edit_messages.
can_manage_topicsBooleanOptionalCreate, rename, close and reopen forum topics. Supergroups with topics enabled only.

Returns — True on success

Common errors400 not enough rights · 400 chat not found · 403 bot is not a member of the chat · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/promoteChatMember \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"user_id":5590124,"can_manage_chat":true,"can_delete_messages":true,"can_restrict_members":true,"can_invite_users":true,"can_pin_messages":true,"can_promote_members":false,"can_change_info":false,"is_anonymous":false}'
python
result = await bot.promote_chat_member(
    chat_id=-1001234567890,
    user_id=5590124,
    can_manage_chat=True,
    can_delete_messages=True,
    can_restrict_members=True,
    can_invite_users=True,
    can_pin_messages=True,
    can_promote_members=False,
    can_change_info=False,
    is_anonymous=False,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/promoteChatMember", [
    'chat_id' => -1001234567890,
    'user_id' => 5590124,
    'can_manage_chat' => true,
    'can_delete_messages' => true,
    'can_restrict_members' => true,
    'can_invite_users' => true,
    'can_pin_messages' => true,
    'can_promote_members' => false,
    'can_change_info' => false,
    'is_anonymous' => false
])->json();

Response

json
{
  "ok": true,
  "result": true
}

setChatTitle planned

Changes the title of a group, supergroup or channel.

The change is announced in the chat as a service message, the same one a human administrator would produce. There is no silent variant.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget group, supergroup or channel. Private chats have no title and return chat_not_found.
titleStringYesNew title, 1–128 characters. Leading and trailing whitespace is trimmed; an empty result is rejected.

Returns — True on success

Common errors400 not enough rights · 400 chat not found · 403 bot is not a member of the chat · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/setChatTitle \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"title":"GROM Developers — Bot API"}'
python
result = await bot.set_chat_title(
    chat_id=-1001234567890,
    title="GROM Developers — Bot API",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/setChatTitle", [
    'chat_id' => -1001234567890,
    'title' => 'GROM Developers — Bot API'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

setChatDescription planned

Changes the description shown on a chat's profile.

Send an empty string to clear the description. Unlike a title change this produces no service message in the chat.

Requires can_change_info. In a group where administrators have restricted who may edit chat info, a bot without that right fails even though the description looks editable to its own administrators.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget group, supergroup or channel.
descriptionStringOptionalNew description, 0–255 characters. Omit or pass an empty string to remove it.

Returns — True on success

Common errors400 not enough rights · 400 chat not found · 403 bot is not a member of the chat · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/setChatDescription \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"description":"Bot API questions, release notes and outage reports."}'
python
result = await bot.set_chat_description(
    chat_id=-1001234567890,
    description="Bot API questions, release notes and outage reports.",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/setChatDescription", [
    'chat_id' => -1001234567890,
    'description' => 'Bot API questions, release notes and outage reports.'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

setChatPhoto planned

Replaces the profile photo of a group, supergroup or channel.

The photo must be uploaded as multipart/form-data. A file_id from an earlier upload is not accepted here, because the server re-encodes the image into the avatar variants instead of reusing the stored file.

The image is cropped to a square from the centre. Upload a square source if you care about the framing.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget group, supergroup or channel.
photoInputFileYesNew photo, uploaded as multipart form data. JPEG or PNG; the bytes are sniffed, so a mislabelled file fails with wrong_file_type regardless of its extension.

Returns — True on success

Common errors400 not enough rights · 400 chat not found · 403 bot is not a member of the chat · 413 Request Entity Too Large · 400 wrong file type · 400 file is not ready · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/setChatPhoto \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"photo":"@/srv/assets/grom-dev-avatar.jpg"}'
python
result = await bot.set_chat_photo(
    chat_id=-1001234567890,
    photo="@/srv/assets/grom-dev-avatar.jpg",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/setChatPhoto", [
    'chat_id' => -1001234567890,
    'photo' => '@/srv/assets/grom-dev-avatar.jpg'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

pinChatMessage planned

Pins a message to the top of a chat.

Requires can_pin_messages in groups and supergroups, and can_edit_messages in channels — the right that governs pinning differs by chat type, which is the usual cause of a not_enough_rights here.

disable_notification defaults to false in groups and supergroups, so every member is notified unless you opt out. In channels the default is inverted: pinning is always silent and the flag has no effect.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget chat. Pinning works in private chats too, where no administrator right is needed.
message_idIntegerYesMessage to pin. Ids are unique per chat, so a valid id from another chat resolves to message_not_found.
disable_notificationBooleanOptionalPass true to pin without notifying members. Ignored in channels, where the pin is silent regardless.

Returns — True on success

Common errors400 not enough rights · 400 chat not found · 403 bot is not a member of the chat · 400 message to edit not found · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/pinChatMessage \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_id":90412,"disable_notification":true}'
python
result = await bot.pin_chat_message(
    chat_id=-1001234567890,
    message_id=90412,
    disable_notification=True,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/pinChatMessage", [
    'chat_id' => -1001234567890,
    'message_id' => 90412,
    'disable_notification' => true
])->json();

Response

json
{
  "ok": true,
  "result": true
}

unpinChatMessage planned

Removes a pinned message from the top of a chat.

Omit message_id to unpin the most recently pinned message. A chat can hold several pins at once, so the version without an id only clears the newest one — call it repeatedly, or pass the specific id you pinned.

The same right applies as for pinning: can_pin_messages in groups, can_edit_messages in channels.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget chat.
message_idIntegerOptionalMessage to unpin. Omit to unpin the most recent pinned message. Passing the id of a message that is not pinned succeeds without doing anything.

Returns — True on success

Common errors400 not enough rights · 400 chat not found · 403 bot is not a member of the chat · 400 message to edit not found · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/unpinChatMessage \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_id":90412}'
python
result = await bot.unpin_chat_message(
    chat_id=-1001234567890,
    message_id=90412,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/unpinChatMessage", [
    'chat_id' => -1001234567890,
    'message_id' => 90412
])->json();

Response

json
{
  "ok": true,
  "result": true
}

leaveChat planned

Removes the bot from a group, supergroup or channel.

Leaving is terminal from the bot's side: it cannot invite itself back and every later call for that chat returns not_a_member. Only a human administrator can re-add it.

The chat history is not deleted, and the bot's own messages stay where they are. A bot cannot leave a private chat — a person ends that conversation by blocking the bot.

ParameterTypeRequiredDescription
chat_idInteger or StringYesGroup, supergroup or channel to leave.

Returns — True on success

Common errors400 chat not found · 403 bot is not a member of the chat · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/leaveChat \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890}'
python
result = await bot.leave_chat(
    chat_id=-1001234567890,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/leaveChat", [
    'chat_id' => -1001234567890
])->json();

Response

json
{
  "ok": true,
  "result": true
}

Named links, join requests, revocation.

Creates an additional invite link for a group or channel.

member_limit and creates_join_request are mutually exclusive: a link that opens a join request has no seat count to decrement, so passing both is rejected. Choose one — a capped link for self-serve onboarding, a request link when a human should approve each arrival.

The link this returns is a secondary link. It never replaces the chat's primary link, and a chat may hold many of them, each with its own name, expiry and counters.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget chat. Negative for groups, -100-prefixed for channels; a private chat has no invite links at all. The bot must hold the can_invite_users administrator right.
nameStringOptionalInternal label, up to 32 characters, shown only to administrators in the chat's invite-link list. Members who follow the link never see it. Use it to attribute joins to a campaign or a page.
expire_dateIntegerOptionalUnix timestamp after which the link stops working. Seconds, not milliseconds — a value in milliseconds lands tens of thousands of years out and silently produces a link that never expires. Omit for no expiry.
member_limitIntegerOptionalHow many users may join through this link before it stops working, 1-99999. The counter never resets, and it is not decremented when someone leaves. Cannot be combined with creates_join_request.
creates_join_requestBooleanOptionalWhen true, following the link files a pending request instead of adding the user, and your bot receives a chat_join_request update. Cannot be combined with member_limit.

Returns — ChatInviteLink

Common errors400 chat not found · 403 bot is not a member of the chat · 400 not enough rights · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/createChatInviteLink \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"name":"Landing page CTA","expire_date":1785024000,"member_limit":100}'
python
result = await bot.create_chat_invite_link(
    chat_id=-1001234567890,
    name="Landing page CTA",
    expire_date=1785024000,
    member_limit=100,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/createChatInviteLink", [
    'chat_id' => -1001234567890,
    'name' => 'Landing page CTA',
    'expire_date' => 1785024000,
    'member_limit' => 100
])->json();

Response

json
{
  "ok": true,
  "result": {
    "invite_link": "https://code.casinoplaneta.info/+AbCdEfGh12345",
    "creator": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "creates_join_request": false,
    "is_primary": false,
    "is_revoked": false,
    "name": "Landing page CTA",
    "expire_date": 1785024000,
    "member_limit": 100
  }
}

Changes the settings of an invite link the bot created.

The edit replaces the link's settings wholesale rather than patching them: an optional field you leave out is cleared, not preserved. Send the complete desired state on every call, including the values you are not changing.

A bot can only edit links it created itself, and the same member_limit / creates_join_request exclusivity applies here as on creation.

ParameterTypeRequiredDescription
chat_idInteger or StringYesChat the link belongs to. The bot needs can_invite_users, and the link must be one of its own — links made by other administrators are not editable, even by an owner-level bot.
invite_linkStringYesThe full link URL as returned by createChatInviteLink, not its name. Store it verbatim; there is no lookup by label.
nameStringOptionalNew administrator-facing label, up to 32 characters. Omitting it removes the existing label.
expire_dateIntegerOptionalNew expiry as a Unix timestamp. A timestamp in the past takes effect immediately and the link stops admitting anyone; omitting it makes the link permanent again.
member_limitIntegerOptionalNew seat count, 1-99999. Raising it above the number of users who already joined reopens the link; lowering it below that number closes it. Omitting it removes the cap.
creates_join_requestBooleanOptionalSwitches the link between direct-join and request mode. Requests already pending are unaffected by the switch and still need approving or declining.

Returns — ChatInviteLink

Common errors400 chat not found · 400 not enough rights · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/editChatInviteLink \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"invite_link":"https://code.casinoplaneta.info/+AbCdEfGh12345","name":"Landing page CTA","member_limit":250}'
python
result = await bot.edit_chat_invite_link(
    chat_id=-1001234567890,
    invite_link="https://code.casinoplaneta.info/+AbCdEfGh12345",
    name="Landing page CTA",
    member_limit=250,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/editChatInviteLink", [
    'chat_id' => -1001234567890,
    'invite_link' => 'https://code.casinoplaneta.info/+AbCdEfGh12345',
    'name' => 'Landing page CTA',
    'member_limit' => 250
])->json();

Response

json
{
  "ok": true,
  "result": {
    "invite_link": "https://code.casinoplaneta.info/+AbCdEfGh12345",
    "creator": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "creates_join_request": false,
    "is_primary": false,
    "is_revoked": false,
    "name": "Landing page CTA",
    "member_limit": 250
  }
}

Permanently deactivates an invite link.

Revocation is irreversible. The URL is dead the moment the call returns and cannot be restored or re-issued with the same token — if you need the link live again you must create a new one and redistribute it.

Revoking the chat's primary link automatically generates a replacement primary link. Anything that published the old URL (a website, a printed code) keeps pointing at a dead link, so read the new one from getChat before you re-publish.

ParameterTypeRequiredDescription
chat_idInteger or StringYesChat the link belongs to. The bot needs can_invite_users and, as with editing, may only revoke links it created.
invite_linkStringYesThe full link URL to kill. Users who already joined through it stay in the chat — revoking closes the door, it does not remove anyone.

Returns — ChatInviteLink — the revoked link, with is_revoked set to true

Common errors400 chat not found · 400 not enough rights · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/revokeChatInviteLink \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"invite_link":"https://code.casinoplaneta.info/+AbCdEfGh12345"}'
python
result = await bot.revoke_chat_invite_link(
    chat_id=-1001234567890,
    invite_link="https://code.casinoplaneta.info/+AbCdEfGh12345",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/revokeChatInviteLink", [
    'chat_id' => -1001234567890,
    'invite_link' => 'https://code.casinoplaneta.info/+AbCdEfGh12345'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "invite_link": "https://code.casinoplaneta.info/+AbCdEfGh12345",
    "creator": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "creates_join_request": false,
    "is_primary": false,
    "is_revoked": true,
    "name": "Landing page CTA"
  }
}

approveChatJoinRequest planned

Admits a user who applied to join through a request-mode invite link.

Pending requests do not wait forever — a request that is neither approved nor declined eventually expires, and so does one the user withdraws. Both cases surface as a failure on this call rather than as an update, so treat the error as "already resolved" and drop the record instead of retrying.

Approving is idempotent only in appearance: once the user is in the chat the request no longer exists, so a second call for the same person fails.

ParameterTypeRequiredDescription
chat_idInteger or StringYesChat holding the pending request. The bot needs the can_invite_users administrator right — the same right that governs the links themselves, not a separate moderation right.
user_idIntegerYesApplicant, taken from the chat_join_request update. There is no way to enumerate pending requests through the Bot API, so persist the ids as the updates arrive or you cannot act on them later.

Returns — True on success

Common errors400 chat not found · 400 not enough rights · 403 user is banned · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/approveChatJoinRequest \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"user_id":987654321}'
python
result = await bot.approve_chat_join_request(
    chat_id=-1001234567890,
    user_id=987654321,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/approveChatJoinRequest", [
    'chat_id' => -1001234567890,
    'user_id' => 987654321
])->json();

Response

json
{
  "ok": true,
  "result": true
}

declineChatJoinRequest planned

Rejects a pending request to join a group or channel.

Declining is not a ban. The user is told nothing beyond the request disappearing and may apply again through the same link, so an abusive applicant needs banChatMember as well.

ParameterTypeRequiredDescription
chat_idInteger or StringYesChat holding the pending request. Requires the can_invite_users administrator right.
user_idIntegerYesApplicant to reject, from the chat_join_request update. Fails if the request already expired, was withdrawn, or was approved by a human administrator first — a race worth expecting in busy chats.

Returns — True on success

Common errors400 chat not found · 400 not enough rights · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/declineChatJoinRequest \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"user_id":987654321}'
python
result = await bot.decline_chat_join_request(
    chat_id=-1001234567890,
    user_id=987654321,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/declineChatJoinRequest", [
    'chat_id' => -1001234567890,
    'user_id' => 987654321
])->json();

Response

json
{
  "ok": true,
  "result": true
}

Forum topics

Topics inside groups that have forum mode enabled.

createForumTopic planned

Creates a topic in a supergroup that has forum mode enabled.

Forum mode is a property of the group, not an administrator right, and a bot cannot switch it on. Against an ordinary supergroup every method on this page fails no matter what rights the bot holds — check is_forum on getChat first.

The message_thread_id in the result is how you post into the topic: pass it to sendMessage and the other send methods. Without it a message lands in General, the implicit topic every forum starts with.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget supergroup, negative or -100-prefixed. The bot must hold the can_manage_topics administrator right, which is distinct from can_manage_chat and is off by default when a bot is promoted.
nameStringYesTopic title, 1-128 characters. Titles are not unique — creating a topic that duplicates an existing name succeeds and leaves members with two identical-looking threads.
icon_colorIntegerOptionalColour of the topic icon as an RGB integer. Only six values are accepted: 7322096, 16766590, 13338331, 9367192, 16749490, 16478047. Any other number is rejected rather than rounded to the nearest allowed colour.
icon_custom_emoji_idStringOptionalCustom emoji to use as the icon instead of a coloured dot. Must come from the set returned by getForumTopicIconStickers; an arbitrary emoji id is refused. When set, icon_color is ignored.

Returns — ForumTopic

Common errors400 chat not found · 403 bot is not a member of the chat · 400 not enough rights · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/createForumTopic \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"name":"Bug reports","icon_color":16478047}'
python
result = await bot.create_forum_topic(
    chat_id=-1001234567890,
    name="Bug reports",
    icon_color=16478047,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/createForumTopic", [
    'chat_id' => -1001234567890,
    'name' => 'Bug reports',
    'icon_color' => 16478047
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_thread_id": 512,
    "name": "Bug reports",
    "icon_color": 16478047
  }
}

editForumTopic planned

Renames a topic or changes its icon.

Unlike topic creation, a field you omit here is left alone — passing only name keeps the current icon. Passing an empty icon_custom_emoji_id is the way to drop a custom icon and fall back to the colour.

A bot with only can_manage_topics may edit any topic. Without that right it may still edit a topic it created itself, and nothing else.

ParameterTypeRequiredDescription
chat_idInteger or StringYesSupergroup containing the topic.
message_thread_idIntegerYesIdentifier of the topic, from createForumTopic or from the message_thread_id on any message posted inside it. The General topic cannot be edited through this method — it has its own editGeneralForumTopic.
nameStringOptionalNew title, 1-128 characters. Omit to keep the current one.
icon_custom_emoji_idStringOptionalNew custom emoji icon, or an empty string to remove the existing one. Omit to keep the current icon. The colour chosen at creation cannot be changed afterwards.

Returns — True on success

Common errors400 chat not found · 400 not enough rights · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/editForumTopic \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_thread_id":512,"name":"Bug reports (v2 only)"}'
python
result = await bot.edit_forum_topic(
    chat_id=-1001234567890,
    message_thread_id=512,
    name="Bug reports (v2 only)",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/editForumTopic", [
    'chat_id' => -1001234567890,
    'message_thread_id' => 512,
    'name' => 'Bug reports (v2 only)'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

closeForumTopic planned

Closes a topic so that only administrators can post in it.

Closing hides nothing and deletes nothing: the thread stays visible and readable, ordinary members simply lose the ability to post. Administrators with can_manage_topics can still write, which is what makes this useful for announcement threads.

ParameterTypeRequiredDescription
chat_idInteger or StringYesSupergroup containing the topic. Requires can_manage_topics, unless the bot is closing a topic it created itself.
message_thread_idIntegerYesTopic to close. Closing an already-closed topic is a no-op that still returns success, so you do not need to track state to stay idempotent.

Returns — True on success

Common errors400 chat not found · 400 not enough rights · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/closeForumTopic \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_thread_id":512}'
python
result = await bot.close_forum_topic(
    chat_id=-1001234567890,
    message_thread_id=512,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/closeForumTopic", [
    'chat_id' => -1001234567890,
    'message_thread_id' => 512
])->json();

Response

json
{
  "ok": true,
  "result": true
}

reopenForumTopic planned

Reopens a previously closed topic.

ParameterTypeRequiredDescription
chat_idInteger or StringYesSupergroup containing the topic. Requires can_manage_topics, unless the bot created the topic itself.
message_thread_idIntegerYesTopic to reopen. Members regain posting rights immediately; messages sent while it was closed are unaffected and stay where they are.

Returns — True on success

Common errors400 chat not found · 400 not enough rights · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/reopenForumTopic \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_thread_id":512}'
python
result = await bot.reopen_forum_topic(
    chat_id=-1001234567890,
    message_thread_id=512,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/reopenForumTopic", [
    'chat_id' => -1001234567890,
    'message_thread_id' => 512
])->json();

Response

json
{
  "ok": true,
  "result": true
}

deleteForumTopic planned

Deletes a topic together with every message inside it.

This is not the same as closing. Deletion removes the whole thread and all of its history for everyone, cannot be undone, and does not respect the 48-hour window that limits deleting individual messages.

The General topic is special and cannot be deleted — it is the fallback destination for any message sent without a message_thread_id, so a forum always has one. Use hideGeneralForumTopic if you want it out of the way.

ParameterTypeRequiredDescription
chat_idInteger or StringYesSupergroup containing the topic. Requires the can_delete_messages administrator right — creating the topic yourself is not enough for this method, unlike closing or renaming it.
message_thread_idIntegerYesTopic to destroy. Once the call returns, the id is dead: sending to it afterwards fails rather than recreating the thread.

Returns — True on success

Common errors400 chat not found · 403 bot is not a member of the chat · 400 not enough rights

bash
curl https://api.casinoplaneta.info/bot$TOKEN/deleteForumTopic \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_thread_id":512}'
python
result = await bot.delete_forum_topic(
    chat_id=-1001234567890,
    message_thread_id=512,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/deleteForumTopic", [
    'chat_id' => -1001234567890,
    'message_thread_id' => 512
])->json();

Response

json
{
  "ok": true,
  "result": true
}

unpinAllForumTopicMessages planned

Clears every pinned message inside a single topic.

Pins are scoped per topic, so this touches only the thread you name and leaves pins in other topics — and the group-wide pin — untouched. To clear those, target the General topic or use the chat-level unpin methods.

The messages themselves are not deleted; they merely stop being pinned.

ParameterTypeRequiredDescription
chat_idInteger or StringYesSupergroup containing the topic. Requires the can_pin_messages administrator right.
message_thread_idIntegerYesTopic whose pins should be cleared. Succeeds even when nothing is pinned, so it is safe to call unconditionally during cleanup.

Returns — True on success

Common errors400 chat not found · 400 not enough rights · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/unpinAllForumTopicMessages \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_thread_id":512}'
python
result = await bot.unpin_all_forum_topic_messages(
    chat_id=-1001234567890,
    message_thread_id=512,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/unpinAllForumTopicMessages", [
    'chat_id' => -1001234567890,
    'message_thread_id' => 512
])->json();

Response

json
{
  "ok": true,
  "result": true
}

Callbacks and inline

Answer button presses, inline queries and Mini App queries.

answerCallbackQuery planned

Acknowledges an inline keyboard button press and optionally shows the user a toast or an alert.

Every callback_query must be answered, even when there is nothing to say. Until you answer, the pressed button keeps a loading spinner on the user's screen and it only clears when the query times out. A call with no text is the normal way to acknowledge a press whose effect is visible elsewhere — after editMessageText, for instance.

The window is roughly 20 seconds. After that the id is dead and the call fails, so acknowledge first and do the slow work afterwards rather than answering at the end of your handler.

ParameterTypeRequiredDescription
callback_query_idStringYesThe id field of the incoming callback_query. Single-use and short-lived — handing it to a background worker usually means it has expired by the time the worker runs.
textStringOptionalUp to 200 characters, shown as a toast that fades on its own. Omit it to dismiss the spinner without showing anything.
show_alertBooleanOptionalTurns the toast into a modal the user has to dismiss. Worth it for failures the user must read, wrong for routine confirmations.
urlStringOptionalOpens a link in the user's client instead of showing text. Only honoured for game buttons and for deep links back into this same bot; other URLs are ignored rather than rejected, so a silent no-op here is expected behaviour.
cache_timeIntegerOptionalSeconds the client may reuse this answer for the same button without calling the bot again. Keep it at 0 whenever the answer depends on state that can change between presses.

Returns — True on success

Common errors401 Unauthorized · 429 Too Many Requests: retry after N · 400 message is too long

bash
curl https://api.casinoplaneta.info/bot$TOKEN/answerCallbackQuery \
  -H 'Content-Type: application/json' \
  -d '{"callback_query_id":"4382bfdwdsb323b2d9","text":"Added to your watchlist","show_alert":false,"cache_time":0}'
python
result = await bot.answer_callback_query(
    callback_query_id="4382bfdwdsb323b2d9",
    text="Added to your watchlist",
    show_alert=False,
    cache_time=0,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/answerCallbackQuery", [
    'callback_query_id' => '4382bfdwdsb323b2d9',
    'text' => 'Added to your watchlist',
    'show_alert' => false,
    'cache_time' => 0
])->json();

Response

json
{
  "ok": true,
  "result": true
}

answerInlineQuery planned

Returns the result list for an inline query typed in any chat's composer.

cache_time and is_personal have to be decided together. A long cache is only correct when the answer depends on nothing but the query text. Anything derived from who is asking needs is_personal: true, otherwise a result set computed for one user is served to everyone who types the same words — the usual way private data escapes an inline bot. The cache lives on the server, so lowering cache_time later does not invalidate what is already stored.

An unanswered query shows the user nothing at all: no error, no spinner, no retry. A broken inline handler is therefore easy to miss in production — log the queries you fail to answer.

ParameterTypeRequiredDescription
inline_query_idStringYesThe id of the incoming inline_query. Valid for a few seconds only, since the user is waiting on it.
resultsArray of InlineQueryResultYesJSON-serialized array of at most 50 results. Each id must be unique inside the response and is what comes back in chosen_inline_result, so store your own key there rather than an array index.
cache_timeIntegerOptionalSeconds the server may serve this set again for the same query without asking the bot. Defaults to 300, which is far too long for anything live.
is_personalBooleanOptionalScopes the cache to the requesting user instead of sharing it across everyone.
next_offsetStringOptionalCursor for the next page; it arrives as offset on the follow-up query when the user scrolls to the end. Send an empty string or omit it to signal the end — repeating the offset you were given makes the client page forever.
buttonInlineQueryResultsButtonOptionalA button rendered above the results, normally used to send the user into a Mini App or into a private chat with the bot to authenticate before results can be produced.

Returns — True on success

Common errors401 Unauthorized · 429 Too Many Requests: retry after N · 400 can't parse entities

bash
curl https://api.casinoplaneta.info/bot$TOKEN/answerInlineQuery \
  -H 'Content-Type: application/json' \
  -d '{"inline_query_id":"1030329944103343201","results":[{"type":"article","id":"city-tashkent","title":"Tashkent","description":"Clear, 31 °C","input_message_content":{"message_text":"*Tashkent* — clear, 31 °C, wind 3 m/s","parse_mode":"MarkdownV2"}},{"type":"article","id":"city-samarkand","title":"Samarkand","description":"Partly cloudy, 28 °C","input_message_content":{"message_text":"*Samarkand* — partly cloudy, 28 °C, wind 5 m/s","parse_mode":"MarkdownV2"}}],"cache_time":60,"is_personal":true,"next_offset":"20"}'
python
result = await bot.answer_inline_query(
    inline_query_id="1030329944103343201",
    results=[
        {
            "type": "article",
            "id": "city-tashkent",
            "title": "Tashkent",
            "description": "Clear, 31 °C",
            "input_message_content": {
                "message_text": "*Tashkent* — clear, 31 °C, wind 3 m/s",
                "parse_mode": "MarkdownV2"
            }
        },
        {
            "type": "article",
            "id": "city-samarkand",
            "title": "Samarkand",
            "description": "Partly cloudy, 28 °C",
            "input_message_content": {
                "message_text": "*Samarkand* — partly cloudy, 28 °C, wind 5 m/s",
                "parse_mode": "MarkdownV2"
            }
        }
    ],
    cache_time=60,
    is_personal=True,
    next_offset="20",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/answerInlineQuery", [
    'inline_query_id' => '1030329944103343201',
    'results' => [
        [
            'type' => 'article',
            'id' => 'city-tashkent',
            'title' => 'Tashkent',
            'description' => 'Clear, 31 °C',
            'input_message_content' => [
                'message_text' => '*Tashkent* — clear, 31 °C, wind 3 m/s',
                'parse_mode' => 'MarkdownV2'
            ]
        ],
        [
            'type' => 'article',
            'id' => 'city-samarkand',
            'title' => 'Samarkand',
            'description' => 'Partly cloudy, 28 °C',
            'input_message_content' => [
                'message_text' => '*Samarkand* — partly cloudy, 28 °C, wind 5 m/s',
                'parse_mode' => 'MarkdownV2'
            ]
        ]
    ],
    'cache_time' => 60,
    'is_personal' => true,
    'next_offset' => '20'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

answerWebAppQuery planned

Sends the result a Mini App prepared into the chat the Mini App was opened from.

The message arrives from the user, not from the bot, and the bot is never told which chat received it. All you get back is an inline_message_id for editing it later.

The query id exists only when the Mini App was launched from an inline keyboard button or an inline results button — a Mini App opened from the menu button has no query_id in its initData, and there is nothing to answer.

ParameterTypeRequiredDescription
web_app_query_idStringYesThe query_id field of the Mini App's initData. Single-use and short-lived; validate the initData signature before trusting it.
resultInlineQueryResultYesOne result object, the same shape as an entry in answerInlineQuery.

Returns — SentWebAppMessage

Common errors401 Unauthorized · 429 Too Many Requests: retry after N · 400 can't parse entities

bash
curl https://api.casinoplaneta.info/bot$TOKEN/answerWebAppQuery \
  -H 'Content-Type: application/json' \
  -d '{"web_app_query_id":"AAHdF6IQAAAAAN0XohDhrOrc","result":{"type":"article","id":"order-8842","title":"Order #8842 confirmed","input_message_content":{"message_text":"Order #8842 confirmed — 2 items, delivery Thursday."}}}'
python
result = await bot.answer_web_app_query(
    web_app_query_id="AAHdF6IQAAAAAN0XohDhrOrc",
    result={
        "type": "article",
        "id": "order-8842",
        "title": "Order #8842 confirmed",
        "input_message_content": {
            "message_text": "Order #8842 confirmed — 2 items, delivery Thursday."
        }
    },
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/answerWebAppQuery", [
    'web_app_query_id' => 'AAHdF6IQAAAAAN0XohDhrOrc',
    'result' => [
        'type' => 'article',
        'id' => 'order-8842',
        'title' => 'Order #8842 confirmed',
        'input_message_content' => [
            'message_text' => 'Order #8842 confirmed — 2 items, delivery Thursday.'
        ]
    ]
])->json();

Response

json
{
  "ok": true,
  "result": {
    "inline_message_id": "BAAAAM0XohDhrOrcAAEC"
  }
}

savePreparedInlineMessage planned

Stores a message for one user so their Mini App can offer it in the chat picker later.

The Mini App passes the returned id to shareMessage, which opens the picker. If all four allow_* flags are left false the picker has nowhere to send it, and the failure shows up inside the Mini App rather than as an error on this call.

Prepared messages expire — read expiration_date from the result. Prepare at the moment the user taps share, not ahead of time during page load.

ParameterTypeRequiredDescription
user_idIntegerYesThe only account permitted to send it. Preparing a message for one user and passing the id to another does not work.
resultInlineQueryResultYesThe message to store, in the same result-object shape answerInlineQuery accepts.
allow_user_chatsBooleanOptionalLets the picker offer private chats with other people.
allow_bot_chatsBooleanOptionalLets the picker offer private chats with other bots.
allow_group_chatsBooleanOptionalLets the picker offer groups and supergroups.
allow_channel_chatsBooleanOptionalLets the picker offer channels where the user may post.

Returns — PreparedInlineMessage

Common errors401 Unauthorized · 429 Too Many Requests: retry after N · 400 chat not found

bash
curl https://api.casinoplaneta.info/bot$TOKEN/savePreparedInlineMessage \
  -H 'Content-Type: application/json' \
  -d '{"user_id":41028837,"result":{"type":"article","id":"invite-9f21c0","title":"Invite to my team","input_message_content":{"message_text":"Join my team: https://code.casinoplaneta.info/team_bot?start=9f21c0"}},"allow_user_chats":true,"allow_group_chats":true}'
python
result = await bot.save_prepared_inline_message(
    user_id=41028837,
    result={
        "type": "article",
        "id": "invite-9f21c0",
        "title": "Invite to my team",
        "input_message_content": {
            "message_text": "Join my team: https://code.casinoplaneta.info/team_bot?start=9f21c0"
        }
    },
    allow_user_chats=True,
    allow_group_chats=True,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/savePreparedInlineMessage", [
    'user_id' => 41028837,
    'result' => [
        'type' => 'article',
        'id' => 'invite-9f21c0',
        'title' => 'Invite to my team',
        'input_message_content' => [
            'message_text' => 'Join my team: https://code.casinoplaneta.info/team_bot?start=9f21c0'
        ]
    ],
    'allow_user_chats' => true,
    'allow_group_chats' => true
])->json();

Response

json
{
  "ok": true,
  "result": {
    "id": "pim_9f21c0b4e7",
    "expiration_date": 1784500800
  }
}

Bot settings

Commands, menu button, names and descriptions -- all scoped per language.

setMyCommands planned

Replaces the bot's command list for one scope and language.

Resolution runs from narrow to broad: chat member, chat, all chat administrators, all group chats, all private chats, then default. The first scope holding a list wins and it replaces the broader one rather than merging — a two-command list on a narrow scope hides the twenty you set on the default scope, which is the usual reason commands disappear in groups.

Language resolves the same way inside each scope: the client's language first, then the entry saved with no language_code. That empty-language entry is the fallback, so a bot that only ever sets language_code: "ru" shows no commands at all to everyone else.

ParameterTypeRequiredDescription
commandsArray of BotCommandYesJSON-serialized list of up to 100 {command, description} pairs. The command is 1–32 characters of lowercase Latin letters, digits and underscores, written without the leading slash; uppercase is rejected rather than folded. Descriptions run 1–256 characters.
scopeBotCommandScopeOptionalWhich users see this list. Defaults to BotCommandScopeDefault, the list used wherever no narrower scope applies.
language_codeStringOptionalTwo-letter ISO 639-1 code. Omit it to write the fallback list used for every language you have not translated.

Returns — True on success

Common errors401 Unauthorized · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/setMyCommands \
  -H 'Content-Type: application/json' \
  -d '{"commands":[{"command":"start","description":"Start the bot"},{"command":"forecast","description":"Five-day forecast for a city"},{"command":"settings","description":"Change units and default city"}],"scope":{"type":"all_private_chats"},"language_code":"en"}'
python
result = await bot.set_my_commands(
    commands=[
        {
            "command": "start",
            "description": "Start the bot"
        },
        {
            "command": "forecast",
            "description": "Five-day forecast for a city"
        },
        {
            "command": "settings",
            "description": "Change units and default city"
        }
    ],
    scope={
        "type": "all_private_chats"
    },
    language_code="en",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/setMyCommands", [
    'commands' => [
        [
            'command' => 'start',
            'description' => 'Start the bot'
        ],
        [
            'command' => 'forecast',
            'description' => 'Five-day forecast for a city'
        ],
        [
            'command' => 'settings',
            'description' => 'Change units and default city'
        ]
    ],
    'scope' => [
        'type' => 'all_private_chats'
    ],
    'language_code' => 'en'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

getMyCommands planned

Reads back the command list stored for one scope and language.

This reads one exact scope-and-language slot and does not walk the fallback chain, so a scope you never wrote to returns [] instead of the list a user would actually see there. An empty array is a normal answer, not an error — do not treat it as a failed call.

ParameterTypeRequiredDescription
scopeBotCommandScopeOptionalThe slot to read. Defaults to BotCommandScopeDefault.
language_codeStringOptionalTwo-letter ISO 639-1 code. Omit it to read the fallback entry rather than a translation.

Returns — Array of BotCommand

Common errors401 Unauthorized · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getMyCommands \
  -H 'Content-Type: application/json' \
  -d '{"scope":{"type":"all_private_chats"},"language_code":"en"}'
python
result = await bot.get_my_commands(
    scope={
        "type": "all_private_chats"
    },
    language_code="en",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/getMyCommands", [
    'scope' => [
        'type' => 'all_private_chats'
    ],
    'language_code' => 'en'
])->json();

Response

json
{
  "ok": true,
  "result": [
    {
      "command": "start",
      "description": "Start the bot"
    },
    {
      "command": "forecast",
      "description": "Five-day forecast for a city"
    },
    {
      "command": "settings",
      "description": "Change units and default city"
    }
  ]
}

deleteMyCommands planned

Clears the command list stored for one scope and language.

Only that exact pair is removed. The next broader scope, or the empty-language entry, immediately takes over again — this is how you undo an override, not how you leave a user with no commands. To do that, write an empty array with setMyCommands instead.

ParameterTypeRequiredDescription
scopeBotCommandScopeOptionalThe slot to clear. Defaults to BotCommandScopeDefault.
language_codeStringOptionalTwo-letter ISO 639-1 code. Omit it to clear the fallback entry.

Returns — True on success

Common errors401 Unauthorized · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/deleteMyCommands \
  -H 'Content-Type: application/json' \
  -d '{"scope":{"type":"all_private_chats"},"language_code":"en"}'
python
result = await bot.delete_my_commands(
    scope={
        "type": "all_private_chats"
    },
    language_code="en",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/deleteMyCommands", [
    'scope' => [
        'type' => 'all_private_chats'
    ],
    'language_code' => 'en'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

setChatMenuButton planned

Sets the button shown next to the composer in a private chat, or the default for all of them.

ParameterTypeRequiredDescription
chat_idIntegerOptionalA private chat to change on its own. Omit it to change the default inherited by every private chat that has no button of its own; groups and channels have no menu button at all.
menu_buttonMenuButtonOptionalOne of MenuButtonCommands, MenuButtonWebApp or MenuButtonDefault. The Web App variant needs both text and a web_app.url on HTTPS. Omit the parameter entirely to fall back to MenuButtonDefault.

Returns — True on success

Common errors401 Unauthorized · 429 Too Many Requests: retry after N · 400 chat not found

bash
curl https://api.casinoplaneta.info/bot$TOKEN/setChatMenuButton \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":41028837,"menu_button":{"type":"web_app","text":"Open forecast","web_app":{"url":"https://weather.example.com/app"}}}'
python
result = await bot.set_chat_menu_button(
    chat_id=41028837,
    menu_button={
        "type": "web_app",
        "text": "Open forecast",
        "web_app": {
            "url": "https://weather.example.com/app"
        }
    },
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/setChatMenuButton", [
    'chat_id' => 41028837,
    'menu_button' => [
        'type' => 'web_app',
        'text' => 'Open forecast',
        'web_app' => [
            'url' => 'https://weather.example.com/app'
        ]
    ]
])->json();

Response

json
{
  "ok": true,
  "result": true
}

getChatMenuButton planned

Reads the menu button set for one private chat, or the bot-wide default.

ParameterTypeRequiredDescription
chat_idIntegerOptionalThe private chat to inspect. Omit it to read the bot-wide default; a chat that was never customised reports the default it inherits.

Returns — MenuButton

Common errors401 Unauthorized · 429 Too Many Requests: retry after N · 400 chat not found

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getChatMenuButton \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":41028837}'
python
result = await bot.get_chat_menu_button(
    chat_id=41028837,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/getChatMenuButton", [
    'chat_id' => 41028837
])->json();

Response

json
{
  "ok": true,
  "result": {
    "type": "web_app",
    "text": "Open forecast",
    "web_app": {
      "url": "https://weather.example.com/app"
    }
  }
}

setMyDefaultAdministratorRights planned

Sets which administrator rights are pre-selected when someone promotes the bot.

These only tick boxes in the dialog shown while adding the bot as an administrator. The user can untick any of them, so the bot still has to cope with rights it did not get — read getChatMember on its own id instead of assuming the defaults were accepted.

ParameterTypeRequiredDescription
rightsChatAdministratorRightsOptionalJSON-serialized rights object. Omit it to clear the defaults, which leaves every box unchecked.
for_channelsBooleanOptionalTargets the channel defaults instead of the group ones. The two sets are stored separately, so clearing one leaves the other untouched.

Returns — True on success

Common errors401 Unauthorized · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/setMyDefaultAdministratorRights \
  -H 'Content-Type: application/json' \
  -d '{"rights":{"is_anonymous":false,"can_manage_chat":true,"can_delete_messages":true,"can_manage_video_chats":false,"can_restrict_members":true,"can_promote_members":false,"can_change_info":false,"can_invite_users":true,"can_pin_messages":true},"for_channels":false}'
python
result = await bot.set_my_default_administrator_rights(
    rights={
        "is_anonymous": False,
        "can_manage_chat": True,
        "can_delete_messages": True,
        "can_manage_video_chats": False,
        "can_restrict_members": True,
        "can_promote_members": False,
        "can_change_info": False,
        "can_invite_users": True,
        "can_pin_messages": True
    },
    for_channels=False,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/setMyDefaultAdministratorRights", [
    'rights' => [
        'is_anonymous' => false,
        'can_manage_chat' => true,
        'can_delete_messages' => true,
        'can_manage_video_chats' => false,
        'can_restrict_members' => true,
        'can_promote_members' => false,
        'can_change_info' => false,
        'can_invite_users' => true,
        'can_pin_messages' => true
    ],
    'for_channels' => false
])->json();

Response

json
{
  "ok": true,
  "result": true
}

getMyDefaultAdministratorRights planned

Reads the administrator rights pre-selected when the bot is promoted.

ParameterTypeRequiredDescription
for_channelsBooleanOptionalReads the channel defaults instead of the group ones.

Returns — ChatAdministratorRights

Common errors401 Unauthorized · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getMyDefaultAdministratorRights \
  -H 'Content-Type: application/json' \
  -d '{"for_channels":false}'
python
result = await bot.get_my_default_administrator_rights(
    for_channels=False,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/getMyDefaultAdministratorRights", [
    'for_channels' => false
])->json();

Response

json
{
  "ok": true,
  "result": {
    "is_anonymous": false,
    "can_manage_chat": true,
    "can_delete_messages": true,
    "can_manage_video_chats": false,
    "can_restrict_members": true,
    "can_promote_members": false,
    "can_change_info": false,
    "can_invite_users": true,
    "can_pin_messages": true
  }
}

setMyName planned

Sets the bot's display name for one language.

Renaming is rate-limited far more tightly than ordinary methods, so a deploy script that rewrites the name every run will start collecting flood_wait. Set it when it changes, not on every boot.

ParameterTypeRequiredDescription
nameStringOptional0–64 characters. An empty string removes the translation for that language and lets the fallback name apply again; there is no way to leave a bot with no name at all.
language_codeStringOptionalTwo-letter ISO 639-1 code. Omit it to set the fallback name shown to every language you have not translated.

Returns — True on success

Common errors401 Unauthorized · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/setMyName \
  -H 'Content-Type: application/json' \
  -d '{"name":"Weather","language_code":"en"}'
python
result = await bot.set_my_name(
    name="Weather",
    language_code="en",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/setMyName", [
    'name' => 'Weather',
    'language_code' => 'en'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

getMyName planned

Reads the bot's display name for one language.

ParameterTypeRequiredDescription
language_codeStringOptionalTwo-letter ISO 639-1 code. Omit it to read the fallback name rather than a translation.

Returns — BotName

Common errors401 Unauthorized · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getMyName \
  -H 'Content-Type: application/json' \
  -d '{"language_code":"en"}'
python
result = await bot.get_my_name(
    language_code="en",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/getMyName", [
    'language_code' => 'en'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "name": "Weather"
  }
}

setMyDescription planned

Sets the text shown in an empty chat before the user has written anything.

This is the block under What can this bot do? on a chat that has not started yet. It vanishes the moment the first message is sent, which makes it onboarding copy rather than a profile blurb — say what the bot does and which command starts it.

ParameterTypeRequiredDescription
descriptionStringOptional0–512 characters. An empty string removes the translation for that language and falls back to the untranslated entry.
language_codeStringOptionalTwo-letter ISO 639-1 code. Omit it to write the fallback description.

Returns — True on success

Common errors401 Unauthorized · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/setMyDescription \
  -H 'Content-Type: application/json' \
  -d '{"description":"Send me a city name and I reply with current conditions and a five-day forecast. Use /settings to switch between °C and °F.","language_code":"en"}'
python
result = await bot.set_my_description(
    description="Send me a city name and I reply with current conditions and a five-day forecast. Use /settings to switch between °C and °F.",
    language_code="en",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/setMyDescription", [
    'description' => 'Send me a city name and I reply with current conditions and a five-day forecast. Use /settings to switch between °C and °F.',
    'language_code' => 'en'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

getMyDescription planned

Reads the empty-chat description for one language.

ParameterTypeRequiredDescription
language_codeStringOptionalTwo-letter ISO 639-1 code. Omit it to read the fallback entry.

Returns — BotDescription

Common errors401 Unauthorized · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getMyDescription \
  -H 'Content-Type: application/json' \
  -d '{"language_code":"en"}'
python
result = await bot.get_my_description(
    language_code="en",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/getMyDescription", [
    'language_code' => 'en'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "description": "Send me a city name and I reply with current conditions and a five-day forecast. Use /settings to switch between °C and °F."
  }
}

setMyShortDescription planned

Sets the one-line text shown on the bot's profile page and in shared links.

Easy to confuse with setMyDescription. The short one is on the profile and in link previews, so most people only ever read this; the long one appears only in a chat nobody has written in yet.

ParameterTypeRequiredDescription
short_descriptionStringOptional0–120 characters. An empty string removes the translation for that language and falls back to the untranslated entry.
language_codeStringOptionalTwo-letter ISO 639-1 code. Omit it to write the fallback short description.

Returns — True on success

Common errors401 Unauthorized · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/setMyShortDescription \
  -H 'Content-Type: application/json' \
  -d '{"short_description":"Current conditions and a five-day forecast for any city.","language_code":"en"}'
python
result = await bot.set_my_short_description(
    short_description="Current conditions and a five-day forecast for any city.",
    language_code="en",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/setMyShortDescription", [
    'short_description' => 'Current conditions and a five-day forecast for any city.',
    'language_code' => 'en'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

getMyShortDescription planned

Reads the profile-page short description for one language.

ParameterTypeRequiredDescription
language_codeStringOptionalTwo-letter ISO 639-1 code. Omit it to read the fallback entry.

Returns — BotShortDescription

Common errors401 Unauthorized · 429 Too Many Requests: retry after N

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getMyShortDescription \
  -H 'Content-Type: application/json' \
  -d '{"language_code":"en"}'
python
result = await bot.get_my_short_description(
    language_code="en",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/getMyShortDescription", [
    'language_code' => 'en'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "short_description": "Current conditions and a five-day forecast for any city."
  }
}

Reactions

Set the bot's reaction on a message.

setMessageReaction planned

Sets or clears the bot's reaction on a message.

A bot holds at most one reaction per message. The call replaces whatever it set before rather than adding to it, and passing an empty reaction array removes it — there is no separate delete method.

A chat can restrict reactions to a whitelist, or disable them entirely. An emoji outside that set fails with reaction_not_allowed, so read the permitted list from getChat before reacting rather than discovering it one emoji at a time.

Reacting to your own messages is allowed. Reacting to a message you cannot see is not — the bot must be a member of the chat and the message must be within the history it can read.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget chat: positive for a private chat, negative for a group, -100-prefixed for a channel. A channel may also be given as @channelusername.
message_idIntegerYesMessage to react to. Ids are unique only within their chat, so this is meaningless without the matching chat_id. If the message was deleted the call fails with message_not_found.
reactionArray of ReactionTypeOptionalThe reaction to set, as a one-element array such as [{"type":"emoji","emoji":"👍"}]. Omit it or pass [] to remove the bot's existing reaction. Only emoji the chat permits are accepted; custom emoji are restricted further and are not available to every bot.
is_bigBooleanOptionalPlays the large animation on the recipient's screen. Purely presentational — it does not change what is stored or what other clients count.

Returns — True on success

Common errors400 reaction is not allowed · 400 chat not found · 403 bot is not a member of the chat · 400 message to edit not found · 400 not enough rights · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error: temporarily unavailable · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/setMessageReaction \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":-1001234567890,"message_id":4471,"reaction":[{"type":"emoji","emoji":"👍"}],"is_big":false}'
python
result = await bot.set_message_reaction(
    chat_id=-1001234567890,
    message_id=4471,
    reaction=[
        {
            "type": "emoji",
            "emoji": "👍"
        }
    ],
    is_big=False,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/setMessageReaction", [
    'chat_id' => -1001234567890,
    'message_id' => 4471,
    'reaction' => [
        [
            'type' => 'emoji',
            'emoji' => '👍'
        ]
    ],
    'is_big' => false
])->json();

Response

json
{
  "ok": true,
  "result": true
}

Payments

Invoices and Stars. See the note on currency before using these.

sendInvoice planned

Sends an invoice message that the recipient can pay from the chat.

The currency model for this platform is not finalised. The TZ describes a Telegram-style Stars (XTR) balance, while GROM's own internal token is called Take, and which of the two applies to bot payments — or how fiat would enter the system at all — is an open decision, not something this page is withholding. The parameter shapes below follow the Telegram Bot API so that existing code ports unchanged, but currency, prices and provider_token are the fields most likely to change once that decision is made. No payment provider is connected yet, so this method cannot currently charge anyone.

Do not put anything meaningful in payload beyond a key into your own storage — it is echoed back to you in pre_checkout_query and in the successful-payment message, and it is not a place for prices or entitlements you would not want a client to see.

With is_flexible set you must answer the resulting shipping_query within 10 seconds, and you must answer every pre_checkout_query within 10 seconds regardless. An unanswered query fails the payment on the user's side.

ParameterTypeRequiredDescription
chat_idInteger or StringYesTarget chat: positive for a private chat, negative for a group, -100-prefixed for a channel.
message_thread_idIntegerOptionalTopic id, for a forum-enabled supergroup. Ignored elsewhere.
titleStringYesProduct name, 1–32 characters. Shown as the invoice heading, and truncated hard rather than wrapped.
descriptionStringYesProduct description, 1–255 characters.
payloadStringYes1–128 bytes of bot-defined data, returned to you with the pre-checkout query and the successful payment. Use it as an opaque order key that lets you look the order up server-side; do not encode the price in it.
provider_tokenStringOptionalToken identifying the payment provider. Which providers exist here is undecided — see the note above — so treat this field as reserved rather than as something you can obtain today.
currencyStringYesThree-letter ISO 4217 code. Which currencies this platform will actually accept, and whether an internal-token code sits alongside them, is undecided.
pricesArray of LabeledPriceYesPrice breakdown as [{"label":"...","amount":N}]. Amounts are integers in the currency's smallest unit — cents, not dollars — and the total charged is their sum, so a single line item is fine when you do not need an itemised view. Negative amounts are how discounts are expressed.
max_tip_amountIntegerOptionalLargest accepted tip, in the smallest units of currency, default 0 meaning tipping is off.
suggested_tip_amountsArray of IntegerOptionalUp to 4 suggested tip amounts in the smallest units, strictly increasing and all at most max_tip_amount.
start_parameterStringOptionalDeep-link parameter for the invoice's Pay button. Omit it to produce a forwarded copy that cannot be paid by a second person — which is usually what you want for a per-user order.
provider_dataStringOptionalJSON blob passed through to the provider verbatim. Its contents depend entirely on which provider is used, and no provider is wired up yet.
photo_urlStringOptionalImage of the product. Fetched by the gateway from a public URL, not uploaded.
photo_sizeIntegerOptionalSize of the product photo in bytes.
photo_widthIntegerOptionalProduct photo width in pixels.
photo_heightIntegerOptionalProduct photo height in pixels.
need_nameBooleanOptionalRequires the buyer's full name to complete the order. Only ask for what you will actually use — every field here is data you then have to handle.
need_phone_numberBooleanOptionalRequires the buyer's phone number.
need_emailBooleanOptionalRequires the buyer's email address.
need_shipping_addressBooleanOptionalRequires a shipping address. Combine with is_flexible when the address changes the price.
send_phone_number_to_providerBooleanOptionalForwards the collected phone number to the payment provider.
send_email_to_providerBooleanOptionalForwards the collected email address to the payment provider.
is_flexibleBooleanOptionalMarks the final price as dependent on the shipping address. Setting this makes the platform send you a shipping_query that you must answer with answerShippingQuery; if you do not handle that update, the payment stalls.
disable_notificationBooleanOptionalDelivers the invoice without a notification sound.
protect_contentBooleanOptionalBlocks forwarding and saving of the invoice message.
reply_parametersReplyParametersOptionalMakes the invoice a reply to a message in the same chat.
reply_markupInlineKeyboardMarkupOptionalInline keyboard. If you supply one, the first button must be the Pay button; otherwise one is added for you.

Returns — Message

Common errors400 chat not found · 403 bot is not a member of the chat · 403 bot was blocked by the user · 400 not enough rights · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error: temporarily unavailable · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendInvoice \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":918273645,"title":"Pro plan — 1 month","description":"Unlimited alerts and priority delivery for 30 days.","payload":"order_8241f0c3","currency":"USD","prices":[{"label":"Pro plan","amount":999}],"need_email":true,"protect_content":true}'
python
result = await bot.send_invoice(
    chat_id=918273645,
    title="Pro plan — 1 month",
    description="Unlimited alerts and priority delivery for 30 days.",
    payload="order_8241f0c3",
    currency="USD",
    prices=[
        {
            "label": "Pro plan",
            "amount": 999
        }
    ],
    need_email=True,
    protect_content=True,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendInvoice", [
    'chat_id' => 918273645,
    'title' => 'Pro plan — 1 month',
    'description' => 'Unlimited alerts and priority delivery for 30 days.',
    'payload' => 'order_8241f0c3',
    'currency' => 'USD',
    'prices' => [
        [
            'label' => 'Pro plan',
            'amount' => 999
        ]
    ],
    'need_email' => true,
    'protect_content' => true
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 4472,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Weather",
      "username": "weather_bot"
    },
    "chat": {
      "id": 918273645,
      "first_name": "Aziz",
      "username": "aziz",
      "type": "private"
    },
    "date": 1784500310,
    "invoice": {
      "title": "Pro plan — 1 month",
      "description": "Unlimited alerts and priority delivery for 30 days.",
      "start_parameter": "",
      "currency": "USD",
      "total_amount": 999
    }
  }
}

Creates a shareable invoice link instead of sending an invoice into a chat.

The currency model is not finalised, exactly as described on sendInvoice: Stars/XTR versus GROM's internal Take token is an open decision, no payment provider is connected, and currency, prices and provider_token may change shape. Build against this signature knowing it is provisional.

The link is not bound to a chat or to a person — anyone holding it can open it. That makes it right for a website checkout button and wrong for a per-user order you expected only one person to be able to pay. Scope entitlements by whoever actually pays, which you learn from the successful-payment update, not by who you handed the link to.

ParameterTypeRequiredDescription
titleStringYesProduct name, 1–32 characters.
descriptionStringYesProduct description, 1–255 characters.
payloadStringYes1–128 bytes of bot-defined data echoed back at pre-checkout and on success. Because the link is shareable, treat this as an order-template key rather than an identifier for one specific buyer.
provider_tokenStringOptionalToken identifying the payment provider. Reserved — no provider is connected yet.
currencyStringYesThree-letter ISO 4217 code. Which codes are accepted here is undecided.
pricesArray of LabeledPriceYesPrice breakdown, amounts as integers in the currency's smallest unit. The charge is the sum of the line items.
max_tip_amountIntegerOptionalLargest accepted tip in the smallest units, default 0 meaning tipping is off.
suggested_tip_amountsArray of IntegerOptionalUp to 4 suggested tips, strictly increasing, each at most max_tip_amount.
provider_dataStringOptionalJSON blob forwarded to the provider verbatim.
photo_urlStringOptionalPublic URL of the product image, fetched by the gateway.
photo_sizeIntegerOptionalSize of the product photo in bytes.
photo_widthIntegerOptionalProduct photo width in pixels.
photo_heightIntegerOptionalProduct photo height in pixels.
need_nameBooleanOptionalRequires the buyer's full name before the order completes.
need_phone_numberBooleanOptionalRequires the buyer's phone number.
need_emailBooleanOptionalRequires the buyer's email address.
need_shipping_addressBooleanOptionalRequires a shipping address.
send_phone_number_to_providerBooleanOptionalForwards the collected phone number to the payment provider.
send_email_to_providerBooleanOptionalForwards the collected email address to the payment provider.
is_flexibleBooleanOptionalMarks the final price as dependent on the shipping address, which obliges you to answer the resulting shipping_query.

Returns — String — the created invoice link

Common errors429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error: temporarily unavailable · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/createInvoiceLink \
  -H 'Content-Type: application/json' \
  -d '{"title":"Pro plan — 1 month","description":"Unlimited alerts and priority delivery for 30 days.","payload":"plan_pro_monthly","currency":"USD","prices":[{"label":"Pro plan","amount":999}],"need_email":true}'
python
result = await bot.create_invoice_link(
    title="Pro plan — 1 month",
    description="Unlimited alerts and priority delivery for 30 days.",
    payload="plan_pro_monthly",
    currency="USD",
    prices=[
        {
            "label": "Pro plan",
            "amount": 999
        }
    ],
    need_email=True,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/createInvoiceLink", [
    'title' => 'Pro plan — 1 month',
    'description' => 'Unlimited alerts and priority delivery for 30 days.',
    'payload' => 'plan_pro_monthly',
    'currency' => 'USD',
    'prices' => [
        [
            'label' => 'Pro plan',
            'amount' => 999
        ]
    ],
    'need_email' => true
])->json();

Response

json
{
  "ok": true,
  "result": "https://code.casinoplaneta.info/invoice/9dK2mQ7bXr"
}

answerShippingQuery planned

Replies to a shipping_query raised by an invoice sent with is_flexible.

You get roughly 10 seconds. Look the address up against a precomputed table rather than calling a carrier API inline — a slow lookup fails the checkout for the user with no useful message.

Answering ok: false is a legitimate outcome, not an error path: it is how you say you do not ship to that address. Put something the buyer can act on in error_message, because it is shown to them verbatim.

ParameterTypeRequiredDescription
shipping_query_idStringYesId from the incoming shipping_query update. Single-use and short-lived — it stops being answerable once the window closes.
okBooleanYestrue if delivery to the submitted address is possible, in which case shipping_options is required. false rejects the address and requires error_message.
shipping_optionsArray of ShippingOptionOptionalAvailable delivery options, required when ok is true. Each carries its own prices array whose amounts are added to the invoice total, in the smallest units of the invoice currency.
error_messageStringOptionalHuman-readable reason shown to the buyer when ok is false, e.g. why that region is unavailable. Required in that case.

Returns — True on success

Common errors429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error: temporarily unavailable · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/answerShippingQuery \
  -H 'Content-Type: application/json' \
  -d '{"shipping_query_id":"6031849573920164","ok":true,"shipping_options":[{"id":"standard","title":"Standard (3–5 days)","prices":[{"label":"Standard","amount":500}]},{"id":"express","title":"Express (next day)","prices":[{"label":"Express","amount":1500}]}]}'
python
result = await bot.answer_shipping_query(
    shipping_query_id="6031849573920164",
    ok=True,
    shipping_options=[
        {
            "id": "standard",
            "title": "Standard (3–5 days)",
            "prices": [
                {
                    "label": "Standard",
                    "amount": 500
                }
            ]
        },
        {
            "id": "express",
            "title": "Express (next day)",
            "prices": [
                {
                    "label": "Express",
                    "amount": 1500
                }
            ]
        }
    ],
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/answerShippingQuery", [
    'shipping_query_id' => '6031849573920164',
    'ok' => true,
    'shipping_options' => [
        [
            'id' => 'standard',
            'title' => 'Standard (3–5 days)',
            'prices' => [
                [
                    'label' => 'Standard',
                    'amount' => 500
                ]
            ]
        ],
        [
            'id' => 'express',
            'title' => 'Express (next day)',
            'prices' => [
                [
                    'label' => 'Express',
                    'amount' => 1500
                ]
            ]
        ]
    ]
])->json();

Response

json
{
  "ok": true,
  "result": true
}

answerPreCheckoutQuery planned

Confirms or rejects a payment immediately before it is charged.

This is the last point at which you can stop a charge, and you have about 10 seconds to respond. Re-check the thing that can change between invoice and payment — stock, seat availability, whether the offer expired — using the payload you set on the invoice to find the order.

Answering ok: true is a commitment. Once the charge goes through, undoing it means a refund, so do the validation here rather than in the successful-payment handler.

ParameterTypeRequiredDescription
pre_checkout_query_idStringYesId from the incoming pre_checkout_query update. Single-use; letting the window lapse fails the payment on the buyer's side with no explanation.
okBooleanYestrue to proceed with the charge, false to abort it. When false, error_message is required.
error_messageStringOptionalReason shown to the buyer when you decline, e.g. that the last item sold out while they were paying. Write it for the buyer, not for your logs.

Returns — True on success

Common errors429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error: temporarily unavailable · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/answerPreCheckoutQuery \
  -H 'Content-Type: application/json' \
  -d '{"pre_checkout_query_id":"3841027465019283","ok":true}'
python
result = await bot.answer_pre_checkout_query(
    pre_checkout_query_id="3841027465019283",
    ok=True,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/answerPreCheckoutQuery", [
    'pre_checkout_query_id' => '3841027465019283',
    'ok' => true
])->json();

Response

json
{
  "ok": true,
  "result": true
}

refundStarPayment planned

Refunds a completed payment back to the buyer.

What this actually refunds is undecided. The method name and parameter names are inherited from the Telegram Bot API so that ported code compiles, but whether GROM settles bot payments in Stars/XTR, in its internal Take token, or in something else is an open question on this project. No payment provider is connected yet, so nothing can be charged and therefore nothing can be refunded today.

Refund timing, fees and whether a refund can be partial all depend on that unresolved decision — this page does not state them because they have not been chosen.

The charge id arrives in the successful_payment object on the message that completed the order. Store it at that moment; it is the only handle you get on the transaction.

ParameterTypeRequiredDescription
user_idIntegerYesIdentifier of the user whose payment is being refunded. Must be the account that actually paid, which for an invoice link is not necessarily the account you sent the link to.
telegram_payment_charge_idStringYesCharge identifier from the successful_payment object. The telegram_ prefix is kept for drop-in compatibility with existing Telegram bots and may be renamed once the currency model is settled.

Returns — True on success

Common errors429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error: temporarily unavailable · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/refundStarPayment \
  -H 'Content-Type: application/json' \
  -d '{"user_id":918273645,"telegram_payment_charge_id":"chg_5f81a3c90e7d4b26"}'
python
result = await bot.refund_star_payment(
    user_id=918273645,
    telegram_payment_charge_id="chg_5f81a3c90e7d4b26",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/refundStarPayment", [
    'user_id' => 918273645,
    'telegram_payment_charge_id' => 'chg_5f81a3c90e7d4b26'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

Games

HTML5 games and their high-score tables.

sendGame planned

Sends a game card with a Play button into a chat.

A game is identified by the short_name you registered for the bot, never by its URL. The gateway resolves the short name to the hosted game at send time, so moving the game to a different host does not change how you send it — and a short name registered to another bot is not visible to yours.

If you omit reply_markup, the card carries a single Play button. If you supply one, its first button must be a callback_game button, otherwise the message has no way to launch the game.

ParameterTypeRequiredDescription
chat_idIntegerYesTarget chat. Positive for a private chat, negative for a group, -100-prefixed for a channel. Games cannot be sent to a chat the bot has never been introduced to.
game_short_nameStringYesShort name of a game registered for this bot. Serves as the unique identifier of the game; an unregistered or foreign short name is rejected rather than rendered as plain text.
message_thread_idIntegerOptionalTopic to post into, for groups with forum mode enabled. Ignored elsewhere.
disable_notificationBooleanOptionalDelivers the message without a push or sound. The message still counts as unread.
protect_contentBooleanOptionalBlocks forwarding and saving of the sent message. Applies from the moment of sending and cannot be turned off later.
reply_parametersReplyParametersOptionalMakes the card a reply. Set allow_sending_without_reply inside it to send anyway when the target message is gone, instead of failing the call.
reply_markupInlineKeyboardMarkupOptionalInline keyboard shown under the card. The first button must be a callback_game button; the remaining buttons behave as ordinary inline buttons.

Returns — Message

Common errors400 chat not found · 403 bot is not a member of the chat · 403 bot was blocked by the user · 400 not enough rights · 400 message to reply not found · 429 Too Many Requests: retry after N · 429 Too Many Requests: retry after N · 401 Unauthorized

bash
curl https://api.casinoplaneta.info/bot$TOKEN/sendGame \
  -H 'Content-Type: application/json' \
  -d '{"chat_id":152874431,"game_short_name":"tower_blocks"}'
python
result = await bot.send_game(
    chat_id=152874431,
    game_short_name="tower_blocks",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/sendGame", [
    'chat_id' => 152874431,
    'game_short_name' => 'tower_blocks'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 1487,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Arcade",
      "username": "arcade_bot"
    },
    "chat": {
      "id": 152874431,
      "first_name": "Dilshod",
      "username": "dilshod",
      "type": "private"
    },
    "date": 1784469200,
    "game": {
      "title": "Tower Blocks",
      "description": "Stack the blocks as high as you can.",
      "photo": [
        {
          "file_id": "AgACAgIAAxkBAAIFo2ZQK3RvdGVzdGdhbWU",
          "file_unique_id": "AQADk7cxG1KJgVN9",
          "width": 320,
          "height": 180,
          "file_size": 12034
        }
      ]
    }
  }
}

setGameScore planned

Records a user's score in the game message the bot sent.

By default the call is refused when the new score is not higher than the score already stored for that user in that chat. This is deliberate: a game client that replays or resubmits an old result cannot walk the high-score table backwards. Pass force to overwrite anyway — the intended use is correcting a score your own backend awarded wrongly, not routine submission.

Address the message either by chat_id + message_id, or by inline_message_id for a game sent through inline mode. Sending both, or neither, is an error.

ParameterTypeRequiredDescription
user_idIntegerYesUser whose score is being set. Must be the player the game was launched by, not the chat owner.
scoreIntegerYesNew score, non-negative. Interpreted in whatever unit your game uses; the platform only compares it against the stored value.
forceBooleanOptionalAccepts a score that is lower than or equal to the stored one. Without it, such a call fails and the stored score is left untouched.
disable_edit_messageBooleanOptionalLeaves the game message alone instead of rewriting its high-score table in place. Useful when several scores land in quick succession and the edits would be visible churn.
chat_idIntegerOptionalChat holding the game message. Required unless inline_message_id is given.
message_idIntegerOptionalIdentifier of the sent game message. Required unless inline_message_id is given, and only meaningful together with chat_id.
inline_message_idStringOptionalIdentifier of an inline message carrying the game. Required unless chat_id and message_id are given.

Returns — The edited Message, or True when the game lives in an inline message

Common errors400 chat not found · 400 message to edit not found · 400 message can't be edited · 429 Too Many Requests: retry after N · 401 Unauthorized

bash
curl https://api.casinoplaneta.info/bot$TOKEN/setGameScore \
  -H 'Content-Type: application/json' \
  -d '{"user_id":152874431,"score":4820,"chat_id":152874431,"message_id":1487}'
python
result = await bot.set_game_score(
    user_id=152874431,
    score=4820,
    chat_id=152874431,
    message_id=1487,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/setGameScore", [
    'user_id' => 152874431,
    'score' => 4820,
    'chat_id' => 152874431,
    'message_id' => 1487
])->json();

Response

json
{
  "ok": true,
  "result": {
    "message_id": 1487,
    "from": {
      "id": 4210,
      "is_bot": true,
      "first_name": "Arcade",
      "username": "arcade_bot"
    },
    "chat": {
      "id": 152874431,
      "first_name": "Dilshod",
      "username": "dilshod",
      "type": "private"
    },
    "date": 1784469200,
    "edit_date": 1784469865,
    "game": {
      "title": "Tower Blocks",
      "description": "Stack the blocks as high as you can.",
      "text": "Dilshod scored 4820",
      "photo": [
        {
          "file_id": "AgACAgIAAxkBAAIFo2ZQK3RvdGVzdGdhbWU",
          "file_unique_id": "AQADk7cxG1KJgVN9",
          "width": 320,
          "height": 180,
          "file_size": 12034
        }
      ]
    }
  }
}

getGameHighScores planned

Returns the high-score table around a given user for one game message.

This is not a full leaderboard. You get the target user plus a few neighbours on either side, and the top of the table when the user is far from it. There is no paging — a game that needs a complete ranking has to keep its own copy of the scores it submits.

Address the message the same way as setGameScore: chat_id + message_id, or inline_message_id.

ParameterTypeRequiredDescription
user_idIntegerYesUser the returned window is centred on.
chat_idIntegerOptionalChat holding the game message. Required unless inline_message_id is given.
message_idIntegerOptionalIdentifier of the sent game message. Required unless inline_message_id is given.
inline_message_idStringOptionalIdentifier of an inline message carrying the game. Required unless chat_id and message_id are given.

Returns — Array of GameHighScore

Common errors400 chat not found · 400 message to edit not found · 400 message can't be edited · 429 Too Many Requests: retry after N · 401 Unauthorized

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getGameHighScores \
  -H 'Content-Type: application/json' \
  -d '{"user_id":152874431,"chat_id":152874431,"message_id":1487}'
python
result = await bot.get_game_high_scores(
    user_id=152874431,
    chat_id=152874431,
    message_id=1487,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/getGameHighScores", [
    'user_id' => 152874431,
    'chat_id' => 152874431,
    'message_id' => 1487
])->json();

Response

json
{
  "ok": true,
  "result": [
    {
      "position": 1,
      "user": {
        "id": 98110234,
        "is_bot": false,
        "first_name": "Aziza",
        "username": "aziza"
      },
      "score": 9120
    },
    {
      "position": 2,
      "user": {
        "id": 152874431,
        "is_bot": false,
        "first_name": "Dilshod",
        "username": "dilshod"
      },
      "score": 4820
    },
    {
      "position": 3,
      "user": {
        "id": 77450912,
        "is_bot": false,
        "first_name": "Bekzod"
      },
      "score": 3310
    }
  ]
}

Stickers

Read and build sticker sets.

getStickerSet planned

Returns a sticker set by name, including every sticker in it.

Works for any set, not only the ones this bot created. Use it to resolve a set_name you received on an incoming sticker into the full list of file_ids you can re-send.

ParameterTypeRequiredDescription
nameStringYesName of the set, as it appears in sticker.set_name on a received message. Matched case-insensitively, but the bot-username suffix is part of the name and cannot be dropped.

Returns — StickerSet

Common errors429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/getStickerSet \
  -H 'Content-Type: application/json' \
  -d '{"name":"cats_by_arcade_bot"}'
python
result = await bot.get_sticker_set(
    name="cats_by_arcade_bot",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/getStickerSet", [
    'name' => 'cats_by_arcade_bot'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "name": "cats_by_arcade_bot",
    "title": "Cats",
    "sticker_type": "regular",
    "stickers": [
      {
        "file_id": "CAACAgIAAxkBAAIEd2ZQK3IAAUqJgVMAAWNhdHMB",
        "file_unique_id": "AgADQwEAAlKJgVM",
        "type": "regular",
        "width": 512,
        "height": 512,
        "is_animated": false,
        "is_video": false,
        "emoji": "🐱",
        "set_name": "cats_by_arcade_bot",
        "file_size": 24310
      },
      {
        "file_id": "CAACAgIAAxkBAAIEeGZQK3MAAkqJgVMAAWNhdHMC",
        "file_unique_id": "AgADRAEAAlKJgVM",
        "type": "regular",
        "width": 512,
        "height": 512,
        "is_animated": false,
        "is_video": false,
        "emoji": "😻",
        "set_name": "cats_by_arcade_bot",
        "file_size": 21877
      }
    ]
  }
}

uploadStickerFile planned

Uploads a file for later use in createNewStickerSet or addStickerToSet.

This is a multipart/form-data upload — the bytes go in the request body, there is no URL or file_id form of it. The returned file_id is only accepted by the sticker-set methods; you cannot send it to a chat.

The format must match what you declare. A static sticker is a PNG or WEBP whose longer side is exactly 512 pixels; an animated sticker is a TGS; a video sticker is WEBM encoded with VP9. The server sniffs the actual bytes, so renaming a file does not change how it is classified — a mismatch comes back as wrong file type, not as a broken sticker. Size ceilings are on the Limits page.

ParameterTypeRequiredDescription
user_idIntegerYesOwner of the sticker set the file is being prepared for. The upload is scoped to this user, so a file uploaded for one owner cannot be used in another owner's set.
stickerInputFileYesThe file itself, sent as a multipart part. Passing a URL or an existing file_id is not supported here.
sticker_formatStringYesOne of static, animated or video. Must agree with the bytes you send; the declared value alone is not trusted.

Returns — File

Common errors400 wrong file type · 413 Request Entity Too Large · 400 file is not ready · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/uploadStickerFile \
  -H 'Content-Type: application/json' \
  -d '{"user_id":152874431,"sticker":"@cat_01.png","sticker_format":"static"}'
python
result = await bot.upload_sticker_file(
    user_id=152874431,
    sticker="@cat_01.png",
    sticker_format="static",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/uploadStickerFile", [
    'user_id' => 152874431,
    'sticker' => '@cat_01.png',
    'sticker_format' => 'static'
])->json();

Response

json
{
  "ok": true,
  "result": {
    "file_id": "CAACAgIAAxkBAAIEd2ZQK3IAAUqJgVMAAWNhdHMB",
    "file_unique_id": "AgADQwEAAlKJgVM",
    "file_size": 24310,
    "file_path": "stickers/file_12.png"
  }
}

createNewStickerSet planned

Creates a new sticker set owned by a user.

The set name must end with _by_<bot_username> — for example cats_by_arcade_bot. It may contain only English letters, digits and underscores, and must start with a letter. The suffix is what ties the set to your bot: without it the name is rejected, and it is also why a set name is globally unique across all bots.

Each entry in stickers carries its own emoji_list, and it is required — a sticker with no emoji cannot be found through the picker's search. All entries must share one format: a set is static, animated or video as a whole.

ParameterTypeRequiredDescription
user_idIntegerYesUser who will own the set. They must have started the bot at least once, otherwise the bot cannot act on their behalf.
nameStringYesShort name used in URLs and in sticker.set_name. Must end with _by_<bot_username> and is permanent — renaming a set later is not possible.
titleStringYesHuman-readable name shown above the set in the picker. Unlike name, it can be changed later and needs no suffix.
stickersArray of InputStickerYesThe initial stickers. Each element gives the file (a file_id from uploadStickerFile, an HTTP URL, or a multipart attachment), its format, and the emoji_list it answers to.
sticker_typeStringOptionalOne of regular, mask or custom_emoji. Defaults to regular. Fixed at creation — a set cannot change type afterwards.
needs_repaintingBooleanOptionalFor custom-emoji sets only: recolours the stickers to the text colour where they are shown. Ignored for other set types.

Returns — True on success

Common errors400 wrong file type · 413 Request Entity Too Large · 400 file is not ready · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/createNewStickerSet \
  -H 'Content-Type: application/json' \
  -d '{"user_id":152874431,"name":"cats_by_arcade_bot","title":"Cats","sticker_type":"regular","stickers":[{"sticker":"CAACAgIAAxkBAAIEd2ZQK3IAAUqJgVMAAWNhdHMB","format":"static","emoji_list":["🐱"]},{"sticker":"CAACAgIAAxkBAAIEeGZQK3MAAkqJgVMAAWNhdHMC","format":"static","emoji_list":["😻","❤️"]}]}'
python
result = await bot.create_new_sticker_set(
    user_id=152874431,
    name="cats_by_arcade_bot",
    title="Cats",
    sticker_type="regular",
    stickers=[
        {
            "sticker": "CAACAgIAAxkBAAIEd2ZQK3IAAUqJgVMAAWNhdHMB",
            "format": "static",
            "emoji_list": [
                "🐱"
            ]
        },
        {
            "sticker": "CAACAgIAAxkBAAIEeGZQK3MAAkqJgVMAAWNhdHMC",
            "format": "static",
            "emoji_list": [
                "😻",
                "❤️"
            ]
        }
    ],
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/createNewStickerSet", [
    'user_id' => 152874431,
    'name' => 'cats_by_arcade_bot',
    'title' => 'Cats',
    'sticker_type' => 'regular',
    'stickers' => [
        [
            'sticker' => 'CAACAgIAAxkBAAIEd2ZQK3IAAUqJgVMAAWNhdHMB',
            'format' => 'static',
            'emoji_list' => [
                '🐱'
            ]
        ],
        [
            'sticker' => 'CAACAgIAAxkBAAIEeGZQK3MAAkqJgVMAAWNhdHMC',
            'format' => 'static',
            'emoji_list' => [
                '😻',
                '❤️'
            ]
        ]
    ]
])->json();

Response

json
{
  "ok": true,
  "result": true
}

addStickerToSet planned

Appends one sticker to a set the bot created.

The new sticker must have the same format and type as the set — you cannot mix a video sticker into a static set, or add a mask to a regular one. The per-set sticker ceiling is on the Limits page.

ParameterTypeRequiredDescription
user_idIntegerYesOwner of the set. Must match the owner given at creation.
nameStringYesName of the set to extend, including the _by_<bot_username> suffix. A bot can only modify sets it created itself.
stickerInputStickerYesThe sticker to append: the file, its format, and its emoji_list. Appended at the end; use setStickerPositionInSet to move it.

Returns — True on success

Common errors400 wrong file type · 413 Request Entity Too Large · 400 file is not ready · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/addStickerToSet \
  -H 'Content-Type: application/json' \
  -d '{"user_id":152874431,"name":"cats_by_arcade_bot","sticker":{"sticker":"CAACAgIAAxkBAAIEeWZQK3QAA0qJgVMAAWNhdHMD","format":"static","emoji_list":["🙀"]}}'
python
result = await bot.add_sticker_to_set(
    user_id=152874431,
    name="cats_by_arcade_bot",
    sticker={
        "sticker": "CAACAgIAAxkBAAIEeWZQK3QAA0qJgVMAAWNhdHMD",
        "format": "static",
        "emoji_list": [
            "🙀"
        ]
    },
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/addStickerToSet", [
    'user_id' => 152874431,
    'name' => 'cats_by_arcade_bot',
    'sticker' => [
        'sticker' => 'CAACAgIAAxkBAAIEeWZQK3QAA0qJgVMAAWNhdHMD',
        'format' => 'static',
        'emoji_list' => [
            '🙀'
        ]
    ]
])->json();

Response

json
{
  "ok": true,
  "result": true
}

setStickerPositionInSet planned

Moves a sticker to a different position inside its set.

The sticker is identified by its own file_id, not by the set name — the set is inferred from it. Positions are zero-based and shift the other stickers rather than swapping with them.

ParameterTypeRequiredDescription
stickerStringYesFile identifier of the sticker to move. Must belong to a set this bot created.
positionIntegerYesNew zero-based position. A value past the end of the set is rejected rather than clamped.

Returns — True on success

Common errors429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/setStickerPositionInSet \
  -H 'Content-Type: application/json' \
  -d '{"sticker":"CAACAgIAAxkBAAIEeWZQK3QAA0qJgVMAAWNhdHMD","position":0}'
python
result = await bot.set_sticker_position_in_set(
    sticker="CAACAgIAAxkBAAIEeWZQK3QAA0qJgVMAAWNhdHMD",
    position=0,
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/setStickerPositionInSet", [
    'sticker' => 'CAACAgIAAxkBAAIEeWZQK3QAA0qJgVMAAWNhdHMD',
    'position' => 0
])->json();

Response

json
{
  "ok": true,
  "result": true
}

deleteStickerFromSet planned

Removes one sticker from the set it belongs to.

Removing the last sticker deletes the set itself. Messages already sent with that sticker keep rendering it — deletion affects the set, not history.

ParameterTypeRequiredDescription
stickerStringYesFile identifier of the sticker to remove. Must belong to a set this bot created.

Returns — True on success

Common errors429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/deleteStickerFromSet \
  -H 'Content-Type: application/json' \
  -d '{"sticker":"CAACAgIAAxkBAAIEeWZQK3QAA0qJgVMAAWNhdHMD"}'
python
result = await bot.delete_sticker_from_set(
    sticker="CAACAgIAAxkBAAIEeWZQK3QAA0qJgVMAAWNhdHMD",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/deleteStickerFromSet", [
    'sticker' => 'CAACAgIAAxkBAAIEeWZQK3QAA0qJgVMAAWNhdHMD'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

setStickerSetThumbnail planned

Sets the thumbnail shown for a sticker set in the picker.

The thumbnail's format has to match the set's: a static set takes a PNG or WEBP, an animated set a TGS, a video set a WEBM. Uploading the wrong one fails on the sniffed bytes rather than silently falling back.

Omit thumbnail to drop the custom image — the set then shows its first sticker again.

ParameterTypeRequiredDescription
nameStringYesName of the set, including the _by_<bot_username> suffix. Only sets this bot created can be changed.
user_idIntegerYesOwner of the set.
formatStringYesOne of static, animated or video. Must be the format of the set itself, even when thumbnail is omitted.
thumbnailInputFile or StringOptionalThe thumbnail as a multipart attachment, an HTTP URL, or a file_id already on the server. Leave it out to reset the set to its first sticker.

Returns — True on success

Common errors400 wrong file type · 413 Request Entity Too Large · 400 file is not ready · 429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/setStickerSetThumbnail \
  -H 'Content-Type: application/json' \
  -d '{"name":"cats_by_arcade_bot","user_id":152874431,"format":"static","thumbnail":"CAACAgIAAxkBAAIEd2ZQK3IAAUqJgVMAAWNhdHMB"}'
python
result = await bot.set_sticker_set_thumbnail(
    name="cats_by_arcade_bot",
    user_id=152874431,
    format="static",
    thumbnail="CAACAgIAAxkBAAIEd2ZQK3IAAUqJgVMAAWNhdHMB",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/setStickerSetThumbnail", [
    'name' => 'cats_by_arcade_bot',
    'user_id' => 152874431,
    'format' => 'static',
    'thumbnail' => 'CAACAgIAAxkBAAIEd2ZQK3IAAUqJgVMAAWNhdHMB'
])->json();

Response

json
{
  "ok": true,
  "result": true
}

deleteStickerSet planned

Deletes an entire sticker set.

Only works on sets this bot created — there is no way to delete a set that belongs to another bot, even if the same user owns it. Messages already sent with stickers from the set keep rendering them; deletion removes the set, not history.

ParameterTypeRequiredDescription
nameStringYesName of the set to delete, including the _by_<bot_username> suffix.

Returns — True on success

Common errors429 Too Many Requests: retry after N · 401 Unauthorized · 500 Internal Server Error

bash
curl https://api.casinoplaneta.info/bot$TOKEN/deleteStickerSet \
  -H 'Content-Type: application/json' \
  -d '{"name":"cats_by_arcade_bot"}'
python
result = await bot.delete_sticker_set(
    name="cats_by_arcade_bot",
)
php
$result = Http::post("https://api.casinoplaneta.info/bot{$token}/deleteStickerSet", [
    'name' => 'cats_by_arcade_bot'
])->json();

Response

json
{
  "ok": true,
  "result": true
}