Desarrollo de bots
Referencia de la Bot API
Todos los métodos que acepta la Bot API HTTP de Pabal, los objetos que intercambia y sus errores, sin omitir nada. Compatible con la Bot API de Telegram.
La Bot API HTTP de Pabal usa el mismo formato de direcciones, peticiones, respuestas y errores que la Bot API de Telegram. Este documento recoge solo lo que Pabal acepta de verdad. Los métodos que no aparecen aquí devuelven 404 Not Found: method not found. Si es tu primera vez, empieza por el tutorial para crear un bot.
Enviar peticiones
https://pabal.me/bot<token>/<método>
- Método HTTP: se admiten tanto
GETcomoPOST. - Hay cuatro formas de enviar los parámetros: cadena de consulta (
?chat_id=1&text=hi),application/x-www-form-urlencoded,application/jsony, cuando subes archivos,multipart/form-data. Se pueden combinar. - Los parámetros de tipo objeto (
reply_markup,commands,allowed_updates) van tal cual, como objeto o array, en un cuerpo JSON, y como cadena JSON en formularios y cadenas de consulta. - El nombre del método no distingue entre mayúsculas y minúsculas (
sendMessage=sendmessage). - El tamaño del cuerpo puede llegar a 12 MB (por encima,
413). - La descarga de archivos usa otra dirección:
https://pabal.me/file/bot<token>/<file_path>(getFile).
Respuestas y errores
La respuesta siempre es JSON. Si todo va bien, llega un HTTP 200 con result; si falla, llega el estado HTTP correspondiente, un error_code igual a ese estado y una description legible para personas.
{"ok": true, "result": { … }}{"ok": false, "error_code": 400, "description": "Bad Request: chat not found"}| error_code | Cuándo | Ejemplos de description |
|---|---|---|
400 | Un parámetro es incorrecto | 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 | El token es incorrecto | Unauthorized |
403 | No hay permiso para enviar | Forbidden: bot can't initiate conversation with a user, Forbidden: bot is not a member of the group chat |
404 | Método o archivo inexistente | Not Found: method not found |
409 | getUpdates con un webhook configurado | Conflict: can't use getUpdates method while webhook is active; use deleteWebhook to delete the webhook first |
413 | El cuerpo supera los 12 MB | Request Entity Too Large |
500 | Error interno del servidor | Internal Server Error |
Pabal todavía no aplica un límite de frecuencia al conjunto de las peticiones, pero los métodos de voz tienen un límite por hora y pueden devolver 429 Too Many Requests: retry after N (Llamadas de voz). Prepara tu código para que, si recibe un 429, espere parameters.retry_after segundos antes de volver a enviar.
Recibir actualizaciones
Se usa uno de estos dos modos. No se pueden usar los dos a la vez.
getUpdates
POST/bot<token>/getUpdates
Recoge las actualizaciones pendientes (long polling). Si hay un webhook configurado, 409.
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
offset | Integer | Opcional | Solo las actualizaciones con este número o superior. Las anteriores se borran como «recibidas». Pasa el update_id + 1 del último que procesaste. |
limit | Integer | Opcional | De 1 a 100, 100 por defecto |
timeout | Integer | Opcional | Segundos de espera, de 0 a 50, 0 por defecto. Se recomienda entre 25 y 30. |
allowed_updates | Array of String | Opcional | Tipos que quieres recibir (Update). Si está vacío, todos los tipos predeterminados (message, edited_message, callback_query). Los tipos de voz solo llegan si los nombras. La lista que das queda guardada como ajuste del bot y se usa también en las llamadas siguientes y en el webhook (si no das ninguna, se mantiene la anterior) |
Devuelve: Array of Update
setWebhook
POST/bot<token>/setWebhook
Recibe las actualizaciones en una dirección HTTPS. La explicación detallada está en el documento Webhooks.
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
url | String | Sí | Dirección pública https://. Una cadena vacía elimina el webhook |
secret_token | String | Opcional | De 1 a 256 caracteres, A-Z a-z 0-9 _ -. Se envía en la cabecera de la petición X-Telegram-Bot-Api-Secret-Token |
allowed_updates | Array of String | Opcional | Entre message, edited_message y callback_query. Si se deja vacío, esos tres. Los tipos de voz (voice_call, voice_call_tool_query, voice_call_transcript) solo llegan si los nombras. Si no das ninguna lista, se mantiene la anterior |
drop_pending_updates | Boolean | Opcional | Descarta las actualizaciones pendientes |
max_connections | Integer | Opcional | De 1 a 100, 40 por defecto. Solo se guarda; la entrega es de una en una por bot |
certificate | InputFile | No admitido | 400: usa un certificado público |
ip_address | String | Se ignora |
Devuelve: True
deleteWebhook
POST/bot<token>/deleteWebhook
Elimina el webhook y vuelve a getUpdates. Tiene éxito aunque no haya webhook.
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
drop_pending_updates | Boolean | Opcional | Descarta las actualizaciones pendientes |
Devuelve: True
getWebhookInfo
GET/bot<token>/getWebhookInfo
Estado del webhook. Sin parámetros. Si no hay webhook, url es una cadena vacía.
Devuelve: WebhookInfo
Métodos
| Método | Qué hace |
|---|---|
getMe | Información del propio bot |
sendMessage | Enviar texto (con botones) |
sendPhoto | Enviar una foto |
sendDocument | Enviar un archivo |
sendLocation | Enviar una ubicación (también en tiempo real) |
sendVenue | Enviar un lugar (una ubicación con nombre y dirección) |
sendContact | Enviar una tarjeta de contacto |
editMessageLiveLocation · stopMessageLiveLocation | Mover o detener una ubicación en tiempo real |
editMessageText | Editar el texto y los botones de un mensaje enviado |
editMessageCaption | Editar el pie de una foto |
editMessageReplyMarkup | Editar solo los botones |
deleteMessage | Borrar un mensaje |
answerCallbackQuery | Responder a la pulsación de un botón |
sendChatAction | Indicador «escribiendo» (solo se acepta) |
getChat | Información de un chat |
getFile | Ruta de descarga de una foto recibida |
setMyCommands · getMyCommands · deleteMyCommands | Menú de comandos |
logOut · close | Por compatibilidad (no hacen nada) |
getUpdates · setWebhook · deleteWebhook · getWebhookInfo | Recibir actualizaciones (arriba) |
setVoiceCallSettings · getVoiceCallSettings · endVoiceCall · sendVoiceCallContext · getVoiceCallUsage · startVoiceCall · answerVoiceCallToolQuery · setVoiceCallApiKey · setVoiceCallMcpServer … | Bots de voz (abajo, en Llamadas de voz) |
chat_id
En un chat uno a uno, el ID de la persona (positivo); en un grupo básico, el ID del grupo (negativo); o bien el nombre de usuario de la persona ("@hana_lee"). Todos se pueden obtener del chat.id de las actualizaciones. Los IDs de canales y supergrupos (-100…) todavía no existen (400 chat not found).
getMe
GET/bot<token>/getMe
Sirve para comprobar que el token es correcto. Sin parámetros.
Devuelve: User. En el caso de un bot se añaden can_join_groups (true), can_read_all_group_messages (true), supports_inline_queries (false), can_connect_to_business (false) y has_main_web_app (false).
sendMessage
POST/bot<token>/sendMessage
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
chat_id | Integer o String | Sí | Chat de destino (ver el recuadro de arriba) |
text | String | Sí | De 1 a 4.096 caracteres. Se envía tal cual |
reply_markup | InlineKeyboardMarkup · ReplyKeyboardMarkup · ReplyKeyboardRemove · ForceReply | Opcional | Botones |
disable_notification | Boolean | Opcional | Enviar sin sonido |
parse_mode, entities, reply_parameters, link_preview_options … | Se ignora | Se aceptan pero no se usan. No se aplica ningún formato |
Devuelve: el Message enviado
Para escribir a una persona, esa persona tiene que haber escrito antes al bot (403 Forbidden: bot can't initiate conversation with a user). Para escribir en un grupo, el bot tiene que ser miembro de ese grupo.
sendPhoto
POST/bot<token>/sendPhoto
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
chat_id | Integer o String | Sí | Chat de destino |
photo | InputFile o String | Sí | Un archivo subido como multipart/form-data (hasta 10 MB; JPEG, PNG o GIF) o el file_id de una foto recibida antes. Las URL todavía no se admiten |
caption | String | Opcional | De 0 a 1.024 caracteres |
reply_markup | Igual que en sendMessage | Opcional | |
disable_notification | Boolean | Opcional |
Devuelve: el Message enviado (con un file_id nuevo en photo)
sendDocument
POST/bot<token>/sendDocument
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
chat_id | Integer o String | Sí | Chat de destino |
document | InputFile o String | Sí | Un archivo subido como multipart/form-data (sin restricción de formato, hasta 20 MB) o el file_id de un archivo recibido antes. Las URL todavía no se admiten |
filename | String | Opcional | Nombre que verá quien lo reciba. Si no se indica, se usa el del archivo subido |
caption | String | Opcional | De 0 a 1.024 caracteres |
reply_markup | Igual que en sendMessage | Opcional | |
disable_notification | Boolean | Opcional | |
thumbnail, disable_content_type_detection | Se ignoran | Se aceptan, pero no se usan |
Devuelve: el Message enviado (con un file_id nuevo en document)
El formato (mime_type) se deduce de la extensión del nombre del archivo. Los archivos enviados desde la aplicación pueden llegar a 2 GB, pero lo que el bot sube y descarga por HTTP tiene un límite de 20 MB, igual que en Telegram.
sendLocation
POST/bot<token>/sendLocation
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
chat_id | Integer o String | Sí | Chat de destino |
latitude, longitude | Float | Sí | Latitud (de −90 a 90) y longitud (de −180 a 180). Fuera de rango: 400 Bad Request: GEO_POINT_INVALID |
horizontal_accuracy | Float | Opcional | Radio de incertidumbre en metros, de 0 a 1.500. Se redondea a un entero al guardarse |
live_period | Integer | Opcional | La convierte en una ubicación en tiempo real: de 60 a 86.400 segundos, o 2147483647 (0x7FFFFFFF) hasta que se detenga. Cualquier otro valor da 400 Bad Request: wrong live_period |
heading | Integer | Opcional | Dirección de desplazamiento de una ubicación en tiempo real, de 1 a 360 grados |
proximity_alert_radius | Integer | Opcional | Radio de la alerta de proximidad de una ubicación en tiempo real, en metros. Se guarda y se muestra, pero todavía no se envía ninguna alerta |
reply_markup | Igual que en sendMessage | Opcional | |
disable_notification | Boolean | Opcional |
Devuelve: el Message enviado (con una Location en location)
sendVenue
POST/bot<token>/sendVenue
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
chat_id | Integer o String | Sí | Chat de destino |
latitude, longitude | Float | Sí | Dónde está el lugar (igual que en sendLocation) |
title | String | Sí | Nombre del lugar |
address | String | Sí | Dirección |
foursquare_id, foursquare_type | String | Opcional | ID y tipo del lugar en Foursquare; se transmiten tal cual |
google_place_id, google_place_type | String | Opcional | ID y tipo del lugar en Google. Solo se usan si no hay foursquare_id |
reply_markup | Igual que en sendMessage | Opcional | |
disable_notification | Boolean | Opcional |
Devuelve: el Message enviado (con un Venue en venue y el mismo punto en location)
Sin title o address obtienes 400 Bad Request: title and address are required.
sendContact
POST/bot<token>/sendContact
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
chat_id | Integer o String | Sí | Chat de destino |
phone_number | String | Sí | Número de teléfono; se transmite tal como está escrito |
first_name | String | Sí | Nombre |
last_name | String | Opcional | Apellido |
vcard | String | Opcional | Datos adicionales en formato vCard, hasta 64 KB |
reply_markup | Igual que en sendMessage | Opcional | |
disable_notification | Boolean | Opcional |
Devuelve: el Message enviado (con un Contact en contact)
Una tarjeta de contacto enviada por un bot nunca lleva user_id, aunque el número pertenezca a una cuenta de Pabal: un bot no tiene contactos, y si se rellenara, el bot podría averiguar de quién es un número enviando una tarjeta por número. Sin phone_number o first_name obtienes 400 Bad Request: phone_number and first_name are required.
editMessageLiveLocation
POST/bot<token>/editMessageLiveLocation
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
chat_id | Integer o String | Sí | Chat en el que está el mensaje |
message_id | Integer | Sí | La ubicación en tiempo real que se mueve (una enviada por el bot) |
latitude, longitude | Float | Sí | La nueva posición |
horizontal_accuracy | Float | Opcional | Radio de incertidumbre en metros, de 0 a 1.500 |
heading | Integer | Opcional | Dirección de desplazamiento, de 1 a 360 grados |
proximity_alert_radius | Integer | Opcional | Radio de la alerta de proximidad en metros. Con 0 se desactiva |
live_period | Integer | Opcional | Nuevo periodo para compartir, en segundos contados desde que se envió el mensaje. No se puede ampliar más de un día sobre el actual; 2147483647 significa hasta que se detenga |
reply_markup | InlineKeyboardMarkup | Opcional | Botones nuevos |
inline_message_id | String | No admitido | No hay modo inline, así que 400 |
Devuelve: el Message editado
Una ubicación en tiempo real que ya terminó —detenida o con el periodo agotado— no se puede mover ni revivir ampliando su periodo (400 Bad Request: message can't be edited). Lo mismo vale para un mensaje que no es una ubicación en tiempo real.
Un movimiento hecho menos de 10 segundos después del último que se aplicó, o de menos de 10 m, se deja pasar sin guardarlo. Detenerla y cambiar el periodo o el radio de alerta siempre se aplican. Cuando se deja pasar un movimiento, el Message devuelto muestra la última posición aplicada, así que no pasa nada por enviar uno cada pocos segundos.
stopMessageLiveLocation
POST/bot<token>/stopMessageLiveLocation
Parámetros: chat_id, message_id y reply_markup (opcional). Detiene la ubicación en tiempo real donde estaba por última vez. Su periodo se reduce al tiempo que de verdad pasó, así que todas las aplicaciones la muestran como terminada. Si ya estaba detenida o terminada, 400 Bad Request: message is not modified: …. Devuelve: el Message editado.
editMessageText
POST/bot<token>/editMessageText
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
chat_id | Integer o String | Sí | Chat en el que está el mensaje |
message_id | Integer | Sí | Mensaje que se edita (uno enviado por el bot) |
text | String | Sí | Texto nuevo, de 1 a 4.096 caracteres |
reply_markup | InlineKeyboardMarkup | Opcional | Botones nuevos. Si lo omites, los botones desaparecen (igual que en Telegram) |
inline_message_id | String | No admitido | No hay modo inline, así que 400 |
Devuelve: el Message editado. El cambio se refleja al instante en la aplicación, y los mensajes que edita una persona le llegan al bot como edited_message.
Si el texto y los botones son idénticos a los anteriores, 400 Bad Request: message is not modified: …; si intentas editar un mensaje ajeno, 400 Bad Request: message can't be edited.
editMessageCaption
POST/bot<token>/editMessageCaption
Parámetros: chat_id, message_id, caption (de 0 a 1.024 caracteres; si lo omites, se borra el pie) y reply_markup. Devuelve: el Message editado.
editMessageReplyMarkup
POST/bot<token>/editMessageReplyMarkup
Deja el texto como está y cambia solo los botones. Parámetros: chat_id, message_id y reply_markup (si lo omites, se borran los botones). Devuelve: el Message editado.
deleteMessage
POST/bot<token>/deleteMessage
Parámetros: chat_id y message_id. Borra el mensaje para ambas partes del chat. Si el mensaje no existe, 400 Bad Request: message to delete not found. Devuelve: True.
answerCallbackQuery
POST/bot<token>/answerCallbackQuery
Responde a un botón pulsado por una persona (CallbackQuery). Solo se puede responder en menos de 10 segundos y una sola vez.
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
callback_query_id | String | Sí | El callback_query.id de la actualización |
text | String | Opcional | Texto que aparece un momento sobre la pantalla |
show_alert | Boolean | Opcional | Con true, se muestra en un cuadro con botón de confirmación |
url | String | Opcional | Dirección que abrirá la aplicación |
cache_time | Integer | Opcional | Segundos que la aplicación recordará esta respuesta |
Devuelve: True. Si es demasiado tarde o ya se respondió, 400 Bad Request: query is too old and response timeout expired or query ID is invalid.
sendChatAction
POST/bot<token>/sendChatAction
Se acepta por compatibilidad y devuelve True, pero por ahora la aplicación no muestra «escribiendo…».
getChat
GET/bot<token>/getChat?chat_id=…
Parámetro: chat_id (numérico). Si es una persona, esa persona; si es un grupo, solo los grupos de los que el bot es miembro. Devuelve: Chat. Si no existe o no se puede ver, 400 Bad Request: chat not found.
getFile
GET/bot<token>/getFile?file_id=…
Obtiene la ruta de descarga de una foto recibida a partir de su file_id. Devuelve: File. Después se descarga desde esta dirección.
https://pabal.me/file/bot<token>/<file_path>
El file_id es también el derecho a obtener esa foto. Si el valor no es válido, 400 Bad Request: wrong file identifier/HTTP URL specified.
setMyCommands
POST/bot<token>/setMyCommands
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
commands | Array of BotCommand | Sí | 100 como máximo |
language_code | String | Opcional | Se guarda como lista de ese idioma. Por ahora la aplicación solo muestra la lista predeterminada, sin código de idioma |
scope | BotCommandScope | Se ignora |
Devuelve: True. Si el menú no cumple las reglas, no se guarda nada: 400 Bad Request: BOT_COMMAND_INVALID (un comando), BOT_COMMAND_DESCRIPTION_INVALID (una descripción vacía o de más de 256 caracteres), BOT_COMMANDS_TOO_MUCH (más de 100).
getMyCommands
GET/bot<token>/getMyCommands
Parámetro: language_code (opcional). Devuelve: Array of BotCommand.
deleteMyCommands
POST/bot<token>/deleteMyCommands
Parámetro: language_code (opcional). Vacía esa lista. Devuelve: True.
logOut · close
En Telegram son métodos que se usan al pasar a un servidor local de la Bot API. En Pabal no hay adónde pasar, así que solo se aceptan y devuelven True. Para invalidar un token, usa /revoke de BotFather.
Objetos
opcional junto a un campo significa que puede no aparecer. Pabal no envía los demás campos de Telegram que no figuran aquí.
Update
Una actualización. Contiene update_id y uno de los campos siguientes.
| Campo | Tipo | Descripción |
|---|---|---|
update_id | Integer | Número que aumenta de 1 en 1. Se usa en el offset de getUpdates |
message opcional | Message | Mensaje nuevo para el bot (en un chat uno a uno, o todos los mensajes de los grupos de los que el bot es miembro) |
edited_message opcional | Message | Mensaje editado |
callback_query opcional | CallbackQuery | Pulsación de un botón inline |
voice_call opcional | VoiceCall | Inicio y fin de una llamada a un bot de voz o de una llamada que hizo (solo llega si se pide por su nombre) |
voice_call_tool_query opcional | VoiceCallToolQuery | Herramienta del bot a la que llamó el modelo durante la llamada (solo llega si se pide por su nombre) |
voice_call_transcript opcional | VoiceCallTranscript | Un turno de la transcripción durante la llamada (solo llega si se pide por su nombre) |
User
| Campo | Tipo | Descripción |
|---|---|---|
id | Integer | ID de usuario (solo tiene sentido dentro de este servidor) |
is_bot | Boolean | true si es un bot |
first_name | String | Nombre. En una cuenta eliminada, Deleted Account |
last_name opcional | String | Apellido |
username opcional | String | Nombre de usuario (sin @) |
Chat
| Campo | Tipo | Descripción |
|---|---|---|
id | Integer | Si es una persona, su ID (positivo); si es un grupo básico, un número negativo |
type | String | private o group |
title opcional | String | Nombre del grupo (group) |
first_name, last_name, username opcional | String | Nombre y nombre de usuario de la otra persona (private) |
Message
| Campo | Tipo | Descripción |
|---|---|---|
message_id | Integer | Número del mensaje dentro de este chat |
from opcional | User | Remitente |
chat | Chat | Chat en el que está el mensaje |
date | Integer | Hora de envío (segundos Unix) |
edit_date opcional | Integer | Hora de la última edición |
text opcional | String | Texto (siempre presente si no es un mensaje con foto, archivo, ubicación, lugar o contacto) |
entities opcional | Array of MessageEntity | Comandos, menciones, URL y hashtags del texto |
photo opcional | Array of PhotoSize | Foto (en Pabal, un único elemento con el original) |
document opcional | Document | Archivo |
location opcional | Location | Una ubicación o una ubicación en tiempo real. Los mensajes de lugar también traen aquí su ubicación |
venue opcional | Venue | Un lugar |
contact opcional | Contact | Una tarjeta de contacto |
caption opcional | String | Pie de foto |
caption_entities opcional | Array of MessageEntity | Comandos, menciones, URL y hashtags del pie de foto |
reply_markup opcional | InlineKeyboardMarkup | Botones inline que lleva el mensaje |
MessageEntity
| Campo | Tipo | Descripción |
|---|---|---|
type | String | bot_command, mention, url, hashtag |
offset | Integer | Posición inicial (en unidades de código UTF-16) |
length | Integer | Longitud (en unidades de código UTF-16) |
El servidor las detecta en el texto y las añade automáticamente. Todavía no hay entidades de formato como negrita o cursiva.
PhotoSize
| Campo | Tipo | Descripción |
|---|---|---|
file_id | String | ID para descargar (getFile) y para volver a enviar (sendPhoto) |
file_unique_id | String | El mismo valor para la misma foto, aunque sea otro bot. No sirve para descargar |
width, height | Integer | Tamaño en píxeles |
file_size | Integer | Bytes |
Document
| Campo | Tipo | Descripción |
|---|---|---|
file_id | String | ID para descargar (getFile) y para volver a enviar (sendDocument) |
file_unique_id | String | El mismo valor para el mismo archivo, aunque sea otro bot. No sirve para descargar |
file_name | String | Nombre que le puso quien lo envió |
mime_type | String | Formato que indicó quien lo envió |
file_size | Integer | Bytes |
Location
| Campo | Tipo | Descripción |
|---|---|---|
latitude, longitude | Float | Latitud y longitud |
horizontal_accuracy opcional | Float | Radio de incertidumbre en metros |
live_period opcional | Integer | Periodo para compartir una ubicación en tiempo real, en segundos contados desde que se envió el mensaje. Al detenerla, se reduce al tiempo que de verdad pasó |
heading opcional | Integer | Dirección de desplazamiento, de 1 a 360 grados |
proximity_alert_radius opcional | Integer | Radio de la alerta de proximidad en metros |
Una ubicación en tiempo real que comparte una persona vuelve a llegar como edited_message cada vez que el servidor aplica un movimiento.
Venue
| Campo | Tipo | Descripción |
|---|---|---|
location | Location | Dónde está el lugar |
title | String | Nombre del lugar |
address | String | Dirección |
foursquare_id, foursquare_type opcional | String | ID y tipo del lugar en Foursquare |
google_place_id, google_place_type opcional | String | ID y tipo del lugar en Google |
Contact
| Campo | Tipo | Descripción |
|---|---|---|
phone_number | String | El número tal como lo escribió quien lo envió |
first_name | String | Nombre |
last_name opcional | String | Apellido |
user_id opcional | Integer | La cuenta de Pabal con ese número. Solo aparece si quien envía tiene a esa persona guardada en sus contactos, o si es esa misma persona |
vcard opcional | String | Datos adicionales en formato vCard |
File
| Campo | Tipo | Descripción |
|---|---|---|
file_id, file_unique_id | String | Igual que en PhotoSize |
file_size | Integer | Bytes |
file_path | String | Con la forma photos/<file_id>.jpg o documents/<file_id>. Se añade detrás de /file/bot<token>/ para descargar |
CallbackQuery
| Campo | Tipo | Descripción |
|---|---|---|
id | String | ID que se pasa a answerCallbackQuery |
from | User | Persona que pulsó el botón |
message opcional | Message | Mensaje que lleva el botón |
chat_instance | String | Valor que identifica ese chat |
data opcional | String | El callback_data del botón |
InlineKeyboardMarkup
inline_keyboard: Array of Array of InlineKeyboardButton. El array exterior son las filas y cada array interior, los botones de una fila. Como máximo 100 filas y 100 botones por mensaje.
InlineKeyboardButton
| Campo | Tipo | Descripción |
|---|---|---|
text | String | Texto del botón |
callback_data uno de los dos | String | Valor que se envía al bot al pulsar, de 1 a 64 bytes |
url uno de los dos | String | Dirección que se abre al pulsar |
Otros tipos como switch_inline_query, web_app, login_url o pay todavía no existen (400).
ReplyKeyboardMarkup
| Campo | Tipo | Descripción |
|---|---|---|
keyboard | Array of Array of KeyboardButton | Panel de botones bajo el campo de texto |
resize_keyboard, one_time_keyboard, is_persistent, selective opcional | Boolean | Ajustar el tamaño · ocultar tras un uso · mostrar siempre · solo para ciertas personas |
input_field_placeholder opcional | String | Texto de ayuda en el campo de texto |
KeyboardButton
Una cadena, o un objeto con text y los campos opcionales request_contact (enviar mi contacto) y request_location (enviar mi ubicación), de tipo Boolean.
ReplyKeyboardRemove
{"remove_keyboard": true}: oculta el panel de botones. selective es opcional.
ForceReply
{"force_reply": true}: la aplicación abre el campo de texto en modo de respuesta a este mensaje. selective e input_field_placeholder son opcionales.
BotCommand
| Campo | Tipo | Descripción |
|---|---|---|
command | String | De 1 a 32 caracteres: letras latinas minúsculas, números y guion bajo (sin /) |
description | String | De 1 a 256 caracteres |
WebhookInfo
| Campo | Tipo | Descripción |
|---|---|---|
url | String | Dirección del webhook; si no hay, una cadena vacía |
has_custom_certificate | Boolean | Siempre false |
pending_update_count | Integer | Número de actualizaciones que esperan a ser entregadas |
ip_address opcional | String | Última IP a la que se envió |
last_error_date opcional | Integer | Hora del último fallo (segundos Unix) |
last_error_message opcional | String | Motivo del último fallo (lista) |
max_connections opcional | Integer | El valor que pasaste a setWebhook |
allowed_updates opcional | Array of String | El valor que pasaste a setWebhook |
Llamadas de voz
Estos métodos solo existen en Pabal. Un bot de voz es un bot al que se llama desde la aplicación y cuyas llamadas atiende un modelo de voz de IA; con estos métodos se configura ese modelo y se gestionan las llamadas. Lo que tiene que estar activado, cómo activarlo y los ejemplos están en la guía Bots de voz. Los métodos existen también en un servidor con la voz desactivada, así que se pueden leer los ajustes, pero un bot con la voz desactivada o que el operador aún no ha autorizado no puede cambiar sus ajustes salvo para desactivar o borrar (403 VOICE_NOT_AVAILABLE).
setVoiceCallSettings
POST/bot<token>/setVoiceCallSettings
Cambia los ajustes de voz del bot. Solo cambia lo que envías; el servidor lo revisa todo y lo escribe de una vez, o lo rechaza entero. 30 veces por hora por bot (sumando @BotFather).
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
enabled | Boolean | Opcional | Activar solo es posible después de aceptar las reglas de los bots de voz en @BotFather (403 VOICE_RULES_NOT_ACCEPTED). Desactivar siempre funciona |
instructions | String | Opcional | Instrucciones del modelo, hasta 8.192 bytes. "" las borra (no en un bot gestionado activado) |
greeting | String | Opcional | Primer saludo, hasta 512 bytes. Con "", lo hace el modelo por su cuenta |
voice | String | Opcional | ID de voz, kore por defecto (lista) |
language | String | Opcional | ko-KR (por defecto) · en-US · ja-JP · cmn-CN · fr-FR · es-US · id-ID · vi-VN · ar-EG (lista). No distingue mayúsculas y minúsculas, y zh-CN se guarda como cmn-CN. Un bot que no está en coreano ni en inglés solo recibe llamadas después de que el operador lea las instrucciones y el saludo y lo autorice |
max_call_duration | Integer | Opcional | Segundos, de 60 a limits.max_call_duration |
max_concurrent_calls | Integer | Opcional | De 1 a limits.max_concurrent_calls |
tools | Array of VoiceCallTool | Opcional | Herramientas del bot a las que el modelo puede llamar durante la llamada: {"name", "description", "parameters"?, "timeout_ms"?}. Reglas en Definir herramientas. En un formulario, como cadena JSON |
transcript | Boolean | Opcional | Interruptor de la transcripción. Para activarlo hacen falta las condiciones para propietarios de bots |
mode | String | Opcional | managed (el modelo de IA de Pabal) o stream (modo stream) |
stream_url, stream_secret | String | Opcional | Junto con mode=stream. El secreto, solo en el cuerpo de la petición |
Devuelve: True
getVoiceCallSettings
GET/bot<token>/getVoiceCallSettings
Sin parámetros. Los ajustes actuales; si el bot puede recibir llamadas ahora (available, unavailable_reason); los límites del servidor; la lista de voces e idiomas; adónde irían las consultas de herramienta y la transcripción; y el estado de la clave propia, el modo stream y los servidores MCP. Las claves y los secretos nunca aparecen, solo sus huellas. Los campos están en Bots de voz — Ver los ajustes actuales.
Devuelve: VoiceCallSettings (objeto)
endVoiceCall
POST/bot<token>/endVoiceCall
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
voice_call_id | String | Sí | voice_call.id |
farewell | Boolean | Opcional | true por defecto: el modelo se despide brevemente y cuelga (si no ha colgado en 10 segundos, el servidor la termina). false: cuelga de inmediato |
Devuelve: True. Si no es una llamada en curso de este bot: 400 VOICE_CALL_NOT_FOUND.
sendVoiceCallContext
POST/bot<token>/sendVoiceCallContext
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
voice_call_id | String | Sí | Una llamada establecida |
text | String | Sí | Nota del propietario del bot para el modelo. Hasta 2.000 caracteres y 6.144 bytes, revisada con las reglas de contenido |
respond | Boolean | Opcional | false por defecto. true: el modelo reacciona enseguida |
Devuelve: True. Antes de la conexión: VOICE_CALL_NOT_ACTIVE; llamada en modo stream: VOICE_CONTEXT_NOT_SUPPORTED; demasiado larga: VOICE_CONTEXT_TOO_LONG; más de 10 por minuto en una llamada: 429.
getVoiceCallUsage
GET/bot<token>/getVoiceCallUsage
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
period | String | Opcional | today (por defecto) o month |
month | String | Opcional | YYYY-MM, solo junto con period=month |
Devuelve: period, from_date, to_date, calls, seconds, missed, refused (busy·budget·unavailable), platform_seconds, own_key_seconds, remaining_seconds_today (bot·owner) y, en un bot que alguna vez activó las llamadas, outbound (today·limits); los días se cuentan según la zona horaria del servidor
startVoiceCall
POST/bot<token>/startVoiceCall
El bot llama a una persona. Solo puede hacerlo un bot cuyo propietario lo activó en @BotFather y que el operador revisó, y solo a quienes permitieron que ese bot les llame. Las respuestas y cómo se evalúan las peticiones están en Bots de voz — Llamar.
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
user_id | Integer | Sí | La persona a la que llamar |
purpose | String | Sí | El que eligió el bot entre reminder, wake_up, delivery, appointment |
context | String | Sí | Lo que solo el modelo sabe, hasta 1.000 caracteres y 3.072 bytes, comprobado con las reglas de contenido |
max_duration | Integer | opcional | Lo más que puede durar una vez contestada, en segundos, de 30 a 300 (hasta el máximo del bot). 180 por defecto |
ring_timeout | Integer | opcional | Segundos que suena, de 30 a 45. 40 por defecto |
Devuelve: {"voice_call_id": "…"}; solo significa que se recibió la petición: si sonó y si contestaron llega en actualizaciones voice_call. Rechazos: 403 VOICE_OUTBOUND_OFF: <motivo>, 403 USER_UNAVAILABLE (siempre el mismo cuerpo), 429, 503 VOICE_UNAVAILABLE. No se puede llamar desde la respuesta de un webhook.
answerVoiceCallToolQuery
POST/bot<token>/answerVoiceCallToolQuery
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
tool_query_id | String | Sí | El id de la VoiceCallToolQuery |
result | String · Object · Array | Uno de los dos | Resultado de la herramienta, hasta 16.384 bytes |
error | String | Uno de los dos | Mensaje de fallo del bot, hasta 512 bytes |
Devuelve: True. La primera respuesta es la última. Si llega tarde: 400 TOOL_QUERY_EXPIRED: answered|timeout|cancelled|call_ended|unreachable; ID desconocido: TOOL_QUERY_ID_INVALID; respuesta rechazada: TOOL_RESULT_EMPTY·TOOL_RESULT_INVALID·TOOL_RESULT_TOO_LONG·TOOL_ERROR_TOO_LONG·TOOL_RESULT_REFUSED: <código>. Al POST de una consulta de herramienta recibido por webhook también se puede responder en el cuerpo de la respuesta.
setVoiceCallApiKey · deleteVoiceCallApiKey
POST/bot<token>/setVoiceCallApiKey
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
provider | String | Sí | google (clave JSON de cuenta de servicio de Vertex AI) o openai (sk-…) |
api_key | String | Sí | La clave. Solo en el cuerpo de la petición (en la cadena de consulta: 400), hasta 8 KiB |
Devuelve: True; la clave solo se guarda tras pasar una sesión de prueba de la pasarela. API_KEY_INVALID, API_KEY_NO_REALTIME_ACCESS, 503 API_KEY_CHECK_UNAVAILABLE (no se guarda), 403 VOICE_OWN_KEY_NOT_AVAILABLE (este servidor no acepta claves propias). deleteVoiceCallApiKey no lleva parámetros y siempre devuelve True. Ninguno de los dos métodos se puede llamar con bots.sendCustomRequest, y setVoiceCallApiKey tampoco en una respuesta de webhook. Más detalles en Clave de modelo propia.
Métodos de los servidores MCP
| Método | Parámetros | Devuelve |
|---|---|---|
setVoiceCallMcpServer | label, url, auth (objeto) y, opcionalmente, audience, allowed_tools, requested_level, timeout_ms, tools (clasificación aportada por el propietario), declaration, auto_repin | Resultado del registro (status, job_id). Si cambia el destinatario: 400 MCP_CHANGE_NEEDS_CONFIRMATION y después confirmación en @BotFather |
refreshVoiceCallMcpServer | label | Vuelve a revisarlo (refreshing: true, job_id) |
deleteVoiceCallMcpServer | label | True; funciona aunque MCP esté desactivado |
getVoiceCallMcpBinding | Ninguno | iss, sub (bot:<id>), issuer_document, jwks_uri, audiences |
createVoiceCallMcpClientKey | label | La jwk pública y su thumbprint (la clave privada nunca sale de Pabal) |
auth y los secretos se envían solo en el cuerpo de la petición. En un servidor sin MCP: 403 VOICE_NOT_AVAILABLE: MCP tools are not available on this server. Más detalles en Bots de voz — Servidores MCP.
Para recibir actualizaciones de voz
voice_call, voice_call_tool_query y voice_call_transcript solo llegan si las pides por su nombre: ponlas en el allowed_updates de getUpdates o de setWebhook. Con una lista vacía no llegan, y lo que ocurrió antes de pedirlas no se acumula. Una consulta de herramienta llega por un POST aparte si hay webhook, o al principio del siguiente getUpdates si se llamó a getUpdates en los últimos 15 segundos.
VoiceCall
| Campo | Tipo | Descripción |
|---|---|---|
id | String | ID de la llamada (decimal sin signo) |
direction | String | incoming (una llamada al bot) u outgoing (una llamada que hizo el bot). Siempre está |
event | String | started, ended, missed, refused; en una llamada que hizo el bot, started, ended, not_answered, failed |
from opcional | User | Solo en llamadas al bot. Quien llama: id, is_bot, first_name, last_name, language_code. Sin nombre de usuario ni número de teléfono |
to opcional | Object | Solo en llamadas que hizo el bot: {"id": …}, solo el id de quien recibe la llamada |
purpose opcional | String | Solo en llamadas que hizo el bot: el propósito de la petición |
date | Integer | Momento en que ocurrió (segundos Unix). started: en una llamada desde las aplicaciones, cuando termina el intercambio de claves; en una llamada desde el Apple Watch, cuando se conecta el audio del reloj. ended llega hasta 10 segundos más tarde en un servidor que espera la transcripción o que tiene «Guardar conversación» activado. En una llamada que hizo el bot, el ended de una llamada contestada siempre llega hasta 10 segundos más tarde, sea cual sea la configuración del servidor, mientras se espera el uso de la pasarela |
max_duration opcional | Integer | Duración máxima de esta llamada (segundos): started, ended |
start_date, duration opcional | Integer | Momento en que se estableció y segundos que estuvo establecida (reloj del servidor): ended |
end_reason opcional | String | caller (callee en una llamada que hizo el bot), bot, max_length, failed, operator, suspended: ended, missed |
refused_reason opcional | String | busy, budget, unavailable: refused |
transcript opcional | Object | {"turns": [{"role", "text", "offset_ms"}], "truncated"}: ended, cuando la transcripción estaba activada. Nunca en una llamada que hizo el bot |
VoiceCall no tiene ningún campo que indique si quien llama eligió «Guardar conversación». Es intencionado: ese texto pertenece a quien llama y no va al bot. Por eso, en un servidor con «Guardar conversación» activado, el ended de todas las llamadas que no son de modo stream espera lo mismo, y el número del mensaje siguiente al registro de la llamada es, en todas las llamadas, al menos 31 mayor que el del registro (los números se reservan de antemano).
VoiceCallToolQuery
| Campo | Tipo | Descripción |
|---|---|---|
id | String | ID que se pasa a answerVoiceCallToolQuery |
voice_call_id | String | ID de la llamada |
from | User | Quien llama (mismos campos que en VoiceCall) |
name | String | Nombre de la herramienta |
arguments | String | Argumentos: el texto de un objeto JSON (revisado por el servidor) |
budget_ms | Integer | Milisegundos desde que se recibe hasta que la respuesta tiene que llegar al servidor |
VoiceCallTranscript
| Campo | Tipo | Descripción |
|---|---|---|
voice_call_id | String | ID de la llamada |
seq | Integer | Número de turno |
role | String | caller o bot |
text | String | Texto escrito por la IA. Si contiene un número de tarjeta o de identificación nacional, los dígitos pasan a * |
is_final | Boolean | Siempre true |
offset_ms, date | Integer | Milisegundos desde que empezó el audio de la llamada, y la hora |
Solo llega por la Bot API HTTP. La llamada completa está en el campo transcript del voice_call de fin de llamada.
Modo stream (mode=stream)
En modo stream, el audio de la llamada va al servidor del propietario del bot en lugar del modelo de IA de Pabal. Si envías a setVoiceCallSettings mode=stream, stream_url=wss://… y stream_secret=… (de 32 a 128 caracteres ASCII imprimibles, solo en el cuerpo de la petición), la pasarela de Pabal intenta conectarse una vez a esa dirección (sondeo) y, si lo consigue, se activa. Hacen falta las condiciones para propietarios de bots y una compilación de la aplicación con la pantalla de confirmación del modo stream. Configuración, resultados del sondeo y ejemplos: Bots de voz — Modo stream.
- Conexión: TLS 1.2 o superior, certificado público que coincida con el nombre de la URL, subprotocolo
pabal.voice.v1, sin compresión, hasta 64 KiB por mensaje y 4 KiB por texto, sin redirecciones. - Firma:
X-Pabal-Voice-Signature: t=…,nonce=…,call=…,v1=HMAC-SHA256(stream_secret, "pabal-voice-stream/1\n<t>\n<nonce>\n<call>\n<host>\n<path?query>").hostes el host de la URL que diste. Comprueba los 300 segundos, el nonce de un solo uso, la comparación byte a byte y questart.call_idsea igual acall(más detalles). - Audio: los mensajes de texto son eventos JSON, y los binarios, PCM16 little-endian, mono, 24 kHz. La voz de quien llama llega en trozos de 20 ms (960 bytes).
- Pabal → bot:
start(una vez) ·live(terminó el aviso de IA) · audio ·mark·cleared·buffer_full·stop(→ cierre 1000). - Bot → Pabal:
ready(en los 3 segundos siguientes astart, lo primero de todo) · audio (después delive) ·clear·mark·hangup.
La forma de cada mensaje está en la tabla de Bots de voz — Especificación.
Errores de los métodos de voz
Los códigos de error de los métodos de voz y su significado están reunidos en Bots de voz — Errores. Un 403 tiene la forma Forbidden: <código>: <descripción> (VOICE_RULES_NOT_ACCEPTED, VOICE_NOT_AVAILABLE, VOICE_SUSPENDED, VOICE_OWNER_TERMS_NOT_ACCEPTED, VOICE_OWN_KEY_NOT_AVAILABLE, VOICE_STREAM_NOT_AVAILABLE, VOICE_OUTBOUND_OFF); USER_UNAVAILABLE siempre es exactamente Forbidden: USER_UNAVAILABLE, sin descripción, y si se supera el límite por hora llega 429 con retry_after.
Diferencias con la Bot API de Telegram
- Tipos de actualización: solo llegan
message,edited_message,callback_queryy los tipos de bots de voz propios de Pabal (voice_call,voice_call_tool_query,voice_call_transcript). No hay publicaciones de canal, consultas inline, pagos, encuestas, cambios de miembros (my_chat_member), etc. - Formato:
parse_modeyentitiesse ignoran y el texto se envía tal cual. Solo los comandos, las menciones, las URL y los hashtags se marcan automáticamente. - Tipos de chat: solo chats uno a uno y grupos básicos. No hay canales, supergrupos ni temas de foro.
- Multimedia: solo fotos, archivos (
sendDocument), ubicaciones, ubicaciones en tiempo real, lugares y contactos. No hay mensajes que se reproduzcan como vídeo o como voz, ni stickers, álbumes (sendMediaGroup), encuestas o envío mediante URL. Lo que se sube y se descarga por HTTP tiene un límite de 20 MB. - Ubicaciones: la alerta de proximidad de una ubicación en tiempo real (
proximity_alert_radius) solo se guarda, nunca se envía. Los movimientos a menos de 10 segundos o de 10 m del último aplicado se dejan pasar, y una tarjeta de contacto enviada por un bot no llevauser_id. - Bots en grupos: no hay modo de privacidad, así que reciben todos los mensajes del grupo.
- Cola: las actualizaciones no recogidas se guardan en memoria, hasta las 1.000 más recientes por bot, y se pierden al reiniciar el servidor (Telegram las conserva 24 horas).
- Webhooks: las actualizaciones se envían de una en una y en orden (
max_connectionssolo se guarda), no se aceptan certificados autofirmados y no hay restricción de puerto. - IDs: los IDs de usuario, de mensaje y de archivo solo tienen sentido dentro de este servidor. Como no hay canales ni supergrupos, tampoco hay IDs con la forma
-100…. - Métodos inexistentes: los que no están en la lista de arriba (
forwardMessage,copyMessage,sendVideo,sendPoll,getChatMember,banChatMember,answerInlineQuery…) devuelven404 Not Found: method not found.