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
GETandPOSTwork. - Parameters can be sent in four ways — a query string (
?chat_id=1&text=hi),application/x-www-form-urlencoded,application/json, andmultipart/form-datawhen 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_code | When | Example description |
|---|---|---|
400 | A parameter is wrong | Bad 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 |
401 | The token is wrong | Unauthorized |
403 | No permission to send | Forbidden: bot can't initiate conversation with a user, Forbidden: bot is not a member of the group chat |
404 | Unknown method or file | Not Found: method not found |
409 | getUpdates while a webhook is set | Conflict: can't use getUpdates method while webhook is active; use deleteWebhook to delete the webhook first |
413 | The body is larger than 12 MB | Request Entity Too Large |
500 | Internal server error | Internal 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
offset | Integer | Optional | Only updates with this number or higher. Lower ones are removed as "received". Pass the last handled update_id + 1. |
limit | Integer | Optional | 1–100, default 100 |
timeout | Integer | Optional | Seconds to wait, 0–50, default 0. We recommend 25–30. |
allowed_updates | Array of String | Ignored | Accepted 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
url | String | Yes | A public https:// address. An empty string removes the webhook |
secret_token | String | Optional | 1–256 characters, A-Z a-z 0-9 _ -. Sent in the X-Telegram-Bot-Api-Secret-Token request header |
allowed_updates | Array of String | Optional | Any of message, edited_message, callback_query. Empty means all |
drop_pending_updates | Boolean | Optional | Discards the waiting updates |
max_connections | Integer | Optional | 1–100, default 40. Only stored; delivery is one at a time per bot |
certificate | InputFile | Not supported | 400 — use a certificate from a public authority |
ip_address | String | Ignored |
Returns: True
deleteWebhook
POST/bot<token>/deleteWebhook
Removes the webhook and goes back to getUpdates. Succeeds even if there's no webhook.
| Parameter | Type | Required | Description |
|---|---|---|---|
drop_pending_updates | Boolean | Optional | Discards 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
| Method | What it does |
|---|---|
getMe | Information about the bot itself |
sendMessage | Send text (buttons included) |
sendPhoto | Send a photo |
editMessageText | Edit the text and buttons of a sent message |
editMessageCaption | Edit a photo caption |
editMessageReplyMarkup | Edit only the buttons |
deleteMessage | Delete a message |
answerCallbackQuery | Answer a button press |
sendChatAction | "Typing" indicator (accepted only) |
getChat | Chat information |
getFile | Download path of a received photo |
setMyCommands · getMyCommands · deleteMyCommands | Command menu |
logOut · close | For compatibility (do nothing) |
getUpdates · setWebhook · deleteWebhook · getWebhookInfo | Getting updates (above) |
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
| Parameter | Type | Required | Description |
|---|---|---|---|
chat_id | Integer or String | Yes | The chat to send to (see the box above) |
text | String | Yes | 1–4,096 characters. Sent as plain text |
reply_markup | InlineKeyboardMarkup · ReplyKeyboardMarkup · ReplyKeyboardRemove · ForceReply | Optional | Buttons |
disable_notification | Boolean | Optional | Send silently |
parse_mode, entities, reply_parameters, link_preview_options … | Ignored | Accepted 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
| Parameter | Type | Required | Description |
|---|---|---|---|
chat_id | Integer or String | Yes | The chat to send to |
photo | InputFile or String | Yes | A 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 |
caption | String | Optional | 0–1,024 characters |
reply_markup | Same as sendMessage | Optional | |
disable_notification | Boolean | Optional |
Returns: the sent Message (with a new file_id in photo)
editMessageText
POST/bot<token>/editMessageText
| Parameter | Type | Required | Description |
|---|---|---|---|
chat_id | Integer or String | Yes | The chat the message is in |
message_id | Integer | Yes | The message to edit (one the bot sent) |
text | String | Yes | The new text, 1–4,096 characters |
reply_markup | InlineKeyboardMarkup | Optional | New buttons. If you leave it out, the buttons are removed (same as Telegram) |
inline_message_id | String | Not supported | 400, 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
callback_query_id | String | Yes | The update's callback_query.id |
text | String | Optional | Text that pops up briefly at the top of the screen |
show_alert | Boolean | Optional | If true, shown as a dialog with an OK button |
url | String | Optional | An address for the app to open |
cache_time | Integer | Optional | Seconds 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
| Parameter | Type | Required | Description |
|---|---|---|---|
commands | Array of BotCommand | Yes | Up to 100 |
language_code | String | Optional | Stored as a per-language list. The app currently shows only the default list without a language code |
scope | BotCommandScope | Ignored |
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.
| Field | Type | Description |
|---|---|---|
update_id | Integer | A number that grows by 1. Used for getUpdates' offset |
message Optional | Message | A new message to the bot (one-to-one, or every message in a group the bot is a member of) |
edited_message Optional | Message | An edited message |
callback_query Optional | CallbackQuery | An inline button press |
User
| Field | Type | Description |
|---|---|---|
id | Integer | User ID (meaningful only within this server) |
is_bot | Boolean | true for bots |
first_name | String | First name. Deleted Account for deleted accounts |
last_name Optional | String | Last name |
username Optional | String | Username (without @) |
Chat
| Field | Type | Description |
|---|---|---|
id | Integer | For a person, that person's ID (positive); for a basic group, negative |
type | String | private or group |
title Optional | String | Group name (group) |
first_name, last_name, username Optional | String | The other person's name and username (private) |
Message
| Field | Type | Description |
|---|---|---|
message_id | Integer | The message's number within this chat |
from Optional | User | The sender |
chat | Chat | The chat the message is in |
date | Integer | When it was sent (Unix seconds) |
edit_date Optional | Integer | When it was last edited |
text Optional | String | The text (always present unless it's a photo message) |
entities Optional | Array of MessageEntity | Commands, mentions, URLs and hashtags in the text |
photo Optional | Array of PhotoSize | Photo (Pabal has just the original) |
caption Optional | String | Photo caption |
caption_entities Optional | Array of MessageEntity | Commands, mentions, URLs and hashtags in the caption |
reply_markup Optional | InlineKeyboardMarkup | Inline buttons attached to the message |
MessageEntity
| Field | Type | Description |
|---|---|---|
type | String | bot_command, mention, url, hashtag |
offset | Integer | Start position (in UTF-16 code units) |
length | Integer | Length (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
| Field | Type | Description |
|---|---|---|
file_id | String | The ID used for downloading (getFile) and resending (sendPhoto) |
file_unique_id | String | The same value for the same photo, even across bots. Can't be used for downloading |
width, height | Integer | Size in pixels |
file_size | Integer | Bytes |
File
| Field | Type | Description |
|---|---|---|
file_id, file_unique_id | String | Same as PhotoSize |
file_size | Integer | Bytes |
file_path | String | Of the form photos/<file_id>.jpg. Append it to /file/bot<token>/ to download |
CallbackQuery
| Field | Type | Description |
|---|---|---|
id | String | The ID to pass to answerCallbackQuery |
from | User | The person who pressed the button |
message Optional | Message | The message the button is attached to |
chat_instance | String | A value identifying that chat |
data Optional | String | The 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
| Field | Type | Description |
|---|---|---|
text | String | The button's label |
callback_data One of the two | String | The value sent to the bot when pressed, 1–64 bytes |
url One of the two | String | The address opened when pressed |
Other kinds, such as switch_inline_query, web_app, login_url and pay, don't exist yet (400).
ReplyKeyboardMarkup
| Field | Type | Description |
|---|---|---|
keyboard | Array of Array of KeyboardButton | The panel of buttons below the input field |
resize_keyboard, one_time_keyboard, is_persistent, selective Optional | Boolean | Fit to size · hide after one use · always shown · only for specific people |
input_field_placeholder Optional | String | Placeholder 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
| Field | Type | Description |
|---|---|---|
command | String | 1–32 characters of lowercase letters, digits and underscores (without /) |
description | String | 1–256 characters |
WebhookInfo
| Field | Type | Description |
|---|---|---|
url | String | The webhook address; an empty string if there is none |
has_custom_certificate | Boolean | Always false |
pending_update_count | Integer | The number of updates waiting to be delivered |
ip_address Optional | String | The IP it last sent to |
last_error_date Optional | Integer | When the last failure happened (Unix seconds) |
last_error_message Optional | String | The reason for the last failure (list) |
max_connections Optional | Integer | The value given to setWebhook |
allowed_updates Optional | Array of String | The value given to setWebhook |
Differences from the Telegram Bot API
- Kinds of updates: only
message,edited_messageandcallback_query. There are no channel posts, inline queries, payments, polls, membership changes (my_chat_member) and so on. - Formatting:
parse_modeandentitiesare 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_connectionsis 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…) returns404 Not Found: method not found.