Developer docs
English

Bot development

Bot API reference

Every method Pabal's HTTP Bot API accepts, the objects it exchanges and its errors, in full. Compatible with the Telegram Bot API.

Pabal's HTTP Bot API uses the same address format, requests, responses and errors as the Telegram Bot API. This reference covers only what Pabal actually accepts. Methods not listed here return 404 Not Found: method not found. If you're new, start with the bot tutorial.

Making requests

https://pabal.me/bot<token>/<method>
  • HTTP methods: both GET and POST work.
  • Parameters can be sent in four ways — a query string (?chat_id=1&text=hi), application/x-www-form-urlencoded, application/json, and multipart/form-data when uploading files. You can mix them.
  • Object parameters (reply_markup, commands, allowed_updates) go in as objects and arrays in a JSON body, and as JSON strings in forms and query strings.
  • Method names are case-insensitive (sendMessage = sendmessage).
  • Request bodies can be up to 12 MB (beyond that, 413).
  • File downloads use a separate address, https://pabal.me/file/bot<token>/<file_path> (see getFile).

Responses and errors

Responses are always JSON. On success you get HTTP 200 and result; on failure, an error_code equal to the HTTP status and a human-readable description.

{"ok": true, "result": { … }}
{"ok": false, "error_code": 400, "description": "Bad Request: chat not found"}
error_codeWhenExample description
400A parameter is wrongBad Request: chat not found, Bad Request: message text is empty, Bad Request: message is not modified: …, Bad Request: message to edit not found, Bad Request: wrong file identifier/HTTP URL specified, Bad Request: query is too old and response timeout expired or query ID is invalid
401The token is wrongUnauthorized
403No permission to sendForbidden: bot can't initiate conversation with a user, Forbidden: bot is not a member of the group chat
404Unknown method or fileNot Found: method not found
409getUpdates while a webhook is setConflict: can't use getUpdates method while webhook is active; use deleteWebhook to delete the webhook first
413The body is larger than 12 MBRequest Entity Too Large
500Internal server errorInternal Server Error

Pabal doesn't rate-limit requests yet (429 Too Many Requests), but that may come, so write your code to wait parameters.retry_after seconds and retry when it gets a 429.

Getting updates

Use one of these two approaches. You can't use both at the same time.

getUpdates

POST/bot<token>/getUpdates

Fetches waiting updates (long polling). Returns 409 if a webhook is set.

ParameterTypeRequiredDescription
offsetIntegerOptionalOnly updates with this number or higher. Lower ones are removed as "received". Pass the last handled update_id + 1.
limitIntegerOptional1–100, default 100
timeoutIntegerOptionalSeconds to wait, 0–50, default 0. We recommend 25–30.
allowed_updatesArray of StringIgnoredAccepted but not used. To filter by kind, filter after receiving.

Returns: Array of Update

setWebhook

POST/bot<token>/setWebhook

Receive updates at an HTTPS address. The details are in the Webhooks page.

ParameterTypeRequiredDescription
urlStringYesA public https:// address. An empty string removes the webhook
secret_tokenStringOptional1–256 characters, A-Z a-z 0-9 _ -. Sent in the X-Telegram-Bot-Api-Secret-Token request header
allowed_updatesArray of StringOptionalAny of message, edited_message, callback_query. Empty means all
drop_pending_updatesBooleanOptionalDiscards the waiting updates
max_connectionsIntegerOptional1–100, default 40. Only stored; delivery is one at a time per bot
certificateInputFileNot supported400 — use a certificate from a public authority
ip_addressStringIgnored

Returns: True

deleteWebhook

POST/bot<token>/deleteWebhook

Removes the webhook and goes back to getUpdates. Succeeds even if there's no webhook.

ParameterTypeRequiredDescription
drop_pending_updatesBooleanOptionalDiscards the waiting updates

Returns: True

getWebhookInfo

GET/bot<token>/getWebhookInfo

The webhook's status. No parameters. If there's no webhook, url is an empty string.

Returns: WebhookInfo

Methods

MethodWhat it does
getMeInformation about the bot itself
sendMessageSend text (buttons included)
sendPhotoSend a photo
editMessageTextEdit the text and buttons of a sent message
editMessageCaptionEdit a photo caption
editMessageReplyMarkupEdit only the buttons
deleteMessageDelete a message
answerCallbackQueryAnswer a button press
sendChatAction"Typing" indicator (accepted only)
getChatChat information
getFileDownload path of a received photo
setMyCommands · getMyCommands · deleteMyCommandsCommand menu
logOut · closeFor compatibility (do nothing)
getUpdates · setWebhook · deleteWebhook · getWebhookInfoGetting updates (above)
What you can put in chat_id

A person's ID (positive) for a one-to-one chat, the group ID (negative) for a basic group, or a person's username ("@hana_lee"). You can find all of them in an update's chat.id. Channel and supergroup IDs (-100…) don't exist yet (400 chat not found).

getMe

GET/bot<token>/getMe

Use it to check that the token is right. No parameters.

Returns: User — for bots, it also includes can_join_groups (true), can_read_all_group_messages (true), supports_inline_queries (false), can_connect_to_business (false) and has_main_web_app (false).

sendMessage

POST/bot<token>/sendMessage

ParameterTypeRequiredDescription
chat_idInteger or StringYesThe chat to send to (see the box above)
textStringYes1–4,096 characters. Sent as plain text
reply_markupInlineKeyboardMarkup · ReplyKeyboardMarkup · ReplyKeyboardRemove · ForceReplyOptionalButtons
disable_notificationBooleanOptionalSend silently
parse_mode, entities, reply_parameters, link_preview_optionsIgnoredAccepted but not used. Formatting is not applied

Returns: the sent Message

To send to a person, that person must have messaged the bot first (403 Forbidden: bot can't initiate conversation with a user). To send to a group, the bot must be a member of that group.

sendPhoto

POST/bot<token>/sendPhoto

ParameterTypeRequiredDescription
chat_idInteger or StringYesThe chat to send to
photoInputFile or StringYesA file uploaded as multipart/form-data (up to 10 MB; JPEG, PNG or GIF), or the file_id of a photo received earlier. URLs don't work yet
captionStringOptional0–1,024 characters
reply_markupSame as sendMessageOptional
disable_notificationBooleanOptional

Returns: the sent Message (with a new file_id in photo)

editMessageText

POST/bot<token>/editMessageText

ParameterTypeRequiredDescription
chat_idInteger or StringYesThe chat the message is in
message_idIntegerYesThe message to edit (one the bot sent)
textStringYesThe new text, 1–4,096 characters
reply_markupInlineKeyboardMarkupOptionalNew buttons. If you leave it out, the buttons are removed (same as Telegram)
inline_message_idStringNot supported400, since there's no inline mode

Returns: the edited Message. The change shows in the app right away, and messages edited by people reach the bot as edited_message.

If the text and buttons are exactly the same as before you get 400 Bad Request: message is not modified: …, and trying to edit someone else's message gives 400 Bad Request: message can't be edited.

editMessageCaption

POST/bot<token>/editMessageCaption

Parameters: chat_id, message_id, caption (0–1,024 characters; leave it out to remove the caption), reply_markup. Returns: the edited Message.

editMessageReplyMarkup

POST/bot<token>/editMessageReplyMarkup

Changes only the buttons and leaves the text as it is. Parameters: chat_id, message_id, reply_markup (leave it out to remove the buttons). Returns: the edited Message.

deleteMessage

POST/bot<token>/deleteMessage

Parameters: chat_id, message_id. Deletes the message for both sides of the chat. If the message doesn't exist: 400 Bad Request: message to delete not found. Returns: True.

answerCallbackQuery

POST/bot<token>/answerCallbackQuery

Answers a button someone pressed (CallbackQuery). You can answer within 10 seconds, and only once.

ParameterTypeRequiredDescription
callback_query_idStringYesThe update's callback_query.id
textStringOptionalText that pops up briefly at the top of the screen
show_alertBooleanOptionalIf true, shown as a dialog with an OK button
urlStringOptionalAn address for the app to open
cache_timeIntegerOptionalSeconds for the app to remember this answer

Returns: True. If it's too late or the query was already answered: 400 Bad Request: query is too old and response timeout expired or query ID is invalid.

sendChatAction

POST/bot<token>/sendChatAction

Accepted for compatibility and returns True, but the app doesn't show "typing…" yet.

getChat

GET/bot<token>/getChat?chat_id=…

Parameter: chat_id (a number). For a person, that person; for a group, only groups the bot is a member of. Returns: Chat. If it doesn't exist or can't be seen: 400 Bad Request: chat not found.

getFile

GET/bot<token>/getFile?file_id=…

Gets a download path from the file_id of a received photo. Returns: File. Then download it from this address.

https://pabal.me/file/bot<token>/<file_path>

A file_id is also the right to fetch that photo. If the value is wrong: 400 Bad Request: wrong file identifier/HTTP URL specified.

setMyCommands

POST/bot<token>/setMyCommands

ParameterTypeRequiredDescription
commandsArray of BotCommandYesUp to 100
language_codeStringOptionalStored as a per-language list. The app currently shows only the default list without a language code
scopeBotCommandScopeIgnored

Returns: True. If a command doesn't follow the rules: 400 Bad Request: BOT_COMMAND_INVALID.

getMyCommands

GET/bot<token>/getMyCommands

Parameter: language_code (optional). Returns: Array of BotCommand.

deleteMyCommands

POST/bot<token>/deleteMyCommands

Parameter: language_code (optional). Empties that list. Returns: True.

logOut · close

On Telegram, these methods are used when moving to a local Bot API server. Pabal has nowhere to move to, so it just accepts them and returns True. To invalidate a token, use BotFather's /revoke.

Objects

Optional next to a field means it may be absent. Other Telegram fields not listed here are never sent by Pabal.

Update

One new update. Contains update_id and one of the three fields below.

FieldTypeDescription
update_idIntegerA number that grows by 1. Used for getUpdates' offset
message OptionalMessageA new message to the bot (one-to-one, or every message in a group the bot is a member of)
edited_message OptionalMessageAn edited message
callback_query OptionalCallbackQueryAn inline button press

User

FieldTypeDescription
idIntegerUser ID (meaningful only within this server)
is_botBooleantrue for bots
first_nameStringFirst name. Deleted Account for deleted accounts
last_name OptionalStringLast name
username OptionalStringUsername (without @)

Chat

FieldTypeDescription
idIntegerFor a person, that person's ID (positive); for a basic group, negative
typeStringprivate or group
title OptionalStringGroup name (group)
first_name, last_name, username OptionalStringThe other person's name and username (private)

Message

FieldTypeDescription
message_idIntegerThe message's number within this chat
from OptionalUserThe sender
chatChatThe chat the message is in
dateIntegerWhen it was sent (Unix seconds)
edit_date OptionalIntegerWhen it was last edited
text OptionalStringThe text (always present unless it's a photo message)
entities OptionalArray of MessageEntityCommands, mentions, URLs and hashtags in the text
photo OptionalArray of PhotoSizePhoto (Pabal has just the original)
caption OptionalStringPhoto caption
caption_entities OptionalArray of MessageEntityCommands, mentions, URLs and hashtags in the caption
reply_markup OptionalInlineKeyboardMarkupInline buttons attached to the message

MessageEntity

FieldTypeDescription
typeStringbot_command, mention, url, hashtag
offsetIntegerStart position (in UTF-16 code units)
lengthIntegerLength (in UTF-16 code units)

The server finds these in the text and adds them automatically. Formatting entities such as bold and italic don't exist yet.

PhotoSize

FieldTypeDescription
file_idStringThe ID used for downloading (getFile) and resending (sendPhoto)
file_unique_idStringThe same value for the same photo, even across bots. Can't be used for downloading
width, heightIntegerSize in pixels
file_sizeIntegerBytes

File

FieldTypeDescription
file_id, file_unique_idStringSame as PhotoSize
file_sizeIntegerBytes
file_pathStringOf the form photos/<file_id>.jpg. Append it to /file/bot<token>/ to download

CallbackQuery

FieldTypeDescription
idStringThe ID to pass to answerCallbackQuery
fromUserThe person who pressed the button
message OptionalMessageThe message the button is attached to
chat_instanceStringA value identifying that chat
data OptionalStringThe button's callback_data

InlineKeyboardMarkup

inline_keyboard: Array of Array of InlineKeyboardButton — the outer array is the rows, and each inner array is the buttons in one row. Up to 100 rows and 100 buttons per message.

InlineKeyboardButton

FieldTypeDescription
textStringThe button's label
callback_data One of the twoStringThe value sent to the bot when pressed, 1–64 bytes
url One of the twoStringThe address opened when pressed

Other kinds, such as switch_inline_query, web_app, login_url and pay, don't exist yet (400).

ReplyKeyboardMarkup

FieldTypeDescription
keyboardArray of Array of KeyboardButtonThe panel of buttons below the input field
resize_keyboard, one_time_keyboard, is_persistent, selective OptionalBooleanFit to size · hide after one use · always shown · only for specific people
input_field_placeholder OptionalStringPlaceholder text in the input field

KeyboardButton

A single string, or an object with text and the optional fields request_contact (send my contact) and request_location (send my location), both Boolean.

ReplyKeyboardRemove

{"remove_keyboard": true} — hides the panel of buttons. selective is optional.

ForceReply

{"force_reply": true} — the app opens the input field ready to reply to this message. selective and input_field_placeholder are optional.

BotCommand

FieldTypeDescription
commandString1–32 characters of lowercase letters, digits and underscores (without /)
descriptionString1–256 characters

WebhookInfo

FieldTypeDescription
urlStringThe webhook address; an empty string if there is none
has_custom_certificateBooleanAlways false
pending_update_countIntegerThe number of updates waiting to be delivered
ip_address OptionalStringThe IP it last sent to
last_error_date OptionalIntegerWhen the last failure happened (Unix seconds)
last_error_message OptionalStringThe reason for the last failure (list)
max_connections OptionalIntegerThe value given to setWebhook
allowed_updates OptionalArray of StringThe value given to setWebhook

Differences from the Telegram Bot API

  • Kinds of updates: only message, edited_message and callback_query. There are no channel posts, inline queries, payments, polls, membership changes (my_chat_member) and so on.
  • Formatting: parse_mode and entities are ignored and text is sent as is. Only commands, mentions, URLs and hashtags are highlighted automatically.
  • Kinds of chats: one-to-one and basic groups only. No channels, supergroups or forum topics.
  • Media: photos only. No documents, videos, audio, stickers or albums, and no sending by URL.
  • Bots in groups: there's no privacy mode, so bots receive every message in the group.
  • Queue: unfetched updates are kept in memory, up to the most recent 1,000 per bot, and are lost when the server restarts (Telegram keeps them for 24 hours).
  • Webhooks: updates are sent one at a time, in order (max_connections is only stored), self-signed certificates aren't accepted, and there's no restriction on the port.
  • IDs: user, message and file IDs are meaningful only within this server. With no channels or supergroups, there are no IDs of the form -100… either.
  • Missing methods: anything not in the list above (forwardMessage, copyMessage, sendDocument, sendPoll, getChatMember, banChatMember, answerInlineQuery …) returns 404 Not Found: method not found.
© 2026 Pabal.me · Based on the Pabal server as of 2026-09-19 Docs home · Pabal.me